Exemplo n.º 1
0
 def _ask_confirm_question(self, title, msg):
     no_action = functools.partial(self.cancel, remove_data=False)
     url = 'file://{}'.format(self._filename)
     message.confirm_async(title=title, text=msg,
                           yes_action=self._after_set_filename,
                           no_action=no_action, cancel_action=no_action,
                           abort_on=[self.cancelled, self.error], url=url)
Exemplo n.º 2
0
    def quickmark_add(self, win_id, url, name):
        """Add a new quickmark.

        Args:
            win_id: The window ID to display the errors in.
            url: The url to add as quickmark.
            name: The name for the new quickmark.
        """
        # We don't raise cmdexc.CommandError here as this can be called async
        # via prompt_save.
        if not name:
            message.error(win_id, "Can't set mark with empty name!")
            return
        if not url:
            message.error(win_id, "Can't set mark with empty URL!")
            return

        def set_mark():
            """Really set the quickmark."""
            self.marks[name] = url
            self.changed.emit()
            self.added.emit(name, url)

        if name in self.marks:
            message.confirm_async(win_id,
                                  "Override existing quickmark?",
                                  set_mark,
                                  default=True)
        else:
            set_mark()
Exemplo n.º 3
0
    def quickmark_add(self, url, name):
        """Add a new quickmark.

        You can view all saved quickmarks on the
        link:qute://bookmarks[bookmarks page].

        Args:
            url: The url to add as quickmark.
            name: The name for the new quickmark.
        """
        # We don't raise cmdexc.CommandError here as this can be called async
        # via prompt_save.
        if not name:
            message.error("Can't set mark with empty name!")
            return
        if not url:
            message.error("Can't set mark with empty URL!")
            return

        def set_mark():
            """Really set the quickmark."""
            self.marks[name] = url
            self.changed.emit()
            log.misc.debug("Added quickmark {} for {}".format(name, url))

        if name in self.marks:
            message.confirm_async(
                title="Override existing quickmark?",
                yes_action=set_mark, default=True, url=url)
        else:
            set_mark()
Exemplo n.º 4
0
    def quickmark_add(self, url, name):
        """Add a new quickmark.

        You can view all saved quickmarks on the
        link:qute://bookmarks[bookmarks page].

        Args:
            url: The url to add as quickmark.
            name: The name for the new quickmark.
        """
        # We don't raise cmdexc.CommandError here as this can be called async
        # via prompt_save.
        if not name:
            message.error("Can't set mark with empty name!")
            return
        if not url:
            message.error("Can't set mark with empty URL!")
            return

        def set_mark():
            """Really set the quickmark."""
            self.marks[name] = url
            self.changed.emit()
            log.misc.debug("Added quickmark {} for {}".format(name, url))

        if name in self.marks:
            message.confirm_async(title="Override existing quickmark?",
                                  yes_action=set_mark,
                                  default=True)
        else:
            set_mark()
Exemplo n.º 5
0
def quickmark_add(url, name):
    """Add a new quickmark.

    Args:
        url: The url to add as quickmark.
        name: The name for the new quickmark.
    """
    # We don't raise cmdexc.CommandError here as this can be called async via
    # prompt_save.
    if not name:
        message.error("Can't set mark with empty name!")
        return
    if not url:
        message.error("Can't set mark with empty URL!")
        return

    def set_mark():
        """Really set the quickmark."""
        marks[name] = url

    if name in marks:
        message.confirm_async("Override existing quickmark?", set_mark,
                              default=True)
    else:
        set_mark()
Exemplo n.º 6
0
 def _ask_confirm_question(self, title, msg):
     no_action = functools.partial(self.cancel, remove_data=False)
     url = 'file://{}'.format(self._filename)
     message.confirm_async(title=title, text=msg,
                           yes_action=self._after_set_filename,
                           no_action=no_action, cancel_action=no_action,
                           abort_on=[self.cancelled, self.error], url=url)
Exemplo n.º 7
0
    def quickmark_add(self, win_id, url, name):
        """Add a new quickmark.

        Args:
            win_id: The window ID to display the errors in.
            url: The url to add as quickmark.
            name: The name for the new quickmark.
        """
        # We don't raise cmdexc.CommandError here as this can be called async
        # via prompt_save.
        if not name:
            message.error(win_id, "Can't set mark with empty name!")
            return
        if not url:
            message.error(win_id, "Can't set mark with empty URL!")
            return

        def set_mark():
            """Really set the quickmark."""
            self.marks[name] = url
            self.changed.emit()
            self.added.emit(name, url)

        if name in self.marks:
            message.confirm_async(
                win_id, "Override existing quickmark?", set_mark, default=True)
        else:
            set_mark()
Exemplo n.º 8
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
Exemplo n.º 9
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
Exemplo n.º 10
0
 def _ask_create_parent_question(self, title, msg, force_overwrite,
                                 remember_directory):
     no_action = functools.partial(self.cancel, remove_data=False)
     message.confirm_async(
         title=title,
         text=msg,
         yes_action=(lambda: self._after_create_parent_question(
             force_overwrite, remember_directory)),
         no_action=no_action,
         cancel_action=no_action,
         abort_on=[self.cancelled, self.error])
Exemplo n.º 11
0
 def _ask_create_parent_question(self, title, msg,
                                 force_overwrite, remember_directory):
     no_action = functools.partial(self.cancel, remove_data=False)
     url = 'file://{}'.format(os.path.dirname(self._filename))
     message.confirm_async(title=title, text=msg,
                           yes_action=(lambda:
                                       self._after_create_parent_question(
                                           force_overwrite,
                                           remember_directory)),
                           no_action=no_action, cancel_action=no_action,
                           abort_on=[self.cancelled, self.error], url=url)
Exemplo n.º 12
0
    def tab_close_prompt_if_pinned(self, tab, force, yes_action):
        """Helper method for tab_close.

        If tab is pinned, prompt. If everything is good, run yes_action.
        """
        if tab.data.pinned and not force:
            message.confirm_async(
                title='Pinned Tab',
                text="Are you sure you want to close a pinned tab?",
                yes_action=yes_action, default=False)
        else:
            yes_action()
Exemplo n.º 13
0
    def tab_close_prompt_if_pinned(self, tab, force, yes_action):
        """Helper method for tab_close.

        If tab is pinned, prompt. If everything is good, run yes_action.
        """
        if tab.data.pinned and not force:
            message.confirm_async(
                title='Pinned Tab',
                text="Are you sure you want to close a pinned tab?",
                yes_action=yes_action, default=False)
        else:
            yes_action()
Exemplo n.º 14
0
 def _ask_create_parent_question(self, title, msg,
                                 force_overwrite, remember_directory):
     assert self._filename is not None
     no_action = functools.partial(self.cancel, remove_data=False)
     url = 'file://{}'.format(os.path.dirname(self._filename))
     message.confirm_async(title=title, text=msg,
                           yes_action=(lambda:
                                       self._after_create_parent_question(
                                           force_overwrite,
                                           remember_directory)),
                           no_action=no_action, cancel_action=no_action,
                           abort_on=[self.cancelled, self.error], url=url)
Exemplo n.º 15
0
    def tab_close_prompt_if_pinned(self, tab, force, yes_action):
        """Helper method for tab_close.

        If tab is pinned, prompt. If not, run yes_action.
        If tab is destroyed, abort question.
        """
        if tab.data.pinned and not force:
            message.confirm_async(
                title='Pinned Tab',
                text="Are you sure you want to close a pinned tab?",
                yes_action=yes_action, default=False, abort_on=[tab.destroyed])
        else:
            yes_action()
Exemplo n.º 16
0
    def clear(self, force=False):
        """Clear all browsing history.

        Note this only clears the global history
        (e.g. `~/.local/share/qutebrowser/history` on Linux) but not cookies,
        the back/forward history of a tab, cache or other persistent data.

        Args:
            force: Don't ask for confirmation.
        """
        if force:
            self._do_clear()
        else:
            message.confirm_async(self._do_clear, title="Clear all browsing "
                                "history?")
Exemplo n.º 17
0
def history_clear(force=False):
    """Clear all browsing history.

    Note this only clears the global history
    (e.g. `~/.local/share/qutebrowser/history` on Linux) but not cookies,
    the back/forward history of a tab, cache or other persistent data.

    Args:
        force: Don't ask for confirmation.
    """
    if force:
        web_history.clear()
    else:
        message.confirm_async(yes_action=web_history.clear,
                              title="Clear all browsing history?")
Exemplo n.º 18
0
    def tab_close_prompt_if_pinned(
            self, tab, force, yes_action,
            text="Are you sure you want to close a pinned tab?"):
        """Helper method for tab_close.

        If tab is pinned, prompt. If not, run yes_action.
        If tab is destroyed, abort question.
        """
        if tab.data.pinned and not force:
            message.confirm_async(
                title='Pinned Tab',
                text=text,
                yes_action=yes_action, default=False, abort_on=[tab.destroyed])
        else:
            yes_action()
Exemplo n.º 19
0
def feature_permission(url,
                       option,
                       msg,
                       yes_action,
                       no_action,
                       abort_on,
                       blocking=False):
    """Handle a feature permission request.

    Args:
        url: The URL the request was done for.
        option: An option name to check.
        msg: A string like "show notifications"
        yes_action: A callable to call if the request was approved
        no_action: A callable to call if the request was denied
        abort_on: A list of signals which interrupt the question.
        blocking: If True, ask a blocking question.

    Return:
        The Question object if a question was asked (and blocking=False),
        None otherwise.
    """
    config_val = config.instance.get(option, url=url)
    if config_val == 'ask':
        if url.isValid():
            urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
            text = "Allow the website at <b>{}</b> to {}?".format(
                html.escape(url.toDisplayString()), msg)
        else:
            urlstr = None
            option = None  # For message.ask/confirm_async
            text = "Allow the website to {}?".format(msg)

        if blocking:
            answer = message.ask(abort_on=abort_on,
                                 title='Permission request',
                                 text=text,
                                 url=urlstr,
                                 option=option,
                                 mode=usertypes.PromptMode.yesno)
            if answer:
                yes_action()
            else:
                no_action()
            return None
        else:
            return message.confirm_async(yes_action=yes_action,
                                         no_action=no_action,
                                         cancel_action=no_action,
                                         abort_on=abort_on,
                                         title='Permission request',
                                         text=text,
                                         url=urlstr,
                                         option=option)
    elif config_val:
        yes_action()
        return None
    else:
        no_action()
        return None
Exemplo n.º 20
0
def feature_permission(url, option, msg, yes_action, no_action, abort_on):
    """Handle a feature permission request.

    Args:
        url: The URL the request was done for.
        option: A (section, option) tuple for the option to check.
        msg: A string like "show notifications"
        yes_action: A callable to call if the request was approved
        no_action: A callable to call if the request was denied
        abort_on: A list of signals which interrupt the question.

    Return:
        The Question object if a question was asked, None otherwise.
    """
    config_val = config.get(*option)
    if config_val == 'ask':
        if url.isValid():
            text = "Allow the website at <b>{}</b> to {}?".format(
                html.escape(url.toDisplayString()), msg)
        else:
            text = "Allow the website to {}?".format(msg)

        return message.confirm_async(
            yes_action=yes_action, no_action=no_action,
            cancel_action=no_action, abort_on=abort_on,
            title='Permission request', text=text)
    elif config_val:
        yes_action()
        return None
    else:
        no_action()
        return None
Exemplo n.º 21
0
def feature_permission(url, option, msg, yes_action, no_action, abort_on):
    """Handle a feature permission request.

    Args:
        url: The URL the request was done for.
        option: A (section, option) tuple for the option to check.
        msg: A string like "show notifications"
        yes_action: A callable to call if the request was approved
        no_action: A callable to call if the request was denied
        abort_on: A list of signals which interrupt the question.

    Return:
        The Question object if a question was asked, None otherwise.
    """
    config_val = config.get(*option)
    if config_val == 'ask':
        if url.isValid():
            text = "Allow the website at <b>{}</b> to {}?".format(
                html.escape(url.toDisplayString()), msg)
        else:
            text = "Allow the website to {}?".format(msg)

        return message.confirm_async(yes_action=yes_action,
                                     no_action=no_action,
                                     cancel_action=no_action,
                                     abort_on=abort_on,
                                     title='Permission request',
                                     text=text)
    elif config_val:
        yes_action()
        return None
    else:
        no_action()
        return None
Exemplo n.º 22
0
    def add(self, url, name):
        if not name:
            message.error("Can't set mark with empty name!")
            return
        if not url:
            message.error("Can't set mark with empty URL!")
            return

        def set_mark():
            """Really set the quickmark."""
            self.buku.add_rec(url,
                              title_in=name,
                              tags_in="qutebrowser,quickmark",
                              fetch=False)
            log.misc.debug("Added quickmark {} for {}".format(name, url))

        if name in self.marks:
            message.confirm_async(title="Override existing quickmark?",
                                  yes_action=set_mark,
                                  default=True,
                                  url=url)
        else:
            set_mark()
Exemplo n.º 23
0
    def on_feature_permission_requested(self, frame, feature):
        """Ask the user for approval for geolocation/notifications."""
        if not isinstance(frame, QWebFrame):  # pragma: no cover
            # This makes no sense whatsoever, but someone reported this being
            # called with a QBuffer...
            log.misc.error("on_feature_permission_requested got called with "
                           "{!r}!".format(frame))
            return

        options = {
            QWebPage.Notifications: ('content', 'notifications'),
            QWebPage.Geolocation: ('content', 'geolocation'),
        }
        config_val = config.get(*options[feature])
        if config_val == 'ask':
            msgs = {
                QWebPage.Notifications: 'show notifications',
                QWebPage.Geolocation: 'access your location',
            }

            host = frame.url().host()
            if host:
                text = "Allow the website at <b>{}</b> to {}?".format(
                    html.escape(frame.url().toDisplayString()), msgs[feature])
            else:
                text = "Allow the website to {}?".format(msgs[feature])

            yes_action = functools.partial(
                self.setFeaturePermission, frame, feature,
                QWebPage.PermissionGrantedByUser)
            no_action = functools.partial(
                self.setFeaturePermission, frame, feature,
                QWebPage.PermissionDeniedByUser)

            question = message.confirm_async(yes_action=yes_action,
                                             no_action=no_action,
                                             cancel_action=no_action,
                                             abort_on=[self.shutting_down,
                                                       self.loadStarted],
                                             title='Permission request',
                                             text=text)
            self.featurePermissionRequestCanceled.connect(
                functools.partial(self.on_feature_permission_cancelled,
                                  question, frame, feature))
        elif config_val:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionGrantedByUser)
        else:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionDeniedByUser)
Exemplo n.º 24
0
    def on_feature_permission_requested(self, frame, feature):
        """Ask the user for approval for geolocation/notifications."""
        if not isinstance(frame, QWebFrame):  # pragma: no cover
            # This makes no sense whatsoever, but someone reported this being
            # called with a QBuffer...
            log.misc.error("on_feature_permission_requested got called with "
                           "{!r}!".format(frame))
            return

        options = {
            QWebPage.Notifications: ('content', 'notifications'),
            QWebPage.Geolocation: ('content', 'geolocation'),
        }
        config_val = config.get(*options[feature])
        if config_val == 'ask':
            msgs = {
                QWebPage.Notifications: 'show notifications',
                QWebPage.Geolocation: 'access your location',
            }

            host = frame.url().host()
            if host:
                text = "Allow the website at <b>{}</b> to {}?".format(
                    html.escape(frame.url().toDisplayString()), msgs[feature])
            else:
                text = "Allow the website to {}?".format(msgs[feature])

            yes_action = functools.partial(self.setFeaturePermission, frame,
                                           feature,
                                           QWebPage.PermissionGrantedByUser)
            no_action = functools.partial(self.setFeaturePermission, frame,
                                          feature,
                                          QWebPage.PermissionDeniedByUser)

            question = message.confirm_async(
                yes_action=yes_action,
                no_action=no_action,
                cancel_action=no_action,
                abort_on=[self.shutting_down, self.loadStarted],
                title='Permission request',
                text=text)
            self.featurePermissionRequestCanceled.connect(
                functools.partial(self.on_feature_permission_cancelled,
                                  question, frame, feature))
        elif config_val:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionGrantedByUser)
        else:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionDeniedByUser)
Exemplo n.º 25
0
def feature_permission(url, option, msg, yes_action, no_action, abort_on,
                       blocking=False):
    """Handle a feature permission request.

    Args:
        url: The URL the request was done for.
        option: An option name to check.
        msg: A string like "show notifications"
        yes_action: A callable to call if the request was approved
        no_action: A callable to call if the request was denied
        abort_on: A list of signals which interrupt the question.
        blocking: If True, ask a blocking question.

    Return:
        The Question object if a question was asked (and blocking=False),
        None otherwise.
    """
    config_val = config.instance.get(option, url=url)
    if config_val == 'ask':
        if url.isValid():
            urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
            text = "Allow the website at <b>{}</b> to {}?".format(
                html.escape(url.toDisplayString()), msg)
        else:
            urlstr = None
            text = "Allow the website to {}?".format(msg)

        if blocking:
            answer = message.ask(abort_on=abort_on, title='Permission request',
                                 text=text, url=urlstr,
                                 mode=usertypes.PromptMode.yesno)
            if answer:
                yes_action()
            else:
                no_action()
            return None
        else:
            return message.confirm_async(
                yes_action=yes_action, no_action=no_action,
                cancel_action=no_action, abort_on=abort_on,
                title='Permission request', text=text, url=urlstr)
    elif config_val:
        yes_action()
        return None
    else:
        no_action()
        return None
Exemplo n.º 26
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
Exemplo n.º 27
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