diff --git a/.travis.yml b/.travis.yml index 1000de613..d6b3b1d6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,10 +21,6 @@ matrix: - os: linux env: DOCKER=ubuntu-xenial services: docker - - os: linux - language: python - python: 3.5 - env: TESTENV=py35-pyqt56 - os: linux language: python python: 3.6 diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 5deca80d4..b06017ea1 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -36,6 +36,7 @@ Added Changed ~~~~~~~ +- PyQt/Qt 5.7.1 is now required for the QtWebEngine backend - Scrolling with the scrollwheel while holding shift now scrolls sideways - New way of clicking hints with which solves various small issues - When yanking a mailto: link via hints, the mailto: prefix is now stripped diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 04424d3aa..47e6ab29f 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -158,7 +158,7 @@ |<>|Whether images are automatically loaded in web pages. |<>|Enables or disables the running of JavaScript programs. |<>|Enables or disables plugins in Web pages. -|<>|Enables or disables WebGL. For QtWebEngine, Qt/PyQt >= 5.7 is required for this setting. +|<>|Enables or disables WebGL. |<>|Enable or disable support for CSS regions. |<>|Enable or disable hyperlink auditing (). |<>|Allow websites to request geolocations. @@ -1426,7 +1426,7 @@ Default: +pass:[false]+ [[content-webgl]] === webgl -Enables or disables WebGL. For QtWebEngine, Qt/PyQt >= 5.7 is required for this setting. +Enables or disables WebGL. Valid values: diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 072d363ba..59a81df69 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -49,12 +49,8 @@ class DownloadItem(downloads.AbstractDownloadItem): def _is_page_download(self): """Check if this item is a page (i.e. mhtml) download.""" - try: - return (self._qt_item.savePageFormat() != - QWebEngineDownloadItem.UnknownSaveFormat) - except AttributeError: - # Added in Qt 5.7 - return False + return (self._qt_item.savePageFormat() != + QWebEngineDownloadItem.UnknownSaveFormat) @pyqtSlot(QWebEngineDownloadItem.DownloadState) def _on_state_changed(self, state): @@ -209,9 +205,6 @@ class DownloadManager(downloads.AbstractDownloadManager): def get_mhtml(self, tab, target): """Download the given tab as mhtml to the given target.""" assert tab.backend == usertypes.Backend.QtWebEngine - # Raises browsertab.UnsupportedOperationError on older Qt versions - # but we let the caller handle that. - tab.action.check_save_page_supported() assert self._mhtml_target is None, self._mhtml_target self._mhtml_target = target tab.action.save_page() diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py index b3bdd8016..36f4e0e64 100644 --- a/qutebrowser/browser/webengine/webengineelem.py +++ b/qutebrowser/browser/webengine/webengineelem.py @@ -25,7 +25,7 @@ from PyQt5.QtCore import QRect, Qt, QPoint from PyQt5.QtGui import QMouseEvent -from qutebrowser.utils import log, javascript, qtutils +from qutebrowser.utils import log, javascript from qutebrowser.browser import webelem @@ -159,15 +159,11 @@ class WebEngineElement(webelem.AbstractWebElement): # because it was added in Qt 5.6, but we can be sure we use that with # QtWebEngine. # pylint: disable=no-member - # This also seems to break stuff on Qt 5.6 for some reason... - if qtutils.version_check('5.7'): - ev = QMouseEvent(QMouseEvent.MouseButtonPress, QPoint(0, 0), - QPoint(0, 0), QPoint(0, 0), Qt.NoButton, - Qt.NoButton, Qt.NoModifier, - Qt.MouseEventSynthesizedBySystem) - # pylint: enable=no-member - self._tab.send_event(ev) - + ev = QMouseEvent(QMouseEvent.MouseButtonPress, QPoint(0, 0), + QPoint(0, 0), QPoint(0, 0), Qt.NoButton, Qt.NoButton, + Qt.NoModifier, Qt.MouseEventSynthesizedBySystem) + # pylint: enable=no-member + self._tab.send_event(ev) # This actually "clicks" the element by calling focus() on it in JS. js_code = javascript.assemble('webelem', 'focus', self._id) self._tab.run_js_async(js_code) diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index fc2386e89..a87bc4802 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -176,16 +176,16 @@ def shutdown(): # Missing QtWebEngine attributes: -# - ScreenCaptureEnabled (5.7) -# - Accelerated2dCanvasEnabled (5.7) -# - AutoLoadIconsForPage (5.7) -# - TouchIconsEnabled (5.7) +# - ScreenCaptureEnabled +# - Accelerated2dCanvasEnabled +# - AutoLoadIconsForPage +# - TouchIconsEnabled # - FocusOnNavigationEnabled (5.8) # - AllowRunningInsecureContent (5.8) # # Missing QtWebEngine fonts: # - FantasyFont -# - PictographFont (5.7) +# - PictographFont MAPPINGS = { @@ -209,6 +209,8 @@ MAPPINGS = { # https://bugreports.qt.io/browse/QTBUG-58650 # 'cookies-store': # PersistentCookiePolicy(), + 'webgl': + Attribute(QWebEngineSettings.WebGLEnabled), }, 'input': { 'spatial-navigation': @@ -278,12 +280,6 @@ MAPPINGS = { } } -try: - MAPPINGS['content']['webgl'] = Attribute(QWebEngineSettings.WebGLEnabled) -except AttributeError: - # Added in Qt 5.7 - pass - try: MAPPINGS['general']['print-element-backgrounds'] = Attribute( QWebEngineSettings.PrintElementBackgrounds) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index c5394ae7d..6fb5bca26 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -90,11 +90,6 @@ class WebEngineAction(browsertab.AbstractAction): def exit_fullscreen(self): self._action(QWebEnginePage.ExitFullScreen) - def check_save_page_supported(self): - if not hasattr(QWebEnginePage, 'SavePage'): - raise browsertab.UnsupportedOperationError( - "Saving as mhtml is unsupported with Qt < 5.7") - def save_page(self): """Save the current page.""" self._action(QWebEnginePage.SavePage) @@ -105,9 +100,7 @@ class WebEnginePrinting(browsertab.AbstractPrinting): """QtWebEngine implementations related to printing.""" def check_pdf_support(self): - if not hasattr(self._widget.page(), 'printToPdf'): - raise browsertab.WebTabError( - "Printing to PDF is unsupported with QtWebEngine on Qt < 5.7") + return True def check_printer_support(self): if not hasattr(self._widget.page(), 'print'): @@ -261,11 +254,7 @@ class WebEngineScroller(browsertab.AbstractScroller): def _init_widget(self, widget): super()._init_widget(widget) page = widget.page() - try: - page.scrollPositionChanged.connect(self._update_pos) - except AttributeError: - log.stub('scrollPositionChanged, on Qt < 5.7') - self._pos_perc = (None, None) + page.scrollPositionChanged.connect(self._update_pos) def _key_press(self, key, count=1): for _ in range(min(count, 5000)): @@ -501,7 +490,6 @@ class WebEngineTab(browsertab.AbstractTab): self.backend = usertypes.Backend.QtWebEngine self._init_js() self._child_event_filter = None - self.needs_qtbug54419_workaround = False self._saved_zoom = None def _init_js(self): @@ -516,13 +504,7 @@ class WebEngineTab(browsertab.AbstractTab): script.setSourceCode(js_code) page = self._widget.page() - try: - page.runJavaScript("", QWebEngineScript.ApplicationWorld) - except TypeError: - # We're unable to pass a world to runJavaScript - script.setWorldId(QWebEngineScript.MainWorld) - else: - script.setWorldId(QWebEngineScript.ApplicationWorld) + script.setWorldId(QWebEngineScript.ApplicationWorld) # FIXME:qtwebengine what about runsOnSubFrames? page.scripts().insert(script) @@ -567,19 +549,10 @@ class WebEngineTab(browsertab.AbstractTab): else: world_id = _JS_WORLD_MAP[world] - try: - if callback is None: - self._widget.page().runJavaScript(code, world_id) - else: - self._widget.page().runJavaScript(code, world_id, callback) - except TypeError: - if world is not None and world != usertypes.JsWorld.jseval: - log.webview.warning("Ignoring world ID on Qt < 5.7") - # Qt < 5.7 - if callback is None: - self._widget.page().runJavaScript(code) - else: - self._widget.page().runJavaScript(code, callback) + if callback is None: + self._widget.page().runJavaScript(code, world_id) + else: + self._widget.page().runJavaScript(code, world_id, callback) def shutdown(self): self.shutting_down.emit() @@ -602,11 +575,7 @@ class WebEngineTab(browsertab.AbstractTab): return self._widget.title() def icon(self): - try: - return self._widget.icon() - except AttributeError: - log.stub('on Qt < 5.7') - return QIcon() + return self._widget.icon() def set_html(self, html, base_url=None): # FIXME:qtwebengine @@ -712,19 +681,13 @@ class WebEngineTab(browsertab.AbstractTab): page.certificate_error.connect(self._on_ssl_errors) page.authenticationRequired.connect(self._on_authentication_required) page.fullScreenRequested.connect(self._on_fullscreen_requested) - try: - page.contentsSizeChanged.connect(self.contents_size_changed) - except AttributeError: - log.stub('contentsSizeChanged, on Qt < 5.7') + page.contentsSizeChanged.connect(self.contents_size_changed) view.titleChanged.connect(self.title_changed) view.urlChanged.connect(self._on_url_changed) view.renderProcessTerminated.connect( self._on_render_process_terminated) - try: - view.iconChanged.connect(self.icon_changed) - except AttributeError: - log.stub('iconChanged, on Qt < 5.7') + view.iconChanged.connect(self.icon_changed) def event_target(self): return self._widget.focusProxy() diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py index 2d9c2eb0a..a861b9219 100644 --- a/qutebrowser/browser/webengine/webview.py +++ b/qutebrowser/browser/webengine/webview.py @@ -71,7 +71,7 @@ class WebEngineView(QWebEngineView): A window without decoration. QWebEnginePage::WebBrowserBackgroundTab: A web browser tab without hiding the current visible - WebEngineView. (Added in Qt 5.7) + WebEngineView. Return: The new QWebEngineView object. @@ -82,13 +82,6 @@ class WebEngineView(QWebEngineView): log.webview.debug("createWindow with type {}, background_tabs " "{}".format(debug_type, background_tabs)) - try: - background_tab_wintype = QWebEnginePage.WebBrowserBackgroundTab - except AttributeError: - # This is unavailable with an older PyQt, but we still might get - # this with a newer Qt... - background_tab_wintype = 0x0003 - if wintype == QWebEnginePage.WebBrowserWindow: # Shift-Alt-Click target = usertypes.ClickTarget.window @@ -103,7 +96,7 @@ class WebEngineView(QWebEngineView): target = usertypes.ClickTarget.tab else: target = usertypes.ClickTarget.tab_bg - elif wintype == background_tab_wintype: + elif wintype == QWebEnginePage.WebBrowserBackgroundTab: # Middle-click / Ctrl-Click if background_tabs: target = usertypes.ClickTarget.tab_bg @@ -113,15 +106,6 @@ class WebEngineView(QWebEngineView): raise ValueError("Invalid wintype {}".format(debug_type)) tab = shared.get_tab(self._win_id, target) - - # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-54419 - vercheck = qtutils.version_check - qtbug54419_fixed = ((vercheck('5.6.2') and not vercheck('5.7.0')) or - qtutils.version_check('5.7.1') or - os.environ.get('QUTE_QTBUG54419_PATCHED', '')) - if not qtbug54419_fixed: - tab.needs_qtbug54419_workaround = True - return tab._widget # pylint: disable=protected-access @@ -261,20 +245,16 @@ class WebEnginePage(QWebEnginePage): except shared.CallSuper: return super().javaScriptConfirm(url, js_msg) - if PYQT_VERSION > 0x050700: - # WORKAROUND - # Can't override javaScriptPrompt with older PyQt versions - # https://www.riverbankcomputing.com/pipermail/pyqt/2016-November/038293.html - def javaScriptPrompt(self, url, js_msg, default): - """Override javaScriptPrompt to use qutebrowser prompts.""" - if self._is_shutting_down: - return (False, "") - try: - return shared.javascript_prompt(url, js_msg, default, - abort_on=[self.loadStarted, - self.shutting_down]) - except shared.CallSuper: - return super().javaScriptPrompt(url, js_msg, default) + def javaScriptPrompt(self, url, js_msg, default): + """Override javaScriptPrompt to use qutebrowser prompts.""" + if self._is_shutting_down: + return (False, "") + try: + return shared.javascript_prompt(url, js_msg, default, + abort_on=[self.loadStarted, + self.shutting_down]) + except shared.CallSuper: + return super().javaScriptPrompt(url, js_msg, default) def javaScriptAlert(self, url, js_msg): """Override javaScriptAlert to use qutebrowser prompts.""" diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 2ec593378..0180537b0 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -823,8 +823,7 @@ def data(readonly=False): ('webgl', SettingValue(typ.Bool(), 'true'), - "Enables or disables WebGL. For QtWebEngine, Qt/PyQt >= 5.7 is " - "required for this setting."), + "Enables or disables WebGL."), ('css-regions', SettingValue(typ.Bool(), 'true', diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 115ee6f75..bafd178f1 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -539,22 +539,6 @@ class TabbedBrowser(tabwidget.TabWidget): # We can get signals for tabs we already deleted... return - # If needed, re-open the tab as a workaround for QTBUG-54419. - # See https://bugreports.qt.io/browse/QTBUG-54419 - if (tab.backend == usertypes.Backend.QtWebEngine and - tab.needs_qtbug54419_workaround and url.isValid()): - log.misc.debug("Doing QTBUG-54419 workaround for {}, " - "url {}".format(tab, url)) - background = self.currentIndex() != idx - self.setUpdatesEnabled(False) - try: - self.tabopen(url, background=background, idx=idx, - ignore_tabs_are_windows=True) - self.close_tab(tab, add_undo=False) - finally: - self.setUpdatesEnabled(True) - tab.needs_qtbug54419_workaround = False - @pyqtSlot(browsertab.AbstractTab, QIcon) def on_icon_changed(self, tab, icon): """Set the icon of a tab. diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index 96e1ed787..5e71e8bb1 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -262,15 +262,16 @@ def get_backend(args): def check_qt_version(backend): """Check if the Qt version is recent enough.""" - from PyQt5.QtCore import qVersion + from PyQt5.QtCore import qVersion, PYQT_VERSION from qutebrowser.utils import qtutils if qtutils.version_check('5.2.0', operator.lt): text = ("Fatal error: Qt and PyQt >= 5.2.0 are required, but {} is " "installed.".format(qVersion())) _die(text) - elif backend == 'webengine' and qtutils.version_check('5.6.0', - operator.lt): - text = ("Fatal error: Qt and PyQt >= 5.6.0 are required for " + elif (backend == 'webengine' and ( + qtutils.version_check('5.7.0', operator.lt) or + PYQT_VERSION < 0x050701)): + text = ("Fatal error: Qt and PyQt >= 5.7.1 are required for " "QtWebEngine support, but {} is installed.".format(qVersion())) _die(text) diff --git a/scripts/link_pyqt.py b/scripts/link_pyqt.py index c93449b47..fffe27fb3 100644 --- a/scripts/link_pyqt.py +++ b/scripts/link_pyqt.py @@ -154,15 +154,6 @@ def link_pyqt(executable, venv_path): copy_or_link(path, dest) -def patch_pypi_pyqt(venv_path): - executable = get_venv_executable(venv_path) - pyqt_dir = os.path.dirname(get_lib_path(executable, 'PyQt5.QtCore')) - qt_conf = os.path.join(pyqt_dir, 'Qt', 'libexec', 'qt.conf') - with open(qt_conf, 'w', encoding='utf-8') as f: - f.write('[Paths]\n') - f.write('Prefix = ..\n') - - def copy_or_link(source, dest): """Copy or symlink source to dest.""" if os.name == 'nt': @@ -186,15 +177,11 @@ def remove(filename): os.unlink(filename) -def get_venv_executable(path): - """Get the Python executable in a virtualenv.""" - subdir = 'Scripts' if os.name == 'nt' else 'bin' - return os.path.join(path, subdir, 'python') - - def get_venv_lib_path(path): """Get the library path of a virtualenv.""" - return run_py(get_venv_executable(path), + subdir = 'Scripts' if os.name == 'nt' else 'bin' + executable = os.path.join(path, subdir, 'python') + return run_py(executable, 'from distutils.sysconfig import get_python_lib', 'print(get_python_lib())') @@ -213,19 +200,15 @@ def main(): parser.add_argument('path', help="Base path to the venv.") parser.add_argument('--tox', help="Add when called via tox.", action='store_true') - parser.add_argument('--pypi', help="Patch a PyPI-installed PyQt 5.6", - action='store_true') args = parser.parse_args() - if args.pypi: - patch_pypi_pyqt(args.path) + if args.tox: + executable = get_tox_syspython(args.path) else: - if args.tox: - executable = get_tox_syspython(args.path) - else: - executable = sys.executable - venv_path = get_venv_lib_path(args.path) - link_pyqt(executable, venv_path) + executable = sys.executable + + venv_path = get_venv_lib_path(args.path) + link_pyqt(executable, venv_path) if __name__ == '__main__': diff --git a/tests/conftest.py b/tests/conftest.py index 8b040361d..f782e810e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -120,13 +120,9 @@ def pytest_collection_modifyitems(config, items): _apply_platform_markers(item) if item.get_marker('xfail_norun'): item.add_marker(pytest.mark.xfail(run=False)) - if item.get_marker('js_prompt'): - if config.webengine: - js_prompt_pyqt_version = 0x050700 - else: - js_prompt_pyqt_version = 0x050300 + if item.get_marker('js_prompt') and not config.webengine: item.add_marker(pytest.mark.skipif( - PYQT_VERSION <= js_prompt_pyqt_version, + PYQT_VERSION <= 0x050300, reason='JS prompts are not supported with this PyQt version')) if deselected: diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index 1e461040d..b55673344 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -197,7 +197,6 @@ Feature: Downloading things from a website. ## mhtml downloads - @qt>=5.8 Scenario: Downloading as mhtml is available When I open data/title.html And I run :download --mhtml @@ -227,7 +226,6 @@ Feature: Downloading things from a website. And I wait for "File successfully written." in the log Then the downloaded file Test title.mhtml should exist - @qt>=5.8 Scenario: Opening a mhtml download directly When I set storage -> prompt-download-directory to true And I open html diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 40b405d9e..3edcfce4d 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -80,7 +80,7 @@ Feature: Various utility commands. And I wait for the javascript message "Hello from JS!" Then "Ignoring world ID 1" should be logged - @qtwebkit_skip @pyqt>=5.7.0 + @qtwebkit_skip Scenario: :jseval uses separate world without --world When I set general -> log-javascript-console to info And I open data/misc/jseval.html @@ -88,14 +88,14 @@ Feature: Various utility commands. Then the javascript message "Hello from the page!" should not be logged And the javascript message "Uncaught ReferenceError: do_log is not defined" should be logged - @qtwebkit_skip @pyqt>=5.7.0 + @qtwebkit_skip Scenario: :jseval using the main world When I set general -> log-javascript-console to info And I open data/misc/jseval.html And I run :jseval --world 0 do_log() Then the javascript message "Hello from the page!" should be logged - @qtwebkit_skip @pyqt>=5.7.0 + @qtwebkit_skip Scenario: :jseval using the main world as name When I set general -> log-javascript-console to info And I open data/misc/jseval.html diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 2ca361529..0f4acaaf6 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -334,9 +334,6 @@ class QuteProc(testprocess.Process): # pylint: disable=no-name-in-module,useless-suppression from PyQt5.QtWebEngineWidgets import QWebEnginePage # pylint: enable=no-name-in-module,useless-suppression - if not hasattr(QWebEnginePage, 'scrollPositionChanged'): - # Qt < 5.7 - pytest.skip("QWebEnginePage.scrollPositionChanged missing") if x is None and y is None: point = 'PyQt5.QtCore.QPoint(*, *)' # not counting 0/0 here elif x == '0' and y == '0': diff --git a/tests/end2end/test_mhtml_e2e.py b/tests/end2end/test_mhtml_e2e.py index 8181b1ed9..46ece8f0e 100644 --- a/tests/end2end/test_mhtml_e2e.py +++ b/tests/end2end/test_mhtml_e2e.py @@ -26,8 +26,6 @@ import collections import pytest -from qutebrowser.utils import qtutils - def collect_tests(): basedir = os.path.dirname(__file__) @@ -106,9 +104,6 @@ def _test_mhtml_requests(test_dir, test_path, httpbin): @pytest.mark.parametrize('test_name', collect_tests()) def test_mhtml(request, test_name, download_dir, quteproc, httpbin): - if not qtutils.version_check('5.7'): - pytest.skip("mhtml is unsupported with Qt < 5.7") - quteproc.set_setting('storage', 'download-directory', download_dir.location) quteproc.set_setting('storage', 'prompt-download-directory', 'false') diff --git a/tox.ini b/tox.ini index c70d44992..2a9ed8c24 100644 --- a/tox.ini +++ b/tox.ini @@ -66,10 +66,7 @@ passenv = {[testenv]passenv} deps = {[testenv]deps} PyQt5==5.6 - sip==4.18.1 -commands = - {envpython} scripts/link_pyqt.py --pypi {envdir} - {envpython} scripts/dev/run_pytest.py {posargs:tests} +commands = {envpython} scripts/dev/run_pytest.py {posargs:tests} [testenv:py35-pyqt571] basepython = python3.5