Redirect qute:foo to qute://foo

Before, we just returned the same data for both, but then we'll run into
same-origin restrictions as qute:history and qute:history/data are not the same
host.
This commit is contained in:
Florian Bruhin 2017-04-06 20:38:15 +02:00
parent 3cc9f9f073
commit 4ec5700cbf
20 changed files with 118 additions and 63 deletions

View File

@ -9,7 +9,7 @@ directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode.
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
WARNING: the passwords are stored in qutebrowser's 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 $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
Usage: run as a userscript form qutebrowser, e.g.: Usage: run as a userscript form qutebrowser, e.g.:

View File

@ -273,7 +273,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
if self.fileobj is None or self._reply is None: 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 # 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 # got a readyRead after the reply was finished (which happens on
# qute:log for example). # qute://log for example).
return return
if not self._reply.isOpen(): if not self._reply.isOpen():
raise OSError("Reply is closed!") raise OSError("Reply is closed!")

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Backend-independent qute:* code. """Backend-independent qute://* code.
Module attributes: Module attributes:
pyeval_output: The output of the last :pyeval command. pyeval_output: The output of the last :pyeval command.
@ -31,7 +31,7 @@ import time
import urllib.parse import urllib.parse
import datetime import datetime
from PyQt5.QtCore import QUrlQuery from PyQt5.QtCore import QUrlQuery, QUrl
import qutebrowser import qutebrowser
from qutebrowser.config import config from qutebrowser.config import config
@ -78,12 +78,25 @@ class QuteSchemeError(Exception):
super().__init__(errorstring) 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: 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. 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): def wrong_backend_handler(self, url):
"""Show an error page about using the invalid backend.""" """Show an error page about using the invalid backend."""
html = jinja.render('error.html', html = jinja.render('error.html',
title="Error while opening qute:url", title="Error while opening qute://url",
url=url.toDisplayString(), url=url.toDisplayString(),
error='{} is not available with this ' error='{} is not available with this '
'backend'.format(url.toDisplayString()), '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". # A url like "qute:foo" is split as "scheme:path", not "scheme:host".
log.misc.debug("url: {}, path: {}, host {}".format( log.misc.debug("url: {}, path: {}, host {}".format(
url.toDisplayString(), path, host)) 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: try:
handler = _HANDLERS[path] handler = _HANDLERS[host]
except KeyError: except KeyError:
try: raise NoHandlerFound(url)
handler = _HANDLERS[host]
except KeyError:
raise NoHandlerFound(url)
try: try:
mimetype, data = handler(url) mimetype, data = handler(url)
except OSError as e: except OSError as e:
@ -153,7 +170,7 @@ def data_for_url(url):
@add_handler('bookmarks') @add_handler('bookmarks')
def qute_bookmarks(_url): 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(), bookmarks = sorted(objreg.get('bookmark-manager').marks.items(),
key=lambda x: x[1]) # Sort by title key=lambda x: x[1]) # Sort by title
quickmarks = sorted(objreg.get('quickmark-manager').marks.items(), quickmarks = sorted(objreg.get('quickmark-manager').marks.items(),
@ -246,7 +263,7 @@ def history_data(start_time): # noqa
@add_handler('history') @add_handler('history')
def qute_history(url): def qute_history(url):
"""Handler for qute:history. Display and serve history.""" """Handler for qute://history. Display and serve history."""
if url.path() == '/data': if url.path() == '/data':
# Use start_time in query or current time. # Use start_time in query or current time.
try: try:
@ -309,7 +326,7 @@ def qute_history(url):
@add_handler('javascript') @add_handler('javascript')
def qute_javascript(url): def qute_javascript(url):
"""Handler for qute:javascript. """Handler for qute://javascript.
Return content of file given as query parameter. Return content of file given as query parameter.
""" """
@ -323,7 +340,7 @@ def qute_javascript(url):
@add_handler('pyeval') @add_handler('pyeval')
def qute_pyeval(_url): def qute_pyeval(_url):
"""Handler for qute:pyeval.""" """Handler for qute://pyeval."""
html = jinja.render('pre.html', title='pyeval', content=pyeval_output) html = jinja.render('pre.html', title='pyeval', content=pyeval_output)
return 'text/html', html return 'text/html', html
@ -331,7 +348,7 @@ def qute_pyeval(_url):
@add_handler('version') @add_handler('version')
@add_handler('verizon') @add_handler('verizon')
def qute_version(_url): def qute_version(_url):
"""Handler for qute:version.""" """Handler for qute://version."""
html = jinja.render('version.html', title='Version info', html = jinja.render('version.html', title='Version info',
version=version.version(), version=version.version(),
copyright=qutebrowser.__copyright__) copyright=qutebrowser.__copyright__)
@ -340,7 +357,7 @@ def qute_version(_url):
@add_handler('plainlog') @add_handler('plainlog')
def qute_plainlog(url): def qute_plainlog(url):
"""Handler for qute:plainlog. """Handler for qute://plainlog.
An optional query parameter specifies the minimum log level to print. An optional query parameter specifies the minimum log level to print.
For example, qute://log?level=warning prints warnings and errors. For example, qute://log?level=warning prints warnings and errors.
@ -360,7 +377,7 @@ def qute_plainlog(url):
@add_handler('log') @add_handler('log')
def qute_log(url): def qute_log(url):
"""Handler for qute:log. """Handler for qute://log.
An optional query parameter specifies the minimum log level to print. An optional query parameter specifies the minimum log level to print.
For example, qute://log?level=warning prints warnings and errors. For example, qute://log?level=warning prints warnings and errors.
@ -381,13 +398,13 @@ def qute_log(url):
@add_handler('gpl') @add_handler('gpl')
def qute_gpl(_url): 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') return 'text/html', utils.read_file('html/COPYING.html')
@add_handler('help') @add_handler('help')
def qute_help(url): def qute_help(url):
"""Handler for qute:help.""" """Handler for qute://help."""
try: try:
utils.read_file('html/doc/index.html') utils.read_file('html/doc/index.html')
except OSError: except OSError:

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""QtWebEngine specific qute:* handlers and glue code.""" """QtWebEngine specific qute://* handlers and glue code."""
from PyQt5.QtCore import QBuffer, QIODevice from PyQt5.QtCore import QBuffer, QIODevice
# pylint: disable=no-name-in-module,import-error,useless-suppression # 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 # pylint: enable=no-name-in-module,import-error,useless-suppression
from qutebrowser.browser import qutescheme from qutebrowser.browser import qutescheme
from qutebrowser.utils import log from qutebrowser.utils import log, qtutils
class QuteSchemeHandler(QWebEngineUrlSchemeHandler): class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
"""Handle qute:* requests on QtWebEngine.""" """Handle qute://* requests on QtWebEngine."""
def install(self, profile): 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) profile.installUrlSchemeHandler(b'qute', self)
def requestStarted(self, job): def requestStarted(self, job):
@ -58,12 +58,15 @@ class QuteSchemeHandler(QWebEngineUrlSchemeHandler):
job.fail(QWebEngineUrlRequestJob.UrlNotFound) job.fail(QWebEngineUrlRequestJob.UrlNotFound)
except qutescheme.QuteSchemeOSError: except qutescheme.QuteSchemeOSError:
# FIXME:qtwebengine how do we show a better error here? # 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) job.fail(QWebEngineUrlRequestJob.UrlNotFound)
except qutescheme.QuteSchemeError: except qutescheme.QuteSchemeError:
# FIXME:qtwebengine how do we show a better error here? # 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) job.fail(QWebEngineUrlRequestJob.RequestFailed)
except qutescheme.Redirect as e:
qtutils.ensure_valid(e.url)
job.redirect(e.url)
else: else:
log.misc.debug("Returning {} data".format(mimetype)) log.misc.debug("Returning {} data".format(mimetype))

View File

@ -55,7 +55,7 @@ def init():
app = QApplication.instance() app = QApplication.instance()
profile = QWebEngineProfile.defaultProfile() 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 = webenginequtescheme.QuteSchemeHandler(parent=app)
_qute_scheme_handler.install(profile) _qute_scheme_handler.install(profile)

View File

@ -19,11 +19,15 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
#
# 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..""" """Special network replies.."""
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
from PyQt5.QtCore import pyqtSlot, QIODevice, QByteArray, QTimer from PyQt5.QtCore import pyqtSlot, QIODevice, QByteArray, QTimer, QUrl
class FixedDataNetworkReply(QNetworkReply): class FixedDataNetworkReply(QNetworkReply):
@ -114,9 +118,6 @@ class ErrorNetworkReply(QNetworkReply):
# the device to avoid getting a warning. # the device to avoid getting a warning.
self.setOpenMode(QIODevice.ReadOnly) self.setOpenMode(QIODevice.ReadOnly)
self.setError(error, errorstring) 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.error.emit(error))
QTimer.singleShot(0, lambda: self.finished.emit()) QTimer.singleShot(0, lambda: self.finished.emit())
@ -137,3 +138,16 @@ class ErrorNetworkReply(QNetworkReply):
def isRunning(self): def isRunning(self):
return False 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()

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""QtWebKit specific qute:* handlers and glue code.""" """QtWebKit specific qute://* handlers and glue code."""
import mimetypes import mimetypes
import functools import functools
@ -28,13 +28,13 @@ from PyQt5.QtNetwork import QNetworkReply
from qutebrowser.browser import pdfjs, qutescheme from qutebrowser.browser import pdfjs, qutescheme
from qutebrowser.browser.webkit.network import schemehandler, networkreply 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 from qutebrowser.config import configexc, configdata
class QuteSchemeHandler(schemehandler.SchemeHandler): class QuteSchemeHandler(schemehandler.SchemeHandler):
"""Scheme handler for qute: URLs.""" """Scheme handler for qute:// URLs."""
def createRequest(self, _op, request, _outgoing_data): def createRequest(self, _op, request, _outgoing_data):
"""Create a new request. """Create a new request.
@ -62,6 +62,9 @@ class QuteSchemeHandler(schemehandler.SchemeHandler):
except qutescheme.QuteSchemeError as e: except qutescheme.QuteSchemeError as e:
return networkreply.ErrorNetworkReply(request, e.errorstring, return networkreply.ErrorNetworkReply(request, e.errorstring,
e.error, self.parent()) 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, return networkreply.FixedDataNetworkReply(request, data, mimetype,
self.parent()) self.parent())
@ -69,15 +72,15 @@ class QuteSchemeHandler(schemehandler.SchemeHandler):
class JSBridge(QObject): class JSBridge(QObject):
"""Javascript-bridge for special qute:... pages.""" """Javascript-bridge for special qute://... pages."""
@pyqtSlot(str, str, str) @pyqtSlot(str, str, str)
def set(self, sectname, optname, value): 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 # https://github.com/qutebrowser/qutebrowser/issues/727
if ((sectname, optname) == ('content', 'allow-javascript') and if ((sectname, optname) == ('content', 'allow-javascript') and
value == 'false'): 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.") "as it needs javascript support.")
return return
try: try:
@ -88,7 +91,7 @@ class JSBridge(QObject):
@qutescheme.add_handler('settings', backend=usertypes.Backend.QtWebKit) @qutescheme.add_handler('settings', backend=usertypes.Backend.QtWebKit)
def qute_settings(_url): 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) config_getter = functools.partial(objreg.get('config').get, raw=True)
html = jinja.render('settings.html', title='settings', config=configdata, html = jinja.render('settings.html', title='settings', config=configdata,
confget=config_getter) confget=config_getter)

View File

@ -140,7 +140,7 @@ class WebView(QWebView):
@pyqtSlot() @pyqtSlot()
def add_js_bridge(self): def add_js_bridge(self):
"""Add the javascript bridge for qute:... pages.""" """Add the javascript bridge for qute://... pages."""
frame = self.sender() frame = self.sender()
if not isinstance(frame, QWebFrame): if not isinstance(frame, QWebFrame):
log.webview.error("Got non-QWebFrame {!r} in " log.webview.error("Got non-QWebFrame {!r} in "

View File

@ -805,7 +805,7 @@ class ConfigManager(QObject):
if section_ is None and option is None: if section_ is None and option is None:
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id) window=win_id)
tabbed_browser.openurl(QUrl('qute:settings'), newtab=False) tabbed_browser.openurl(QUrl('qute://settings'), newtab=False)
return return
if option.endswith('?') and option != '?': if option.endswith('?') and option != '?':

View File

@ -1689,7 +1689,7 @@ KEY_DATA = collections.OrderedDict([
('home', ['<Ctrl-h>']), ('home', ['<Ctrl-h>']),
('stop', ['<Ctrl-s>']), ('stop', ['<Ctrl-s>']),
('print', ['<Ctrl-Alt-p>']), ('print', ['<Ctrl-Alt-p>']),
('open qute:settings', ['Ss']), ('open qute://settings', ['Ss']),
('follow-selected', RETURN_KEYS), ('follow-selected', RETURN_KEYS),
('follow-selected -t', ['<Ctrl-Return>', '<Ctrl-Enter>']), ('follow-selected -t', ['<Ctrl-Return>', '<Ctrl-Enter>']),
('repeat-command', ['.']), ('repeat-command', ['.']),

View File

@ -228,7 +228,7 @@ def debug_pyeval(s, quiet=False):
else: else:
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused') window='last-focused')
tabbed_browser.openurl(QUrl('qute:pyeval'), newtab=True) tabbed_browser.openurl(QUrl('qute://pyeval'), newtab=True)
@cmdutils.register(debug=True) @cmdutils.register(debug=True)

View File

@ -74,7 +74,7 @@ def whitelist_generator():
yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().fileNames' yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().fileNames'
yield 'PyQt5.QtWidgets.QStyleOptionViewItem.backgroundColor' yield 'PyQt5.QtWidgets.QStyleOptionViewItem.backgroundColor'
## qute:... handlers ## qute://... handlers
for name in qutescheme._HANDLERS: # pylint: disable=protected-access for name in qutescheme._HANDLERS: # pylint: disable=protected-access
yield 'qutebrowser.browser.qutescheme.qute_' + name yield 'qutebrowser.browser.qutescheme.qute_' + name

View File

@ -83,6 +83,14 @@ Feature: Page history
Then the page should contain the plaintext "3.txt" Then the page should contain the plaintext "3.txt"
Then the page should contain the plaintext "4.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 ## Bugs
@qtwebengine_skip @qtwebkit_ng_skip @qtwebengine_skip @qtwebkit_ng_skip

View File

@ -405,12 +405,12 @@ Feature: Various utility commands.
# :pyeval # :pyeval
Scenario: Running :pyeval Scenario: Running :pyeval
When I run :debug-pyeval 1+1 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" Then the page should contain the plaintext "2"
Scenario: Causing exception in :pyeval Scenario: Causing exception in :pyeval
When I run :debug-pyeval 1/0 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" Then the page should contain the plaintext "ZeroDivisionError"
Scenario: Running :pyeval with --quiet Scenario: Running :pyeval with --quiet
@ -512,12 +512,12 @@ Feature: Various utility commands.
When I run :messages cataclysmic When I run :messages cataclysmic
Then the error "Invalid log level cataclysmic!" should be shown Then the error "Invalid log level cataclysmic!" should be shown
Scenario: Using qute:log directly Scenario: Using qute://log directly
When I open qute:log When I open qute://log
Then no crash should happen Then no crash should happen
Scenario: Using qute:plainlog directly Scenario: Using qute://plainlog directly
When I open qute:plainlog When I open qute://plainlog
Then no crash should happen Then no crash should happen
Scenario: Using :messages without messages Scenario: Using :messages without messages

View File

@ -78,15 +78,15 @@ Feature: Setting settings.
When I run :set -t colors statusbar.bg green When I run :set -t colors statusbar.bg green
Then colors -> statusbar.bg should be 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) # (and displays a page saying it's not available)
Scenario: Opening qute:settings Scenario: Opening qute://settings
When I run :set 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: 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 Scenario: Focusing input fields in qute://settings and entering valid value
When I set general -> ignore-case to false When I set general -> ignore-case to false
And I open qute://settings And I open qute://settings
@ -101,7 +101,7 @@ Feature: Setting settings.
And I press the key "<Tab>" And I press the key "<Tab>"
Then general -> ignore-case should be true 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 Scenario: Focusing input fields in qute://settings and entering invalid value
When I open qute://settings When I open qute://settings
# scroll to the right - the table does not fit in the default screen # scroll to the right - the table does not fit in the default screen

View File

@ -225,12 +225,12 @@ Feature: quickmarks and bookmarks
Scenario: Listing quickmarks Scenario: Listing quickmarks
When I run :quickmark-add http://localhost:(port)/data/numbers/20.txt twenty 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 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" Then the page should contain the plaintext "twenty"
And the page should contain the plaintext "twentyone" And the page should contain the plaintext "twentyone"
Scenario: Listing bookmarks Scenario: Listing bookmarks
When I open data/title.html in a new tab When I open data/title.html in a new tab
And I run :bookmark-add And I run :bookmark-add
And I open qute:bookmarks And I open qute://bookmarks
Then the page should contain the plaintext "Test title" Then the page should contain the plaintext "Test title"

View File

@ -145,7 +145,7 @@ Feature: Miscellaneous utility commands exposed to the user.
And I run :message-info oldstuff And I run :message-info oldstuff
And I run :repeat 20 message-info otherstuff And I run :repeat 20 message-info otherstuff
And I run :message-info newstuff And I run :message-info newstuff
And I open qute:log And I open qute://log
Then the page should contain the plaintext "newstuff" Then the page should contain the plaintext "newstuff"
And the page should not contain the plaintext "oldstuff" And the page should not contain the plaintext "oldstuff"

View File

@ -138,10 +138,10 @@ def test_misconfigured_user_dirs(request, httpbin, temp_basedir_env,
def test_no_loglines(request, quteproc_new): 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'] + quteproc_new.start(args=['--temp-basedir', '--loglines=0'] +
_base_args(request.config)) _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.' assert quteproc_new.get_content() == 'Log output was disabled.'

View File

@ -91,3 +91,10 @@ def test_error_network_reply(qtbot, req):
assert reply.readData(1) == b'' assert reply.readData(1) == b''
assert reply.error() == QNetworkReply.UnknownNetworkError assert reply.error() == QNetworkReply.UnknownNetworkError
assert reply.errorString() == "This is an error" 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

View File

@ -265,6 +265,7 @@ class TestFuzzyUrl:
('file:///tmp/foo', True), ('file:///tmp/foo', True),
('about:blank', True), ('about:blank', True),
('qute:version', True), ('qute:version', True),
('qute://version', True),
('http://www.qutebrowser.org/', False), ('http://www.qutebrowser.org/', False),
('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, 'file:///tmp/foo'),
(True, True, False, 'about:blank'), (True, True, False, 'about:blank'),
(True, True, False, 'qute:version'), (True, True, False, 'qute:version'),
(True, True, False, 'qute://version'),
(True, True, False, 'localhost'), (True, True, False, 'localhost'),
# _has_explicit_scheme False, special_url True # _has_explicit_scheme False, special_url True
(True, True, False, 'qute::foo'), (True, True, False, 'qute::foo'),
(True, True, False, 'qute:://foo'),
# Invalid URLs # Invalid URLs
(False, False, False, ''), (False, False, False, ''),
(False, True, False, 'onlyscheme:'), (False, True, False, 'onlyscheme:'),