diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index c91cfca2b..4ba5c657f 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -1460,15 +1460,7 @@ class CommandDispatcher:
text: The new text to insert.
"""
try:
- if elem.is_content_editable():
- log.misc.debug("Filling element {} via setPlainText.".format(
- elem.debug_text()))
- elem.setPlainText(text)
- else:
- log.misc.debug("Filling element {} via javascript.".format(
- elem.debug_text()))
- text = webelem.javascript_escape(text)
- elem.evaluateJavaScript("this.value='{}'".format(text))
+ elem.set_text(text)
except webelem.IsNullError:
raise cmdexc.CommandError("Element vanished while editing!")
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index be2b1bcef..a17d03972 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -335,16 +335,16 @@ class HintManager(QObject):
def _is_hidden(self, elem):
"""Check if the element is hidden via display=none."""
- display = elem.styleProperty('display', QWebElement.InlineStyle)
+ display = elem.style_property('display', QWebElement.InlineStyle)
return display == 'none'
def _show_elem(self, elem):
"""Show a given element."""
- elem.setStyleProperty('display', 'inline !important')
+ elem.set_style_property('display', 'inline !important')
def _hide_elem(self, elem):
"""Hide a given element."""
- elem.setStyleProperty('display', 'none !important')
+ elem.set_style_property('display', 'none !important')
def _set_style_properties(self, elem, label):
"""Set the hint CSS on the element given.
@@ -373,7 +373,7 @@ class HintManager(QObject):
attrs.append(('text-transform', 'none !important'))
for k, v in attrs:
- label.setStyleProperty(k, v)
+ label.set_style_property(k, v)
self._set_style_position(elem, label)
def _set_style_position(self, elem, label):
@@ -389,8 +389,8 @@ class HintManager(QObject):
top = rect.y()
log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}' "
"(no_js: {})".format(label, left, top, elem, no_js))
- label.setStyleProperty('left', '{}px !important'.format(left))
- label.setStyleProperty('top', '{}px !important'.format(top))
+ label.set_style_property('left', '{}px !important'.format(left))
+ label.set_style_property('top', '{}px !important'.format(top))
def _draw_label(self, elem, string):
"""Draw a hint label over an element.
@@ -402,22 +402,16 @@ class HintManager(QObject):
Return:
The newly created label element
"""
- doc = elem.webFrame().documentElement()
- # It seems impossible to create an empty QWebElement for which isNull()
- # is false so we can work with it.
- # As a workaround, we use appendInside() with markup as argument, and
- # then use lastChild() to get a reference to it.
- # See: http://stackoverflow.com/q/7364852/2085149
+ doc = elem.document_element()
body = doc.findFirst('body')
- if not body.isNull():
- parent = body
- else:
+ if body is None:
parent = doc
- parent.appendInside('')
- label = webelem.WebElementWrapper(parent.lastChild())
+ else:
+ parent = body
+ label = parent.create_inside('span')
label['class'] = 'qutehint'
self._set_style_properties(elem, label)
- label.setPlainText(string)
+ label.set_text(string)
return label
def _show_url_error(self):
@@ -490,7 +484,7 @@ class HintManager(QObject):
self.mouse_event.emit(evt)
if elem.is_text_input() and elem.is_editable():
QTimer.singleShot(0, functools.partial(
- elem.webFrame().page().triggerAction,
+ elem.frame().page().triggerAction,
QWebPage.MoveToEndOfDocument))
QTimer.singleShot(0, self.stop_hinting.emit)
@@ -559,7 +553,7 @@ class HintManager(QObject):
download_manager = objreg.get('download-manager', scope='window',
window=self._win_id)
- download_manager.get(url, page=elem.webFrame().page(),
+ download_manager.get(url, page=elem.frame().page(),
prompt_download_directory=prompt)
def _call_userscript(self, elem, context):
@@ -878,7 +872,7 @@ class HintManager(QObject):
matched = string[:len(keystr)]
rest = string[len(keystr):]
match_color = config.get('colors', 'hints.fg.match')
- elem.label.setInnerXml(
+ elem.label.set_inner_xml(
'{}{}'.format(
match_color, matched, rest))
if self._is_hidden(elem.label):
@@ -913,7 +907,7 @@ class HintManager(QObject):
strings = self._hint_strings(elems)
self._context.elems = {}
for elem, string in zip(elems, strings):
- elem.label.setInnerXml(string)
+ elem.label.set_inner_xml(string)
self._context.elems[string] = elem
keyparsers = objreg.get('keyparsers', scope='window',
window=self._win_id)
@@ -1017,7 +1011,7 @@ class HintManager(QObject):
Target.spawn: self._spawn,
}
elem = self._context.elems[keystr].elem
- if elem.webFrame() is None:
+ if elem.frame() is None:
message.error(self._win_id,
"This element has no webframe.",
immediately=True)
@@ -1042,7 +1036,7 @@ class HintManager(QObject):
self.filter_hints(None)
# Undo keystring highlighting
for string, elem in self._context.elems.items():
- elem.label.setInnerXml(string)
+ elem.label.set_inner_xml(string)
handler()
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
@@ -1068,7 +1062,7 @@ class HintManager(QObject):
log.hints.debug("Contents size changed...!")
for e in self._context.all_elems:
try:
- if e.elem.webFrame() is None:
+ if e.elem.frame() is None:
# This sometimes happens for some reason...
e.label.removeFromDocument()
continue
diff --git a/qutebrowser/browser/webkit/webelem.py b/qutebrowser/browser/webkit/webelem.py
index 97210bebd..9cacada2d 100644
--- a/qutebrowser/browser/webkit/webelem.py
+++ b/qutebrowser/browser/webkit/webelem.py
@@ -28,7 +28,6 @@ Module attributes:
"""
import collections.abc
-import functools
from PyQt5.QtCore import QRect, QUrl
from PyQt5.QtWebKit import QWebElement
@@ -83,40 +82,6 @@ class WebElementWrapper(collections.abc.MutableMapping):
if elem.isNull():
raise IsNullError('{} is a null element!'.format(elem))
self._elem = elem
- for name in ['addClass', 'appendInside', 'appendOutside',
- 'attributeNS', 'classes', 'clone', 'document',
- 'encloseContentsWith', 'encloseWith',
- 'evaluateJavaScript', 'findAll', 'findFirst',
- 'firstChild', 'geometry', 'hasAttributeNS',
- 'hasAttributes', 'hasClass', 'hasFocus', 'lastChild',
- 'localName', 'namespaceUri', 'nextSibling', 'parent',
- 'prefix', 'prependInside', 'prependOutside',
- 'previousSibling', 'removeAllChildren',
- 'removeAttributeNS', 'removeClass', 'removeFromDocument',
- 'render', 'replace', 'setAttributeNS', 'setFocus',
- 'setInnerXml', 'setOuterXml', 'setPlainText',
- 'setStyleProperty', 'styleProperty', 'tagName',
- 'takeFromDocument', 'toInnerXml', 'toOuterXml',
- 'toggleClass', 'webFrame', '__eq__', '__ne__']:
- # We don't wrap some methods for which we have better alternatives:
- # - Mapping access for attributeNames/hasAttribute/setAttribute/
- # attribute/removeAttribute.
- # - isNull is checked automagically.
- # - str(...) instead of toPlainText
- # For the rest, we create a wrapper which checks if the element is
- # null.
-
- method = getattr(self._elem, name)
-
- def _wrapper(meth, *args, **kwargs):
- self._check_vanished()
- return meth(*args, **kwargs)
-
- wrapper = functools.partial(_wrapper, method)
- # We used to do functools.update_wrapper here, but for some reason
- # when using hints with many links, this accounted for nearly 50%
- # of the time when profiling, which is unacceptable.
- setattr(self, name, wrapper)
def __str__(self):
self._check_vanished()
@@ -162,6 +127,75 @@ class WebElementWrapper(collections.abc.MutableMapping):
if self._elem.isNull():
raise IsNullError('Element {} vanished!'.format(self._elem))
+ def frame(self):
+ """Get the main frame of this element."""
+ # FIXME:qtwebengine how to get rid of this?
+ self._check_vanished()
+ return self._elem.webFrame()
+
+ def geometry(self):
+ """Get the geometry for this element."""
+ self._check_vanished()
+ return self._elem.geometry()
+
+ def document_element(self):
+ """Get the document element of this element."""
+ self._check_vanished()
+ elem = self._elem.webFrame().documentElement()
+ return WebElementWrapper(elem)
+
+ def create_inside(self, tagname):
+ """Append the given element inside the current one."""
+ # It seems impossible to create an empty QWebElement for which isNull()
+ # is false so we can work with it.
+ # As a workaround, we use appendInside() with markup as argument, and
+ # then use lastChild() to get a reference to it.
+ # See: http://stackoverflow.com/q/7364852/2085149
+ self._check_vanished()
+ self._elem.appendInside('<{}>{}>'.format(tagname, tagname))
+ return WebElementWrapper(self._elem.lastChild())
+
+ def find_first(self, selector):
+ """Find the first child based on the given CSS selector."""
+ self._check_vanished()
+ elem = self._elem.findFirst(selector)
+ if elem.isNull():
+ return None
+ return WebElementWrapper(elem)
+
+ def style_property(self, name, strategy):
+ """Get the element style resolved with the given strategy."""
+ self._check_vanished()
+ return self._elem.styleProperty(name, strategy)
+
+ def set_text(self, text):
+ """Set the given plain text."""
+ self._check_vanished()
+ if self.is_content_editable():
+ log.misc.debug("Filling element {} via set_text.".format(
+ self.debug_text()))
+ self._elem.setPlainText(text)
+ else:
+ log.misc.debug("Filling element {} via javascript.".format(
+ self.debug_text()))
+ text = javascript_escape(text)
+ self._elem.evaluateJavaScript("this.value='{}'".format(text))
+
+ def set_inner_xml(self, xml):
+ """Set the given inner XML."""
+ self._check_vanished()
+ self._elem.setInnerXml(text)
+
+ def remove(self):
+ """Remove the node from the document."""
+ self._check_vanished()
+ self._elem.removeFromDocument()
+
+ def set_style_property(self, name, value):
+ """Set the element style."""
+ self._check_vanished()
+ return self._elem.setStyleProperty(name, value)
+
def is_visible(self, mainframe):
"""Check whether the element is currently visible on the screen.
@@ -404,14 +438,14 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False):
height = rect.get("height", 0)
if width > 1 and height > 1:
# fix coordinates according to zoom level
- zoom = elem.webFrame().zoomFactor()
+ zoom = elem.frame().zoomFactor()
if not config.get('ui', 'zoom-text-only') and adjust_zoom:
rect["left"] *= zoom
rect["top"] *= zoom
width *= zoom
height *= zoom
rect = QRect(rect["left"], rect["top"], width, height)
- frame = elem.webFrame()
+ frame = elem.frame()
while frame is not None:
# Translate to parent frames' position
# (scroll position is taken care of inside getClientRects)
@@ -424,7 +458,7 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False):
geometry = elem.geometry()
else:
geometry = elem_geometry
- frame = elem.webFrame()
+ frame = elem.frame()
rect = QRect(geometry)
while frame is not None:
rect.translate(frame.geometry().topLeft())
@@ -432,7 +466,7 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False):
frame = frame.parentFrame()
# We deliberately always adjust the zoom here, even with adjust_zoom=False
if elem_geometry is None:
- zoom = elem.webFrame().zoomFactor()
+ zoom = elem.frame().zoomFactor()
if not config.get('ui', 'zoom-text-only'):
rect.moveTo(rect.left() / zoom, rect.top() / zoom)
rect.setWidth(rect.width() / zoom)
@@ -476,7 +510,7 @@ def is_visible(elem, mainframe):
visible_on_screen = mainframe_geometry.contains(elem_rect.topLeft())
# Then check if it's visible in its frame if it's not in the main
# frame.
- elem_frame = elem.webFrame()
+ elem_frame = elem.frame()
framegeom = QRect(elem_frame.geometry())
if not framegeom.isValid():
visible_in_frame = False