Conflicts:
	qutebrowser/commands/runners.py
This commit is contained in:
Jan Verbeek 2016-10-17 19:14:58 +02:00
commit 2a304d7a6b
48 changed files with 435 additions and 117 deletions

View File

@ -19,7 +19,12 @@ matrix:
env: DOCKER=ubuntu-xenial
services: docker
- os: osx
env: TESTENV=py35
env: TESTENV=py35 OSX=elcapitan
osx_image: xcode7.3
# https://github.com/The-Compiler/qutebrowser/issues/2013
# - os: osx
# env: TESTENV=py35 OSX=yosemite
# osx_image: xcode6.4
- os: linux
env: TESTENV=pylint
- os: linux
@ -36,6 +41,10 @@ matrix:
env: TESTENV=check-manifest
- os: linux
env: TESTENV=eslint
allow_failures:
- os: osx
env: TESTENV=py35 OSX=elcapitan
osx_image: xcode7.3
cache:
directories:

View File

@ -47,6 +47,8 @@ Added
https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API[HTML5 page visibility API]
- New `readability` userscript which shows a readable version of a page (using
the `readability-lxml` python package)
- New `cast` userscript to show a video on a Google Chromecast
- New `:run-with-count` command which replaces the (undocumented) `:count:command` syntax.
Changed
~~~~~~~
@ -179,6 +181,7 @@ Fixed
- Fixed hang when using multiple spaces in a row with the URL completion
- Fixed crash when closing a window without focusing it
- Userscripts now can access QUTE_FIFO correctly on Windows
- Compatibility with pdfjs v1.6.210
v0.8.3 (unreleased)
-------------------

View File

@ -652,4 +652,4 @@ as closed.
* OS X: Run `python3 scripts/dev/build_release.py --upload v0.X.Y` (replace X/Y by hand)
* On server: Run `python3 scripts/dev/download_release.py v0.X.Y` (replace X/Y by hand)
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed
* Announce to qutebrowser mailinglist
* Announce to qutebrowser and qutebrowser-announce mailinglist

View File

@ -70,6 +70,9 @@ message to the
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
mailto:qutebrowser@lists.qutebrowser.org[].
There's also a https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[announce-only mailinglist]
at mailto:qutebrowser-announce@lists.qutebrowser.org[].
Contributions / Bugs
--------------------
@ -159,10 +162,10 @@ Contributors, sorted by the number of commits in descending order:
* Claude
* Corentin Julé
* meles5
* Kevin Velghe
* Philipp Hansch
* Daniel Karbach
* Panagiotis Ktistakis
* Kevin Velghe
* Artur Shaik
* Nathan Isom
* Thorsten Wißmann
@ -180,6 +183,7 @@ Contributors, sorted by the number of commits in descending order:
* knaggita
* Oliver Caldwell
* Julian Weigt
* Sebastian Frysztak
* Jonas Schürmann
* error800
* Michael Hoang
@ -244,6 +248,7 @@ Contributors, sorted by the number of commits in descending order:
* Tim Harder
* Thiago Barroso Perrotta
* Sorokin Alexei
* Simon Désaulniers
* Rok Mandeljc
* Noah Huesser
* Moez Bouhlel

View File

@ -1017,6 +1017,7 @@ How many steps to zoom out.
|<<rl-unix-line-discard,rl-unix-line-discard>>|Remove chars backward from the cursor to the beginning of the line.
|<<rl-unix-word-rubout,rl-unix-word-rubout>>|Remove chars from the cursor to the beginning of the word.
|<<rl-yank,rl-yank>>|Paste the most recently deleted text.
|<<run-with-count,run-with-count>>|Run a command with the given count.
|<<scroll,scroll>>|Scroll the current tab in the given direction.
|<<scroll-page,scroll-page>>|Scroll the frame page-wise.
|<<scroll-perc,scroll-perc>>|Scroll to a specific percentage of the page.
@ -1365,6 +1366,26 @@ Paste the most recently deleted text.
This acts like readline's yank.
[[run-with-count]]
=== run-with-count
Syntax: +:run-with-count 'count-arg' 'command'+
Run a command with the given count.
If run_with_count itself is run with a count, it multiplies count_arg.
==== positional arguments
* +'count-arg'+: The count to pass to the command.
* +'command'+: The command to run, with optional args.
==== count
The count that run_with_count itself received.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
* This command does not replace variables like +\{url\}+.
[[scroll]]
=== scroll
Syntax: +:scroll 'direction'+

View File

@ -69,6 +69,7 @@
|<<network-ssl-strict,ssl-strict>>|Whether to validate SSL handshakes.
|<<network-dns-prefetch,dns-prefetch>>|Whether to try to pre-fetch DNS entries to speed up browsing.
|<<network-custom-headers,custom-headers>>|Set custom headers for qutebrowser HTTP requests.
|<<network-netrc-file,netrc-file>>|Set location of a netrc-file for HTTP authentication. If empty, ~/.netrc is used.
|==============
.Quick reference for section ``completion''
@ -808,6 +809,12 @@ Set custom headers for qutebrowser HTTP requests.
Default: empty
[[network-netrc-file]]
=== netrc-file
Set location of a netrc-file for HTTP authentication. If empty, ~/.netrc is used.
Default: empty
== completion
Options related to completion and command history.

View File

@ -32,7 +32,8 @@ image:http://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding c
`scripts/asciidoc2html.py` to generate the documentation.
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
* Subscribe to
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist]
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] or
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[the announce-only mailinglist].
* Let me know what features you are missing or things that need (even small!)
improvements.

View File

@ -162,6 +162,8 @@ this program. If not, see <http://www.gnu.org/licenses/>.
* Website: http://www.qutebrowser.org/
* Mailinglist: mailto:qutebrowser@lists.qutebrowser.org[] /
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser
* Announce-only mailinglist: mailto:qutebrowser-announce@lists.qutebrowser.org[] /
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce
* IRC: irc://irc.freenode.org/#qutebrowser[`#qutebrowser`] on
http://freenode.net/[Freenode]
* Github: https://github.com/The-Compiler/qutebrowser

View File

@ -19,7 +19,7 @@ pbr==1.10.0
pep8==1.7.0
pep8-naming==0.4.1
pycodestyle==2.0.0
pydocstyle==1.0.0 # rq.filter: != 1.1.0
pydocstyle==1.1.1
pyflakes==1.3.0
pyparsing==2.1.9
pyparsing==2.1.10
six==1.10.0

View File

@ -12,7 +12,7 @@ flake8-tidy-imports
flake8-tuple
hacking
pep8-naming
pydocstyle!=1.1.0
pydocstyle
pyflakes
# Pinned to 1.5.7 by hacking otherwise
@ -23,6 +23,3 @@ pep8==1.7.0
# https://github.com/JBKahn/flake8-debugger/issues/5
#@ filter: flake8-debugger != 2.0.0
# https://gitlab.com/pycqa/flake8-docstrings/issues/16
#@ filter: pydocstyle != 1.1.0

View File

@ -1,2 +1,2 @@
pip==8.1.2
setuptools==28.2.0
setuptools==28.6.0

View File

@ -8,7 +8,7 @@ decorator==4.0.10
Flask==0.11.1
glob2==0.4.1
httpbin==0.5.0
hypothesis==3.5.2
hypothesis==3.5.3
itsdangerous==0.24
# Jinja2==2.8
Mako==1.0.4
@ -19,7 +19,7 @@ py==1.4.31
pytest==3.0.3
pytest-bdd==2.18.0
pytest-catchlog==1.2.2
pytest-cov==2.3.1
pytest-cov==2.4.0
pytest-faulthandler==1.3.0
pytest-instafail==0.3.0
pytest-mock==1.2
@ -31,7 +31,7 @@ pytest-warnings==0.1.0
pytest-xvfb==0.3.0
six==1.10.0
spark-parser==1.4.0
uncompyle6==2.8.3
uncompyle6==2.9.2
vulture==0.10
Werkzeug==0.11.11
xdis==2.3.1
xdis==3.1.0

View File

@ -2,5 +2,5 @@
pluggy==0.4.0
py==1.4.31
tox==2.3.1
tox==2.4.1
virtualenv==15.0.3

150
misc/userscripts/cast Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env bash
#
# Behaviour
# Userscript for qutebrowser which casts the url passed in $1 to the default
# ChromeCast device in the network using the program `castnow`
#
# Usage
# You can launch the script from qutebrowser as follows:
# spawn --userscript ${PATH_TO_FILE} {url}
#
# Then, you can control the chromecast by launching the simple command
# `castnow` in a shell which will connect to the running castnow instance.
#
# For stopping the script, issue the command `pkill -f castnow` which would
# then let the rest of the userscript execute for cleaning temporary file.
#
# Thanks
# This userscript borrows Thorsten Wißmann's javascript code from his `mpv`
# userscript.
#
# Dependencies
# - castnow, https://github.com/xat/castnow
#
# Author
# Simon Désaulniers <sim.desaulniers@gmail.com>
if [ -z "$QUTE_FIFO" ] ; then
cat 1>&2 <<EOF
Error: $0 can not be run as a standalone script.
It is a qutebrowser userscript. In order to use it, call it using
'spawn --userscript' as described in qute://help/userscripts.html
EOF
exit 1
fi
msg() {
local cmd="$1"
shift
local msg="$*"
if [ -z "$QUTE_FIFO" ] ; then
echo "$cmd: $msg" >&2
else
echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
fi
}
js() {
cat <<EOF
function descendantOfTagName(child, ancestorTagName) {
// tells whether child has some (proper) ancestor
// with the tag name ancestorTagName
while (child.parentNode != null) {
child = child.parentNode;
if (typeof child.tagName === 'undefined') break;
if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
return true;
}
}
return false;
}
var App = {};
var all_videos = [];
all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
App.backup_videos = Array();
App.all_replacements = Array();
for (i = 0; i < all_videos.length; i++) {
var video = all_videos[i];
if (descendantOfTagName(video, "object")) {
// skip tags that are contained in an object, because we hide
// the object anyway.
continue;
}
var replacement = document.createElement("div");
replacement.innerHTML = "
<p style=\\"margin-bottom: 0.5em\\">
The video is being cast on your ChromeCast device.
</p>
<p>
In order to restore this particular video
<a style=\\"font-weight: bold;
color: white;
background: transparent;
\\"
onClick=\\"restore_video(this, " + i + ");\\"
href=\\"javascript: restore_video(this, " + i + ")\\"
>click here</a>.
</p>
";
replacement.style.position = "relative";
replacement.style.zIndex = "100003000000";
replacement.style.fontSize = "1rem";
replacement.style.textAlign = "center";
replacement.style.verticalAlign = "middle";
replacement.style.height = "100%";
replacement.style.background = "#101010";
replacement.style.color = "white";
replacement.style.border = "4px dashed #545454";
replacement.style.padding = "2em";
replacement.style.margin = "auto";
App.all_replacements[i] = replacement;
App.backup_videos[i] = video;
video.parentNode.replaceChild(replacement, video);
}
function restore_video(obj, index) {
obj = App.all_replacements[index];
video = App.backup_videos[index];
console.log(video);
obj.parentNode.replaceChild(video, obj);
}
/** force repainting the video, thanks to:
* http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
*/
var siteHeader = document.getElementById('header');
siteHeader.style.display='none';
siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
siteHeader.style.display='block';
EOF
}
printjs() {
js | sed 's,//.*$,,' | tr '\n' ' '
}
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
tmpdir=$(mktemp -d)
file_to_cast=${tmpdir}/qutecast
# kill any running instance of castnow
pkill -f /usr/bin/castnow
# start youtube download in stream mode (-o -) into temporary file
youtube-dl -qo - "$1" > ${file_to_cast} &
ytdl_pid=$!
msg info "Casting $1" >> "$QUTE_FIFO"
# start castnow in stream mode to cast on ChromeCast
tail -F "${file_to_cast}" | castnow -
# cleanup remaining background process and file on disk
kill ${ytdl_pid}
rm -rf ${tmpdir}

View File

@ -24,9 +24,9 @@
# Caveat: Does not use authentication of any kind. Add it in if you want it to.
#
path=/tmp/qutebrowser_$(mktemp XXXXXXXX).html
path=$(mktemp --tmpdir qutebrowser_XXXXXXXX.html)
curl "$QUTE_URL" > $path
curl "$QUTE_URL" > "$path"
urxvt -e vim "$path"
rm "$path"

View File

@ -30,6 +30,9 @@ qt_log_ignore =
^QProcess: Destroyed while process .* is still running\.
^"Method "GetAll" with signature "s" on interface "org\.freedesktop\.DBus\.Properties" doesn't exist
^"Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
^propsReply "Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
^nmReply "Method \\"GetDevices\\" with signature \\"\\" on interface \\"org\.freedesktop\.NetworkManager\\" doesn't exist\\n"
^"Object path cannot be empty"
^virtual void QSslSocketBackendPrivate::transmit\(\) SSL write failed with error: -9805
^virtual void QSslSocketBackendPrivate::transmit\(\) SSLRead failed with: -9805
^Type conversion already registered from type .*

View File

@ -50,7 +50,8 @@ from qutebrowser.browser.webkit import cookies, cache, downloads
from qutebrowser.browser.webkit.network import (webkitqutescheme, proxy,
networkmanager)
from qutebrowser.mainwindow import mainwindow
from qutebrowser.misc import readline, ipc, savemanager, sessions, crashsignal
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
crashsignal, earlyinit)
from qutebrowser.misc import utilcmds # pylint: disable=unused-import
from qutebrowser.utils import (log, version, message, utils, qtutils, urlutils,
objreg, usertypes, standarddir, error, debug)
@ -198,6 +199,9 @@ def _process_args(args):
_open_startpage()
_open_quickstart(args)
delta = datetime.datetime.now() - earlyinit.START_TIME
log.init.debug("Init finished after {}s".format(delta.total_seconds()))
def _load_session(name):
"""Load the default session.

View File

@ -62,8 +62,10 @@ def _generate_pdfjs_script(url):
url: The url of the pdf page as QUrl.
"""
return (
'PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
'PDFView.open("{url}");\n'
'document.addEventListener("DOMContentLoaded", function() {{\n'
' PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
' (window.PDFView || window.PDFViewerApplication).open("{url}");\n'
'}});\n'
).format(url=javascript.string_escape(url.toString(QUrl.FullyEncoded)))

View File

@ -37,14 +37,6 @@ from PyQt5.QtCore import QUrl
from qutebrowser.browser.webkit import webkitelem, downloads
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
try:
import cssutils
except (ImportError, re.error):
# Catching re.error because cssutils in earlier releases (<= 1.0) is broken
# on Python 3.5
# See https://bitbucket.org/cthedot/cssutils/issues/52
cssutils = None
_File = collections.namedtuple('_File',
['content', 'content_type', 'content_location',
'transfer_encoding'])
@ -85,6 +77,14 @@ def _get_css_imports_cssutils(data, inline=False):
data: The content of the stylesheet to scan as string.
inline: True if the argument is an inline HTML style attribute.
"""
try:
import cssutils
except (ImportError, re.error):
# Catching re.error because cssutils in earlier releases (<= 1.0) is
# broken on Python 3.5
# See https://bitbucket.org/cthedot/cssutils/issues/52
return None
# We don't care about invalid CSS data, this will only litter the log
# output with CSS errors
parser = cssutils.CSSParser(loglevel=100,
@ -114,10 +114,10 @@ def _get_css_imports(data, inline=False):
data: The content of the stylesheet to scan as string.
inline: True if the argument is an inline HTML style attribute.
"""
if cssutils is None:
return _get_css_imports_regex(data)
else:
return _get_css_imports_cssutils(data, inline)
imports = _get_css_imports_cssutils(data, inline)
if imports is None:
imports = _get_css_imports_regex(data)
return imports
def _check_rel(element):

View File

@ -330,7 +330,7 @@ class NetworkManager(QNetworkAccessManager):
# altogether.
reply.netrc_used = True
try:
net = netrc.netrc()
net = netrc.netrc(config.get('network', 'netrc-file'))
authenticators = net.authenticators(reply.url().host())
if authenticators is not None:
(user, _account, password) = authenticators

View File

@ -31,8 +31,7 @@ from qutebrowser.utils import message, objreg, qtutils, usertypes, utils
from qutebrowser.misc import split
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline',
'count'])
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline'])
last_command = {}
macro = {}
@ -158,26 +157,6 @@ class CommandRunner(QObject):
for sub in sub_texts:
yield self.parse(sub, *args, **kwargs)
def _parse_count(self, cmdstr):
"""Split a count prefix off from a command for parse().
Args:
cmdstr: The command/args including the count.
Return:
A (count, cmdstr) tuple, with count being None or int.
"""
if ':' not in cmdstr:
return (None, cmdstr)
count, cmdstr = cmdstr.split(':', maxsplit=1)
try:
count = int(count)
except ValueError:
# We just ignore invalid prefixes
count = None
return (count, cmdstr)
def parse(self, text, *, fallback=False, keep=False):
"""Split the commandline text into command and arguments.
@ -191,7 +170,6 @@ class CommandRunner(QObject):
A ParseResult tuple.
"""
cmdstr, sep, argstr = text.partition(' ')
count, cmdstr = self._parse_count(cmdstr)
if not cmdstr and not fallback:
raise cmdexc.NoSuchCommandError("No command given")
@ -206,8 +184,7 @@ class CommandRunner(QObject):
raise cmdexc.NoSuchCommandError(
'{}: no such command'.format(cmdstr))
cmdline = split.split(text, keep=keep)
return ParseResult(cmd=None, args=None, cmdline=cmdline,
count=count)
return ParseResult(cmd=None, args=None, cmdline=cmdline)
args = self._split_args(cmd, argstr, keep)
if keep and args:
@ -217,7 +194,7 @@ class CommandRunner(QObject):
else:
cmdline = [cmdstr] + args[:]
return ParseResult(cmd=cmd, args=args, cmdline=cmdline, count=count)
return ParseResult(cmd=cmd, args=args, cmdline=cmdline)
def _completion_match(self, cmdstr):
"""Replace cmdstr with a matching completion if there's only one match.
@ -295,18 +272,9 @@ class CommandRunner(QObject):
args = result.args
else:
args = replace_variables(self._win_id, result.args)
if count is not None:
if result.count is not None:
raise cmdexc.CommandMetaError("Got count via command and "
"prefix!")
result.cmd.run(self._win_id, args, count=count)
elif result.count is not None:
result.cmd.run(self._win_id, args, count=result.count)
else:
result.cmd.run(self._win_id, args)
result.cmd.run(self._win_id, args, count=count)
parsed_command = (self._parse_count(text)[1],
count if count is not None else result.count)
parsed_command = (text, count)
if result.cmdline[0] != 'repeat-command':
last_command[cur_mode] = parsed_command

View File

@ -157,6 +157,7 @@ class Completer(QObject):
result = runner.parse(text, fallback=True, keep=True)
parts = [x for x in result.cmdline if x]
pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
pos = min(pos, len(text)) # Qt treats 2-byte UTF-16 chars as 2 chars
log.completion.debug('partitioning {} around position {}'.format(parts,
pos))
for i, part in enumerate(parts):

View File

@ -438,6 +438,11 @@ def data(readonly=False):
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
)),

View File

@ -503,8 +503,8 @@ class MainWindow(QWidget):
# Ask if multiple downloads running
if 'downloads' in confirm_quit and download_count > 0:
quit_texts.append("{} {} running.".format(
tab_count,
"download is" if tab_count == 1 else "downloads are"))
download_count,
"download is" if download_count == 1 else "downloads are"))
# Process all quit messages that user must confirm
if quit_texts or 'always' in confirm_quit:
text = '\n'.join(['Really quit?'] + quit_texts)

View File

@ -703,13 +703,10 @@ class TabBarStyle(QCommonStyle):
text_rect.adjust(indicator_width + indicator_padding.left +
indicator_padding.right, 0, 0, 0)
if opt.icon.isNull():
icon_rect = QRect()
else:
icon_rect = self._get_icon_rect(opt, text_rect)
if icon_rect.isValid():
icon_padding = self.pixelMetric(PixelMetrics.icon_padding, opt)
icon_rect = self._get_icon_rect(opt, text_rect)
if icon_rect.isValid():
text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)
text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)
text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
return Layouts(text=text_rect, icon=icon_rect,
@ -733,9 +730,17 @@ class TabBarStyle(QCommonStyle):
else QIcon.Disabled)
icon_state = (QIcon.On if opt.state & QStyle.State_Selected
else QIcon.Off)
tab_icon_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
tab_icon_size = QSize(min(tab_icon_size.width(), icon_size.width()),
min(tab_icon_size.height(), icon_size.height()))
# reserve space for favicon when tab bar is vertical (issue #1968)
position = config.get('tabs', 'position')
if (opt.icon.isNull() and
position in [QTabWidget.East, QTabWidget.West] and
config.get('tabs', 'show-favicons')):
tab_icon_size = icon_size
else:
actual_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
tab_icon_size = QSize(
min(actual_size.width(), icon_size.width()),
min(actual_size.height(), icon_size.height()))
icon_rect = QRect(text_rect.left(), text_rect.top() + 1,
tab_icon_size.width(), tab_icon_size.height())
icon_rect = self._style.visualRect(opt.direction, opt.rect, icon_rect)

View File

@ -37,6 +37,7 @@ import signal
import operator
import importlib
import pkg_resources
import datetime
try:
import tkinter
except ImportError:
@ -45,6 +46,9 @@ except ImportError:
# initialization needs to take place before that!
START_TIME = datetime.datetime.now()
def _missing_str(name, *, windows=None, pip=None, webengine=False):
"""Get an error string for missing packages.

View File

@ -481,3 +481,5 @@ class SessionManager(QObject):
log.sessions.exception("Error while deleting session!")
raise cmdexc.CommandError("Error while deleting session: {}"
.format(e))
else:
log.sessions.debug("Deleted session {}.".format(name))

View File

@ -86,6 +86,23 @@ def repeat(times: int, command, win_id):
commandrunner.run_safely(command)
@cmdutils.register(maxsplit=1, hide=True, no_cmd_split=True,
no_replace_variables=True)
@cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('count', count=True)
def run_with_count(count_arg: int, command, win_id, count=1):
"""Run a command with the given count.
If run_with_count itself is run with a count, it multiplies count_arg.
Args:
count_arg: The count to pass to the command.
command: The command to run, with optional args.
count: The count that run_with_count itself received.
"""
runners.CommandRunner(win_id).run(command, count_arg * count)
@cmdutils.register(hide=True)
def message_error(text):
"""Show an error message in the statusbar.

View File

@ -148,7 +148,11 @@ console_filter = None
def stub(suffix=''):
"""Show a STUB: message for the calling function."""
function = inspect.stack()[1][3]
try:
function = inspect.stack()[1][3]
except IndexError: # pragma: no cover
misc.exception("Failed to get stack")
function = '<unknown>'
text = "STUB: {}".format(function)
if suffix:
text = '{} ({})'.format(text, suffix)

View File

@ -88,11 +88,13 @@ if [[ $DOCKER ]]; then
elif [[ $TRAVIS_OS_NAME == osx ]]; then
# Disable App Nap
defaults write NSGlobalDomain NSAppSleepDisabled -bool YES
curl -LO https://github.com/The-Compiler/homebrew-qt5-webkit/releases/download/v5.6.0-1/pyqt5-5.6.el_capitan.bottle.1.tar.gz
curl -LO https://github.com/The-Compiler/homebrew-qt5-webkit/releases/download/v5.6.1_1-1/qt5-5.6.1-1.yosemite.bottle.1.tar.gz
curl -LO https://bootstrap.pypa.io/get-pip.py
sudo -H python get-pip.py
brew --version
brew_install python3 {qt5,pyqt5}-*.bottle.1.tar.gz
pip_install pip
brew_install python3 qt5 pyqt5
pip_install tox
pip --version
tox --version

View File

@ -3,8 +3,17 @@
<head>
<meta charset="utf-8">
<title>Inputs Autofocus</title>
<script type="text/javascript">
function setup_event_listener() {
var elem = document.getElementById('qute-input-autofocus');
console.log(elem);
elem.addEventListener('input', function() {
console.log("contents: " + elem.value);
});
}
</script>
</head>
<body>
<body onload="setup_event_listener()">
<input id="qute-input-autofocus" type="text" autofocus value=""/>
<input id="qute-input-disabled" disabled="disabled" href="#"/>
</body>

View File

@ -3,8 +3,17 @@
<head>
<meta charset="utf-8">
<title>Input</title>
<script type="text/javascript">
function setup_event_listener() {
var elem = document.getElementById('qute-input');
console.log(elem);
elem.addEventListener('input', function() {
console.log("contents: " + elem.value);
});
}
</script>
</head>
<body>
<body onload="setup_event_listener()">
<input id="qute-input" type="text" value=""/>
</body>
</html>

View File

@ -3,8 +3,16 @@
<head>
<meta charset="utf-8">
<title>Textarea</title>
<script type="text/javascript">
function setup_event_listener() {
var elem = document.getElementById('qute-textarea');
elem.addEventListener('input', function() {
console.log("contents: " + elem.value);
});
}
</script>
</head>
<body>
<body onload="setup_event_listener()">
<textarea id="qute-textarea"></textarea>
</body>
</html>

View File

@ -3,7 +3,7 @@ Feature: Caret mode
Background:
Given I open data/caret.html
And I run :tab-only ;; :enter-mode caret
And I run :tab-only ;; enter-mode caret
# document

View File

@ -22,3 +22,8 @@ Feature: Command bar completion
# Make sure qutebrowser doesn't hang
And I run :message-info "Still alive!"
Then the message "Still alive!" should be shown
Scenario: Crash when pasting emoji into the command line (#2007)
Given I open about:blank
When I run :set-cmd-text -s :🌀
Then no crash should happen

View File

@ -182,10 +182,11 @@ Feature: Using hints
### hints -> auto-follow-timeout
@not_osx
Scenario: Ignoring key presses after auto-following hints
When I set hints -> auto-follow-timeout to 500
When I set hints -> auto-follow-timeout to 1000
And I set hints -> mode to number
And I run :bind --force , message-error "This should not happen"
And I run :bind --force , message-error "This error message was triggered via a keybinding which should have been inhibited"
And I open data/hints/html/simple.html
And I hint with args "all"
And I press the key "f"

View File

@ -165,7 +165,7 @@ Feature: Keyboard input
Then the javascript message "key press: 88" should be logged
And the javascript message "key release: 88" should be logged
@no_xvfb @posix
@no_xvfb @posix @qtwebengine_skip
Scenario: :fake-key sending key to the website with other window focused
When I open data/keyinput/log.html
And I set general -> developer-extras to true

View File

@ -149,7 +149,7 @@ Feature: Various utility commands.
And I run :inspector
Then the error "Please enable developer-extras before using the webinspector!" should be shown
@no_xvfb @posix
@no_xvfb @posix @qtwebengine_skip
Scenario: Inspector smoke test
When I set general -> developer-extras to true
And I run :inspector
@ -165,7 +165,7 @@ Feature: Various utility commands.
Then the error "Please enable developer-extras before using the webinspector!" should be shown
# Different code path as an inspector got created now
@no_xvfb @posix
@no_xvfb @posix @qtwebengine_skip
Scenario: Inspector smoke test 2
When I set general -> developer-extras to true
And I run :inspector
@ -499,7 +499,7 @@ Feature: Various utility commands.
Scenario: Using :debug-log-capacity
When I run :debug-log-capacity 100
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 open qute:log
Then the page should contain the plaintext "newstuff"
@ -549,6 +549,7 @@ Feature: Various utility commands.
And I open cookies/set?qute-test=42 without waiting
And I wait until cookies is loaded
And I open cookies in a new tab
And I set general -> private-browsing to false
Then the cookie qute-test should be set to 42
## https://github.com/The-Compiler/qutebrowser/issues/1742
@ -557,6 +558,7 @@ Feature: Various utility commands.
Scenario: Private browsing is activated in QtWebKit without restart
When I set general -> private-browsing to true
And I open data/javascript/localstorage.html
And I set general -> private-browsing to false
Then the page should contain the plaintext "Local storage status: not working"
Scenario: :repeat-command
@ -695,3 +697,63 @@ Feature: Various utility commands.
And I wait until the scroll position changed
And I run :click-element id link
Then the error "Element position is out of view!" should be shown
## :command-history-{prev,next}
Scenario: Calling previous command
When I run :set-cmd-text :message-info blah
And I run :command-accept
And I wait for "blah" in the log
And I run :set-cmd-text :
And I run :command-history-prev
And I run :command-accept
Then the message "blah" should be shown
Scenario: Browsing through commands
When I run :set-cmd-text :message-info blarg
And I run :command-accept
And I wait for "blarg" in the log
And I run :set-cmd-text :
And I run :command-history-prev
And I run :command-history-prev
And I run :command-history-next
And I run :command-history-next
And I run :command-accept
Then the message "blarg" should be shown
Scenario: Calling previous command when history is empty
Given I have a fresh instance
When I run :set-cmd-text :
And I run :command-history-prev
And I run :command-accept
Then the error "No command given" should be shown
Scenario: Calling next command when there's no next command
When I run :set-cmd-text :
And I run :command-history-next
And I run :command-accept
Then the error "No command given" should be shown
@qtwebengine_todo: private browsing is not implemented yet
Scenario: Calling previous command with private-browsing mode
When I run :set-cmd-text :message-info blah
And I run :command-accept
And I set general -> private-browsing to true
And I run :set-cmd-text :message-error "This should only be shown once"
And I run :command-accept
And I wait for the error "This should only be shown once"
And I run :set-cmd-text :
And I run :command-history-prev
And I run :command-accept
And I set general -> private-browsing to false
Then the message "blah" should be shown
## :run-with-count
Scenario: :run-with-count
When I run :run-with-count 2 scroll down
Then "command called: scroll ['down'] (count=2)" should be logged
Scenario: :run-with-count with count
When I run :run-with-count 2 scroll down with count 3
Then "command called: scroll ['down'] (count=6)" should be logged

View File

@ -294,11 +294,13 @@ Feature: Saving and loading sessions
Scenario: Deleting internal session with --force
When I run :session-save --force _internal
And I run :session-delete --force _internal
And I wait for "Deleted session _internal." in the log
Then the session _internal should not exist
Scenario: Normally deleting a session
When I run :session-save deleted_session
And I run :session-delete deleted_session
And I wait for "Deleted session deleted_session." in the log
Then the session deleted_session should not exist
Scenario: Deleting a session which doesn't exist

View File

@ -987,13 +987,13 @@ Feature: Tab management
Scenario: Using :tab-next after closing last tab (#1448)
When I set tabs -> last-close to close
And I run :tab-only
And I run :tab-close ;; :tab-next
And I run :tab-close ;; tab-next
Then qutebrowser should quit
And no crash should happen
Scenario: Using :tab-prev after closing last tab (#1448)
When I set tabs -> last-close to close
And I run :tab-only
And I run :tab-close ;; :tab-prev
And I run :tab-close ;; tab-prev
Then qutebrowser should quit
And no crash should happen

View File

@ -437,7 +437,8 @@ class QuteProc(testprocess.Process):
command = command.replace('\\', r'\\')
if count is not None:
command = ':{}:{}'.format(count, command.lstrip(':'))
command = ':run-with-count {} {}'.format(count,
command.lstrip(':'))
self.send_ipc([command])
if not invalid:

View File

@ -80,8 +80,11 @@ def redirect_later():
@app.route('/custom/redirect-later-continue')
def redirect_later_continue():
"""Continue a redirect-later request."""
_redirect_later_event.set()
return flask.Response(b'Continued redirect.')
if _redirect_later_event is None:
return flask.Response(b'Timed out or no redirect pending.')
else:
_redirect_later_event.set()
return flask.Response(b'Continued redirect.')
@app.route('/custom/content-size')

View File

@ -56,6 +56,10 @@ def test_insert_mode(file_name, elem_id, source, input_text, auto_insert,
# second time.
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(input_text))
quteproc.send_cmd(':insert-text {clipboard}')
else:
raise ValueError("Invalid source {!r}".format(source))
quteproc.wait_for_js('contents: {}'.format(input_text))
quteproc.send_cmd(':leave-mode')
quteproc.send_cmd(':hint all')

View File

@ -36,11 +36,11 @@ from qutebrowser.browser import pdfjs
'http://foobar/%22);alert(%22attack!%22);'),
])
def test_generate_pdfjs_script(url, expected):
expected_code = ('PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
'PDFView.open("{}");\n'.format(expected))
expected_open = 'open("{}");'.format(expected)
url = QUrl(url)
actual = pdfjs._generate_pdfjs_script(url)
assert actual == expected_code
assert expected_open in actual
assert 'PDFView' in actual
def test_fix_urls():

View File

@ -26,6 +26,15 @@ import pytest
mhtml = pytest.importorskip('qutebrowser.browser.webkit.mhtml')
try:
import cssutils
except (ImportError, re.error):
# Catching re.error because cssutils in earlier releases (<= 1.0) is
# broken on Python 3.5
# See https://bitbucket.org/cthedot/cssutils/issues/52
cssutils = None
@pytest.fixture(autouse=True)
def patch_uuid(monkeypatch):
monkeypatch.setattr("uuid.uuid4", lambda: "UUID")
@ -248,8 +257,7 @@ def test_empty_content_type(checker):
@pytest.mark.parametrize('has_cssutils', [
pytest.mark.skipif(mhtml.cssutils is None,
reason="requires cssutils")(True),
pytest.mark.skipif(cssutils is None, reason="requires cssutils")(True),
False,
], ids=['with_cssutils', 'no_cssutils'])
@pytest.mark.parametrize('inline, style, expected_urls', [
@ -267,7 +275,8 @@ def test_empty_content_type(checker):
def test_css_url_scanner(monkeypatch, has_cssutils, inline, style,
expected_urls):
if not has_cssutils:
monkeypatch.setattr('qutebrowser.browser.webkit.mhtml.cssutils', None)
monkeypatch.setattr(mhtml, '_get_css_imports_cssutils',
lambda data, inline=False: None)
expected_urls.sort()
urls = mhtml._get_css_imports(style, inline=inline)
urls.sort()

View File

@ -64,15 +64,6 @@ class TestCommandRunner:
with pytest.raises(cmdexc.NoSuchCommandError):
list(cr.parse_all(command))
def test_parse_with_count(self):
"""Test parsing of commands with a count."""
cr = runners.CommandRunner(0)
result = cr.parse('20:scroll down')
assert result.cmd.name == 'scroll'
assert result.count == 20
assert result.args == ['down']
assert result.cmdline == ['scroll', 'down']
def test_partial_parsing(self):
"""Test partial parsing with a runner where it's enabled.

View File

@ -181,6 +181,7 @@ def _set_cmd_prompt(cmd, txt):
(':open -- |', None, ''),
(':gibberish nonesense |', None, ''),
('/:help|', None, ''),
('::bind|', usertypes.Completion.command, ':bind'),
])
def test_update_completion(txt, kind, pattern, status_command_stub,
completer_obj, completion_widget_stub):

View File

@ -41,6 +41,7 @@ class TestTabWidget:
'position': 0,
'select-on-remove': 1,
'show': 'always',
'show-favicons': True,
'padding': configtypes.PaddingValues(0, 0, 5, 5),
'indicator-width': 3,
'indicator-padding': configtypes.PaddingValues(2, 2, 0, 4),