diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 0216e2926..4f6a0afb3 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -32,7 +32,7 @@ import qutebrowser.config.config as config import qutebrowser.utils.log as log from qutebrowser.utils.misc import read_file from qutebrowser.utils.qt import check_print_compat -from qutebrowser.utils.usertypes import PromptMode +from qutebrowser.utils.usertypes import PromptMode, ClickTarget class BrowserPage(QWebPage): @@ -41,16 +41,19 @@ class BrowserPage(QWebPage): Attributes: _extension_handlers: Mapping of QWebPage extensions to their handlers. + _view: The QWebView associated with this page. network_access_manager: The QNetworkAccessManager used. Signals: start_download: Emitted when a file should be downloaded. + change_title: Emitted when the title should be changed. """ start_download = pyqtSignal('QNetworkReply*') + change_title = pyqtSignal(str) - def __init__(self, parent=None): - super().__init__(parent) + def __init__(self, view): + super().__init__(view) self._extension_handlers = { QWebPage.ErrorPageExtension: self._handle_errorpage, QWebPage.ChooseMultipleFilesExtension: self._handle_multiple_files, @@ -60,6 +63,7 @@ class BrowserPage(QWebPage): self.setForwardUnsupportedContent(True) self.printRequested.connect(self.on_print_requested) self.downloadRequested.connect(self.on_download_requested) + self._view = view if PYQT_VERSION > 0x050300: # This breaks in <= 5.3.0, but in anything later it hopefully @@ -232,3 +236,36 @@ class BrowserPage(QWebPage): if answer is None: answer = True return answer + + def acceptNavigationRequest(self, _frame, request, typ): + """Override acceptNavigationRequest to handle clicked links. + + Setting linkDelegationPolicy to DelegateAllLinks and using a slot bound + to linkClicked won't work correctly, because when in a frameset, we + have no idea in which frame the link should be opened. + + Checks if it should open it in a tab (middle-click or control) or not, + and then opens the URL. + + Args: + _frame: QWebFrame (target frame) + request: QNetworkRequest + typ: QWebPage::NavigationType + """ + if typ != QWebPage.NavigationTypeLinkClicked: + return True + url = request.url() + urlstr = url.toDisplayString() + if not url.isValid(): + message.error("Invalid link {} clicked!".format(urlstr)) + log.webview.debug(url.errorString()) + return False + if self._view.open_target == ClickTarget.tab: + self._view.tabbedbrowser.tabopen(url, False) + return False + elif self._view.open_target == ClickTarget.tab_bg: + self._view.tabbedbrowser.tabopen(url, True) + return False + else: + self.change_title.emit(urlstr) + return True diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 3de928b8a..2cf5a75d5 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -235,6 +235,9 @@ class NeighborList(collections.abc.Sequence): # The mode of a Question. PromptMode = enum('yesno', 'text', 'user_pwd', 'alert') +# Where to open a clicked link. +ClickTarget = enum('normal', 'tab', 'tab_bg') + class Question(QObject): diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index b4a67c5f3..a6902a765 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -21,7 +21,7 @@ import functools -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKitWidgets import QWebView, QWebPage @@ -35,11 +35,10 @@ from qutebrowser.utils.misc import elide from qutebrowser.utils.qt import qt_ensure_valid from qutebrowser.browser.webpage import BrowserPage from qutebrowser.browser.hints import HintManager -from qutebrowser.utils.usertypes import NeighborList, enum +from qutebrowser.utils.usertypes import NeighborList, ClickTarget, enum from qutebrowser.commands.exceptions import CommandError -Target = enum('normal', 'tab', 'tab_bg') LoadStatus = enum('none', 'success', 'error', 'warn', 'loading') @@ -58,6 +57,7 @@ class WebView(QWebView): scroll_pos: The current scroll position as (x%, y%) tuple. statusbar_message: The current javscript statusbar message. inspector: The QWebInspector used for this webview. + open_target: Where to open the next tab ("normal", "tab", "tab_bg") _page: The QWebPage behind the view _url_text: The current URL as string. Accessed via url_text property. @@ -67,7 +67,6 @@ class WebView(QWebView): _zoom: A NeighborList with the zoom levels. _old_scroll_pos: The old scroll position. _shutdown_callback: Callback to be called after shutdown. - _open_target: Where to open the next tab ("normal", "tab", "tab_bg") _force_open_target: Override for _open_target. _shutdown_callback: The callback to call after shutting down. _destroyed: Dict of all items to be destroyed on shtudown. @@ -95,7 +94,7 @@ class WebView(QWebView): self.statusbar_message = '' self._old_scroll_pos = (-1, -1) self._shutdown_callback = None - self._open_target = Target.normal + self.open_target = ClickTarget.normal self._force_open_target = None self._destroyed = {} self._zoom = None @@ -108,10 +107,9 @@ class WebView(QWebView): self.hintmanager = HintManager(self) self.hintmanager.mouse_event.connect(self.on_mouse_event) self.hintmanager.set_open_target.connect(self.set_force_open_target) - self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.page().linkHovered.connect(self.linkHovered) - self.linkClicked.connect(self.on_link_clicked) self.page().mainFrame().loadStarted.connect(self.on_load_started) + self.page().change_title.connect(self.titleChanged) self.urlChanged.connect(self.on_url_changed) self.loadFinished.connect(self.on_load_finished) self.loadProgress.connect(lambda p: setattr(self, 'progress', p)) @@ -236,20 +234,20 @@ class WebView(QWebView): e: The QMouseEvent. """ if self._force_open_target is not None: - self._open_target = self._force_open_target + self.open_target = self._force_open_target self._force_open_target = None log.mouse.debug("Setting force target: {}".format( - Target[self._open_target])) + ClickTarget[self.open_target])) elif (e.button() == Qt.MidButton or e.modifiers() & Qt.ControlModifier): if config.get('general', 'background-tabs'): - self._open_target = Target.tab_bg + self.open_target = ClickTarget.tab_bg else: - self._open_target = Target.tab + self.open_target = ClickTarget.tab log.mouse.debug("Middle click, setting target: {}".format( - Target[self._open_target])) + ClickTarget[self.open_target])) else: - self._open_target = Target.normal + self.open_target = ClickTarget.normal log.mouse.debug("Normal click, setting normal target") def openurl(self, url): @@ -354,28 +352,6 @@ class WebView(QWebView): qt_ensure_valid(url) self.url_text = url.toDisplayString() - @pyqtSlot(str) - def on_link_clicked(self, urlstr): - """Handle a link. - - Called from the linkClicked signal. Checks if it should open it in a - tab (middle-click or control) or not, and does so. - - Args: - urlstr: The URL to handle, as string. - """ - url = QUrl(urlstr) - if not url.isValid(): - message.error("Invalid link {} clicked!".format(urlstr)) - log.webview.debug(url.errorString()) - return - if self._open_target == Target.tab: - self.tabbedbrowser.tabopen(url, False) - elif self._open_target == Target.tab_bg: - self.tabbedbrowser.tabopen(url, True) - else: - self.openurl(url) - @pyqtSlot(str, str) def on_config_changed(self, section, option): """Update tab config when config was changed.""" @@ -425,7 +401,7 @@ class WebView(QWebView): Args: target: A string to set self._force_open_target to. """ - t = getattr(Target, target) + t = getattr(ClickTarget, target) log.webview.debug("Setting force target to {}/{}".format(target, t)) self._force_open_target = t