diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 3826e6e84..eebdc5072 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -1447,6 +1447,8 @@ Default: Enable support for the HTML 5 web application cache feature. An application cache acts like an HTTP cache in some sense. For documents that use the application cache via JavaScript, the loader engine will first ask the application cache for the contents, before hitting the network. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1524,6 +1526,8 @@ This setting is only available with the QtWebKit backend. === content.dns_prefetch Try to pre-fetch DNS entries to speed up browsing. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1535,6 +1539,8 @@ This setting is only available with the QtWebKit backend. Expand each subframe to its contents. This will flatten all the frames to become one scrollable page. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1651,6 +1657,8 @@ Default: === content.hyperlink_auditing Enable hyperlink auditing (``). +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1659,6 +1667,8 @@ Default: +pass:[false]+ === content.images Load images automatically in web pages. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1676,6 +1686,8 @@ Default: +pass:[true]+ Allow JavaScript to read from or write to the clipboard. With QtWebEngine, writing the clipboard as response to a user interaction is always allowed. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1684,6 +1696,8 @@ Default: +pass:[false]+ === content.javascript.can_close_tabs Allow JavaScript to close tabs. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1694,6 +1708,8 @@ This setting is only available with the QtWebKit backend. === content.javascript.can_open_tabs_automatically Allow JavaScript to open new tabs without user interaction. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1702,6 +1718,8 @@ Default: +pass:[false]+ === content.javascript.enabled Enable JavaScript. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1741,6 +1759,8 @@ Default: +pass:[true]+ === content.local_content_can_access_file_urls Allow locally loaded documents to access other local URLs. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1749,6 +1769,8 @@ Default: +pass:[true]+ === content.local_content_can_access_remote_urls Allow locally loaded documents to access remote URLs. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1757,6 +1779,8 @@ Default: +pass:[false]+ === content.local_storage Enable support for HTML 5 local storage and Web SQL. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1817,6 +1841,8 @@ This setting is only available with the QtWebKit backend. === content.plugins Enable plugins in Web pages. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -1825,6 +1851,8 @@ Default: +pass:[false]+ === content.print_element_backgrounds Draw the background color and images also when the page is printed. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1889,6 +1917,8 @@ Default: empty === content.webgl Enable WebGL. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -1906,6 +1936,8 @@ Default: +pass:[false]+ Monitor load requests for cross-site scripting attempts. Suspicious scripts will be blocked and reported in the inspector's JavaScript console. Enabling this feature might have an impact on performance. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -2379,6 +2411,8 @@ Default: +pass:[false]+ === input.links_included_in_focus_chain Include hyperlinks in the keyboard focus chain when tabbing. +This setting supports URL patterns. + Type: <> Default: +pass:[true]+ @@ -2406,6 +2440,8 @@ Default: +pass:[false]+ Enable spatial navigation. Spatial navigation consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using Left, Right, Up and Down arrow keys. For example, if the user presses the Right key, heuristics determine whether there is an element he might be trying to reach towards the right and which element he probably wants. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -2550,6 +2586,8 @@ Default: +pass:[false]+ Enable smooth scrolling for web pages. Note smooth scrolling does not work with the `:scroll-px` command. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ @@ -3137,6 +3175,8 @@ Default: +pass:[512]+ === zoom.text_only Apply the zoom factor on a frame only to the text or to all content. +This setting supports URL patterns. + Type: <> Default: +pass:[false]+ diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 010eeb3d5..33e04a90d 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -280,6 +280,7 @@ class Config(QObject): raise configexc.BackendError(opt.name, objects.backend) opt.typ.to_py(value) # for validation + self._values[opt.name].add(opt.typ.from_obj(value), pattern) self.changed.emit(opt.name) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 3e0a6d8b1..52ad123e1 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -48,6 +48,7 @@ class Option: backends = attr.ib() raw_backends = attr.ib() description = attr.ib() + supports_pattern = attr.ib(default=False) restart = attr.ib(default=False) @@ -197,7 +198,8 @@ def _read_yaml(yaml_data): migrations = Migrations() data = utils.yaml_load(yaml_data) - keys = {'type', 'default', 'desc', 'backend', 'restart'} + keys = {'type', 'default', 'desc', 'backend', 'restart', + 'supports_pattern'} for name, option in data.items(): if set(option.keys()) == {'renamed'}: @@ -223,7 +225,9 @@ def _read_yaml(yaml_data): backends=_parse_yaml_backends(name, backends), raw_backends=backends if isinstance(backends, dict) else None, description=option['desc'], - restart=option.get('restart', False)) + restart=option.get('restart', False), + supports_pattern=option.get('supports_pattern', False), + ) # Make sure no key shadows another. for key1 in parsed: diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 72450978b..2228bc1b4 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -240,6 +240,7 @@ content.cache.appcache: default: true type: Bool backend: QtWebKit + supports_pattern: true desc: >- Enable support for the HTML 5 web application cache feature. @@ -298,12 +299,14 @@ content.dns_prefetch: default: true type: Bool backend: QtWebKit + supports_pattern: true desc: Try to pre-fetch DNS entries to speed up browsing. content.frame_flattening: default: false type: Bool backend: QtWebKit + supports_pattern: true desc: >- Expand each subframe to its contents. @@ -459,12 +462,14 @@ content.host_blocking.whitelist: content.hyperlink_auditing: default: false type: Bool + supports_pattern: true desc: Enable hyperlink auditing (``). content.images: default: true type: Bool desc: Load images automatically in web pages. + supports_pattern: true content.javascript.alert: default: true @@ -474,6 +479,7 @@ content.javascript.alert: content.javascript.can_access_clipboard: default: false type: Bool + supports_pattern: true desc: >- Allow JavaScript to read from or write to the clipboard. @@ -484,16 +490,19 @@ content.javascript.can_close_tabs: default: false type: Bool backend: QtWebKit + supports_pattern: true desc: Allow JavaScript to close tabs. content.javascript.can_open_tabs_automatically: default: false type: Bool + supports_pattern: true desc: Allow JavaScript to open new tabs without user interaction. content.javascript.enabled: default: true type: Bool + supports_pattern: true desc: Enable JavaScript. content.javascript.log: @@ -536,16 +545,19 @@ content.javascript.prompt: content.local_content_can_access_remote_urls: default: false type: Bool + supports_pattern: true desc: Allow locally loaded documents to access remote URLs. content.local_content_can_access_file_urls: default: true type: Bool + supports_pattern: true desc: Allow locally loaded documents to access other local URLs. content.local_storage: default: true type: Bool + supports_pattern: true desc: Enable support for HTML 5 local storage and Web SQL. content.media_capture: @@ -583,6 +595,7 @@ content.pdfjs: content.plugins: default: false type: Bool + supports_pattern: true desc: Enable plugins in Web pages. content.print_element_backgrounds: @@ -591,6 +604,7 @@ content.print_element_backgrounds: backend: QtWebKit: true QtWebEngine: Qt 5.8 + supports_pattern: true desc: >- Draw the background color and images also when the page is printed. @@ -631,11 +645,13 @@ content.user_stylesheets: content.webgl: default: true type: Bool + supports_pattern: true desc: Enable WebGL. content.xss_auditing: type: Bool default: false + supports_pattern: true desc: >- Monitor load requests for cross-site scripting attempts. @@ -978,6 +994,7 @@ input.insert_mode.plugins: input.links_included_in_focus_chain: default: true type: Bool + supports_pattern: true desc: Include hyperlinks in the keyboard focus chain when tabbing. input.partial_timeout: @@ -1003,6 +1020,7 @@ input.rocker_gestures: input.spatial_navigation: default: false type: Bool + supports_pattern: true desc: >- Enable spatial navigation. @@ -1083,6 +1101,7 @@ scrolling.bar: scrolling.smooth: type: Bool default: false + supports_pattern: true desc: >- Enable smooth scrolling for web pages. @@ -1557,6 +1576,7 @@ zoom.text_only: type: Bool default: false backend: QtWebKit + supports_pattern: true desc: Apply the zoom factor on a frame only to the text or to all content. ## colors diff --git a/qutebrowser/config/configexc.py b/qutebrowser/config/configexc.py index 2067878b9..e08bec913 100644 --- a/qutebrowser/config/configexc.py +++ b/qutebrowser/config/configexc.py @@ -40,6 +40,15 @@ class BackendError(Error): "backend!".format(name, backend.name)) +class NoPatternError(Error): + + """Raised when the given setting does not support URL patterns.""" + + def __init__(self, name): + super().__init__("The {} setting does not support URL patterns!" + .format(name)) + + class ValidationError(Error): """Raised when a value for a config type was invalid. diff --git a/qutebrowser/config/configutils.py b/qutebrowser/config/configutils.py index 0d324bb47..96fc0f02d 100644 --- a/qutebrowser/config/configutils.py +++ b/qutebrowser/config/configutils.py @@ -24,6 +24,7 @@ import attr from qutebrowser.utils import utils +from qutebrowser.config import configexc class _UnsetObject: @@ -107,8 +108,14 @@ class Values: """Check whether this value is customized.""" return bool(self._values) + def _check_pattern_support(self, arg): + """Make sure patterns are supported if one was given.""" + if arg is not None and not self.opt.supports_pattern: + raise configexc.NoPatternError(self.opt.name) + def add(self, value, pattern=None): """Add a value with the given pattern to the list of values.""" + self._check_pattern_support(pattern) self.remove(pattern) scoped = ScopedValue(value, pattern) self._values.append(scoped) @@ -119,6 +126,7 @@ class Values: If a matching pattern was removed, True is returned. If no matching pattern was found, False is returned. """ + self._check_pattern_support(pattern) old_len = len(self._values) self._values = [v for v in self._values if v.pattern != pattern] return old_len != len(self._values) @@ -146,6 +154,7 @@ class Values: With fallback=True, the global/default setting is returned. With fallback=False, UNSET is returned. """ + self._check_pattern_support(url) if url is not None: for scoped in reversed(self._values): if scoped.pattern is not None and scoped.pattern.matches(url): @@ -165,6 +174,7 @@ class Values: With fallback=True, the global/default setting is returned. With fallback=False, UNSET is returned. """ + self._check_pattern_support(pattern) if pattern is not None: for scoped in reversed(self._values): if scoped.pattern == pattern: diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py index 0f057d865..eb6c2ce49 100644 --- a/qutebrowser/config/websettings.py +++ b/qutebrowser/config/websettings.py @@ -225,8 +225,9 @@ def update_for_tab(mappings, tab, url): for values in config.instance: if values.opt.name not in mappings: continue + if not values.opt.supports_pattern: + continue - # FIXME:conf handle settings != None with global/static setters mapping = mappings[values.opt.name] value = values.get_for_url(url, fallback=False) @@ -237,10 +238,7 @@ def update_for_tab(mappings, tab, url): settings = tab._widget.settings() # pylint: disable=protected-access if value is configutils.UNSET: - try: - mapping.unset(settings=settings) - except NotImplementedError: - pass + mapping.unset(settings=settings) else: mapping.set(value, settings=settings) diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index 6307e3a5c..5fab901fc 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -419,6 +419,8 @@ def _generate_setting_option(f, opt): f.write(opt.description + "\n") if opt.restart: f.write("This setting requires a restart.\n") + if opt.supports_pattern: + f.write("\nThis setting supports URL patterns.\n") f.write("\n") typ = opt.typ.get_name().replace(',', ',') f.write('Type: <>\n'.format(typ=typ)) diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index 46559bffa..8a33cd393 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -581,6 +581,14 @@ class TestConfig: meth('content.cookies.accept', 'all') assert not conf._values['content.cookies.accept'] + @pytest.mark.parametrize('method', ['set_obj', 'set_str']) + def test_set_no_pattern(self, conf, method, qtbot): + meth = getattr(conf, method) + pattern = urlmatch.UrlPattern('https://www.example.com/') + with pytest.raises(configexc.NoPatternError): + with qtbot.assert_not_emitted(conf.changed): + meth('colors.statusbar.normal.bg', '#abcdef', pattern=pattern) + def test_dump_userconfig(self, conf): conf.set_obj('content.plugins', True) conf.set_obj('content.headers.custom', {'X-Foo': 'bar'}) diff --git a/tests/unit/config/test_configcommands.py b/tests/unit/config/test_configcommands.py index 8d48c94f2..27075e869 100644 --- a/tests/unit/config/test_configcommands.py +++ b/tests/unit/config/test_configcommands.py @@ -104,6 +104,16 @@ class TestSet: match='Error while parsing :/: No scheme given'): commands.set(0, option, 'false', pattern=':/') + def test_set_no_pattern(self, monkeypatch, commands): + """Run ':set --pattern=*://* colors.statusbar.normal.bg #abcdef. + + Should show an error as patterns are unsupported. + """ + with pytest.raises(cmdexc.CommandError, + match='does not support URL patterns'): + commands.set(0, 'colors.statusbar.normal.bg', '#abcdef', + pattern='*://*') + @pytest.mark.parametrize('temp', [True, False]) def test_set_temp_override(self, commands, config_stub, yaml_value, temp): """Invoking :set twice. diff --git a/tests/unit/config/test_configexc.py b/tests/unit/config/test_configexc.py index 87f3abb6a..c41e02b4c 100644 --- a/tests/unit/config/test_configexc.py +++ b/tests/unit/config/test_configexc.py @@ -54,6 +54,12 @@ def test_backend_error(): assert str(e) == expected +def test_no_pattern_error(): + e = configexc.NoPatternError('foo') + expected = "The foo setting does not support URL patterns!" + assert str(e) == expected + + def test_desc_with_text(): """Test ConfigErrorDesc.with_text.""" old = configexc.ConfigErrorDesc("Error text", Exception("Exception text")) diff --git a/tests/unit/config/test_configutils.py b/tests/unit/config/test_configutils.py index 0793b8271..587a0bd68 100644 --- a/tests/unit/config/test_configutils.py +++ b/tests/unit/config/test_configutils.py @@ -38,7 +38,8 @@ def test_unset_object_repr(): def opt(): return configdata.Option(name='example.option', typ=configtypes.String(), default='default value', backends=None, - raw_backends=None, description=None) + raw_backends=None, description=None, + supports_pattern=True) @pytest.fixture