ff-automationv2 2.2.35 → 2.2.36-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/ai/llmcalls/llmAction.js +6 -5
  2. package/dist/ai/llmprompts/llmPromptTypes/systemPromptTypes.d.ts +3 -0
  3. package/dist/ai/llmprompts/promptRegistry.js +4 -0
  4. package/dist/ai/llmprompts/systemPrompts/actionExtractorPrompt.js +133 -133
  5. package/dist/ai/llmprompts/systemPrompts/arithmeticPrompt.js +40 -40
  6. package/dist/ai/llmprompts/systemPrompts/fileVerifyActionExtractorPrompt.js +36 -36
  7. package/dist/ai/llmprompts/systemPrompts/fireflinkElementIndexExtractor_Mob.js +4 -4
  8. package/dist/ai/llmprompts/systemPrompts/getActionExtractorPrompt.js +51 -51
  9. package/dist/ai/llmprompts/systemPrompts/inputContextPrompt.js +49 -49
  10. package/dist/ai/llmprompts/systemPrompts/mobileKeywordExtractor.js +86 -86
  11. package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.js +47 -47
  12. package/dist/ai/llmprompts/systemPrompts/otpExtractorPrompt.js +16 -16
  13. package/dist/ai/llmprompts/systemPrompts/sliderSwipeVisionPrompt.d.ts +2 -0
  14. package/dist/ai/llmprompts/systemPrompts/sliderSwipeVisionPrompt.js +54 -0
  15. package/dist/ai/llmprompts/systemPrompts/verifyActionExtractorPromptMob.js +43 -43
  16. package/dist/ai/llmprompts/userPrompts/userPrompt.js +21 -0
  17. package/dist/automation/actions/executor.d.ts +1 -0
  18. package/dist/automation/actions/executor.js +23 -0
  19. package/dist/automation/actions/interaction/swipe/slider_Swipe.d.ts +2 -0
  20. package/dist/automation/actions/interaction/swipe/slider_Swipe.js +129 -0
  21. package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +6 -0
  22. package/dist/automation/actions/interface/FireFlinkResponseSchema.js +8 -1
  23. package/dist/automation/actions/interface/swipeActionInterface.d.ts +15 -0
  24. package/dist/core/helpers/mobileSwipeHelper.d.ts +3 -0
  25. package/dist/core/helpers/mobileSwipeHelper.js +78 -0
  26. package/dist/core/helpers/visionFallbackHandler.js +5 -3
  27. package/dist/core/interfaces/actionInterface.d.ts +1 -0
  28. package/dist/core/interfaces/mobileSwipeSliderInterface.d.ts +25 -0
  29. package/dist/core/interfaces/mobileSwipeSliderInterface.js +2 -0
  30. package/dist/core/interfaces/promptInterface.d.ts +4 -0
  31. package/dist/core/main/actionHandlerFactory.js +2 -2
  32. package/dist/core/main/runAutomationScript.js +44 -1
  33. package/dist/core/types/promptType.d.ts +1 -0
  34. package/dist/core/types/promptType.js +1 -0
  35. package/dist/domAnalysis/searchBest.js +0 -1
  36. package/dist/utils/DomExtraction/jsForAttributeInjection.js +232 -232
  37. package/dist/utils/helpers/xpathcreation.js +18 -2
  38. package/package.json +91 -91
  39. package/dist/tests/Framework.d.ts +0 -0
  40. package/dist/tests/Framework.js +0 -62
  41. package/dist/tests/itertaion.d.ts +0 -1
  42. package/dist/tests/itertaion.js +0 -43
  43. package/dist/tests/multiBrowser.d.ts +0 -1
  44. package/dist/tests/multiBrowser.js +0 -34
  45. package/dist/tests/poc.d.ts +0 -0
  46. package/dist/tests/poc.js +0 -212
  47. package/dist/tests/senario.d.ts +0 -0
  48. package/dist/tests/senario.js +0 -1120
  49. package/dist/tests/start_iteration_suggestion.d.ts +0 -6
  50. package/dist/tests/start_iteration_suggestion.js +0 -46
  51. package/dist/tests/test.d.ts +0 -1
  52. package/dist/tests/test.js +0 -26
  53. package/dist/tests/test1.d.ts +0 -1
  54. package/dist/tests/test1.js +0 -30
  55. package/dist/tests/test12.d.ts +0 -1
  56. package/dist/tests/test12.js +0 -35
  57. package/dist/tests/test254.d.ts +0 -0
  58. package/dist/tests/test254.js +0 -87
  59. package/dist/tests/test3.d.ts +0 -1
  60. package/dist/tests/test3.js +0 -27
  61. package/dist/tests/testkaf.d.ts +0 -0
  62. package/dist/tests/testkaf.js +0 -52
  63. package/dist/tests/tests.data.d.ts +0 -1
  64. package/dist/tests/tests.data.js +0 -8
  65. package/dist/tests/testss.d.ts +0 -1
  66. package/dist/tests/testss.js +0 -23
  67. package/dist/tests/testwe.d.ts +0 -1
  68. package/dist/tests/testwe.js +0 -24
@@ -32,6 +32,7 @@ const stepActionInspector_js_1 = require("../helpers/stepActionInspector.js");
32
32
  const elementSelectorResolver_js_1 = require("../helpers/elementSelectorResolver.js");
33
33
  const runnerPostActionHelper_js_1 = require("../helpers/runnerPostActionHelper.js");
34
34
  const visionFallbackHandler_js_1 = require("../helpers/visionFallbackHandler.js");
35
+ const mobileSwipeHelper_js_1 = require("../helpers/mobileSwipeHelper.js");
35
36
  class AutomationRunner {
36
37
  constructor() {
37
38
  this.KafkaPayload = {};
@@ -588,6 +589,48 @@ class AutomationRunner {
588
589
  stepResult = retryActionResolution.stepResult;
589
590
  stepAction = retryActionResolution.stepAction;
590
591
  }
592
+ const swipeVisionResult = await (0, mobileSwipeHelper_js_1.checkMobileSwipeSlider)({
593
+ driver,
594
+ currentPlatform,
595
+ stepAction,
596
+ step,
597
+ domInfo,
598
+ doc,
599
+ stepProcessor,
600
+ priorAndNextSteps,
601
+ fallbackAttempt,
602
+ driverContext,
603
+ });
604
+ domInfo = swipeVisionResult.domInfo;
605
+ doc = swipeVisionResult.doc;
606
+ if (swipeVisionResult.isSlider) {
607
+ handler = actionHandlers["MOB_SwipeSlider"];
608
+ await handler({
609
+ parentXpath: swipeVisionResult.parentXpath,
610
+ targetXpath: swipeVisionResult.targetXpath,
611
+ xpath: swipeVisionResult.targetXpath,
612
+ selector: swipeVisionResult.targetXpath,
613
+ parentIndex: swipeVisionResult.parentIndex,
614
+ targetIndex: swipeVisionResult.targetIndex,
615
+ fireflinkIndex: swipeVisionResult.targetIndex,
616
+ direction: swipeVisionResult.direction,
617
+ currentStep: step,
618
+ elementName: currentElementName || result.response.elementName,
619
+ elementType: stepResult.response.elementType || stepResult.response.type,
620
+ pageDOM: domInfo.dom,
621
+ currentPlatform,
622
+ driver,
623
+ driverContext,
624
+ priorAndNextSteps,
625
+ stepCount,
626
+ stepIndex: currentStepIndex,
627
+ manualStep: step,
628
+ stepsInputData,
629
+ returnTypeVariableName: currentReturnTypeVariableName,
630
+ ...(currentVariableName ? { variableName: currentVariableName } : {}),
631
+ });
632
+ break;
633
+ }
591
634
  if (stepAction === "Builtin_DragAndDropToElement") {
592
635
  handler = actionHandlers[stepAction];
593
636
  const fireflinkIndex1 = stepResult.response.drag;
@@ -647,7 +690,7 @@ class AutomationRunner {
647
690
  fireflinkIndex,
648
691
  pageDOM,
649
692
  elementName: currentElementName || result.response.elementName,
650
- elementType: stepResult.response.elementType,
693
+ elementType: stepResult.response.elementType || stepResult.response.type,
651
694
  numOfScrolls: stepResult.response.numOfScrolls,
652
695
  direction: stepResult.response.direction,
653
696
  stepIndex: currentStepIndex,
@@ -5,6 +5,7 @@ export declare enum PromptType {
5
5
  ERROR_DESCRIPTION = "errorDescriptionPrompt",
6
6
  FF_INSPECTOR = "ffInspectorNumExtractor",
7
7
  VISION_PROMPT = "visionPrompt",
8
+ SWIPE_VISION_PROMPT = "swipe_vision_prompt",
8
9
  VERIFY_PROMPT = "verifyActionExtractorPrompt",
9
10
  GET_PROMPT = "getActionExtractorPrompt",
10
11
  WAIT_ACTION_EXTRACTOR_PROMPT = "waitActionExtractorPrompt",
@@ -9,6 +9,7 @@ var PromptType;
9
9
  PromptType["ERROR_DESCRIPTION"] = "errorDescriptionPrompt";
10
10
  PromptType["FF_INSPECTOR"] = "ffInspectorNumExtractor";
11
11
  PromptType["VISION_PROMPT"] = "visionPrompt";
12
+ PromptType["SWIPE_VISION_PROMPT"] = "swipe_vision_prompt";
12
13
  PromptType["VERIFY_PROMPT"] = "verifyActionExtractorPrompt";
13
14
  PromptType["GET_PROMPT"] = "getActionExtractorPrompt";
14
15
  PromptType["WAIT_ACTION_EXTRACTOR_PROMPT"] = "waitActionExtractorPrompt";
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DomSearcher = void 0;
4
4
  const fastest_levenshtein_1 = require("fastest-levenshtein");
5
5
  const fast_fuzzy_1 = require("fast-fuzzy");
6
- // import { logger } from "../utils/logger/logData";
7
6
  class DomSearcher {
8
7
  search(nodes, keywords, topN = 20, threshold = 40) {
9
8
  if (typeof keywords === "string") {
@@ -281,238 +281,238 @@ async function getReactAnnotatedDOM(browser, startIndex = 0) {
281
281
  // ==========================================================
282
282
  // ANGULAR INJECTION
283
283
  // ==========================================================
284
- exports.INJECT_AND_SERIALIZE_DOM_JS = `
285
- return (function (startIndex) {
286
- let nodeIndex = typeof startIndex === "number" ? startIndex : 0;
287
- function getCoverageStatus(el) {
288
- if (!el || !el.getBoundingClientRect) return "unknown";
289
- const s = getComputedStyle(el);
290
- if (s.pointerEvents === "none") return "false";
291
- const rect = el.getBoundingClientRect();
292
- if (rect.width === 0 || rect.height === 0) return "false";
293
- // Off-screen element — check if something is blocking the viewport
294
- const isOffScreen =
295
- rect.bottom < 0 ||
296
- rect.right < 0 ||
297
- rect.top > window.innerHeight ||
298
- rect.left > window.innerWidth;
299
- if (isOffScreen) {
300
- const centerEl = document.elementFromPoint(
301
- window.innerWidth / 2,
302
- window.innerHeight / 2
303
- );
304
- if (centerEl && centerEl !== document.body && centerEl !== document.documentElement) {
305
- // Something is at center of viewport — check if el and centerEl
306
- // share the same scrollable container
307
- const elScrollParent = getScrollParent(el);
308
- const centerScrollParent = getScrollParent(centerEl);
309
- if (elScrollParent !== centerScrollParent) return "true";
310
- }
311
- return "false";
312
- }
313
- // On-screen element — use elementFromPoint
314
- const testPoints = [
315
- [rect.left + 5, rect.top + 5],
316
- [rect.right - 5, rect.top + 5],
317
- [rect.left + 5, rect.bottom - 5],
318
- [rect.right - 5, rect.bottom - 5],
319
- [rect.left + rect.width / 2, rect.top + rect.height / 2]
320
- ];
321
- for (const [x, y] of testPoints) {
322
- if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) continue;
323
- const topEl = document.elementFromPoint(x, y);
324
- if (!topEl) continue;
325
- if (topEl === el || el.contains(topEl)) continue;
326
- if (topEl.contains(el)) continue; // topEl is ancestor, not a cover
327
- return "true";
328
- }
329
- return "false";
330
- }
331
- function getScrollParent(node) {
332
- if (!node || node === document.body) return document.body;
333
- const s = getComputedStyle(node);
334
- if (
335
- s.overflow === "auto" || s.overflow === "scroll" ||
336
- s.overflowY === "auto" || s.overflowY === "scroll"
337
- ) return node;
338
- return getScrollParent(node.parentElement);
339
- }
340
- function isUsable(node) {
341
- const s = getComputedStyle(node);
342
- if (s.display === "none" || s.visibility === "hidden" || s.opacity === "0") return false;
343
- const rect = node.getBoundingClientRect();
344
- if (rect.width === 0 || rect.height === 0) return false;
345
- return true;
346
- }
347
-
348
- function isSelectRelated(node) {
349
- return (
350
- node.matches?.("select, option") ||
351
- node.closest?.("select, [role='listbox'], [role='combobox']")
352
- );
353
- }
354
- function annotate(node) {
355
- const s = getComputedStyle(node);
356
- node.setAttribute("element-visibility", s.visibility || "");
357
- node.setAttribute("element-pointer-events", s.pointerEvents || "");
358
- node.setAttribute("element-zindex", s.zIndex || "");
359
- if (isSelectRelated(node)) {
360
- node.setAttribute("element-covered", "false");
361
- return;
362
- }
363
-
364
- if (!isUsable(node)) {
365
- node.setAttribute("element-covered", "true");
366
- return;
367
- }
368
- try {
369
- node.setAttribute("element-covered", getCoverageStatus(node));
370
-
371
- } catch {
372
- node.setAttribute("element-covered", "unknown");
373
- }
374
- }
375
- function getXPath(node) {
376
- if (!node || node.nodeType !== 1) return "";
377
- let segments = [];
378
- let current = node;
379
- while (current && current.nodeType === 1) {
380
- const parent = current.parentNode;
381
- if (parent instanceof ShadowRoot) {
382
- const host = parent.host;
383
- const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
384
- const index = siblings.indexOf(current) + 1;
385
- segments.unshift("shadow/" + current.tagName.toLowerCase() + "[" + index + "]");
386
- current = host;
387
- continue;
388
- }
389
- let index = 1;
390
- if (parent && parent.children) {
391
- const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
392
- index = siblings.indexOf(current) + 1;
393
- }
394
- segments.unshift(current.tagName.toLowerCase() + "[" + index + "]");
395
- if (!parent || parent.nodeType !== 1) break;
396
- current = parent;
397
- }
398
- let path = "/" + segments.join("/");
399
- if (node.ownerDocument !== document) {
400
- const frameEl = node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement;
401
- if (frameEl) {
402
- let framePath = frameEl.getAttribute("ff-xpath");
403
- if (!framePath) framePath = getXPath(frameEl);
404
- path = framePath + path;
405
- }
406
- }
407
- path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
408
- return path;
409
- }
410
- function walk(root) {
411
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
412
- let node = walker.currentNode;
413
- while (node) {
414
- if (node.nodeType === 1
415
- && typeof node.setAttribute === 'function'
416
- && !["META", "STYLE", "SCRIPT"].includes(node.tagName)) {
417
- node.setAttribute("ff-inspect", "Fire-Flink-" + nodeIndex++);
418
- node.setAttribute("ff-xpath", getXPath(node));
419
- annotate(node);
420
- }
421
- if (node.shadowRoot) walk(node.shadowRoot);
422
- if (node.tagName === "IFRAME") {
423
- try {
424
- const doc = node.contentDocument;
425
- if (doc && doc.documentElement) {
426
- walk(doc.documentElement);
427
- } else {
428
- node.setAttribute("ff-cross-origin", "true");
429
- }
430
- } catch {
431
- node.setAttribute("ff-cross-origin", "true");
432
- }
433
- }
434
- node = walker.nextNode();
435
- }
436
- }
437
- function cleanup(root) {
438
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
439
- let node = walker.currentNode;
440
- while (node) {
441
- if (node.nodeType === 1 && typeof node.removeAttribute === 'function') {
442
- node.removeAttribute("ff-inspect");
443
- node.removeAttribute("ff-xpath");
444
- node.removeAttribute("element-visibility");
445
- node.removeAttribute("element-pointer-events");
446
- node.removeAttribute("element-zindex");
447
- node.removeAttribute("element-covered");
448
- node.removeAttribute("ff-cross-origin");
449
- }
450
- if (node.shadowRoot) cleanup(node.shadowRoot);
451
- node = walker.nextNode();
452
- }
453
- }
454
- cleanup(document.documentElement);
455
- // Force browser to settle DOM after cleanup
456
- void document.documentElement.offsetHeight;
457
- walk(document.documentElement);
458
- function serializeNode(node) {
459
- if (node.nodeType === Node.TEXT_NODE) return node.textContent;
460
- if (node.nodeType !== Node.ELEMENT_NODE) return "";
461
- const tag = node.tagName.toLowerCase();
462
- let html = "<" + tag;
463
- for (const attr of node.attributes) {
464
- if (attr.name === "srcdoc") continue;
465
- html += " " + attr.name + "=\"" + attr.value.replace(/"/g, "&quot;") + "\"";
466
- }
467
- html += ">";
468
- if (node.shadowRoot) {
469
- html += "<!--#shadow-root-->";
470
- node.shadowRoot.childNodes.forEach(child => { html += serializeNode(child); });
471
- html += "<!--#/shadow-root-->";
472
- }
473
- if (tag === "iframe" && node.getAttribute("ff-cross-origin") !== "true") {
474
- try {
475
- const doc = node.contentDocument;
476
- if (doc) html += serializeNode(doc.documentElement);
477
- } catch {}
478
- }
479
- node.childNodes.forEach(child => { html += serializeNode(child); });
480
- html += "</" + tag + ">";
481
- return html;
482
- }
483
-
484
- const crossOriginIframes =
485
- Array.from(
486
- document.querySelectorAll("iframe")
487
- )
488
- .filter(
489
- iframe =>
490
- iframe.getAttribute(
491
- "ff-cross-origin"
492
- ) === "true"
493
- )
494
- .map(iframe => ({
495
- xpath:
496
- iframe.getAttribute(
497
- "ff-xpath"
498
- ),
499
-
500
- src:
501
- iframe.getAttribute("src"),
502
-
503
- title:
504
- iframe.getAttribute("title"),
505
-
506
- name:
507
- iframe.getAttribute("name")
508
- }));
509
-
510
- return {
511
- dom: "<!DOCTYPE html>\n" + serializeNode(document.documentElement),
512
- crossOriginIframes: crossOriginIframes,
513
- nextNodeIndex: nodeIndex
514
- };
515
- })(arguments[0]);
284
+ exports.INJECT_AND_SERIALIZE_DOM_JS = `
285
+ return (function (startIndex) {
286
+ let nodeIndex = typeof startIndex === "number" ? startIndex : 0;
287
+ function getCoverageStatus(el) {
288
+ if (!el || !el.getBoundingClientRect) return "unknown";
289
+ const s = getComputedStyle(el);
290
+ if (s.pointerEvents === "none") return "false";
291
+ const rect = el.getBoundingClientRect();
292
+ if (rect.width === 0 || rect.height === 0) return "false";
293
+ // Off-screen element — check if something is blocking the viewport
294
+ const isOffScreen =
295
+ rect.bottom < 0 ||
296
+ rect.right < 0 ||
297
+ rect.top > window.innerHeight ||
298
+ rect.left > window.innerWidth;
299
+ if (isOffScreen) {
300
+ const centerEl = document.elementFromPoint(
301
+ window.innerWidth / 2,
302
+ window.innerHeight / 2
303
+ );
304
+ if (centerEl && centerEl !== document.body && centerEl !== document.documentElement) {
305
+ // Something is at center of viewport — check if el and centerEl
306
+ // share the same scrollable container
307
+ const elScrollParent = getScrollParent(el);
308
+ const centerScrollParent = getScrollParent(centerEl);
309
+ if (elScrollParent !== centerScrollParent) return "true";
310
+ }
311
+ return "false";
312
+ }
313
+ // On-screen element — use elementFromPoint
314
+ const testPoints = [
315
+ [rect.left + 5, rect.top + 5],
316
+ [rect.right - 5, rect.top + 5],
317
+ [rect.left + 5, rect.bottom - 5],
318
+ [rect.right - 5, rect.bottom - 5],
319
+ [rect.left + rect.width / 2, rect.top + rect.height / 2]
320
+ ];
321
+ for (const [x, y] of testPoints) {
322
+ if (x < 0 || y < 0 || x > window.innerWidth || y > window.innerHeight) continue;
323
+ const topEl = document.elementFromPoint(x, y);
324
+ if (!topEl) continue;
325
+ if (topEl === el || el.contains(topEl)) continue;
326
+ if (topEl.contains(el)) continue; // topEl is ancestor, not a cover
327
+ return "true";
328
+ }
329
+ return "false";
330
+ }
331
+ function getScrollParent(node) {
332
+ if (!node || node === document.body) return document.body;
333
+ const s = getComputedStyle(node);
334
+ if (
335
+ s.overflow === "auto" || s.overflow === "scroll" ||
336
+ s.overflowY === "auto" || s.overflowY === "scroll"
337
+ ) return node;
338
+ return getScrollParent(node.parentElement);
339
+ }
340
+ function isUsable(node) {
341
+ const s = getComputedStyle(node);
342
+ if (s.display === "none" || s.visibility === "hidden" || s.opacity === "0") return false;
343
+ const rect = node.getBoundingClientRect();
344
+ if (rect.width === 0 || rect.height === 0) return false;
345
+ return true;
346
+ }
347
+
348
+ function isSelectRelated(node) {
349
+ return (
350
+ node.matches?.("select, option") ||
351
+ node.closest?.("select, [role='listbox'], [role='combobox']")
352
+ );
353
+ }
354
+ function annotate(node) {
355
+ const s = getComputedStyle(node);
356
+ node.setAttribute("element-visibility", s.visibility || "");
357
+ node.setAttribute("element-pointer-events", s.pointerEvents || "");
358
+ node.setAttribute("element-zindex", s.zIndex || "");
359
+ if (isSelectRelated(node)) {
360
+ node.setAttribute("element-covered", "false");
361
+ return;
362
+ }
363
+
364
+ if (!isUsable(node)) {
365
+ node.setAttribute("element-covered", "true");
366
+ return;
367
+ }
368
+ try {
369
+ node.setAttribute("element-covered", getCoverageStatus(node));
370
+
371
+ } catch {
372
+ node.setAttribute("element-covered", "unknown");
373
+ }
374
+ }
375
+ function getXPath(node) {
376
+ if (!node || node.nodeType !== 1) return "";
377
+ let segments = [];
378
+ let current = node;
379
+ while (current && current.nodeType === 1) {
380
+ const parent = current.parentNode;
381
+ if (parent instanceof ShadowRoot) {
382
+ const host = parent.host;
383
+ const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
384
+ const index = siblings.indexOf(current) + 1;
385
+ segments.unshift("shadow/" + current.tagName.toLowerCase() + "[" + index + "]");
386
+ current = host;
387
+ continue;
388
+ }
389
+ let index = 1;
390
+ if (parent && parent.children) {
391
+ const siblings = Array.from(parent.children).filter(n => n.tagName === current.tagName);
392
+ index = siblings.indexOf(current) + 1;
393
+ }
394
+ segments.unshift(current.tagName.toLowerCase() + "[" + index + "]");
395
+ if (!parent || parent.nodeType !== 1) break;
396
+ current = parent;
397
+ }
398
+ let path = "/" + segments.join("/");
399
+ if (node.ownerDocument !== document) {
400
+ const frameEl = node.ownerDocument.defaultView && node.ownerDocument.defaultView.frameElement;
401
+ if (frameEl) {
402
+ let framePath = frameEl.getAttribute("ff-xpath");
403
+ if (!framePath) framePath = getXPath(frameEl);
404
+ path = framePath + path;
405
+ }
406
+ }
407
+ path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
408
+ return path;
409
+ }
410
+ function walk(root) {
411
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
412
+ let node = walker.currentNode;
413
+ while (node) {
414
+ if (node.nodeType === 1
415
+ && typeof node.setAttribute === 'function'
416
+ && !["META", "STYLE", "SCRIPT"].includes(node.tagName)) {
417
+ node.setAttribute("ff-inspect", "Fire-Flink-" + nodeIndex++);
418
+ node.setAttribute("ff-xpath", getXPath(node));
419
+ annotate(node);
420
+ }
421
+ if (node.shadowRoot) walk(node.shadowRoot);
422
+ if (node.tagName === "IFRAME") {
423
+ try {
424
+ const doc = node.contentDocument;
425
+ if (doc && doc.documentElement) {
426
+ walk(doc.documentElement);
427
+ } else {
428
+ node.setAttribute("ff-cross-origin", "true");
429
+ }
430
+ } catch {
431
+ node.setAttribute("ff-cross-origin", "true");
432
+ }
433
+ }
434
+ node = walker.nextNode();
435
+ }
436
+ }
437
+ function cleanup(root) {
438
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
439
+ let node = walker.currentNode;
440
+ while (node) {
441
+ if (node.nodeType === 1 && typeof node.removeAttribute === 'function') {
442
+ node.removeAttribute("ff-inspect");
443
+ node.removeAttribute("ff-xpath");
444
+ node.removeAttribute("element-visibility");
445
+ node.removeAttribute("element-pointer-events");
446
+ node.removeAttribute("element-zindex");
447
+ node.removeAttribute("element-covered");
448
+ node.removeAttribute("ff-cross-origin");
449
+ }
450
+ if (node.shadowRoot) cleanup(node.shadowRoot);
451
+ node = walker.nextNode();
452
+ }
453
+ }
454
+ cleanup(document.documentElement);
455
+ // Force browser to settle DOM after cleanup
456
+ void document.documentElement.offsetHeight;
457
+ walk(document.documentElement);
458
+ function serializeNode(node) {
459
+ if (node.nodeType === Node.TEXT_NODE) return node.textContent;
460
+ if (node.nodeType !== Node.ELEMENT_NODE) return "";
461
+ const tag = node.tagName.toLowerCase();
462
+ let html = "<" + tag;
463
+ for (const attr of node.attributes) {
464
+ if (attr.name === "srcdoc") continue;
465
+ html += " " + attr.name + "=\"" + attr.value.replace(/"/g, "&quot;") + "\"";
466
+ }
467
+ html += ">";
468
+ if (node.shadowRoot) {
469
+ html += "<!--#shadow-root-->";
470
+ node.shadowRoot.childNodes.forEach(child => { html += serializeNode(child); });
471
+ html += "<!--#/shadow-root-->";
472
+ }
473
+ if (tag === "iframe" && node.getAttribute("ff-cross-origin") !== "true") {
474
+ try {
475
+ const doc = node.contentDocument;
476
+ if (doc) html += serializeNode(doc.documentElement);
477
+ } catch {}
478
+ }
479
+ node.childNodes.forEach(child => { html += serializeNode(child); });
480
+ html += "</" + tag + ">";
481
+ return html;
482
+ }
483
+
484
+ const crossOriginIframes =
485
+ Array.from(
486
+ document.querySelectorAll("iframe")
487
+ )
488
+ .filter(
489
+ iframe =>
490
+ iframe.getAttribute(
491
+ "ff-cross-origin"
492
+ ) === "true"
493
+ )
494
+ .map(iframe => ({
495
+ xpath:
496
+ iframe.getAttribute(
497
+ "ff-xpath"
498
+ ),
499
+
500
+ src:
501
+ iframe.getAttribute("src"),
502
+
503
+ title:
504
+ iframe.getAttribute("title"),
505
+
506
+ name:
507
+ iframe.getAttribute("name")
508
+ }));
509
+
510
+ return {
511
+ dom: "<!DOCTYPE html>\n" + serializeNode(document.documentElement),
512
+ crossOriginIframes: crossOriginIframes,
513
+ nextNodeIndex: nodeIndex
514
+ };
515
+ })(arguments[0]);
516
516
  `;
517
517
  async function injectAngular(browser, startIndex = 0) {
518
518
  const fn = new Function("startIndex", exports.INJECT_AND_SERIALIZE_DOM_JS);
@@ -61,8 +61,24 @@ function getFFIndexAndXPath(annotatedDom, fireflinkIndex, platform) {
61
61
  else {
62
62
  xpathQuery = `//*[@FF-inspecter='${fireflinkIndex}']`;
63
63
  }
64
- const nodes = xpath_1.default.select(xpathQuery, xmlDoc);
65
- const element = nodes.length ? nodes[0] : null;
64
+ let nodes = xpath_1.default.select(xpathQuery, xmlDoc);
65
+ let element = nodes.length ? nodes[0] : null;
66
+ if (!element && isBounds) {
67
+ if (platform === "android") {
68
+ xpathQuery = `//*[@bounds='${fireflinkIndex}']`;
69
+ nodes = xpath_1.default.select(xpathQuery, xmlDoc);
70
+ element = nodes.length ? nodes[0] : null;
71
+ }
72
+ else {
73
+ const match = fireflinkIndex.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
74
+ if (match) {
75
+ const [, x1, y1, x2, y2] = match.map(Number);
76
+ xpathQuery = `//*[@x='${x1}' and @y='${y1}' and @width='${x2 - x1}' and @height='${y2 - y1}']`;
77
+ nodes = xpath_1.default.select(xpathQuery, xmlDoc);
78
+ element = nodes.length ? nodes[0] : null;
79
+ }
80
+ }
81
+ }
66
82
  if (!element) {
67
83
  return { xpathPath: "" };
68
84
  }