From 9a17591fb7761bbe31c853ca01d22f1c82037944 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 8 Aug 2016 16:24:34 +0200 Subject: [PATCH] Start getting :open-editor to work with WebEngine It doesn't actually work yet (as it claims the field is not editable), but at least does not crash when the backend limitation for the command is removed. --- qutebrowser/browser/browsertab.py | 9 ++++++ qutebrowser/browser/commands.py | 29 ++++++++++--------- qutebrowser/browser/webengine/webenginetab.py | 19 ++++++++++++ qutebrowser/browser/webkit/webkittab.py | 12 ++++++++ qutebrowser/browser/webkit/webview.py | 1 + qutebrowser/javascript/webelem.js | 25 ++++++++++++---- 6 files changed, 76 insertions(+), 19 deletions(-) diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index a0362c4c9..662c88b5f 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -613,6 +613,15 @@ class AbstractTab(QWidget): """ raise NotImplementedError + def find_focus_element(self, callback): + """Find the focused element on the page async. + + Args: + callback: The callback to be called when the search finished. + Called with a WebEngineElement or None. + """ + raise NotImplementedError + def __repr__(self): try: url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode), diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 9ff8564cc..41256c6f8 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1408,6 +1408,21 @@ class CommandDispatcher: url = QUrl('qute://log?level={}'.format(level)) self._open(url, tab, bg, window) + def _open_editor_cb(self, elem): + """Open editor after the focus elem was found in open_editor.""" + if elem is None: + message.error(self._win_id, "No element focused!") + return + if not elem.is_editable(strict=True): + message.error(self._win_id, "Focused element is not editable!") + return + + text = elem.text(use_js=True) + ed = editor.ExternalEditor(self._win_id, self._tabbed_browser) + ed.editing_finished.connect(functools.partial( + self.on_editing_finished, elem)) + ed.edit(text) + @cmdutils.register(instance='command-dispatcher', modes=[KeyMode.insert], hide=True, scope='window', backend=usertypes.Backend.QtWebKit) @@ -1417,20 +1432,8 @@ class CommandDispatcher: The editor which should be launched can be configured via the `general -> editor` config option. """ - # FIXME:qtwebengine have a proper API for this tab = self._current_widget() - page = tab._widget.page() # pylint: disable=protected-access - try: - elem = webkitelem.focus_elem(page.currentFrame()) - except webkitelem.IsNullError: - raise cmdexc.CommandError("No element focused!") - if not elem.is_editable(strict=True): - raise cmdexc.CommandError("Focused element is not editable!") - text = elem.text(use_js=True) - ed = editor.ExternalEditor(self._win_id, self._tabbed_browser) - ed.editing_finished.connect(functools.partial( - self.on_editing_finished, elem)) - ed.edit(text) + tab.find_focus_element(self._open_editor_cb) def on_editing_finished(self, elem, text): """Write the editor text into the form field and clean up tempfile. diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 3ac460a87..7f43d352a 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -431,6 +431,25 @@ class WebEngineTab(browsertab.AbstractTab): js_cb = functools.partial(self._find_all_elements_js_cb, callback) self.run_js_async(js_code, js_cb) + def _find_focus_element_js_cb(self, callback, js_elem): + """Handle a found focus elem coming from JS and call the real callback. + + Args: + callback: The callback originally passed to find_focus_element. + Called with a WebEngineElement or None. + js_elem: The element serialized from javascript. + """ + log.webview.debug("Got focus element from JS: {!r}".format(js_elem)) + if js_elem is None: + callback(None) + else: + callback(webengineelem.WebEngineElement(js_elem)) + + def find_focus_element(self, callback): + js_code = javascript.assemble('webelem', 'focus_element') + js_cb = functools.partial(self._find_focus_element_js_cb, callback) + self.run_js_async(js_code, js_cb) + def _connect_signals(self): view = self._widget page = view.page() diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 0f479886c..47f866559 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -574,6 +574,18 @@ class WebKitTab(browsertab.AbstractTab): callback(elems) + def find_focus_element(self, callback): + frame = self._widget.page().currentFrame() + if frame is None: + callback(None) + return + + elem = frame.findFirstElement('*:focus') + if elem.isNull(): + callback(None) + else: + callback(webkitelem.WebKitElement(elem)) + @pyqtSlot() def _on_frame_load_finished(self): """Make sure we emit an appropriate status when loading finished. diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index 34246e069..adf2ea925 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -223,6 +223,7 @@ class WebView(QWebView): def mouserelease_insertmode(self): """If we have an insertmode check scheduled, handle it.""" + # FIXME:qtwebengine Use tab.find_focus_element here if not self._check_insertmode: return self._check_insertmode = False diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 9aa132e41..9163d6727 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -22,7 +22,12 @@ document._qutebrowser_elements = []; function _qutebrowser_serialize_elem(elem, id) { - var out = {}; + var out = { + "id": id, + "text": elem.text, + "tag_name": elem.tagName, + "outer_xml": elem.outerHTML + }; var attributes = {}; for (var i = 0; i < elem.attributes.length; ++i) { @@ -31,11 +36,6 @@ function _qutebrowser_serialize_elem(elem, id) { } out["attributes"] = attributes; - out["text"] = elem.text; - out["tag_name"] = elem.tagName; - out["outer_xml"] = elem.outerHTML; - out["id"] = id; - // console.log(JSON.stringify(out)); return out; @@ -58,6 +58,19 @@ function _qutebrowser_find_all_elements(selector) { } +function _qutebrowser_focus_element() { + var elem = document.activeElement; + if (!elem || elem === document.body) { + // "When there is no selection, the active element is the page's + // or null." + return null; + } + + var id = document._qutebrowser_elements.length; + return _qutebrowser_serialize_elem(elem, id); +} + + function _qutebrowser_get_element(id) { return document._qutebrowser_elements[id]; }