def _prevnext_cb(elems): elem = _find_prevnext(prev, elems) word = 'prev' if prev else 'forward' if elem is None: message.error("No {} links found!".format(word)) return url = elem.resolve_url(baseurl) if url is None: message.error("No {} links found!".format(word)) return qtutils.ensure_valid(url) cur_tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) if window: new_window = mainwindow.MainWindow( private=cur_tabbed_browser.is_private) new_window.show() tabbed_browser = objreg.get('tabbed-browser', scope='window', window=new_window.win_id) tabbed_browser.tabopen(url, background=False) elif tab: cur_tabbed_browser.tabopen(url, background=background) else: browsertab.load_url(url)
def get_tab(win_id, target): """Get a tab widget for the given usertypes.ClickTarget. Args: win_id: The window ID to open new tabs in target: A usertypes.ClickTarget """ if target == usertypes.ClickTarget.tab: win_id = win_id bg_tab = False elif target == usertypes.ClickTarget.tab_bg: win_id = win_id bg_tab = True elif target == usertypes.ClickTarget.window: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) window = mainwindow.MainWindow(private=tabbed_browser.is_private) 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) return tabbed_browser.tabopen(url=None, background=bg_tab)
def run_async(tab, cmd, *args, win_id, env, verbose=False): """Run a userscript after dumping page html/source. Raises: UnsupportedError if userscripts are not supported on the current platform. NotFoundError if the command could not be found. Args: tab: The WebKitTab/WebEngineTab to get the source from. cmd: The userscript binary to run. *args: The arguments to pass to the userscript. win_id: The window id the userscript is executed in. env: A dictionary of variables to add to the process environment. verbose: Show notifications when the command started/exited. """ tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser) if utils.is_posix: runner = _POSIXUserscriptRunner(tabbed_browser) elif utils.is_windows: # pragma: no cover runner = _WindowsUserscriptRunner(tabbed_browser) else: # pragma: no cover raise UnsupportedError runner.got_cmd.connect(lambda cmd: log.commands.debug( "Got userscript command: {}".format(cmd))) runner.got_cmd.connect(commandrunner.run_safely) user_agent = config.val.content.headers.user_agent if user_agent is not None: env['QUTE_USER_AGENT'] = user_agent env['QUTE_CONFIG_DIR'] = standarddir.config() env['QUTE_DATA_DIR'] = standarddir.data() env['QUTE_DOWNLOAD_DIR'] = downloads.download_dir() env['QUTE_COMMANDLINE_TEXT'] = objreg.get('status-command', scope='window', window=win_id).text() cmd_path = os.path.expanduser(cmd) # if cmd is not given as an absolute path, look it up # ~/.local/share/glimpsebrowser/userscripts (or $XDG_DATA_HOME) if not os.path.isabs(cmd_path): log.misc.debug("{} is no absolute path".format(cmd_path)) cmd_path = _lookup_path(cmd) elif not os.path.exists(cmd_path): raise NotFoundError(cmd_path) log.misc.debug("Userscript to run: {}".format(cmd_path)) runner.finished.connect(commandrunner.deleteLater) runner.finished.connect(runner.deleteLater) runner.prepare_run(cmd_path, *args, env=env, verbose=verbose) tab.dump_async(runner.store_html) tab.dump_async(runner.store_text, plain=True) return runner
def _show_dialog(*args, **kwargs): """Show a dialog for a backend problem.""" cmd_args = objreg.get('args') if cmd_args.no_err_windows: text = _error_text(*args, **kwargs) print(text, file=sys.stderr) sys.exit(usertypes.Exit.err_init) dialog = _Dialog(*args, **kwargs) status = dialog.exec_() quitter = objreg.get('quitter') if status in [_Result.quit, QDialog.Rejected]: pass elif status == _Result.restart_webkit: quitter.restart(override_args={'backend': 'webkit'}) elif status == _Result.restart_webengine: quitter.restart(override_args={'backend': 'webengine'}) elif status == _Result.restart: quitter.restart() else: raise utils.Unreachable(status) sys.exit(usertypes.Exit.err_init)
def _save_all(self, *, only_window=None, with_private=False): """Get a dict with data for all windows/tabs.""" data = {'windows': []} if only_window is not None: winlist = [only_window] else: winlist = objreg.window_registry for win_id in sorted(winlist): tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) main_window = objreg.get('main-window', scope='window', window=win_id) # We could be in the middle of destroying a window here if sip.isdeleted(main_window): continue if tabbed_browser.is_private and not with_private: continue win_data = {} active_window = QApplication.instance().activeWindow() if getattr(active_window, 'win_id', None) == win_id: win_data['active'] = True win_data['geometry'] = bytes(main_window.saveGeometry()) win_data['tabs'] = [] if tabbed_browser.is_private: win_data['private'] = True for i, tab in enumerate(tabbed_browser.widgets()): active = i == tabbed_browser.widget.currentIndex() win_data['tabs'].append(self._save_tab(tab, active)) data['windows'].append(win_data) return data
def _set_cookiejar(self): """Set the cookie jar of the NetworkManager correctly.""" if self._private: cookie_jar = objreg.get('ram-cookie-jar') else: cookie_jar = objreg.get('cookie-jar') # We have a shared cookie jar - we restore its parent so we don't # take ownership of it. self.setCookieJar(cookie_jar) app = QCoreApplication.instance() cookie_jar.setParent(app)
def _do_close(self): """Helper function for closeEvent.""" try: last_visible = objreg.get('last-visible-main-window') if self is last_visible: objreg.delete('last-visible-main-window') except KeyError: pass objreg.get('session-manager').save_last_window_session() self._save_geometry() log.destroy.debug("Closing window {}".format(self.win_id)) self.tabbed_browser.shutdown()
def glimpse_bookmarks(_url): """Handler for glimpse://bookmarks. Display all quickmarks / bookmarks.""" bookmarks = sorted(objreg.get('bookmark-manager').marks.items(), key=lambda x: x[1]) # Sort by title quickmarks = sorted(objreg.get('quickmark-manager').marks.items(), key=lambda x: x[0]) # Sort by name src = jinja.render('bookmarks.html', title='Bookmarks', bookmarks=bookmarks, quickmarks=quickmarks) return 'text/html', src
def __init__(self, parent=None, *, line_parser=None): super().__init__(parent) if line_parser: self._lineparser = line_parser else: self._lineparser = lineparser.LineParser( standarddir.data(), 'cookies', binary=True, parent=self) self.parse_cookies() config.instance.changed.connect(self._on_cookies_store_changed) objreg.get('save-manager').add_saveable( 'cookies', self.save, self.changed, config_opt='content.cookies.store')
def url(*, info): """A model which combines various URLs. This combines: - bookmarks - quickmarks - search engines - web history URLs Used for the `open` command. """ model = completionmodel.CompletionModel(column_widths=(40, 50, 10)) # pylint: disable=bad-config-option quickmarks = [(url, name) for (name, url) in objreg.get('quickmark-manager').marks.items()] bookmarks = objreg.get('bookmark-manager').marks.items() searchengines = [(k, v) for k, v in sorted(config.val.url.searchengines.items()) if k != 'DEFAULT'] # pylint: enable=bad-config-option categories = config.val.completion.open_categories models = {} if searchengines and 'searchengines' in categories: models['searchengines'] = listcategory.ListCategory('Search engines', searchengines, sort=False) if quickmarks and 'quickmarks' in categories: models['quickmarks'] = listcategory.ListCategory( 'Quickmarks', quickmarks, delete_func=_delete_quickmark, sort=False) if bookmarks and 'bookmarks' in categories: models['bookmarks'] = listcategory.ListCategory( 'Bookmarks', bookmarks, delete_func=_delete_bookmark, sort=False) history_disabled = info.config.get('completion.web_history.max_items') == 0 if not history_disabled and 'history' in categories: hist_cat = histcategory.HistoryCategory(delete_func=_delete_history) models['history'] = hist_cat for category in categories: if category in models: model.add_category(models[category]) return model
def _get_required_scripts(self, script, force=False): required_dls = [(url, self._required_url_to_file_path(url)) for url in script.requires] if not force: required_dls = [(url, path) for (url, path) in required_dls if not os.path.exists(path)] if not required_dls: # All the required files exist already self._add_script_with_requires(script, quiet=True) return download_manager = objreg.get('qtnetwork-download-manager') for url, target_path in required_dls: target = downloads.FileDownloadTarget(target_path, force_overwrite=True) download = download_manager.get(QUrl(url), target=target, auto_remove=True) download.requested_url = url self._in_progress_dls.append(download) if download.successful: self._on_required_download_finished(script, download) else: download.finished.connect( functools.partial(self._on_required_download_finished, script, download))
def _handle_auto_follow(self, keystr="", filterstr="", visible=None): """Handle the auto_follow option.""" if visible is None: visible = {string: label for string, label in self._context.labels.items() if label.isVisible()} if len(visible) != 1: return auto_follow = config.val.hints.auto_follow if auto_follow == "always": follow = True elif auto_follow == "unique-match": follow = keystr or filterstr elif auto_follow == "full-match": elemstr = str(list(visible.values())[0].elem) filter_match = self._filter_matches_exactly(filterstr, elemstr) follow = (keystr in visible) or filter_match else: follow = False # save the keystr of the only one visible hint to be picked up # later by self.follow_hint self._context.to_follow = list(visible.keys())[0] if follow: # apply auto_follow_timeout timeout = config.val.hints.auto_follow_timeout keyparsers = objreg.get('keyparsers', scope='window', window=self._win_id) normal_parser = keyparsers[usertypes.KeyMode.normal] normal_parser.set_inhibited_timeout(timeout) # unpacking gets us the first (and only) key in the dict. self._fire(*visible)
def test_init(self, backend, qapp, tmpdir, monkeypatch, cleanup_init): if backend == usertypes.Backend.QtWebKit: pytest.importorskip('PyQt5.QtWebKitWidgets') else: assert backend == usertypes.Backend.QtWebEngine monkeypatch.setattr(history.objects, 'backend', backend) history.init(qapp) hist = objreg.get('web-history') assert hist.parent() is qapp try: from PyQt5.QtWebKit import QWebHistoryInterface except ImportError: QWebHistoryInterface = None if backend == usertypes.Backend.QtWebKit: default_interface = QWebHistoryInterface.defaultInterface() assert default_interface._history is hist else: assert backend == usertypes.Backend.QtWebEngine if QWebHistoryInterface is None: default_interface = None else: default_interface = QWebHistoryInterface.defaultInterface() # For this to work, nothing can ever have called # setDefaultInterface before (so we need to test webengine before # webkit) assert default_interface is None
def _filter_signals(self, signal, tab, *args): """Filter signals and trigger TabbedBrowser signals if needed. Triggers signal if the original signal was sent from the _current_ tab and not from any other one. The original signal does not matter, since we get the new signal and all args. Args: signal: The signal to emit if the sender was the current widget. tab: The WebView which the filter belongs to. *args: The args to pass to the signal. """ log_signal = debug.signal_name(signal) not in self.BLACKLIST tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) try: tabidx = tabbed_browser.widget.indexOf(tab) except RuntimeError: # The tab has been deleted already return if tabidx == tabbed_browser.widget.currentIndex(): if log_signal: log.signals.debug("emitting: {} (tab {})".format( debug.dbg_signal(signal, args), tabidx)) signal.emit(*args) else: if log_signal: log.signals.debug("ignoring: {} (tab {})".format( debug.dbg_signal(signal, args), tabidx))
def _inject_userjs(self, frame): """Inject user JavaScripts into the page. Args: frame: The QWebFrame to inject the user scripts into. """ if sip.isdeleted(frame): log.greasemonkey.debug("_inject_userjs called for deleted frame!") return url = frame.url() if url.isEmpty(): url = frame.requestedUrl() log.greasemonkey.debug("_inject_userjs called for {} ({})".format( frame, url.toDisplayString())) greasemonkey = objreg.get('greasemonkey') scripts = greasemonkey.scripts_for(url) # QtWebKit has trouble providing us with signals representing # page load progress at reasonable times, so we just load all # scripts on the same event. toload = scripts.start + scripts.end + scripts.idle if url.isEmpty(): # This happens during normal usage like with view source but may # also indicate a bug. log.greasemonkey.debug("Not running scripts for frame with no " "url: {}".format(frame)) assert not toload, toload for script in toload: if frame is self.mainFrame() or script.runs_on_sub_frames: log.webview.debug('Running GM script: {}'.format(script.name)) frame.evaluateJavaScript(script.code())
def createWindow(self, wintype): """Called by Qt when a page wants to create a new window. This function is called from the createWindow() method of the associated QWebPage, each time the page wants to create a new window of the given type. This might be the result, for example, of a JavaScript request to open a document in a new window. Args: wintype: This enum describes the types of window that can be created by the createWindow() function. QWebPage::WebBrowserWindow: The window is a regular web browser window. QWebPage::WebModalDialog: The window acts as modal dialog. Return: The new QWebView object. """ debug_type = debug.qenum_key(QWebPage, wintype) log.webview.debug("createWindow with type {}".format(debug_type)) if wintype == QWebPage.WebModalDialog: log.webview.warning("WebModalDialog requested, but we don't " "support that!") tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self.win_id) # pylint: disable=protected-access return tabbed_browser.tabopen(background=False)._widget
def later(ms: int, command: str, win_id: int) -> None: """Execute a command after some time. Args: ms: How many milliseconds to wait. command: The command to run, with optional args. """ if ms < 0: raise cmdutils.CommandError("I can't run something in the past!") commandrunner = runners.CommandRunner(win_id) app = objreg.get('app') timer = usertypes.Timer(name='later', parent=app) try: timer.setSingleShot(True) try: timer.setInterval(ms) except OverflowError: raise cmdutils.CommandError("Numeric argument is too large for " "internal int representation.") timer.timeout.connect( functools.partial(commandrunner.run_safely, command)) timer.timeout.connect(timer.deleteLater) timer.start() except: timer.deleteLater() raise
def _init_downloadmanager(self): log.init.debug("Initializing downloads...") qtnetwork_download_manager = objreg.get('qtnetwork-download-manager') try: webengine_download_manager = objreg.get( 'webengine-download-manager') except KeyError: webengine_download_manager = None download_model = downloads.DownloadModel(qtnetwork_download_manager, webengine_download_manager) objreg.register('download-model', download_model, scope='window', window=self.win_id)
def on_keystring_updated(self, keystr): """Update hintmanager when the keystring was updated.""" hintmanager = objreg.get('hintmanager', scope='tab', window=self._win_id, tab='current') hintmanager.handle_partial_key(keystr)
def render_widget(self): """Get the RenderWidgetHostViewQt for this view. Normally, this would always be the focusProxy(). However, it sometimes isn't, so we use this as a WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68727 This got introduced in Qt 5.11.0 and fixed in 5.12.0. """ if 'lost-focusproxy' not in objreg.get('args').debug_flags: proxy = self.focusProxy() if proxy is not None: return proxy # We don't want e.g. a QMenu. rwhv_class = 'QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget' children = [ c for c in self.findChildren(QWidget) if c.isVisible() and c.inherits(rwhv_class) ] log.webview.debug( "Found possibly lost focusProxy: {}".format(children)) return children[-1] if children else None
def delete_buffer(data): """Close the selected tab.""" win_id, tab_index = data[0].split('/') tabbed_browser = objreg.get('tabbed-browser', scope='window', window=int(win_id)) tabbed_browser.on_tab_close_requested(int(tab_index) - 1)
def download_temp(url: QUrl) -> TempDownload: """Download the given URL into a file object. The download is not saved to disk. Returns a ``TempDownload`` object, which triggers a ``finished`` signal when the download has finished:: dl = downloads.download_temp(QUrl("https://www.example.com/")) dl.finished.connect(functools.partial(on_download_finished, dl)) After the download has finished, its ``successful`` attribute can be checked to make sure it finished successfully. If so, its contents can be read by accessing the ``fileobj`` attribute:: def on_download_finished(download: downloads.TempDownload) -> None: if download.successful: print(download.fileobj.read()) download.fileobj.close() """ fobj = io.BytesIO() fobj.name = 'temporary: ' + url.host() target = downloads.FileObjDownloadTarget(fobj) download_manager = objreg.get('qtnetwork-download-manager') return download_manager.get(url, target=target, auto_remove=True)
def _load_session(name): """Load the default session. Args: name: The name of the session to load, or None to read state file. """ session_manager = objreg.get('session-manager') if name is None and session_manager.exists('_autosave'): name = '_autosave' elif name is None: try: name = configfiles.state['general']['session'] except KeyError: # No session given as argument and none in the session file -> # start without loading a session return 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 configfiles.state['general']['session'] except KeyError: pass # If this was a _restart session, delete it. if name == '_restart': session_manager.delete('_restart')
def open_desktopservices_url(url): """Handler to open a URL via QDesktopServices.""" win_id = mainwindow.get_window(via_ipc=True, force_window=False) tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) tabbed_browser.tabopen(url)
def _recover_pages(self, forgiving=False): """Try to recover all open pages. Called from exception_hook, so as forgiving as possible. Args: forgiving: Whether to ignore exceptions. Return: A list containing a list for each window, which in turn contain the opened URLs. """ pages = [] for win_id in objreg.window_registry: win_pages = [] tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) for tab in tabbed_browser.widgets(): try: urlstr = tab.url().toString(QUrl.RemovePassword | QUrl.FullyEncoded) if urlstr: win_pages.append(urlstr) except Exception: if forgiving: log.destroy.exception("Error while recovering tab") else: raise pages.append(win_pages) return pages
def _open_special_pages(args): """Open special notification pages which are only shown once. Args: args: The argparse namespace. """ if args.basedir is not None: # With --basedir given, don't open anything. return general_sect = configfiles.state['general'] tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') pages = [ # state, condition, URL ('quickstart-done', True, 'https://www.glimpsebrowser.org/quickstart.html'), ('config-migration-shown', os.path.exists( os.path.join(standarddir.config(), 'glimpsebrowser.conf')), 'glimpse://help/configuring.html'), ('webkit-warning-shown', objects.backend == usertypes.Backend.QtWebKit, 'glimpse://warning/webkit'), ('old-qt-warning-shown', not qtutils.version_check('5.9'), 'glimpse://warning/old-qt'), ] for state, condition, url in pages: if general_sect.get(state) != '1' and condition: tabbed_browser.tabopen(QUrl(url), background=False) general_sect[state] = '1'
def __call__(self, parser, _namespace, _values, _option_string=None): tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') tabbed_browser.tabopen( QUrl('glimpse://help/commands.html#{}'.format(parser.name))) parser.exit()
def get_cmd_completions(info, include_hidden, include_aliases, prefix=''): """Get a list of completions info for commands, sorted by name. Args: info: The CompletionInfo. include_hidden: Include commands which are not in normal mode. include_aliases: True to include command aliases. prefix: String to append to the command name. Return: A list of tuples of form (name, description, bindings). """ assert objects.commands cmdlist = [] cmd_to_keys = info.keyconf.get_reverse_bindings_for('normal') for obj in set(objects.commands.values()): hide_debug = obj.debug and not objreg.get('args').debug hide_mode = (usertypes.KeyMode.normal not in obj.modes and not include_hidden) if not (hide_debug or hide_mode or obj.deprecated): bindings = ', '.join(cmd_to_keys.get(obj.name, [])) cmdlist.append((prefix + obj.name, obj.desc, bindings)) if include_aliases: for name, cmd in info.config.get('aliases').items(): bindings = ', '.join(cmd_to_keys.get(name, [])) cmdlist.append((name, "Alias for '{}'".format(cmd), bindings)) return sorted(cmdlist)
def eventFilter(self, obj, event): """Act on ChildAdded events.""" if event.type() == QEvent.ChildAdded: child = event.child() log.misc.debug("{} got new child {}, installing filter".format( obj, child)) assert obj is self._widget child.installEventFilter(self._filter) if qtutils.version_check('5.11', compiled=False, exact=True): # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68076 pass_modes = [ usertypes.KeyMode.command, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno ] if modeman.instance(self._win_id).mode not in pass_modes: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) current_index = tabbed_browser.widget.currentIndex() try: widget_index = tabbed_browser.widget.indexOf( self._widget.parent()) except RuntimeError: widget_index = -1 if current_index == widget_index: QTimer.singleShot(0, self._widget.setFocus) elif event.type() == QEvent.ChildRemoved: child = event.child() log.misc.debug("{}: removed child {}".format(obj, child)) return False
def __init__(self, *, win_id, tab_id, private, parent=None): log.init.debug("Initializing NetworkManager") with log.disable_qt_msghandler(): # WORKAROUND for a hang when a message is printed - See: # http://www.riverbankcomputing.com/pipermail/pyqt/2014-November/035045.html super().__init__(parent) log.init.debug("NetworkManager init done") self.adopted_downloads = 0 self._args = objreg.get('args') self._win_id = win_id self._tab_id = tab_id self._private = private self._scheme_handlers = { 'glimpse': webkitglimpsescheme.handler, 'file': filescheme.handler, } self._set_cookiejar() self._set_cache() self.sslErrors.connect(self.on_ssl_errors) self._rejected_ssl_errors = collections.defaultdict(list) self._accepted_ssl_errors = collections.defaultdict(list) self.authenticationRequired.connect(self.on_authentication_required) self.proxyAuthenticationRequired.connect( self.on_proxy_authentication_required) self.netrc_used = False