Refactor QWebElement API, part 1
This commit is contained in:
parent
243ba02f5f
commit
c764733472
@ -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!")
|
||||
|
||||
|
@ -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('<span></span>')
|
||||
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(
|
||||
'<font color="{}">{}</font>{}'.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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user