Require a filename for user-stylesheet; add hide-scrollbar setting
This commit is contained in:
parent
964ddb472b
commit
fcb955458c
@ -50,6 +50,8 @@ Added
|
||||
- New `cast` userscript to show a video on a Google Chromecast
|
||||
- New `:run-with-count` command which replaces the (undocumented) `:count:command` syntax.
|
||||
- New `:record-macro` (`q`) and `:run-macro` (`@`) commands for keyboard macros.
|
||||
- New `ui -> hide-scrollbar` setting to hide the scrollbar independently of the
|
||||
`user-stylesheet` setting.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@ -143,6 +145,7 @@ Changed
|
||||
- Various functionality now works when javascript is disabled with QtWebKit
|
||||
- Various commands/settings taking `left`/`right`/`previous` arguments now take
|
||||
`prev`/`next`/`last-used` to remove ambiguity.
|
||||
- The `ui -> user-stylesheet` setting now only takes filenames, not CSS snippets
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
@ -222,3 +222,19 @@ def get_tab(win_id, target):
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=win_id)
|
||||
return tabbed_browser.tabopen(url=None, background=bg_tab)
|
||||
|
||||
|
||||
def get_user_stylesheet():
|
||||
"""Get the combined user-stylesheet."""
|
||||
filename = config.get('ui', 'user-stylesheet')
|
||||
|
||||
if filename is None:
|
||||
css = ''
|
||||
else:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
css = f.read()
|
||||
|
||||
if config.get('ui', 'hide-scrollbar'):
|
||||
css += '\nhtml > ::-webkit-scrollbar { width: 0px; height: 0px; }'
|
||||
|
||||
return css
|
||||
|
@ -26,10 +26,12 @@ Module attributes:
|
||||
|
||||
import os.path
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtWebKit import QWebSettings
|
||||
|
||||
from qutebrowser.config import config, websettings
|
||||
from qutebrowser.utils import standarddir, objreg
|
||||
from qutebrowser.utils import standarddir, objreg, urlutils
|
||||
from qutebrowser.browser import shared
|
||||
|
||||
|
||||
class Attribute(websettings.Attribute):
|
||||
@ -80,14 +82,24 @@ class CookiePolicy(websettings.Base):
|
||||
self.MAPPING[value])
|
||||
|
||||
|
||||
def _set_user_stylesheet():
|
||||
"""Set the generated user-stylesheet."""
|
||||
stylesheet = shared.get_user_stylesheet().encode('utf-8')
|
||||
url = urlutils.data_url('text/css;charset=utf-8', stylesheet)
|
||||
QWebSettings.globalSettings().setUserStyleSheetUrl(url)
|
||||
|
||||
|
||||
def update_settings(section, option):
|
||||
"""Update global settings when qwebsettings changed."""
|
||||
cache_path = standarddir.cache()
|
||||
if (section, option) == ('general', 'private-browsing'):
|
||||
cache_path = standarddir.cache()
|
||||
if config.get('general', 'private-browsing') or cache_path is None:
|
||||
QWebSettings.setIconDatabasePath('')
|
||||
else:
|
||||
QWebSettings.setIconDatabasePath(cache_path)
|
||||
elif section == 'ui' and option in ['hide-scrollbar', 'user-stylesheet']:
|
||||
_set_user_stylesheet()
|
||||
|
||||
websettings.update_mappings(MAPPINGS, section, option)
|
||||
|
||||
|
||||
@ -108,6 +120,7 @@ def init():
|
||||
os.path.join(data_path, 'offline-storage'))
|
||||
|
||||
websettings.init_mappings(MAPPINGS)
|
||||
_set_user_stylesheet()
|
||||
objreg.get('config').changed.connect(update_settings)
|
||||
|
||||
|
||||
@ -204,9 +217,7 @@ MAPPINGS = {
|
||||
Attribute(QWebSettings.ZoomTextOnly),
|
||||
'frame-flattening':
|
||||
Attribute(QWebSettings.FrameFlatteningEnabled),
|
||||
'user-stylesheet':
|
||||
Setter(getter=QWebSettings.userStyleSheetUrl,
|
||||
setter=QWebSettings.setUserStyleSheetUrl),
|
||||
# user-stylesheet is handled separately
|
||||
'css-media-type':
|
||||
NullStringSetter(getter=QWebSettings.cssMediaType,
|
||||
setter=QWebSettings.setCSSMediaType),
|
||||
|
@ -437,6 +437,11 @@ class ConfigManager(QObject):
|
||||
('fonts', 'hints'): _transform_hint_font,
|
||||
('completion', 'show'):
|
||||
_get_value_transformer({'false': 'never', 'true': 'always'}),
|
||||
('ui', 'user-stylesheet'):
|
||||
_get_value_transformer({
|
||||
'html > ::-webkit-scrollbar { width: 0px; height: 0px; }': '',
|
||||
'::-webkit-scrollbar { width: 0px; height: 0px; }': '',
|
||||
}),
|
||||
}
|
||||
|
||||
changed = pyqtSignal(str, str)
|
||||
|
@ -323,13 +323,15 @@ def data(readonly=False):
|
||||
"page."),
|
||||
|
||||
('user-stylesheet',
|
||||
SettingValue(typ.UserStyleSheet(none_ok=True),
|
||||
'html > ::-webkit-scrollbar { width: 0px; '
|
||||
'height: 0px; }',
|
||||
SettingValue(typ.File(none_ok=True), '',
|
||||
backends=[usertypes.Backend.QtWebKit]),
|
||||
"User stylesheet to use (absolute filename, filename relative to "
|
||||
"the config directory or CSS string). Will expand environment "
|
||||
"variables."),
|
||||
"User stylesheet to use (absolute filename or filename relative to "
|
||||
"the config directory). Will expand environment variables."),
|
||||
|
||||
('hide-scrollbar',
|
||||
SettingValue(typ.Bool(), 'true',
|
||||
backends=[usertypes.Backend.QtWebKit]),
|
||||
"Hide the main scrollbar."),
|
||||
|
||||
('css-media-type',
|
||||
SettingValue(typ.String(none_ok=True), '',
|
||||
|
@ -1165,40 +1165,6 @@ class Encoding(BaseType):
|
||||
raise configexc.ValidationError(value, "is not a valid encoding!")
|
||||
|
||||
|
||||
class UserStyleSheet(File):
|
||||
|
||||
"""QWebSettings UserStyleSheet."""
|
||||
|
||||
def transform(self, value):
|
||||
if not value:
|
||||
return None
|
||||
|
||||
path = super().transform(value)
|
||||
if path is not None and os.path.exists(path):
|
||||
return QUrl.fromLocalFile(path)
|
||||
else:
|
||||
return urlutils.data_url('text/css', value.encode('utf-8'))
|
||||
|
||||
def validate(self, value):
|
||||
self._basic_validation(value)
|
||||
if not value:
|
||||
return
|
||||
value = os.path.expandvars(value)
|
||||
value = os.path.expanduser(value)
|
||||
try:
|
||||
super().validate(value)
|
||||
except configexc.ValidationError:
|
||||
try:
|
||||
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?
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/115
|
||||
value.encode('utf-8')
|
||||
except UnicodeEncodeError as e:
|
||||
raise configexc.ValidationError(value, str(e))
|
||||
|
||||
|
||||
class AutoSearch(BaseType):
|
||||
|
||||
"""Whether to start a search when something else than a URL is entered."""
|
||||
|
@ -1211,15 +1211,9 @@ def unrequired_class(**kwargs):
|
||||
|
||||
@pytest.mark.usefixtures('qapp')
|
||||
@pytest.mark.usefixtures('config_tmpdir')
|
||||
class TestFileAndUserStyleSheet:
|
||||
class TestFile:
|
||||
|
||||
"""Test File/UserStyleSheet."""
|
||||
|
||||
@pytest.fixture(params=[
|
||||
configtypes.File,
|
||||
configtypes.UserStyleSheet,
|
||||
unrequired_class,
|
||||
])
|
||||
@pytest.fixture(params=[configtypes.File, unrequired_class])
|
||||
def klass(self, request):
|
||||
return request.param
|
||||
|
||||
@ -1227,18 +1221,12 @@ class TestFileAndUserStyleSheet:
|
||||
def file_class(self):
|
||||
return configtypes.File
|
||||
|
||||
@pytest.fixture
|
||||
def userstylesheet_class(self):
|
||||
return configtypes.UserStyleSheet
|
||||
|
||||
def _expected(self, klass, arg):
|
||||
"""Get the expected value."""
|
||||
if not arg:
|
||||
return None
|
||||
elif klass is configtypes.File:
|
||||
return arg
|
||||
elif klass is configtypes.UserStyleSheet:
|
||||
return QUrl.fromLocalFile(arg)
|
||||
elif klass is unrequired_class:
|
||||
return arg
|
||||
else:
|
||||
@ -1262,11 +1250,6 @@ class TestFileAndUserStyleSheet:
|
||||
os_mock.path.isfile.return_value = False
|
||||
configtypes.File(required=False).validate('foobar')
|
||||
|
||||
def test_validate_does_not_exist_userstylesheet(self, os_mock):
|
||||
"""Test validate with a file which does not exist (UserStyleSheet)."""
|
||||
os_mock.path.isfile.return_value = False
|
||||
configtypes.UserStyleSheet().validate('foobar')
|
||||
|
||||
def test_validate_exists_abs(self, klass, os_mock):
|
||||
"""Test validate with a file which does exist."""
|
||||
os_mock.path.isfile.return_value = True
|
||||
@ -1286,11 +1269,8 @@ class TestFileAndUserStyleSheet:
|
||||
|
||||
@pytest.mark.parametrize('configtype, value, raises', [
|
||||
(configtypes.File(), 'foobar', True),
|
||||
(configtypes.UserStyleSheet(), 'foobar', False),
|
||||
(configtypes.UserStyleSheet(), '\ud800', True),
|
||||
(configtypes.File(required=False), 'foobar', False),
|
||||
], ids=['file-foobar', 'userstylesheet-foobar', 'userstylesheet-unicode',
|
||||
'file-optional-foobar'])
|
||||
], ids=['file-foobar', 'file-optional-foobar'])
|
||||
def test_validate_rel_inexistent(self, os_mock, monkeypatch, configtype,
|
||||
value, raises):
|
||||
"""Test with a relative path and standarddir.config returning None."""
|
||||
@ -1339,7 +1319,6 @@ class TestFileAndUserStyleSheet:
|
||||
|
||||
def test_transform_relative(self, klass, os_mock, monkeypatch):
|
||||
"""Test transform() with relative dir and an available configdir."""
|
||||
os_mock.path.exists.return_value = True # for TestUserStyleSheet
|
||||
os_mock.path.isabs.return_value = False
|
||||
monkeypatch.setattr(
|
||||
'qutebrowser.config.configtypes.standarddir.config',
|
||||
@ -1347,12 +1326,6 @@ class TestFileAndUserStyleSheet:
|
||||
expected = self._expected(klass, '/configdir/foo')
|
||||
assert klass().transform('foo') == expected
|
||||
|
||||
def test_transform_userstylesheet_base64(self, monkeypatch):
|
||||
"""Test transform with a data string."""
|
||||
b64 = base64.b64encode(b"test").decode('ascii')
|
||||
url = QUrl("data:text/css;base64,{}".format(b64))
|
||||
assert configtypes.UserStyleSheet().transform("test") == url
|
||||
|
||||
|
||||
class TestDirectory:
|
||||
|
||||
|
@ -50,8 +50,7 @@ def gen_classes():
|
||||
@hypothesis.given(strategies.text())
|
||||
@hypothesis.example('\x00')
|
||||
def test_configtypes_hypothesis(klass, s):
|
||||
if (klass in [configtypes.File, configtypes.UserStyleSheet] and
|
||||
sys.platform == 'linux' and
|
||||
if (klass == configtypes.File and sys.platform == 'linux' and
|
||||
not os.environ.get('DISPLAY', '')):
|
||||
pytest.skip("No DISPLAY available")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user