diff --git a/misc/userscripts/password_fill b/misc/userscripts/password_fill
index 5d709512c..327a55690 100755
--- a/misc/userscripts/password_fill
+++ b/misc/userscripts/password_fill
@@ -9,7 +9,7 @@ directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode.
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
WARNING: the passwords are stored in qutebrowser's
- debug log reachable via the url qute:log
+ debug log reachable via the url qute://log
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
Usage: run as a userscript form qutebrowser, e.g.:
diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py
index 920673d4b..56149a8cc 100644
--- a/qutebrowser/browser/qtnetworkdownloads.py
+++ b/qutebrowser/browser/qtnetworkdownloads.py
@@ -273,7 +273,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
if self.fileobj is None or self._reply is None:
# No filename has been set yet (so we don't empty the buffer) or we
# got a readyRead after the reply was finished (which happens on
- # qute:log for example).
+ # qute://log for example).
return
if not self._reply.isOpen():
raise OSError("Reply is closed!")
diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py
index f7f074c54..5f25c24d1 100644
--- a/qutebrowser/browser/qutescheme.py
+++ b/qutebrowser/browser/qutescheme.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""Backend-independent qute:* code.
+"""Backend-independent qute://* code.
Module attributes:
pyeval_output: The output of the last :pyeval command.
@@ -31,7 +31,7 @@ import time
import urllib.parse
import datetime
-from PyQt5.QtCore import QUrlQuery
+from PyQt5.QtCore import QUrlQuery, QUrl
import qutebrowser
from qutebrowser.config import config
@@ -78,12 +78,25 @@ class QuteSchemeError(Exception):
super().__init__(errorstring)
-class add_handler: # pylint: disable=invalid-name
+class Redirect(Exception):
- """Decorator to register a qute:* URL handler.
+ """Exception to signal a redirect should happen.
Attributes:
- _name: The 'foo' part of qute:foo
+ url: The URL to redirect to, as a QUrl.
+ """
+
+ def __init__(self, url):
+ super().__init__(url.toDisplayString())
+ self.url = url
+
+
+class add_handler: # pylint: disable=invalid-name
+
+ """Decorator to register a qute://* URL handler.
+
+ Attributes:
+ _name: The 'foo' part of qute://foo
backend: Limit which backends the handler can run with.
"""
@@ -106,7 +119,7 @@ class add_handler: # pylint: disable=invalid-name
def wrong_backend_handler(self, url):
"""Show an error page about using the invalid backend."""
html = jinja.render('error.html',
- title="Error while opening qute:url",
+ title="Error while opening qute://url",
url=url.toDisplayString(),
error='{} is not available with this '
'backend'.format(url.toDisplayString()),
@@ -128,13 +141,17 @@ def data_for_url(url):
# A url like "qute:foo" is split as "scheme:path", not "scheme:host".
log.misc.debug("url: {}, path: {}, host {}".format(
url.toDisplayString(), path, host))
+ if path and not host:
+ new_url = QUrl()
+ new_url.setScheme('qute')
+ new_url.setHost(path)
+ raise Redirect(new_url)
+
try:
- handler = _HANDLERS[path]
+ handler = _HANDLERS[host]
except KeyError:
- try:
- handler = _HANDLERS[host]
- except KeyError:
- raise NoHandlerFound(url)
+ raise NoHandlerFound(url)
+
try:
mimetype, data = handler(url)
except OSError as e:
@@ -153,7 +170,7 @@ def data_for_url(url):
@add_handler('bookmarks')
def qute_bookmarks(_url):
- """Handler for qute:bookmarks. Display all quickmarks / bookmarks."""
+ """Handler for qute://bookmarks. Display all quickmarks / bookmarks."""
bookmarks = sorted(objreg.get('bookmark-manager').marks.items(),
key=lambda x: x[1]) # Sort by title
quickmarks = sorted(objreg.get('quickmark-manager').marks.items(),
@@ -246,7 +263,7 @@ def history_data(start_time): # noqa
@add_handler('history')
def qute_history(url):
- """Handler for qute:history. Display and serve history."""
+ """Handler for qute://history. Display and serve history."""
if url.path() == '/data':
# Use start_time in query or current time.
try:
@@ -309,7 +326,7 @@ def qute_history(url):
@add_handler('javascript')
def qute_javascript(url):
- """Handler for qute:javascript.
+ """Handler for qute://javascript.
Return content of file given as query parameter.
"""
@@ -323,7 +340,7 @@ def qute_javascript(url):
@add_handler('pyeval')
def qute_pyeval(_url):
- """Handler for qute:pyeval."""
+ """Handler for qute://pyeval."""
html = jinja.render('pre.html', title='pyeval', content=pyeval_output)
return 'text/html', html
@@ -331,7 +348,7 @@ def qute_pyeval(_url):
@add_handler('version')
@add_handler('verizon')
def qute_version(_url):
- """Handler for qute:version."""
+ """Handler for qute://version."""
html = jinja.render('version.html', title='Version info',
version=version.version(),
copyright=qutebrowser.__copyright__)
@@ -340,7 +357,7 @@ def qute_version(_url):
@add_handler('plainlog')
def qute_plainlog(url):
- """Handler for qute:plainlog.
+ """Handler for qute://plainlog.
An optional query parameter specifies the minimum log level to print.
For example, qute://log?level=warning prints warnings and errors.
@@ -360,7 +377,7 @@ def qute_plainlog(url):
@add_handler('log')
def qute_log(url):
- """Handler for qute:log.
+ """Handler for qute://log.
An optional query parameter specifies the minimum log level to print.
For example, qute://log?level=warning prints warnings and errors.
@@ -381,13 +398,13 @@ def qute_log(url):
@add_handler('gpl')
def qute_gpl(_url):
- """Handler for qute:gpl. Return HTML content as string."""
+ """Handler for qute://gpl. Return HTML content as string."""
return 'text/html', utils.read_file('html/COPYING.html')
@add_handler('help')
def qute_help(url):
- """Handler for qute:help."""
+ """Handler for qute://help."""
try:
utils.read_file('html/doc/index.html')
except OSError:
diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py
index 6bc31f9be..cebf31356 100644
--- a/qutebrowser/browser/webengine/webenginequtescheme.py
+++ b/qutebrowser/browser/webengine/webenginequtescheme.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""QtWebEngine specific qute:* handlers and glue code."""
+"""QtWebEngine specific qute://* handlers and glue code."""
from PyQt5.QtCore import QBuffer, QIODevice
# pylint: disable=no-name-in-module,import-error,useless-suppression
@@ -26,15 +26,15 @@ from PyQt5.QtWebEngineCore import (QWebEngineUrlSchemeHandler,
# pylint: enable=no-name-in-module,import-error,useless-suppression
from qutebrowser.browser import qutescheme
-from qutebrowser.utils import log
+from qutebrowser.utils import log, qtutils
class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
- """Handle qute:* requests on QtWebEngine."""
+ """Handle qute://* requests on QtWebEngine."""
def install(self, profile):
- """Install the handler for qute: URLs on the given profile."""
+ """Install the handler for qute:// URLs on the given profile."""
profile.installUrlSchemeHandler(b'qute', self)
def requestStarted(self, job):
@@ -58,12 +58,15 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
job.fail(QWebEngineUrlRequestJob.UrlNotFound)
except qutescheme.QuteSchemeOSError:
# FIXME:qtwebengine how do we show a better error here?
- log.misc.exception("OSError while handling qute:* URL")
+ log.misc.exception("OSError while handling qute://* URL")
job.fail(QWebEngineUrlRequestJob.UrlNotFound)
except qutescheme.QuteSchemeError:
# FIXME:qtwebengine how do we show a better error here?
- log.misc.exception("Error while handling qute:* URL")
+ log.misc.exception("Error while handling qute://* URL")
job.fail(QWebEngineUrlRequestJob.RequestFailed)
+ except qutescheme.Redirect as e:
+ qtutils.ensure_valid(e.url)
+ job.redirect(e.url)
else:
log.misc.debug("Returning {} data".format(mimetype))
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 76a526670..7eef0b03a 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -55,7 +55,7 @@ def init():
app = QApplication.instance()
profile = QWebEngineProfile.defaultProfile()
- log.init.debug("Initializing qute:* handler...")
+ log.init.debug("Initializing qute://* handler...")
_qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app)
_qute_scheme_handler.install(profile)
diff --git a/qutebrowser/browser/webkit/network/networkreply.py b/qutebrowser/browser/webkit/network/networkreply.py
index 2cc5727be..9638daf4a 100644
--- a/qutebrowser/browser/webkit/network/networkreply.py
+++ b/qutebrowser/browser/webkit/network/networkreply.py
@@ -19,11 +19,15 @@
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
+#
+# For some reason, a segfault will be triggered if the unnecessary lambdas in
+# this file aren't there.
+# pylint: disable=unnecessary-lambda
"""Special network replies.."""
-from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
-from PyQt5.QtCore import pyqtSlot, QIODevice, QByteArray, QTimer
+from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
+from PyQt5.QtCore import pyqtSlot, QIODevice, QByteArray, QTimer, QUrl
class FixedDataNetworkReply(QNetworkReply):
@@ -114,9 +118,6 @@ class ErrorNetworkReply(QNetworkReply):
# the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring)
- # For some reason, a segfault will be triggered if these lambdas aren't
- # there.
- # pylint: disable=unnecessary-lambda
QTimer.singleShot(0, lambda: self.error.emit(error))
QTimer.singleShot(0, lambda: self.finished.emit())
@@ -137,3 +138,16 @@ class ErrorNetworkReply(QNetworkReply):
def isRunning(self):
return False
+
+
+class RedirectNetworkReply(QNetworkReply):
+
+ """A reply which redirects to the given URL."""
+
+ def __init__(self, new_url, parent=None):
+ super().__init__(parent)
+ self.setAttribute(QNetworkRequest.RedirectionTargetAttribute, new_url)
+ QTimer.singleShot(0, lambda: self.finished.emit())
+
+ def readData(self, _maxlen):
+ return bytes()
diff --git a/qutebrowser/browser/webkit/network/webkitqutescheme.py b/qutebrowser/browser/webkit/network/webkitqutescheme.py
index 61ef760bc..34db29ee9 100644
--- a/qutebrowser/browser/webkit/network/webkitqutescheme.py
+++ b/qutebrowser/browser/webkit/network/webkitqutescheme.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see .
-"""QtWebKit specific qute:* handlers and glue code."""
+"""QtWebKit specific qute://* handlers and glue code."""
import mimetypes
import functools
@@ -28,13 +28,13 @@ from PyQt5.QtNetwork import QNetworkReply
from qutebrowser.browser import pdfjs, qutescheme
from qutebrowser.browser.webkit.network import schemehandler, networkreply
-from qutebrowser.utils import jinja, log, message, objreg, usertypes
+from qutebrowser.utils import jinja, log, message, objreg, usertypes, qtutils
from qutebrowser.config import configexc, configdata
class QuteSchemeHandler(schemehandler.SchemeHandler):
- """Scheme handler for qute: URLs."""
+ """Scheme handler for qute:// URLs."""
def createRequest(self, _op, request, _outgoing_data):
"""Create a new request.
@@ -62,6 +62,9 @@ class QuteSchemeHandler(schemehandler.SchemeHandler):
except qutescheme.QuteSchemeError as e:
return networkreply.ErrorNetworkReply(request, e.errorstring,
e.error, self.parent())
+ except qutescheme.Redirect as e:
+ qtutils.ensure_valid(e.url)
+ return networkreply.RedirectNetworkReply(e.url, self.parent())
return networkreply.FixedDataNetworkReply(request, data, mimetype,
self.parent())
@@ -69,15 +72,15 @@ class QuteSchemeHandler(schemehandler.SchemeHandler):
class JSBridge(QObject):
- """Javascript-bridge for special qute:... pages."""
+ """Javascript-bridge for special qute://... pages."""
@pyqtSlot(str, str, str)
def set(self, sectname, optname, value):
- """Slot to set a setting from qute:settings."""
+ """Slot to set a setting from qute://settings."""
# https://github.com/qutebrowser/qutebrowser/issues/727
if ((sectname, optname) == ('content', 'allow-javascript') and
value == 'false'):
- message.error("Refusing to disable javascript via qute:settings "
+ message.error("Refusing to disable javascript via qute://settings "
"as it needs javascript support.")
return
try:
@@ -88,7 +91,7 @@ class JSBridge(QObject):
@qutescheme.add_handler('settings', backend=usertypes.Backend.QtWebKit)
def qute_settings(_url):
- """Handler for qute:settings. View/change qute configuration."""
+ """Handler for qute://settings. View/change qute configuration."""
config_getter = functools.partial(objreg.get('config').get, raw=True)
html = jinja.render('settings.html', title='settings', config=configdata,
confget=config_getter)
diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py
index 12670be4f..0c3aa179e 100644
--- a/qutebrowser/browser/webkit/webview.py
+++ b/qutebrowser/browser/webkit/webview.py
@@ -140,7 +140,7 @@ class WebView(QWebView):
@pyqtSlot()
def add_js_bridge(self):
- """Add the javascript bridge for qute:... pages."""
+ """Add the javascript bridge for qute://... pages."""
frame = self.sender()
if not isinstance(frame, QWebFrame):
log.webview.error("Got non-QWebFrame {!r} in "
diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py
index 1e0cec92b..82c6aca00 100644
--- a/qutebrowser/config/config.py
+++ b/qutebrowser/config/config.py
@@ -805,7 +805,7 @@ class ConfigManager(QObject):
if section_ is None and option is None:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
- tabbed_browser.openurl(QUrl('qute:settings'), newtab=False)
+ tabbed_browser.openurl(QUrl('qute://settings'), newtab=False)
return
if option.endswith('?') and option != '?':
diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py
index 1fbc22937..2c5b27808 100644
--- a/qutebrowser/config/configdata.py
+++ b/qutebrowser/config/configdata.py
@@ -1689,7 +1689,7 @@ KEY_DATA = collections.OrderedDict([
('home', ['']),
('stop', ['']),
('print', ['']),
- ('open qute:settings', ['Ss']),
+ ('open qute://settings', ['Ss']),
('follow-selected', RETURN_KEYS),
('follow-selected -t', ['', '']),
('repeat-command', ['.']),
diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py
index fc777c2e9..de43c71f4 100644
--- a/qutebrowser/misc/utilcmds.py
+++ b/qutebrowser/misc/utilcmds.py
@@ -228,7 +228,7 @@ def debug_pyeval(s, quiet=False):
else:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused')
- tabbed_browser.openurl(QUrl('qute:pyeval'), newtab=True)
+ tabbed_browser.openurl(QUrl('qute://pyeval'), newtab=True)
@cmdutils.register(debug=True)
diff --git a/scripts/dev/run_vulture.py b/scripts/dev/run_vulture.py
index 55f4d9c86..f1eed1803 100755
--- a/scripts/dev/run_vulture.py
+++ b/scripts/dev/run_vulture.py
@@ -74,7 +74,7 @@ def whitelist_generator():
yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().fileNames'
yield 'PyQt5.QtWidgets.QStyleOptionViewItem.backgroundColor'
- ## qute:... handlers
+ ## qute://... handlers
for name in qutescheme._HANDLERS: # pylint: disable=protected-access
yield 'qutebrowser.browser.qutescheme.qute_' + name
diff --git a/tests/end2end/features/history.feature b/tests/end2end/features/history.feature
index 27454c522..613b660af 100644
--- a/tests/end2end/features/history.feature
+++ b/tests/end2end/features/history.feature
@@ -83,6 +83,14 @@ Feature: Page history
Then the page should contain the plaintext "3.txt"
Then the page should contain the plaintext "4.txt"
+ Scenario: Listing history with qute:history redirect
+ When I open data/numbers/3.txt
+ And I open data/numbers/4.txt
+ And I open qute:history without waiting
+ And I wait until qute://history is loaded
+ Then the page should contain the plaintext "3.txt"
+ Then the page should contain the plaintext "4.txt"
+
## Bugs
@qtwebengine_skip @qtwebkit_ng_skip
diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature
index 0aeb65f92..e3bb13b6f 100644
--- a/tests/end2end/features/misc.feature
+++ b/tests/end2end/features/misc.feature
@@ -405,12 +405,12 @@ Feature: Various utility commands.
# :pyeval
Scenario: Running :pyeval
When I run :debug-pyeval 1+1
- And I wait until qute:pyeval is loaded
+ And I wait until qute://pyeval is loaded
Then the page should contain the plaintext "2"
Scenario: Causing exception in :pyeval
When I run :debug-pyeval 1/0
- And I wait until qute:pyeval is loaded
+ And I wait until qute://pyeval is loaded
Then the page should contain the plaintext "ZeroDivisionError"
Scenario: Running :pyeval with --quiet
@@ -512,12 +512,12 @@ Feature: Various utility commands.
When I run :messages cataclysmic
Then the error "Invalid log level cataclysmic!" should be shown
- Scenario: Using qute:log directly
- When I open qute:log
+ Scenario: Using qute://log directly
+ When I open qute://log
Then no crash should happen
- Scenario: Using qute:plainlog directly
- When I open qute:plainlog
+ Scenario: Using qute://plainlog directly
+ When I open qute://plainlog
Then no crash should happen
Scenario: Using :messages without messages
diff --git a/tests/end2end/features/set.feature b/tests/end2end/features/set.feature
index b2b165542..bc144eb6e 100644
--- a/tests/end2end/features/set.feature
+++ b/tests/end2end/features/set.feature
@@ -78,15 +78,15 @@ Feature: Setting settings.
When I run :set -t colors statusbar.bg green
Then colors -> statusbar.bg should be green
- # qute:settings isn't actually implemented on QtWebEngine, but this works
+ # qute://settings isn't actually implemented on QtWebEngine, but this works
# (and displays a page saying it's not available)
- Scenario: Opening qute:settings
+ Scenario: Opening qute://settings
When I run :set
- And I wait until qute:settings is loaded
+ And I wait until qute://settings is loaded
Then the following tabs should be open:
- - qute:settings (active)
+ - qute://settings (active)
- @qtwebengine_todo: qute:settings is not implemented yet
+ @qtwebengine_todo: qute://settings is not implemented yet
Scenario: Focusing input fields in qute://settings and entering valid value
When I set general -> ignore-case to false
And I open qute://settings
@@ -101,7 +101,7 @@ Feature: Setting settings.
And I press the key ""
Then general -> ignore-case should be true
- @qtwebengine_todo: qute:settings is not implemented yet
+ @qtwebengine_todo: qute://settings is not implemented yet
Scenario: Focusing input fields in qute://settings and entering invalid value
When I open qute://settings
# scroll to the right - the table does not fit in the default screen
diff --git a/tests/end2end/features/urlmarks.feature b/tests/end2end/features/urlmarks.feature
index 873d83563..f233215b8 100644
--- a/tests/end2end/features/urlmarks.feature
+++ b/tests/end2end/features/urlmarks.feature
@@ -225,12 +225,12 @@ Feature: quickmarks and bookmarks
Scenario: Listing quickmarks
When I run :quickmark-add http://localhost:(port)/data/numbers/20.txt twenty
And I run :quickmark-add http://localhost:(port)/data/numbers/21.txt twentyone
- And I open qute:bookmarks
+ And I open qute://bookmarks
Then the page should contain the plaintext "twenty"
And the page should contain the plaintext "twentyone"
Scenario: Listing bookmarks
When I open data/title.html in a new tab
And I run :bookmark-add
- And I open qute:bookmarks
+ And I open qute://bookmarks
Then the page should contain the plaintext "Test title"
diff --git a/tests/end2end/features/utilcmds.feature b/tests/end2end/features/utilcmds.feature
index 5696de75d..7fd5952a8 100644
--- a/tests/end2end/features/utilcmds.feature
+++ b/tests/end2end/features/utilcmds.feature
@@ -145,7 +145,7 @@ Feature: Miscellaneous utility commands exposed to the user.
And I run :message-info oldstuff
And I run :repeat 20 message-info otherstuff
And I run :message-info newstuff
- And I open qute:log
+ And I open qute://log
Then the page should contain the plaintext "newstuff"
And the page should not contain the plaintext "oldstuff"
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py
index d6697ec29..ed0222014 100644
--- a/tests/end2end/test_invocations.py
+++ b/tests/end2end/test_invocations.py
@@ -138,10 +138,10 @@ def test_misconfigured_user_dirs(request, httpbin, temp_basedir_env,
def test_no_loglines(request, quteproc_new):
- """Test qute:log with --loglines=0."""
+ """Test qute://log with --loglines=0."""
quteproc_new.start(args=['--temp-basedir', '--loglines=0'] +
_base_args(request.config))
- quteproc_new.open_path('qute:log')
+ quteproc_new.open_path('qute://log')
assert quteproc_new.get_content() == 'Log output was disabled.'
diff --git a/tests/unit/browser/webkit/network/test_networkreply.py b/tests/unit/browser/webkit/network/test_networkreply.py
index 7a9c89393..7505dbc8c 100644
--- a/tests/unit/browser/webkit/network/test_networkreply.py
+++ b/tests/unit/browser/webkit/network/test_networkreply.py
@@ -91,3 +91,10 @@ def test_error_network_reply(qtbot, req):
assert reply.readData(1) == b''
assert reply.error() == QNetworkReply.UnknownNetworkError
assert reply.errorString() == "This is an error"
+
+
+def test_redirect_network_reply():
+ url = QUrl('https://www.example.com/')
+ reply = networkreply.RedirectNetworkReply(url)
+ assert reply.readData(1) == b''
+ assert reply.attribute(QNetworkRequest.RedirectionTargetAttribute) == url
diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py
index f944f95b2..4729bd661 100644
--- a/tests/unit/utils/test_urlutils.py
+++ b/tests/unit/utils/test_urlutils.py
@@ -265,6 +265,7 @@ class TestFuzzyUrl:
('file:///tmp/foo', True),
('about:blank', True),
('qute:version', True),
+ ('qute://version', True),
('http://www.qutebrowser.org/', False),
('www.qutebrowser.org', False),
])
@@ -317,9 +318,11 @@ def test_get_search_url_invalid(urlutils_config_stub, url):
(True, True, False, 'file:///tmp/foo'),
(True, True, False, 'about:blank'),
(True, True, False, 'qute:version'),
+ (True, True, False, 'qute://version'),
(True, True, False, 'localhost'),
# _has_explicit_scheme False, special_url True
(True, True, False, 'qute::foo'),
+ (True, True, False, 'qute:://foo'),
# Invalid URLs
(False, False, False, ''),
(False, True, False, 'onlyscheme:'),