diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index 1d2c7d8d7..adc9e8f82 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -164,58 +164,11 @@ class WebElementWrapper(collections.abc.MutableMapping): Return: True if the element is visible, False otherwise. """ - self._check_vanished() - # CSS attributes which hide an element - hidden_attributes = { - 'visibility': 'hidden', - 'display': 'none', - } - for k, v in hidden_attributes.items(): - if self._elem.styleProperty(k, QWebElement.ComputedStyle) == v: - return False - geometry = self._elem.geometry() - if not geometry.isValid() and geometry.x() == 0: - # Most likely an invisible link - return False - # First check if the element is visible on screen - elem_rect = self.rect_on_view() - if elem_rect.isValid(): - visible_on_screen = mainframe.geometry().intersects(elem_rect) - else: - # We got an invalid rectangle (width/height 0/0 probably), but this - # can still be a valid link. - 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 = self._elem.webFrame() - elem_rect = self._elem.geometry() - framegeom = QRect(elem_frame.geometry()) - if not framegeom.isValid(): - visible_in_frame = False - elif elem_frame.parentFrame() is not None: - framegeom.moveTo(0, 0) - framegeom.translate(elem_frame.scrollPosition()) - if elem_rect.isValid(): - visible_in_frame = framegeom.intersects(elem_rect) - else: - # We got an invalid rectangle (width/height 0/0 probably), but - # this can still be a valid link. - visible_in_frame = framegeom.contains(elem_rect.topLeft()) - else: - visible_in_frame = visible_on_screen - return all([visible_on_screen, visible_in_frame]) + return is_visible(self._elem, mainframe) def rect_on_view(self): """Get the geometry of the element relative to the webview.""" - self._check_vanished() - frame = self._elem.webFrame() - rect = QRect(self._elem.geometry()) - while frame is not None: - rect.translate(frame.geometry().topLeft()) - rect.translate(frame.scrollPosition() * -1) - frame = frame.parentFrame() - return rect + return rect_on_view(self._elem) def is_writable(self): """Check whether an element is writable.""" @@ -381,3 +334,79 @@ def focus_elem(frame): """ elem = frame.findFirstElement(SELECTORS[Group.focus]) return WebElementWrapper(elem) + + +def rect_on_view(elem): + """Get the geometry of the element relative to the webview. + + We need this as a standalone function (as opposed to a WebElementWrapper + method) because we want to run is_visible before wrapping when hinting for + performance reasons. + + Args: + elem: The QWebElement to get the rect for. + """ + if elem.isNull(): + raise IsNullError("Got called on a null element!") + frame = elem.webFrame() + rect = QRect(elem.geometry()) + while frame is not None: + rect.translate(frame.geometry().topLeft()) + rect.translate(frame.scrollPosition() * -1) + frame = frame.parentFrame() + return rect + + +def is_visible(elem, mainframe): + """Check if the given element is visible in the frame. + + We need this as a standalone function (as opposed to a WebElementWrapper + method) because we want to check this before wrapping when hinting for + performance reasons. + + 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!") + # CSS attributes which hide an element + hidden_attributes = { + 'visibility': 'hidden', + 'display': 'none', + } + for k, v in hidden_attributes.items(): + if elem.styleProperty(k, QWebElement.ComputedStyle) == v: + return False + geometry = elem.geometry() + if not geometry.isValid() and geometry.x() == 0: + # Most likely an invisible link + return False + # First check if the element is visible on screen + elem_rect = rect_on_view(elem) + if elem_rect.isValid(): + visible_on_screen = mainframe.geometry().intersects(elem_rect) + else: + # We got an invalid rectangle (width/height 0/0 probably), but this + # can still be a valid link. + 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_rect = elem.geometry() + framegeom = QRect(elem_frame.geometry()) + if not framegeom.isValid(): + visible_in_frame = False + elif elem_frame.parentFrame() is not None: + framegeom.moveTo(0, 0) + framegeom.translate(elem_frame.scrollPosition()) + if elem_rect.isValid(): + visible_in_frame = framegeom.intersects(elem_rect) + else: + # We got an invalid rectangle (width/height 0/0 probably), but + # this can still be a valid link. + visible_in_frame = framegeom.contains(elem_rect.topLeft()) + else: + visible_in_frame = visible_on_screen + return all([visible_on_screen, visible_in_frame])