def test_selectors(self, webframe, group, val, matching):
     webframe.setHtml('<html><body>{}</body></html>'.format(val))
     # Make sure setting HTML succeeded and there's a new element
     assert len(webframe.findAllElements('*')) == 3
     elems = webframe.findAllElements(webelem.SELECTORS[group])
     elems = [webkitelem.WebKitElement(e, tab=None) for e in elems]
     assert bool(elems) == matching
Beispiel #2
0
    def find_at_pos(self, pos, callback):
        assert pos.x() >= 0
        assert pos.y() >= 0
        frame = self._widget.page().frameAt(pos)
        if frame is None:
            # This happens when we click inside the webview, but not actually
            # on the QWebPage - for example when clicking the scrollbar
            # sometimes.
            log.webview.debug("Hit test at {} but frame is None!".format(pos))
            callback(None)
            return

        # You'd think we have to subtract frame.geometry().topLeft() from the
        # position, but it seems QWebFrame::hitTestContent wants a position
        # relative to the QWebView, not to the frame. This makes no sense to
        # me, but it works this way.
        hitresult = frame.hitTestContent(pos)
        if hitresult.isNull():
            # For some reason, the whole hit result can be null sometimes (e.g.
            # on doodle menu links).
            log.webview.debug("Hit test result is null!")
            callback(None)
            return

        try:
            elem = webkitelem.WebKitElement(hitresult.element(), tab=self._tab)
        except webkitelem.IsNullError:
            # For some reason, the hit result element can be a null element
            # sometimes (e.g. when clicking the timetable fields on
            # http://www.sbb.ch/ ).
            log.webview.debug("Hit test result element is null!")
            callback(None)
            return

        callback(elem)
Beispiel #3
0
 def test_selectors(self, webframe, group, val, matching, config_stub):
     webframe.setHtml('<html><body>{}</body></html>'.format(val))
     # Make sure setting HTML succeeded and there's a new element
     assert len(webframe.findAllElements('*')) == 3
     selector = ','.join(config_stub.val.hints.selectors[group])
     elems = webframe.findAllElements(selector)
     elems = [webkitelem.WebKitElement(e, tab=None) for e in elems]
     assert bool(elems) == matching
Beispiel #4
0
    def find_focused(self, callback):
        frame = self._widget.page().currentFrame()
        if frame is None:
            callback(None)
            return

        elem = frame.findFirstElement('*:focus')
        if elem.isNull():
            callback(None)
        else:
            callback(webkitelem.WebKitElement(elem, tab=self._tab))
Beispiel #5
0
    def find_css(self, selector, callback, *, only_visible=False):
        mainframe = self._widget.page().mainFrame()
        if mainframe is None:
            raise browsertab.WebTabError("No frame focused!")

        elems = []
        frames = webkitelem.get_child_frames(mainframe)
        for f in frames:
            for elem in f.findAllElements(selector):
                elems.append(webkitelem.WebKitElement(elem, tab=self._tab))

        if only_visible:
            elems = [e for e in elems if e.is_visible(mainframe)]

        callback(elems)
Beispiel #6
0
    def find_css(self, selector, callback, error_cb, *, only_visible=False):
        utils.unused(error_cb)
        mainframe = self._widget.page().mainFrame()
        if mainframe is None:
            raise browsertab.WebTabError("No frame focused!")

        elems = []
        frames = webkitelem.get_child_frames(mainframe)
        for f in frames:
            for elem in f.findAllElements(selector):
                elems.append(webkitelem.WebKitElement(elem, tab=self._tab))

        if only_visible:
            # pylint: disable=protected-access
            elems = [e for e in elems if e._is_visible(mainframe)]
            # pylint: enable=protected-access

        callback(elems)
Beispiel #7
0
def get_webelem(geometry=None,
                frame=None,
                *,
                null=False,
                style=None,
                attributes=None,
                tagname=None,
                classes=None,
                parent=None,
                js_rect_return=None,
                zoom_text_only=False):
    """Factory for WebKitElement objects based on a mock.

    Args:
        geometry: The geometry of the QWebElement as QRect.
        frame: The QWebFrame the element is in.
        null: Whether the element is null or not.
        style: A dict with the styleAttributes of the element.
        attributes: Boolean HTML attributes to be added.
        tagname: The tag name.
        classes: HTML classes to be added.
        js_rect_return: If None, what evaluateJavaScript returns is based on
                        geometry. If set, the return value of
                        evaluateJavaScript.
        zoom_text_only: Whether zoom.text_only is set in the config
    """
    elem = mock.Mock()
    elem.isNull.return_value = null
    elem.geometry.return_value = geometry
    elem.webFrame.return_value = frame
    elem.tagName.return_value = tagname
    elem.toOuterXml.return_value = '<fakeelem/>'
    elem.toPlainText.return_value = 'text'
    elem.parent.return_value = parent

    if geometry is not None:
        if frame is None:
            scroll_x = 0
            scroll_y = 0
        else:
            scroll_x = frame.scrollPosition().x()
            scroll_y = frame.scrollPosition().y()

        if js_rect_return is None:
            if frame is None or zoom_text_only:
                zoom = 1.0
            else:
                zoom = frame.zoomFactor()

            elem.evaluateJavaScript.return_value = {
                "length": 1,
                "0": {
                    "left": (geometry.left() - scroll_x) / zoom,
                    "top": (geometry.top() - scroll_y) / zoom,
                    "right": (geometry.right() - scroll_x) / zoom,
                    "bottom": (geometry.bottom() - scroll_y) / zoom,
                    "width": geometry.width() / zoom,
                    "height": geometry.height() / zoom,
                }
            }
        else:
            elem.evaluateJavaScript.return_value = js_rect_return

    attribute_dict = {}
    if attributes is None:
        pass
    elif not isinstance(attributes, collections.abc.Mapping):
        attribute_dict.update({e: None for e in attributes})
    else:
        attribute_dict.update(attributes)

    elem.hasAttribute.side_effect = lambda k: k in attribute_dict
    elem.attribute.side_effect = lambda k: attribute_dict.get(k, '')
    elem.setAttribute.side_effect = (
        lambda k, v: operator.setitem(attribute_dict, k, v))
    elem.removeAttribute.side_effect = attribute_dict.pop
    elem.attributeNames.return_value = list(attribute_dict)

    if classes is not None:
        elem.classes.return_value = classes.split(' ')
    else:
        elem.classes.return_value = []

    style_dict = {
        'visibility': '',
        'display': '',
        'foo': 'bar',
        'opacity': '100'
    }
    if style is not None:
        style_dict.update(style)

    def _style_property(name, strategy):
        """Helper function to act as styleProperty method."""
        if strategy != QWebElement.ComputedStyle:
            raise ValueError("styleProperty called with strategy != "
                             "ComputedStyle ({})!".format(strategy))
        return style_dict[name]

    elem.styleProperty.side_effect = _style_property
    tab = mock.Mock(autospec=browsertab.AbstractTab)
    tab.is_deleted.return_value = False
    wrapped = webkitelem.WebKitElement(elem, tab=tab)
    return wrapped
Beispiel #8
0
 def test_eq(self):
     one = get_webelem()
     two = webkitelem.WebKitElement(one._elem, tab=None)
     assert one == two
Beispiel #9
0
 def test_double_wrap(self, elem):
     """Test wrapping a WebKitElement."""
     with pytest.raises(TypeError, match="Trying to wrap a wrapper!"):
         webkitelem.WebKitElement(elem, tab=None)
Beispiel #10
0
    def run(self):
        """Download and save the page.

        The object must not be reused, you should create a new one if
        you want to download another page.
        """
        if self._used:
            raise ValueError("Downloader already used")
        self._used = True
        web_url = self.tab.url()

        # FIXME:qtwebengine have a proper API for this
        page = self.tab._widget.page()  # pylint: disable=protected-access
        web_frame = page.mainFrame()

        self.writer = MHTMLWriter(
            web_frame.toHtml().encode('utf-8'),
            content_location=urlutils.encoded_url(web_url),
            # I've found no way of getting the content type of a QWebView, but
            # since we're using .toHtml, it's probably safe to say that the
            # content-type is HTML
            content_type='text/html; charset="UTF-8"',
        )
        # Currently only downloading <link> (stylesheets), <script>
        # (javascript) and <img> (image) elements.
        elements = web_frame.findAllElements('link, script, img')

        for element in elements:
            element = webkitelem.WebKitElement(element, tab=self.tab)
            # Websites are free to set whatever rel=... attribute they want.
            # We just care about stylesheets and icons.
            if not _check_rel(element):
                continue
            if 'src' in element:
                element_url = element['src']
            elif 'href' in element:
                element_url = element['href']
            else:
                # Might be a local <script> tag or something else
                continue
            absolute_url = web_url.resolved(QUrl(element_url))
            self._fetch_url(absolute_url)

        styles = web_frame.findAllElements('style')
        for style in styles:
            style = webkitelem.WebKitElement(style, tab=self.tab)
            # The Mozilla Developer Network says:
            # > type: This attribute defines the styling language as a MIME
            # > type (charset should not be specified). This attribute is
            # > optional and default to text/css if it's missing.
            # https://developer.mozilla.org/en/docs/Web/HTML/Element/style
            if 'type' in style and style['type'] != 'text/css':
                continue
            for element_url in _get_css_imports(str(style)):
                self._fetch_url(web_url.resolved(QUrl(element_url)))

        # Search for references in inline styles
        for element in web_frame.findAllElements('[style]'):
            element = webkitelem.WebKitElement(element, tab=self.tab)
            style = element['style']
            for element_url in _get_css_imports(style, inline=True):
                self._fetch_url(web_url.resolved(QUrl(element_url)))

        # Shortcut if no assets need to be downloaded, otherwise the file would
        # never be saved. Also might happen if the downloads are fast enough to
        # complete before connecting their finished signal.
        self._collect_zombies()
        if not self.pending_downloads and not self._finished_file:
            self._finish_file()
 def test_double_wrap(self, elem):
     """Test wrapping a WebKitElement."""
     with pytest.raises(TypeError) as excinfo:
         webkitelem.WebKitElement(elem, tab=None)
     assert str(excinfo.value) == "Trying to wrap a wrapper!"