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
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)
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
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))
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)
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)
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
def test_eq(self): one = get_webelem() two = webkitelem.WebKitElement(one._elem, tab=None) assert one == two
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)
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!"