Refactor websettings default handling

With per-domain settings, having a getter for a setting gets really complicated,
as there isn't one true value for a setting.

The only reason we needed those getters is to save away the default values for
some settings where we were unsure what the defaults are.

- For font setters, we can get the defaults from QFont, like QtWeb{Kit,Engine}
  do.
- For font sizes, we hardcode the defaults QtWeb{Kit,Engine} hardcodes too.
- For maximum-page-in-cache, we hardcode 0, just like QtWebKit.
- For default-encoding, we hardcode iso-8559-1, like QtWeb{Kit,Engine}
- For offline-storage-default-quota, we hardcode 5MB, like QtWebKit
- For offline-web-application-cache-quota, we hardcode MAXINT as default value,
  but we still keep the empty value in the config. It means "no quota"
  internally in QtWebKit, but it's a too confusing value to have in the config.
- For object-cache-capacities it's a bit more complicated (the defaults are
  calculated based on disk space), but let's just get rid of the setting
  altogether in the next commit (see #1751).

Closes #2639.
This commit is contained in:
Florian Bruhin 2017-06-06 11:52:25 +02:00
parent 2f5756e63b
commit 1785b72393
5 changed files with 170 additions and 211 deletions

View File

@ -30,6 +30,7 @@ Module attributes:
import os
import logging
from PyQt5.QtGui import QFont
from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile,
QWebEngineScript)
@ -62,25 +63,41 @@ class Attribute(Base, websettings.Attribute):
class Setter(Base, websettings.Setter):
"""A setting set via QWebEngineSettings getter/setter methods."""
"""A setting set via a QWebEngineSettings setter method."""
pass
class FontFamilySetter(Base, websettings.FontFamilySetter):
"""A setter for a font family.
Gets the default value from QFont.
"""
def __init__(self, font):
# Mapping from WebEngineSettings::initDefaults in
# qtwebengine/src/core/web_engine_settings.cpp
font_to_qfont = {
QWebEngineSettings.StandardFont: QFont.Serif,
QWebEngineSettings.FixedFont: QFont.Monospace,
QWebEngineSettings.SerifFont: QFont.Serif,
QWebEngineSettings.SansSerifFont: QFont.SansSerif,
QWebEngineSettings.CursiveFont: QFont.Cursive,
QWebEngineSettings.FantasyFont: QFont.Fantasy,
}
super().__init__(setter=QWebEngineSettings.setFontFamily, font=font,
qfont=font_to_qfont[font])
class DefaultProfileSetter(websettings.Base):
"""A setting set on the QWebEngineProfile."""
def __init__(self, getter, setter):
super().__init__()
self._getter = getter
def __init__(self, setter, default=websettings.UNSET):
super().__init__(default)
self._setter = setter
def get(self, settings=None):
utils.unused(settings)
getter = getattr(default_profile, self._getter)
return getter()
def _set(self, value, settings=None):
utils.unused(settings)
setter = getattr(default_profile, self._setter)
@ -92,8 +109,7 @@ class PersistentCookiePolicy(DefaultProfileSetter):
"""The cookies -> store setting is different from other settings."""
def __init__(self):
super().__init__(getter='persistentCookiesPolicy',
setter='setPersistentCookiesPolicy')
super().__init__('setPersistentCookiesPolicy')
def get(self, settings=None):
utils.unused(settings)
@ -256,44 +272,28 @@ MAPPINGS = {
},
'fonts': {
'web-family-standard':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.StandardFont]),
FontFamilySetter(QWebEngineSettings.StandardFont),
'web-family-fixed':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.FixedFont]),
FontFamilySetter(QWebEngineSettings.FixedFont),
'web-family-serif':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.SerifFont]),
FontFamilySetter(QWebEngineSettings.SerifFont),
'web-family-sans-serif':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.SansSerifFont]),
FontFamilySetter(QWebEngineSettings.SansSerifFont),
'web-family-cursive':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.CursiveFont]),
FontFamilySetter(QWebEngineSettings.CursiveFont),
'web-family-fantasy':
Setter(getter=QWebEngineSettings.fontFamily,
setter=QWebEngineSettings.setFontFamily,
args=[QWebEngineSettings.FantasyFont]),
FontFamilySetter(QWebEngineSettings.FantasyFont),
'web-size-minimum':
Setter(getter=QWebEngineSettings.fontSize,
setter=QWebEngineSettings.setFontSize,
Setter(QWebEngineSettings.setFontSize,
args=[QWebEngineSettings.MinimumFontSize]),
'web-size-minimum-logical':
Setter(getter=QWebEngineSettings.fontSize,
setter=QWebEngineSettings.setFontSize,
Setter(QWebEngineSettings.setFontSize,
args=[QWebEngineSettings.MinimumLogicalFontSize]),
'web-size-default':
Setter(getter=QWebEngineSettings.fontSize,
setter=QWebEngineSettings.setFontSize,
Setter(QWebEngineSettings.setFontSize,
args=[QWebEngineSettings.DefaultFontSize]),
'web-size-default-fixed':
Setter(getter=QWebEngineSettings.fontSize,
setter=QWebEngineSettings.setFontSize,
Setter(QWebEngineSettings.setFontSize,
args=[QWebEngineSettings.DefaultFixedFontSize]),
},
'ui': {
@ -304,15 +304,14 @@ MAPPINGS = {
'local-storage':
Attribute(QWebEngineSettings.LocalStorageEnabled),
'cache-size':
DefaultProfileSetter(getter='httpCacheMaximumSize',
setter='setHttpCacheMaximumSize')
# 0: automatically managed by QtWebEngine
DefaultProfileSetter('setHttpCacheMaximumSize', default=0),
},
'general': {
'xss-auditing':
Attribute(QWebEngineSettings.XSSAuditingEnabled),
'default-encoding':
Setter(getter=QWebEngineSettings.defaultTextEncoding,
setter=QWebEngineSettings.setDefaultTextEncoding),
Setter(QWebEngineSettings.setDefaultTextEncoding),
}
}

View File

@ -29,6 +29,7 @@ Module attributes:
import os.path
from PyQt5.QtGui import QFont
from PyQt5.QtWebKit import QWebSettings
from qutebrowser.config import config, websettings
@ -53,25 +54,48 @@ class Attribute(Base, websettings.Attribute):
class Setter(Base, websettings.Setter):
"""A setting set via QWebSettings getter/setter methods."""
"""A setting set via a QWebSettings setter method."""
pass
class NullStringSetter(Base, websettings.NullStringSetter):
class NullStringSetter(Base, websettings.Setter):
"""A setter for settings requiring a null QString as default."""
pass
def set_default(self, settings=None):
self._set(None, settings=settings)
class StaticSetter(Base, websettings.StaticSetter):
"""A setting set via static QWebSettings getter/setter methods."""
"""A setting set via a static QWebSettings setter method."""
pass
class FontFamilySetter(Base, websettings.FontFamilySetter):
"""A setter for a font family.
Gets the default value from QFont.
"""
def __init__(self, font):
# Mapping from QWebSettings::QWebSettings() in
# qtwebkit/Source/WebKit/qt/Api/qwebsettings.cpp
font_to_qfont = {
QWebSettings.StandardFont: QFont.Serif,
QWebSettings.FixedFont: QFont.Monospace,
QWebSettings.SerifFont: QFont.Serif,
QWebSettings.SansSerifFont: QFont.SansSerif,
QWebSettings.CursiveFont: QFont.Cursive,
QWebSettings.FantasyFont: QFont.Fantasy,
}
super().__init__(setter=QWebSettings.setFontFamily, font=font,
qfont=font_to_qfont[font])
class CookiePolicy(websettings.Base):
"""The ThirdPartyCookiePolicy setting is different from other settings."""
@ -83,9 +107,6 @@ class CookiePolicy(websettings.Base):
'no-unknown-3rdparty': QWebSettings.AllowThirdPartyWithExistingCookies,
}
def get(self, settings=None):
return config.get('content', 'cookies-accept')
def _set(self, value, settings=None):
QWebSettings.globalSettings().setThirdPartyCookiePolicy(
self.MAPPING[value])
@ -179,44 +200,28 @@ MAPPINGS = {
},
'fonts': {
'web-family-standard':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.StandardFont]),
FontFamilySetter(QWebSettings.StandardFont),
'web-family-fixed':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.FixedFont]),
FontFamilySetter(QWebSettings.FixedFont),
'web-family-serif':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.SerifFont]),
FontFamilySetter(QWebSettings.SerifFont),
'web-family-sans-serif':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.SansSerifFont]),
FontFamilySetter(QWebSettings.SansSerifFont),
'web-family-cursive':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.CursiveFont]),
FontFamilySetter(QWebSettings.CursiveFont),
'web-family-fantasy':
Setter(getter=QWebSettings.fontFamily,
setter=QWebSettings.setFontFamily,
args=[QWebSettings.FantasyFont]),
FontFamilySetter(QWebSettings.FantasyFont),
'web-size-minimum':
Setter(getter=QWebSettings.fontSize,
setter=QWebSettings.setFontSize,
Setter(QWebSettings.setFontSize,
args=[QWebSettings.MinimumFontSize]),
'web-size-minimum-logical':
Setter(getter=QWebSettings.fontSize,
setter=QWebSettings.setFontSize,
Setter(QWebSettings.setFontSize,
args=[QWebSettings.MinimumLogicalFontSize]),
'web-size-default':
Setter(getter=QWebSettings.fontSize,
setter=QWebSettings.setFontSize,
Setter(QWebSettings.setFontSize,
args=[QWebSettings.DefaultFontSize]),
'web-size-default-fixed':
Setter(getter=QWebSettings.fontSize,
setter=QWebSettings.setFontSize,
Setter(QWebSettings.setFontSize,
args=[QWebSettings.DefaultFixedFontSize]),
},
'ui': {
@ -226,8 +231,7 @@ MAPPINGS = {
Attribute(QWebSettings.FrameFlatteningEnabled),
# user-stylesheet is handled separately
'css-media-type':
NullStringSetter(getter=QWebSettings.cssMediaType,
setter=QWebSettings.setCSSMediaType),
NullStringSetter(QWebSettings.setCSSMediaType),
'smooth-scrolling':
Attribute(QWebSettings.ScrollAnimatorEnabled),
#'accelerated-compositing':
@ -243,19 +247,17 @@ MAPPINGS = {
'local-storage':
Attribute(QWebSettings.LocalStorageEnabled),
'maximum-pages-in-cache':
StaticSetter(getter=QWebSettings.maximumPagesInCache,
setter=QWebSettings.setMaximumPagesInCache),
StaticSetter(QWebSettings.setMaximumPagesInCache),
'object-cache-capacities':
StaticSetter(getter=None,
setter=QWebSettings.setObjectCacheCapacities,
StaticSetter(QWebSettings.setObjectCacheCapacities,
unpack=True),
'offline-storage-default-quota':
StaticSetter(getter=QWebSettings.offlineStorageDefaultQuota,
setter=QWebSettings.setOfflineStorageDefaultQuota),
StaticSetter(QWebSettings.setOfflineStorageDefaultQuota),
# Default from ApplicationCacheStorage::ApplicationCacheStorage in
# qtwebkit/Source/WebCore/loader/appcache/ApplicationCacheStorage.cpp
'offline-web-application-cache-quota':
StaticSetter(
getter=QWebSettings.offlineWebApplicationCacheQuota,
setter=QWebSettings.setOfflineWebApplicationCacheQuota),
StaticSetter(QWebSettings.setOfflineWebApplicationCacheQuota,
default=qtutils.MAXVALS['int64']), # no quota
},
'general': {
'developer-extras':
@ -267,7 +269,6 @@ MAPPINGS = {
'site-specific-quirks':
Attribute(QWebSettings.SiteSpecificQuirksEnabled),
'default-encoding':
Setter(getter=QWebSettings.defaultTextEncoding,
setter=QWebSettings.setDefaultTextEncoding),
Setter(QWebSettings.setDefaultTextEncoding),
}
}

View File

@ -444,7 +444,22 @@ class ConfigManager(QObject):
'html > ::-webkit-scrollbar { width: 0px; height: 0px; }': '',
'::-webkit-scrollbar { width: 0px; height: 0px; }': '',
}),
('contents', 'cache-size'): _get_value_transformer({'52428800': ''}),
('general', 'default-encoding'):
_get_value_transformer({'': 'iso-8859-1'}),
('contents', 'cache-size'):
_get_value_transformer({'52428800': ''}),
('storage', 'maximum-pages-in-cache'):
_get_value_transformer({'': '0'}),
('storage', 'offline-storage-default-quota'):
_get_value_transformer({'': str(5 * 1024 * 1024)}),
('fonts', 'web-size-minimum'):
_get_value_transformer({'': '0'}),
('fonts', 'web-size-minimum-logical'):
_get_value_transformer({'': '6'}),
('fonts', 'web-size-default'):
_get_value_transformer({'': '16'}),
('fonts', 'web-size-default-fixed'):
_get_value_transformer({'': '13'}),
}
changed = pyqtSignal(str, str)

View File

@ -221,11 +221,10 @@ def data(readonly=False):
"Enable QtWebKit workarounds for broken sites."),
('default-encoding',
SettingValue(typ.String(none_ok=True), ''),
SettingValue(typ.String(), 'iso-8859-1'),
"Default encoding to use for websites.\n\n"
"The encoding must be a string describing an encoding such as "
"_utf-8_, _iso-8859-1_, etc. If left empty a default value will "
"be used."),
"_utf-8_, _iso-8859-1_, etc."),
('new-instance-open-target',
SettingValue(typ.String(
@ -764,10 +763,12 @@ def data(readonly=False):
SettingValue(typ.Bool(), 'true'),
"Whether to remember the last used download directory."),
# Defaults from QWebSettings::QWebSettings() in
# qtwebkit/Source/WebKit/qt/Api/qwebsettings.cpp
('maximum-pages-in-cache',
SettingValue(
typ.Int(none_ok=True, minval=0, maxval=MAXVALS['int']), '',
backends=[usertypes.Backend.QtWebKit]),
SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '0',
backends=[usertypes.Backend.QtWebKit]),
"The maximum number of pages to hold in the global memory page "
"cache.\n\n"
"The Page Cache allows for a nicer user experience when "
@ -793,8 +794,8 @@ def data(readonly=False):
"that the cache should consume *overall*."),
('offline-storage-default-quota',
SettingValue(typ.WebKitBytes(maxsize=MAXVALS['int64'],
none_ok=True), '',
SettingValue(typ.WebKitBytes(maxsize=MAXVALS['int64']),
str(5 * 1024 * 1024),
backends=[usertypes.Backend.QtWebKit]),
"Default quota for new offline storage databases."),
@ -1453,25 +1454,28 @@ def data(readonly=False):
SettingValue(typ.FontFamily(none_ok=True), ''),
"Font family for fantasy fonts."),
# Defaults for web-size-* from WebEngineSettings::initDefaults in
# qtwebengine/src/core/web_engine_settings.cpp and
# QWebSettings::QWebSettings() in
# qtwebkit/Source/WebKit/qt/Api/qwebsettings.cpp
('web-size-minimum',
SettingValue(
typ.Int(none_ok=True, minval=1, maxval=MAXVALS['int']), ''),
SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '0'),
"The hard minimum font size."),
# This is 0 as default on QtWebKit, and 6 on QtWebEngine - so let's
# just go for 6 here.
('web-size-minimum-logical',
SettingValue(
typ.Int(none_ok=True, minval=1, maxval=MAXVALS['int']), ''),
SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '6'),
"The minimum logical font size that is applied when zooming "
"out."),
('web-size-default',
SettingValue(
typ.Int(none_ok=True, minval=1, maxval=MAXVALS['int']), ''),
SettingValue(typ.Int(minval=1, maxval=MAXVALS['int']), '16'),
"The default font size for regular text."),
('web-size-default-fixed',
SettingValue(
typ.Int(none_ok=True, minval=1, maxval=MAXVALS['int']), ''),
SettingValue(typ.Int(minval=1, maxval=MAXVALS['int']), '13'),
"The default font size for fixed-pitch text."),
('keyhint',

View File

@ -22,6 +22,8 @@
"""Bridge from QWeb(Engine)Settings to our own settings."""
from PyQt5.QtGui import QFont
from qutebrowser.config import config
from qutebrowser.utils import log, utils, debug, usertypes
from qutebrowser.misc import objects
@ -31,14 +33,10 @@ UNSET = object()
class Base:
"""Base class for QWeb(Engine)Settings wrappers.
"""Base class for QWeb(Engine)Settings wrappers."""
Attributes:
_default: The default value of this setting.
"""
def __init__(self):
self._default = UNSET
def __init__(self, default=UNSET):
self._default = default
def _get_global_settings(self):
"""Get a list of global QWeb(Engine)Settings to use."""
@ -60,61 +58,29 @@ class Base:
else:
return [settings]
def save_default(self, settings=None):
"""Save the default value based on the currently set one.
This does nothing if no getter is configured for this setting.
Args:
settings: The QWeb(Engine)Settings instance to use, or None to use
the global instance.
Return:
The saved default value.
"""
try:
self._default = self.get(settings)
return self._default
except AttributeError:
return None
def restore_default(self, settings=None):
"""Restore the default value from the saved one.
This does nothing if the default has never been set.
Args:
settings: The QWeb(Engine)Settings instance to use, or None to use
the global instance.
"""
if self._default is not UNSET:
log.config.vdebug("Restoring default {!r}.".format(self._default))
self._set(self._default, settings=settings)
def get(self, settings=None):
"""Get the value of this setting.
Must be overridden by subclasses.
Args:
settings: The QWeb(Engine)Settings instance to use, or None to use
the global instance.
"""
raise NotImplementedError
def set(self, value, settings=None):
"""Set the value of this setting.
Args:
value: The value to set.
value: The value to set, or None to restore the default.
settings: The QWeb(Engine)Settings instance to use, or None to use
the global instance.
"""
if value is None:
self.restore_default(settings)
self.set_default(settings=settings)
else:
self._set(value, settings=settings)
def set_default(self, settings=None):
"""Set the default value for this setting.
Not implemented for most settings.
"""
if self._default is UNSET:
raise ValueError("No default set for {!r}".format(self))
else:
self._set(self._default, settings=settings)
def _set(self, value, settings):
"""Inner function to set the value of this setting.
@ -138,8 +104,8 @@ class Attribute(Base):
ENUM_BASE = None
def __init__(self, attribute):
super().__init__()
def __init__(self, attribute, default=UNSET):
super().__init__(default=default)
self._attribute = attribute
def __repr__(self):
@ -147,9 +113,6 @@ class Attribute(Base):
self, attribute=debug.qenum_key(self.ENUM_BASE, self._attribute),
constructor=True)
def get(self, settings=None):
return self._get_settings(settings)[0].attribute(self._attribute)
def _set(self, value, settings=None):
for obj in self._get_settings(settings):
obj.setAttribute(self._attribute, value)
@ -157,36 +120,27 @@ class Attribute(Base):
class Setter(Base):
"""A setting set via QWeb(Engine)Settings getter/setter methods.
"""A setting set via a QWeb(Engine)Settings setter method.
This will pass the QWeb(Engine)Settings instance ("self") as first argument
to the methods, so self._getter/self._setter are the *unbound* methods.
to the methods, so self._setter is the *unbound* method.
Attributes:
_getter: The unbound QWeb(Engine)Settings method to get this value, or
None.
_setter: The unbound QWeb(Engine)Settings method to set this value.
_args: An iterable of the arguments to pass to the setter/getter
(before the value, for the setter).
_args: An iterable of the arguments to pass to the setter (before the
value).
_unpack: Whether to unpack args (True) or pass them directly (False).
"""
def __init__(self, getter, setter, args=(), unpack=False):
super().__init__()
self._getter = getter
def __init__(self, setter, args=(), unpack=False, default=UNSET):
super().__init__(default=default)
self._setter = setter
self._args = args
self._unpack = unpack
def __repr__(self):
return utils.get_repr(self, getter=self._getter, setter=self._setter,
args=self._args, unpack=self._unpack,
constructor=True)
def get(self, settings=None):
if self._getter is None:
raise AttributeError("No getter set!")
return self._getter(self._get_settings(settings)[0], *self._args)
return utils.get_repr(self, setter=self._setter, args=self._args,
unpack=self._unpack, constructor=True)
def _set(self, value, settings=None):
for obj in self._get_settings(settings):
@ -199,42 +153,13 @@ class Setter(Base):
self._setter(*args)
class NullStringSetter(Setter):
"""A setter for settings requiring a null QString as default.
This overrides save_default so None is saved for an empty string. This is
needed for the CSS media type, because it returns an empty Python string
when getting the value, but setting it to the default requires passing None
(a null QString) instead of an empty string.
"""
def save_default(self, settings=None):
try:
val = self.get(settings)
except AttributeError:
return None
if val == '':
self._set(None, settings=settings)
else:
self._set(val, settings=settings)
return val
class StaticSetter(Setter):
"""A setting set via static QWeb(Engine)Settings getter/setter methods.
"""A setting set via a static QWeb(Engine)Settings method.
self._getter/self._setter are the *bound* methods.
self._setter is the *bound* method.
"""
def get(self, settings=None):
if settings is not None:
raise ValueError("'settings' may not be set with GlobalSetters!")
if self._getter is None:
raise AttributeError("No getter set!")
return self._getter(*self._args)
def _set(self, value, settings=None):
if settings is not None:
raise ValueError("'settings' may not be set with GlobalSetters!")
@ -246,13 +171,28 @@ class StaticSetter(Setter):
self._setter(*args)
class FontFamilySetter(Setter):
"""A setter for a font family.
Gets the default value from QFont.
"""
def __init__(self, setter, font, qfont):
super().__init__(setter=setter, args=[font])
self._qfont = qfont
def set_default(self, settings=None):
font = QFont()
font.setStyleHint(self._qfont)
value = font.defaultFamily()
self._set(value, settings=settings)
def init_mappings(mappings):
"""Initialize all settings based on a settings mapping."""
for sectname, section in mappings.items():
for optname, mapping in section.items():
default = mapping.save_default()
log.config.vdebug("Saved default for {} -> {}: {!r}".format(
sectname, optname, default))
value = config.get(sectname, optname)
log.config.vdebug("Setting {} -> {} to {!r}".format(
sectname, optname, value))