Override QWebPage::acceptNavigationRequest.

We now do this instead of using linkDelegationPolicy and the linkClicked signal
of QWebView, as we are unable to get the target frame with linkClicked, causing
frames to open full-page instead of where they should.

See https://bugs.webkit.org/show_bug.cgi?id=37847
This commit is contained in:
Florian Bruhin 2014-07-02 22:17:36 +02:00
parent ca4336cab3
commit c18027f3ae
3 changed files with 55 additions and 39 deletions

View File

@ -32,7 +32,7 @@ import qutebrowser.config.config as config
import qutebrowser.utils.log as log import qutebrowser.utils.log as log
from qutebrowser.utils.misc import read_file from qutebrowser.utils.misc import read_file
from qutebrowser.utils.qt import check_print_compat from qutebrowser.utils.qt import check_print_compat
from qutebrowser.utils.usertypes import PromptMode from qutebrowser.utils.usertypes import PromptMode, ClickTarget
class BrowserPage(QWebPage): class BrowserPage(QWebPage):
@ -41,16 +41,19 @@ class BrowserPage(QWebPage):
Attributes: Attributes:
_extension_handlers: Mapping of QWebPage extensions to their handlers. _extension_handlers: Mapping of QWebPage extensions to their handlers.
_view: The QWebView associated with this page.
network_access_manager: The QNetworkAccessManager used. network_access_manager: The QNetworkAccessManager used.
Signals: Signals:
start_download: Emitted when a file should be downloaded. start_download: Emitted when a file should be downloaded.
change_title: Emitted when the title should be changed.
""" """
start_download = pyqtSignal('QNetworkReply*') start_download = pyqtSignal('QNetworkReply*')
change_title = pyqtSignal(str)
def __init__(self, parent=None): def __init__(self, view):
super().__init__(parent) super().__init__(view)
self._extension_handlers = { self._extension_handlers = {
QWebPage.ErrorPageExtension: self._handle_errorpage, QWebPage.ErrorPageExtension: self._handle_errorpage,
QWebPage.ChooseMultipleFilesExtension: self._handle_multiple_files, QWebPage.ChooseMultipleFilesExtension: self._handle_multiple_files,
@ -60,6 +63,7 @@ class BrowserPage(QWebPage):
self.setForwardUnsupportedContent(True) self.setForwardUnsupportedContent(True)
self.printRequested.connect(self.on_print_requested) self.printRequested.connect(self.on_print_requested)
self.downloadRequested.connect(self.on_download_requested) self.downloadRequested.connect(self.on_download_requested)
self._view = view
if PYQT_VERSION > 0x050300: if PYQT_VERSION > 0x050300:
# This breaks in <= 5.3.0, but in anything later it hopefully # This breaks in <= 5.3.0, but in anything later it hopefully
@ -232,3 +236,36 @@ class BrowserPage(QWebPage):
if answer is None: if answer is None:
answer = True answer = True
return answer 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

View File

@ -235,6 +235,9 @@ class NeighborList(collections.abc.Sequence):
# The mode of a Question. # The mode of a Question.
PromptMode = enum('yesno', 'text', 'user_pwd', 'alert') PromptMode = enum('yesno', 'text', 'user_pwd', 'alert')
# Where to open a clicked link.
ClickTarget = enum('normal', 'tab', 'tab_bg')
class Question(QObject): class Question(QObject):

View File

@ -21,7 +21,7 @@
import functools import functools
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebPage 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.utils.qt import qt_ensure_valid
from qutebrowser.browser.webpage import BrowserPage from qutebrowser.browser.webpage import BrowserPage
from qutebrowser.browser.hints import HintManager 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 from qutebrowser.commands.exceptions import CommandError
Target = enum('normal', 'tab', 'tab_bg')
LoadStatus = enum('none', 'success', 'error', 'warn', 'loading') LoadStatus = enum('none', 'success', 'error', 'warn', 'loading')
@ -58,6 +57,7 @@ class WebView(QWebView):
scroll_pos: The current scroll position as (x%, y%) tuple. scroll_pos: The current scroll position as (x%, y%) tuple.
statusbar_message: The current javscript statusbar message. statusbar_message: The current javscript statusbar message.
inspector: The QWebInspector used for this webview. 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 _page: The QWebPage behind the view
_url_text: The current URL as string. _url_text: The current URL as string.
Accessed via url_text property. Accessed via url_text property.
@ -67,7 +67,6 @@ class WebView(QWebView):
_zoom: A NeighborList with the zoom levels. _zoom: A NeighborList with the zoom levels.
_old_scroll_pos: The old scroll position. _old_scroll_pos: The old scroll position.
_shutdown_callback: Callback to be called after shutdown. _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. _force_open_target: Override for _open_target.
_shutdown_callback: The callback to call after shutting down. _shutdown_callback: The callback to call after shutting down.
_destroyed: Dict of all items to be destroyed on shtudown. _destroyed: Dict of all items to be destroyed on shtudown.
@ -95,7 +94,7 @@ class WebView(QWebView):
self.statusbar_message = '' self.statusbar_message = ''
self._old_scroll_pos = (-1, -1) self._old_scroll_pos = (-1, -1)
self._shutdown_callback = None self._shutdown_callback = None
self._open_target = Target.normal self.open_target = ClickTarget.normal
self._force_open_target = None self._force_open_target = None
self._destroyed = {} self._destroyed = {}
self._zoom = None self._zoom = None
@ -108,10 +107,9 @@ class WebView(QWebView):
self.hintmanager = HintManager(self) self.hintmanager = HintManager(self)
self.hintmanager.mouse_event.connect(self.on_mouse_event) self.hintmanager.mouse_event.connect(self.on_mouse_event)
self.hintmanager.set_open_target.connect(self.set_force_open_target) self.hintmanager.set_open_target.connect(self.set_force_open_target)
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.page().linkHovered.connect(self.linkHovered) self.page().linkHovered.connect(self.linkHovered)
self.linkClicked.connect(self.on_link_clicked)
self.page().mainFrame().loadStarted.connect(self.on_load_started) self.page().mainFrame().loadStarted.connect(self.on_load_started)
self.page().change_title.connect(self.titleChanged)
self.urlChanged.connect(self.on_url_changed) self.urlChanged.connect(self.on_url_changed)
self.loadFinished.connect(self.on_load_finished) self.loadFinished.connect(self.on_load_finished)
self.loadProgress.connect(lambda p: setattr(self, 'progress', p)) self.loadProgress.connect(lambda p: setattr(self, 'progress', p))
@ -236,20 +234,20 @@ class WebView(QWebView):
e: The QMouseEvent. e: The QMouseEvent.
""" """
if self._force_open_target is not None: 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 self._force_open_target = None
log.mouse.debug("Setting force target: {}".format( log.mouse.debug("Setting force target: {}".format(
Target[self._open_target])) ClickTarget[self.open_target]))
elif (e.button() == Qt.MidButton or elif (e.button() == Qt.MidButton or
e.modifiers() & Qt.ControlModifier): e.modifiers() & Qt.ControlModifier):
if config.get('general', 'background-tabs'): if config.get('general', 'background-tabs'):
self._open_target = Target.tab_bg self.open_target = ClickTarget.tab_bg
else: else:
self._open_target = Target.tab self.open_target = ClickTarget.tab
log.mouse.debug("Middle click, setting target: {}".format( log.mouse.debug("Middle click, setting target: {}".format(
Target[self._open_target])) ClickTarget[self.open_target]))
else: else:
self._open_target = Target.normal self.open_target = ClickTarget.normal
log.mouse.debug("Normal click, setting normal target") log.mouse.debug("Normal click, setting normal target")
def openurl(self, url): def openurl(self, url):
@ -354,28 +352,6 @@ class WebView(QWebView):
qt_ensure_valid(url) qt_ensure_valid(url)
self.url_text = url.toDisplayString() 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) @pyqtSlot(str, str)
def on_config_changed(self, section, option): def on_config_changed(self, section, option):
"""Update tab config when config was changed.""" """Update tab config when config was changed."""
@ -425,7 +401,7 @@ class WebView(QWebView):
Args: Args:
target: A string to set self._force_open_target to. 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)) log.webview.debug("Setting force target to {}/{}".format(target, t))
self._force_open_target = t self._force_open_target = t