Compare commits

...

24 Commits

Author SHA1 Message Date
Florian Bruhin
e9b4c2a66e Release v1.3.2 2018-06-10 15:58:54 +02:00
Florian Bruhin
6a7ab7edb3 Remove unused import
(cherry picked from commit 7949335a2b)
2018-06-09 21:45:13 +02:00
Florian Bruhin
f7f96484e8 Fix waiting for initial focus object with Qt 5.11 workarounds
This was broken in d32d541ac0 because now
apparently PyQt knows it's a QQuickWidget.

(cherry picked from commit ec88c15390)
2018-06-09 20:11:00 +02:00
Florian Bruhin
840d2e4423 Further simplify getting focusProxy children
(cherry picked from commit d32d541ac0)
2018-06-08 17:11:33 +02:00
Florian Bruhin
e54f2a090a Improve RWHV typecheck for focusProxy
(cherry picked from commit cc497bf2ea)
2018-06-08 15:20:33 +02:00
Florian Bruhin
55ce4b7ed2 Exclude QMenu when trying to find the missing focusProxy
(cherry picked from commit 9725d9ce33)
2018-06-08 15:20:31 +02:00
Florian Bruhin
3daf823da8 Show children in focusProxy workaround
(cherry picked from commit 1531961aeb)
2018-06-08 15:20:29 +02:00
Florian Bruhin
72feb2c19f Fix check for reloads on Qt < 5.11
Equivalent commit on master: 91b4106dcf
This was broken in 900efe4a36
2018-06-08 08:57:26 +02:00
Florian Bruhin
6d04508490 Remove unused import
(cherry picked from commit 4614ad5063)
2018-06-07 18:02:11 +02:00
Florian Bruhin
93ebd846ab Implement a better workaround for chrome-error:// URLs
It looks like chrome-error://chromewebdata/ triggers another invalid scheme
load which is why the endless loop happens. When we install a custom scheme
handler for chrome-error:// we can at least show an error page.

(cherry picked from commit b1506274c5)
2018-06-07 16:04:37 +02:00
Florian Bruhin
9ee473a54c Go back to using an invalid scheme for invalid_link.html
Otherwise, this breaks the tests on Qt 5.10

(cherry picked from commit 596041c40e)
2018-06-07 15:42:02 +02:00
Florian Bruhin
d1cced0da4 Make sure external schemes are clickable via hints
This issue was probably introduced in 545539f28d
- with JavaScript, we can't "click" on an external link.

There might be a better solution using
QWebEngineSettings::setUnknownUrlSchemePolicy(QWebEngineSettings::AllowAllUnknownUrlSchemes)
temporarily when using hints with PyQt 5.11.

Fixes #2833

(cherry picked from commit 89f4333df1)
2018-06-07 14:36:12 +02:00
Florian Bruhin
22644c41da Add a workaround for chrome-error:// loops on Qt 5.11
See #3661

(cherry picked from commit 0c0d204fd4)
2018-06-07 14:36:10 +02:00
Florian Bruhin
6b5857ef7d Skip invalid link tests on Qt 5.11
Qt 5.11 just loads about:blank and doesn't let us catch this in
acceptNavigationRequest, but the same happens in Chromium as well.

See #3661

(cherry picked from commit 999513d5d8)
2018-06-07 14:36:07 +02:00
Florian Bruhin
b0f4cc6924 Use a valid scheme in invalid_link.html
This is to avoid triggering QTBUG-63378 which fails differently with a custom
scheme.

See #3661

(cherry picked from commit d059197bc9)
2018-06-07 14:36:03 +02:00
Florian Bruhin
218637af84 Reenable Qt 5.11 tests on Travis
Fixes #3661

(cherry picked from commit eb6478dd3e)
2018-06-07 14:35:56 +02:00
Florian Bruhin
dd84845f01 Don't run test with failed download on Qt 5.11
Looks like we can't use an <a> tag with download-attribute to trigger a failed
download in the test on Qt 5.11...

See #2298, #3661

(cherry picked from commit 8cc3804119)
2018-06-06 21:14:20 +02:00
Florian Bruhin
cf53f9042a Only set PseudoLayout with Qt 5.11
(cherry picked from commit 456fdc55cc)
2018-06-06 20:30:42 +02:00
Florian Bruhin
f48266f72f Fix lint
(cherry picked from commit 7e31897dcc)
2018-06-06 20:30:39 +02:00
Florian Bruhin
5dac848968 Handle resizing via PseudoLayout
This fixes the scenario where we just get a grey view when opening a link in a
tab from DuckDuckGo.

(cherry picked from commit 5147fc832c)
2018-06-06 20:30:37 +02:00
Florian Bruhin
341aa1e700 Try harder to get the RenderWidgetHostViewQt
(cherry picked from commit ec6c5ebb69)
2018-06-06 20:30:34 +02:00
Florian Bruhin
ebf81c06ae Initial proof of concept for pseudo layout
Fixes #3920 - hopefully properly this time...

(cherry picked from commit cee88cd7ca)
2018-06-06 20:30:31 +02:00
Florian Bruhin
a3eb8d6561 travis: Allow Archlinux to fail for now
See #3661

(cherry picked from commit 6fc3546923)
2018-05-29 13:19:36 +02:00
Florian Bruhin
af4b02bf46 setup.py: Set long_description_content_type
Otherwise, Warehouse (new PyPI) refuses the upload...

(cherry picked from commit 52c44d3da6)
2018-05-29 11:23:48 +02:00
16 changed files with 141 additions and 31 deletions

View File

@@ -15,6 +15,21 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
v1.3.2
------
Fixed
~~~~~
- QtWebEngine: Improved workaround for a bug in Qt 5.11 where only the
top/bottom half of the window is used.
- QtWebEngine: Work around a bug in Qt 5.11 where an endless loading-loop is
triggered when clicking a link with an unknown scheme.
- QtWebEngine: When switching between pages with changed settings, less
unnecessary reloads are done now.
- QtWebEngine: It's now possible to open external links such as `magnet://` or
`mailto:` via hints.
v1.3.1
------

View File

@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2018 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
__version_info__ = (1, 3, 1)
__version_info__ = (1, 3, 2)
__version__ = '.'.join(str(e) for e in __version_info__)
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."

View File

@@ -307,6 +307,10 @@ class AbstractWebElement(collections.abc.MutableMapping):
href_tags = ['a', 'area', 'link']
return self.tag_name() in href_tags and 'href' in self
def _requires_user_interaction(self):
"""Return True if clicking this element needs user interaction."""
raise NotImplementedError
def _mouse_pos(self):
"""Get the position to click/hover."""
# Click the center of the largest square fitting into the top/left
@@ -405,7 +409,7 @@ class AbstractWebElement(collections.abc.MutableMapping):
return
if click_target == usertypes.ClickTarget.normal:
if self.is_link():
if self.is_link() and not self._requires_user_interaction():
log.webelem.debug("Clicking via JS click()")
self._click_js(click_target)
elif self.is_editable(strict=True):

View File

@@ -27,7 +27,7 @@ from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineSettings
from qutebrowser.utils import log, javascript
from qutebrowser.utils import log, javascript, urlutils
from qutebrowser.browser import webelem
@@ -198,6 +198,13 @@ class WebEngineElement(webelem.AbstractWebElement):
if self.is_text_input() and self.is_editable():
self._js_call('move_cursor_to_end')
def _requires_user_interaction(self):
baseurl = self._tab.url()
url = self.resolve_url(baseurl)
if url is None:
return True
return url.scheme() not in urlutils.WEBENGINE_SCHEMES
def _click_editable(self, click_target):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58515
ev = QMouseEvent(QMouseEvent.MouseButtonPress, QPoint(0, 0),

View File

@@ -34,6 +34,9 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
def install(self, profile):
"""Install the handler for qute:// URLs on the given profile."""
profile.installUrlSchemeHandler(b'qute', self)
if qtutils.version_check('5.11', compiled=False):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
profile.installUrlSchemeHandler(b'chrome-error', self)
def requestStarted(self, job):
"""Handle a request for a qute: scheme.
@@ -45,6 +48,12 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
job: QWebEngineUrlRequestJob
"""
url = job.requestUrl()
if url.scheme() == 'chrome-error':
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-63378
job.fail(QWebEngineUrlRequestJob.UrlInvalid)
return
assert job.requestMethod() == b'GET'
assert url.scheme() == 'qute'
log.misc.debug("Got request for {}".format(url.toDisplayString()))

View File

@@ -30,7 +30,7 @@ from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QEvent, QPoint, QPointF,
QUrl, QTimer)
from PyQt5.QtGui import QKeyEvent, QIcon
from PyQt5.QtNetwork import QAuthenticator
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
from qutebrowser.config import configdata, config
@@ -1048,28 +1048,6 @@ class WebEngineTab(browsertab.AbstractTab):
def _on_navigation_request(self, navigation):
super()._on_navigation_request(navigation)
if qtutils.version_check('5.11.0', exact=True, compiled=False):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-68224
layout = self._widget.layout()
count = layout.count()
children = self._widget.findChildren(QWidget)
if not count and children:
log.webview.warning("Found children not in layout: {}, "
"focus proxy {} (QTBUG-68224)".format(
children, self._widget.focusProxy()))
if count > 1:
log.webview.debug("Found {} widgets! (QTBUG-68224)"
.format(count))
for i in range(count):
item = layout.itemAt(i)
if item is None:
continue
widget = item.widget()
if widget is not self._widget.focusProxy():
log.webview.debug("Removing widget {} (QTBUG-68224)"
.format(widget))
layout.removeWidget(widget)
if not navigation.accepted or not navigation.is_main_frame:
return
@@ -1092,7 +1070,7 @@ class WebEngineTab(browsertab.AbstractTab):
# TODO on Qt > 5.11.0, we hopefully never need a reload:
# https://codereview.qt-project.org/#/c/229525/1
if not qtutils.version_check('5.11.0', exact=True, compiled=False):
if navigation.navigation_type != navigation.Type.link_clicked:
if navigation.navigation_type == navigation.Type.link_clicked:
reload_needed = False
if reload_needed:
@@ -1137,4 +1115,4 @@ class WebEngineTab(browsertab.AbstractTab):
self.predicted_navigation.connect(self._on_predicted_navigation)
def event_target(self):
return self._widget.focusProxy()
return self._widget.render_widget()

View File

@@ -21,8 +21,10 @@
import functools
import sip
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, PYQT_VERSION
from PyQt5.QtGui import QPalette
from PyQt5.QtQuickWidgets import QQuickWidget
from PyQt5.QtWebEngineWidgets import (QWebEngineView, QWebEnginePage,
QWebEngineScript)
@@ -30,6 +32,7 @@ from qutebrowser.browser import shared
from qutebrowser.browser.webengine import certificateerror, webenginesettings
from qutebrowser.config import config
from qutebrowser.utils import log, debug, usertypes, jinja, objreg, qtutils
from qutebrowser.misc import miscwidgets
class WebEngineView(QWebEngineView):
@@ -51,6 +54,34 @@ class WebEngineView(QWebEngineView):
parent=self)
self.setPage(page)
if qtutils.version_check('5.11', compiled=False):
# Set a PseudoLayout as a WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-68224
# and other related issues.
sip.delete(self.layout())
self._layout = miscwidgets.PseudoLayout(self)
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
"""
proxy = self.focusProxy()
if proxy is not None:
return proxy
# This should only find the RenderWidgetHostViewQtDelegateWidget,
# but not e.g. a QMenu
children = self.findChildren(QQuickWidget)
if not children:
return None
assert len(children) == 1, children
return children[0]
def shutdown(self):
self.page().shutdown()

View File

@@ -305,6 +305,9 @@ class WebKitElement(webelem.AbstractWebElement):
if self.is_text_input() and self.is_editable():
self._tab.caret.move_to_end_of_document()
def _requires_user_interaction(self):
return False
def _click_editable(self, click_target):
ok = self._elem.evaluateJavaScript('this.focus(); true;')
if ok:

View File

@@ -74,7 +74,7 @@ function tryagain()
</td>
<td style="padding-left: 40px;">
<h1>Unable to load page</h1>
Error while opening {{ url }}<br>
Error while opening {{ url | default('page', true) }}<br>
<p id="error-message-text" style="color: #a31a1a;">{{ error }}</p><br><br>
<form name="bl">

View File

@@ -266,6 +266,47 @@ class WrapperLayout(QLayout):
self._widget.deleteLater()
class PseudoLayout(QLayout):
"""A layout which isn't actually a real layout.
This is used to replace QWebEngineView's internal layout, as a WORKAROUND
for https://bugreports.qt.io/browse/QTBUG-68224 and other related issues.
This is partly inspired by https://codereview.qt-project.org/#/c/230894/
which does something similar as part of Qt.
"""
def addItem(self, item):
assert self.parent() is not None
item.widget().setParent(self.parent())
def removeItem(self, item):
item.widget().setParent(None)
def count(self):
return 0
def itemAt(self, _pos):
return None
def widget(self):
return self.parent().render_widget()
def setGeometry(self, rect):
"""Resize the render widget when the view is resized."""
widget = self.widget()
if widget is not None:
widget.setGeometry(rect)
def sizeHint(self):
"""Make sure the view has the sizeHint of the render widget."""
widget = self.widget()
if widget is not None:
return widget.sizeHint()
return QSize()
class FullscreenNotification(QLabel):
"""A label telling the user this page is now fullscreen."""

View File

@@ -39,6 +39,21 @@ from qutebrowser.browser.network import pac
# https://github.com/qutebrowser/qutebrowser/issues/108
# URL schemes supported by QtWebEngine
WEBENGINE_SCHEMES = [
'about',
'data',
'file',
'filesystem',
'ftp',
'http',
'https',
'javascript',
'ws',
'wss',
]
class InvalidUrlError(ValueError):
"""Error raised if a function got an invalid URL.

View File

@@ -77,6 +77,7 @@ try:
version='.'.join(str(e) for e in _get_constant('version_info')),
description=_get_constant('description'),
long_description=read_file('README.asciidoc'),
long_description_content_type='text/plain',
url='https://www.qutebrowser.org/',
author=_get_constant('author'),
author_email=_get_constant('email'),

View File

@@ -7,5 +7,6 @@
</head>
<body>
<a href="what://::">I'm broken</a>
<a href="foo://bar">Unknown scheme</a>
</body>
</html>

View File

@@ -201,8 +201,8 @@ Feature: Downloading things from a website.
And I run :download-retry
Then the error "Retrying downloads is unsupported *" should be shown
@qtwebkit_skip @qt>=5.10
Scenario: Retrying a failed download with QtWebEngine (Qt >= 5.10)
@qtwebkit_skip @qt==5.10.1
Scenario: Retrying a failed download with QtWebEngine (Qt 5.10)
When I open data/downloads/issue2298.html
And I run :click-element id download
And I wait for "Download error: *" in the log

View File

@@ -156,11 +156,13 @@ Feature: Using hints
And I hint with args "all run message-info {hint-url}" and follow a
Then the message "http://localhost:(port)/data/hello.txt" should be shown
@qt!=5.11.0
Scenario: Clicking an invalid link
When I open data/invalid_link.html
And I hint with args "all" and follow a
Then the error "Invalid link clicked - *" should be shown
@qt!=5.11.0
Scenario: Clicking an invalid link opening in a new tab
When I open data/invalid_link.html
And I hint with args "all tab" and follow a

View File

@@ -361,6 +361,9 @@ class QuteProc(testprocess.Process):
"Focus object changed: "
"<qutebrowser.browser.webengine.webview.WebEngineView object "
"at *>",
# Qt >= 5.11 with workarounds
"Focus object changed: "
"<PyQt5.QtQuickWidgets.QQuickWidget object at *>",
]
if (log_line.category == 'ipc' and