Coverage for qutebrowser/browser/webelem.py : 22%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org> # # This file is part of qutebrowser. # # qutebrowser is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # qutebrowser is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
Module attributes: Group: Enum for different kinds of groups. SELECTORS: CSS selectors for different groups of elements. """
Group.all: ('a, area, textarea, select, input:not([type=hidden]), button, ' 'frame, iframe, link, [onclick], [onmousedown], [role=link], ' '[role=option], [role=button], img, ' # Angular 1 selectors '[ng-click], [ngClick], [data-ng-click], [x-ng-click]'), Group.links: 'a[href], area[href], link[href], [role=link][href]', Group.images: 'img', Group.url: '[src], [href]', Group.inputs: ('input[type=text], input[type=email], input[type=url], ' 'input[type=tel], input[type=number], ' 'input[type=password], input[type=search], ' 'input:not([type]), textarea'), }
"""Base class for WebElement errors."""
"""Raised when a webelement's parent has vanished."""
"""A wrapper around QtWebKit/QtWebEngine web element.
Attributes: tab: The tab associated with this element. """
self._tab = tab
raise NotImplementedError
raise NotImplementedError
raise NotImplementedError
raise NotImplementedError
raise NotImplementedError
raise NotImplementedError
raise NotImplementedError
def __repr__(self): try: html = utils.compact_text(self.outer_xml(), 500) except Error: html = None return utils.get_repr(self, html=html)
"""Check if this element has a valid frame attached.""" raise NotImplementedError
"""Get the geometry for this element.""" raise NotImplementedError
"""Get a list of classes assigned to this element.""" raise NotImplementedError
"""Get the tag name of this element.
The returned name will always be lower-case. """ raise NotImplementedError
"""Get the full HTML representation of this element.""" raise NotImplementedError
"""Get the value attribute for this element, or None.""" raise NotImplementedError
"""Set the element value.""" raise NotImplementedError
"""Insert the given text into the element.""" raise NotImplementedError
"""Get the geometry of the element relative to the webview.
Uses the getClientRects() JavaScript method to obtain the collection of rectangles containing the element and returns the first rectangle which is large enough (larger than 1px times 1px). If all rectangles returned by getClientRects() are too small, falls back to elem.rect_on_view().
Skipping of small rectangles is due to <a> elements containing other elements with "display:block" style, see https://github.com/qutebrowser/qutebrowser/issues/1298
Args: elem_geometry: The geometry of the element, or None. Calling QWebElement::geometry is rather expensive so we want to avoid doing it twice. no_js: Fall back to the Python implementation """ raise NotImplementedError
"""Check whether an element is writable.""" return not ('disabled' in self or 'readonly' in self)
"""Check if an element has a contenteditable attribute.
Args: elem: The QWebElement to check.
Return: True if the element has a contenteditable attribute, False otherwise. """ try: return self['contenteditable'].lower() not in ['false', 'inherit'] except KeyError: return False
"""Check if an object-element is editable.""" if 'type' not in self: log.webelem.debug("<object> without type clicked...") return False objtype = self['type'].lower() if objtype.startswith('application/') or 'classid' in self: # Let's hope flash/java stuff has an application/* mimetype OR # at least a classid attribute. Oh, and let's hope images/... # DON'T have a classid attribute. HTML sucks. log.webelem.debug("<object type='{}'> clicked.".format(objtype)) return config.val.input.insert_mode.plugins else: # Image/Audio/... return False
"""Check if an input-element is editable.
Return: True if the element is editable, False otherwise. """ try: objtype = self['type'].lower() except KeyError: return self.is_writable() else: if objtype in ['text', 'email', 'url', 'tel', 'number', 'password', 'search']: return self.is_writable() else: return False
"""Check if an element is editable based on its classes.
Return: True if the element is editable, False otherwise. """ # Beginnings of div-classes which are actually some kind of editor. classes = { 'div': ['CodeMirror', # Javascript editor over a textarea 'kix-', # Google Docs editor 'ace_'], # http://ace.c9.io/ 'pre': ['CodeMirror'], } relevant_classes = classes[self.tag_name()] for klass in self.classes(): if any(klass.strip().startswith(e) for e in relevant_classes): return True return False
"""Check whether we should switch to insert mode for this element.
Args: strict: Whether to do stricter checking so only fields where we can get the value match, for use with the :editor command.
Return: True if we should switch to insert mode, False otherwise. """ roles = ('combobox', 'textbox') log.webelem.debug("Checking if element is editable: {}".format( repr(self))) tag = self.tag_name() if self.is_content_editable() and self.is_writable(): return True elif self.get('role', None) in roles and self.is_writable(): return True elif tag == 'input': return self._is_editable_input() elif tag == 'textarea': return self.is_writable() elif tag in ['embed', 'applet']: # Flash/Java/... return config.val.input.insert_mode.plugins and not strict elif tag == 'object': return self._is_editable_object() and not strict elif tag in ['div', 'pre']: return self._is_editable_classes() and not strict return False
"""Check if this element is some kind of text box.""" roles = ('combobox', 'textbox') tag = self.tag_name() return self.get('role', None) in roles or tag in ['input', 'textarea']
"""Remove target from link.""" raise NotImplementedError
"""Resolve the URL in the element's src/href attribute.
Args: baseurl: The URL to base relative URLs on as QUrl.
Return: A QUrl with the absolute URL, or None. """ if baseurl.isRelative(): raise ValueError("Need an absolute base URL!")
for attr in ['href', 'src']: if attr in self: text = self[attr].strip() break else: return None
url = QUrl(text) if not url.isValid(): return None if url.isRelative(): url = baseurl.resolved(url) qtutils.ensure_valid(url) return url
"""Return True if this AbstractWebElement is a link.""" href_tags = ['a', 'area', 'link'] return self.tag_name() in href_tags and 'href' in self
"""Get the position to click/hover.""" # Click the center of the largest square fitting into the top/left # corner of the rectangle, this will help if part of the <a> element # is hidden behind other elements # https://github.com/qutebrowser/qutebrowser/issues/1005 rect = self.rect_on_view() if rect.width() > rect.height(): rect.setWidth(rect.height()) else: rect.setHeight(rect.width()) pos = rect.center() if pos.x() < 0 or pos.y() < 0: raise Error("Element position is out of view!") return pos
"""Move cursor to end after clicking.""" raise NotImplementedError
"""Send a fake click event to the element.""" pos = self._mouse_pos()
log.webelem.debug("Sending fake click to {!r} at position {} with " "target {}".format(self, pos, click_target))
modifiers = { usertypes.ClickTarget.normal: Qt.NoModifier, usertypes.ClickTarget.window: Qt.AltModifier | Qt.ShiftModifier, usertypes.ClickTarget.tab: Qt.ControlModifier, usertypes.ClickTarget.tab_bg: Qt.ControlModifier, } if config.val.tabs.background: modifiers[usertypes.ClickTarget.tab] |= Qt.ShiftModifier else: modifiers[usertypes.ClickTarget.tab_bg] |= Qt.ShiftModifier
events = [ QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton, Qt.NoModifier), QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton, Qt.LeftButton, modifiers[click_target]), QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton, Qt.NoButton, modifiers[click_target]), ]
for evt in events: self._tab.send_event(evt)
QTimer.singleShot(0, self._move_text_cursor)
"""Fake a click on an editable input field.""" raise NotImplementedError
"""Fake a click by using the JS .click() method.""" raise NotImplementedError
"""Fake a click on an element with a href by opening the link.""" baseurl = self._tab.url() url = self.resolve_url(baseurl) if url is None: self._click_fake_event(click_target) return
tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._tab.win_id)
if click_target in [usertypes.ClickTarget.tab, usertypes.ClickTarget.tab_bg]: background = click_target == usertypes.ClickTarget.tab_bg tabbed_browser.tabopen(url, background=background) elif click_target == usertypes.ClickTarget.window: window = mainwindow.MainWindow(private=tabbed_browser.private) window.show() window.tabbed_browser.tabopen(url) else: raise ValueError("Unknown ClickTarget {}".format(click_target))
"""Simulate a click on the element.
Args: click_target: A usertypes.ClickTarget member, what kind of click to simulate. force_event: Force generating a fake mouse event. """ log.webelem.debug("Clicking {!r} with click_target {}, force_event {}" .format(self, click_target, force_event))
if force_event: self._click_fake_event(click_target) return
if click_target == usertypes.ClickTarget.normal: if self.is_link(): log.webelem.debug("Clicking via JS click()") self._click_js(click_target) elif self.is_editable(strict=True): log.webelem.debug("Clicking via JS focus()") self._click_editable(click_target) modeman.enter(self._tab.win_id, usertypes.KeyMode.insert, 'clicking input') else: self._click_fake_event(click_target) elif click_target in [usertypes.ClickTarget.tab, usertypes.ClickTarget.tab_bg, usertypes.ClickTarget.window]: if self.is_link(): self._click_href(click_target) else: self._click_fake_event(click_target) else: raise ValueError("Unknown ClickTarget {}".format(click_target))
"""Simulate a mouse hover over the element.""" pos = self._mouse_pos() event = QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton, Qt.NoModifier) self._tab.send_event(event) |