Refactor WebElement API, part 2
Now we don't get a crash, but not any hints either...
This commit is contained in:
parent
c5a91004f5
commit
193c755637
@ -633,6 +633,10 @@ class AbstractTab(QWidget):
|
||||
def set_html(self, html, base_url):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_all_elements(self, selector):
|
||||
"""Find all HTML elements matching a given selector."""
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode),
|
||||
|
@ -503,21 +503,13 @@ class CommandDispatcher:
|
||||
if widget.backend == usertypes.Backend.QtWebEngine:
|
||||
raise cmdexc.CommandError(":navigate prev/next is not "
|
||||
"supported yet with QtWebEngine")
|
||||
page = widget._widget.page() # pylint: disable=protected-access
|
||||
frame = page.currentFrame()
|
||||
if frame is None:
|
||||
raise cmdexc.CommandError("No frame focused!")
|
||||
else:
|
||||
frame = None
|
||||
|
||||
hintmanager = objreg.get('hintmanager', scope='tab', tab='current')
|
||||
if where == 'prev':
|
||||
assert frame is not None
|
||||
hintmanager.follow_prevnext(frame, url, prev=True, tab=tab,
|
||||
hintmanager.follow_prevnext(widget, url, prev=True, tab=tab,
|
||||
background=bg, window=window)
|
||||
elif where == 'next':
|
||||
assert frame is not None
|
||||
hintmanager.follow_prevnext(frame, url, prev=False, tab=tab,
|
||||
hintmanager.follow_prevnext(widget, url, prev=False, tab=tab,
|
||||
background=bg, window=window)
|
||||
elif where == 'up':
|
||||
self._navigate_up(url, tab, bg, window)
|
||||
|
@ -80,7 +80,6 @@ class HintContext:
|
||||
to_follow: The link to follow when enter is pressed.
|
||||
args: Custom arguments for userscript/spawn
|
||||
rapid: Whether to do rapid hinting.
|
||||
mainframe: The main QWebFrame where we started hinting in.
|
||||
tab: The WebTab object we started hinting in.
|
||||
group: The group of web elements to hint.
|
||||
"""
|
||||
@ -94,7 +93,6 @@ class HintContext:
|
||||
self.rapid = False
|
||||
self.frames = []
|
||||
self.args = []
|
||||
self.mainframe = None
|
||||
self.tab = None
|
||||
self.group = None
|
||||
|
||||
@ -173,7 +171,7 @@ class HintManager(QObject):
|
||||
"""Clean up after hinting."""
|
||||
for elem in self._context.all_elems:
|
||||
try:
|
||||
elem.label.removeFromDocument()
|
||||
elem.label.remove_from_document()
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
text = self._get_text()
|
||||
@ -384,7 +382,7 @@ class HintManager(QObject):
|
||||
The newly created label element
|
||||
"""
|
||||
doc = elem.document_element()
|
||||
body = doc.findFirst('body')
|
||||
body = doc.find_first('body')
|
||||
if body is None:
|
||||
parent = doc
|
||||
else:
|
||||
@ -598,13 +596,12 @@ class HintManager(QObject):
|
||||
qtutils.ensure_valid(url)
|
||||
return url
|
||||
|
||||
def _find_prevnext(self, frame, prev=False):
|
||||
def _find_prevnext(self, tab, prev=False):
|
||||
"""Find a prev/next element in frame."""
|
||||
# First check for <link rel="prev(ious)|next">
|
||||
elems = frame.findAllElements(webelem.SELECTORS[webelem.Group.links])
|
||||
elems = tab.find_all_elements(webelem.SELECTORS[webelem.Group.links])
|
||||
rel_values = ('prev', 'previous') if prev else ('next')
|
||||
for e in elems:
|
||||
e = webelem.WebElementWrapper(e)
|
||||
try:
|
||||
rel_attr = e['rel']
|
||||
except KeyError:
|
||||
@ -614,9 +611,8 @@ class HintManager(QObject):
|
||||
e.debug_text(), rel_attr))
|
||||
return e
|
||||
# Then check for regular links/buttons.
|
||||
elems = frame.findAllElements(
|
||||
elems = tab.find_all_elements(
|
||||
webelem.SELECTORS[webelem.Group.prevnext])
|
||||
elems = [webelem.WebElementWrapper(e) for e in elems]
|
||||
filterfunc = webelem.FILTERS[webelem.Group.prevnext]
|
||||
elems = [e for e in elems if filterfunc(e)]
|
||||
|
||||
@ -659,14 +655,9 @@ class HintManager(QObject):
|
||||
|
||||
def _init_elements(self):
|
||||
"""Initialize the elements and labels based on the context set."""
|
||||
elems = []
|
||||
for f in self._context.frames:
|
||||
elems += f.findAllElements(webelem.SELECTORS[self._context.group])
|
||||
elems = [e for e in elems
|
||||
if webelem.is_visible(e, self._context.mainframe)]
|
||||
# We wrap the elements late for performance reasons, as wrapping 1000s
|
||||
# of elements (with ~50 methods each) just takes too much time...
|
||||
elems = [webelem.WebElementWrapper(e) for e in elems]
|
||||
selector = webelem.SELECTORS[self._context.group]
|
||||
elems = self._context.tab.find_all_elements(selector)
|
||||
elems = [e for e in elems if e.is_visible()]
|
||||
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
|
||||
elems = [e for e in elems if filterfunc(e)]
|
||||
if not elems:
|
||||
@ -693,12 +684,12 @@ class HintManager(QObject):
|
||||
# Do multi-word matching
|
||||
return all(word in elemstr for word in filterstr.split())
|
||||
|
||||
def follow_prevnext(self, frame, baseurl, prev=False, tab=False,
|
||||
def follow_prevnext(self, browsertab, baseurl, prev=False, tab=False,
|
||||
background=False, window=False):
|
||||
"""Click a "previous"/"next" element on the page.
|
||||
|
||||
Args:
|
||||
frame: The frame where the element is in.
|
||||
browsertab: The WebKitTab/WebEngineTab of the page.
|
||||
baseurl: The base URL of the current tab.
|
||||
prev: True to open a "previous" link, False to open a "next" link.
|
||||
tab: True to open in a new tab, False for the current tab.
|
||||
@ -706,7 +697,7 @@ class HintManager(QObject):
|
||||
window: True to open in a new window, False for the current one.
|
||||
"""
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
elem = self._find_prevnext(frame, prev)
|
||||
elem = self._find_prevnext(browsertab, prev)
|
||||
if elem is None:
|
||||
raise cmdexc.CommandError("No {} links found!".format(
|
||||
"prev" if prev else "forward"))
|
||||
@ -789,11 +780,6 @@ class HintManager(QObject):
|
||||
tab = tabbed_browser.currentWidget()
|
||||
if tab is None:
|
||||
raise cmdexc.CommandError("No WebView available yet!")
|
||||
# FIXME:qtwebengine have a proper API for this
|
||||
page = tab._widget.page() # pylint: disable=protected-access
|
||||
mainframe = page.mainFrame()
|
||||
if mainframe is None:
|
||||
raise cmdexc.CommandError("No frame focused!")
|
||||
mode_manager = objreg.get('mode-manager', scope='window',
|
||||
window=self._win_id)
|
||||
if mode_manager.mode == usertypes.KeyMode.hint:
|
||||
@ -821,15 +807,13 @@ class HintManager(QObject):
|
||||
self._context.baseurl = tabbed_browser.current_url()
|
||||
except qtutils.QtValueError:
|
||||
raise cmdexc.CommandError("No URL set for this page yet!")
|
||||
self._context.frames = webelem.get_child_frames(mainframe)
|
||||
self._context.tab = tab
|
||||
self._context.args = args
|
||||
self._context.mainframe = mainframe
|
||||
self._context.group = group
|
||||
self._init_elements()
|
||||
message_bridge = objreg.get('message-bridge', scope='window',
|
||||
window=self._win_id)
|
||||
message_bridge.set_text(self._get_text())
|
||||
self._connect_frame_signals()
|
||||
modeman.enter(self._win_id, usertypes.KeyMode.hint,
|
||||
'HintManager.start')
|
||||
|
||||
@ -1034,7 +1018,7 @@ class HintManager(QObject):
|
||||
try:
|
||||
if e.elem.frame() is None:
|
||||
# This sometimes happens for some reason...
|
||||
e.label.removeFromDocument()
|
||||
e.label.remove_from_document()
|
||||
continue
|
||||
self._set_style_position(e.elem, e.label)
|
||||
except webelem.IsNullError:
|
||||
|
@ -184,9 +184,9 @@ class WebElementWrapper(collections.abc.MutableMapping):
|
||||
def set_inner_xml(self, xml):
|
||||
"""Set the given inner XML."""
|
||||
self._check_vanished()
|
||||
self._elem.setInnerXml(text)
|
||||
self._elem.setInnerXml(xml)
|
||||
|
||||
def remove(self):
|
||||
def remove_from_document(self):
|
||||
"""Remove the node from the document."""
|
||||
self._check_vanished()
|
||||
self._elem.removeFromDocument()
|
||||
@ -196,16 +196,13 @@ class WebElementWrapper(collections.abc.MutableMapping):
|
||||
self._check_vanished()
|
||||
return self._elem.setStyleProperty(name, value)
|
||||
|
||||
def is_visible(self, mainframe):
|
||||
def is_visible(self):
|
||||
"""Check whether the element is currently visible on the screen.
|
||||
|
||||
Args:
|
||||
mainframe: The main QWebFrame.
|
||||
|
||||
Return:
|
||||
True if the element is visible, False otherwise.
|
||||
"""
|
||||
return is_visible(self._elem, mainframe)
|
||||
return is_visible(self._elem)
|
||||
|
||||
def rect_on_view(self, **kwargs):
|
||||
"""Get the geometry of the element relative to the webview."""
|
||||
@ -438,14 +435,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.frame().zoomFactor()
|
||||
zoom = elem.webFrame().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.frame()
|
||||
frame = elem.webFrame()
|
||||
while frame is not None:
|
||||
# Translate to parent frames' position
|
||||
# (scroll position is taken care of inside getClientRects)
|
||||
@ -458,7 +455,7 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False):
|
||||
geometry = elem.geometry()
|
||||
else:
|
||||
geometry = elem_geometry
|
||||
frame = elem.frame()
|
||||
frame = elem.webFrame()
|
||||
rect = QRect(geometry)
|
||||
while frame is not None:
|
||||
rect.translate(frame.geometry().topLeft())
|
||||
@ -466,7 +463,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.frame().zoomFactor()
|
||||
zoom = elem.webFrame().zoomFactor()
|
||||
if not config.get('ui', 'zoom-text-only'):
|
||||
rect.moveTo(rect.left() / zoom, rect.top() / zoom)
|
||||
rect.setWidth(rect.width() / zoom)
|
||||
@ -474,7 +471,7 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False):
|
||||
return rect
|
||||
|
||||
|
||||
def is_visible(elem, mainframe):
|
||||
def is_visible(elem):
|
||||
"""Check if the given element is visible in the frame.
|
||||
|
||||
We need this as a standalone function (as opposed to a WebElementWrapper
|
||||
@ -483,10 +480,10 @@ def is_visible(elem, mainframe):
|
||||
|
||||
Args:
|
||||
elem: The QWebElement to check.
|
||||
mainframe: The QWebFrame in which the element should be visible.
|
||||
"""
|
||||
if elem.isNull():
|
||||
raise IsNullError("Got called on a null element!")
|
||||
mainframe = elem.webFrame()
|
||||
# CSS attributes which hide an element
|
||||
hidden_attributes = {
|
||||
'visibility': 'hidden',
|
||||
@ -510,7 +507,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.frame()
|
||||
elem_frame = elem.webFrame()
|
||||
framegeom = QRect(elem_frame.geometry())
|
||||
if not framegeom.isValid():
|
||||
visible_in_frame = False
|
||||
|
@ -30,7 +30,7 @@ from PyQt5.QtWebKit import QWebSettings
|
||||
from PyQt5.QtPrintSupport import QPrinter
|
||||
|
||||
from qutebrowser.browser import browsertab
|
||||
from qutebrowser.browser.webkit import webview, tabhistory
|
||||
from qutebrowser.browser.webkit import webview, tabhistory, webelem
|
||||
from qutebrowser.utils import qtutils, objreg, usertypes, utils
|
||||
|
||||
|
||||
@ -557,6 +557,18 @@ class WebKitTab(browsertab.AbstractTab):
|
||||
def set_html(self, html, base_url):
|
||||
self._widget.setHtml(html, base_url)
|
||||
|
||||
def find_all_elements(self, selector):
|
||||
mainframe = self._widget.page().mainFrame()
|
||||
if mainframe is None:
|
||||
raise WebTabError("No frame focused!")
|
||||
|
||||
elems = []
|
||||
frames = webelem.get_child_frames(mainframe)
|
||||
for f in frames:
|
||||
for elem in f.findAllElements(selector):
|
||||
elems.append(webelem.WebElementWrapper(elem))
|
||||
return elems
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_frame_load_finished(self):
|
||||
"""Make sure we emit an appropriate status when loading finished.
|
||||
|
Loading…
Reference in New Issue
Block a user