diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 080fd59a8..ca7a846c7 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -268,7 +268,7 @@ DATA = collections.OrderedDict([ "This will flatten all the frames to become one scrollable page."), ('user-stylesheet', - SettingValue(typ.WebSettingsFile(), ''), + SettingValue(typ.UserStyleSheet(), ''), "User stylesheet to use."), ('css-media-type', diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index c50094a93..a8178c164 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -21,6 +21,7 @@ import re import shlex +import base64 import codecs import os.path import sre_constants @@ -1082,20 +1083,43 @@ class Encoding(BaseType): raise ValidationError(value, "is not a valid encoding!") -class WebSettingsFile(File): +class UserStyleSheet(File): - """QWebSettings file.""" + """QWebSettings UserStyleSheet.""" - typestr = 'file' + typestr = 'user-stylesheet' def __init__(self): super().__init__(none_ok=True) + def validate(self, value): + if not value: + if self.none_ok: + return + else: + raise ValidationError(value, "may not be empty!") + value = os.path.expanduser(value) + if not os.path.isabs(value): + # probably a CSS, so we don't handle it as filename. + # FIXME We just try if it is encodable, maybe we should validate + # CSS? + try: + value.encode('utf-8') + except UnicodeEncodeError as e: + raise ValidationError(value, str(e)) + return + elif not os.path.isfile(value): + raise ValidationError(value, "must be a valid file!") + def transform(self, value): + path = os.path.expanduser(value) if not value: return None + elif os.path.isabs(path): + return QUrl.fromLocalFile(path) else: - return QUrl.fromLocalFile(value) + data = base64.b64encode(value.encode('utf-8')).decode('ascii') + return QUrl("data:text/css;charset=utf-8;base64,{}".format(data)) class AutoSearch(BaseType): diff --git a/qutebrowser/test/config/test_configtypes.py b/qutebrowser/test/config/test_configtypes.py index aa17c9436..59d654be2 100644 --- a/qutebrowser/test/config/test_configtypes.py +++ b/qutebrowser/test/config/test_configtypes.py @@ -21,6 +21,8 @@ import unittest import re import collections +import os.path +import base64 from unittest import mock from qutebrowser.config import configtypes @@ -1759,20 +1761,27 @@ class KeyBindingNameTests(unittest.TestCase): self.assertEqual(self.t.transform("foobar"), "foobar") -class WebSettingsFileTests(unittest.TestCase): +class UserStyleSheetTests(unittest.TestCase): - """Test WebSettingsFile.""" + """Test UserStyleSheet.""" def setUp(self): - self.t = configtypes.WebSettingsFile() + self.t = configtypes.UserStyleSheet() def test_transform_empty(self): """Test transform with an empty value.""" self.assertIsNone(self.t.transform('')) - def test_transform(self): - """Test transform with a value.""" - self.assertEqual(self.t.transform("/foo/bar"), QUrl("file:///foo/bar")) + def test_transform_file(self): + """Test transform with a filename.""" + path = os.path.join(os.path.sep, 'foo', 'bar') + self.assertEqual(self.t.transform(path), QUrl("file:///foo/bar")) + + def test_transform_base64(self): + """Test transform with a data string.""" + b64 = base64.b64encode(b"test").decode('ascii') + url = QUrl("data:text/css;charset=utf-8;base64,{}".format(b64)) + self.assertEqual(self.t.transform("test"), url) class AutoSearchTests(unittest.TestCase):