Drop legacy QtWebKit support

See #2742
This commit is contained in:
Florian Bruhin 2017-09-18 21:15:14 +02:00
parent 3e70bf5af9
commit 3772dc5930
20 changed files with 44 additions and 375 deletions

View File

@ -170,11 +170,11 @@ Make sure you have `python3_4` in your `PYTHON_TARGETS`
(`/etc/portage/make.conf`) and rebuild your system (`emerge -uDNav @world`) if
necessary.
It's also recommended to install QtWebKit-NG via
https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71[this ebuild],
or install Qt >= 5.7.1 with QtWebEngine in order to use an up-to-date backend.
You'll also need to install `dev-qt/qtwebengine` or a newer QtWebKit using
https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71[this ebuild].
If video or sound don't seem to work, try installing the gstreamer plugins:
If video or sound don't work with QtWebKit, try installing the gstreamer
plugins:
----
# emerge -av gst-plugins-{base,good,bad,ugly,libav}
@ -219,6 +219,8 @@ On openSUSE
There are prebuilt RPMs available at https://software.opensuse.org/download.html?project=network&package=qutebrowser[OBS].
To use the QtWebEngine backend, install `libqt5-qtwebengine`.
On OpenBSD
----------

View File

@ -19,8 +19,6 @@ markers =
qtwebengine_todo: Features still missing with QtWebEngine
qtwebengine_skip: Tests not applicable with QtWebEngine
qtwebkit_skip: Tests not applicable with QtWebKit
qtwebkit_ng_xfail: Tests failing with QtWebKit-NG
qtwebkit_ng_skip: Tests skipped with QtWebKit-NG
qtwebengine_flaky: Tests which are flaky (and currently skipped) with QtWebEngine
qtwebengine_mac_xfail: Tests which fail on macOS with QtWebEngine
js_prompt: Tests needing to display a javascript prompt

View File

@ -332,17 +332,6 @@ def _open_special_pages(args):
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused')
# Legacy QtWebKit warning
needs_warning = (objects.backend == usertypes.Backend.QtWebKit and
not qtutils.is_qtwebkit_ng())
warning_shown = general_sect.get('backend-warning-shown') == '1'
if not warning_shown and needs_warning:
tabbed_browser.tabopen(QUrl('qute://backend-warning'),
background=False)
general_sect['backend-warning-shown'] = '1'
# Quickstart page
quickstart_done = general_sect.get('quickstart-done') == '1'

View File

@ -224,50 +224,13 @@ def qute_history(url):
return 'text/html', json.dumps(history_data(start_time, offset))
else:
if (
config.val.content.javascript.enabled and
(objects.backend == usertypes.Backend.QtWebEngine or
qtutils.is_qtwebkit_ng())
):
return 'text/html', jinja.render(
'history.html',
title='History',
gap_interval=config.val.history_gap_interval
)
else:
# Get current date from query parameter, if not given choose today.
curr_date = datetime.date.today()
try:
query_date = QUrlQuery(url).queryItemValue("date")
if query_date:
curr_date = datetime.datetime.strptime(query_date,
"%Y-%m-%d").date()
except ValueError:
log.misc.debug("Invalid date passed to qute:history: " +
query_date)
one_day = datetime.timedelta(days=1)
next_date = curr_date + one_day
prev_date = curr_date - one_day
# start_time is the last second of curr_date
start_time = time.mktime(next_date.timetuple()) - 1
history = [
(i["url"], i["title"],
datetime.datetime.fromtimestamp(i["time"]),
QUrl(i["url"]).host())
for i in history_data(start_time)
]
return 'text/html', jinja.render(
'history_nojs.html',
title='History',
history=history,
curr_date=curr_date,
next_date=next_date,
prev_date=prev_date,
today=datetime.date.today(),
)
if not config.val.content.javascript.enabled:
return 'text/plain', b'JavaScript is required for qute://history'
return 'text/html', jinja.render(
'history.html',
title='History',
gap_interval=config.val.history_gap_interval
)
@add_handler('javascript')

View File

@ -25,13 +25,7 @@ from PyQt5.QtCore import QByteArray, QDataStream, QIODevice, QUrl
from qutebrowser.utils import qtutils
def _encode_url(url):
"""Encode a QUrl suitable to pass to QWebHistory."""
data = bytes(QUrl.toPercentEncoding(url.toString(), b':/#?&+=@%*'))
return data.decode('ascii')
def _serialize_ng(items, current_idx, stream):
def _serialize_items(items, current_idx, stream):
# {'currentItemIndex': 0,
# 'history': [{'children': [],
# 'documentSequenceNumber': 1485030525573123,
@ -47,13 +41,13 @@ def _serialize_ng(items, current_idx, stream):
# 'urlString': 'about:blank'}]}
data = {'currentItemIndex': current_idx, 'history': []}
for item in items:
data['history'].append(_serialize_item_ng(item))
data['history'].append(_serialize_item(item))
stream.writeInt(3) # history stream version
stream.writeQVariantMap(data)
def _serialize_item_ng(item):
def _serialize_item(item):
data = {
'originalURLString': item.original_url.toString(QUrl.FullyEncoded),
'scrollPosition': {'x': 0, 'y': 0},
@ -68,82 +62,6 @@ def _serialize_item_ng(item):
return data
def _serialize_old(items, current_idx, stream):
### Source/WebKit/qt/Api/qwebhistory.cpp operator<<
stream.writeInt(2) # history stream version
stream.writeInt(len(items))
stream.writeInt(current_idx)
for i, item in enumerate(items):
_serialize_item_old(i, item, stream)
def _serialize_item_old(i, item, stream):
"""Serialize a single WebHistoryItem into a QDataStream.
Args:
i: The index of the current item.
item: The WebHistoryItem to write.
stream: The QDataStream to write to.
"""
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
## urlString
stream.writeQString(_encode_url(item.url))
## title
stream.writeQString(item.title)
## originalURLString
stream.writeQString(_encode_url(item.original_url))
### Source/WebCore/history/HistoryItem.cpp decodeBackForwardTree
## backForwardTreeEncodingVersion
stream.writeUInt32(2)
## size (recursion stack)
stream.writeUInt64(0)
## node->m_documentSequenceNumber
# If two HistoryItems have the same document sequence number, then they
# refer to the same instance of a document. Traversing history from one
# such HistoryItem to another preserves the document.
stream.writeInt64(i + 1)
## size (node->m_documentState)
stream.writeUInt64(0)
## node->m_formContentType
# info used to repost form data
stream.writeQString(None)
## hasFormData
stream.writeBool(False)
## node->m_itemSequenceNumber
# If two HistoryItems have the same item sequence number, then they are
# clones of one another. Traversing history from one such HistoryItem to
# another is a no-op. HistoryItem clones are created for parent and
# sibling frames when only a subframe navigates.
stream.writeInt64(i + 1)
## node->m_referrer
stream.writeQString(None)
## node->m_scrollPoint (x)
try:
stream.writeInt32(item.user_data['scroll-pos'].x())
except (KeyError, TypeError):
stream.writeInt32(0)
## node->m_scrollPoint (y)
try:
stream.writeInt32(item.user_data['scroll-pos'].y())
except (KeyError, TypeError):
stream.writeInt32(0)
## node->m_pageScaleFactor
stream.writeFloat(1)
## hasStateObject
# Support for HTML5 History
stream.writeBool(False)
## node->m_target
stream.writeQString(None)
### Source/WebCore/history/qt/HistoryItemQt.cpp restoreState
## validUserData
# We could restore the user data here, but we prefer to use the
# QWebHistoryItem API for that.
stream.writeBool(False)
def serialize(items):
"""Serialize a list of QWebHistoryItems to a data stream.
@ -180,10 +98,7 @@ def serialize(items):
else:
current_idx = 0
if qtutils.is_qtwebkit_ng():
_serialize_ng(items, current_idx, stream)
else:
_serialize_old(items, current_idx, stream)
_serialize_items(items, current_idx, stream)
user_data += [item.user_data for item in items]

View File

@ -28,7 +28,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl
from PyQt5.QtWidgets import QMessageBox
from qutebrowser.config import configdata, configexc, configtypes, configfiles
from qutebrowser.utils import utils, objreg, message, log, usertypes, jinja
from qutebrowser.utils import utils, objreg, message, log, usertypes, jinja, qtutils
from qutebrowser.misc import objects, msgbox, earlyinit
from qutebrowser.commands import cmdexc, cmdutils, runners
from qutebrowser.completion.models import configmodel
@ -692,12 +692,12 @@ def early_init(args):
def get_backend(args):
"""Find out what backend to use based on available libraries."""
from qutebrowser.utils import usertypes
try:
import PyQt5.QtWebKit # pylint: disable=unused-variable
webkit_available = True
except ImportError:
webkit_available = False
else:
webkit_available = qtutils.is_new_webkit()
if args.backend is not None:
backends = {

View File

@ -51,8 +51,10 @@ class StateConfig(configparser.ConfigParser):
self.add_section(sect)
except configparser.DuplicateSectionError:
pass
# See commit a98060e020a4ba83b663813a4b9404edb47f28ad.
self['general'].pop('fooled', None)
deleted_keys = ['fooled', 'backend-warning-shown']
for key in deleted_keys:
self['general'].pop(key, None)
def init_save_manager(self, save_manager):
"""Make sure the config gets saved properly.

View File

@ -1,100 +0,0 @@
{% extends "styled.html" %}
{% block style %}
{{super()}}
.note {
font-size: smaller;
color: grey;
}
.mono {
font-family: monospace;
}
{% endblock %}
{% block content %}
<h1>Legacy QtWebKit backend</h1>
<span class="note">Note this warning will only appear once. Use <span class="mono">:open
qute://backend-warning</span> to show it again at a later time.</span>
<p>
You're using qutebrowser with the legacy QtWebKit backend. It's still the
default until a few remaining issues are sorted out. If you can, it's
strongly suggested to switch earlier, as legacy QtWebKit has known security
issues and also breaks things on various websites.
</p>
<h2>Using QtWebEngine instead</h2>
<span class="note">This is usually the better choice if you aren't using Nouveau graphics, and
don't need any features which are currently unavailable with QtWebEngine (like
the <span class="mono">qute://settings</span> page or caret browsing).</span>
{% macro install_webengine(package) -%}
You should be able to install <span class="mono">{{ package }}</span> and start qutebrowser with <span class="mono">--backend webengine</span> to use the new backend.
{%- endmacro %}
{% macro please_open_issue() -%}
If you know more, please <a href="https://github.com/qutebrowser/qutebrowser/issues/new">open an issue</a>!
{%- endmacro %}
{% macro unknown_system() -%}
There's no information available for your system. {{ please_open_issue() }}
{%- endmacro %}
<p>
{% if distribution.parsed == Distribution.ubuntu %}
{% if distribution.version == none %}
{{ unknown_system() }}
{% elif distribution.version >= version('17.04') %}
{{ install_webengine('python3-pyqt5.qtwebengine') }}
{% elif distribution.version >= version('16.04') %}
QtWebEngine is only available in Ubuntu's repositories since 17.04, but you can <a href="https://github.com/qutebrowser/qutebrowser/blob/master/doc/install.asciidoc#installing-qutebrowser-with-tox">install qutebrowser via tox</a> with <span class="mono">tox -e mkvenv-pypi</span> to use the new backend.
{% else %}
Unfortunately, no easy way is known to install QtWebEngine on Ubuntu &lt; 16.04. {{ please_open_issue() }}
{% endif %}
{% elif distribution.parsed == Distribution.debian %}
{% if distribution.version == none %}
{{ unknown_system() }}
{% elif distribution.version >= version('9') %}
{{ install_webengine('python3-pyqt5.qtwebengine') }}
{% else %}
Unfortunately, no easy way is known to install QtWebEngine on Debian &lt; 9. {{ please_open_issue() }}
{% endif %}
{% elif distribution.parsed in [Distribution.arch, Distribution.manjaro] %}
{{ install_webengine('qt5-webengine') }}
{% elif distribution.parsed == Distribution.void %}
{{ install_webengine('python-PyQt5-webengine') }}
{% elif distribution.parsed == Distribution.fedora %}
{{ install_webengine('qt5-qtwebengine') }}
{% elif distribution.parsed == Distribution.opensuse %}
{{ install_webengine('libqt5-qtwebengine') }}
{% elif distribution.parsed == Distribution.gentoo %}
{{ install_webengine('dev-qt/qtwebengine') }}
{% else %}
{{ unknown_system() }}
{% endif %}
</p>
<h2>Using QtWebKit-NG instead</h2>
<span class="note">This is a drop-in replacement for legacy QtWebKit.</span>
<p>
{% if distribution.parsed == Distribution.debian and distribution.version != none and distribution.version >= version('9') %}
There are unofficial QtWebKit-NG packages <a href="http://repo.paretje.be/unstable/">available</a>.
{% elif distribution.parsed in [Distribution.ubuntu, Distribution.debian] %}
No easy way is known to install QtWebKit-NG on your system.
There are unofficial QtWebKit-NG packages <a href="http://repo.paretje.be/unstable/">available</a>, but they are intended for Debian Unstable.
{{ please_open_issue() }}
{% elif distribution.parsed in [Distribution.arch, Distribution.manjaro] %}
With an updated <span class="mono">qt5-webkit</span> package, you should already get QtWebKit-NG.
{% elif distribution.parsed == Distribution.gentoo %}
There's an unofficial <a href="https://gist.github.com/annulen/309569fb61e5d64a703c055c1e726f71">ebuild</a> available.
{% else %}
{{ unknown_system() }}
{% endif %}
</p>
{% endblock %}

View File

@ -1,61 +0,0 @@
{% extends "styled.html" %}
{% block style %}
{{super()}}
body {
max-width: 1440px;
}
td.title {
word-break: break-all;
}
td.time {
color: #555;
text-align: right;
white-space: nowrap;
}
table {
margin-bottom: 30px;
}
.date {
color: #555;
font-size: 12pt;
padding-bottom: 15px;
font-weight: bold;
text-align: left;
}
.pagination-link {
color: #555;
font-weight: bold;
margn-bottom: 15px;
text-decoration: none;
}
{% endblock %}
{% block content %}
<h1>Browsing history</h1>
<table>
<caption class="date">{{curr_date.strftime("%a, %d %B %Y")}}</caption>
<tbody>
{% for url, title, time, host in history %}
<tr>
<td class="title">
<a href="{{url}}">{{title}}</a>
<span class="hostname">{{host}}</span>
</td>
<td class="time">{{time.strftime("%X")}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<span class="pagination-link"><a href="qute://history/?date={{prev_date.strftime("%Y-%m-%d")}}" rel="prev">Previous</a></span>
{% if today >= next_date %}
<span class="pagination-link"><a href="qute://history/?date={{next_date.strftime("%Y-%m-%d")}}" rel="next">Next</a></span>
{% endif %}
{% endblock %}

View File

@ -288,6 +288,14 @@ def check_backend_libraries(backend):
_check_modules(modules)
def check_new_webkit(backend):
"""Make sure we use QtWebEngine or a new QtWebKit."""
from qutebrowser.utils import usertypes, qtutils
if backend == usertypes.Backend.QtWebKit and not qtutils.is_new_qtwebkit():
_die("qutebrowser does not support legacy QtWebKit versions anymore, "
"see the installation docs for details.")
def remove_inputhook():
"""Remove the PyQt input hook.
@ -352,3 +360,4 @@ def init_with_backend(backend):
assert backend is not None
check_backend_libraries(backend)
check_backend_ssl_support(backend)
check_new_webkit(backend)

View File

@ -91,8 +91,8 @@ def version_check(version, exact=False, strict=False):
return result
def is_qtwebkit_ng():
"""Check if the given version is QtWebKit-NG."""
def is_new_qtwebkit():
"""Check if the given version is a new QtWebKit."""
assert qWebKitVersion is not None
return (pkg_resources.parse_version(qWebKitVersion()) >
pkg_resources.parse_version('538.1'))

View File

@ -304,9 +304,7 @@ def _chromium_version():
def _backend():
"""Get the backend line with relevant information."""
if objects.backend == usertypes.Backend.QtWebKit:
return '{} (WebKit {})'.format(
'QtWebKit-NG' if qtutils.is_qtwebkit_ng() else 'legacy QtWebKit',
qWebKitVersion())
return 'new QtWebKit (WebKit {})'.format(qWebKitVersion())
else:
webengine = usertypes.Backend.QtWebEngine
assert objects.backend == webengine, objects.backend

View File

@ -109,8 +109,6 @@ def _get_backend_tag(tag):
'qtwebengine_todo': pytest.mark.qtwebengine_todo,
'qtwebengine_skip': pytest.mark.qtwebengine_skip,
'qtwebkit_skip': pytest.mark.qtwebkit_skip,
'qtwebkit_ng_xfail': pytest.mark.qtwebkit_ng_xfail,
'qtwebkit_ng_skip': pytest.mark.qtwebkit_ng_skip,
}
if not any(tag.startswith(t + ':') for t in pytest_marks):
return None
@ -143,10 +141,6 @@ def pytest_collection_modifyitems(config, items):
config.webengine),
('qtwebkit_skip', 'Skipped with QtWebKit', pytest.mark.skipif,
not config.webengine),
('qtwebkit_ng_xfail', 'Failing with QtWebKit-NG', pytest.mark.xfail,
not config.webengine and qtutils.is_qtwebkit_ng()),
('qtwebkit_ng_skip', 'Skipped with QtWebKit-NG', pytest.mark.skipif,
not config.webengine and qtutils.is_qtwebkit_ng()),
('qtwebengine_flaky', 'Flaky with QtWebEngine', pytest.mark.skipif,
config.webengine),
('qtwebengine_mac_xfail', 'Fails on macOS with QtWebEngine',

View File

@ -6,11 +6,7 @@
var my_window;
function open_modal() {
if (window.showModalDialog) {
window.showModalDialog();
} else {
window.open('about:blank', 'window', 'modal');
}
window.open('about:blank', 'window', 'modal');
}
function open_normal() {

View File

@ -103,13 +103,4 @@ Feature: Page history
And I open qute:history without waiting
And I wait until qute://history is loaded
Then the page should contain the plaintext "3.txt"
Then the page should contain the plaintext "4.txt"
## Bugs
@qtwebengine_skip @qtwebkit_ng_skip
Scenario: Opening a valid URL which turns out invalid
When I set url.auto_search to naive
And I run :open http://foo%40bar@baz
Then "QFSFileEngine::open: No file name specified" should be logged
And "Error while loading : Host not found" should be logged
Then the page should contain the plaintext "4.txt"

View File

@ -17,7 +17,7 @@ Feature: Javascript stuff
And I run :click-element id close-normal
Then "Focus object changed: *" should be logged
@qtwebkit_ng_skip
@qtwebkit_skip
Scenario: Opening/closing a modal window via JS
When I open data/javascript/window_open.html
And I run :tab-only
@ -26,7 +26,6 @@ Feature: Javascript stuff
And I run :tab-focus 1
And I run :click-element id close-normal
Then "Focus object changed: *" should be logged
# WebModalDialog with QtWebKit, WebDialog with QtWebEngine
And "Web*Dialog requested, but we don't support that!" should be logged
# https://github.com/qutebrowser/qutebrowser/issues/906

View File

@ -42,7 +42,6 @@ Feature: Using private browsing
## https://github.com/qutebrowser/qutebrowser/issues/1219
@qtwebkit_ng_skip: private browsing is not implemented yet
Scenario: Sharing cookies with private browsing
When I open cookies/set?qute-test=42 without waiting in a private window
And I wait until cookies is loaded

View File

@ -31,21 +31,3 @@ def test_init_faulthandler_stderr_none(monkeypatch, attr):
"""Make sure init_faulthandler works when sys.stderr/__stderr__ is None."""
monkeypatch.setattr(sys, attr, None)
earlyinit.init_faulthandler()
@pytest.mark.parametrize('same', [True, False])
def test_qt_version(same):
if same:
qt_version_str = '5.4.0'
expected = '5.4.0'
else:
qt_version_str = '5.3.0'
expected = '5.4.0 (compiled 5.3.0)'
actual = earlyinit.qt_version(qversion='5.4.0',
qt_version_str=qt_version_str)
assert actual == expected
def test_qt_version_no_args():
"""Make sure qt_version without arguments at least works."""
earlyinit.qt_version()

View File

@ -82,14 +82,14 @@ def test_version_check(monkeypatch, qversion, compiled, version, exact,
assert qtutils.version_check(version, exact, strict=strict) == expected
@pytest.mark.parametrize('version, ng', [
@pytest.mark.parametrize('version, is_new', [
('537.21', False), # QtWebKit 5.1
('538.1', False), # Qt 5.8
('602.1', True) # QtWebKit-NG TP5
('602.1', True) # new QtWebKit TP5, 5.212 Alpha
])
def test_is_qtwebkit_ng(monkeypatch, version, ng):
def test_is_new_qtwebkit(monkeypatch, version, is_new):
monkeypatch.setattr(qtutils, 'qWebKitVersion', lambda: version)
assert qtutils.is_qtwebkit_ng() == ng
assert qtutils.is_new_qtwebkit() == is_new
class TestCheckOverflow:

View File

@ -839,7 +839,6 @@ class VersionParams:
VersionParams('frozen', frozen=True),
VersionParams('no-style', style=False),
VersionParams('no-webkit', with_webkit=False),
VersionParams('webkit-ng', with_webkit='ng'),
VersionParams('unknown-dist', known_distribution=False),
VersionParams('no-ssl', ssl_support=False),
], ids=lambda param: param.name)
@ -884,13 +883,7 @@ def test_version_output(params, stubs, monkeypatch):
patches['qWebKitVersion'] = lambda: 'WEBKIT VERSION'
patches['objects.backend'] = usertypes.Backend.QtWebKit
patches['QWebEngineProfile'] = None
if params.with_webkit == 'ng':
backend = 'QtWebKit-NG'
patches['qtutils.is_qtwebkit_ng'] = lambda: True
else:
backend = 'legacy QtWebKit'
patches['qtutils.is_qtwebkit_ng'] = lambda: False
substitutions['backend'] = backend + ' (WebKit WEBKIT VERSION)'
substitutions['backend'] = 'new QtWebKit (WebKit WEBKIT VERSION)'
else:
monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False)
patches['objects.backend'] = usertypes.Backend.QtWebEngine