From 59eaf3d6fa98a6216f73d001223336febb6f03f1 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Tue, 20 Jul 2021 15:38:10 +0200 Subject: [PATCH 1/8] Add function to webelem.js to query all elements matching an xpath. --- qutebrowser/javascript/webelem.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index b4cef24bc..5c50d016f 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -238,6 +238,20 @@ window._qutebrowser.webelem = (function() { return {"success": true, "result": out}; }; + funcs.find_xpath = (xpath) => { + xpath_result = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + const out = []; + + for(let i = 0; i < xpath_result.snapshotLength; i++) { + out.push(serialize_elem(xpath_result.snapshotItem(i)); + } + + // Maybe need to rescurse into the subframes. + + return {"success": true, "result": out}; + } + // Runs a function in a frame until the result is not null, then return // If no frame succeeds, return null function run_frames(func) { From 6bde50c6fea0917d45b2038ccf75e86ec35834f0 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Tue, 20 Jul 2021 16:06:13 +0200 Subject: [PATCH 2/8] Add find_xpath method to Tab API. Missing implementation in webkit. --- qutebrowser/browser/browsertab.py | 8 ++++++++ qutebrowser/browser/webengine/webenginetab.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 81915e11c..9178a26da 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -832,6 +832,14 @@ class AbstractElements: """ raise NotImplementedError + def find_xpath(self, xpath: str, callback: _MultiCallback) -> None: + """Find all HTML elements matching the given XPath. + + Args: + xpath: The XPath to search for. + callback: The callback to be called when the search finished. + """ + class AbstractAudio(QObject): diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 8057d5800..704bd3f92 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -796,6 +796,11 @@ class WebEngineElements(browsertab.AbstractElements): js_cb = functools.partial(self._js_cb_single, callback) self._tab.run_js_async(js_code, js_cb) + def find_xpath(self, xpath, callback): + js_code = javascript.assemble('webelem', 'find_xpath', xpath) + js_cb = functools.partial(self._js_cb_single, callback) + self._tab.run_js_async(js_code, js_cb) + class WebEngineAudio(browsertab.AbstractAudio): From c46f1160343bb94ec2d4a9982dfa9077598df725 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Tue, 20 Jul 2021 16:25:57 +0200 Subject: [PATCH 3/8] Fixed missing closing bracket. --- qutebrowser/javascript/webelem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 5c50d016f..6e4e6f00e 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -244,7 +244,7 @@ window._qutebrowser.webelem = (function() { const out = []; for(let i = 0; i < xpath_result.snapshotLength; i++) { - out.push(serialize_elem(xpath_result.snapshotItem(i)); + out.push(serialize_elem(xpath_result.snapshotItem(i))); } // Maybe need to rescurse into the subframes. From b1c24bef331bd145163cb110fe77d872b65c2f18 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Tue, 20 Jul 2021 16:39:12 +0200 Subject: [PATCH 4/8] Fixed javascript problems. --- qutebrowser/javascript/webelem.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 6e4e6f00e..681a34401 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -239,18 +239,19 @@ window._qutebrowser.webelem = (function() { }; funcs.find_xpath = (xpath) => { - xpath_result = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + const xpath_result = document.evaluate(xpath, document, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); const out = []; - for(let i = 0; i < xpath_result.snapshotLength; i++) { + for (let i = 0; i < xpath_result.snapshotLength; i++) { out.push(serialize_elem(xpath_result.snapshotItem(i))); } // Maybe need to rescurse into the subframes. return {"success": true, "result": out}; - } + }; // Runs a function in a frame until the result is not null, then return // If no frame succeeds, return null From 2046253c2b0d59369a82af70295be4744b070a3f Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Thu, 16 Jun 2022 16:55:28 +0200 Subject: [PATCH 5/8] find_by_xpath: Also search subframes. --- qutebrowser/javascript/webelem.js | 33 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 681a34401..815a827d7 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -180,10 +180,10 @@ window._qutebrowser.webelem = (function() { let rect = add_offset_rect(elem.getBoundingClientRect(), offset_rect); if (!rect || - rect.top > window.innerHeight || - rect.bottom < 0 || - rect.left > window.innerWidth || - rect.right < 0) { + rect.top > window.innerHeight || + rect.bottom < 0 || + rect.left > window.innerWidth || + rect.right < 0) { return false; } @@ -239,18 +239,27 @@ window._qutebrowser.webelem = (function() { }; funcs.find_xpath = (xpath) => { - const xpath_result = document.evaluate(xpath, document, null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + function finder(win) { + const doc = win.document; + const xpath_result = doc.evaluate(xpath, doc, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - const out = []; + const out = []; - for (let i = 0; i < xpath_result.snapshotLength; i++) { - out.push(serialize_elem(xpath_result.snapshotItem(i))); + for (let i = 0; i < xpath_result.snapshotLength; i++) { + out.push(serialize_elem(xpath_result.snapshotItem(i))); + } + + if (out.length > 0) { + return out; + } + return null; } - // Maybe need to rescurse into the subframes. + const direct = finder(window) ?? []; + const in_frames = run_frames(finder) ?? []; - return {"success": true, "result": out}; + return {"success": true, "result": [...direct, ...in_frames]}; }; // Runs a function in a frame until the result is not null, then return @@ -331,7 +340,7 @@ window._qutebrowser.webelem = (function() { (frame) => { // Subtract offsets due to being in an iframe const frame_offset_rect = - frame.frameElement.getBoundingClientRect(); + frame.frameElement.getBoundingClientRect(); return serialize_elem(frame.document. elementFromPoint(x - frame_offset_rect.left, y - frame_offset_rect.top), frame); From ff8505e685678b801ec645597b7d83801031c31d Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Thu, 16 Jun 2022 17:18:41 +0200 Subject: [PATCH 6/8] Add javascript-helper to traverse DOM in prefix order. --- qutebrowser/javascript/webelem.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 815a827d7..75e9c0957 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -352,6 +352,26 @@ window._qutebrowser.webelem = (function() { return serialize_elem(elem); }; + function *traverse_dom(start_node) { + while (start_node !== null) { + yield start_node; + if (start_node.children.length > 0) { + start_node = start_node.children[0]; + continue; + } + + if (iframe_same_domain(start_node.contentWindow)) { + yield* traverse_dom(start_node.document); + } + + while (start_node !== null && start_node.nextSibling === null) { + start_node = start_node.parentNode; + } + + start_node = start_node?.nextElementSibling; + } + } + // Function for returning a selection or focus to python (so we can click // it). If nothing is selected but there is something focused, returns // "focused" From b15044825086f6a04610b0be08fd0bcfac20fe99 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Sun, 19 Jun 2022 11:58:19 +0200 Subject: [PATCH 7/8] Update eslint to use ecma standard 11 --- qutebrowser/javascript/.eslintrc.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qutebrowser/javascript/.eslintrc.yaml b/qutebrowser/javascript/.eslintrc.yaml index 939500aa3..f845fdbd0 100644 --- a/qutebrowser/javascript/.eslintrc.yaml +++ b/qutebrowser/javascript/.eslintrc.yaml @@ -14,6 +14,9 @@ env: browser: true es6: true +parserOptions: + ecmaVersion: 11 + extends: "eslint:all" @@ -34,7 +37,7 @@ rules: max-statements: ["error", {"max": 40}] quotes: ["error", "double", {"avoidEscape": true}] object-property-newline: ["error", {"allowMultiplePropertiesPerLine": true}] - comma-dangle: ["error", "always-multiline"] + comma-dangle: ["error", {"arrays": "always-multiline", "objects": "always-multiline"}] no-magic-numbers: "off" no-undefined: "off" wrap-iife: ["error", "inside"] From bde362ca2194a87d6a824c9b270fe99f9294ad50 Mon Sep 17 00:00:00 2001 From: Nicholas Schwab Date: Sun, 19 Jun 2022 12:01:52 +0200 Subject: [PATCH 8/8] Fix some problems in webelem.js --- qutebrowser/javascript/webelem.js | 51 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 75e9c0957..a453876ff 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -238,6 +238,21 @@ window._qutebrowser.webelem = (function() { return {"success": true, "result": out}; }; + // Runs a function in a frame until the result is not null, then return + // If no frame succeeds, return null + function run_frames(func) { + for (let i = 0; i < window.frames.length; ++i) { + const frame = window.frames[i]; + if (iframe_same_domain(frame)) { + const result = func(frame); + if (result) { + return result; + } + } + } + return null; + } + funcs.find_xpath = (xpath) => { function finder(win) { const doc = win.document; @@ -262,21 +277,6 @@ window._qutebrowser.webelem = (function() { return {"success": true, "result": [...direct, ...in_frames]}; }; - // Runs a function in a frame until the result is not null, then return - // If no frame succeeds, return null - function run_frames(func) { - for (let i = 0; i < window.frames.length; ++i) { - const frame = window.frames[i]; - if (iframe_same_domain(frame)) { - const result = func(frame); - if (result) { - return result; - } - } - } - return null; - } - funcs.find_id = (id) => { const elem = document.getElementById(id); if (elem) { @@ -353,22 +353,23 @@ window._qutebrowser.webelem = (function() { }; function *traverse_dom(start_node) { - while (start_node !== null) { - yield start_node; - if (start_node.children.length > 0) { - start_node = start_node.children[0]; - continue; + let cur_node = start_node; + while (cur_node !== null) { + yield cur_node; + if (cur_node.children.length > 0) { + cur_node = cur_node.children[0]; + continue; // eslint-disable-line no-continue } - if (iframe_same_domain(start_node.contentWindow)) { - yield* traverse_dom(start_node.document); + if (iframe_same_domain(cur_node.contentWindow)) { + yield* traverse_dom(cur_node.document); } - while (start_node !== null && start_node.nextSibling === null) { - start_node = start_node.parentNode; + while (cur_node !== null && cur_node.nextSibling === null) { + cur_node = cur_node.parentNode; } - start_node = start_node?.nextElementSibling; + cur_node = cur_node?.nextElementSibling; } }