def _start_cb(self, elems): """Initialize the elements and labels based on the context set.""" filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True) elems = [e for e in elems if filterfunc(e)] if not elems: message.error(self._win_id, "No elements found.", immediately=True) return strings = self._hint_strings(elems) log.hints.debug("hints: {}".format(', '.join(strings))) for elem, string in zip(elems, strings): label = HintLabel(elem, self._context) label.update_text('', string) self._context.all_labels.append(label) self._context.labels[string] = label keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) keyparser = keyparsers[usertypes.KeyMode.hint] keyparser.update_bindings(strings) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.set_text(self._get_text()) modeman.enter(self._win_id, usertypes.KeyMode.hint, 'HintManager.start') # to make auto-follow == 'always' work self._handle_auto_follow()
def _open_startpage(self, win_id=None): """Open startpage. The startpage is never opened if the given windows are not empty. Args: win_id: If None, open startpage in all empty windows. If set, open the startpage in the given window. """ if win_id is not None: window_ids = [win_id] else: window_ids = objreg.window_registry for cur_win_id in window_ids: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=cur_win_id) if tabbed_browser.count() == 0: log.init.debug("Opening startpage") for urlstr in config.get('general', 'startpage'): try: url = urlutils.fuzzy_url(urlstr) except urlutils.FuzzyUrlError as e: message.error(0, "Error when opening startpage: " "{}".format(e)) tabbed_browser.tabopen(QUrl('about:blank')) else: tabbed_browser.tabopen(url)
def _parse_line(self, line): try: key, url = line.rsplit(maxsplit=1) except ValueError: message.error('current', "Invalid quickmark '{}'".format(line)) else: self.marks[key] = url
def on_proc_closed(self, exitcode, exitstatus): """Write the editor text into the form field and clean up tempfile. Callback for QProcess when the editor was closed. """ log.procs.debug("Editor closed") if exitstatus != QProcess.NormalExit: # No error/cleanup here, since we already handle this in # on_proc_error return try: if exitcode != 0: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error( self._win_id, "Editor did quit abnormally (status " "{})!".format(exitcode)) return encoding = config.get('general', 'editor-encoding') with open(self._filename, 'r', encoding=encoding) as f: text = ''.join(f.readlines()) log.procs.debug("Read back: {}".format(text)) self.editing_finished.emit(text) finally: self._cleanup()
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()
def edit(self, text): """Edit a given text. Args: text: The initial text to edit. """ if self._text is not None: raise ValueError("Already editing a file!") self._text = text try: self._oshandle, self._filename = tempfile.mkstemp( text=True, prefix='qutebrowser-editor-') if text: encoding = config.get('general', 'editor-encoding') with open(self._filename, 'w', encoding=encoding) as f: f.write(text) # pragma: no branch except OSError as e: message.error(self._win_id, "Failed to create initial file: " "{}".format(e)) return self._proc = guiprocess.GUIProcess(self._win_id, what='editor', parent=self) self._proc.finished.connect(self.on_proc_closed) self._proc.error.connect(self.on_proc_error) editor = config.get('general', 'editor') executable = editor[0] args = [self._filename if arg == '{}' else arg for arg in editor[1:]] log.procs.debug("Calling \"{}\" with args {}".format(executable, args)) self._proc.start(executable, args)
def on_proc_closed(self, exitcode, exitstatus): """Write the editor text into the form field and clean up tempfile. Callback for QProcess when the editor was closed. """ log.procs.debug("Editor closed") if exitstatus != QProcess.NormalExit: # No error/cleanup here, since we already handle this in # on_proc_error. return try: if exitcode != 0: return encoding = config.get('general', 'editor-encoding') try: with open(self._filename, 'r', encoding=encoding) as f: text = f.read() # pragma: no branch except OSError as e: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error(self._win_id, "Failed to read back edited file: " "{}".format(e)) return log.procs.debug("Read back: {}".format(text)) self.editing_finished.emit(text) finally: self._cleanup()
def _load_session(name): """Load the default session. Args: name: The name of the session to load, or None to read state file. """ state_config = objreg.get('state-config') if name is None: try: name = state_config['general']['session'] except KeyError: # No session given as argument and none in the session file -> # start without loading a session return session_manager = objreg.get('session-manager') try: session_manager.load(name) except sessions.SessionNotFoundError: message.error("Session {} not found!".format(name)) except sessions.SessionError as e: message.error("Failed to load session {}: {}".format(name, e)) try: del state_config['general']['session'] except KeyError: pass # If this was a _restart session, delete it. if name == '_restart': session_manager.delete('_restart')
def message_error(text): """Show an error message in the statusbar. Args: text: The text to show. """ message.error(text)
def qute_help(url): """Handler for qute:help.""" try: utils.read_file('html/doc/index.html') except OSError: html = jinja.render( 'error.html', title="Error while loading documentation", url=url.toDisplayString(), error="This most likely means the documentation was not generated " "properly. If you are running qutebrowser from the git " "repository, please run scripts/asciidoc2html.py. " "If you're running a released version this is a bug, please " "use :report to report it.", icon='', qutescheme=True) return 'text/html', html urlpath = url.path() if not urlpath or urlpath == '/': urlpath = 'index.html' else: urlpath = urlpath.lstrip('/') if not docutils.docs_up_to_date(urlpath): message.error("Your documentation is outdated! Please re-run " "scripts/asciidoc2html.py.") path = 'html/doc/{}'.format(urlpath) if urlpath.endswith('.png'): return 'image/png', utils.read_file(path, binary=True) else: data = utils.read_file(path) return 'text/html', data
def _process_args(args): """Open startpage etc. and process commandline args.""" config_obj = objreg.get('config') for sect, opt, val in args.temp_settings: try: config_obj.set('temp', sect, opt, val) except (configexc.Error, configparser.Error) as e: message.error("set: {} - {}".format(e.__class__.__name__, e)) if not args.override_restore: _load_session(args.session) session_manager = objreg.get('session-manager') if not session_manager.did_load: log.init.debug("Initializing main window...") window = mainwindow.MainWindow() if not args.nowindow: window.show() qApp.setActiveWindow(window) process_pos_args(args.command) _open_startpage() _open_quickstart(args) delta = datetime.datetime.now() - earlyinit.START_TIME log.init.debug("Init finished after {}s".format(delta.total_seconds()))
def qute_help(win_id, request): """Handler for qute:help. Return HTML content as bytes.""" try: utils.read_file('html/doc/index.html') except FileNotFoundError: html = jinja.env.get_template('error.html').render( title="Error while loading documentation", url=request.url().toDisplayString(), error="This most likely means the documentation was not generated " "properly. If you are running qutebrowser from the git " "repository, please run scripts/asciidoc2html.py." "If you're running a released version this is a bug, please " "use :report to report it.", icon='') return html.encode('UTF-8', errors='xmlcharrefreplace') urlpath = request.url().path() if not urlpath or urlpath == '/': urlpath = 'index.html' else: urlpath = urlpath.lstrip('/') if not docutils.docs_up_to_date(urlpath): message.error(win_id, "Your documentation is outdated! Please re-run " "scripts/asciidoc2html.py.") path = 'html/doc/{}'.format(urlpath) return utils.read_file(path).encode('UTF-8', errors='xmlcharrefreplace')
def search(self, text, flags): """Search for text in the current page. Args: text: The text to search for. flags: The QWebPage::FindFlags. """ log.webview.debug("Searching with text '{}' and flags " "0x{:04x}.".format(text, int(flags))) widget = self.currentWidget() old_scroll_pos = widget.scroll_pos found = widget.findText(text, flags) if not found and not flags & QWebPage.HighlightAllOccurrences and text: message.error(self._win_id, "Text '{}' not found on " "page!".format(text), immediately=True) else: backward = int(flags) & QWebPage.FindBackward def check_scroll_pos(): """Check if the scroll position got smaller and show info.""" if not backward and widget.scroll_pos < old_scroll_pos: message.info(self._win_id, "Search hit BOTTOM, continuing " "at TOP", immediately=True) elif backward and widget.scroll_pos > old_scroll_pos: message.info(self._win_id, "Search hit TOP, continuing at " "BOTTOM", immediately=True) # We first want QWebPage to refresh. QTimer.singleShot(0, check_scroll_pos)
def _hint_strings(self, elems): """Calculate the hint strings for elems. Inspired by Vimium. Args: elems: The elements to get hint strings for. Return: A list of hint strings, in the same order as the elements. """ hint_mode = config.get('hints', 'mode') if hint_mode == 'word': try: return self._word_hinter.hint(elems) except WordHintingError as e: message.error(self._win_id, str(e), immediately=True) # falls back on letter hints if hint_mode == 'number': chars = '0123456789' else: chars = config.get('hints', 'chars') min_chars = config.get('hints', 'min-chars') if config.get('hints', 'scatter') and hint_mode != 'number': return self._hint_scattered(min_chars, chars, elems) else: return self._hint_linear(min_chars, chars, elems)
def fire(self, keystr, force=False): """Fire a completed hint. Args: keystr: The keychain string to follow. force: When True, follow even when auto-follow is false. """ if not (force or config.get('hints', 'auto-follow')): self.handle_partial_key(keystr) self._context.to_follow = keystr return # Handlers which take a QWebElement elem_handlers = { Target.normal: self._click, Target.current: self._click, Target.tab: self._click, Target.tab_fg: self._click, Target.tab_bg: self._click, Target.window: self._click, Target.hover: self._click, # _download needs a QWebElement to get the frame. Target.download: self._download, Target.userscript: self._call_userscript, } # Handlers which take a QUrl url_handlers = { Target.yank: self._yank, Target.yank_primary: self._yank, Target.run: self._run_cmd, Target.fill: self._preset_cmd_text, Target.spawn: self._spawn, } elem = self._context.elems[keystr].elem if elem.webFrame() is None: message.error(self._win_id, "This element has no webframe.", immediately=True) return if self._context.target in elem_handlers: handler = functools.partial(elem_handlers[self._context.target], elem, self._context) elif self._context.target in url_handlers: url = self._resolve_url(elem, self._context.baseurl) if url is None: self._show_url_error() return handler = functools.partial(url_handlers[self._context.target], url, self._context) else: raise ValueError("No suitable handler found!") if not self._context.rapid: modeman.maybe_leave(self._win_id, usertypes.KeyMode.hint, 'followed') else: # Reset filtering self.filter_hints(None) # Undo keystring highlighting for string, elem in self._context.elems.items(): elem.label.setInnerXml(string) handler()
def _on_navigation_request( self, navigation: usertypes.NavigationRequest ) -> None: """Handle common acceptNavigationRequest code.""" url = utils.elide(navigation.url.toDisplayString(), 100) log.webview.debug("navigation request: url {}, type {}, is_main_frame " "{}".format(url, navigation.navigation_type, navigation.is_main_frame)) if not navigation.url.isValid(): # Also a WORKAROUND for missing IDNA 2008 support in QUrl, see # https://bugreports.qt.io/browse/QTBUG-60364 if navigation.navigation_type == navigation.Type.link_clicked: msg = urlutils.get_errstring(navigation.url, "Invalid link clicked") message.error(msg) self.data.open_target = usertypes.ClickTarget.normal log.webview.debug("Ignoring invalid URL {} in " "acceptNavigationRequest: {}".format( navigation.url.toDisplayString(), navigation.url.errorString())) navigation.accepted = False
def on_ssl_errors(self, reply, errors): """Decide if SSL errors should be ignored or not. This slot is called on SSL/TLS errors by the self.sslErrors signal. Args: reply: The QNetworkReply that is encountering the errors. errors: A list of errors. """ ssl_strict = config.get('network', 'ssl-strict') if ssl_strict == 'ask': err_string = '\n'.join('- ' + err.errorString() for err in errors) answer = self._ask(self._win_id, 'SSL errors - continue?\n{}'.format(err_string), mode=usertypes.PromptMode.yesno, owners=(reply,)) if answer: reply.ignoreSslErrors() elif ssl_strict: pass else: for err in errors: # FIXME we might want to use warn here (non-fatal error) # https://github.com/The-Compiler/qutebrowser/issues/114 message.error(self._win_id, 'SSL error: {}'.format(err.errorString())) reply.ignoreSslErrors()
def autosave(self): """Slot used when the configs are auto-saved.""" for (key, saveable) in self.saveables.items(): try: saveable.save(silent=True) except OSError as e: message.error("Failed to auto-save {}: {}".format(key, e))
def edit(self, text, caret_position=0): """Edit a given text. Args: text: The initial text to edit. caret_position: The position of the caret in the text. """ if self._filename is not None: raise ValueError("Already editing a file!") try: # Close while the external process is running, as otherwise systems # with exclusive write access (e.g. Windows) may fail to update # the file from the external editor, see # https://github.com/qutebrowser/qutebrowser/issues/1767 with tempfile.NamedTemporaryFile( mode='w', prefix='qutebrowser-editor-', encoding=config.val.editor.encoding, delete=False) as fobj: if text: fobj.write(text) self._filename = fobj.name except OSError as e: message.error("Failed to create initial file: {}".format(e)) return self._remove_file = True line, column = self._calc_line_and_column(text, caret_position) self._start_editor(line=line, column=column)
def run(self, win_id, args=None, count=None): """Run the command. Note we don't catch CommandError here as it might happen async. Args: win_id: The window ID the command is run in. args: Arguments to the command. count: Command repetition count. """ dbgout = ["command called:", self.name] if args: dbgout.append(str(args)) elif args is None: args = [] if count is not None: dbgout.append("(count={})".format(count)) log.commands.debug(' '.join(dbgout)) try: self.namespace = self.parser.parse_args(args) except argparser.ArgumentParserError as e: message.error('{}: {}'.format(self.name, e), stack=traceback.format_exc()) return except argparser.ArgumentParserExit as e: log.commands.debug("argparser exited with status {}: {}".format( e.status, e)) return self._count = count self._check_prerequisites(win_id) posargs, kwargs = self._get_call_args(win_id) log.commands.debug('Calling {}'.format( debug_utils.format_call(self.handler, posargs, kwargs))) self.handler(*posargs, **kwargs)
def _on_finished(self, code, status): """Show a message when the process finished.""" self._started = False log.procs.debug("Process finished with code {}, status {}.".format( code, status)) encoding = locale.getpreferredencoding(do_setlocale=False) stderr = bytes(self._proc.readAllStandardError()).decode( encoding, 'replace') stdout = bytes(self._proc.readAllStandardOutput()).decode( encoding, 'replace') if status == QProcess.CrashExit: exitinfo = "{} crashed!".format(self._what.capitalize()) message.error(exitinfo) elif status == QProcess.NormalExit and code == 0: exitinfo = "{} exited successfully.".format( self._what.capitalize()) if self.verbose: message.info(exitinfo) else: assert status == QProcess.NormalExit # We call this 'status' here as it makes more sense to the user - # it's actually 'code'. exitinfo = ("{} exited with status {}, see :messages for " "details.").format(self._what.capitalize(), code) message.error(exitinfo) if stdout: log.procs.error("Process stdout:\n" + stdout.strip()) if stderr: log.procs.error("Process stderr:\n" + stderr.strip()) qutescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)
def _set_download_target(self, download, suggested_filename, target): """Set the target for a given download. Args: download: The download to set the filename for. suggested_filename: The suggested filename. target: The usertypes.DownloadTarget for this download. """ if isinstance(target, usertypes.FileObjDownloadTarget): download.set_fileobj(target.fileobj) download.autoclose = False elif isinstance(target, usertypes.FileDownloadTarget): download.set_filename(target.filename) elif isinstance(target, usertypes.OpenFileDownloadTarget): tmp_manager = objreg.get('temporary-downloads') try: fobj = tmp_manager.get_tmpfile(suggested_filename) except OSError as exc: msg = "Download error: {}".format(exc) message.error(self._win_id, msg) download.cancel() return download.finished.connect( functools.partial(self._open_download, download)) download.autoclose = True download.set_fileobj(fobj) else: log.downloads.error("Unknown download target: {}".format(target))
def _move_data(old, new): """Migrate data from an old to a new directory. If the old directory does not exist, the migration is skipped. If the new directory already exists, an error is shown. Return: True if moving succeeded, False otherwise. """ if not os.path.exists(old): return False log.init.debug("Migrating data from {} to {}".format(old, new)) if os.path.exists(new): if not os.path.isdir(new) or os.listdir(new): message.error("Failed to move data from {} as {} is non-empty!" .format(old, new)) return False os.rmdir(new) try: shutil.move(old, new) except OSError as e: message.error("Failed to move data from {} to {}: {}".format( old, new, e)) return False return True
def adblock_update(self, win_id: {'special': 'win_id'}): """Update the adblock block lists.""" self.blocked_hosts = set() self._done_count = 0 urls = config.get('content', 'host-block-lists') download_manager = objreg.get('download-manager', scope='window', window='last-focused') if urls is None: return for url in urls: if url.scheme() == 'file': try: fileobj = open(url.path(), 'rb') except OSError as e: message.error(win_id, "adblock: Error while reading {}: " "{}".format(url.path(), e.strerror)) continue download = FakeDownload(fileobj) self._in_progress.append(download) self.on_download_finished(download) else: fobj = io.BytesIO() fobj.name = 'adblock: ' + url.host() download = download_manager.get(url, fileobj=fobj, auto_remove=True) self._in_progress.append(download) download.finished.connect( functools.partial(self.on_download_finished, download))
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()
def _prevnext_cb(elems): elem = _find_prevnext(prev, elems) word = 'prev' if prev else 'forward' if elem is None: message.error(win_id, "No {} links found!".format(word)) return url = elem.resolve_url(baseurl) if url is None: message.error(win_id, "No {} links found!".format(word)) return qtutils.ensure_valid(url) if window: from qutebrowser.mainwindow import mainwindow new_window = mainwindow.MainWindow() new_window.show() tabbed_browser = objreg.get('tabbed-browser', scope='window', window=new_window.win_id) tabbed_browser.tabopen(url, background=False) elif tab: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) tabbed_browser.tabopen(url, background=background) else: browsertab.openurl(url)
def _on_link_clicked(self, url): log.webview.debug("link clicked: url {}, hint target {}, " "open_target {}".format( url.toDisplayString(), self.data.hint_target, self.data.open_target)) if not url.isValid(): msg = urlutils.get_errstring(url, "Invalid link clicked") message.error(self.win_id, msg) self.data.open_target = usertypes.ClickTarget.normal return False target = self.data.combined_target() if target == usertypes.ClickTarget.normal: return elif target == usertypes.ClickTarget.tab: win_id = self.win_id bg_tab = False elif target == usertypes.ClickTarget.tab_bg: win_id = self.win_id bg_tab = True elif target == usertypes.ClickTarget.window: from qutebrowser.mainwindow import mainwindow window = mainwindow.MainWindow() window.show() win_id = window.win_id bg_tab = False else: raise ValueError("Invalid ClickTarget {}".format(target)) tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) tabbed_browser.tabopen(url, background=bg_tab) self.data.open_target = usertypes.ClickTarget.normal
def _open_startpage(win_id=None): """Open startpage. The startpage is never opened if the given windows are not empty. Args: win_id: If None, open startpage in all empty windows. If set, open the startpage in the given window. """ if win_id is not None: window_ids = [win_id] else: window_ids = objreg.window_registry for cur_win_id in window_ids: tabbed_browser = objreg.get("tabbed-browser", scope="window", window=cur_win_id) if tabbed_browser.count() == 0: log.init.debug("Opening startpage") for urlstr in config.get("general", "startpage"): try: url = urlutils.fuzzy_url(urlstr, do_search=False) except urlutils.InvalidUrlError as e: message.error("current", "Error when opening startpage: " "{}".format(e)) tabbed_browser.tabopen(QUrl("about:blank")) else: tabbed_browser.tabopen(url)
def edit(self, text): """Edit a given text. Args: text: The initial text to edit. """ if self._text is not None: raise ValueError("Already editing a file!") self._text = text encoding = config.get('general', 'editor-encoding') try: # Close while the external process is running, as otherwise systems # with exclusive write access (e.g. Windows) may fail to update # the file from the external editor, see # https://github.com/The-Compiler/qutebrowser/issues/1767 with tempfile.NamedTemporaryFile( mode='w', prefix='qutebrowser-editor-', encoding=encoding, delete=False) as fobj: if text: fobj.write(text) self._file = fobj except OSError as e: message.error("Failed to create initial file: {}".format(e)) return self._proc = guiprocess.GUIProcess(what='editor', parent=self) self._proc.finished.connect(self.on_proc_closed) self._proc.error.connect(self.on_proc_error) editor = config.get('general', 'editor') executable = editor[0] args = [arg.replace('{}', self._file.name) for arg in editor[1:]] log.procs.debug("Calling \"{}\" with args {}".format(executable, args)) self._proc.start(executable, args)
def jump_mark(self, key): """Jump to the mark named by `key`. Args: key: mark identifier; capital indicates a global mark """ # consider urls that differ only in fragment to be identical urlkey = self.current_url().adjusted(QUrl.RemoveFragment) frame = self.currentWidget().page().currentFrame() if key.isupper() and key in self._global_marks: point, url = self._global_marks[key] @pyqtSlot(bool) def callback(ok): if ok: self.cur_load_finished.disconnect(callback) frame.setScrollPosition(point) self.openurl(url, newtab=False) self.cur_load_finished.connect(callback) elif urlkey in self._local_marks and key in self._local_marks[urlkey]: point = self._local_marks[urlkey][key] # save the pre-jump position in the special ' mark # this has to happen after we read the mark, otherwise jump_mark # "'" would just jump to the current position every time self.set_mark("'") frame.setScrollPosition(point) else: message.error(self._win_id, "Mark {} is not set".format(key))
def _set_filename(self, filename, *, force_overwrite=False, remember_directory=True): """Set the filename to save the download to. Args: filename: The full filename to save the download to. None: special value to stop the download. force_overwrite: Force overwriting existing files. remember_directory: If True, remember the directory for future downloads. """ global last_used_directory filename = os.path.expanduser(filename) self._ensure_can_set_filename(filename) self._filename = create_full_filename(self.basename, filename) if self._filename is None: # We only got a filename (without directory) or a relative path # from the user, so we append that to the default directory and # try again. self._filename = create_full_filename( self.basename, os.path.join(download_dir(), filename)) # At this point, we have a misconfigured XDG_DOWNLOAD_DIR, as # download_dir() + filename is still no absolute path. # The config value is checked for "absoluteness", but # ~/.config/user-dirs.dirs may be misconfigured and a non-absolute path # may be set for XDG_DOWNLOAD_DIR if self._filename is None: message.error( "XDG_DOWNLOAD_DIR points to a relative path - please check" " your ~/.config/user-dirs.dirs. The download is saved in" " your home directory.", ) # fall back to $HOME as download_dir self._filename = create_full_filename(self.basename, os.path.expanduser('~')) self.basename = os.path.basename(self._filename) if remember_directory: last_used_directory = os.path.dirname(self._filename) log.downloads.debug("Setting filename to {}".format(filename)) if force_overwrite: self._after_set_filename() elif os.path.isfile(self._filename): # The file already exists, so ask the user if it should be # overwritten. txt = "<b>{}</b> already exists. Overwrite?".format( html.escape(self._filename)) self._ask_confirm_question("Overwrite existing file?", txt) # FIFO, device node, etc. Make sure we want to do this elif (os.path.exists(self._filename) and not os.path.isdir(self._filename)): txt = ("<b>{}</b> already exists and is a special file. Write to " "it anyways?".format(html.escape(self._filename))) self._ask_confirm_question("Overwrite special file?", txt) else: self._after_set_filename()
def print_callback(ok: bool) -> None: """Called when printing finished.""" if not ok: message.error("Printing failed!") diag.deleteLater()
def _on_error(self, msg): """Display error message on download errors.""" message.error("Download error: {}".format(msg))
def try_retry(self): """Try to retry a download and show an error if it's unsupported.""" try: self.retry() except UnsupportedOperationError as e: message.error(str(e))
def _fire(self, keystr): """Fire a completed hint. Args: keystr: The keychain string to follow. """ # Handlers which take a QWebElement elem_handlers = { Target.normal: self._actions.click, Target.current: self._actions.click, Target.tab: self._actions.click, Target.tab_fg: self._actions.click, Target.tab_bg: self._actions.click, Target.window: self._actions.click, Target.hover: self._actions.click, # _download needs a QWebElement to get the frame. Target.download: self._actions.download, Target.userscript: self._actions.call_userscript, } # Handlers which take a QUrl url_handlers = { Target.yank: self._actions.yank, Target.yank_primary: self._actions.yank, Target.run: self._actions.run_cmd, Target.fill: self._actions.preset_cmd_text, Target.spawn: self._actions.spawn, } elem = self._context.labels[keystr].elem if not elem.has_frame(): message.error("This element has no webframe.") return if self._context.target in elem_handlers: handler = functools.partial(elem_handlers[self._context.target], elem, self._context) elif self._context.target in url_handlers: url = elem.resolve_url(self._context.baseurl) if url is None: message.error("No suitable link found for this element.") return handler = functools.partial(url_handlers[self._context.target], url, self._context) if self._context.add_history: objreg.get('web-history').add_url(url, "") else: raise ValueError("No suitable handler found!") if not self._context.rapid: modeman.leave(self._win_id, usertypes.KeyMode.hint, 'followed', maybe=True) else: # Reset filtering self.filter_hints(None) # Undo keystring highlighting for string, label in self._context.labels.items(): label.update_text('', string) try: handler() except HintingError as e: message.error(str(e))
def _handle_sql_errors(self): try: yield except sql.KnownError as e: message.error("Failed to write history: {}".format(e.text()))
def _on_paste_version_err(text): message.error("Failed to pastebin version" " info: {}".format(text)) pbclient.deleteLater()
def _on_paste_version_err(text: str) -> None: assert pbclient is not None message.error("Failed to pastebin version" " info: {}".format(text)) pbclient.deleteLater()
def run_safely(self, text, count=None): """Run a command and display exceptions in the statusbar.""" try: self.run(text, count) except cmdexc.Error as e: message.error(str(e), stack=traceback.format_exc())
def start( self, # pylint: disable=keyword-arg-before-vararg group='all', target=Target.normal, *args, mode=None, add_history=False, rapid=False, first=False): """Start hinting. Args: rapid: Whether to do rapid hinting. With rapid hinting, the hint mode isn't left after a hint is followed, so you can easily open multiple links. This is only possible with targets `tab` (with `tabs.background=true`), `tab-bg`, `window`, `run`, `hover`, `userscript` and `spawn`. add_history: Whether to add the spawned or yanked link to the browsing history. first: Click the first hinted element without prompting. group: The element types to hint. - `all`: All clickable elements. - `links`: Only links. - `images`: Only images. - `inputs`: Only input fields. Custom groups can be added via the `hints.selectors` setting and also used here. target: What to do with the selected element. - `normal`: Open the link. - `current`: Open the link in the current tab. - `tab`: Open the link in a new tab (honoring the `tabs.background` setting). - `tab-fg`: Open the link in a new foreground tab. - `tab-bg`: Open the link in a new background tab. - `window`: Open the link in a new window. - `hover` : Hover over the link. - `yank`: Yank the link to the clipboard. - `yank-primary`: Yank the link to the primary selection. - `run`: Run the argument as command. - `fill`: Fill the commandline with the command given as argument. - `download`: Download the link. - `userscript`: Call a userscript with `$QUTE_URL` set to the link. - `spawn`: Spawn a command. - `delete`: Delete the selected element. mode: The hinting mode to use. - `number`: Use numeric hints. - `letter`: Use the chars in the hints.chars setting. - `word`: Use hint words based on the html elements and the extra words. *args: Arguments for spawn/userscript/run/fill. - With `spawn`: The executable and arguments to spawn. `{hint-url}` will get replaced by the selected URL. - With `userscript`: The userscript to execute. Either store the userscript in `~/.local/share/qutebrowser/userscripts` (or `$XDG_DATA_HOME`), or use an absolute path. - With `fill`: The command to fill the statusbar with. `{hint-url}` will get replaced by the selected URL. - With `run`: Same as `fill`. """ tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) tab = tabbed_browser.widget.currentWidget() if tab is None: raise cmdutils.CommandError("No WebView available yet!") mode_manager = objreg.get('mode-manager', scope='window', window=self._win_id) if mode_manager.mode == usertypes.KeyMode.hint: modeman.leave(self._win_id, usertypes.KeyMode.hint, 're-hinting') if rapid: if target in [ Target.tab_bg, Target.window, Target.run, Target.hover, Target.userscript, Target.spawn, Target.download, Target.normal, Target.current, Target.yank, Target.yank_primary ]: pass elif target == Target.tab and config.val.tabs.background: pass else: name = target.name.replace('_', '-') raise cmdutils.CommandError("Rapid hinting makes no sense " "with target {}!".format(name)) self._check_args(target, *args) self._context = HintContext() self._context.tab = tab self._context.target = target self._context.rapid = rapid self._context.hint_mode = self._get_hint_mode(mode) self._context.add_history = add_history self._context.first = first try: self._context.baseurl = tabbed_browser.current_url() except qtutils.QtValueError: raise cmdutils.CommandError("No URL set for this page yet!") self._context.args = list(args) self._context.group = group try: selector = webelem.css_selector(self._context.group, self._context.baseurl) except webelem.Error as e: raise cmdutils.CommandError(str(e)) self._context.tab.elements.find_css( selector, callback=self._start_cb, error_cb=lambda err: message.error(str(err)), only_visible=True)
def _warn_for_pac(): """Show a warning if PAC is used with QtWebEngine.""" proxy = config.val.content.proxy if (isinstance(proxy, pac.PACFetcher) and objects.backend == usertypes.Backend.QtWebEngine): message.error("PAC support isn't implemented for QtWebEngine yet!")
def on_error(self, error): """Show a message if there was an error while spawning.""" msg = ERROR_STRINGS[error] message.error("Error while spawning {}: {}".format(self._what, msg))
def execute(self, cmdstr, count=None): try: self._commandrunner.run(cmdstr, count) except cmdexc.Error as e: message.error(str(e), stack=traceback.format_exc())
def run_safely(self, text, count=None): """Run a command and display exceptions in the statusbar.""" try: self.run(text, count) except (cmdexc.CommandMetaError, cmdexc.CommandError) as e: message.error(self._win_id, e, immediately=True)
def _handle_sql_errors(self): try: yield except sql.KnownError as e: message.error(f"Failed to write history: {e.text()}")
def ignore_certificate_error( *, request_url: QUrl, first_party_url: QUrl, error: usertypes.AbstractCertificateErrorWrapper, abort_on: Iterable[pyqtBoundSignal], ) -> bool: """Display a certificate error question. Args: request_url: The URL of the request where the errors happened. first_party_url: The URL of the page we're visiting. Might be an invalid QUrl. error: A single error. abort_on: Signals aborting a question. Return: True if the error should be ignored, False otherwise. """ conf = config.instance.get('content.tls.certificate_errors', url=request_url) log.network.debug(f"Certificate error {error!r}, config {conf}") assert error.is_overridable(), repr(error) # We get the first party URL with a heuristic - with HTTP -> HTTPS redirects, the # scheme might not match. is_resource = ( first_party_url.isValid() and not request_url.matches( first_party_url, QUrl.RemoveScheme)) # type: ignore[arg-type] if conf == 'ask' or conf == 'ask-block-thirdparty' and not is_resource: err_template = jinja.environment.from_string(""" {% if is_resource %} <p> Error while loading resource <b>{{request_url.toDisplayString()}}</b><br/> on page <b>{{first_party_url.toDisplayString()}}</b>: </p> {% else %} <p>Error while loading page <b>{{request_url.toDisplayString()}}</b>:</p> {% endif %} {{error.html()|safe}} {% if is_resource %} <p><i>Consider reporting this to the website operator, or set <tt>content.tls.certificate_errors</tt> to <tt>ask-block-thirdparty</tt> to always block invalid resource loads.</i></p> {% endif %} Do you want to ignore these errors and continue loading the page <b>insecurely</b>? """.strip()) msg = err_template.render( request_url=request_url, first_party_url=first_party_url, is_resource=is_resource, error=error, ) urlstr = request_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) # type: ignore[arg-type] ignore = message.ask(title="Certificate error", text=msg, mode=usertypes.PromptMode.yesno, default=False, abort_on=abort_on, url=urlstr) if ignore is None: # prompt aborted ignore = False return ignore elif conf == 'load-insecurely': message.error(f'Certificate error: {error}') return True elif conf == 'block': return False elif conf == 'ask-block-thirdparty' and is_resource: log.network.error( f"Certificate error in resource load: {error}\n" f" request URL: {request_url.toDisplayString()}\n" f" first party URL: {first_party_url.toDisplayString()}") return False raise utils.Unreachable(conf, is_resource)
def _show_url_error(self): """Show an error because no link was found.""" message.error(self._win_id, "No suitable link found for this element.", immediately=True)
def _on_error(self): """Show a message if there was an error while spawning.""" msg = self._proc.errorString() message.error("Error while spawning {}: {}".format(self._what, msg))
def execute(self, cmdstr, _keytype, count=None): try: self._commandrunner.run(cmdstr, count) except (cmdexc.CommandMetaError, cmdexc.CommandError) as e: message.error(self._win_id, e, immediately=True)