Greasemonkey: Don't attempt scope isolation on webkit

Since the JSCore used by WebKit 602.1 doesn't fully support Proxy and I
can't think of a way to provide isolation otherwise just revert to the
old behaviour in that case. I am checking for the specific WebKit
version because I'm pretty sure that version just happened to be
released when Proxy support was only partially done, any later release
will presumably have a newer JSCore where it works.

There I changed the indentation of a block in the jinja template which
will have inflated the diff.

I added mocking of `objects.backend` to the `webview` and
`webenginewebview` fixtures, I am pretty sure they are mutually
exclusive so don't expect any issues from that.

Because of the feature detection being at template compile time I had to
tweak the test setup to be done via a fixture instead of the setupClass
functionality that I was using before.
This commit is contained in:
Jimmy 2018-04-25 16:54:54 +12:00
parent 13249329f7
commit b0d1a137da
5 changed files with 91 additions and 72 deletions

View File

@ -31,9 +31,10 @@ import attr
from PyQt5.QtCore import pyqtSignal, QObject, QUrl from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from qutebrowser.utils import (log, standarddir, jinja, objreg, utils, from qutebrowser.utils import (log, standarddir, jinja, objreg, utils,
javascript, urlmatch) javascript, urlmatch, version, usertypes)
from qutebrowser.commands import cmdutils from qutebrowser.commands import cmdutils
from qutebrowser.browser import downloads from qutebrowser.browser import downloads
from qutebrowser.misc import objects
def _scripts_dir(): def _scripts_dir():
@ -108,13 +109,18 @@ class GreasemonkeyScript:
browser's debugger/inspector will not match up to the line browser's debugger/inspector will not match up to the line
numbers in the source script directly. numbers in the source script directly.
""" """
# Don't use Proxy on this webkit version, the support isn't there.
use_proxy = not (
objects.backend == usertypes.Backend.QtWebKit and
version.qWebKitVersion() == '602.1')
template = jinja.js_environment.get_template('greasemonkey_wrapper.js') template = jinja.js_environment.get_template('greasemonkey_wrapper.js')
return template.render( return template.render(
scriptName=javascript.string_escape( scriptName=javascript.string_escape(
"/".join([self.namespace or '', self.name])), "/".join([self.namespace or '', self.name])),
scriptInfo=self._meta_json(), scriptInfo=self._meta_json(),
scriptMeta=javascript.string_escape(self.script_meta), scriptMeta=javascript.string_escape(self.script_meta),
scriptSource=self._code) scriptSource=self._code,
use_proxy=use_proxy)
def _meta_json(self): def _meta_json(self):
return json.dumps({ return json.dumps({

View File

@ -145,6 +145,7 @@
} }
}; };
{% if use_proxy %}
/* /*
* Try to give userscripts an environment that they expect. Which * Try to give userscripts an environment that they expect. Which
* seems to be that the global window object should look the same as * seems to be that the global window object should look the same as
@ -199,4 +200,9 @@
// ====== End User Script ====== // // ====== End User Script ====== //
})(); })();
}; };
{% else %}
// ====== The actual user script source ====== //
{{ scriptSource }}
// ====== End User Script ====== //
{% endif %}
})(); })();

View File

@ -43,10 +43,10 @@ import helpers.stubs as stubsmod
import helpers.utils import helpers.utils
from qutebrowser.config import (config, configdata, configtypes, configexc, from qutebrowser.config import (config, configdata, configtypes, configexc,
configfiles) configfiles)
from qutebrowser.utils import objreg, standarddir, utils from qutebrowser.utils import objreg, standarddir, utils, usertypes
from qutebrowser.browser import greasemonkey from qutebrowser.browser import greasemonkey
from qutebrowser.browser.webkit import cookies from qutebrowser.browser.webkit import cookies
from qutebrowser.misc import savemanager, sql from qutebrowser.misc import savemanager, sql, objects
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
@ -360,9 +360,10 @@ def qnam(qapp):
@pytest.fixture @pytest.fixture
def webengineview(qtbot): def webengineview(qtbot, monkeypatch):
"""Get a QWebEngineView if QtWebEngine is available.""" """Get a QWebEngineView if QtWebEngine is available."""
QtWebEngineWidgets = pytest.importorskip('PyQt5.QtWebEngineWidgets') QtWebEngineWidgets = pytest.importorskip('PyQt5.QtWebEngineWidgets')
monkeypatch.setattr(objects, 'backend', usertypes.Backend.QtWebEngine)
view = QtWebEngineWidgets.QWebEngineView() view = QtWebEngineWidgets.QWebEngineView()
qtbot.add_widget(view) qtbot.add_widget(view)
return view return view
@ -379,9 +380,10 @@ def webpage(qnam):
@pytest.fixture @pytest.fixture
def webview(qtbot, webpage): def webview(qtbot, webpage, monkeypatch):
"""Get a new QWebView object.""" """Get a new QWebView object."""
QtWebKitWidgets = pytest.importorskip('PyQt5.QtWebKitWidgets') QtWebKitWidgets = pytest.importorskip('PyQt5.QtWebKitWidgets')
monkeypatch.setattr(objects, 'backend', usertypes.Backend.QtWebKit)
view = QtWebKitWidgets.QWebView() view = QtWebKitWidgets.QWebView()
qtbot.add_widget(view) qtbot.add_widget(view)

View File

@ -163,10 +163,14 @@ def test_required_scripts_are_included(download_stub, tmpdir):
class TestWindowIsolation: class TestWindowIsolation:
"""Check that greasemonkey scripts get a shadowed global scope.""" """Check that greasemonkey scripts get a shadowed global scope."""
@classmethod @pytest.fixture
def setup_class(cls): def setup(self):
class SetupData:
pass
ret = SetupData()
# Change something in the global scope # Change something in the global scope
cls.setup_script = "window.$ = 'global'" ret.setup_script = "window.$ = 'global'"
# Greasemonkey script to report back on its scope. # Greasemonkey script to report back on its scope.
test_script = greasemonkey.GreasemonkeyScript.parse( test_script = greasemonkey.GreasemonkeyScript.parse(
@ -188,7 +192,7 @@ class TestWindowIsolation:
# The compiled source of that scripts with some additional setup # The compiled source of that scripts with some additional setup
# bookending it. # bookending it.
cls.test_script = "\n".join([ ret.test_script = "\n".join([
"var result = [];", "var result = [];",
test_script.code(), test_script.code(),
""" """
@ -201,21 +205,22 @@ class TestWindowIsolation:
]) ])
# What we expect the script to report back. # What we expect the script to report back.
cls.expected = [ ret.expected = [
"global", "global", "global", "global",
"shadowed", "shadowed", "shadowed", "shadowed",
"global", "global"] "global", "global"]
return ret
def test_webengine(self, callback_checker, webengineview): def test_webengine(self, callback_checker, webengineview, setup):
page = webengineview.page() page = webengineview.page()
page.runJavaScript(self.setup_script) page.runJavaScript(setup.setup_script)
page.runJavaScript(self.test_script, callback_checker.callback) page.runJavaScript(setup.test_script, callback_checker.callback)
callback_checker.check(self.expected) callback_checker.check(setup.expected)
# The JSCore in 602.1 doesn't fully support Proxy. # The JSCore in 602.1 doesn't fully support Proxy.
@pytest.mark.qtwebkit6021_skip @pytest.mark.qtwebkit6021_skip
def test_webkit(self, webview): def test_webkit(self, webview, setup):
elem = webview.page().mainFrame().documentElement() elem = webview.page().mainFrame().documentElement()
elem.evaluateJavaScript(self.setup_script) elem.evaluateJavaScript(setup.setup_script)
result = elem.evaluateJavaScript(self.test_script) result = elem.evaluateJavaScript(setup.test_script)
assert result == self.expected assert result == setup.expected

View File

@ -26,7 +26,7 @@ import pytest
@pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, None)]) @pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, None)])
def test_simple_js_webkit(webview, js_enabled, expected): def test_simple_js_webkit(webview, js_enabled, expected):
"""With QtWebKit, evaluateJavaScript works when JS is on.""" """With QtWebKit, evaluateJavaScript works when JS is on."""
# If we get there (because of the webengineview fixture) we can be certain # If we get there (because of the webview fixture) we can be certain
# QtWebKit is available # QtWebKit is available
from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKit import QWebSettings
webview.settings().setAttribute(QWebSettings.JavascriptEnabled, js_enabled) webview.settings().setAttribute(QWebSettings.JavascriptEnabled, js_enabled)
@ -37,7 +37,7 @@ def test_simple_js_webkit(webview, js_enabled, expected):
@pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, 2.0)]) @pytest.mark.parametrize('js_enabled, expected', [(True, 2.0), (False, 2.0)])
def test_element_js_webkit(webview, js_enabled, expected): def test_element_js_webkit(webview, js_enabled, expected):
"""With QtWebKit, evaluateJavaScript on an element works with JS off.""" """With QtWebKit, evaluateJavaScript on an element works with JS off."""
# If we get there (because of the webengineview fixture) we can be certain # If we get there (because of the webview fixture) we can be certain
# QtWebKit is available # QtWebKit is available
from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKit import QWebSettings
webview.settings().setAttribute(QWebSettings.JavascriptEnabled, js_enabled) webview.settings().setAttribute(QWebSettings.JavascriptEnabled, js_enabled)