Esempio n. 1
0
    def _ask(self, text, mode, owner=None):
        """Ask a blocking question in the statusbar.

        Args:
            text: The text to display to the user.
            mode: A PromptMode.
            owner: An object which will abort the question if destroyed, or
                   None.

        Return:
            The answer the user gave or None if the prompt was cancelled.
        """
        q = usertypes.Question()
        q.text = text
        q.mode = mode
        self.shutting_down.connect(q.abort)
        if owner is not None:
            owner.destroyed.connect(q.abort)

        # This might be a generic network manager, e.g. one belonging to a
        # DownloadManager. In this case, just skip the webview thing.
        if self._tab_id is not None:
            tab = objreg.get('tab',
                             scope='tab',
                             window=self._win_id,
                             tab=self._tab_id)
            tab.load_started.connect(q.abort)
        bridge = objreg.get('message-bridge',
                            scope='window',
                            window=self._win_id)
        bridge.ask(q, blocking=True)
        q.deleteLater()
        return q.answer
Esempio n. 2
0
    def fetch(self, reply):
        """Download a QNetworkReply to disk.

        Args:
            reply: The QNetworkReply to download.
        """
        _inline, suggested_filename = http.parse_content_disposition(reply)
        log.downloads.debug("fetch: {} -> {}".format(reply.url(),
                                                     suggested_filename))

        download = DownloadItem(reply, self)
        download.finished.connect(
            functools.partial(self.on_finished, download))
        download.data_changed.connect(
            functools.partial(self.on_data_changed, download))
        download.error.connect(self.on_error)
        download.basename = suggested_filename
        idx = len(self.downloads) + 1
        self.beginInsertRows(QModelIndex(), idx, idx)
        self.downloads.append(download)
        self.endInsertRows()

        q = usertypes.Question(self)
        q.text = "Save file to:"
        q.mode = usertypes.PromptMode.text
        q.default = suggested_filename
        q.answered.connect(download.set_filename)
        q.cancelled.connect(download.cancel)
        q.completed.connect(q.deleteLater)
        q.destroyed.connect(functools.partial(self.questions.remove, q))
        self.questions.append(q)
        download.cancelled.connect(q.abort)
        message_bridge = objreg.get('message-bridge', scope='window',
                                    window=self._win_id)
        message_bridge.ask(q, blocking=False)
Esempio n. 3
0
    def _ask(self, text, mode, owner=None):
        """Ask a blocking question in the statusbar.

        Args:
            text: The text to display to the user.
            mode: A PromptMode.
            owner: An object which will abort the question if destroyed, or
                   None.

        Return:
            The answer the user gave or None if the prompt was cancelled.
        """
        q = usertypes.Question()
        q.text = text
        q.mode = mode
        self.shutting_down.connect(q.abort)
        if owner is not None:
            owner.destroyed.connect(q.abort)
        webview = objreg.get('webview',
                             scope='tab',
                             window=self._win_id,
                             tab=self._tab_id)
        webview.loadStarted.connect(q.abort)
        bridge = objreg.get('message-bridge',
                            scope='window',
                            window=self._win_id)
        bridge.ask(q, blocking=True)
        q.deleteLater()
        return q.answer
Esempio n. 4
0
def _build_question(title: str,
                    text: str = None,
                    *,
                    mode: usertypes.PromptMode,
                    default: Union[None, bool, str] = None,
                    abort_on: Iterable[pyqtBoundSignal] = (),
                    url: str = None,
                    option: bool = None) -> usertypes.Question:
    """Common function for ask/ask_async."""
    question = usertypes.Question()
    question.title = title
    question.text = text
    question.mode = mode
    question.default = default
    question.url = url

    if option is not None:
        if mode != usertypes.PromptMode.yesno:
            raise ValueError("Can only 'option' with PromptMode.yesno")
        if url is None:
            raise ValueError("Need 'url' given when 'option' is given")
    question.option = option

    for sig in abort_on:
        sig.connect(question.abort)
    return question
Esempio n. 5
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':
            bridge = objreg.get('message-bridge',
                                scope='window',
                                window=self._win_id)
            q = usertypes.Question(bridge)
            q.mode = usertypes.PromptMode.yesno

            msgs = {
                QWebPage.Notifications: 'show notifications',
                QWebPage.Geolocation: 'access your location',
            }

            host = frame.url().host()
            if host:
                q.text = "Allow the website at {} to {}?".format(
                    frame.url().host(), msgs[feature])
            else:
                q.text = "Allow the website to {}?".format(msgs[feature])

            yes_action = functools.partial(self.setFeaturePermission, frame,
                                           feature,
                                           QWebPage.PermissionGrantedByUser)
            q.answered_yes.connect(yes_action)

            no_action = functools.partial(self.setFeaturePermission, frame,
                                          feature,
                                          QWebPage.PermissionDeniedByUser)
            q.answered_no.connect(no_action)
            q.cancelled.connect(no_action)

            self.shutting_down.connect(q.abort)
            q.completed.connect(q.deleteLater)

            self.featurePermissionRequestCanceled.connect(
                functools.partial(self.on_feature_permission_cancelled, q,
                                  frame, feature))
            self.loadStarted.connect(q.abort)

            bridge.ask(q, blocking=False)
        elif config_val:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionGrantedByUser)
        else:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionDeniedByUser)
Esempio n. 6
0
 def _prepare_question(self):
     """Prepare a Question object to be asked."""
     q = usertypes.Question(self)
     q.text = "Save file to:"
     q.mode = usertypes.PromptMode.text
     q.completed.connect(q.deleteLater)
     q.destroyed.connect(functools.partial(self.questions.remove, q))
     self.questions.append(q)
     return q
Esempio n. 7
0
    def fetch(self, reply, fileobj=None, filename=None):
        """Download a QNetworkReply to disk.

        Args:
            reply: The QNetworkReply to download.
            fileobj: The file object to write the answer to.
            filename: A path to write the data to.

        Return:
            The created DownloadItem.
        """
        if fileobj is not None and filename is not None:
            raise TypeError("Only one of fileobj/filename may be given!")
        if filename is not None:
            suggested_filename = os.path.basename(filename)
        elif fileobj is not None and getattr(fileobj, 'name', None):
            suggested_filename = fileobj.name
        else:
            _inline, suggested_filename = http.parse_content_disposition(reply)
        log.downloads.debug("fetch: {} -> {}".format(reply.url(),
                                                     suggested_filename))
        download = DownloadItem(reply, self)
        download.finished.connect(functools.partial(self.on_finished,
                                                    download))
        download.data_changed.connect(
            functools.partial(self.on_data_changed, download))
        download.error.connect(self.on_error)
        download.redirected.connect(
            functools.partial(self.on_redirect, download))
        download.basename = suggested_filename
        idx = len(self.downloads) + 1
        self.beginInsertRows(QModelIndex(), idx, idx)
        self.downloads.append(download)
        self.endInsertRows()

        if filename is not None:
            download.set_filename(filename)
        elif fileobj is not None:
            download.set_fileobj(fileobj)
            download.autoclose = False
        else:
            q = usertypes.Question(self)
            q.text = "Save file to:"
            q.mode = usertypes.PromptMode.text
            q.default = suggested_filename
            q.answered.connect(download.set_filename)
            q.cancelled.connect(download.cancel)
            q.completed.connect(q.deleteLater)
            q.destroyed.connect(functools.partial(self.questions.remove, q))
            self.questions.append(q)
            download.cancelled.connect(q.abort)
            message_bridge = objreg.get('message-bridge',
                                        scope='window',
                                        window=self._win_id)
            message_bridge.ask(q, blocking=False)

        return download
Esempio n. 8
0
 def _ask_overwrite_question(self):
     """Create a Question object to be asked."""
     q = usertypes.Question(self)
     q.text = self._filename + " already exists. Overwrite? (y/n)"
     q.mode = usertypes.PromptMode.yesno
     q.answered_yes.connect(self._create_fileobj)
     q.answered_no.connect(functools.partial(self.cancel, False))
     q.cancelled.connect(functools.partial(self.cancel, False))
     message_bridge = objreg.get('message-bridge', scope='window',
                                 window=self._win_id)
     message_bridge.ask(q, blocking=False)
Esempio n. 9
0
 def _ask_confirm_question(self, title, msg):
     no_action = functools.partial(self.cancel, remove_data=False)
     question = usertypes.Question()
     question.title = title
     question.text = msg
     question.mode = usertypes.PromptMode.yesno
     question.answered_yes.connect(self._after_set_filename)
     question.answered_no.connect(no_action)
     question.cancelled.connect(no_action)
     self.cancelled.connect(question.abort)
     self.error.connect(question.abort)
     message.global_bridge.ask(question, blocking=True)
Esempio n. 10
0
def _build_question(title, text=None, *, mode, default=None, abort_on=()):
    """Common function for ask/ask_async."""
    if not isinstance(mode, usertypes.PromptMode):
        raise TypeError("Mode {} is no PromptMode member!".format(mode))
    question = usertypes.Question()
    question.title = title
    question.text = text
    question.mode = mode
    question.default = default
    for sig in abort_on:
        sig.connect(question.abort)
    return question
Esempio n. 11
0
def alert(win_id, message):
    """Display an alert which needs to be confirmed.

    Args:
        win_id: The ID of the window which is calling this function.
        message: The message to show.
    """
    q = usertypes.Question()
    q.text = message
    q.mode = usertypes.PromptMode.alert
    _wrapper(win_id, 'ask', q, blocking=True)
    q.deleteLater()
Esempio n. 12
0
        def _get_prompt_func(path):
            question = usertypes.Question()
            question.title = "test"
            question.default = path

            prompt = promptmod.DownloadFilenamePrompt(question)
            qtbot.add_widget(prompt)
            with qtbot.wait_signal(prompt._file_model.directoryLoaded):
                pass
            assert prompt._lineedit.text() == path

            return prompt
Esempio n. 13
0
def start_download_checked(target, tab):
    """First check if dest is already a file, then start the download.

    Args:
        target: The DownloadTarget where the resulting file should be saved.
        tab: Specify the tab whose page should be loaded.
    """
    if not isinstance(target, downloads.FileDownloadTarget):
        _start_download(target, tab)
        return
    # The default name is 'page title.mhtml'
    title = tab.title()
    default_name = utils.sanitize_filename(title + '.mhtml')

    # Remove characters which cannot be expressed in the file system encoding
    encoding = sys.getfilesystemencoding()
    default_name = utils.force_encoding(default_name, encoding)
    dest = utils.force_encoding(target.filename, encoding)

    dest = os.path.expanduser(dest)

    # See if we already have an absolute path
    path = downloads.create_full_filename(default_name, dest)
    if path is None:
        # We still only have a relative path, prepend download_dir and
        # try again.
        path = downloads.create_full_filename(
            default_name, os.path.join(downloads.download_dir(), dest))
    downloads.last_used_directory = os.path.dirname(path)

    # Avoid downloading files if we can't save the output anyway...
    # Yes, this is prone to race conditions, but we're checking again before
    # saving the file anyway.
    if not os.path.isdir(os.path.dirname(path)):
        folder = os.path.dirname(path)
        message.error("Directory {} does not exist.".format(folder))
        return

    target = downloads.FileDownloadTarget(path)
    if not os.path.isfile(path):
        _start_download(target, tab=tab)
        return

    q = usertypes.Question()
    q.mode = usertypes.PromptMode.yesno
    q.title = "Overwrite existing file?"
    q.text = "<b>{}</b> already exists. Overwrite?".format(
        html.escape(path))
    q.completed.connect(q.deleteLater)
    q.answered_yes.connect(functools.partial(
        _start_download, target, tab=tab))
    message.global_bridge.ask(q, blocking=False)
Esempio n. 14
0
 def _ask_confirm_question(self, msg):
     """Create a Question object to be asked."""
     q = usertypes.Question(self)
     q.text = msg
     q.mode = usertypes.PromptMode.yesno
     q.answered_yes.connect(self._create_fileobj)
     q.answered_no.connect(functools.partial(self.cancel, False))
     q.cancelled.connect(functools.partial(self.cancel, False))
     self.cancelled.connect(q.abort)
     self.error.connect(q.abort)
     message_bridge = objreg.get('message-bridge', scope='window',
                                 window=self._win_id)
     message_bridge.ask(q, blocking=False)
Esempio n. 15
0
    def on_feature_permission_requested(self, frame, feature):
        """Ask the user for approval for geolocation/notifications."""
        options = {
            QWebPage.Notifications: ('content', 'notifications'),
            QWebPage.Geolocation: ('content', 'geolocation'),
        }
        config_val = config.get(*options[feature])
        if config_val == 'ask':
            bridge = objreg.get('message-bridge',
                                scope='window',
                                window=self._win_id)
            q = usertypes.Question(bridge)
            q.mode = usertypes.PromptMode.yesno

            msgs = {
                QWebPage.Notifications: 'show notifications',
                QWebPage.Geolocation: 'access your location',
            }

            host = frame.url().host()
            if host:
                q.text = "Allow the website at {} to {}?".format(
                    frame.url().host(), msgs[feature])
            else:
                q.text = "Allow the website to {}?".format(msgs[feature])

            yes_action = functools.partial(self.setFeaturePermission, frame,
                                           feature,
                                           QWebPage.PermissionGrantedByUser)
            q.answered_yes.connect(yes_action)

            no_action = functools.partial(self.setFeaturePermission, frame,
                                          feature,
                                          QWebPage.PermissionDeniedByUser)
            q.answered_no.connect(no_action)
            q.cancelled.connect(no_action)

            q.completed.connect(q.deleteLater)

            self.featurePermissionRequestCanceled.connect(
                functools.partial(self.on_feature_permission_cancelled, q,
                                  frame, feature))
            self.loadStarted.connect(q.abort)

            bridge.ask(q, blocking=False)
        elif config_val:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionGrantedByUser)
        else:
            self.setFeaturePermission(frame, feature,
                                      QWebPage.PermissionDeniedByUser)
Esempio n. 16
0
def start_download_checked(dest, web_view):
    """First check if dest is already a file, then start the download.

    Args:
        dest: The filename where the resulting file should be saved.
        web_view: Specify the webview whose page should be loaded.
    """
    # The default name is 'page title.mht'
    title = web_view.title()
    default_name = utils.sanitize_filename(title + '.mht')

    # Remove characters which cannot be expressed in the file system encoding
    encoding = sys.getfilesystemencoding()
    default_name = utils.force_encoding(default_name, encoding)
    dest = utils.force_encoding(dest, encoding)

    dest = os.path.expanduser(dest)

    # See if we already have an absolute path
    path = downloads.create_full_filename(default_name, dest)
    if path is None:
        # We still only have a relative path, prepend download_dir and
        # try again.
        path = downloads.create_full_filename(
            default_name, os.path.join(downloads.download_dir(), dest))
    downloads.last_used_directory = os.path.dirname(path)

    # Avoid downloading files if we can't save the output anyway...
    # Yes, this is prone to race conditions, but we're checking again before
    # saving the file anyway.
    if not os.path.isdir(os.path.dirname(path)):
        folder = os.path.dirname(path)
        message.error(web_view.win_id,
                      "Directory {} does not exist.".format(folder))
        return

    if not os.path.isfile(path):
        _start_download(path, web_view=web_view)
        return

    q = usertypes.Question()
    q.mode = usertypes.PromptMode.yesno
    q.text = "{} exists. Overwrite?".format(path)
    q.completed.connect(q.deleteLater)
    q.answered_yes.connect(
        functools.partial(_start_download, path, web_view=web_view))
    message_bridge = objreg.get('message-bridge',
                                scope='window',
                                window=web_view.win_id)
    message_bridge.ask(q, blocking=False)
Esempio n. 17
0
 def _ask_create_parent_question(self, title, msg,
                                 force_overwrite, remember_directory):
     no_action = functools.partial(self.cancel, remove_data=False)
     question = usertypes.Question()
     question.title = title
     question.text = msg
     question.mode = usertypes.PromptMode.yesno
     question.answered_yes.connect(lambda:
                                   self._after_create_parent_question(
                                       force_overwrite, remember_directory))
     question.answered_no.connect(no_action)
     question.cancelled.connect(no_action)
     self.cancelled.connect(question.abort)
     self.error.connect(question.abort)
     message.global_bridge.ask(question, blocking=True)
Esempio n. 18
0
def ask_for_filename(suggested_filename,
                     win_id,
                     *,
                     parent=None,
                     prompt_download_directory=None):
    """Prepare a question for a download-path.

    If a filename can be determined directly, it is returned instead.

    Returns a (filename, question)-namedtuple, in which one component is
    None. filename is a string, question is a usertypes.Question. The
    question has a special .ask() method that takes no arguments for
    convenience, as this function does not yet ask the question, it
    only prepares it.

    Args:
        suggested_filename: The "default"-name that is pre-entered as path.
        win_id: The window where the question will be asked.
        parent: The parent of the question (a QObject).
        prompt_download_directory: If this is something else than None, it
                                   will overwrite the
                                   storage->prompt-download-directory setting.
    """
    if prompt_download_directory is None:
        prompt_download_directory = config.get('storage',
                                               'prompt-download-directory')

    if not prompt_download_directory:
        return DownloadPath(filename=download_dir(), question=None)

    encoding = sys.getfilesystemencoding()
    suggested_filename = utils.force_encoding(suggested_filename, encoding)

    q = usertypes.Question(parent)
    q.text = "Save file to:"
    q.mode = usertypes.PromptMode.text
    q.completed.connect(q.deleteLater)
    q.default = path_suggestion(suggested_filename)

    message_bridge = objreg.get('message-bridge',
                                scope='window',
                                window=win_id)
    q.ask = lambda: message_bridge.ask(q, blocking=False)
    return DownloadPath(filename=None, question=q)
Esempio n. 19
0
def ask(win_id, message, mode, default=None):
    """Ask a modular question in the statusbar (blocking).

    Args:
        win_id: The ID of the window which is calling this function.
        message: The message to display to the user.
        mode: A PromptMode.
        default: The default value to display.

    Return:
        The answer the user gave or None if the prompt was cancelled.
    """
    q = usertypes.Question()
    q.text = message
    q.mode = mode
    q.default = default
    _get_bridge(win_id).ask(q, blocking=True)
    q.deleteLater()
    return q.answer
Esempio n. 20
0
def get_filename_question(*, suggested_filename, url, parent=None):
    """Get a Question object for a download-path.

    Args:
        suggested_filename: The "default"-name that is pre-entered as path.
        url: The URL the download originated from.
        parent: The parent of the question (a QObject).
    """
    encoding = sys.getfilesystemencoding()
    suggested_filename = utils.force_encoding(suggested_filename, encoding)

    q = usertypes.Question(parent)
    q.title = "Save file to:"
    q.text = "Please enter a location for <b>{}</b>".format(
        html.escape(url.toDisplayString()))
    q.mode = usertypes.PromptMode.download
    q.completed.connect(q.deleteLater)
    q.default = _path_suggestion(suggested_filename)
    return q
Esempio n. 21
0
def confirm_async(win_id, message, yes_action, no_action=None, default=None):
    """Ask a yes/no question to the user and execute the given actions.

    Args:
        win_id: The ID of the window which is calling this function.
        message: The message to display to the user.
        yes_action: Callable to be called when the user answered yes.
        no_action: Callable to be called when the user answered no.
        default: True/False to set a default value, or None.
    """
    q = usertypes.Question()
    q.text = message
    q.mode = usertypes.PromptMode.yesno
    q.default = default
    q.answered_yes.connect(yes_action)
    if no_action is not None:
        q.answered_no.connect(no_action)
    q.completed.connect(q.deleteLater)
    _wrapper(win_id, 'ask', q, blocking=False)
Esempio n. 22
0
def ask_async(win_id, message, mode, handler, default=None):
    """Ask an async question in the statusbar.

    Args:
        win_id: The ID of the window which is calling this function.
        message: The message to display to the user.
        mode: A PromptMode.
        handler: The function to get called with the answer as argument.
        default: The default value to display.
    """
    if not isinstance(mode, usertypes.PromptMode):
        raise TypeError("Mode {} is no PromptMode member!".format(mode))
    q = usertypes.Question()
    q.text = message
    q.mode = mode
    q.default = default
    q.answered.connect(handler)
    q.completed.connect(q.deleteLater)
    _wrapper(win_id, 'ask', q, blocking=False)
Esempio n. 23
0
def _build_question(title, text=None, *, mode, default=None, abort_on=(),
                    url=None, option=None):
    """Common function for ask/ask_async."""
    if not isinstance(mode, usertypes.PromptMode):
        raise TypeError("Mode {} is no PromptMode member!".format(mode))
    question = usertypes.Question()
    question.title = title
    question.text = text
    question.mode = mode
    question.default = default
    question.url = url

    if option is not None:
        if mode != usertypes.PromptMode.yesno:
            raise ValueError("Can only 'option' with PromptMode.yesno")
        if url is None:
            raise ValueError("Need 'url' given when 'option' is given")
    question.option = option

    for sig in abort_on:
        sig.connect(question.abort)
    return question
Esempio n. 24
0
    def _ask(self, text, mode, default=None):
        """Ask a blocking question in the statusbar.

        Args:
            text: The text to display to the user.
            mode: A PromptMode.
            default: The default value to display.

        Return:
            The answer the user gave or None if the prompt was cancelled.
        """
        q = usertypes.Question()
        q.text = text
        q.mode = mode
        q.default = default
        self.loadStarted.connect(q.abort)
        self.shutting_down.connect(q.abort)
        bridge = objreg.get('message-bridge', scope='window',
                            window=self._win_id)
        bridge.ask(q, blocking=True)
        q.deleteLater()
        return q.answer
Esempio n. 25
0
def question():
    return usertypes.Question()