From 52f6ea2525c97b1aaf7983bf959b50f2a6eca3fd Mon Sep 17 00:00:00 2001
From: Florian Bruhin <git@the-compiler.org>
Date: Fri, 9 Jun 2017 17:21:36 +0200
Subject: [PATCH] Initial parsing

---
 qutebrowser/config/config.py                  |   10 +-
 qutebrowser/config/configdata.py              | 1496 ++---------------
 qutebrowser/config/configdata.yml             |   20 +-
 qutebrowser/config/configtypes.py             |   64 +-
 qutebrowser/config/newconfig.py               |   14 +-
 .../dev/pylint_checkers/qute_pylint/config.py |    7 +-
 tests/unit/config/test_configdata.py          |  203 ++-
 7 files changed, 405 insertions(+), 1409 deletions(-)

diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index fe3beb545..a723d2f10 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -73,10 +73,11 @@ class change_filter:  # pylint: disable=invalid-name
             optname: The option to be filtered.
             function: Whether a function rather than a method is decorated.
         """
-        if sectname not in configdata.DATA:
-            raise configexc.NoSectionError(sectname)
-        if optname is not None and optname not in configdata.DATA[sectname]:
-            raise configexc.NoOptionError(optname, sectname)
+        # FIXME:conf
+        # if sectname not in configdata.DATA:
+        #     raise configexc.NoSectionError(sectname)
+        # if optname is not None and optname not in configdata.DATA[sectname]:
+        #     raise configexc.NoOptionError(optname, sectname)
         self._sectname = sectname
         self._optname = optname
         self._function = function
@@ -256,6 +257,7 @@ def init(parent=None):
         parent: The parent to pass to QObjects which get initialized.
     """
     # _init_main_config(parent)
+    configdata.init()
     _init_new_config(parent)
     _init_key_config(parent)
     _init_misc()
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
index 711fca495..db1a9ce34 100644
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -33,11 +33,12 @@ import sys
 import re
 import collections
 
-from qutebrowser.config import configtypes as typ
-from qutebrowser.config import sections as sect
-from qutebrowser.config.value import SettingValue
-from qutebrowser.utils.qtutils import MAXVALS
-from qutebrowser.utils import usertypes, qtutils
+import yaml
+
+from qutebrowser.config import configtypes, sections
+from qutebrowser.utils import usertypes, qtutils, utils
+
+DATA = None
 
 
 FIRST_COMMENT = r"""
@@ -131,1343 +132,6 @@ MONOSPACE = (' xos4 Terminus, Terminus, Monospace, '
              'monospace, Fixed, Consolas, Terminal')
 
 
-
-def data(readonly=False):
-    """Get the default config data.
-
-    Return:
-        A {name: section} OrderedDict.
-    """
-    return collections.OrderedDict([
-        ('general', sect.KeyValue(
-            ('ignore-case',
-             SettingValue(typ.IgnoreCase(), 'smart'),
-             "Whether to find text on a page case-insensitively."),
-
-            ('startpage',
-             SettingValue(typ.List(typ.String()),
-                          'https://start.duckduckgo.com'),
-             "The default page(s) to open at the start, separated by commas."),
-
-            ('yank-ignored-url-parameters',
-             SettingValue(typ.List(typ.String()),
-                          'ref,utm_source,utm_medium,utm_campaign,utm_term,'
-                          'utm_content'),
-            "The URL parameters to strip with :yank url, separated by "
-            "commas."),
-
-            ('default-open-dispatcher',
-             SettingValue(typ.String(none_ok=True), ''),
-            "The default program used to open downloads. Set to an empty "
-            "string to use the default internal handler.\n\n"
-            "Any {} in the string will be expanded to the filename, else "
-            "the filename will be appended."),
-
-            ('default-page',
-             SettingValue(typ.FuzzyUrl(), 'https://start.duckduckgo.com/'),
-             "The page to open if :open -t/-b/-w is used without URL. Use "
-             "`about:blank` for a blank page."),
-
-            ('auto-search',
-             SettingValue(typ.AutoSearch(), 'naive'),
-             "Whether to start a search when something else than a URL is "
-             "entered."),
-
-            ('auto-save-config',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to save the config automatically on quit."),
-
-            ('auto-save-interval',
-             SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '15000'),
-             "How often (in milliseconds) to auto-save config/cookies/etc."),
-
-            ('editor',
-             SettingValue(typ.ShellCommand(placeholder=True), 'gvim -f "{}"'),
-             "The editor (and arguments) to use for the `open-editor` "
-             "command.\n\n"
-             "The arguments get split like in a shell, so you can use `\"` or "
-             "`'` to quote them.\n"
-             "`{}` gets replaced by the filename of the file to be edited."),
-
-            ('editor-encoding',
-             SettingValue(typ.Encoding(), 'utf-8'),
-             "Encoding to use for editor."),
-
-            ('private-browsing',
-             SettingValue(typ.Bool(), 'false'),
-             "Open new windows in private browsing mode which does not record "
-             "visited pages."),
-
-            ('developer-extras',
-             SettingValue(typ.Bool(), 'false',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Enable extra tools for Web developers.\n\n"
-             "This needs to be enabled for `:inspector` to work and also adds "
-             "an _Inspect_ entry to the context menu. For QtWebEngine, see "
-             "'qutebrowser --help' instead."),
-
-            ('print-element-backgrounds',
-             SettingValue(typ.Bool(), 'true',
-                          backends=(
-                              None if qtutils.version_check('5.8', strict=True)
-                              else [usertypes.Backend.QtWebKit])),
-             "Whether the background color and images are also drawn when the "
-             "page is printed.\n"
-             "This setting only works with Qt 5.8 or newer when using the "
-             "QtWebEngine backend."),
-
-            ('xss-auditing',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether load requests should be monitored for cross-site "
-             "scripting attempts.\n\n"
-             "Suspicious scripts will be blocked and reported in the "
-             "inspector's JavaScript console. Enabling this feature might "
-             "have an impact on performance."),
-
-            ('default-encoding',
-             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."),
-
-            ('new-instance-open-target',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('tab', "Open a new tab in the existing "
-                      "window and activate the window."),
-                     ('tab-bg', "Open a new background tab in the "
-                      "existing window and activate the "
-                      "window."),
-                     ('tab-silent', "Open a new tab in the existing "
-                      "window without activating "
-                      "the window."),
-                     ('tab-bg-silent', "Open a new background tab "
-                      "in the existing window "
-                      "without activating the "
-                      "window."),
-                     ('window', "Open in a new window.")
-                 )), 'tab'),
-             "How to open links in an existing instance if a new one is "
-             "launched."),
-
-            ('new-instance-open-target.window',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('first-opened', "Open new tabs in the first (oldest) "
-                                      "opened window."),
-                     ('last-opened', "Open new tabs in the last (newest) "
-                                     "opened window."),
-                     ('last-focused', "Open new tabs in the most recently "
-                                      "focused window."),
-                     ('last-visible', "Open new tabs in the most recently "
-                                      "visible window.")
-                 )), 'last-focused'),
-             "Which window to choose when opening links as new tabs."),
-
-            ('log-javascript-console',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('none', "Don't log messages."),
-                     ('debug', "Log messages with debug level."),
-                     ('info', "Log messages with info level.")
-                 )), 'debug'),
-             "How to log javascript console messages."),
-
-            ('save-session',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to always save the open pages."),
-
-            ('session-default-name',
-             SettingValue(typ.SessionName(none_ok=True), ''),
-             "The name of the session to save by default, or empty for the "
-             "last loaded session."),
-
-            ('url-incdec-segments',
-             SettingValue(
-                 typ.FlagList(valid_values=typ.ValidValues(
-                     'host', 'path', 'query', 'anchor')),
-                 'path,query'),
-             "The URL segments where `:navigate increment/decrement` will "
-             "search for a number."),
-
-            readonly=readonly
-        )),
-
-        ('ui', sect.KeyValue(
-            ('history-session-interval',
-             SettingValue(typ.Int(), '30'),
-             "The maximum time in minutes between two history items for them "
-             "to be considered being from the same session. Use -1 to "
-             "disable separation."),
-
-            ('zoom-levels',
-             SettingValue(typ.List(typ.Perc(minval=0)),
-                          '25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,'
-                          '200%,250%,300%,400%,500%'),
-             "The available zoom levels, separated by commas."),
-
-            ('default-zoom',
-             SettingValue(typ.Perc(), '100%'),
-             "The default zoom level."),
-
-            ('downloads-position',
-             SettingValue(typ.VerticalPosition(), 'top'),
-             "Where to show the downloaded files."),
-
-            ('status-position',
-             SettingValue(typ.VerticalPosition(), 'bottom'),
-             "The position of the status bar."),
-
-            ('message-timeout',
-             SettingValue(typ.Int(minval=0), '2000'),
-             "Time (in ms) to show messages in the statusbar for.\n"
-             "Set to 0 to never clear messages."),
-
-            ('message-unfocused',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to show messages in unfocused windows."),
-
-            ('confirm-quit',
-             SettingValue(typ.ConfirmQuit(), 'never'),
-             "Whether to confirm quitting the application."),
-
-            ('zoom-text-only',
-             SettingValue(typ.Bool(), 'false',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether the zoom factor on a frame applies only to the text or "
-             "to all content."),
-
-            ('frame-flattening',
-             SettingValue(typ.Bool(), 'false',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether to  expand each subframe to its contents.\n\n"
-             "This will flatten all the frames to become one scrollable "
-             "page."),
-
-            ('user-stylesheet',
-             SettingValue(typ.File(none_ok=True), ''),
-             "User stylesheet to use (absolute filename or filename relative "
-             "to the config directory). Will expand environment variables."),
-
-            ('hide-scrollbar',
-             SettingValue(typ.Bool(), 'true'),
-             "Hide the main scrollbar."),
-
-            ('smooth-scrolling',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to enable smooth scrolling for web pages. Note smooth "
-             "scrolling does not work with the :scroll-px command."),
-
-            ('remove-finished-downloads',
-             SettingValue(typ.Int(minval=-1), '-1'),
-             "Number of milliseconds to wait before removing finished "
-             "downloads. Will not be removed if value is -1."),
-
-            ('hide-statusbar',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to hide the statusbar unless a message is shown."),
-
-            ('statusbar-padding',
-             SettingValue(typ.Padding(), '1,1,0,0'),
-             "Padding for statusbar (top, bottom, left, right)."),
-
-            ('window-title-format',
-             SettingValue(typ.FormatString(fields=['perc', 'perc_raw', 'title',
-                                                   'title_sep', 'id',
-                                                   'scroll_pos', 'host',
-                                                   'backend', 'private']),
-                          '{perc}{title}{title_sep}qutebrowser'),
-             "The format to use for the window title. The following "
-             "placeholders are defined:\n\n"
-             "* `{perc}`: The percentage as a string like `[10%]`.\n"
-             "* `{perc_raw}`: The raw percentage, e.g. `10`\n"
-             "* `{title}`: The title of the current web page\n"
-             "* `{title_sep}`: The string ` - ` if a title is set, empty "
-             "otherwise.\n"
-             "* `{id}`: The internal window ID of this window.\n"
-             "* `{scroll_pos}`: The page scroll position.\n"
-             "* `{host}`: The host of the current web page.\n"
-             "* `{backend}`: Either 'webkit' or 'webengine'\n"
-             "* `{private}` : Indicates when private mode is enabled.\n"),
-
-            ('modal-js-dialog',
-             SettingValue(typ.Bool(), 'false'),
-             "Use standard JavaScript modal dialog for alert() and confirm()"),
-
-            ('hide-wayland-decoration',
-             SettingValue(typ.Bool(), 'false'),
-             "Hide the window decoration when using wayland "
-             "(requires restart)"),
-
-            ('keyhint-blacklist',
-             SettingValue(typ.List(typ.String(), none_ok=True), ''),
-             "Keychains that shouldn't be shown in the keyhint dialog\n\n"
-             "Globs are supported, so ';*' will blacklist all keychains"
-             "starting with ';'. Use '*' to disable keyhints"),
-
-            ('keyhint-delay',
-             SettingValue(typ.Int(minval=0), '500'),
-             "Time from pressing a key to seeing the keyhint dialog (ms)"),
-
-            ('prompt-radius',
-             SettingValue(typ.Int(minval=0), '8'),
-             "The rounding radius for the edges of prompts."),
-
-            ('prompt-filebrowser',
-             SettingValue(typ.Bool(), 'true'),
-             "Show a filebrowser in upload/download prompts."),
-
-            readonly=readonly
-        )),
-
-        ('network', sect.KeyValue(
-            ('do-not-track',
-             SettingValue(typ.Bool(), 'true'),
-             "Value to send in the `DNT` header."),
-
-            ('accept-language',
-             SettingValue(typ.String(none_ok=True), 'en-US,en'),
-             "Value to send in the `accept-language` header."),
-
-            ('referer-header',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('always', "Always send."),
-                     ('never', "Never send; this is not recommended,"
-                      " as some sites may break."),
-                     ('same-domain', "Only send for the same domain."
-                      " This will still protect your privacy, but"
-                      " shouldn't break any sites.")
-                 )), 'same-domain', backends=[usertypes.Backend.QtWebKit]),
-             "Send the Referer header"),
-
-            ('user-agent',
-             SettingValue(typ.UserAgent(none_ok=True), ''),
-             "User agent to send. Empty to send the default."),
-
-            ('proxy',
-             SettingValue(typ.Proxy(), 'system',
-                          backends=(None if qtutils.version_check('5.8')
-                                    else [usertypes.Backend.QtWebKit])),
-             "The proxy to use.\n\n"
-             "In addition to the listed values, you can use a `socks://...` "
-             "or `http://...` URL.\n\n"
-             "This setting only works with Qt 5.8 or newer when using the "
-             "QtWebEngine backend."),
-
-            ('proxy-dns-requests',
-             SettingValue(typ.Bool(), 'true',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether to send DNS requests over the configured proxy."),
-
-            ('ssl-strict',
-             SettingValue(typ.BoolAsk(), 'ask'),
-             "Whether to validate SSL handshakes."),
-
-            ('dns-prefetch',
-             SettingValue(typ.Bool(), 'true',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether to try to pre-fetch DNS entries to speed up browsing."),
-
-            ('custom-headers',
-             SettingValue(typ.HeaderDict(none_ok=True), ''),
-             "Set custom headers for qutebrowser HTTP requests."),
-
-            ('netrc-file',
-             SettingValue(typ.File(none_ok=True), ''),
-             "Set location of a netrc-file for HTTP authentication. If empty, "
-             "~/.netrc is used."),
-
-            readonly=readonly
-        )),
-
-        ('completion', sect.KeyValue(
-            ('show',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('always', "Whenever a completion is available."),
-                     ('auto', "Whenever a completion is requested."),
-                     ('never', "Never.")
-                 )), 'always'),
-             "When to show the autocompletion window."),
-
-            ('download-path-suggestion',
-             SettingValue(
-                 typ.String(valid_values=typ.ValidValues(
-                     ('path', "Show only the download path."),
-                     ('filename', "Show only download filename."),
-                     ('both', "Show download path and filename."))),
-                 'path'),
-             "What to display in the download filename input."),
-
-            ('timestamp-format',
-             SettingValue(typ.TimestampTemplate(none_ok=True), '%Y-%m-%d'),
-             "How to format timestamps (e.g. for history)"),
-
-            ('height',
-             SettingValue(typ.PercOrInt(minperc=0, maxperc=100, minint=1),
-                          '50%'),
-             "The height of the completion, in px or as percentage of the "
-             "window."),
-
-            ('cmd-history-max-items',
-             SettingValue(typ.Int(minval=-1), '100'),
-             "How many commands to save in the command history.\n\n"
-             "0: no history / -1: unlimited"),
-
-            ('web-history-max-items',
-             SettingValue(typ.Int(minval=-1), '1000'),
-             "How many URLs to show in the web history.\n\n"
-             "0: no history / -1: unlimited"),
-
-            ('quick-complete',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to move on to the next part when there's only one "
-             "possible completion left."),
-
-            ('shrink',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to shrink the completion to be smaller than the "
-             "configured size if there are no scrollbars."),
-
-            ('scrollbar-width',
-             SettingValue(typ.Int(minval=0), '12'),
-             "Width of the scrollbar in the completion window (in px)."),
-
-            ('scrollbar-padding',
-             SettingValue(typ.Int(minval=0), '2'),
-             "Padding of scrollbar handle in completion window (in px)."),
-
-            readonly=readonly
-        )),
-
-        ('input', sect.KeyValue(
-            ('timeout',
-             SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '500'),
-             "Timeout (in milliseconds) for ambiguous key bindings.\n\n"
-             "If the current input forms both a complete match and a partial "
-             "match, the complete match will be executed after this time."),
-
-            ('partial-timeout',
-             SettingValue(typ.Int(minval=0, maxval=MAXVALS['int']), '5000'),
-             "Timeout (in milliseconds) for partially typed key bindings.\n\n"
-             "If the current input forms only partial matches, the keystring "
-             "will be cleared after this time."),
-
-            ('insert-mode-on-plugins',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to switch to insert mode when clicking flash and other "
-             "plugins."),
-
-            ('auto-leave-insert-mode',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to leave insert mode if a non-editable element is "
-             "clicked."),
-
-            ('auto-insert-mode',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to automatically enter insert mode if an editable "
-             "element is focused after page load."),
-
-            ('forward-unbound-keys',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('all', "Forward all unbound keys."),
-                     ('auto', "Forward unbound non-alphanumeric "
-                      "keys."),
-                     ('none', "Don't forward any keys.")
-                 )), 'auto'),
-             "Whether to forward unbound keys to the webview in normal mode."),
-
-            ('spatial-navigation',
-             SettingValue(typ.Bool(), 'false'),
-             "Enables or disables the Spatial Navigation feature.\n\n"
-             "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 a 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."),
-
-            ('links-included-in-focus-chain',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether hyperlinks should be included in the keyboard focus "
-             "chain."),
-
-            ('rocker-gestures',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to enable Opera-like mouse rocker gestures. This "
-             "disables the context menu."),
-
-            ('mouse-zoom-divider',
-             SettingValue(typ.Int(minval=0), '512'),
-             "How much to divide the mouse wheel movements to translate them "
-             "into zoom increments."),
-
-            readonly=readonly
-        )),
-
-        ('tabs', sect.KeyValue(
-            ('background-tabs',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to open new tabs (middleclick/ctrl+click) in "
-             "background."),
-
-            ('select-on-remove',
-             SettingValue(typ.SelectOnRemove(), 'next'),
-             "Which tab to select when the focused tab is removed."),
-
-            ('new-tab-position',
-             SettingValue(typ.NewTabPosition(), 'next'),
-             "How new tabs are positioned."),
-
-            ('new-tab-position-explicit',
-             SettingValue(typ.NewTabPosition(), 'last'),
-             "How new tabs opened explicitly are positioned."),
-
-            ('last-close',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('ignore', "Don't do anything."),
-                     ('blank', "Load a blank page."),
-                     ('startpage', "Load the start page."),
-                     ('default-page', "Load the default page."),
-                     ('close', "Close the window.")
-                 )), 'ignore'),
-             "Behavior when the last tab is closed."),
-
-            ('show',
-             SettingValue(
-                 typ.String(valid_values=typ.ValidValues(
-                     ('always', "Always show the tab bar."),
-                     ('never', "Always hide the tab bar."),
-                     ('multiple', "Hide the tab bar if only one tab "
-                      "is open."),
-                     ('switching', "Show the tab bar when switching "
-                      "tabs.")
-                 )), 'always'),
-             "When to show the tab bar"),
-
-            ('show-switching-delay',
-             SettingValue(typ.Int(), '800'),
-             "Time to show the tab bar before hiding it when tabs->show is "
-             "set to 'switching'."),
-
-            ('wrap',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to wrap when changing tabs."),
-
-            ('movable',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether tabs should be movable."),
-
-            ('close-mouse-button',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('right', "Close tabs on right-click."),
-                     ('middle', "Close tabs on middle-click."),
-                     ('none', "Don't close tabs using the mouse.")
-                 )), 'middle'),
-             "On which mouse button to close tabs."),
-
-            ('position',
-             SettingValue(typ.Position(), 'top'),
-             "The position of the tab bar."),
-
-            ('show-favicons',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to show favicons in the tab bar."),
-
-            ('favicon-scale',
-             SettingValue(typ.Float(minval=0.0), '1.0'),
-             "Scale for favicons in the tab bar. The tab size is unchanged, "
-             "so big favicons also require extra `tabs->padding`."),
-
-            ('width',
-             SettingValue(typ.PercOrInt(minperc=0, maxperc=100, minint=1),
-                          '20%'),
-             "The width of the tab bar if it's vertical, in px or as "
-             "percentage of the window."),
-
-            ('pinned-width',
-             SettingValue(typ.Int(minval=10),
-                          '43'),
-             "The width for pinned tabs with a horizontal tabbar, in px."),
-
-            ('indicator-width',
-             SettingValue(typ.Int(minval=0), '3'),
-             "Width of the progress indicator (0 to disable)."),
-
-            ('tabs-are-windows',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether to open windows instead of tabs."),
-
-            ('title-format',
-             SettingValue(typ.FormatString(
-                 fields=['perc', 'perc_raw', 'title', 'title_sep', 'index',
-                         'id', 'scroll_pos', 'host', 'private'], none_ok=True),
-                 '{index}: {title}'),
-             "The format to use for the tab title. The following placeholders "
-             "are defined:\n\n"
-             "* `{perc}`: The percentage as a string like `[10%]`.\n"
-             "* `{perc_raw}`: The raw percentage, e.g. `10`\n"
-             "* `{title}`: The title of the current web page\n"
-             "* `{title_sep}`: The string ` - ` if a title is set, empty "
-             "otherwise.\n"
-             "* `{index}`: The index of this tab.\n"
-             "* `{id}`: The internal tab ID of this tab.\n"
-             "* `{scroll_pos}`: The page scroll position.\n"
-             "* `{host}`: The host of the current web page.\n"
-             "* `{backend}`: Either 'webkit' or 'webengine'\n"
-             "* `{private}` : Indicates when private mode is enabled.\n"),
-
-            ('title-format-pinned',
-             SettingValue(typ.FormatString(
-                 fields=['perc', 'perc_raw', 'title', 'title_sep', 'index',
-                         'id', 'scroll_pos', 'host', 'private'], none_ok=True),
-                 '{index}'),
-             "The format to use for the tab title for pinned tabs. "
-             "The same placeholders like for title-format are defined."),
-
-            ('title-alignment',
-             SettingValue(typ.TextAlignment(), 'left'),
-             "Alignment of the text inside of tabs"),
-
-            ('mousewheel-tab-switching',
-             SettingValue(typ.Bool(), 'true'),
-             "Switch between tabs using the mouse wheel."),
-
-            ('padding',
-             SettingValue(typ.Padding(), '0,0,5,5'),
-             "Padding for tabs (top, bottom, left, right)."),
-
-            ('indicator-padding',
-             SettingValue(typ.Padding(), '2,2,0,4'),
-             "Padding for indicators (top, bottom, left, right)."),
-
-            readonly=readonly
-        )),
-
-        ('storage', sect.KeyValue(
-            ('download-directory',
-             SettingValue(typ.Directory(none_ok=True), ''),
-             "The directory to save downloads to. An empty value selects a "
-             "sensible os-specific default. Will expand environment "
-             "variables."),
-
-            ('prompt-download-directory',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to prompt the user for the download location.\n"
-             "If set to false, 'download-directory' will be used."),
-
-            ('remember-download-directory',
-             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(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 "
-             "navigating forth or back to pages in the forward/back history, "
-             "by pausing and resuming up to _n_ pages.\n\n"
-             "For more information about the feature, please refer to: "
-             "http://webkit.org/blog/427/webkit-page-cache-i-the-basics/"),
-
-            ('offline-web-application-cache',
-             SettingValue(typ.Bool(), 'true',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether support for the HTML 5 web application cache feature is "
-             "enabled.\n\n"
-             "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.\n\n"
-             "The feature is described in details at: "
-             "http://dev.w3.org/html5/spec/Overview.html#appcache"),
-
-            ('local-storage',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether support for HTML 5 local storage and Web SQL is "
-             "enabled."),
-
-            ('cache-size',
-             SettingValue(typ.Int(none_ok=True, minval=0,
-                                  maxval=MAXVALS['int64']), ''),
-             "Size of the HTTP network cache. Empty to use the default "
-             "value."),
-
-            readonly=readonly
-        )),
-
-        ('content', sect.KeyValue(
-            ('allow-images',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether images are automatically loaded in web pages."),
-
-            ('allow-javascript',
-             SettingValue(typ.Bool(), 'true'),
-             "Enables or disables the running of JavaScript programs."),
-
-            ('allow-plugins',
-             SettingValue(typ.Bool(), 'false'),
-             "Enables or disables plugins in Web pages.\n\n"
-             'Qt plugins with a mimetype such as "application/x-qt-plugin" '
-             "are not affected by this setting."),
-
-            ('webgl',
-             SettingValue(typ.Bool(), 'true'),
-             "Enables or disables WebGL."),
-
-            ('hyperlink-auditing',
-             SettingValue(typ.Bool(), 'false'),
-             "Enable or disable hyperlink auditing (<a ping>)."),
-
-            ('geolocation',
-             SettingValue(typ.BoolAsk(), 'ask'),
-             "Allow websites to request geolocations."),
-
-            ('notifications',
-             SettingValue(typ.BoolAsk(), 'ask'),
-             "Allow websites to show notifications."),
-
-            ('media-capture',
-             SettingValue(typ.BoolAsk(), 'ask',
-                          backends=[usertypes.Backend.QtWebEngine]),
-             "Allow websites to record audio/video."),
-
-            ('javascript-can-open-windows-automatically',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether JavaScript programs can open new windows without user "
-             "interaction."),
-
-            ('javascript-can-close-windows',
-             SettingValue(typ.Bool(), 'false',
-                          backends=[usertypes.Backend.QtWebKit]),
-             "Whether JavaScript programs can close windows."),
-
-            ('javascript-can-access-clipboard',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether JavaScript programs can read or write to the "
-             "clipboard.\nWith QtWebEngine, writing the clipboard as response "
-             "to a user interaction is always allowed."),
-
-            ('ignore-javascript-prompt',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether all javascript prompts should be ignored."),
-
-            ('ignore-javascript-alert',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether all javascript alerts should be ignored."),
-
-            ('local-content-can-access-remote-urls',
-             SettingValue(typ.Bool(), 'false'),
-             "Whether locally loaded documents are allowed to access remote "
-             "urls."),
-
-            ('local-content-can-access-file-urls',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether locally loaded documents are allowed to access other "
-             "local urls."),
-
-            ('cookies-accept',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('all', "Accept all cookies."),
-                     ('no-3rdparty', "Accept cookies from the same"
-                      " origin only."),
-                     ('no-unknown-3rdparty', "Accept cookies from "
-                      "the same origin only, unless a cookie is "
-                      "already set for the domain."),
-                     ('never', "Don't accept cookies at all.")
-                 )), 'no-3rdparty', backends=[usertypes.Backend.QtWebKit]),
-             "Control which cookies to accept."),
-
-            ('cookies-store',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to store cookies. Note this option needs a restart with "
-             "QtWebEngine on Qt < 5.9."),
-
-            ('host-block-lists',
-             SettingValue(
-                 typ.List(typ.Url(), none_ok=True),
-                 'https://www.malwaredomainlist.com/hostslist/hosts.txt,'
-                 'http://someonewhocares.org/hosts/hosts,'
-                 'http://winhelp2002.mvps.org/hosts.zip,'
-                 'http://malwaredomains.lehigh.edu/files/justdomains.zip,'
-                 'https://pgl.yoyo.org/adservers/serverlist.php?'
-                 'hostformat=hosts&mimetype=plaintext'),
-             "List of URLs of lists which contain hosts to block.\n\n"
-             "The file can be in one of the following formats:\n\n"
-             "- An '/etc/hosts'-like file\n"
-             "- One host per line\n"
-             "- A zip-file of any of the above, with either only one file, or "
-             "a file named 'hosts' (with any extension)."),
-
-            ('host-blocking-enabled',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether host blocking is enabled."),
-
-            ('host-blocking-whitelist',
-             SettingValue(typ.List(typ.String(), none_ok=True), 'piwik.org'),
-             "List of domains that should always be loaded, despite being "
-             "ad-blocked.\n\n"
-             "Domains may contain * and ? wildcards and are otherwise "
-             "required to exactly match the requested domain.\n\n"
-             "Local domains are always exempt from hostblocking."),
-
-            ('enable-pdfjs', SettingValue(typ.Bool(), 'false'),
-             "Enable pdf.js to view PDF files in the browser.\n\n"
-             "Note that the files can still be downloaded by clicking"
-             " the download button in the pdf.js viewer."),
-
-            readonly=readonly
-        )),
-
-        ('hints', sect.KeyValue(
-            ('border',
-             SettingValue(typ.String(), '1px solid #E3BE23'),
-             "CSS border value for hints."),
-
-            ('mode',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('number', "Use numeric hints. (In this mode you can "
-                      "also type letters form the hinted element to filter "
-                      "and reduce the number of elements that are hinted.)"),
-                     ('letter', "Use the chars in the hints -> "
-                      "chars setting."),
-                     ('word', "Use hints words based on the html "
-                      "elements and the extra words."),
-                 )), 'letter'),
-             "Mode to use for hints."),
-
-            ('chars',
-             SettingValue(typ.UniqueCharString(minlen=2, completions=[
-                 ('asdfghjkl', "Home row"),
-                 ('aoeuidnths', "Home row (Dvorak)"),
-                 ('abcdefghijklmnopqrstuvwxyz', "All letters"),
-             ]), 'asdfghjkl'),
-             "Chars used for hint strings."),
-
-            ('min-chars',
-             SettingValue(typ.Int(minval=1), '1'),
-             "Minimum number of chars used for hint strings."),
-
-            ('scatter',
-             SettingValue(typ.Bool(), 'true'),
-             "Whether to scatter hint key chains (like Vimium) or not (like "
-             "dwb). Ignored for number hints."),
-
-            ('uppercase',
-             SettingValue(typ.Bool(), 'false'),
-             "Make chars in hint strings uppercase."),
-
-            ('dictionary',
-             SettingValue(typ.File(required=False), '/usr/share/dict/words'),
-             "The dictionary file to be used by the word hints."),
-
-            ('auto-follow',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('always', "Auto-follow whenever there is only a single "
-                      "hint on a page."),
-                     ('unique-match', "Auto-follow whenever there is a unique "
-                      "non-empty match in either the hint string (word mode) "
-                      "or filter (number mode)."),
-                     ('full-match', "Follow the hint when the user typed the "
-                      "whole hint (letter, word or number mode) or the "
-                      "element's text (only in number mode)."),
-                     ('never', "The user will always need to press Enter to "
-                      "follow a hint."),
-                 )), 'unique-match'),
-             "Controls when a hint can be automatically followed without the "
-             "user pressing Enter."),
-
-            ('auto-follow-timeout',
-             SettingValue(typ.Int(), '0'),
-             "A timeout (in milliseconds) to inhibit normal-mode key bindings "
-             "after a successful auto-follow."),
-
-            ('next-regexes',
-             SettingValue(typ.List(typ.Regex(flags=re.IGNORECASE)),
-                          r'\bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,'
-                          r'\bcontinue\b'),
-             "A comma-separated list of regexes to use for 'next' links."),
-
-            ('prev-regexes',
-             SettingValue(typ.List(typ.Regex(flags=re.IGNORECASE)),
-                          r'\bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,'
-                          r'\b(<<|«)\b'),
-             "A comma-separated list of regexes to use for 'prev' links."),
-
-            ('find-implementation',
-             SettingValue(typ.String(
-                 valid_values=typ.ValidValues(
-                     ('javascript', "Better but slower"),
-                     ('python', "Slightly worse but faster"),
-                 )), 'python'),
-             "Which implementation to use to find elements to hint."),
-
-            ('hide-unmatched-rapid-hints',
-             SettingValue(typ.Bool(), 'true'),
-             "Controls hiding unmatched hints in rapid mode."),
-
-            readonly=readonly
-        )),
-
-        ('searchengines', sect.ValueList(
-            typ.SearchEngineName(), typ.SearchEngineUrl(),
-            ('DEFAULT', 'https://duckduckgo.com/?q={}'),
-
-            readonly=readonly
-        )),
-
-        ('aliases', sect.ValueList(
-            typ.String(forbidden=' '), typ.Command(),
-
-            readonly=readonly
-        )),
-
-        ('colors', sect.KeyValue(
-            ('completion.fg',
-             SettingValue(typ.QtColor(), 'white'),
-             "Text color of the completion widget."),
-
-            ('completion.bg',
-             SettingValue(typ.QssColor(), '#333333'),
-             "Background color of the completion widget."),
-
-            ('completion.alternate-bg',
-             SettingValue(typ.QssColor(), '#444444'),
-             "Alternating background color of the completion widget."),
-
-            ('completion.category.fg',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color of completion widget category headers."),
-
-            ('completion.category.bg',
-             SettingValue(typ.QssColor(), 'qlineargradient(x1:0, y1:0, x2:0, '
-                          'y2:1, stop:0 #888888, stop:1 #505050)'),
-             "Background color of the completion widget category headers."),
-
-            ('completion.category.border.top',
-             SettingValue(typ.QssColor(), 'black'),
-             "Top border color of the completion widget category headers."),
-
-            ('completion.category.border.bottom',
-             SettingValue(typ.QssColor(), 'black'),
-             "Bottom border color of the completion widget category headers."),
-
-            ('completion.item.selected.fg',
-             SettingValue(typ.QtColor(), 'black'),
-             "Foreground color of the selected completion item."),
-
-            ('completion.item.selected.bg',
-             SettingValue(typ.QssColor(), '#e8c000'),
-             "Background color of the selected completion item."),
-
-            ('completion.item.selected.border.top',
-             SettingValue(typ.QssColor(), '#bbbb00'),
-             "Top border color of the completion widget category headers."),
-
-            ('completion.item.selected.border.bottom',
-             SettingValue(
-                 typ.QssColor(), '#bbbb00'),
-             "Bottom border color of the selected completion item."),
-
-            ('completion.match.fg',
-             SettingValue(typ.QssColor(), '#ff4444'),
-             "Foreground color of the matched text in the completion."),
-
-            ('completion.scrollbar.fg',
-             SettingValue(typ.QssColor(), 'white'),
-             "Color of the scrollbar handle in completion view."),
-
-            ('completion.scrollbar.bg',
-             SettingValue(typ.QssColor(), '#333333'),
-             "Color of the scrollbar in completion view"),
-
-            ('statusbar.fg',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar."),
-
-            ('statusbar.bg',
-             SettingValue(typ.QssColor(), 'black'),
-             "Background color of the statusbar."),
-
-            ('statusbar.fg.private',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in private browsing mode."),
-
-            ('statusbar.bg.private',
-             SettingValue(typ.QssColor(), '#666666'),
-             "Background color of the statusbar in private browsing mode."),
-
-            ('statusbar.fg.insert',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in insert mode."),
-
-            ('statusbar.bg.insert',
-             SettingValue(typ.QssColor(), 'darkgreen'),
-             "Background color of the statusbar in insert mode."),
-
-            ('statusbar.fg.command',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in command mode."),
-
-            ('statusbar.bg.command',
-             SettingValue(typ.QssColor(), 'black'),
-             "Background color of the statusbar in command mode."),
-
-            ('statusbar.fg.command.private',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in private browsing + command "
-             "mode."),
-
-            ('statusbar.bg.command.private',
-             SettingValue(typ.QssColor(), 'grey'),
-             "Background color of the statusbar in private browsing + command "
-             "mode."),
-
-            ('statusbar.fg.caret',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in caret mode."),
-
-            ('statusbar.bg.caret',
-             SettingValue(typ.QssColor(), 'purple'),
-             "Background color of the statusbar in caret mode."),
-
-            ('statusbar.fg.caret-selection',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the statusbar in caret mode with a "
-             "selection"),
-
-            ('statusbar.bg.caret-selection',
-             SettingValue(typ.QssColor(), '#a12dff'),
-             "Background color of the statusbar in caret mode with a "
-             "selection"),
-
-            ('statusbar.progress.bg',
-             SettingValue(typ.QssColor(), 'white'),
-             "Background color of the progress bar."),
-
-            ('statusbar.url.fg',
-             SettingValue(typ.QssColor(), 'white'),
-             "Default foreground color of the URL in the statusbar."),
-
-            ('statusbar.url.fg.success',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of the URL in the statusbar on successful "
-             "load (http)."),
-
-            ('statusbar.url.fg.success.https',
-             SettingValue(typ.QssColor(), 'lime'),
-             "Foreground color of the URL in the statusbar on successful "
-             "load (https)."),
-
-            ('statusbar.url.fg.error',
-             SettingValue(typ.QssColor(), 'orange'),
-             "Foreground color of the URL in the statusbar on error."),
-
-            ('statusbar.url.fg.warn',
-             SettingValue(typ.QssColor(), 'yellow'),
-             "Foreground color of the URL in the statusbar when there's a "
-             "warning."),
-
-            ('statusbar.url.fg.hover',
-             SettingValue(typ.QssColor(), 'aqua'),
-             "Foreground color of the URL in the statusbar for hovered "
-             "links."),
-
-            ('tabs.fg.odd',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color of unselected odd tabs."),
-
-            ('tabs.bg.odd',
-             SettingValue(typ.QtColor(), 'grey'),
-             "Background color of unselected odd tabs."),
-
-            ('tabs.fg.even',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color of unselected even tabs."),
-
-            ('tabs.bg.even',
-             SettingValue(typ.QtColor(), 'darkgrey'),
-             "Background color of unselected even tabs."),
-
-            ('tabs.fg.selected.odd',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color of selected odd tabs."),
-
-            ('tabs.bg.selected.odd',
-             SettingValue(typ.QtColor(), 'black'),
-             "Background color of selected odd tabs."),
-
-            ('tabs.fg.selected.even',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color of selected even tabs."),
-
-            ('tabs.bg.selected.even',
-             SettingValue(typ.QtColor(), 'black'),
-             "Background color of selected even tabs."),
-
-            ('tabs.bg.bar',
-             SettingValue(typ.QtColor(), '#555555'),
-             "Background color of the tab bar."),
-
-            ('tabs.indicator.start',
-             SettingValue(typ.QtColor(), '#0000aa'),
-             "Color gradient start for the tab indicator."),
-
-            ('tabs.indicator.stop',
-             SettingValue(typ.QtColor(), '#00aa00'),
-             "Color gradient end for the tab indicator."),
-
-            ('tabs.indicator.error',
-             SettingValue(typ.QtColor(), '#ff0000'),
-             "Color for the tab indicator on errors.."),
-
-            ('tabs.indicator.system',
-             SettingValue(typ.ColorSystem(), 'rgb'),
-             "Color gradient interpolation system for the tab indicator."),
-
-            ('hints.fg',
-             SettingValue(typ.QssColor(), 'black'),
-             "Font color for hints."),
-
-            ('hints.bg',
-             SettingValue(typ.QssColor(), 'qlineargradient(x1:0, y1:0, x2:0, '
-                          'y2:1, stop:0 rgba(255, 247, 133, 0.8), '
-                          'stop:1 rgba(255, 197, 66, 0.8))'),
-             "Background color for hints. Note that you can use a `rgba(...)` "
-             "value for transparency."),
-
-            ('hints.fg.match',
-             SettingValue(typ.QssColor(), 'green'),
-             "Font color for the matched part of hints."),
-
-            ('downloads.bg.bar',
-             SettingValue(typ.QssColor(), 'black'),
-             "Background color for the download bar."),
-
-            ('downloads.fg.start',
-             SettingValue(typ.QtColor(), 'white'),
-             "Color gradient start for download text."),
-
-            ('downloads.bg.start',
-             SettingValue(typ.QtColor(), '#0000aa'),
-             "Color gradient start for download backgrounds."),
-
-            ('downloads.fg.stop',
-             SettingValue(typ.QtColor(), '#0000aa'),
-             "Color gradient end for download text."),
-
-            ('downloads.bg.stop',
-             SettingValue(typ.QtColor(), '#00aa00'),
-             "Color gradient stop for download backgrounds."),
-
-            ('downloads.fg.system',
-             SettingValue(typ.ColorSystem(), 'rgb'),
-             "Color gradient interpolation system for download text."),
-
-            ('downloads.bg.system',
-             SettingValue(typ.ColorSystem(), 'rgb'),
-             "Color gradient interpolation system for download backgrounds."),
-
-            ('downloads.fg.error',
-             SettingValue(typ.QtColor(), 'white'),
-             "Foreground color for downloads with errors."),
-
-            ('downloads.bg.error',
-             SettingValue(typ.QtColor(), 'red'),
-             "Background color for downloads with errors."),
-
-            ('webpage.bg',
-             SettingValue(typ.QtColor(none_ok=True), 'white'),
-             "Background color for webpages if unset (or empty to use the "
-             "theme's color)"),
-
-            ('keyhint.fg',
-             SettingValue(typ.QssColor(), '#FFFFFF'),
-             "Text color for the keyhint widget."),
-
-            ('keyhint.fg.suffix',
-             SettingValue(typ.CssColor(), '#FFFF00'),
-             "Highlight color for keys to complete the current keychain"),
-
-            ('keyhint.bg',
-             SettingValue(typ.QssColor(), 'rgba(0, 0, 0, 80%)'),
-             "Background color of the keyhint widget."),
-
-            ('messages.fg.error',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color of an error message."),
-
-            ('messages.bg.error',
-             SettingValue(typ.QssColor(), 'red'),
-             "Background color of an error message."),
-
-            ('messages.border.error',
-             SettingValue(typ.QssColor(), '#bb0000'),
-             "Border color of an error message."),
-
-            ('messages.fg.warning',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color a warning message."),
-
-            ('messages.bg.warning',
-             SettingValue(typ.QssColor(), 'darkorange'),
-             "Background color of a warning message."),
-
-            ('messages.border.warning',
-             SettingValue(typ.QssColor(), '#d47300'),
-             "Border color of an error message."),
-
-            ('messages.fg.info',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color an info message."),
-
-            ('messages.bg.info',
-             SettingValue(typ.QssColor(), 'black'),
-             "Background color of an info message."),
-
-            ('messages.border.info',
-             SettingValue(typ.QssColor(), '#333333'),
-             "Border color of an info message."),
-
-            ('prompts.fg',
-             SettingValue(typ.QssColor(), 'white'),
-             "Foreground color for prompts."),
-
-            ('prompts.bg',
-             SettingValue(typ.QssColor(), 'darkblue'),
-             "Background color for prompts."),
-
-            ('prompts.selected.bg',
-             SettingValue(typ.QssColor(), '#308cc6'),
-             "Background color for the selected item in filename prompts."),
-
-            readonly=readonly
-        )),
-
-        ('fonts', sect.KeyValue(
-            ('_monospace',
-             SettingValue(typ.Font(), 'xos4 Terminus, Terminus, Monospace, '
-                          '"DejaVu Sans Mono", Monaco, '
-                          '"Bitstream Vera Sans Mono", "Andale Mono", '
-                          '"Courier New", Courier, "Liberation Mono", '
-                          'monospace, Fixed, Consolas, Terminal'),
-             "Default monospace fonts."),
-
-            ('completion',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used in the completion widget."),
-
-            ('completion.category',
-             SettingValue(typ.Font(), 'bold ' + DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used in the completion categories."),
-
-            ('tabbar',
-             SettingValue(typ.QtFont(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used in the tab bar."),
-
-            ('statusbar',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used in the statusbar."),
-
-            ('downloads',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used for the downloadbar."),
-
-            ('hints',
-             SettingValue(typ.Font(), 'bold 13px ${_monospace}'),
-             "Font used for the hints."),
-
-            ('debug-console',
-             SettingValue(typ.QtFont(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used for the debugging console."),
-
-            ('web-family-standard',
-             SettingValue(typ.FontFamily(none_ok=True), ''),
-             "Font family for standard fonts."),
-
-            ('web-family-fixed',
-             SettingValue(typ.FontFamily(none_ok=True), ''),
-             "Font family for fixed fonts."),
-
-            ('web-family-serif',
-             SettingValue(typ.FontFamily(none_ok=True), ''),
-             "Font family for serif fonts."),
-
-            ('web-family-sans-serif',
-             SettingValue(typ.FontFamily(none_ok=True), ''),
-             "Font family for sans-serif fonts."),
-
-            ('web-family-cursive',
-             SettingValue(typ.FontFamily(none_ok=True), ''),
-             "Font family for cursive fonts."),
-
-            ('web-family-fantasy',
-             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(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(minval=0, maxval=MAXVALS['int']), '6'),
-             "The minimum logical font size that is applied when zooming "
-             "out."),
-
-            ('web-size-default',
-             SettingValue(typ.Int(minval=1, maxval=MAXVALS['int']), '16'),
-             "The default font size for regular text."),
-
-            ('web-size-default-fixed',
-             SettingValue(typ.Int(minval=1, maxval=MAXVALS['int']), '13'),
-             "The default font size for fixed-pitch text."),
-
-            ('keyhint',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used in the keyhint widget."),
-
-            ('messages.error',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used for error messages."),
-
-            ('messages.warning',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used for warning messages."),
-
-            ('messages.info',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + MONOSPACE),
-             "Font used for info messages."),
-
-            ('prompts',
-             SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' sans-serif'),
-             "Font used for prompts."),
-
-            readonly=readonly
-        )),
-    ])
-
-
-DATA = data(readonly=True)
-
-
 KEY_FIRST_COMMENT = """
 # vim: ft=conf
 #
@@ -1858,3 +522,151 @@ CHANGED_KEY_COMMANDS = [
     (re.compile(r'^tab-only -r$'), r'tab-only --next'),
     (re.compile(r'^tab-only --right$'), r'tab-only --next'),
 ]
+
+
+Option = collections.namedtuple('Option', ['typ', 'default', 'backends',
+                                           'description'])
+
+
+def _raise_invalid_node(name, what, node):
+    """Raise an exception for an invalid configdata YAML node.
+
+    Args:
+        name: The name of the setting being parsed.
+        what: The name of the thing being parsed.
+        node: The invalid node.
+    """
+    raise ValueError("Invalid node for {} while reading {}: {!r}".format(
+        name, what, node))
+
+
+def _parse_yaml_type(name, node):
+    if isinstance(node, str):
+        # e.g:
+        #  type: Bool
+        # -> create the type object without any arguments
+        type_name = node
+        kwargs = {}
+    elif isinstance(node, dict):
+        # e.g:
+        #  type:
+        #    name: String
+        #    none_ok: true
+        # -> create the type object and pass arguments
+        type_name = node.pop('name')
+        kwargs = node
+    else:
+        _raise_invalid_node(name, 'type', node)
+
+    try:
+        typ = getattr(configtypes, type_name)
+    except AttributeError as e:
+        raise AttributeError("Did not find type {} for {}".format(
+            type_name, name))
+
+    # Parse sub-types
+    try:
+        if typ is configtypes.Dict:
+            kwargs['keytype'] = _parse_yaml_type(name, kwargs['keytype'])
+            kwargs['valtype'] = _parse_yaml_type(name, kwargs['valtype'])
+        elif typ is configtypes.List:
+            kwargs['valtype'] = _parse_yaml_type(name, kwargs['valtype'])
+    except KeyError as e:
+        _raise_invalid_node(name, str(e), node)
+
+    try:
+        return typ(**kwargs)
+    except TypeError as e:
+        raise TypeError("Error while creating {} with {}: {}".format(
+            type_name, node, e))
+
+
+def _parse_yaml_backends_dict(name, node):
+    """Parse a dict definition for backends.
+
+    Example:
+
+    backends:
+      QtWebKit: true
+      QtWebEngine: Qt 5.9
+    """
+    str_to_backend = {
+        'QtWebKit': usertypes.Backend.QtWebKit,
+        'QtWebEngine': usertypes.Backend.QtWebEngine,
+    }
+
+    if node.keys() != str_to_backend.keys():
+        _raise_invalid_node(name, 'backends', node)
+
+    backends = []
+
+    # The value associated to the key, and whether we should add that backend or
+    # not.
+    conditionals = {
+        True: True,
+        False: False,
+        'Qt 5.8': qtutils.version_check('5.8'),
+        'Qt 5.9': qtutils.version_check('5.9'),
+    }
+    for key in node.keys():
+        if conditionals[node[key]]:
+            backends.append(str_to_backend[key])
+
+    return backends
+
+
+def _parse_yaml_backends(name, node):
+    """Parse a backend node in the yaml.
+
+    It can have one of those four forms:
+    - Not present -> setting applies to both backends.
+    - backend: QtWebKit -> setting only available with QtWebKit
+    - backend: QtWebEngine -> setting only available with QtWebEngine
+    - backend:
+       QtWebKit: true
+       QtWebEngine: Qt 5.9
+      -> setting available based on the given conditionals.
+    """
+    if node is None:
+        return [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine]
+    elif node == 'QtWebKit':
+        return [usertypes.Backend.QtWebKit]
+    elif node == 'QtWebEngine':
+        return [usertypes.Backend.QtWebEngine]
+    elif isinstance(node, dict):
+        return _parse_yaml_backends_dict(name, node)
+    _raise_invalid_node(name, 'backends', node)
+
+
+def _read_yaml(yaml_data):
+    """Read config data from a YAML file.
+
+    Args:
+        yaml_data: The YAML string to parse.
+
+    Return:
+        A dict mapping option names to Option elements.
+    """
+    parsed = {}
+    data = yaml.load(yaml_data)
+
+    keys = {'type', 'default', 'desc', 'backend'}
+
+    for name, option in data.items():
+        if not set(option.keys()).issubset(keys):
+            raise ValueError("Invalid keys {} for {}".format(
+                option.keys(), name))
+
+        parsed[name] = Option(
+            typ=_parse_yaml_type(name, option['type']),
+            default=option['default'],
+            backends=_parse_yaml_backends(name, option.get('backend', None)),
+            description=option['desc'])
+
+    return parsed
+
+
+def init():
+    """Initialize configdata from the YAML file."""
+    global DATA
+    DATA = _read_yaml(utils.read_file('config/configdata.yml'))
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 5245095f6..a66875dba 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -8,14 +8,14 @@ ignore_case:
 start_page:
   type:
     name: List
-    elemtype: String
+    valtype: String
   default: ["https://start.duckduckgo.com"]
   desc: The default page(s) to open at the start.
 
 yank_ignored_url_parameters:
   type:
     name: List
-    elemtype: String
+    valtype: String
   default:
     - ref
     - utm_source
@@ -78,7 +78,7 @@ editor.command:
 editor.encoding:
   type: Encoding
   default: utf-8
-  desc: Encoding to use for the editor.
+  desc : Encoding to use for the editor.
 
 content.private_browsing:
   type: Bool
@@ -198,7 +198,7 @@ history_session_interval:
 zoom.levels:
   type:
     name: List
-    elemtype:
+    valtype:
       name: Perc
       minval: 0
   default:
@@ -354,7 +354,7 @@ window.hide_wayland_decoration:
 keyhint.blacklist:
   type:
     name: List
-    elemtype:
+    valtype:
       name: String
       none_ok: true
   default: ""
@@ -417,11 +417,7 @@ content.user_agent:
 
 content.proxy:
   default: system
-  type:
-    name: Proxy
-    valid_values:
-    - system: "Use the system wide proxy."
-    - none: "Don't use any proxy"
+  type: Proxy
   backend:
     QtWebKit: true
     QtWebEngine: Qt 5.8
@@ -1015,7 +1011,7 @@ content.host_blocking.lists:
     - "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext"
   type:
     name: List
-    elemtype: Url
+    valtype: Url
     none_ok: true
   desc: |
     List of URLs of lists which contain hosts to block.
@@ -1037,7 +1033,7 @@ content.host_blocking.whitelist:
   - piwik.org
   type:
     name: List
-    valtype: string
+    valtype: String
     none_ok: true
   desc: >-
     List of domains that should always be loaded, despite being ad-blocked.
diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py
index 3d6e71426..6f11ab1ef 100644
--- a/qutebrowser/config/configtypes.py
+++ b/qutebrowser/config/configtypes.py
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QTabWidget, QTabBar
 
 from qutebrowser.commands import cmdutils
 from qutebrowser.config import configexc
-from qutebrowser.utils import standarddir, utils
+from qutebrowser.utils import standarddir, utils, qtutils
 
 
 SYSTEM_PROXY = object()  # Return value for Proxy type
@@ -313,27 +313,27 @@ class List(BaseType):
 
     """Base class for a (string-)list setting."""
 
-    _show_inner_type = True
+    _show_valtype = True
 
-    def __init__(self, inner_type, none_ok=False, length=None):
+    def __init__(self, valtype, none_ok=False, length=None):
         super().__init__(none_ok)
-        self.inner_type = inner_type
+        self.valtype = valtype
         self.length = length
 
     def get_name(self):
         name = super().get_name()
-        if self._show_inner_type:
-            name += " of " + self.inner_type.get_name()
+        if self._show_valtype:
+            name += " of " + self.valtype.get_name()
         return name
 
     def get_valid_values(self):
-        return self.inner_type.get_valid_values()
+        return self.valtype.get_valid_values()
 
     def transform(self, value):
         if not value:
             return None
         else:
-            return [self.inner_type.transform(v.strip())
+            return [self.valtype.transform(v.strip())
                     for v in value.split(',')]
 
     def validate(self, value):
@@ -345,7 +345,7 @@ class List(BaseType):
             raise configexc.ValidationError(value, "Exactly {} values need to "
                                             "be set!".format(self.length))
         for val in vals:
-            self.inner_type.validate(val.strip())
+            self.valtype.validate(val.strip())
 
 
 class FlagList(List):
@@ -358,14 +358,14 @@ class FlagList(List):
 
     combinable_values = None
 
-    _show_inner_type = False
+    _show_valtype = False
 
     def __init__(self, none_ok=False, valid_values=None):
         super().__init__(BaseType(), none_ok)
-        self.inner_type.valid_values = valid_values
+        self.valtype.valid_values = valid_values
 
     def validate(self, value):
-        if self.inner_type.valid_values is not None:
+        if self.valtype.valid_values is not None:
             super().validate(value)
         else:
             self._basic_validation(value)
@@ -379,7 +379,7 @@ class FlagList(List):
                 value, "List contains duplicate values!")
 
     def complete(self):
-        valid_values = self.inner_type.valid_values
+        valid_values = self.valtype.valid_values
         if valid_values is None:
             return None
 
@@ -453,11 +453,22 @@ class Int(BaseType):
 
     def __init__(self, minval=None, maxval=None, none_ok=False):
         super().__init__(none_ok)
-        if maxval is not None and minval is not None and maxval < minval:
-            raise ValueError("minval ({}) needs to be <= maxval ({})!".format(
-                minval, maxval))
-        self.minval = minval
-        self.maxval = maxval
+        self.minval = self._parse_limit(minval)
+        self.maxval = self._parse_limit(maxval)
+        if self.maxval is not None and self.minval is not None:
+            if self.maxval < self.minval:
+                raise ValueError("minval ({}) needs to be <= maxval ({})!"
+                                 .format(self.minval, self.maxval))
+
+    def _parse_limit(self, value):
+        if value == 'maxint':
+            return qtutils.MAXVALS['int']
+        elif value == 'maxint64':
+            return qtutils.MAXVALS['int64']
+        else:
+            if value is not None:
+                assert isinstance(value, int), value
+            return value
 
     def transform(self, value):
         if not value:
@@ -1056,12 +1067,12 @@ class Padding(List):
 
     """Setting for paddings around elements."""
 
-    _show_inner_type = False
+    _show_valtype = False
 
     def __init__(self, none_ok=False, valid_values=None):
         super().__init__(Int(minval=0, none_ok=none_ok),
                          none_ok=none_ok, length=4)
-        self.inner_type.valid_values = valid_values
+        self.valtype.valid_values = valid_values
 
     def transform(self, value):
         elems = super().transform(value)
@@ -1179,10 +1190,17 @@ class Url(BaseType):
                                             "{}".format(val.errorString()))
 
 
-class HeaderDict(BaseType):
+class Dict(BaseType):
 
     """A JSON-like dictionary for custom HTTP headers."""
 
+    # FIXME:conf validate correctly
+
+    def __init__(self, keytype, valtype, none_ok=False):
+        super().__init__(none_ok)
+        self.keytype = keytype
+        self.valtype = valtype
+
     def _validate_str(self, value, what):
         """Check if the given thing is an ascii-only string.
 
@@ -1274,8 +1292,8 @@ class ConfirmQuit(FlagList):
 
     def __init__(self, none_ok=False):
         super().__init__(none_ok)
-        self.inner_type.none_ok = none_ok
-        self.inner_type.valid_values = ValidValues(
+        self.valtype.none_ok = none_ok
+        self.valtype.valid_values = ValidValues(
             ('always', "Always show a confirmation."),
             ('multiple-tabs', "Show a confirmation if "
              "multiple tabs are opened."),
diff --git a/qutebrowser/config/newconfig.py b/qutebrowser/config/newconfig.py
index f4c350d66..807ad0bfe 100644
--- a/qutebrowser/config/newconfig.py
+++ b/qutebrowser/config/newconfig.py
@@ -48,14 +48,16 @@ class NewConfigManager(QObject):
         super().__init__(parent)
         self._values = {}
 
-    def _key(self, sect, opt):
-        return sect + ' -> ' + opt
+    def _key(self, sect, opt=None):
+        if opt is None:
+            # New usage
+            return sect
+        return sect + '.' + opt
 
     def read_defaults(self):
-        for name, section in configdata.data().items():
-            for key, value in section.items():
-                self._values[self._key(name, key)] = value
+        for name, option in configdata.DATA.items():
+            self._values[name] = option
 
     def get(self, section, option):
         val = self._values[self._key(section, option)]
-        return val.typ.transform(val.value())
+        return val.typ.transform(val.default)
diff --git a/scripts/dev/pylint_checkers/qute_pylint/config.py b/scripts/dev/pylint_checkers/qute_pylint/config.py
index be8ac8da8..f95979bbd 100644
--- a/scripts/dev/pylint_checkers/qute_pylint/config.py
+++ b/scripts/dev/pylint_checkers/qute_pylint/config.py
@@ -23,16 +23,11 @@ import sys
 import os
 import os.path
 
+import yaml
 import astroid
 from pylint import interfaces, checkers
 from pylint.checkers import utils
 
-sys.path.insert(
-    0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
-                    os.pardir))
-
-from qutebrowser.config import configdata
-
 
 class ConfigChecker(checkers.BaseChecker):
 
diff --git a/tests/unit/config/test_configdata.py b/tests/unit/config/test_configdata.py
index fdb7f00e4..e300318d4 100644
--- a/tests/unit/config/test_configdata.py
+++ b/tests/unit/config/test_configdata.py
@@ -18,27 +18,198 @@
 
 """Tests for qutebrowser.config.configdata."""
 
+import textwrap
+
+import yaml
 import pytest
 
-from qutebrowser.config import configdata
+from qutebrowser.config import configdata, configtypes
+from qutebrowser.utils import usertypes
 
 
-@pytest.mark.parametrize('sect', configdata.DATA.keys())
-def test_section_desc(sect):
-    """Make sure every section has a description."""
-    desc = configdata.SECTION_DESC[sect]
-    assert isinstance(desc, str)
+def test_init():
+    """Test reading the default yaml file."""
+    configdata.init()
+    assert isinstance(configdata.DATA, dict)
+    assert 'ignore_case' in configdata.DATA
 
 
-def test_data():
-    """Some simple sanity tests on data()."""
-    data = configdata.data()
-    assert 'general' in data
-    assert 'ignore-case' in data['general']
+class TestReadYaml:
 
 
-def test_readonly_data():
-    """Make sure DATA is readonly."""
-    with pytest.raises(ValueError, match="Trying to modify a read-only "
-                                         "config!"):
-        configdata.DATA['general'].setv('temp', 'ignore-case', 'true', 'true')
+    def test_valid(self):
+        data = textwrap.dedent("""
+            test1:
+                type: Bool
+                default: true
+                desc: Hello World
+
+            test2:
+                type: String
+                default: foo
+                backend: QtWebKit
+                desc: Hello World 2
+        """)
+        data = configdata._read_yaml(data)
+        assert data.keys() == {'test1', 'test2'}
+        assert data['test1'].description == "Hello World"
+        assert data['test2'].default == "foo"
+        assert data['test2'].backends == [usertypes.Backend.QtWebKit]
+        assert isinstance(data['test1'].typ, configtypes.Bool)
+
+    def test_invalid_keys(self):
+        """Test reading with unknown keys."""
+        data = textwrap.dedent("""
+            test:
+                type: Bool
+                default: true
+                desc: Hello World
+                hello: world
+        """,)
+        with pytest.raises(ValueError, match='Invalid keys'):
+            configdata._read_yaml(data)
+
+
+class TestParseYamlType:
+
+    def _yaml(self, s):
+        """Get the type from parsed YAML data."""
+        return yaml.load(textwrap.dedent(s))['type']
+
+    def test_simple(self):
+        """Test type which is only a name."""
+        data = self._yaml("type: Bool")
+        typ = configdata._parse_yaml_type('test', data)
+        assert isinstance(typ, configtypes.Bool)
+        assert not typ.none_ok
+
+    def test_complex(self):
+        """Test type parsing with arguments."""
+        data = self._yaml("""
+            type:
+              name: String
+              minlen: 2
+        """)
+        typ = configdata._parse_yaml_type('test', data)
+        assert isinstance(typ, configtypes.String)
+        assert not typ.none_ok
+        assert typ.minlen == 2
+
+    def test_list(self):
+        """Test type parsing with a list and subtypes."""
+        data = self._yaml("""
+            type:
+              name: List
+              valtype: String
+        """)
+        typ = configdata._parse_yaml_type('test', data)
+        assert isinstance(typ, configtypes.List)
+        assert isinstance(typ.valtype, configtypes.String)
+        assert not typ.none_ok
+        assert not typ.valtype.none_ok
+
+    def test_dict(self):
+        """Test type parsing with a dict and subtypes."""
+        data = self._yaml("""
+            type:
+              name: Dict
+              keytype: String
+              valtype:
+                name: Int
+                minval: 10
+        """)
+        typ = configdata._parse_yaml_type('test', data)
+        assert isinstance(typ, configtypes.Dict)
+        assert isinstance(typ.keytype, configtypes.String)
+        assert isinstance(typ.valtype, configtypes.Int)
+        assert not typ.none_ok
+        assert typ.valtype.minval == 10
+
+    def test_invalid_node(self):
+        """Test type parsing with invalid node type."""
+        data = self._yaml("type: 42")
+        with pytest.raises(ValueError, match="Invalid node for test while "
+                                             "reading type: 42"):
+            configdata._parse_yaml_type('test', data)
+
+    def test_unknown_type(self):
+        """Test type parsing with type which doesn't exist."""
+        data = self._yaml("type: Foobar")
+        with pytest.raises(AttributeError,
+                           match="Did not find type Foobar for test"):
+            configdata._parse_yaml_type('test', data)
+
+    def test_unknown_dict(self):
+        """Test type parsing with a dict without keytype."""
+        data = self._yaml("type: Dict")
+        with pytest.raises(ValueError, match="Invalid node for test while "
+                                             "reading 'keytype': 'Dict'"):
+            configdata._parse_yaml_type('test', data)
+
+    def test_unknown_args(self):
+        """Test type parsing with unknown type arguments."""
+        data = self._yaml("""
+            type:
+              name: Int
+              answer: 42
+        """)
+        with pytest.raises(TypeError, match="Error while creating Int"):
+            configdata._parse_yaml_type('test', data)
+
+
+class TestParseYamlBackend:
+
+    def _yaml(self, s):
+        """Get the type from parsed YAML data."""
+        return yaml.load(textwrap.dedent(s))['backend']
+
+    @pytest.mark.parametrize('backend, expected', [
+        ('QtWebKit', [usertypes.Backend.QtWebKit]),
+        ('QtWebEngine', [usertypes.Backend.QtWebEngine]),
+        # This is also what _parse_yaml_backends gets when backend: is not given
+        # at all
+        ('null', [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine]),
+    ])
+    def test_simple(self, backend, expected):
+        """Check a simple "backend: QtWebKit"."""
+        data = self._yaml("backend: {}".format(backend))
+        backends = configdata._parse_yaml_backends('test', data)
+        assert backends == expected
+
+    @pytest.mark.parametrize('webkit, has_new_version, expected', [
+        (True, True, [usertypes.Backend.QtWebKit,
+                      usertypes.Backend.QtWebEngine]),
+        (False, True, [usertypes.Backend.QtWebEngine]),
+        (True, False, [usertypes.Backend.QtWebKit]),
+    ])
+    def test_dict(self, monkeypatch, webkit, has_new_version, expected):
+        data = self._yaml("""
+            backend:
+              QtWebKit: {}
+              QtWebEngine: Qt 5.8
+        """.format('true' if webkit else 'false'))
+        monkeypatch.setattr(configdata.qtutils, 'version_check',
+                            lambda v: has_new_version)
+        backends = configdata._parse_yaml_backends('test', data)
+        assert backends == expected
+
+    @pytest.mark.parametrize('yaml_data', [
+        # Wrong type
+        "backend: 42",
+        # Unknown key
+        """
+        backend:
+          QtWebKit: true
+          QtWebEngine: true
+          foo: bar
+        """,
+        # Missing key
+        """
+        backend:
+          QtWebKit: true
+        """,
+    ])
+    def test_invalid_backend(self, yaml_data):
+        with pytest.raises(ValueError, match="Invalid node for test while "
+                                             "reading backends:"):
+            configdata._parse_yaml_backends('test', self._yaml(yaml_data))