summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoofar <toofar@spalge.com>2023-04-30 13:52:20 +1200
committertoofar <toofar@spalge.com>2023-04-30 15:12:46 +1200
commitacb58804c35aef479fa26e5a0b0d8dbf992ec902 (patch)
treed429271d3c76b93073e096abb3502dcbd31d6790
parent346a39ba14bdc05ce21dc1233027803dcaf34cc6 (diff)
downloadqutebrowser-acb58804c35aef479fa26e5a0b0d8dbf992ec902.tar.gz
qutebrowser-acb58804c35aef479fa26e5a0b0d8dbf992ec902.zip
Move all qwebenginescript logic up to profile.
Qt had a regression relating to injecting scripts in 6.5. This only applied to QWebEngineScripts in a page's collection, ones in a profile's collection where fine. Since QWebEngineScripts learnt about greasemonkey compatible metadata (a few years ago now) Qt manages when scripts are injected and into what sites. So from that point of view all we have to do is make sure scripts are registered with Qt when a profile is initialised and then forget about them. So moving up to the profile level fits that lifecycle better. This is an initial, low effort, attempt at moving script registrations to be up in the profile. Here's what I've done: * move everything around QWebEngineScript out of webenginetab up to webenginesettings * injecting greasemonkey scripts * injecting site specific quirks (the site specific part is actually managed by greasemonkey metadata) * injecting global JS that is used on every page like hint, caret and stylesheet utility code * move JS_WORLD_MAP up to qtutils alongside MAX_WORLD_ID * this now introduces backend specific code into this module * move greasemonkey initialisation to be earlier so the singleton manager exists when webenginesettings are initialized * I haven't looked at what dependancies the grasemonkey module has, if this causes issue we could split the greasemonkey initialization up (part one: create singleton, part two: read all scripts + fire reloaded signal) * the profile level stylesheet is still overriden in the tab when a) a search is started or ended, to show/hide the scroll bar b) when the user stylesheets setting changes, so you don't have to reload the page Moving everything up off of an object, in webenginetab, up to module level function in webenginesettings meant removing a bunch of references to "self" and using functools.partial to retain references to profiles for signals. Subclassing QWebEngineProfile would probably help make that stuff a little more robust, and would help us move towards having an arbitrary number of profiles. But the only downside I can think of right now is that signal connections wont get cleaned up when profiles are deleted because they aren't connected to bound methods. But we aren't currently deleting profiles (apart from at shutdown). I left a couple of comments in around possible improvements. The interface for the change_filter decorator surprised me a bit. `_inject_greasemonkey_scripts()` might be able to be made smaller by re-using the script factory. Or moving the world validation out. Regarding the original regression in 6.5, regarding all the global scripts like stylesheet, hint, caret etc. That issue can also be worked around by injecting them twice (at document created and deferred). They all have guards in the code so should be idempotent. That doesn't help greasemonkey scripts though which are also affected. I haven't tried running the tests :) ref: #7662
-rw-r--r--qutebrowser/app.py6
-rw-r--r--qutebrowser/browser/webengine/webenginesettings.py242
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py203
-rw-r--r--qutebrowser/utils/qtutils.py14
4 files changed, 259 insertions, 206 deletions
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index db7eea608..69f181483 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -490,6 +490,9 @@ def _init_modules(*, args):
log.init.debug("Initializing command history...")
cmdhistory.init()
+ log.init.debug("Initializing Greasemonkey...")
+ greasemonkey.init()
+
log.init.debug("Initializing websettings...")
websettings.init(args)
quitter.instance.shutting_down.connect(websettings.shutdown)
@@ -517,9 +520,6 @@ def _init_modules(*, args):
log.init.debug("Initializing downloads...")
qtnetworkdownloads.init()
- log.init.debug("Initializing Greasemonkey...")
- greasemonkey.init()
-
log.init.debug("Misc initialization...")
macros.init()
windowundo.init()
diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py
index 8a8c4766f..e6c4bae98 100644
--- a/qutebrowser/browser/webengine/webenginesettings.py
+++ b/qutebrowser/browser/webengine/webenginesettings.py
@@ -27,20 +27,25 @@ Module attributes:
import os
import operator
import pathlib
+import functools
+import dataclasses
from typing import cast, Any, List, Optional, Tuple, Union, TYPE_CHECKING
from qutebrowser.qt import machinery
from qutebrowser.qt.gui import QFont
from qutebrowser.qt.widgets import QApplication
-from qutebrowser.qt.webenginecore import QWebEngineSettings, QWebEngineProfile
+from qutebrowser.qt.webenginecore import (
+ QWebEngineSettings, QWebEngineProfile, QWebEngineScript,
+)
-from qutebrowser.browser import history
+from qutebrowser.browser import history, shared, greasemonkey
from qutebrowser.browser.webengine import (spell, webenginequtescheme, cookies,
webenginedownloads, notification)
from qutebrowser.config import config, websettings
from qutebrowser.config.websettings import AttributeInfo as Attr
from qutebrowser.utils import (standarddir, qtutils, message, log,
- urlmatch, usertypes, objreg, version)
+ urlmatch, usertypes, objreg, version,
+ javascript, resources, utils)
if TYPE_CHECKING:
from qutebrowser.browser.webengine import interceptor
@@ -359,6 +364,235 @@ def init_user_agent():
_init_user_agent_str(QWebEngineProfile.defaultProfile().httpUserAgent())
+# TODO: add to javascript module?
+def _script_factory(name, js_code, *,
+ world=QWebEngineScript.ScriptWorldId.ApplicationWorld,
+ injection_point=QWebEngineScript.InjectionPoint.DocumentCreation,
+ subframes=False):
+ """Inject the given script to run early on a page load."""
+ script = QWebEngineScript()
+ script.setInjectionPoint(injection_point)
+ script.setSourceCode(js_code)
+ script.setWorldId(world)
+ script.setRunsOnSubFrames(subframes)
+ script.setName(f'_qute_{name}')
+ return script
+
+
+def _remove_js(scripts, name):
+ """Remove an early QWebEngineScript."""
+ if machinery.IS_QT6:
+ for script in scripts.find(f'_qute_{name}'):
+ scripts.remove(script)
+ else: # Qt 5
+ script = scripts.findScript(f'_qute_{name}')
+ if not script.isNull():
+ scripts.remove(script)
+
+
+# TODO: unrelated rambling
+# Hmm, change_filter can be told it is being passed a function (unbound
+# method) or method (method on an instantiated object). Here I'm telling it
+# these are object methods, although they aren't, just because the only
+# difference between those modes is that an argument is passed through for the
+# object methods. Called "self" in the wrapper it doesn't have to be.
+# Probably the change_filter decorator could be changed to support passing
+# trough variable arguments and get rid of that split?
+# Also it would be nice to have a decorator that did the change filtering and
+# handled connecting a signal, and passed the new value into the function.
+@config.change_filter('scrolling.bar')
+@config.change_filter('content.user_stylesheets')
+def _stylesheet_option_changed(profile):
+ _inject_stylesheet(profile.scripts())
+
+
+def _inject_stylesheet(scripts):
+ """Initialize custom stylesheets.
+
+ Stylesheet CSS is also overriden in individual tabs when config is updated
+ and when find operations are started and ended.
+
+ Partially inspired by QupZilla:
+ https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/app/mainapplication.cpp#L1063-L1101
+ """
+ _remove_js(scripts, 'stylesheet')
+ css = shared.get_user_stylesheet()
+ js_code = javascript.wrap_global(
+ 'stylesheet',
+ resources.read_file('javascript/stylesheet.js'),
+ javascript.assemble('stylesheet', 'set_css', css),
+ )
+ scripts.insert(_script_factory('stylesheet', js_code, subframes=True))
+
+
+def _remove_all_greasemonkey_scripts(profile_scripts):
+ for script in profile_scripts.toList():
+ if script.name().startswith("GM-"):
+ log.greasemonkey.debug('Removing script: {}'
+ .format(script.name()))
+ removed = profile_scripts.remove(script)
+ assert removed, script.name()
+
+
+def _inject_all_greasemonkey_scripts(profile):
+ scripts = greasemonkey.gm_manager.all_scripts()
+ _inject_greasemonkey_scripts(profile, scripts)
+
+
+def _inject_greasemonkey_scripts(profile, scripts):
+ """Register user JavaScript files with the current tab.
+
+ Args:
+ scripts: A list of GreasemonkeyScripts.
+ """
+ profile_scripts = profile.scripts()
+ # Remove and re-add all scripts every time to account for scripts
+ # that have been disabled.
+ _remove_all_greasemonkey_scripts(profile_scripts)
+
+ seen_names = set()
+ for script in scripts:
+ while script.full_name() in seen_names:
+ script.dedup_suffix += 1
+ seen_names.add(script.full_name())
+
+ # TODO: move to use _script_factory to shorten the method?
+ new_script = QWebEngineScript()
+
+ try:
+ world = int(script.jsworld)
+ if not 0 <= world <= qtutils.MAX_WORLD_ID:
+ log.greasemonkey.error(
+ f"script {script.name} has invalid value for '@qute-js-world'"
+ f": {script.jsworld}, should be between 0 and "
+ f"{qtutils.MAX_WORLD_ID}")
+ continue
+ except ValueError:
+ try:
+ world = qtutils.JS_WORLD_MAP[usertypes.JsWorld[script.jsworld.lower()]]
+ except KeyError:
+ log.greasemonkey.error(
+ f"script {script.name} has invalid value for '@qute-js-world'"
+ f": {script.jsworld}")
+ continue
+ new_script.setWorldId(world)
+
+ # Corresponds to "@run-at document-end" which is the default according to
+ # https://wiki.greasespot.net/Metadata_Block#.40run-at - however,
+ # QtWebEngine uses QWebEngineScript.InjectionPoint.Deferred (@run-at document-idle) as
+ # default.
+ #
+ # NOTE that this needs to be done before setSourceCode, so that
+ # QtWebEngine's parsing of GreaseMonkey tags will override it if there is a
+ # @run-at comment.
+ new_script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady)
+
+ new_script.setSourceCode(script.code())
+ new_script.setName(script.full_name())
+ new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
+
+ if script.needs_document_end_workaround():
+ log.greasemonkey.debug(
+ f"Forcing @run-at document-end for {script.name}")
+ new_script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady)
+
+ log.greasemonkey.debug(f'adding script: {new_script.name()}')
+ profile_scripts.insert(new_script)
+
+
+@dataclasses.dataclass
+class _Quirk:
+
+ filename: str
+ injection_point: QWebEngineScript.InjectionPoint = (
+ QWebEngineScript.InjectionPoint.DocumentCreation)
+ world: QWebEngineScript.ScriptWorldId = QWebEngineScript.ScriptWorldId.MainWorld
+ predicate: bool = True
+ name: Optional[str] = None
+
+ def __post_init__(self):
+ if self.name is None:
+ self.name = f"js-{self.filename.replace('_', '-')}"
+
+
+def _get_quirks():
+ """Get a list of all available JS quirks."""
+ versions = version.qtwebengine_versions()
+ return [
+ # FIXME:qt6 Double check which of those are still required
+ _Quirk(
+ 'whatsapp_web',
+ injection_point=QWebEngineScript.InjectionPoint.DocumentReady,
+ world=QWebEngineScript.ScriptWorldId.ApplicationWorld,
+ ),
+ _Quirk('discord'),
+ _Quirk(
+ 'googledocs',
+ # will be an UA quirk once we set the JS UA as well
+ name='ua-googledocs',
+ ),
+
+ _Quirk(
+ 'string_replaceall',
+ predicate=versions.webengine < utils.VersionNumber(5, 15, 3),
+ ),
+ _Quirk(
+ 'array_at',
+ predicate=versions.webengine < utils.VersionNumber(6, 3),
+ ),
+ ]
+
+
+def _inject_site_specific_quirks(scripts):
+ """Add site-specific quirk scripts."""
+ if not config.val.content.site_specific_quirks.enabled:
+ return
+
+ for quirk in _get_quirks():
+ if not quirk.predicate:
+ continue
+ src = resources.read_file(f'javascript/quirks/{quirk.filename}.user.js')
+ if quirk.name not in config.val.content.site_specific_quirks.skip:
+ scripts.insert(_script_factory(
+ f'quirk_{quirk.filename}',
+ src,
+ world=quirk.world,
+ injection_point=quirk.injection_point,
+ ))
+
+
+def _init_scripts_for_profile(profile: QWebEngineProfile) -> None:
+ scripts = profile.scripts()
+
+ # Early global scripts
+ js_code = javascript.wrap_global(
+ 'scripts',
+ resources.read_file('javascript/scroll.js'),
+ resources.read_file('javascript/webelem.js'),
+ resources.read_file('javascript/caret.js'),
+ )
+ # FIXME:qtwebengine what about subframes=True?
+ scripts.insert(_script_factory('js', js_code, subframes=True))
+
+ _inject_stylesheet(scripts)
+ config.instance.changed.connect(
+ functools.partial(
+ _stylesheet_option_changed,
+ profile,
+ )
+ )
+
+ greasemonkey.gm_manager.scripts_reloaded.connect(
+ functools.partial(
+ _inject_all_greasemonkey_scripts,
+ profile,
+ )
+ )
+ _inject_all_greasemonkey_scripts(profile)
+
+ _inject_site_specific_quirks(scripts)
+
+
def _init_profile(profile: QWebEngineProfile) -> None:
"""Initialize a new QWebEngineProfile.
@@ -384,6 +618,8 @@ def _init_profile(profile: QWebEngineProfile) -> None:
_global_settings.init_settings()
+ _init_scripts_for_profile(profile)
+
def _init_default_profile():
"""Init the default QWebEngineProfile."""
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 6f0ea82f3..23933cf7f 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -24,7 +24,7 @@ import functools
import dataclasses
import re
import html as html_utils
-from typing import cast, Union, Optional
+from typing import cast, Union
from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QTimer, QUrl,
QObject)
@@ -33,13 +33,13 @@ from qutebrowser.qt.webenginewidgets import QWebEngineView
from qutebrowser.qt.webenginecore import QWebEnginePage, QWebEngineScript, QWebEngineHistory
from qutebrowser.config import config
-from qutebrowser.browser import browsertab, eventfilter, shared, webelem, greasemonkey
+from qutebrowser.browser import browsertab, eventfilter, shared, webelem
from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
webenginesettings, certificateerror,
webengineinspector)
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
- resources, message, jinja, debug, version)
+ message, jinja, debug, version)
from qutebrowser.qt import sip, machinery
from qutebrowser.misc import objects, miscwidgets
@@ -1001,21 +1001,6 @@ class _WebEnginePermissions(QObject):
blocking=True)
-@dataclasses.dataclass
-class _Quirk:
-
- filename: str
- injection_point: QWebEngineScript.InjectionPoint = (
- QWebEngineScript.InjectionPoint.DocumentCreation)
- world: QWebEngineScript.ScriptWorldId = QWebEngineScript.ScriptWorldId.MainWorld
- predicate: bool = True
- name: Optional[str] = None
-
- def __post_init__(self):
- if self.name is None:
- self.name = f"js-{self.filename.replace('_', '-')}"
-
-
class _WebEngineScripts(QObject):
_widget: webview.WebEngineView
@@ -1024,7 +1009,6 @@ class _WebEngineScripts(QObject):
super().__init__(parent)
self._tab = tab
self._widget = cast(webview.WebEngineView, None)
- self._greasemonkey = greasemonkey.gm_manager
def connect_signals(self):
"""Connect signals to our private slots."""
@@ -1037,7 +1021,6 @@ class _WebEngineScripts(QObject):
@pyqtSlot(str)
def _on_config_changed(self, option):
if option in ['scrolling.bar', 'content.user_stylesheets']:
- self._init_stylesheet()
self._update_stylesheet()
@pyqtSlot(bool)
@@ -1047,185 +1030,6 @@ class _WebEngineScripts(QObject):
code = javascript.assemble('stylesheet', 'set_css', css)
self._tab.run_js_async(code)
- def _inject_js(self, name, js_code, *,
- world=QWebEngineScript.ScriptWorldId.ApplicationWorld,
- injection_point=QWebEngineScript.InjectionPoint.DocumentCreation,
- subframes=False):
- """Inject the given script to run early on a page load."""
- script = QWebEngineScript()
- script.setInjectionPoint(injection_point)
- script.setSourceCode(js_code)
- script.setWorldId(world)
- script.setRunsOnSubFrames(subframes)
- script.setName(f'_qute_{name}')
- self._widget.page().scripts().insert(script)
-
- def _remove_js(self, name):
- """Remove an early QWebEngineScript."""
- scripts = self._widget.page().scripts()
- if machinery.IS_QT6:
- for script in scripts.find(f'_qute_{name}'):
- scripts.remove(script)
- else: # Qt 5
- script = scripts.findScript(f'_qute_{name}')
- if not script.isNull():
- scripts.remove(script)
-
- def init(self):
- """Initialize global qutebrowser JavaScript."""
- js_code = javascript.wrap_global(
- 'scripts',
- resources.read_file('javascript/scroll.js'),
- resources.read_file('javascript/webelem.js'),
- resources.read_file('javascript/caret.js'),
- )
- # FIXME:qtwebengine what about subframes=True?
- self._inject_js('js', js_code, subframes=True)
- self._init_stylesheet()
-
- self._greasemonkey.scripts_reloaded.connect(
- self._inject_all_greasemonkey_scripts)
- self._inject_all_greasemonkey_scripts()
- self._inject_site_specific_quirks()
-
- def _init_stylesheet(self):
- """Initialize custom stylesheets.
-
- Partially inspired by QupZilla:
- https://github.com/QupZilla/qupzilla/blob/v2.0/src/lib/app/mainapplication.cpp#L1063-L1101
- """
- self._remove_js('stylesheet')
- css = shared.get_user_stylesheet()
- js_code = javascript.wrap_global(
- 'stylesheet',
- resources.read_file('javascript/stylesheet.js'),
- javascript.assemble('stylesheet', 'set_css', css),
- )
- self._inject_js('stylesheet', js_code, subframes=True)
-
- @pyqtSlot()
- def _inject_all_greasemonkey_scripts(self):
- scripts = self._greasemonkey.all_scripts()
- self._inject_greasemonkey_scripts(scripts)
-
- def _remove_all_greasemonkey_scripts(self):
- page_scripts = self._widget.page().scripts()
- for script in page_scripts.toList():
- if script.name().startswith("GM-"):
- log.greasemonkey.debug('Removing script: {}'
- .format(script.name()))
- removed = page_scripts.remove(script)
- assert removed, script.name()
-
- def _inject_greasemonkey_scripts(self, scripts):
- """Register user JavaScript files with the current tab.
-
- Args:
- scripts: A list of GreasemonkeyScripts.
- """
- if sip.isdeleted(self._widget):
- return
-
- # Since we are inserting scripts into a per-tab collection,
- # rather than just injecting scripts on page load, we need to
- # make sure we replace existing scripts, not just add new ones.
- # While, taking care not to remove any other scripts that might
- # have been added elsewhere, like the one for stylesheets.
- page_scripts = self._widget.page().scripts()
- self._remove_all_greasemonkey_scripts()
-
- seen_names = set()
- for script in scripts:
- while script.full_name() in seen_names:
- script.dedup_suffix += 1
- seen_names.add(script.full_name())
-
- new_script = QWebEngineScript()
-
- try:
- world = int(script.jsworld)
- if not 0 <= world <= qtutils.MAX_WORLD_ID:
- log.greasemonkey.error(
- f"script {script.name} has invalid value for '@qute-js-world'"
- f": {script.jsworld}, should be between 0 and "
- f"{qtutils.MAX_WORLD_ID}")
- continue
- except ValueError:
- try:
- world = _JS_WORLD_MAP[usertypes.JsWorld[script.jsworld.lower()]]
- except KeyError:
- log.greasemonkey.error(
- f"script {script.name} has invalid value for '@qute-js-world'"
- f": {script.jsworld}")
- continue
- new_script.setWorldId(world)
-
- # Corresponds to "@run-at document-end" which is the default according to
- # https://wiki.greasespot.net/Metadata_Block#.40run-at - however,
- # QtWebEngine uses QWebEngineScript.InjectionPoint.Deferred (@run-at document-idle) as
- # default.
- #
- # NOTE that this needs to be done before setSourceCode, so that
- # QtWebEngine's parsing of GreaseMonkey tags will override it if there is a
- # @run-at comment.
- new_script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady)
-
- new_script.setSourceCode(script.code())
- new_script.setName(script.full_name())
- new_script.setRunsOnSubFrames(script.runs_on_sub_frames)
-
- if script.needs_document_end_workaround():
- log.greasemonkey.debug(
- f"Forcing @run-at document-end for {script.name}")
- new_script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady)
-
- log.greasemonkey.debug(f'adding script: {new_script.name()}')
- page_scripts.insert(new_script)
-
- def _get_quirks(self):
- """Get a list of all available JS quirks."""
- versions = version.qtwebengine_versions()
- return [
- # FIXME:qt6 Double check which of those are still required
- _Quirk(
- 'whatsapp_web',
- injection_point=QWebEngineScript.InjectionPoint.DocumentReady,
- world=QWebEngineScript.ScriptWorldId.ApplicationWorld,
- ),
- _Quirk('discord'),
- _Quirk(
- 'googledocs',
- # will be an UA quirk once we set the JS UA as well
- name='ua-googledocs',
- ),
-
- _Quirk(
- 'string_replaceall',
- predicate=versions.webengine < utils.VersionNumber(5, 15, 3),
- ),
- _Quirk(
- 'array_at',
- predicate=versions.webengine < utils.VersionNumber(6, 3),
- ),
- ]
-
- def _inject_site_specific_quirks(self):
- """Add site-specific quirk scripts."""
- if not config.val.content.site_specific_quirks.enabled:
- return
-
- for quirk in self._get_quirks():
- if not quirk.predicate:
- continue
- src = resources.read_file(f'javascript/quirks/{quirk.filename}.user.js')
- if quirk.name not in config.val.content.site_specific_quirks.skip:
- self._inject_js(
- f'quirk_{quirk.filename}',
- src,
- world=quirk.world,
- injection_point=quirk.injection_point,
- )
-
class WebEngineTabPrivate(browsertab.AbstractTabPrivate):
@@ -1301,7 +1105,6 @@ class WebEngineTab(browsertab.AbstractTab):
self.backend = usertypes.Backend.QtWebEngine
self._child_event_filter = None
self._saved_zoom = None
- self._scripts.init()
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-65223
self._needs_qtbug65223_workaround = (
version.qtwebengine_versions().webengine < utils.VersionNumber(5, 15, 5))
diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py
index 0ecc4cba6..d2cd4bb49 100644
--- a/qutebrowser/utils/qtutils.py
+++ b/qutebrowser/utils/qtutils.py
@@ -25,6 +25,7 @@ Module attributes:
MINVALS: A dictionary of C/Qt types (as string) mapped to their minimum
value.
MAX_WORLD_ID: The highest world ID allowed by QtWebEngine.
+ JS_WORLD_MAP: Mapping form usertypes.JsWorld to QWebEngineScript.ScriptWorldId
"""
@@ -41,6 +42,7 @@ from qutebrowser.qt.core import (qVersion, QEventLoop, QDataStream, QByteArray,
QIODevice, QFileDevice, QSaveFile, QT_VERSION_STR,
PYQT_VERSION_STR, QObject, QUrl, QLibraryInfo)
from qutebrowser.qt.gui import QColor
+from qutebrowser.qt.webenginecore import QWebEngineScript
try:
from qutebrowser.qt.webkit import qWebKitVersion
except ImportError: # pragma: no cover
@@ -117,6 +119,18 @@ def version_check(version: str,
MAX_WORLD_ID = 256
+# Mapping worlds from usertypes.JsWorld to QWebEngineScript world IDs.
+if not QWebEngineScript:
+ JS_WORLD_MAP = {}
+else:
+ JS_WORLD_MAP = {
+ usertypes.JsWorld.main: QWebEngineScript.ScriptWorldId.MainWorld,
+ usertypes.JsWorld.application: QWebEngineScript.ScriptWorldId.ApplicationWorld,
+ usertypes.JsWorld.user: QWebEngineScript.ScriptWorldId.UserWorld,
+ usertypes.JsWorld.jseval: QWebEngineScript.ScriptWorldId.UserWorld + 1,
+ }
+
+
def is_new_qtwebkit() -> bool:
"""Check if the given version is a new QtWebKit."""
assert qWebKitVersion is not None