diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc
index 3abf5fb34..18513ea84 100644
--- a/doc/help/settings.asciidoc
+++ b/doc/help/settings.asciidoc
@@ -711,16 +711,12 @@ Valid values:
Default: +pass:[true]+
-This setting is only available with the QtWebKit backend.
-
[[network-accept-language]]
=== accept-language
Value to send in the `accept-language` header.
Default: +pass:[en-US,en]+
-This setting is only available with the QtWebKit backend.
-
[[network-referer-header]]
=== referer-header
Send the Referer header
@@ -804,8 +800,6 @@ Set custom headers for qutebrowser HTTP requests.
Default: empty
-This setting is only available with the QtWebKit backend.
-
== completion
Options related to completion and command history.
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index e00bc584b..d2d9489ed 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -18,3 +18,24 @@
# along with qutebrowser. If not, see .
"""Various utilities shared between webpage/webview subclasses."""
+
+from qutebrowser.config import config
+
+
+def custom_headers():
+ """Get the combined custom headers."""
+ headers = {}
+ dnt = b'1' if config.get('network', 'do-not-track') else b'0'
+ headers[b'DNT'] = dnt
+ headers[b'X-Do-Not-Track'] = dnt
+
+ custom_headers = config.get('network', 'custom-headers')
+ if custom_headers is not None:
+ for header, value in custom_headers.items():
+ headers[header.encode('ascii')] = value.encode('ascii')
+
+ accept_language = config.get('network', 'accept-language')
+ if accept_language is not None:
+ headers[b'Accept-Language'] = accept_language.encode('ascii')
+
+ return sorted(headers.items())
diff --git a/qutebrowser/browser/webengine/interceptor.py b/qutebrowser/browser/webengine/interceptor.py
index 5a1c58fc9..82ee73f77 100644
--- a/qutebrowser/browser/webengine/interceptor.py
+++ b/qutebrowser/browser/webengine/interceptor.py
@@ -22,6 +22,7 @@
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
+from qutebrowser.browser import shared
from qutebrowser.utils import utils, log
@@ -59,3 +60,6 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
log.webview.info("Request to {} blocked by host blocker.".format(
info.requestUrl().host()))
info.block(True)
+
+ for header, value in shared.custom_headers():
+ info.setHttpHeader(header, value)
diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py
index e2938d904..afeddcfd5 100644
--- a/qutebrowser/browser/webkit/network/networkmanager.py
+++ b/qutebrowser/browser/webkit/network/networkmanager.py
@@ -31,6 +31,7 @@ from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkReply, QSslError,
from qutebrowser.config import config
from qutebrowser.utils import (message, log, usertypes, utils, objreg, qtutils,
urlutils, debug)
+from qutebrowser.browser import shared
from qutebrowser.browser.webkit.network import qutescheme, networkreply
from qutebrowser.browser.webkit.network import filescheme
@@ -449,6 +450,9 @@ class NetworkManager(QNetworkAccessManager):
if result is not None:
return result
+ for header, value in shared.custom_headers():
+ req.setRawHeader(header, value)
+
host_blocker = objreg.get('host-blocker')
if (op == QNetworkAccessManager.GetOperation and
host_blocker.is_blocked(req.url())):
@@ -458,20 +462,6 @@ class NetworkManager(QNetworkAccessManager):
req, HOSTBLOCK_ERROR_STRING, QNetworkReply.ContentAccessDenied,
self)
- if config.get('network', 'do-not-track'):
- dnt = '1'.encode('ascii')
- else:
- dnt = '0'.encode('ascii')
- req.setRawHeader('DNT'.encode('ascii'), dnt)
- req.setRawHeader('X-Do-Not-Track'.encode('ascii'), dnt)
-
- # Load custom headers
- custom_headers = config.get('network', 'custom-headers')
-
- if custom_headers is not None:
- for header, value in custom_headers.items():
- req.setRawHeader(header.encode('ascii'), value.encode('ascii'))
-
# There are some scenarios where we can't figure out current_url:
# - There's a generic NetworkManager, e.g. for downloads
# - The download was in a tab which is now closed.
@@ -490,10 +480,6 @@ class NetworkManager(QNetworkAccessManager):
self.set_referer(req, current_url)
- accept_language = config.get('network', 'accept-language')
- if accept_language is not None:
- req.setRawHeader('Accept-Language'.encode('ascii'),
- accept_language.encode('ascii'))
if PYQT_VERSION < 0x050301:
# WORKAROUND (remove this when we bump the requirements to 5.3.1)
#
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
index 2f6b699e1..3979edf19 100644
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -388,13 +388,11 @@ def data(readonly=False):
('network', sect.KeyValue(
('do-not-track',
- SettingValue(typ.Bool(), 'true',
- backends=[usertypes.Backend.QtWebKit]),
+ SettingValue(typ.Bool(), 'true'),
"Value to send in the `DNT` header."),
('accept-language',
- SettingValue(typ.String(none_ok=True), 'en-US,en',
- backends=[usertypes.Backend.QtWebKit]),
+ SettingValue(typ.String(none_ok=True), 'en-US,en'),
"Value to send in the `accept-language` header."),
('referer-header',
@@ -437,8 +435,7 @@ def data(readonly=False):
"Whether to try to pre-fetch DNS entries to speed up browsing."),
('custom-headers',
- SettingValue(typ.HeaderDict(none_ok=True), '',
- backends=[usertypes.Backend.QtWebKit]),
+ SettingValue(typ.HeaderDict(none_ok=True), ''),
"Set custom headers for qutebrowser HTTP requests."),
readonly=readonly
diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py
index 0e973b6bd..8529788f2 100644
--- a/scripts/dev/check_coverage.py
+++ b/scripts/dev/check_coverage.py
@@ -74,6 +74,8 @@ PERFECT_FILES = [
('tests/unit/browser/test_signalfilter.py',
'qutebrowser/browser/signalfilter.py'),
+ ('tests/unit/browser/test_shared.py',
+ 'qutebrowser/browser/shared.py'),
# ('tests/unit/browser/test_tab.py',
# 'qutebrowser/browser/tab.py'),
diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature
index 0b24982e8..5551b9006 100644
--- a/tests/end2end/features/misc.feature
+++ b/tests/end2end/features/misc.feature
@@ -394,12 +394,28 @@ Feature: Various utility commands.
## Custom headers
- @qtwebengine_todo: Custom headers are not implemented yet
Scenario: Setting a custom header
When I set network -> custom-headers to {"X-Qute-Test": "testvalue"}
And I open headers
Then the header X-Qute-Test should be set to testvalue
+ Scenario: DNT header
+ When I set network -> do-not-track to true
+ And I open headers
+ Then the header Dnt should be set to 1
+ And the header X-Do-Not-Track should be set to 1
+
+ Scenario: DNT header (off)
+ When I set network -> do-not-track to false
+ And I open headers
+ Then the header Dnt should be set to 0
+ And the header X-Do-Not-Track should be set to 0
+
+ Scenario: Accept-Language header
+ When I set network -> accept-language to en,de
+ And I open headers
+ Then the header Accept-Language should be set to en,de
+
## :messages
@qtwebengine_todo: qute:log is not implemented yet
diff --git a/tests/unit/browser/test_shared.py b/tests/unit/browser/test_shared.py
new file mode 100644
index 000000000..da81c2059
--- /dev/null
+++ b/tests/unit/browser/test_shared.py
@@ -0,0 +1,51 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2016 Florian Bruhin (The Compiler)
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see .
+
+import pytest
+
+from qutebrowser.browser import shared
+
+
+@pytest.mark.parametrize('dnt, accept_language, custom_headers, expected', [
+ # DNT
+ (True, None, {}, {b'DNT': b'1', b'X-Do-Not-Track': b'1'}),
+ (False, None, {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0'}),
+ # Accept-Language
+ (False, 'de, en', {}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
+ b'Accept-Language': b'de, en'}),
+ # Custom headers
+ (False, None, {'X-Qute': 'yes'}, {b'DNT': b'0', b'X-Do-Not-Track': b'0',
+ b'X-Qute': b'yes'}),
+ # Mixed
+ (False, 'de, en', {'X-Qute': 'yes'}, {b'DNT': b'0',
+ b'X-Do-Not-Track': b'0',
+ b'Accept-Language': b'de, en',
+ b'X-Qute': b'yes'}),
+])
+def test_custom_headers(config_stub, dnt, accept_language, custom_headers,
+ expected):
+ config_stub.data = {
+ 'network': {
+ 'do-not-track': dnt,
+ 'accept-language': accept_language,
+ 'custom-headers': custom_headers,
+ }
+ }
+ expected_items = sorted(expected.items())
+ assert shared.custom_headers() == expected_items