Exemple #1
0
    def _is_blocked(
        self,
        request_url: QUrl,
        first_party_url: Optional[QUrl] = None,
        resource_type: Optional[interceptor.ResourceType] = None,
    ) -> bool:
        """Check whether the given request is blocked."""
        if not self.enabled:
            # Do nothing if `content.blocking.method` is not set to enable the
            # use of this adblocking module.
            return False

        if (
            first_party_url is None
            or not first_party_url.isValid()
            or first_party_url.scheme() == "file"
        ):
            # FIXME: It seems that when `first_party_url` is None, every URL
            # I try is blocked. This may have been a result of me incorrectly
            # using the upstream library, or an upstream bug. For now we don't
            # block any request with `first_party_url=None`.
            return False

        qtutils.ensure_valid(request_url)

        if not config.get("content.blocking.enabled", url=first_party_url):
            # Do nothing if adblocking is disabled for this site.
            return False

        result = self._engine.check_network_urls(
            request_url.toString(),
            first_party_url.toString(),
            _resource_type_to_string(resource_type),
        )

        if not result.matched:
            return False
        elif result.exception is not None and not result.important:
            # Exception is not `None` when the blocker matched on an exception
            # rule. Effectively this means that there was a match, but the
            # request should not be blocked.
            #
            # An `important` match means that exceptions should not apply and
            # no further checking is necessary--the request should be blocked.
            logger.debug(
                "Excepting %s from being blocked by %s because of %s",
                request_url.toDisplayString(),
                result.filter,
                result.exception,
            )
            return False
        elif blockutils.is_whitelisted_url(request_url):
            logger.debug(
                "Request to %s is whitelisted, thus not blocked",
                request_url.toDisplayString(),
            )
            return False
        return True
Exemple #2
0
def data_for_url(url: QUrl) -> Tuple[str, bytes]:
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    norm_url = url.adjusted(
        QUrl.NormalizePathSegments |  # type: ignore[arg-type]
        QUrl.StripTrailingSlash)
    if norm_url != url:
        raise Redirect(norm_url)

    path = url.path()
    host = url.host()
    query = url.query()
    # A url like "qute:foo" is split as "scheme:path", not "scheme:host".
    log.misc.debug("url: {}, path: {}, host {}".format(
        url.toDisplayString(), path, host))
    if not path or not host:
        new_url = QUrl()
        new_url.setScheme('qute')
        # When path is absent, e.g. qute://help (with no trailing slash)
        if host:
            new_url.setHost(host)
        # When host is absent, e.g. qute:help
        else:
            new_url.setHost(path)

        new_url.setPath('/')
        if query:
            new_url.setQuery(query)
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NotFoundError("No handler found for {}".format(
            url.toDisplayString()))

    try:
        mimetype, data = handler(url)
    except OSError as e:
        raise SchemeOSError(e)

    assert mimetype is not None, url
    if mimetype == 'text/html' and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode('utf-8', errors='xmlcharrefreplace')
    assert isinstance(data, bytes)

    return mimetype, data
Exemple #3
0
class HistoryEntry:
    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
    """
    def __init__(self, atime, url):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url

    def __repr__(self):
        return utils.get_repr(self,
                              constructor=True,
                              atime=self.atime,
                              url=self.url.toDisplayString())

    def __str__(self):
        return '{} {}'.format(int(self.atime), self.url_string)

    @classmethod
    def from_str(cls, s):
        """Get a history based on a 'TIME URL' string."""
        return cls(*s.split(' ', maxsplit=1))
    def _on_history_trigger(self):
        try:
            self._widget.page()
        except RuntimeError:
            # Looks like this slot can be triggered on destroyed tabs:
            # https://crashes.qutebrowser.org/view/3abffbed (Qt 5.9.1)
            # wrapped C/C++ object of type WebEngineView has been deleted
            log.misc.debug("Ignoring history trigger for destroyed tab")
            return

        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        # Don't add history entry if the URL is invalid anyways
        if not url.isValid():
            log.misc.debug("Ignoring invalid URL being added to history")
            return

        self.add_history_item.emit(url, requested_url, title)
Exemple #5
0
    def wait_for_load_finished_url(self, url, *, timeout=None,
                                   load_status='success'):
        """Wait until a URL has finished loading."""
        __tracebackhide__ = (lambda e: e.errisinstance(
            testprocess.WaitForTimeout))

        if timeout is None:
            if 'CI' in os.environ:
                timeout = 15000
            else:
                timeout = 5000

        # We really need the same representation that the webview uses in its
        # __repr__
        qurl = QUrl(url)
        if not qurl.isValid():
            raise ValueError("Invalid URL {}: {}".format(url,
                                                         qurl.errorString()))
        url = utils.elide(qurl.toDisplayString(QUrl.EncodeUnicode), 100)
        assert url

        pattern = re.compile(
            r"(load status for <qutebrowser\.browser\..* "
            r"tab_id=\d+ url='{url}/?'>: LoadStatus\.{load_status}|fetch: "
            r"PyQt5\.QtCore\.QUrl\('{url}'\) -> .*)".format(
                load_status=re.escape(load_status), url=re.escape(url)))

        try:
            self.wait_for(message=pattern, timeout=timeout)
        except testprocess.WaitForTimeout:
            raise testprocess.WaitForTimeout("Timed out while waiting for {} "
                                             "to be loaded".format(url))
Exemple #6
0
    def acceptNavigationRequest(self, url: QUrl,
                                typ: QWebEnginePage.NavigationType,
                                is_main_frame: bool):
        """Override acceptNavigationRequest to handle clicked links.

        Setting linkDelegationPolicy to DelegateAllLinks and using a slot bound
        to linkClicked won't work correctly, because when in a frameset, we
        have no idea in which frame the link should be opened.

        Checks if it should open it in a tab (middle-click or control) or not,
        and then conditionally opens the URL. Opening it in a new tab/window
        is handled in the slot connected to link_clicked.
        """
        target = self._tabdata.combined_target()
        log.webview.debug("navigation request: url {}, type {}, "
                          "target {}, is_main_frame {}".format(
                              url.toDisplayString(),
                              debug.qenum_key(QWebEnginePage, typ), target,
                              is_main_frame))

        if typ != QWebEnginePage.NavigationTypeLinkClicked:
            return True

        self.link_clicked.emit(url)

        return url.isValid() and target == usertypes.ClickTarget.normal
Exemple #7
0
    def acceptNavigationRequest(self,
                                url: QUrl,
                                typ: QWebEnginePage.NavigationType,
                                is_main_frame: bool):
        """Override acceptNavigationRequest to handle clicked links.

        Setting linkDelegationPolicy to DelegateAllLinks and using a slot bound
        to linkClicked won't work correctly, because when in a frameset, we
        have no idea in which frame the link should be opened.

        Checks if it should open it in a tab (middle-click or control) or not,
        and then conditionally opens the URL. Opening it in a new tab/window
        is handled in the slot connected to link_clicked.
        """
        target = self._tabdata.combined_target()
        log.webview.debug("navigation request: url {}, type {}, "
                          "target {}, is_main_frame {}".format(
                              url.toDisplayString(),
                              debug.qenum_key(QWebEnginePage, typ),
                              target, is_main_frame))

        if typ != QWebEnginePage.NavigationTypeLinkClicked:
            return True

        self.link_clicked.emit(url)

        return url.isValid() and target == usertypes.ClickTarget.normal
Exemple #8
0
class HistoryEntry:
    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
        hidden: If True, don't save this entry to disk
    """
    def __init__(self, atime, url, title, hidden=False):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url
        self.title = title
        self.hidden = hidden

    def __repr__(self):
        return utils.get_repr(self,
                              constructor=True,
                              atime=self.atime,
                              url=self.url.toDisplayString(),
                              title=self.title,
                              hidden=self.hidden)

    def __str__(self):
        return '{} {} {}'.format(int(self.atime), self.url_string, self.title)
    def _on_history_trigger(self):
        try:
            self._widget.page()
        except RuntimeError:
            # Looks like this slot can be triggered on destroyed tabs:
            # https://crashes.qutebrowser.org/view/3abffbed (Qt 5.9.1)
            # wrapped C/C++ object of type WebEngineView has been deleted
            log.misc.debug("Ignoring history trigger for destroyed tab")
            return

        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        # Don't add history entry if the URL is invalid anyways
        if not url.isValid():
            log.misc.debug("Ignoring invalid URL being added to history")
            return

        self.add_history_item.emit(url, requested_url, title)
Exemple #10
0
    def _get_hints_arg(self, *, origin_url: QUrl,
                       icon: QImage) -> Dict[str, Any]:
        """Get the hints argument for present()."""
        origin_url_str = origin_url.toDisplayString()
        hints: Dict[str, Any] = {
            # Include the origin in case the user wants to do different things
            # with different origin's notifications.
            "x-qutebrowser-origin": origin_url_str,
            "desktop-entry": "org.qutebrowser.qutebrowser",
        }

        is_useful_origin = self._should_include_origin(origin_url)
        if self._capabilities.kde_origin_name and is_useful_origin:
            hints["x-kde-origin-name"] = origin_url_str

        if icon.isNull():
            filename = 'icons/qutebrowser-64x64.png'
            icon = QImage.fromData(resources.read_file_binary(filename))

        key = self._quirks.icon_key or "image-data"
        data = self._convert_image(icon)
        if data is not None:
            hints[key] = data

        return hints
Exemple #11
0
class HistoryEntry:

    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
        hidden: If True, don't save this entry to disk
    """

    def __init__(self, atime, url, title, hidden=False):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url
        self.title = title
        self.hidden = hidden

    def __repr__(self):
        return utils.get_repr(self, constructor=True, atime=self.atime,
                              url=self.url.toDisplayString(), title=self.title,
                              hidden=self.hidden)

    def __str__(self):
        return '{} {} {}'.format(int(self.atime), self.url_string, self.title)
Exemple #12
0
    def _format_body(self, body: str, origin_url: QUrl) -> str:
        """Format the body according to the server capabilities.

        If the server doesn't support x-kde-origin-name, we include the origin URL as a
        prefix. If possible, we hyperlink it.

        For both prefix and body, we'll need to HTML escape it if the server supports
        body markup.
        """
        urlstr = origin_url.toDisplayString()
        is_useful_origin = self._should_include_origin(origin_url)

        if self._capabilities.kde_origin_name or not is_useful_origin:
            prefix = None
        elif self._capabilities.body_markup and self._capabilities.body_hyperlinks:
            href = html.escape(
                origin_url.toString(
                    QUrl.FullyEncoded)  # type: ignore[arg-type]
            )
            text = html.escape(urlstr, quote=False)
            prefix = f'<a href="{href}">{text}</a>'
        elif self._capabilities.body_markup:
            prefix = html.escape(urlstr, quote=False)
        else:
            prefix = urlstr

        if self._capabilities.body_markup:
            body = html.escape(body, quote=False)

        if prefix is None:
            return body

        return prefix + '\n\n' + body
Exemple #13
0
def test_resource_url():
    """Test resource_url() which can be used from templates."""
    data = jinja.render('test2.html')
    print(data)
    url = QUrl(data)
    assert url.isValid()
    assert url.toDisplayString() == 'qute://resource/utils/testfile'
    def createRequest(self, op, req, outgoing_data):
        """Return a new QNetworkReply object.

        Args:
             op: Operation op
             req: const QNetworkRequest & req
             outgoing_data: QIODevice * outgoingData

        Return:
            A QNetworkReply.
        """
        if proxymod.application_factory is not None:
            proxy_error = proxymod.application_factory.get_error()
            if proxy_error is not None:
                return networkreply.ErrorNetworkReply(
                    req, proxy_error, QNetworkReply.UnknownProxyError, self)

        if not req.url().isValid():
            log.network.debug("Ignoring invalid requested URL: {}".format(
                req.url().errorString()))
            return networkreply.ErrorNetworkReply(
                req, "Invalid request URL", QNetworkReply.HostNotFoundError,
                self)

        for header, value in shared.custom_headers(url=req.url()):
            req.setRawHeader(header, value)

        tab = self._get_tab()
        current_url = QUrl()
        if tab is not None:
            try:
                current_url = tab.url()
            except RuntimeError:
                # We could be in the middle of the webpage shutdown here.
                pass

        request = interceptors.Request(first_party_url=current_url,
                                       request_url=req.url())
        interceptors.run(request)
        if request.is_blocked:
            return networkreply.ErrorNetworkReply(
                req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
                self)

        if 'log-requests' in objects.debug_flags:
            operation = debug.qenum_key(QNetworkAccessManager, op)
            operation = operation.replace('Operation', '').upper()
            log.network.debug("{} {}, first-party {}".format(
                operation,
                req.url().toDisplayString(), current_url.toDisplayString()))

        scheme = req.url().scheme()
        if scheme in self._scheme_handlers:
            result = self._scheme_handlers[scheme](req, op, current_url)
            if result is not None:
                result.setParent(self)
                return result

        self.set_referer(req, current_url)
        return super().createRequest(op, req, outgoing_data)
Exemple #15
0
class HistoryEntry:

    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
    """

    def __init__(self, atime, url):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url

    def __repr__(self):
        return utils.get_repr(self, constructor=True, atime=self.atime, url=self.url.toDisplayString())

    def __str__(self):
        return "{} {}".format(int(self.atime), self.url_string)

    @classmethod
    def from_str(cls, s):
        """Get a history based on a 'TIME URL' string."""
        return cls(*s.split(" ", maxsplit=1))
 def update_source(self):
     if self.settings.contains('protofile'):
         source_url = QUrl(self.settings.value('protofile'))
         source = source_url.toDisplayString(QUrl.RemoveScheme)
         if os.path.isfile(source):
             self.source_url = source_url
             self.set_source(source)
     return not self.source_url.isEmpty()
Exemple #17
0
    def _handle_errorpage(self, info, errpage):
        """Display an error page if needed.

        Loosly based on Helpviewer/HelpBrowserWV.py from eric5
        (line 260 @ 5d937eb378dd)

        Args:
            info: The QWebPage.ErrorPageExtensionOption instance.
            errpage: The QWebPage.ErrorPageExtensionReturn instance, where the
                     error page will get written to.

        Return:
            False if no error page should be displayed, True otherwise.
        """
        ignored_errors = [
            (QWebPage.QtNetwork, QNetworkReply.OperationCanceledError),
            (QWebPage.WebKit, 203),  # "Loading is handled by the media engine"
        ]
        errpage.baseUrl = info.url
        urlstr = info.url.toDisplayString()
        if (info.domain, info.error) == (QWebPage.QtNetwork,
                                         QNetworkReply.ProtocolUnknownError):
            # For some reason, we get a segfault when we use
            # QDesktopServices::openUrl with info.url directly - however it
            # works when we construct a copy of it.
            url = QUrl(info.url)
            msg = "Open external application for {}-link?\nURL: {}".format(
                url.scheme(), url.toDisplayString())
            message.confirm_async(
                self._win_id, msg,
                functools.partial(QDesktopServices.openUrl, url))
            return True
        elif (info.domain, info.error) in ignored_errors:
            log.webview.debug("Ignored error on {}: {} (error domain: {}, "
                              "error code: {})".format(
                                  urlstr, info.errorString, info.domain,
                                  info.error))
            return False
        else:
            error_str = info.errorString
            if error_str == networkmanager.HOSTBLOCK_ERROR_STRING:
                error_str = "Request blocked by host blocker."
                # we don't set error_occured in this case.
            else:
                self._ignore_load_started = True
                self.error_occured = True
            log.webview.error("Error while loading {}: {}".format(
                urlstr, error_str))
            log.webview.debug("Error domain: {}, error code: {}".format(
                info.domain, info.error))
            title = "Error loading page: {}".format(urlstr)
            template = jinja.env.get_template('error.html')
            html = template.render(  # pylint: disable=maybe-no-member
                title=title, url=urlstr, error=error_str, icon='')
            errpage.content = html.encode('utf-8')
            errpage.encoding = 'utf-8'
            return True
    def addEngineFromForm(self, res, view):
        """
        Public method to add a new search engine from a form.
        
        @param res result of the JavaScript run on by
            WebBrowserView.__addSearchEngine()
        @type dict or None
        @param view reference to the web browser view
        @type WebBrowserView
        """
        if not res:
            return

        method = res["method"]
        actionUrl = QUrl(res["action"])
        inputName = res["inputName"]

        if method != "get":
            E5MessageBox.warning(
                self, self.tr("Method not supported"),
                self.tr("""{0} method is not supported.""").format(
                    method.upper()))
            return

        if actionUrl.isRelative():
            actionUrl = view.url().resolved(actionUrl)

        searchUrlQuery = QUrlQuery(actionUrl)
        searchUrlQuery.addQueryItem(inputName, "{searchTerms}")

        inputFields = res["inputs"]
        for inputField in inputFields:
            name = inputField[0]
            value = inputField[1]

            if not name or name == inputName or not value:
                continue

            searchUrlQuery.addQueryItem(name, value)

        engineName, ok = QInputDialog.getText(
            view, self.tr("Engine name"),
            self.tr("Enter a name for the engine"), QLineEdit.Normal)
        if not ok:
            return

        actionUrl.setQuery(searchUrlQuery)

        from .OpenSearchEngine import OpenSearchEngine
        engine = OpenSearchEngine()
        engine.setName(engineName)
        engine.setDescription(engineName)
        engine.setSearchUrlTemplate(
            actionUrl.toDisplayString(QUrl.FullyDecoded))
        engine.setImage(view.icon().pixmap(16, 16).toImage())

        self.__addEngineByEngine(engine)
Exemple #19
0
    def preset_cmd_text(self, url: QUrl, context: HintContext) -> None:
        """Preset a commandline text based on a hint URL."""
        urlstr = url.toDisplayString(QUrl.FullyEncoded)  # type: ignore
        args = context.get_args(urlstr)
        text = ' '.join(args)
        if text[0] not in modeparsers.STARTCHARS:
            raise HintingError("Invalid command text '{}'.".format(text))

        cmd = objreg.get('status-command', scope='window', window=self._win_id)
        cmd.set_cmd_text(text)
Exemple #20
0
def safe_display_string(qurl: QUrl) -> str:
    """Get a IDN-homograph phishing safe form of the given QUrl.

    If we're dealing with a Punycode-encoded URL, this prepends the hostname in
    its encoded form, to make sure those URLs are distinguishable.

    See https://github.com/qutebrowser/qutebrowser/issues/2547
    and https://bugreports.qt.io/browse/QTBUG-60365
    """
    ensure_valid(qurl)

    host = qurl.host(QUrl.FullyEncoded)
    assert '..' not in host, qurl  # https://bugreports.qt.io/browse/QTBUG-60364

    for part in host.split('.'):
        url_host = qurl.host(QUrl.FullyDecoded)
        if part.startswith('xn--') and host != url_host:
            return '({}) {}'.format(host, qurl.toDisplayString())

    return qurl.toDisplayString()
Exemple #21
0
    def _handle_errorpage(self, info, errpage):
        """Display an error page if needed.

        Loosly based on Helpviewer/HelpBrowserWV.py from eric5
        (line 260 @ 5d937eb378dd)

        Args:
            info: The QWebPage.ErrorPageExtensionOption instance.
            errpage: The QWebPage.ErrorPageExtensionReturn instance, where the
                     error page will get written to.

        Return:
            False if no error page should be displayed, True otherwise.
        """
        ignored_errors = [
            (QWebPage.QtNetwork, QNetworkReply.OperationCanceledError),
            (QWebPage.WebKit, 203),  # "Loading is handled by the media engine"
        ]
        errpage.baseUrl = info.url
        urlstr = info.url.toDisplayString()
        if (info.domain, info.error) == (QWebPage.QtNetwork,
                                         QNetworkReply.ProtocolUnknownError):
            # For some reason, we get a segfault when we use
            # QDesktopServices::openUrl with info.url directly - however it
            # works when we construct a copy of it.
            url = QUrl(info.url)
            msg = "Open external application for {}-link?\nURL: {}".format(
                url.scheme(), url.toDisplayString())
            message.confirm_async(
                self._win_id, msg,
                functools.partial(QDesktopServices.openUrl, url))
            return True
        elif (info.domain, info.error) in ignored_errors:
            log.webview.debug("Ignored error on {}: {} (error domain: {}, "
                              "error code: {})".format(urlstr,
                                                       info.errorString,
                                                       info.domain,
                                                       info.error))
            return False
        else:
            log.webview.error("Error while loading {}: {}".format(
                urlstr, info.errorString))
            log.webview.debug("Error domain: {}, error code: {}".format(
                info.domain, info.error))
            title = "Error loading page: {}".format(urlstr)
            template = jinja.env.get_template('error.html')
            html = template.render(  # pylint: disable=maybe-no-member
                title=title,
                url=urlstr,
                error=info.errorString,
                icon='')
            errpage.content = html.encode('utf-8')
            errpage.encoding = 'utf-8'
            return True
Exemple #22
0
    def _on_history_trigger(self):
        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        self.add_history_item.emit(url, requested_url, title)
Exemple #23
0
    def _on_history_trigger(self):
        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        self.add_history_item.emit(url, requested_url, title)
Exemple #24
0
def invalid_url_error(url: QUrl, action: str) -> None:
    """Display an error message for a URL.

    Args:
        action: The action which was interrupted by the error.
    """
    if url.isValid():
        raise ValueError("Calling invalid_url_error with valid URL {}".format(
            url.toDisplayString()))
    errstring = get_errstring(url,
                              "Trying to {} with invalid URL".format(action))
    message.error(errstring)
Exemple #25
0
def data_for_url(url: QUrl):
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    norm_url = url.adjusted(QUrl.NormalizePathSegments
                            | QUrl.StripTrailingSlash)
    if norm_url != url:
        raise Redirect(norm_url)

    path = url.path()
    host = url.host()
    query = urlutils.query_string(url)
    # A url like "luminos:foo" is split as "scheme:path", not "scheme:host".

    if not path or not host:
        new_url = QUrl()
        new_url.setScheme("luminos")
        # When path is absent, e.g. luminos://help (with no trailing slash)
        if host:
            new_url.setHost(host)
        # When host is absent, e.g. luminos:help
        else:
            new_url.setHost(path)

        new_url.setPath("/")
        if query:
            new_url.setQuery(query)
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NotFoundError("No handler found for {}".format(
            url.toDisplayString()))

    try:
        mimetype, data = handler(url)
    except OSError as e:
        raise SchemeOSError(e)

    assert mimetype is not None, url
    if mimetype == "text/html" and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode("utf-8", errors="xmlcharrefreplace")

    return mimetype, data
Exemple #26
0
def host_tuple(url: QUrl) -> HostTupleType:
    """Get a (scheme, host, port) tuple from a QUrl.

    This is suitable to identify a connection, e.g. for SSL errors.
    """
    ensure_valid(url)
    scheme, host, port = url.scheme(), url.host(), url.port()
    assert scheme
    if not host:
        raise ValueError("Got URL {} without host.".format(
            url.toDisplayString()))
    if port == -1:
        port_mapping = {
            'http': 80,
            'https': 443,
            'ftp': 21,
        }
        try:
            port = port_mapping[scheme]
        except KeyError:
            raise ValueError("Got URL {} with unknown port.".format(
                url.toDisplayString()))
    return scheme, host, port
    def get_image(self, size):
        if self.source_setting_changed():
            self.update_source()

        image = QImage(size.width(), size.height(), QImage.Format_ARGB32)
        painter = QPainter(image)
        if self.graph is None:
            if self.settings.contains('protofile'):
                url = QUrl(self.settings.value('protofile'))
                self.set_source(url.toDisplayString(QUrl.RemoveScheme))
            else:
                image.fill(QColor(200, 200, 200))
            return image
        self.renderer.load(QByteArray(self.graph))
        self.renderer.render(painter)
        return image
    def _on_history_trigger(self):
        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        # Don't add history entry if the URL is invalid anyways
        if not url.isValid():
            log.misc.debug("Ignoring invalid URL being added to history")
            return

        self.add_history_item.emit(url, requested_url, title)
    def _on_history_trigger(self):
        url = self.url()
        requested_url = self.url(requested=True)

        # Don't save the title if it's generated from the URL
        title = self.title()
        title_url = QUrl(url)
        title_url.setScheme('')
        if title == title_url.toDisplayString(QUrl.RemoveScheme).strip('/'):
            title = ""

        # Don't add history entry if the URL is invalid anyways
        if not url.isValid():
            log.misc.debug("Ignoring invalid URL being added to history")
            return

        self.add_history_item.emit(url, requested_url, title)
Exemple #30
0
    def wait_for_load_finished_url(self,
                                   url,
                                   *,
                                   timeout=None,
                                   load_status='success',
                                   after=None):
        """Wait until a URL has finished loading."""
        __tracebackhide__ = (
            lambda e: e.errisinstance(testprocess.WaitForTimeout))

        if timeout is None:
            if 'CI' in os.environ:
                timeout = 15000
            else:
                timeout = 5000

        qurl = QUrl(url)
        if not qurl.isValid():
            raise ValueError("Invalid URL {}: {}".format(
                url, qurl.errorString()))

        if (qurl == QUrl('about:blank')
                and not qtutils.version_check('5.10', compiled=False)):
            # For some reason, we don't get a LoadStatus.success for
            # about:blank sometimes.
            # However, if we do this for Qt 5.10, we get general testsuite
            # instability as site loads get reported with about:blank...
            pattern = "Changing title for idx * to 'about:blank'"
        else:
            # We really need the same representation that the webview uses in
            # its __repr__
            url = utils.elide(qurl.toDisplayString(QUrl.EncodeUnicode), 100)
            assert url

            pattern = re.compile(
                r"(load status for <qutebrowser\.browser\..* "
                r"tab_id=\d+ url='{url}/?'>: LoadStatus\.{load_status}|fetch: "
                r"PyQt5\.QtCore\.QUrl\('{url}'\) -> .*)".format(
                    load_status=re.escape(load_status), url=re.escape(url)))

        try:
            self.wait_for(message=pattern, timeout=timeout, after=after)
        except testprocess.WaitForTimeout:
            raise testprocess.WaitForTimeout("Timed out while waiting for {} "
                                             "to be loaded".format(url))
Exemple #31
0
    def set_hover_url(self, link):
        """Setter to be used as a Qt slot.

        Saves old shown URL in self._old_url and restores it later if a link is
        "un-hovered" when it gets called with empty parameters.

        Args:
            link: The link which was hovered (string)
        """
        if link:
            qurl = QUrl(link)
            if qurl.isValid():
                self._hover_url = qurl.toDisplayString()
            else:
                self._hover_url = link
        else:
            self._hover_url = None
        self._update_url()
Exemple #32
0
    def acceptNavigationRequest(self, url: QUrl,
                                typ: QWebEnginePage.NavigationType,
                                is_main_frame: bool):
        """Override acceptNavigationRequest to handle clicked links.

        This only show an error on invalid links - everything else is handled
        in createWindow.
        """
        log.webview.debug("navigation request: url {}, type {}, is_main_frame "
                          "{}".format(url.toDisplayString(),
                                      debug.qenum_key(QWebEnginePage, typ),
                                      is_main_frame))
        if (typ == QWebEnginePage.NavigationTypeLinkClicked
                and not url.isValid()):
            msg = urlutils.get_errstring(url, "Invalid link clicked")
            message.error(msg)
            return False
        return True
    def acceptNavigationRequest(self,
                                url: QUrl,
                                typ: QWebEnginePage.NavigationType,
                                is_main_frame: bool):
        """Override acceptNavigationRequest to handle clicked links.

        This only show an error on invalid links - everything else is handled
        in createWindow.
        """
        log.webview.debug("navigation request: url {}, type {}, is_main_frame "
                          "{}".format(url.toDisplayString(),
                                      debug.qenum_key(QWebEnginePage, typ),
                                      is_main_frame))
        if (typ == QWebEnginePage.NavigationTypeLinkClicked and
                not url.isValid()):
            msg = urlutils.get_errstring(url, "Invalid link clicked")
            message.error(msg)
            return False
        return True
Exemple #34
0
    def set_hover_url(self, link, _title, _text):
        """Setter to be used as a Qt slot.

        Saves old shown URL in self._old_url and restores it later if a link is
        "un-hovered" when it gets called with empty parameters.

        Args:
            link: The link which was hovered (string)
            _title: The title of the hovered link (string)
            _text: The text of the hovered link (string)
        """
        if link:
            qurl = QUrl(link)
            if qurl.isValid():
                self._hover_url = qurl.toDisplayString()
            else:
                self._hover_url = link
        else:
            self._hover_url = None
        self._update_url()
    def wait_for_load_finished_url(self, url, *, timeout=None,
                                   load_status='success', after=None):
        """Wait until a URL has finished loading."""
        __tracebackhide__ = (lambda e: e.errisinstance(
            testprocess.WaitForTimeout))

        if timeout is None:
            if 'CI' in os.environ:
                timeout = 15000
            else:
                timeout = 5000

        qurl = QUrl(url)
        if not qurl.isValid():
            raise ValueError("Invalid URL {}: {}".format(url,
                                                         qurl.errorString()))

        if (qurl == QUrl('about:blank') and
                not qtutils.version_check('5.10', compiled=False)):
            # For some reason, we don't get a LoadStatus.success for
            # about:blank sometimes.
            # However, if we do this for Qt 5.10, we get general testsuite
            # instability as site loads get reported with about:blank...
            pattern = "Changing title for idx * to 'about:blank'"
        else:
            # We really need the same representation that the webview uses in
            # its __repr__
            url = utils.elide(qurl.toDisplayString(QUrl.EncodeUnicode), 100)
            assert url

            pattern = re.compile(
                r"(load status for <qutebrowser\.browser\..* "
                r"tab_id=\d+ url='{url}/?'>: LoadStatus\.{load_status}|fetch: "
                r"PyQt5\.QtCore\.QUrl\('{url}'\) -> .*)".format(
                    load_status=re.escape(load_status), url=re.escape(url)))

        try:
            self.wait_for(message=pattern, timeout=timeout, after=after)
        except testprocess.WaitForTimeout:
            raise testprocess.WaitForTimeout("Timed out while waiting for {} "
                                             "to be loaded".format(url))
Exemple #36
0
def data_for_custom_scheme(url: QUrl):
    """"""
    scheme = url.scheme()

    try:
        handler = _CUSTOM_HANDLERS[scheme]
    except KeyError:
        raise NotFoundError("No handler found for scheme {}".format(
            url.toDisplayString()))

    try:
        mimetype, data = handler(url)
    except OSError as e:
        raise SchemeOSError(e)

    assert mimetype is not None, url
    if mimetype == "text/html" and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode("utf-8", errors="xmlcharrefreplace")

    return mimetype, data
Exemple #37
0
    def update_for_url(self, url: QUrl) -> typing.Set[str]:
        """Update settings customized for the given tab.

        Return:
            A set of settings which actually changed.
        """
        qtutils.ensure_valid(url)
        changed_settings = set()
        for values in config.instance:
            if not values.opt.supports_pattern:
                continue

            value = values.get_for_url(url, fallback=False)

            changed = self._update_setting(values.opt.name, value)
            if changed:
                log.config.debug("Changed for {}: {} = {}".format(
                    url.toDisplayString(), values.opt.name, value))
                changed_settings.add(values.opt.name)

        return changed_settings
    def update_for_url(self, url: QUrl) -> typing.Set[str]:
        """Update settings customized for the given tab.

        Return:
            A set of settings which actually changed.
        """
        qtutils.ensure_valid(url)
        changed_settings = set()
        for values in config.instance:
            if not values.opt.supports_pattern:
                continue

            value = values.get_for_url(url, fallback=False)

            changed = self._update_setting(values.opt.name, value)
            if changed:
                log.config.debug("Changed for {}: {} = {}".format(
                    url.toDisplayString(), values.opt.name, value))
                changed_settings.add(values.opt.name)

        return changed_settings
Exemple #39
0
    def set_hover_url(self, link, _title, _text):
        """Setter to be used as a Qt slot.

        Saves old shown URL in self._old_url and restores it later if a link is
        "un-hovered" when it gets called with empty parameters.

        Args:
            link: The link which was hovered (string)
            _title: The title of the hovered link (string)
            _text: The text of the hovered link (string)
        """
        if link:
            # We assume that `link` is always be given in a form that generates
            # a valid QUrl. If this proves to be wrong, we should probably
            # check and fall back to the text version otherwise.
            qurl = QUrl(link)
            assert qurl.isValid(), link
            self._hover_url = qurl.toDisplayString()
        else:
            self._hover_url = None
        self._update_url()
class HistoryEntry:

    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
    """

    def __init__(self, atime, url):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url

    def __repr__(self):
        return utils.get_repr(self, constructor=True, atime=self.atime,
                              url=self.url.toDisplayString())

    def __str__(self):
        return '{} {}'.format(int(self.atime), self.url_string)
Exemple #41
0
class HistoryEntry:
    """A single entry in the web history.

    Attributes:
        atime: The time the page was accessed.
        url: The URL which was accessed as QUrl.
        url_string: The URL which was accessed as string.
    """
    def __init__(self, atime, url):
        self.atime = float(atime)
        self.url = QUrl(url)
        self.url_string = url

    def __repr__(self):
        return utils.get_repr(self,
                              constructor=True,
                              atime=self.atime,
                              url=self.url.toDisplayString())

    def __str__(self):
        return '{} {}'.format(int(self.atime), self.url_string)
Exemple #42
0
    def __addFeed(self, button):
        """
        Private slot to add a RSS feed.
        
        @param button reference to the feed button
        @type QPushButton
        """
        urlString = button.feed[1]
        url = QUrl(urlString)
        if url.isRelative():
            url = self.__browser.url().resolved(url)
            urlString = url.toDisplayString(QUrl.FullyDecoded)

        if not url.isValid():
            return

        if button.feed[0]:
            title = button.feed[0]
        else:
            title = self.__browser.url().host()

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        feedsManager = WebBrowserWindow.feedsManager()
        if feedsManager.addFeed(urlString, title, self.__browser.icon()):
            if WebBrowserWindow.notificationsEnabled():
                WebBrowserWindow.showNotification(
                    UI.PixmapCache.getPixmap("rss48.png"),
                    self.tr("Add RSS Feed"),
                    self.tr("""The feed was added successfully."""))
            else:
                E5MessageBox.information(
                    self, self.tr("Add RSS Feed"),
                    self.tr("""The feed was added successfully."""))
        else:
            E5MessageBox.warning(
                self, self.tr("Add RSS Feed"),
                self.tr("""The feed was already added before."""))

        self.close()
    def createRequest(self, op, req, outgoing_data):
        """Return a new QNetworkReply object.

        Args:
             op: Operation op
             req: const QNetworkRequest & req
             outgoing_data: QIODevice * outgoingData

        Return:
            A QNetworkReply.
        """
        proxy_factory = objreg.get('proxy-factory', None)
        if proxy_factory is not None:
            proxy_error = proxy_factory.get_error()
            if proxy_error is not None:
                return networkreply.ErrorNetworkReply(
                    req, proxy_error, QNetworkReply.UnknownProxyError,
                    self)

        for header, value in shared.custom_headers(url=req.url()):
            req.setRawHeader(header, value)

        # There are some scenarios where we can't figure out current_url:
        # - There's a generic NetworkManager, e.g. for downloads
        # - The download was in a tab which is now closed.
        current_url = QUrl()

        if self._tab_id is not None:
            assert self._win_id is not None
            try:
                tab = objreg.get('tab', scope='tab', window=self._win_id,
                                 tab=self._tab_id)
                current_url = tab.url()
            except (KeyError, RuntimeError):
                # https://github.com/qutebrowser/qutebrowser/issues/889
                # Catching RuntimeError because we could be in the middle of
                # the webpage shutdown here.
                current_url = QUrl()

        request = interceptors.Request(first_party_url=current_url,
                                       request_url=req.url())
        interceptors.run(request)
        if request.is_blocked:
            return networkreply.ErrorNetworkReply(
                req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
                self)

        if 'log-requests' in self._args.debug_flags:
            operation = debug.qenum_key(QNetworkAccessManager, op)
            operation = operation.replace('Operation', '').upper()
            log.webview.debug("{} {}, first-party {}".format(
                operation,
                req.url().toDisplayString(),
                current_url.toDisplayString()))

        scheme = req.url().scheme()
        if scheme in self._scheme_handlers:
            result = self._scheme_handlers[scheme](req, op, current_url)
            if result is not None:
                result.setParent(self)
                return result

        self.set_referer(req, current_url)
        return super().createRequest(op, req, outgoing_data)
Exemple #44
0
    def _handle_errorpage(self, info, errpage):
        """Display an error page if needed.

        Loosely based on Helpviewer/HelpBrowserWV.py from eric5
        (line 260 @ 5d937eb378dd)

        Args:
            info: The QWebPage.ErrorPageExtensionOption instance.
            errpage: The QWebPage.ErrorPageExtensionReturn instance, where the
                     error page will get written to.

        Return:
            False if no error page should be displayed, True otherwise.
        """
        ignored_errors = [
            (QWebPage.QtNetwork, QNetworkReply.OperationCanceledError),
            # "Loading is handled by the media engine"
            (QWebPage.WebKit, 203),
            # "Frame load interrupted by policy change"
            (QWebPage.WebKit, 102),
        ]
        errpage.baseUrl = info.url
        urlstr = info.url.toDisplayString()
        if (info.domain, info.error) == (QWebPage.QtNetwork,
                                         QNetworkReply.ProtocolUnknownError):
            # For some reason, we get a segfault when we use
            # QDesktopServices::openUrl with info.url directly - however it
            # works when we construct a copy of it.
            url = QUrl(info.url)
            scheme = url.scheme()
            message.confirm_async(
                title="Open external application for {}-link?".format(scheme),
                text="URL: <b>{}</b>".format(html.escape(
                    url.toDisplayString())),
                yes_action=functools.partial(QDesktopServices.openUrl, url))
            return True
        elif (info.domain, info.error) in ignored_errors:
            log.webview.debug("Ignored error on {}: {} (error domain: {}, "
                              "error code: {})".format(urlstr,
                                                       info.errorString,
                                                       info.domain,
                                                       info.error))
            return False
        else:
            error_str = info.errorString
            if error_str == networkmanager.HOSTBLOCK_ERROR_STRING:
                # We don't set error_occurred in this case.
                error_str = "Request blocked by host blocker."
                main_frame = info.frame.page().mainFrame()
                if info.frame != main_frame:
                    # Content in an iframe -> Hide the frame so it doesn't use
                    # any space. We can't hide the frame's documentElement
                    # directly though.
                    for elem in main_frame.documentElement().findAll('iframe'):
                        if QUrl(elem.attribute('src')) == info.url:
                            elem.setAttribute('style', 'display: none')
                    return False
            else:
                self._ignore_load_started = True
                self.error_occurred = True
            log.webview.error("Error while loading {}: {}".format(
                urlstr, error_str))
            log.webview.debug("Error domain: {}, error code: {}".format(
                info.domain, info.error))
            title = "Error loading page: {}".format(urlstr)
            error_html = jinja.render('error.html',
                                      title=title,
                                      url=urlstr,
                                      error=error_str)
            errpage.content = error_html.encode('utf-8')
            errpage.encoding = 'utf-8'
            return True
Exemple #45
0
    def _handle_errorpage(self, info, errpage):
        """Display an error page if needed.

        Loosely based on Helpviewer/HelpBrowserWV.py from eric5
        (line 260 @ 5d937eb378dd)

        Args:
            info: The QWebPage.ErrorPageExtensionOption instance.
            errpage: The QWebPage.ErrorPageExtensionReturn instance, where the
                     error page will get written to.

        Return:
            False if no error page should be displayed, True otherwise.
        """
        ignored_errors = [
            (QWebPage.QtNetwork, QNetworkReply.OperationCanceledError),
            # "Loading is handled by the media engine"
            (QWebPage.WebKit, 203),
            # "Frame load interrupted by policy change"
            (QWebPage.WebKit, 102),
        ]
        errpage.baseUrl = info.url
        urlstr = info.url.toDisplayString()
        if (info.domain, info.error) == (QWebPage.QtNetwork,
                                         QNetworkReply.ProtocolUnknownError):
            # For some reason, we get a segfault when we use
            # QDesktopServices::openUrl with info.url directly - however it
            # works when we construct a copy of it.
            url = QUrl(info.url)
            msg = "Open external application for {}-link?\nURL: {}".format(
                url.scheme(), url.toDisplayString())
            message.confirm_async(
                self._win_id, msg,
                functools.partial(QDesktopServices.openUrl, url))
            return True
        elif (info.domain, info.error) in ignored_errors:
            log.webview.debug("Ignored error on {}: {} (error domain: {}, "
                              "error code: {})".format(
                                  urlstr, info.errorString, info.domain,
                                  info.error))
            return False
        else:
            error_str = info.errorString
            if error_str == networkmanager.HOSTBLOCK_ERROR_STRING:
                # We don't set error_occurred in this case.
                error_str = "Request blocked by host blocker."
                main_frame = info.frame.page().mainFrame()
                if info.frame != main_frame:
                    # Content in an iframe -> Hide the frame so it doesn't use
                    # any space. We can't hide the frame's documentElement
                    # directly though.
                    for elem in main_frame.documentElement().findAll('iframe'):
                        if QUrl(elem.attribute('src')) == info.url:
                            elem.setAttribute('style', 'display: none')
                    return False
            else:
                self._ignore_load_started = True
                self.error_occurred = True
            log.webview.error("Error while loading {}: {}".format(
                urlstr, error_str))
            log.webview.debug("Error domain: {}, error code: {}".format(
                info.domain, info.error))
            title = "Error loading page: {}".format(urlstr)
            html = jinja.render(
                'error.html',
                title=title, url=urlstr, error=error_str, icon='')
            errpage.content = html.encode('utf-8')
            errpage.encoding = 'utf-8'
            return True
Exemple #46
0
 def _on_before_load_started(self, url: QUrl) -> None:
     """Adjust the title if we are going to visit a URL soon."""
     qtutils.ensure_valid(url)
     url_string = url.toDisplayString()
     log.webview.debug("Going to start loading: {}".format(url_string))
     self.title_changed.emit(url_string)
Exemple #47
0
 def _on_url_changed(self, url: QUrl) -> None:
     """Update title when URL has changed and no title is available."""
     if url.isValid() and not self.title():
         self.title_changed.emit(url.toDisplayString())
     self.url_changed.emit(url)
 def _on_before_load_started(self, url: QUrl) -> None:
     """Adjust the title if we are going to visit a URL soon."""
     qtutils.ensure_valid(url)
     url_string = url.toDisplayString()
     log.webview.debug("Going to start loading: {}".format(url_string))
     self.title_changed.emit(url_string)
 def _on_url_changed(self, url: QUrl) -> None:
     """Update title when URL has changed and no title is available."""
     if url.isValid() and not self.title():
         self.title_changed.emit(url.toDisplayString())
     self.url_changed.emit(url)