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
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

View File

@ -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):

View File

@ -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