Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9a85b66452
@ -32,6 +32,8 @@ Added
|
||||
- New setting `tabs -> show` which supersedes the old `tabs -> hide-*` options
|
||||
and has an additional `switching` option which shows tab while switching
|
||||
them. There's also a new `show-switching` option to configure the timeout.
|
||||
- New setting `storage -> remember-download-directory` to remember the last
|
||||
used download directory.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@ -48,7 +50,12 @@ Fixed
|
||||
~~~~~
|
||||
|
||||
- `link_pyqt.py` now should work better on untested distributions.
|
||||
- Fixed various corner-cases with crashes when reading invalid config values.
|
||||
- Fixed various corner-cases with crashes when reading invalid config values
|
||||
and the history file.
|
||||
- Fixed various corner-cases when setting text via an external editor.
|
||||
- Fixed potential crash when hinting a text field.
|
||||
- Fixed entering of insert mode when certain disabled text fields were clicked.
|
||||
- Fixed a crash when using `:set` with `-p` and `!` (invert value)
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
@ -89,10 +89,10 @@ Requirements
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* http://www.python.org/[Python] 3.4
|
||||
* http://qt.io/[Qt] 5.2.0 or newer (5.4.2 recommended)
|
||||
* http://qt.io/[Qt] 5.2.0 or newer (5.5.0 recommended)
|
||||
* QtWebKit
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer
|
||||
(5.4.2 recommended) for Python 3
|
||||
(5.5.0 recommended) for Python 3
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* http://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
|
@ -473,10 +473,10 @@ Where to show the downloaded files.
|
||||
|
||||
Valid values:
|
||||
|
||||
* +north+
|
||||
* +south+
|
||||
* +top+
|
||||
* +bottom+
|
||||
|
||||
Default: +pass:[north]+
|
||||
Default: +pass:[top]+
|
||||
|
||||
[[ui-message-timeout]]
|
||||
=== message-timeout
|
||||
@ -1020,12 +1020,12 @@ The position of the tab bar.
|
||||
|
||||
Valid values:
|
||||
|
||||
* +north+
|
||||
* +south+
|
||||
* +east+
|
||||
* +west+
|
||||
* +top+
|
||||
* +bottom+
|
||||
* +left+
|
||||
* +right+
|
||||
|
||||
Default: +pass:[north]+
|
||||
Default: +pass:[top]+
|
||||
|
||||
[[tabs-show-favicons]]
|
||||
=== show-favicons
|
||||
|
@ -307,6 +307,7 @@ def javascript_escape(text):
|
||||
('"', r'\"'), # (note it won't hurt when we escape the wrong one).
|
||||
('\n', r'\n'), # We also need to escape newlines for some reason.
|
||||
('\r', r'\r'),
|
||||
('\x00', r'\x00'),
|
||||
)
|
||||
for orig, repl in replacements:
|
||||
text = text.replace(orig, repl)
|
||||
|
@ -488,6 +488,8 @@ class Completer(QObject):
|
||||
"""Delete the current completion item."""
|
||||
completion = objreg.get('completion', scope='window',
|
||||
window=self._win_id)
|
||||
if not completion.currentIndex().isValid():
|
||||
raise cmdexc.CommandError("No item selected!")
|
||||
try:
|
||||
self.model().srcmodel.delete_cur_item(completion)
|
||||
except NotImplementedError:
|
||||
|
@ -271,6 +271,20 @@ def _get_value_transformer(old, new):
|
||||
return transformer
|
||||
|
||||
|
||||
def _transform_position(val):
|
||||
"""Transformer for position values."""
|
||||
mapping = {
|
||||
'north': 'top',
|
||||
'south': 'bottom',
|
||||
'west': 'left',
|
||||
'east': 'right',
|
||||
}
|
||||
try:
|
||||
return mapping[val]
|
||||
except KeyError:
|
||||
return val
|
||||
|
||||
|
||||
class ConfigManager(QObject):
|
||||
|
||||
"""Configuration manager for qutebrowser.
|
||||
@ -334,6 +348,8 @@ class ConfigManager(QObject):
|
||||
CHANGED_OPTIONS = {
|
||||
('content', 'cookies-accept'):
|
||||
_get_value_transformer('default', 'no-3rdparty'),
|
||||
('tabbar', 'position'): _transform_position,
|
||||
('ui', 'downloads-position'): _transform_position,
|
||||
}
|
||||
|
||||
changed = pyqtSignal(str, str)
|
||||
@ -674,10 +690,11 @@ class ConfigManager(QObject):
|
||||
else:
|
||||
try:
|
||||
if option.endswith('!') and value is None:
|
||||
val = self.get(section_, option[:-1])
|
||||
option = option[:-1]
|
||||
val = self.get(section_, option)
|
||||
layer = 'temp' if temp else 'conf'
|
||||
if isinstance(val, bool):
|
||||
self.set(layer, section_, option[:-1], str(not val))
|
||||
self.set(layer, section_, option, str(not val))
|
||||
else:
|
||||
raise cmdexc.CommandError(
|
||||
"set: Attempted inversion of non-boolean value.")
|
||||
|
@ -240,7 +240,7 @@ def data(readonly=False):
|
||||
"The default zoom level."),
|
||||
|
||||
('downloads-position',
|
||||
SettingValue(typ.VerticalPosition(), 'north'),
|
||||
SettingValue(typ.VerticalPosition(), 'top'),
|
||||
"Where to show the downloaded files."),
|
||||
|
||||
('message-timeout',
|
||||
@ -501,7 +501,7 @@ def data(readonly=False):
|
||||
"On which mouse button to close tabs."),
|
||||
|
||||
('position',
|
||||
SettingValue(typ.Position(), 'north'),
|
||||
SettingValue(typ.Position(), 'top'),
|
||||
"The position of the tab bar."),
|
||||
|
||||
('show-favicons',
|
||||
|
@ -1243,13 +1243,13 @@ class Position(MappingType):
|
||||
|
||||
"""The position of the tab bar."""
|
||||
|
||||
valid_values = ValidValues('north', 'south', 'east', 'west')
|
||||
valid_values = ValidValues('top', 'bottom', 'left', 'right')
|
||||
|
||||
MAPPING = {
|
||||
'north': QTabWidget.North,
|
||||
'south': QTabWidget.South,
|
||||
'west': QTabWidget.West,
|
||||
'east': QTabWidget.East,
|
||||
'top': QTabWidget.North,
|
||||
'bottom': QTabWidget.South,
|
||||
'left': QTabWidget.West,
|
||||
'right': QTabWidget.East,
|
||||
}
|
||||
|
||||
|
||||
@ -1257,7 +1257,7 @@ class VerticalPosition(BaseType):
|
||||
|
||||
"""The position of the download bar."""
|
||||
|
||||
valid_values = ValidValues('north', 'south')
|
||||
valid_values = ValidValues('top', 'bottom')
|
||||
|
||||
|
||||
class UrlList(List):
|
||||
|
@ -198,10 +198,10 @@ class MainWindow(QWidget):
|
||||
self._vbox.removeWidget(self._downloadview)
|
||||
self._vbox.removeWidget(self.status)
|
||||
position = config.get('ui', 'downloads-position')
|
||||
if position == 'north':
|
||||
if position == 'top':
|
||||
self._vbox.addWidget(self._downloadview)
|
||||
self._vbox.addWidget(self.tabbed_browser)
|
||||
elif position == 'south':
|
||||
elif position == 'bottom':
|
||||
self._vbox.addWidget(self.tabbed_browser)
|
||||
self._vbox.addWidget(self._downloadview)
|
||||
else:
|
||||
|
@ -25,10 +25,12 @@ from unittest import mock
|
||||
import collections.abc
|
||||
import operator
|
||||
import itertools
|
||||
import binascii
|
||||
import os.path
|
||||
|
||||
import hypothesis
|
||||
import hypothesis.strategies
|
||||
from PyQt5.QtCore import QRect, QPoint
|
||||
from PyQt5.QtCore import PYQT_VERSION, QRect, QPoint
|
||||
from PyQt5.QtWebKit import QWebElement
|
||||
import pytest
|
||||
|
||||
@ -513,6 +515,11 @@ class TestJavascriptEscape:
|
||||
"foo'bar": r"foo\'bar",
|
||||
'foo"bar': r'foo\"bar',
|
||||
'one\\two\rthree\nfour\'five"six': r'one\\two\rthree\nfour\'five\"six',
|
||||
'\x00': r'\x00',
|
||||
'hellö': 'hellö',
|
||||
'☃': '☃',
|
||||
'\x80Ā': '\x80Ā',
|
||||
'𐀀\x00𐀀\x00': r'𐀀\x00𐀀\x00',
|
||||
}
|
||||
|
||||
@pytest.mark.parametrize('before, after', TESTS.items())
|
||||
@ -520,21 +527,63 @@ class TestJavascriptEscape:
|
||||
"""Test javascript escaping with some expected outcomes."""
|
||||
assert webelem.javascript_escape(before) == after
|
||||
|
||||
def _test_escape(self, text, webframe):
|
||||
def _test_escape(self, text, qtbot, webframe):
|
||||
"""Helper function for test_real_escape*."""
|
||||
try:
|
||||
self._test_escape_simple(text, webframe)
|
||||
except AssertionError:
|
||||
# Try another method if the simple method failed.
|
||||
#
|
||||
# See _test_escape_hexlified documentation on why this is
|
||||
# necessary.
|
||||
self._test_escape_hexlified(text, qtbot, webframe)
|
||||
|
||||
def _test_escape_hexlified(self, text, qtbot, webframe):
|
||||
"""Test conversion by hexlifying in javascript.
|
||||
|
||||
Since the conversion of QStrings to Python strings is broken in some
|
||||
older PyQt versions in some corner cases, we load a HTML file which
|
||||
generates an MD5 of the escaped text and use that for comparisons.
|
||||
"""
|
||||
escaped = webelem.javascript_escape(text)
|
||||
webframe.evaluateJavaScript('window.foo = "{}";'.format(escaped))
|
||||
assert webframe.evaluateJavaScript('window.foo') == text
|
||||
path = os.path.join(os.path.dirname(__file__),
|
||||
'test_webelem_jsescape.html')
|
||||
with open(path, encoding='utf-8') as f:
|
||||
html_source = f.read().replace('%INPUT%', escaped)
|
||||
|
||||
with qtbot.waitSignal(webframe.loadFinished, raising=True):
|
||||
webframe.setHtml(html_source)
|
||||
|
||||
result = webframe.evaluateJavaScript('window.qute_test_result')
|
||||
assert result is not None
|
||||
assert '|' in result
|
||||
result_md5, result_text = result.split('|', maxsplit=1)
|
||||
text_md5 = binascii.hexlify(text.encode('utf-8')).decode('ascii')
|
||||
assert result_md5 == text_md5, result_text
|
||||
|
||||
def _test_escape_simple(self, text, webframe):
|
||||
"""Test conversion by using evaluateJavaScript."""
|
||||
escaped = webelem.javascript_escape(text)
|
||||
result = webframe.evaluateJavaScript('"{}";'.format(escaped))
|
||||
assert result == text
|
||||
|
||||
@pytest.mark.parametrize('text', TESTS)
|
||||
def test_real_escape(self, webframe, text):
|
||||
def test_real_escape(self, webframe, qtbot, text):
|
||||
"""Test javascript escaping with a real QWebPage."""
|
||||
self._test_escape(text, webframe)
|
||||
self._test_escape(text, qtbot, webframe)
|
||||
|
||||
@hypothesis.given(hypothesis.strategies.text())
|
||||
def test_real_escape_hypothesis(self, webframe, text):
|
||||
def test_real_escape_hypothesis(self, webframe, qtbot, text):
|
||||
"""Test javascript escaping with a real QWebPage and hypothesis."""
|
||||
self._test_escape(text, webframe)
|
||||
# We can't simply use self._test_escape because of this:
|
||||
# https://github.com/pytest-dev/pytest-qt/issues/69
|
||||
|
||||
# self._test_escape(text, qtbot, webframe)
|
||||
try:
|
||||
self._test_escape_simple(text, webframe)
|
||||
except AssertionError:
|
||||
if PYQT_VERSION >= 0x050300:
|
||||
self._test_escape_hexlified(text, qtbot, webframe)
|
||||
|
||||
|
||||
class TestGetChildFrames:
|
||||
|
68
tests/browser/test_webelem_jsescape.html
Normal file
68
tests/browser/test_webelem_jsescape.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!--
|
||||
Helper file for test_javascript_escape() in test_webelem.py.
|
||||
|
||||
Since the conversion from QStrings to Python strings is broken in some corner
|
||||
cases in PyQt < 5.4 we hexlify the string we got in javascript here and test
|
||||
that in the test.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
|
||||
/*
|
||||
* hexlify() and str2rstr_utf8() are based on:
|
||||
*
|
||||
* JavaScript MD5 1.0.1
|
||||
* https://github.com/blueimp/JavaScript-MD5
|
||||
*
|
||||
* Copyright 2011, Sebastian Tschan
|
||||
* https://blueimp.net
|
||||
*
|
||||
* Licensed under the MIT license:
|
||||
* http://www.opensource.org/licenses/MIT
|
||||
*
|
||||
* Based on
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function hexlify(input) {
|
||||
var hex_tab = '0123456789abcdef';
|
||||
var output = '';
|
||||
var x;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < input.length; i += 1) {
|
||||
x = input.charCodeAt(i);
|
||||
output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function encode_utf8(input) {
|
||||
return unescape(encodeURIComponent(input));
|
||||
}
|
||||
|
||||
function set_text() {
|
||||
var elems = document.getElementsByTagName("p");
|
||||
var hexlified = hexlify(encode_utf8("%INPUT%"));
|
||||
var result = hexlified + "|" + "%INPUT%";
|
||||
elems[0].innerHTML = result
|
||||
window.qute_test_result = result;
|
||||
}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
</head>
|
||||
<body onload="set_text()">
|
||||
<p>set_text() not called...</p>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user