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.
|
text: The new text to insert.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if elem.is_content_editable():
|
elem.set_text(text)
|
||||||
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))
|
|
||||||
except webelem.IsNullError:
|
except webelem.IsNullError:
|
||||||
raise cmdexc.CommandError("Element vanished while editing!")
|
raise cmdexc.CommandError("Element vanished while editing!")
|
||||||
|
|
||||||
|
@ -335,16 +335,16 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
def _is_hidden(self, elem):
|
def _is_hidden(self, elem):
|
||||||
"""Check if the element is hidden via display=none."""
|
"""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'
|
return display == 'none'
|
||||||
|
|
||||||
def _show_elem(self, elem):
|
def _show_elem(self, elem):
|
||||||
"""Show a given element."""
|
"""Show a given element."""
|
||||||
elem.setStyleProperty('display', 'inline !important')
|
elem.set_style_property('display', 'inline !important')
|
||||||
|
|
||||||
def _hide_elem(self, elem):
|
def _hide_elem(self, elem):
|
||||||
"""Hide a given element."""
|
"""Hide a given element."""
|
||||||
elem.setStyleProperty('display', 'none !important')
|
elem.set_style_property('display', 'none !important')
|
||||||
|
|
||||||
def _set_style_properties(self, elem, label):
|
def _set_style_properties(self, elem, label):
|
||||||
"""Set the hint CSS on the element given.
|
"""Set the hint CSS on the element given.
|
||||||
@ -373,7 +373,7 @@ class HintManager(QObject):
|
|||||||
attrs.append(('text-transform', 'none !important'))
|
attrs.append(('text-transform', 'none !important'))
|
||||||
|
|
||||||
for k, v in attrs:
|
for k, v in attrs:
|
||||||
label.setStyleProperty(k, v)
|
label.set_style_property(k, v)
|
||||||
self._set_style_position(elem, label)
|
self._set_style_position(elem, label)
|
||||||
|
|
||||||
def _set_style_position(self, elem, label):
|
def _set_style_position(self, elem, label):
|
||||||
@ -389,8 +389,8 @@ class HintManager(QObject):
|
|||||||
top = rect.y()
|
top = rect.y()
|
||||||
log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}' "
|
log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}' "
|
||||||
"(no_js: {})".format(label, left, top, elem, no_js))
|
"(no_js: {})".format(label, left, top, elem, no_js))
|
||||||
label.setStyleProperty('left', '{}px !important'.format(left))
|
label.set_style_property('left', '{}px !important'.format(left))
|
||||||
label.setStyleProperty('top', '{}px !important'.format(top))
|
label.set_style_property('top', '{}px !important'.format(top))
|
||||||
|
|
||||||
def _draw_label(self, elem, string):
|
def _draw_label(self, elem, string):
|
||||||
"""Draw a hint label over an element.
|
"""Draw a hint label over an element.
|
||||||
@ -402,22 +402,16 @@ class HintManager(QObject):
|
|||||||
Return:
|
Return:
|
||||||
The newly created label element
|
The newly created label element
|
||||||
"""
|
"""
|
||||||
doc = elem.webFrame().documentElement()
|
doc = elem.document_element()
|
||||||
# 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
|
|
||||||
body = doc.findFirst('body')
|
body = doc.findFirst('body')
|
||||||
if not body.isNull():
|
if body is None:
|
||||||
parent = body
|
|
||||||
else:
|
|
||||||
parent = doc
|
parent = doc
|
||||||
parent.appendInside('<span></span>')
|
else:
|
||||||
label = webelem.WebElementWrapper(parent.lastChild())
|
parent = body
|
||||||
|
label = parent.create_inside('span')
|
||||||
label['class'] = 'qutehint'
|
label['class'] = 'qutehint'
|
||||||
self._set_style_properties(elem, label)
|
self._set_style_properties(elem, label)
|
||||||
label.setPlainText(string)
|
label.set_text(string)
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def _show_url_error(self):
|
def _show_url_error(self):
|
||||||
@ -490,7 +484,7 @@ class HintManager(QObject):
|
|||||||
self.mouse_event.emit(evt)
|
self.mouse_event.emit(evt)
|
||||||
if elem.is_text_input() and elem.is_editable():
|
if elem.is_text_input() and elem.is_editable():
|
||||||
QTimer.singleShot(0, functools.partial(
|
QTimer.singleShot(0, functools.partial(
|
||||||
elem.webFrame().page().triggerAction,
|
elem.frame().page().triggerAction,
|
||||||
QWebPage.MoveToEndOfDocument))
|
QWebPage.MoveToEndOfDocument))
|
||||||
QTimer.singleShot(0, self.stop_hinting.emit)
|
QTimer.singleShot(0, self.stop_hinting.emit)
|
||||||
|
|
||||||
@ -559,7 +553,7 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
download_manager = objreg.get('download-manager', scope='window',
|
download_manager = objreg.get('download-manager', scope='window',
|
||||||
window=self._win_id)
|
window=self._win_id)
|
||||||
download_manager.get(url, page=elem.webFrame().page(),
|
download_manager.get(url, page=elem.frame().page(),
|
||||||
prompt_download_directory=prompt)
|
prompt_download_directory=prompt)
|
||||||
|
|
||||||
def _call_userscript(self, elem, context):
|
def _call_userscript(self, elem, context):
|
||||||
@ -878,7 +872,7 @@ class HintManager(QObject):
|
|||||||
matched = string[:len(keystr)]
|
matched = string[:len(keystr)]
|
||||||
rest = string[len(keystr):]
|
rest = string[len(keystr):]
|
||||||
match_color = config.get('colors', 'hints.fg.match')
|
match_color = config.get('colors', 'hints.fg.match')
|
||||||
elem.label.setInnerXml(
|
elem.label.set_inner_xml(
|
||||||
'<font color="{}">{}</font>{}'.format(
|
'<font color="{}">{}</font>{}'.format(
|
||||||
match_color, matched, rest))
|
match_color, matched, rest))
|
||||||
if self._is_hidden(elem.label):
|
if self._is_hidden(elem.label):
|
||||||
@ -913,7 +907,7 @@ class HintManager(QObject):
|
|||||||
strings = self._hint_strings(elems)
|
strings = self._hint_strings(elems)
|
||||||
self._context.elems = {}
|
self._context.elems = {}
|
||||||
for elem, string in zip(elems, strings):
|
for elem, string in zip(elems, strings):
|
||||||
elem.label.setInnerXml(string)
|
elem.label.set_inner_xml(string)
|
||||||
self._context.elems[string] = elem
|
self._context.elems[string] = elem
|
||||||
keyparsers = objreg.get('keyparsers', scope='window',
|
keyparsers = objreg.get('keyparsers', scope='window',
|
||||||
window=self._win_id)
|
window=self._win_id)
|
||||||
@ -1017,7 +1011,7 @@ class HintManager(QObject):
|
|||||||
Target.spawn: self._spawn,
|
Target.spawn: self._spawn,
|
||||||
}
|
}
|
||||||
elem = self._context.elems[keystr].elem
|
elem = self._context.elems[keystr].elem
|
||||||
if elem.webFrame() is None:
|
if elem.frame() is None:
|
||||||
message.error(self._win_id,
|
message.error(self._win_id,
|
||||||
"This element has no webframe.",
|
"This element has no webframe.",
|
||||||
immediately=True)
|
immediately=True)
|
||||||
@ -1042,7 +1036,7 @@ class HintManager(QObject):
|
|||||||
self.filter_hints(None)
|
self.filter_hints(None)
|
||||||
# Undo keystring highlighting
|
# Undo keystring highlighting
|
||||||
for string, elem in self._context.elems.items():
|
for string, elem in self._context.elems.items():
|
||||||
elem.label.setInnerXml(string)
|
elem.label.set_inner_xml(string)
|
||||||
handler()
|
handler()
|
||||||
|
|
||||||
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
||||||
@ -1068,7 +1062,7 @@ class HintManager(QObject):
|
|||||||
log.hints.debug("Contents size changed...!")
|
log.hints.debug("Contents size changed...!")
|
||||||
for e in self._context.all_elems:
|
for e in self._context.all_elems:
|
||||||
try:
|
try:
|
||||||
if e.elem.webFrame() is None:
|
if e.elem.frame() is None:
|
||||||
# This sometimes happens for some reason...
|
# This sometimes happens for some reason...
|
||||||
e.label.removeFromDocument()
|
e.label.removeFromDocument()
|
||||||
continue
|
continue
|
||||||
|
@ -28,7 +28,6 @@ Module attributes:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import collections.abc
|
import collections.abc
|
||||||
import functools
|
|
||||||
|
|
||||||
from PyQt5.QtCore import QRect, QUrl
|
from PyQt5.QtCore import QRect, QUrl
|
||||||
from PyQt5.QtWebKit import QWebElement
|
from PyQt5.QtWebKit import QWebElement
|
||||||
@ -83,40 +82,6 @@ class WebElementWrapper(collections.abc.MutableMapping):
|
|||||||
if elem.isNull():
|
if elem.isNull():
|
||||||
raise IsNullError('{} is a null element!'.format(elem))
|
raise IsNullError('{} is a null element!'.format(elem))
|
||||||
self._elem = 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):
|
def __str__(self):
|
||||||
self._check_vanished()
|
self._check_vanished()
|
||||||
@ -162,6 +127,75 @@ class WebElementWrapper(collections.abc.MutableMapping):
|
|||||||
if self._elem.isNull():
|
if self._elem.isNull():
|
||||||
raise IsNullError('Element {} vanished!'.format(self._elem))
|
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):
|
def is_visible(self, mainframe):
|
||||||
"""Check whether the element is currently visible on the screen.
|
"""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)
|
height = rect.get("height", 0)
|
||||||
if width > 1 and height > 1:
|
if width > 1 and height > 1:
|
||||||
# fix coordinates according to zoom level
|
# 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:
|
if not config.get('ui', 'zoom-text-only') and adjust_zoom:
|
||||||
rect["left"] *= zoom
|
rect["left"] *= zoom
|
||||||
rect["top"] *= zoom
|
rect["top"] *= zoom
|
||||||
width *= zoom
|
width *= zoom
|
||||||
height *= zoom
|
height *= zoom
|
||||||
rect = QRect(rect["left"], rect["top"], width, height)
|
rect = QRect(rect["left"], rect["top"], width, height)
|
||||||
frame = elem.webFrame()
|
frame = elem.frame()
|
||||||
while frame is not None:
|
while frame is not None:
|
||||||
# Translate to parent frames' position
|
# Translate to parent frames' position
|
||||||
# (scroll position is taken care of inside getClientRects)
|
# (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()
|
geometry = elem.geometry()
|
||||||
else:
|
else:
|
||||||
geometry = elem_geometry
|
geometry = elem_geometry
|
||||||
frame = elem.webFrame()
|
frame = elem.frame()
|
||||||
rect = QRect(geometry)
|
rect = QRect(geometry)
|
||||||
while frame is not None:
|
while frame is not None:
|
||||||
rect.translate(frame.geometry().topLeft())
|
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()
|
frame = frame.parentFrame()
|
||||||
# We deliberately always adjust the zoom here, even with adjust_zoom=False
|
# We deliberately always adjust the zoom here, even with adjust_zoom=False
|
||||||
if elem_geometry is None:
|
if elem_geometry is None:
|
||||||
zoom = elem.webFrame().zoomFactor()
|
zoom = elem.frame().zoomFactor()
|
||||||
if not config.get('ui', 'zoom-text-only'):
|
if not config.get('ui', 'zoom-text-only'):
|
||||||
rect.moveTo(rect.left() / zoom, rect.top() / zoom)
|
rect.moveTo(rect.left() / zoom, rect.top() / zoom)
|
||||||
rect.setWidth(rect.width() / zoom)
|
rect.setWidth(rect.width() / zoom)
|
||||||
@ -476,7 +510,7 @@ def is_visible(elem, mainframe):
|
|||||||
visible_on_screen = mainframe_geometry.contains(elem_rect.topLeft())
|
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
|
# Then check if it's visible in its frame if it's not in the main
|
||||||
# frame.
|
# frame.
|
||||||
elem_frame = elem.webFrame()
|
elem_frame = elem.frame()
|
||||||
framegeom = QRect(elem_frame.geometry())
|
framegeom = QRect(elem_frame.geometry())
|
||||||
if not framegeom.isValid():
|
if not framegeom.isValid():
|
||||||
visible_in_frame = False
|
visible_in_frame = False
|
||||||
|
Loading…
Reference in New Issue
Block a user