Skip to content
Permalink
Browse files
Add initial WebEngineElement implementation
This allows :navigate prev/next to work correctly via the javascript
bridge.
  • Loading branch information
The-Compiler committed Aug 8, 2016
1 parent 1c73751 commit b8e2d5f8f6a3547fede59e1dbc8e65f5e3c7358f
@@ -109,11 +109,6 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
background: True to open in a background tab.
window: True to open in a new window, False for the current one.
"""
# FIXME:qtwebengine have a proper API for this
if browsertab.backend == usertypes.Backend.QtWebEngine:
raise Error(":navigate prev/next is not supported yet with "
"QtWebEngine")

def _prevnext_cb(elems):
elem = _find_prevnext(prev, elems)
word = 'prev' if prev else 'forward'
@@ -79,7 +79,7 @@ def __eq__(self, other):
raise NotImplementedError

def __str__(self):
raise NotImplementedError
return self.text()

def __getitem__(self, key):
raise NotImplementedError
@@ -90,9 +90,6 @@ def __setitem__(self, key, val):
def __delitem__(self, key):
raise NotImplementedError

def __contains__(self, key):
raise NotImplementedError

def __iter__(self):
raise NotImplementedError

@@ -0,0 +1,176 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

# Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.

# FIXME:qtwebengine remove this once the stubs are gone
# pylint: disable=unused-variable

"""QtWebEngine specific part of the web element API."""

from PyQt5.QtCore import QRect

from qutebrowser.utils import log
from qutebrowser.browser import webelem


class WebEngineElement(webelem.AbstractWebElement):

"""A web element for QtWebEngine, using JS under the hood."""

def __init__(self, js_dict):
self._id = js_dict['id']
self._js_dict = js_dict

def __eq__(self, other):
if not isinstance(other, WebEngineElement):
return NotImplemented
return self._id == other._id # pylint: disable=protected-access

def __getitem__(self, key):
attrs = self._js_dict['attributes']
return attrs[key]

def __setitem__(self, key, val):
log.stub()

def __delitem__(self, key):
log.stub()

def __iter__(self):
return iter(self._js_dict['attributes'])

def __len__(self):
return len(self._js_dict['attributes'])

def frame(self):
log.stub()
return None

def geometry(self):
log.stub()
return QRect()

def document_element(self):
log.stub()
return None

def create_inside(self, tagname):
log.stub()
return None

def find_first(self, selector):
log.stub()
return None

def style_property(self, name, *, strategy):
log.stub()
return ''

def classes(self):
"""Get a list of classes assigned to this element."""
log.stub()
return []

def tag_name(self):
"""Get the tag name of this element.
The returned name will always be lower-case.
"""
return self._js_dict['tag_name']

def outer_xml(self):
"""Get the full HTML representation of this element."""
return self._js_dict['outer_xml']

def text(self, *, use_js=False):
"""Get the plain text content for this element.
Args:
use_js: Whether to use javascript if the element isn't
content-editable.
"""
if use_js:
# FIXME:qtwebengine what to do about use_js with WebEngine?
log.stub('with use_js=True')
return self._js_dict.get('text', '')

def set_text(self, text, *, use_js=False):
"""Set the given plain text.
Args:
use_js: Whether to use javascript if the element isn't
content-editable.
"""
# FIXME:qtwebengine what to do about use_js with WebEngine?
log.stub()

def set_inner_xml(self, xml):
"""Set the given inner XML."""
# FIXME:qtwebengine get rid of this?
log.stub()

def remove_from_document(self):
"""Remove the node from the document."""
# FIXME:qtwebengine get rid of this?
log.stub()

def set_style_property(self, name, value):
"""Set the element style."""
# FIXME:qtwebengine get rid of this?
log.stub()

def run_js_async(self, code, callback=None):
"""Run the given JS snippet async on the element."""
# FIXME:qtwebengine get rid of this?
log.stub()

def parent(self):
"""Get the parent element of this element."""
# FIXME:qtwebengine get rid of this?
log.stub()
return None

def rect_on_view(self, *, elem_geometry=None, adjust_zoom=True,
no_js=False):
"""Get the geometry of the element relative to the webview.
Uses the getClientRects() JavaScript method to obtain the collection of
rectangles containing the element and returns the first rectangle which
is large enough (larger than 1px times 1px). If all rectangles returned
by getClientRects() are too small, falls back to elem.rect_on_view().
Skipping of small rectangles is due to <a> elements containing other
elements with "display:block" style, see
https://github.com/The-Compiler/qutebrowser/issues/1298
Args:
elem_geometry: The geometry of the element, or None.
Calling QWebElement::geometry is rather expensive so
we want to avoid doing it twice.
adjust_zoom: Whether to adjust the element position based on the
current zoom level.
no_js: Fall back to the Python implementation
"""
log.stub()
return QRect()

def is_visible(self, mainframe):
"""Check if the given element is visible in the given frame."""
# FIXME:qtwebengine get rid of this?
log.stub()
return True
@@ -22,6 +22,8 @@

"""Wrapper over a QWebEngineView."""

import functools

from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPoint
from PyQt5.QtGui import QKeyEvent, QIcon
from PyQt5.QtWidgets import QApplication
@@ -30,7 +32,7 @@
# pylint: enable=no-name-in-module,import-error,useless-suppression

from qutebrowser.browser import browsertab
from qutebrowser.browser.webengine import webview
from qutebrowser.browser.webengine import webview, webengineelem
from qutebrowser.utils import usertypes, qtutils, log, javascript


@@ -411,9 +413,23 @@ def set_html(self, html, base_url):
def clear_ssl_errors(self):
log.stub()

def _find_all_elements_js_cb(self, callback, js_elems):
"""Handle found elements coming from JS and call the real callback.
Args:
callback: The callback originally passed to find_all_elements.
js_elems: The elements serialized from javascript.
"""
elems = []
for js_elem in js_elems:
elem = webengineelem.WebEngineElement(js_elem)
elems.append(elem)
callback(elems)

def find_all_elements(self, selector, callback, *, only_visible=False):
log.stub()
callback([])
js_code = javascript.assemble('webelem', 'find_all_elements', selector)
js_cb = functools.partial(self._find_all_elements_js_cb, callback)
self.run_js_async(js_code, js_cb)

def _connect_signals(self):
view = self._widget
@@ -50,10 +50,6 @@ def __eq__(self, other):
return NotImplemented
return self._elem == other._elem # pylint: disable=protected-access

def __str__(self):
self._check_vanished()
return self._elem.toPlainText()

def __getitem__(self, key):
self._check_vanished()
if key not in self:
@@ -0,0 +1,63 @@
/**
* Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
*
* This file is part of qutebrowser.
*
* qutebrowser is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* qutebrowser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
*/


document._qutebrowser_elements = [];


function _qutebrowser_serialize_elem(elem, id) {
var out = {};

var attributes = {};
for (var i = 0; i < elem.attributes.length; ++i) {
attr = elem.attributes[i];
attributes[attr.name] = attr.value;
}
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;
}


function _qutebrowser_find_all_elements(selector) {
var elems = document.querySelectorAll(selector);
var out = [];
var id = document._qutebrowser_elements.length;

for (var i = 0; i < elems.length; ++i) {
var elem = elems[i];
out.push(_qutebrowser_serialize_elem(elem, id));
document._qutebrowser_elements[id] = elem;
id++;
}

return out;
}


function _qutebrowser_get_element(id) {
return document._qutebrowser_elements[id];
}

0 comments on commit b8e2d5f

Please sign in to comment.