1# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2
3# Copyright 2016-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4#
5# This file is part of qutebrowser.
6#
7# qutebrowser is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# qutebrowser is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with qutebrowser.  If not, see <https://www.gnu.org/licenses/>.
19
20"""Bridge from QWebSettings to our own settings.
21
22Module attributes:
23    ATTRIBUTES: A mapping from internal setting names to QWebSetting enum
24                constants.
25"""
26
27from typing import cast
28import os.path
29
30from PyQt5.QtCore import QUrl
31from PyQt5.QtGui import QFont
32from PyQt5.QtWebKit import QWebSettings
33from PyQt5.QtWebKitWidgets import QWebPage
34
35from qutebrowser.config import config, websettings
36from qutebrowser.config.websettings import AttributeInfo as Attr
37from qutebrowser.utils import standarddir, urlutils
38from qutebrowser.browser import shared
39
40
41# The global WebKitSettings object
42global_settings = cast('WebKitSettings', None)
43
44parsed_user_agent = None
45
46
47class WebKitSettings(websettings.AbstractSettings):
48
49    """A wrapper for the config for QWebSettings."""
50
51    _ATTRIBUTES = {
52        'content.images':
53            Attr(QWebSettings.AutoLoadImages),
54        'content.javascript.enabled':
55            Attr(QWebSettings.JavascriptEnabled),
56        'content.javascript.can_open_tabs_automatically':
57            Attr(QWebSettings.JavascriptCanOpenWindows),
58        'content.javascript.can_close_tabs':
59            Attr(QWebSettings.JavascriptCanCloseWindows),
60        'content.javascript.can_access_clipboard':
61            Attr(QWebSettings.JavascriptCanAccessClipboard),
62        'content.plugins':
63            Attr(QWebSettings.PluginsEnabled),
64        'content.webgl':
65            Attr(QWebSettings.WebGLEnabled),
66        'content.hyperlink_auditing':
67            Attr(QWebSettings.HyperlinkAuditingEnabled),
68        'content.local_content_can_access_remote_urls':
69            Attr(QWebSettings.LocalContentCanAccessRemoteUrls),
70        'content.local_content_can_access_file_urls':
71            Attr(QWebSettings.LocalContentCanAccessFileUrls),
72        'content.dns_prefetch':
73            Attr(QWebSettings.DnsPrefetchEnabled),
74        'content.frame_flattening':
75            Attr(QWebSettings.FrameFlatteningEnabled),
76        'content.cache.appcache':
77            Attr(QWebSettings.OfflineWebApplicationCacheEnabled),
78        'content.local_storage':
79            Attr(QWebSettings.LocalStorageEnabled,
80                 QWebSettings.OfflineStorageDatabaseEnabled),
81        'content.print_element_backgrounds':
82            Attr(QWebSettings.PrintElementBackgrounds),
83        'content.xss_auditing':
84            Attr(QWebSettings.XSSAuditingEnabled),
85        'content.site_specific_quirks.enabled':
86            Attr(QWebSettings.SiteSpecificQuirksEnabled),
87
88        'input.spatial_navigation':
89            Attr(QWebSettings.SpatialNavigationEnabled),
90        'input.links_included_in_focus_chain':
91            Attr(QWebSettings.LinksIncludedInFocusChain),
92
93        'zoom.text_only':
94            Attr(QWebSettings.ZoomTextOnly),
95        'scrolling.smooth':
96            Attr(QWebSettings.ScrollAnimatorEnabled),
97    }
98
99    _FONT_SIZES = {
100        'fonts.web.size.minimum':
101            QWebSettings.MinimumFontSize,
102        'fonts.web.size.minimum_logical':
103            QWebSettings.MinimumLogicalFontSize,
104        'fonts.web.size.default':
105            QWebSettings.DefaultFontSize,
106        'fonts.web.size.default_fixed':
107            QWebSettings.DefaultFixedFontSize,
108    }
109
110    _FONT_FAMILIES = {
111        'fonts.web.family.standard': QWebSettings.StandardFont,
112        'fonts.web.family.fixed': QWebSettings.FixedFont,
113        'fonts.web.family.serif': QWebSettings.SerifFont,
114        'fonts.web.family.sans_serif': QWebSettings.SansSerifFont,
115        'fonts.web.family.cursive': QWebSettings.CursiveFont,
116        'fonts.web.family.fantasy': QWebSettings.FantasyFont,
117    }
118
119    # Mapping from QWebSettings::QWebSettings() in
120    # qtwebkit/Source/WebKit/qt/Api/qwebsettings.cpp
121    _FONT_TO_QFONT = {
122        QWebSettings.StandardFont: QFont.Serif,
123        QWebSettings.FixedFont: QFont.Monospace,
124        QWebSettings.SerifFont: QFont.Serif,
125        QWebSettings.SansSerifFont: QFont.SansSerif,
126        QWebSettings.CursiveFont: QFont.Cursive,
127        QWebSettings.FantasyFont: QFont.Fantasy,
128    }
129
130
131def _set_user_stylesheet(settings):
132    """Set the generated user-stylesheet."""
133    stylesheet = shared.get_user_stylesheet().encode('utf-8')
134    url = urlutils.data_url('text/css;charset=utf-8', stylesheet)
135    settings.setUserStyleSheetUrl(url)
136
137
138def _set_cookie_accept_policy(settings):
139    """Update the content.cookies.accept setting."""
140    mapping = {
141        'all': QWebSettings.AlwaysAllowThirdPartyCookies,
142        'no-3rdparty': QWebSettings.AlwaysBlockThirdPartyCookies,
143        'never': QWebSettings.AlwaysBlockThirdPartyCookies,
144        'no-unknown-3rdparty': QWebSettings.AllowThirdPartyWithExistingCookies,
145    }
146    value = config.val.content.cookies.accept
147    settings.setThirdPartyCookiePolicy(mapping[value])
148
149
150def _set_cache_maximum_pages(settings):
151    """Update the content.cache.maximum_pages setting."""
152    value = config.val.content.cache.maximum_pages
153    settings.setMaximumPagesInCache(value)
154
155
156def _update_settings(option):
157    """Update global settings when qwebsettings changed."""
158    global_settings.update_setting(option)
159
160    settings = QWebSettings.globalSettings()
161    if option in ['scrollbar.hide', 'content.user_stylesheets']:
162        _set_user_stylesheet(settings)
163    elif option == 'content.cookies.accept':
164        _set_cookie_accept_policy(settings)
165    elif option == 'content.cache.maximum_pages':
166        _set_cache_maximum_pages(settings)
167
168
169def _init_user_agent():
170    global parsed_user_agent
171    ua = QWebPage().userAgentForUrl(QUrl())
172    parsed_user_agent = websettings.UserAgent.parse(ua)
173
174
175def init():
176    """Initialize the global QWebSettings."""
177    cache_path = standarddir.cache()
178    data_path = standarddir.data()
179
180    QWebSettings.setIconDatabasePath(standarddir.cache())
181    QWebSettings.setOfflineWebApplicationCachePath(
182        os.path.join(cache_path, 'application-cache'))
183    QWebSettings.globalSettings().setLocalStoragePath(
184        os.path.join(data_path, 'local-storage'))
185    QWebSettings.setOfflineStoragePath(
186        os.path.join(data_path, 'offline-storage'))
187
188    settings = QWebSettings.globalSettings()
189    _set_user_stylesheet(settings)
190    _set_cookie_accept_policy(settings)
191    _set_cache_maximum_pages(settings)
192
193    _init_user_agent()
194
195    config.instance.changed.connect(_update_settings)
196
197    global global_settings
198    global_settings = WebKitSettings(QWebSettings.globalSettings())
199    global_settings.init_settings()
200
201
202def shutdown():
203    """Disable storage so removing tmpdir will work."""
204    QWebSettings.setIconDatabasePath('')
205    QWebSettings.setOfflineWebApplicationCachePath('')
206    QWebSettings.globalSettings().setLocalStoragePath('')
207