diff --git a/.appveyor.yml b/.appveyor.yml index 0f970d98b..d3a790aaa 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,11 +7,14 @@ environment: PYTHONUNBUFFERED: 1 matrix: - TESTENV: py34 + - TESTENV: py36-pyqt58 + PYTHON: C:\Python36\python.exe - TESTENV: unittests-frozen - TESTENV: pylint install: - C:\Python27\python -u scripts\dev\ci\appveyor_install.py + - set PATH=%PATH%;C:\Python36 test_script: - C:\Python34\Scripts\tox -e %TESTENV% diff --git a/.flake8 b/.flake8 index 7bfc34c0a..d967a505b 100644 --- a/.flake8 +++ b/.flake8 @@ -35,9 +35,9 @@ max-complexity = 12 putty-auto-ignore = True putty-ignore = /# pylint: disable=invalid-name/ : +N801,N806 - /# pylint: disable=wildcard-import/ : +F403 /# pragma: no mccabe/ : +C901 tests/*/test_*.py : +D100,D101,D401 + tests/conftest.py : +F403 tests/unit/browser/webkit/test_history.py : +N806 tests/helpers/fixtures.py : +N806 tests/unit/browser/webkit/http/test_content_disposition.py : +D400 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9ea69b642..b9bf8d399 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,2 +1,2 @@ +`:open qute:version` or `qutebrowser --version` --> diff --git a/.pylintrc b/.pylintrc index 3f06facaf..c94058297 100644 --- a/.pylintrc +++ b/.pylintrc @@ -38,7 +38,8 @@ disable=no-self-use, suppressed-message, too-many-return-statements, duplicate-code, - wrong-import-position + wrong-import-position, + no-else-return [BASIC] function-rgx=[a-z_][a-z0-9_]{2,50}$ diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3831c9e42..bf01a2a17 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -21,18 +21,57 @@ Added ~~~~~ - New `:clear-messages` command to clear shown messages. +- New `ui -> keyhint-delay` setting to configure the delay until + the keyhint overlay pops up. +- New `-s` option for `:open` to force a HTTPS scheme. +- `:debug-log-filter` now accepts `none` as an argument to clear any log + filters. +- New `--debug-flag` argument which replaces `--debug-exit` and + `--pdb-postmortem`. +- New `tabs -> favicon-scale` option to scale up/down favicons. Changed ~~~~~~~ +- To prevent elaborate phishing attacks, the Punycode version is now shown in + addition to the decoded version for IDN domain names. - When using QtWebEngine, the underlying Chromium version is now shown in the version info. +- Improved `qute:history` page with lazy loading +- Messages are now hidden when clicked +- Paths like `C:` are now treated as absolute paths on Windows for downloads, + and invalid paths are handled properly. +- PAC on QtWebKit now supports SOCKS5 as type. +- Comments in the config file are now before the individual + options instead of being before sections. +- The HTTP cache is disabled with QtWebKit on Qt 5.7.1 and 5.8 now as it leads + to frequent crashes due to a Qt bug. +- stdin is now closed immediately for processes spawned from qutebrowser +- When ui -> message-timeout is set to 0, messages are now never cleared. +- Middle/right-clicking the blank parts of the tab bar (when vertical) now + closes the current tab. +- With Qt 5.9, `content -> cookies-store` can now be set on QtWebEngine without + a restart. +- The adblocker now also blocks non-GET requests (e.g. POST) +- `:follow-selected` now also works with QtWebEngine Fixed ~~~~~ - Added a workaround for a black screen with QtWebEngine with some setups - (requires PyOpenGL to be installed) + (the workaround requires PyOpenGL to be installed, but it's optional) +- Crash when trying to retry downloads with QtWebEngine +- Crash when cloning page without history +- Continuing a search after clearing it +- Crash when downloading a download resulting in a HTTP error +- Crash when pressing ctrl-c while a config error is shown +- Crash when the key config isn't writable +- Crash when unbinding an unbound key in the key config +- Crash when using `:debug-log-filter` when `--filter` wasn't given on startup. +- Crash with some invalid setting values +- Crash when cloning a view-source tab with QtWebEngine +- Various rare crashes +- Various styling issues with the tabbar and a crash with qt5ct v0.10.1 ------- diff --git a/CONTRIBUTING.asciidoc b/CONTRIBUTING.asciidoc index 12d52fee7..25a7a8a13 100644 --- a/CONTRIBUTING.asciidoc +++ b/CONTRIBUTING.asciidoc @@ -42,6 +42,12 @@ be easy to solve] * https://github.com/qutebrowser/qutebrowser/labels/not%20code[Issues which require little/no coding] +If you prefer C++ or Javascript to Python, see the relevant issues which involve +work in those languages: + +* https://github.com/qutebrowser/qutebrowser/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue%20label%3Ac%2B%2B[C++] (mostly work on Qt, the library behind qutebrowser) +* https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3Ajavascript[JavaScript] + There are also some things to do if you don't want to write code: * Help the community, e.g., on the mailinglist and the IRC channel. diff --git a/FAQ.asciidoc b/FAQ.asciidoc index 71d5f2834..c934d7e64 100644 --- a/FAQ.asciidoc +++ b/FAQ.asciidoc @@ -124,6 +124,34 @@ When using quickmark, you can give them all names, like `:open foodrecipes`, you will see a list of all the food recipe sites, without having to remember the exact website title or address. +How do I use spell checking?:: + Qutebrowser's support for spell checking is somewhat limited at the moment + (see https://github.com/qutebrowser/qutebrowser/issues/700[#700]), but it + can be done. ++ +For QtWebKit: + +. Install https://github.com/QupZilla/qtwebkit-plugins[qtwebkit-plugins]. + . Note: with QtWebKit reloaded you may experience some issues. See + https://github.com/QupZilla/qtwebkit-plugins/issues/10[#10]. +. The dictionary to use is taken from the `DICTIONARY` environment variable. + The default is `en_US`. For example to use Dutch spell check set `DICTIONARY` + to `nl_NL`; you can't use multiple dictionaries or change them at runtime at + the moment. + (also see the README file for `qtwebkit-plugins`). +. Remember to install the hunspell dictionaries if you don't have them already + (most distros should have packages for this). + ++ +For QtWebEngine: + +. Not yet supported unfortunately :-( + + Adding it shouldn't be too hard though, since QtWebEngine 5.8 added an API for + this (see + https://github.com/qutebrowser/qutebrowser/issues/700#issuecomment-290780706[this + comment for a basic example]), so what are you waiting for and why aren't you + hacking qutebrowser yet? + == Troubleshooting Configuration not saved after modifying config.:: diff --git a/INSTALL.asciidoc b/INSTALL.asciidoc index 21bf4176b..651082bee 100644 --- a/INSTALL.asciidoc +++ b/INSTALL.asciidoc @@ -135,12 +135,42 @@ If video or sound don't seem to work, try installing the gstreamer plugins: On Gentoo --------- -qutebrowser is available in the main repository and can be installed with: +A version of qutebrowser is available in the main repository and can be installed with: ---- # emerge -av qutebrowser ---- +However it is suggested to install the Live version (-9999) to take advantage +of the newest features introduced. + +First of all you need to edit your package.accept_keywords file to accept the live +version: + +---- +# nano /etc/portage/package.accept_keywords +---- + +And add the following line to it: + + =www-client/qutebrowser-9999 ** + +Save the file and then install qutebrowser via + +---- +# emerge -av qutebrowser +---- + +Or rebuild your system if you already installed it. + +To update to the last Live version, remember to do + +---- +# emerge -uDNav @live-rebuild @world +---- + +To include qutebrowser among the updates. + Make sure you have `python3_4` in your `PYTHON_TARGETS` (`/etc/portage/make.conf`) and rebuild your system (`emerge -uDNav @world`) if necessary. @@ -192,19 +222,19 @@ On OpenBSD qutebrowser is in http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/www/qutebrowser/[OpenBSD ports]. -Manual install: +Install the package: + +---- +# pkg_add qutebrowser +---- + +Or alternatively, use the ports system : ---- # cd /usr/ports/www/qutebrowser # make install ---- -Or alternatively if you're using `-current` (or OpenBSD 6.1 once it's been released): - ----- -# pkg_add qutebrowser ----- - On Windows ---------- diff --git a/README.asciidoc b/README.asciidoc index eae5a07c8..215e2e437 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -24,6 +24,13 @@ on Python and PyQt5 and free software, licensed under the GPL. It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl. +// QUTE_WEB_HIDE +**qutebrowser is currently running a crowdfunding campaign for its new +configuration system, allowing for per-domain settings and much more.** + +See the link:https://www.kickstarter.com/projects/the-compiler/qutebrowser-v10-with-per-domain-settings?ref=6zw7qz[Kickstarter campaign] for more information! +// QUTE_WEB_HIDE_END + Screenshots ----------- @@ -71,7 +78,8 @@ 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[]. +at mailto:qutebrowser-announce@lists.qutebrowser.org[] (the announcements also +get sent to the general qutebrowser@ list). Contributions / Bugs -------------------- @@ -152,38 +160,41 @@ Contributors, sorted by the number of commits in descending order: * Lamar Pavel * Marshall Lochbaum * Bruno Oliveira +* Martin Tournoij +* Imran Sobir * Alexander Cogneau * Felix Van der Jeugt * Daniel Karbach -* Martin Tournoij * Kevin Velghe * Raphael Pierzina * Joel Torstensson * Patric Schmitz * Tarcisio Fedrizzi * Claude +* Fritz Reichwald * Corentin Julé * meles5 * Philipp Hansch -* Imran Sobir * Panagiotis Ktistakis * Artur Shaik * Nathan Isom * Thorsten Wißmann * Austin Anderson -* Fritz Reichwald * Jimmy * Niklas Haas * Maciej Wołczyk -* Spreadyy +* sandrosc * Alexey "Averrin" Nabrodov +* pkill9 * nanjekyejoannah * avk * ZDarian * Milan Svoboda * John ShaggyTwoDope Jenkins +* Jay Kamat * Clayton Craft * Peter Vilim +* Jacob Sword * knaggita * Oliver Caldwell * Julian Weigt @@ -205,7 +216,6 @@ Contributors, sorted by the number of commits in descending order: * David Vogt * Claire Cavanaugh * rikn00 -* pkill9 * kanikaa1234 * haitaka * Nick Ginther @@ -239,8 +249,10 @@ Contributors, sorted by the number of commits in descending order: * adam * Samir Benmendil * Regina Hug +* Penaz * Mathias Fussenegger * Marcelo Santos +* Marcel Schilling * Joel Bradshaw * Jean-Louis Fuchs * Franz Fellner @@ -253,6 +265,7 @@ Contributors, sorted by the number of commits in descending order: * haxwithaxe * evan * dylan araps +* caveman * addictedtoflames * Xitian9 * Vasilij Schneidermann @@ -261,19 +274,18 @@ Contributors, sorted by the number of commits in descending order: * Tobias Werth * Tim Harder * Thiago Barroso Perrotta +* Steve Peak * Sorokin Alexei * Simon Désaulniers * Rok Mandeljc * Noah Huesser * Moez Bouhlel * Matthias Lisin -* Marcel Schilling * Lazlow Carmichael * Kevin Wang * Ján Kobezda * Johannes Martinsson * Jean-Christophe Petkovich -* Jay Kamat * Helen Sherwood-Taylor * HalosGhost * Gregor Pohl @@ -281,9 +293,11 @@ Contributors, sorted by the number of commits in descending order: * Dietrich Daroch * Derek Sivers * Daniel Lu +* Daniel Jakots * Arseniy Seroka * Andy Balaam * Andreas Fischer +* Amos Bird * Akselmo // QUTE_AUTHORS_END diff --git a/codecov.yml b/codecov.yml index 6d4f1abce..47e3c919c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,9 +1,7 @@ -status: - project: - enabled: no - patch: - enabled: no - changes: - enabled: no +coverage: + status: + project: off + patch: off + changes: off comment: off diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 02427b97b..c79d83d08 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -544,7 +544,7 @@ For `increment` and `decrement`, the number to change the URL by. For `up`, the [[open]] === open -Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] ['url']+ +Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] [*--secure*] ['url']+ Open a URL in the current/[count]th tab. @@ -559,6 +559,7 @@ If the URL contains newlines, each line gets opened in its own tab. * +*-b*+, +*--bg*+: Open in a new background tab. * +*-t*+, +*--tab*+: Open in a new tab. * +*-w*+, +*--window*+: Open in a new window. +* +*-s*+, +*--secure*+: Force HTTPS. ==== count The tab index to open the URL in. @@ -1598,7 +1599,8 @@ Syntax: +:debug-log-filter 'filters'+ Change the log filter for console logging. ==== positional arguments -* +'filters'+: A comma separated list of logger names. +* +'filters'+: A comma separated list of logger names. Can also be "none" to clear any existing filters. + [[debug-log-level]] === debug-log-level @@ -1653,7 +1655,7 @@ Syntax: +:debug-webaction 'action'+ Execute a webaction. -See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the available actions. +Available actions: http://doc.qt.io/archives/qt-5.5/qwebpage.html#WebAction-enum (WebKit) http://doc.qt.io/qt-5/qwebenginepage.html#WebAction-enum (WebEngine) ==== positional arguments * +'action'+: The action to execute, e.g. MoveToNextChar. diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index 78b5a7022..b2479f959 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -36,6 +36,7 @@ [options="header",width="75%",cols="25%,75%"] |============== |Setting|Description +|<>|The maximum time in minutes between two history items for them to be considered being from the same session. Use -1 to disable separation. |<>|The available zoom levels, separated by commas. |<>|The default zoom level. |<>|Where to show the downloaded files. @@ -56,6 +57,7 @@ |<>|Use standard JavaScript modal dialog for alert() and confirm() |<>|Hide the window decoration when using wayland (requires restart) |<>|Keychains that shouldn't be shown in the keyhint dialog +|<>|Time from pressing a key to seeing the keyhint dialog (ms) |<>|The rounding radius for the edges of prompts. |<>|Show a filebrowser in upload/download prompts. |============== @@ -124,6 +126,7 @@ |<>|On which mouse button to close tabs. |<>|The position of the tab bar. |<>|Whether to show favicons in the tab bar. +|<>|Scale for favicons in the tab bar. The tab size is unchanged, so big favicons also require extra `tabs->padding`. |<>|The width of the tab bar if it's vertical, in px or as percentage of the window. |<>|Width of the progress indicator (0 to disable). |<>|Whether to open windows instead of tabs. @@ -172,7 +175,7 @@ |<>|Whether locally loaded documents are allowed to access remote urls. |<>|Whether locally loaded documents are allowed to access other local urls. |<>|Control which cookies to accept. -|<>|Whether to store cookies. Note this option needs a restart with QtWebEngine. +|<>|Whether to store cookies. Note this option needs a restart with QtWebEngine on Qt < 5.9. |<>|List of URLs of lists which contain hosts to block. |<>|Whether host blocking is enabled. |<>|List of domains that should always be loaded, despite being ad-blocked. @@ -536,6 +539,12 @@ Default: +pass:[path,query]+ == ui General options related to the user interface. +[[ui-history-session-interval]] +=== history-session-interval +The maximum time in minutes between two history items for them to be considered being from the same session. Use -1 to disable separation. + +Default: +pass:[30]+ + [[ui-zoom-levels]] === zoom-levels The available zoom levels, separated by commas. @@ -573,6 +582,7 @@ Default: +pass:[bottom]+ [[ui-message-timeout]] === message-timeout Time (in ms) to show messages in the statusbar for. +Set to 0 to never clear messages. Default: +pass:[2000]+ @@ -732,6 +742,12 @@ Globs are supported, so ';*' will blacklist all keychainsstarting with ';'. Use Default: empty +[[ui-keyhint-delay]] +=== keyhint-delay +Time from pressing a key to seeing the keyhint dialog (ms) + +Default: +pass:[500]+ + [[ui-prompt-radius]] === prompt-radius The rounding radius for the edges of prompts. @@ -1191,6 +1207,12 @@ Valid values: Default: +pass:[true]+ +[[tabs-favicon-scale]] +=== favicon-scale +Scale for favicons in the tab bar. The tab size is unchanged, so big favicons also require extra `tabs->padding`. + +Default: +pass:[1.0]+ + [[tabs-width]] === width The width of the tab bar if it's vertical, in px or as percentage of the window. @@ -1594,7 +1616,7 @@ This setting is only available with the QtWebKit backend. [[content-cookies-store]] === cookies-store -Whether to store cookies. Note this option needs a restart with QtWebEngine. +Whether to store cookies. Note this option needs a restart with QtWebEngine on Qt < 5.9. Valid values: @@ -2252,7 +2274,7 @@ Fonts used for the UI, with optional style/weight/size. === _monospace Default monospace fonts. -Default: +pass:[Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal]+ +Default: +pass:[xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal]+ [[fonts-completion]] === completion diff --git a/doc/img/completion.png b/doc/img/completion.png index 9ba30877e..84a49828f 100644 Binary files a/doc/img/completion.png and b/doc/img/completion.png differ diff --git a/doc/img/downloads.png b/doc/img/downloads.png index 530210c7f..cf2e6951d 100644 Binary files a/doc/img/downloads.png and b/doc/img/downloads.png differ diff --git a/doc/img/hints.png b/doc/img/hints.png index 812af3581..3407ddf47 100644 Binary files a/doc/img/hints.png and b/doc/img/hints.png differ diff --git a/doc/img/main.png b/doc/img/main.png index b371c5fba..85da51a2f 100644 Binary files a/doc/img/main.png and b/doc/img/main.png differ diff --git a/doc/qutebrowser.1.asciidoc b/doc/qutebrowser.1.asciidoc index 36530bffe..cd95bf8e5 100644 --- a/doc/qutebrowser.1.asciidoc +++ b/doc/qutebrowser.1.asciidoc @@ -93,12 +93,6 @@ show it. *--nowindow*:: Don't show the main window. -*--debug-exit*:: - Turn on debugging of late exit. - -*--pdb-postmortem*:: - Drop into pdb on exceptions. - *--temp-basedir*:: Use a temporary basedir. @@ -110,6 +104,9 @@ show it. *--qt-flag* 'QT_FLAG':: Pass an argument to Qt as flag. + +*--debug-flag* 'DEBUG_FLAGS':: + Pass name of debugging feature to be turned on. // QUTE_OPTIONS_END == FILES diff --git a/misc/requirements/requirements-codecov.txt b/misc/requirements/requirements-codecov.txt index 4f020b5cd..5fee39d9a 100644 --- a/misc/requirements/requirements-codecov.txt +++ b/misc/requirements/requirements-codecov.txt @@ -1,5 +1,5 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -codecov==2.0.5 -coverage==4.3.4 -requests==2.13.0 +codecov==2.0.9 +coverage==4.4 +requests==2.14.1 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index 9c0aad110..8cd1eb296 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -4,7 +4,7 @@ flake8==2.6.2 # rq.filter: < 3.0.0 flake8-copyright==0.2.0 flake8-debugger==1.4.0 # rq.filter: != 2.0.0 flake8-deprecated==1.1 -flake8-docstrings==1.0.3 +flake8-docstrings==1.0.3 # rq.filter: < 1.1.0 flake8-future-import==0.4.3 flake8-mock==0.3 flake8-pep3101==1.0 @@ -12,9 +12,9 @@ flake8-polyfill==1.0.1 flake8-putty==0.4.0 flake8-string-format==0.2.3 flake8-tidy-imports==1.0.6 -flake8-tuple==0.2.12 +flake8-tuple==0.2.13 mccabe==0.6.1 pep8-naming==0.4.1 pycodestyle==2.3.1 -pydocstyle==1.1.1 +pydocstyle==1.1.1 # rq.filter: < 2.0.0 pyflakes==1.5.0 diff --git a/misc/requirements/requirements-flake8.txt-raw b/misc/requirements/requirements-flake8.txt-raw index cf660a8ce..aadae5e8b 100644 --- a/misc/requirements/requirements-flake8.txt-raw +++ b/misc/requirements/requirements-flake8.txt-raw @@ -2,7 +2,7 @@ flake8<3.0.0 flake8-copyright flake8-debugger!=2.0.0 flake8-deprecated -flake8-docstrings +flake8-docstrings<1.1.0 flake8-future-import flake8-mock flake8-pep3101 @@ -11,7 +11,7 @@ flake8-string-format flake8-tidy-imports flake8-tuple pep8-naming -pydocstyle +pydocstyle<2.0.0 pyflakes # Pinned to 2.0.0 otherwise @@ -21,6 +21,8 @@ mccabe==0.6.1 # Waiting until flake8-putty updated #@ filter: flake8 < 3.0.0 +#@ filter: pydocstyle < 2.0.0 +#@ filter: flake8-docstrings < 1.1.0 # https://github.com/JBKahn/flake8-debugger/issues/5 #@ filter: flake8-debugger != 2.0.0 diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt index c35abcf09..1eb264be8 100644 --- a/misc/requirements/requirements-pip.txt +++ b/misc/requirements/requirements-pip.txt @@ -3,6 +3,6 @@ appdirs==1.4.3 packaging==16.8 pyparsing==2.2.0 -setuptools==34.3.2 +setuptools==35.0.2 six==1.10.0 wheel==0.29.0 diff --git a/misc/requirements/requirements-pylint-master.txt b/misc/requirements/requirements-pylint-master.txt index 64963a705..22622f14c 100644 --- a/misc/requirements/requirements-pylint-master.txt +++ b/misc/requirements/requirements-pylint-master.txt @@ -2,10 +2,13 @@ -e git+https://github.com/PyCQA/astroid.git#egg=astroid editdistance==0.3.1 +github3.py==0.9.6 isort==4.2.5 -lazy-object-proxy==1.2.2 +lazy-object-proxy==1.3.1 mccabe==0.6.1 -e git+https://github.com/PyCQA/pylint.git#egg=pylint ./scripts/dev/pylint_checkers -requests==2.13.0 +requests==2.14.1 +uritemplate==3.0.0 +uritemplate.py==3.0.2 wrapt==1.10.10 diff --git a/misc/requirements/requirements-pylint-master.txt-raw b/misc/requirements/requirements-pylint-master.txt-raw index 50c76d902..a963803d3 100644 --- a/misc/requirements/requirements-pylint-master.txt-raw +++ b/misc/requirements/requirements-pylint-master.txt-raw @@ -2,6 +2,7 @@ -e git+https://github.com/PyCQA/pylint.git#egg=pylint ./scripts/dev/pylint_checkers requests +github3.py # remove @commit-id for scm installs #@ replace: @.*# # diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index 12d94c8eb..2c6cf7729 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -1,13 +1,13 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -astroid==1.4.9 +astroid==1.5.2 github3.py==0.9.6 isort==4.2.5 -lazy-object-proxy==1.2.2 +lazy-object-proxy==1.3.1 mccabe==0.6.1 -pylint==1.6.5 +pylint==1.7.1 ./scripts/dev/pylint_checkers -requests==2.13.0 +requests==2.14.1 uritemplate==3.0.0 uritemplate.py==3.0.2 wrapt==1.10.10 diff --git a/misc/requirements/requirements-pyqt.txt b/misc/requirements/requirements-pyqt.txt index 774c0d79c..da611589a 100644 --- a/misc/requirements/requirements-pyqt.txt +++ b/misc/requirements/requirements-pyqt.txt @@ -1,4 +1,4 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -PyQt5==5.8.1.1 -sip==4.19.1 +PyQt5==5.8.2 +sip==4.19.2 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index f00f11f7c..e78b98e5d 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -1,15 +1,15 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -beautifulsoup4==4.5.3 -cheroot==5.3.0 +beautifulsoup4==4.6.0 +cheroot==5.5.0 click==6.7 -coverage==4.3.4 +coverage==4.4 decorator==4.0.11 EasyProcess==0.2.3 -Flask==0.12 +Flask==0.12.1 glob2==0.5 httpbin==0.5.0 -hypothesis==3.6.1 +hypothesis==3.8.3 itsdangerous==0.24 # Jinja2==2.9.5 Mako==1.0.6 @@ -18,13 +18,13 @@ parse==1.8.0 parse-type==0.3.4 py==1.4.33 pytest==3.0.7 -pytest-bdd==2.18.1 +pytest-bdd==2.18.2 pytest-benchmark==3.0.0 pytest-catchlog==1.2.2 -pytest-cov==2.4.0 +pytest-cov==2.5.0 pytest-faulthandler==1.3.1 pytest-instafail==0.3.0 -pytest-mock==1.5.0 +pytest-mock==1.6.0 pytest-qt==2.1.0 pytest-repeat==0.4.1 pytest-rerunfailures==2.1.0 @@ -32,5 +32,5 @@ pytest-travis-fold==1.2.0 pytest-warnings==0.2.0 pytest-xvfb==1.0.0 PyVirtualDisplay==0.2.1 -vulture==0.13 +vulture==0.14 Werkzeug==0.12.1 diff --git a/misc/requirements/requirements-tox.txt b/misc/requirements/requirements-tox.txt index df38c7fca..59f0806bf 100644 --- a/misc/requirements/requirements-tox.txt +++ b/misc/requirements/requirements-tox.txt @@ -2,5 +2,5 @@ pluggy==0.4.0 py==1.4.33 -tox==2.6.0 +tox==2.7.0 virtualenv==15.1.0 diff --git a/misc/requirements/requirements-vulture.txt b/misc/requirements/requirements-vulture.txt index 26ee55a6e..56e20c603 100644 --- a/misc/requirements/requirements-vulture.txt +++ b/misc/requirements/requirements-vulture.txt @@ -1,3 +1,3 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -vulture==0.13 +vulture==0.14 diff --git a/misc/userscripts/dmenu_qutebrowser b/misc/userscripts/dmenu_qutebrowser index dbc7c05bf..6917dae98 100755 --- a/misc/userscripts/dmenu_qutebrowser +++ b/misc/userscripts/dmenu_qutebrowser @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Copyright 2015 Zach-Button +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/misc/userscripts/openfeeds b/misc/userscripts/openfeeds index 085bdbe67..8bc4c2d33 100755 --- a/misc/userscripts/openfeeds +++ b/misc/userscripts/openfeeds @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # Copyright 2015 jnphilipp +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # 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/misc/userscripts/qutebrowser_viewsource b/misc/userscripts/qutebrowser_viewsource index b528c41e8..a8ad71de3 100755 --- a/misc/userscripts/qutebrowser_viewsource +++ b/misc/userscripts/qutebrowser_viewsource @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Copyright 2015 Zach-Button +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/pytest.ini b/pytest.ini index bd57e7854..905bca52c 100644 --- a/pytest.ini +++ b/pytest.ini @@ -24,11 +24,12 @@ markers = js_prompt: Tests needing to display a javascript prompt this: Used to mark tests during development no_invalid_lines: Don't fail on unparseable lines in end2end tests + issue2478: Tests which are broken on Windows with QtWebEngine, https://github.com/qutebrowser/qutebrowser/issues/2478 qt_log_level_fail = WARNING qt_log_ignore = ^SpellCheck: .* ^SetProcessDpiAwareness failed: .* - ^QWindowsWindow::setGeometryDp: Unable to set geometry .* + ^QWindowsWindow::setGeometry(Dp)?: Unable to set geometry .* ^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" @@ -51,4 +52,5 @@ qt_log_ignore = ^Image of format '' blocked because it is not considered safe. If you are sure it is safe to do so, you can white-list the format by setting the environment variable QTWEBKIT_IMAGEFORMAT_WHITELIST= ^QPainter::end: Painter ended with \d+ saved states ^QSslSocket: cannot resolve SSLv[23]_(client|server)_method + ^QQuickWidget::invalidateRenderControl could not make context current xfail_strict = true diff --git a/qutebrowser/__init__.py b/qutebrowser/__init__.py index 348cf407a..e61419c0c 100644 --- a/qutebrowser/__init__.py +++ b/qutebrowser/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -22,7 +22,7 @@ import os.path __author__ = "Florian Bruhin" -__copyright__ = "Copyright 2014-2016 Florian Bruhin (The Compiler)" +__copyright__ = "Copyright 2014-2017 Florian Bruhin (The Compiler)" __license__ = "GPL" __maintainer__ = __author__ __email__ = "mail@qutebrowser.org" diff --git a/qutebrowser/__main__.py b/qutebrowser/__main__.py index 4f977d2d2..506039890 100644 --- a/qutebrowser/__main__.py +++ b/qutebrowser/__main__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 73215cbfe..cca9e1a76 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -170,12 +170,15 @@ def _init_icon(): for size in [16, 24, 32, 48, 64, 96, 128, 256, 512]: filename = ':/icons/qutebrowser-{}x{}.png'.format(size, size) pixmap = QPixmap(filename) - qtutils.ensure_not_null(pixmap) - fallback_icon.addPixmap(pixmap) - qtutils.ensure_not_null(fallback_icon) + if pixmap.isNull(): + log.init.warning("Failed to load {}".format(filename)) + else: + fallback_icon.addPixmap(pixmap) icon = QIcon.fromTheme('qutebrowser', fallback_icon) - qtutils.ensure_not_null(icon) - qApp.setWindowIcon(icon) + if icon.isNull(): + log.init.warning("Failed to load icon") + else: + qApp.setWindowIcon(icon) def _process_args(args): @@ -301,7 +304,7 @@ def _open_startpage(win_id=None): window_ids = [win_id] else: window_ids = objreg.window_registry - for cur_win_id in window_ids: + for cur_win_id in list(window_ids): # Copying as the dict could change tabbed_browser = objreg.get('tabbed-browser', scope='window', window=cur_win_id) if tabbed_browser.count() == 0: @@ -340,8 +343,9 @@ def _open_quickstart(args): def _save_version(): """Save the current version to the state config.""" - state_config = objreg.get('state-config') - state_config['general']['version'] = qutebrowser.__version__ + state_config = objreg.get('state-config', None) + if state_config is not None: + state_config['general']['version'] = qutebrowser.__version__ def on_focus_changed(_old, new): @@ -647,14 +651,14 @@ class Quitter: self._shutting_down = True log.destroy.debug("Shutting down with status {}, session {}...".format( status, session)) - - session_manager = objreg.get('session-manager') - if session is not None: - session_manager.save(session, last_window=last_window, - load_next_time=True) - elif config.get('general', 'save-session'): - session_manager.save(sessions.default, last_window=last_window, - load_next_time=True) + session_manager = objreg.get('session-manager', None) + if session_manager is not None: + if session is not None: + session_manager.save(session, last_window=last_window, + load_next_time=True) + elif config.get('general', 'save-session'): + session_manager.save(sessions.default, last_window=last_window, + load_next_time=True) if prompt.prompt_queue.shutdown(): # If shutdown was called while we were asking a question, we're in @@ -671,7 +675,7 @@ class Quitter: # event loop, so we can shut down immediately. self._shutdown(status, restart=restart) - def _shutdown(self, status, restart): + def _shutdown(self, status, restart): # noqa """Second stage of shutdown.""" log.destroy.debug("Stage 2 of shutting down...") if qApp is None: @@ -680,7 +684,9 @@ class Quitter: # Remove eventfilter try: log.destroy.debug("Removing eventfilter...") - qApp.removeEventFilter(objreg.get('event-filter')) + event_filter = objreg.get('event-filter', None) + if event_filter is not None: + qApp.removeEventFilter(event_filter) except AttributeError: pass # Close all windows @@ -722,7 +728,9 @@ class Quitter: # Now we can hopefully quit without segfaults log.destroy.debug("Deferring QApplication::exit...") objreg.get('signal-handler').deactivate() - objreg.get('session-manager').delete_autosave() + session_manager = objreg.get('session-manager', None) + if session_manager is not None: + session_manager.delete_autosave() # We use a singleshot timer to exit here to minimize the likelihood of # segfaults. QTimer.singleShot(0, functools.partial(qApp.exit, status)) @@ -784,7 +792,7 @@ class Application(QApplication): def exit(self, status): """Extend QApplication::exit to log the event.""" log.destroy.debug("Now calling QApplication::exit.") - if self._args.debug_exit: + if 'debug-exit' in self._args.debug_flags: if hunter is None: print("Not logging late shutdown because hunter could not be " "imported!", file=sys.stderr) diff --git a/qutebrowser/browser/__init__.py b/qutebrowser/browser/__init__.py index dbc790589..c5d5e6c92 100644 --- a/qutebrowser/browser/__init__.py +++ b/qutebrowser/browser/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/adblock.py b/qutebrowser/browser/adblock.py index a7d0d43bb..276dec08b 100644 --- a/qutebrowser/browser/adblock.py +++ b/qutebrowser/browser/adblock.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 48aacf086..96e0cb838 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -21,7 +21,7 @@ import itertools -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QWidget, QApplication @@ -107,7 +107,15 @@ class TabData: class AbstractAction: - """Attribute of AbstractTab for Qt WebActions.""" + """Attribute of AbstractTab for Qt WebActions. + + Class attributes (overridden by subclasses): + action_class: The class actions are defined on (QWeb{Engine,}Page) + action_base: The type of the actions (QWeb{Engine,}Page.WebAction) + """ + + action_class = None + action_base = None def __init__(self): self._widget = None @@ -120,6 +128,13 @@ class AbstractAction: """Save the current page.""" raise NotImplementedError + def run_string(self, name): + """Run a webaction based on its name.""" + member = getattr(self.action_class, name, None) + if not isinstance(member, self.action_base): + raise WebTabError("{} is not a valid web action!".format(name)) + self._widget.triggerPageAction(member) + class AbstractPrinting: @@ -157,6 +172,8 @@ class AbstractSearch(QObject): Attributes: text: The last thing this view was searched for. + search_displayed: Whether we're currently displaying search results in + this view. _flags: The flags of the last search (needs to be set by subclasses). _widget: The underlying WebView widget. """ @@ -165,6 +182,7 @@ class AbstractSearch(QObject): super().__init__(parent) self._widget = None self.text = None + self.search_displayed = False def search(self, text, *, ignore_case=False, reverse=False, result_cb=None): @@ -742,6 +760,10 @@ class AbstractTab(QWidget): def clear_ssl_errors(self): raise NotImplementedError + def key_press(self, key, modifier=Qt.NoModifier): + """Send a fake key event to this tab.""" + raise NotImplementedError + def dump_async(self, callback, *, plain=False): """Dump the current page to a file ascync. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index e6f6571c0..f55e77c6f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -28,14 +28,6 @@ from PyQt5.QtWidgets import QApplication, QTabBar from PyQt5.QtCore import Qt, QUrl, QEvent, QUrlQuery from PyQt5.QtGui import QKeyEvent from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog -try: - from PyQt5.QtWebKitWidgets import QWebPage -except ImportError: - QWebPage = None -try: - from PyQt5.QtWebEngineWidgets import QWebEnginePage -except ImportError: - QWebEnginePage = None import pygments import pygments.lexers import pygments.formatters @@ -289,7 +281,7 @@ class CommandDispatcher: @cmdutils.argument('url', completion=usertypes.Completion.url) @cmdutils.argument('count', count=True) def openurl(self, url=None, implicit=False, - bg=False, tab=False, window=False, count=None): + bg=False, tab=False, window=False, count=None, secure=False): """Open a URL in the current/[count]th tab. If the URL contains newlines, each line gets opened in its own tab. @@ -302,6 +294,7 @@ class CommandDispatcher: implicit: If opening a new tab, treat the tab as implicit (like clicking on a link). count: The tab index to open the URL in, or None. + secure: Force HTTPS. """ if url is None: urls = [config.get('general', 'default-page')] @@ -309,6 +302,8 @@ class CommandDispatcher: urls = self._parse_url_input(url) for i, cur_url in enumerate(urls): + if secure: + cur_url.setScheme('https') if not window and i > 0: tab = False bg = True @@ -686,7 +681,7 @@ class CommandDispatcher: scope='window') @cmdutils.argument('count', count=True) @cmdutils.argument('horizontal', flag='x') - def scroll_perc(self, perc: float=None, horizontal=False, count=None): + def scroll_perc(self, perc: float = None, horizontal=False, count=None): """Scroll to a specific percentage of the page. The percentage can be given either as argument or as count. @@ -722,7 +717,7 @@ class CommandDispatcher: @cmdutils.argument('bottom_navigate', metavar='ACTION', choices=('next', 'increment')) def scroll_page(self, x: float, y: float, *, - top_navigate: str=None, bottom_navigate: str=None, + top_navigate: str = None, bottom_navigate: str = None, count=1): """Scroll the frame page-wise. @@ -859,7 +854,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('count', count=True) - def zoom(self, zoom: int=None, count=None): + def zoom(self, zoom: int = None, count=None): """Set the zoom level for the current tab. The zoom can be given as argument or as [count]. If neither is @@ -1285,7 +1280,7 @@ class CommandDispatcher: except urlmarks.Error as e: raise cmdexc.CommandError(str(e)) else: - msg = "Bookmarked {}!" if was_added else "Removed bookmark {}!" + msg = "Bookmarked {}" if was_added else "Removed bookmark {}" message.info(msg.format(url.toDisplayString())) @cmdutils.register(instance='command-dispatcher', scope='window', @@ -1389,6 +1384,9 @@ class CommandDispatcher: scope='window', window=self._win_id) target = None if dest is not None: + dest = downloads.transform_path(dest) + if dest is None: + raise cmdexc.CommandError("Invalid target filename") target = downloads.FileDownloadTarget(dest) tab = self._current_widget() @@ -1554,6 +1552,7 @@ class CommandDispatcher: if text is None: message.error("Could not get text from the focused element.") return + assert isinstance(text, str), text ed = editor.ExternalEditor(self._tabbed_browser) ed.editing_finished.connect(functools.partial( @@ -1591,10 +1590,7 @@ class CommandDispatcher: backend=usertypes.Backend.QtWebKit) def paste_primary(self): """Paste the primary selection at cursor position.""" - try: - self.insert_text(utils.get_clipboard(selection=True)) - except utils.SelectionUnsupportedError: - self.insert_text(utils.get_clipboard()) + self.insert_text(utils.get_clipboard(selection=True, fallback=True)) @cmdutils.register(instance='command-dispatcher', maxsplit=0, scope='window') @@ -1705,21 +1701,22 @@ class CommandDispatcher: tab = self._current_widget() tab.search.clear() + if not text: + return + options = { 'ignore_case': config.get('general', 'ignore-case'), 'reverse': reverse, } + self._tabbed_browser.search_text = text self._tabbed_browser.search_options = dict(options) - if text: - cb = functools.partial(self._search_cb, tab=tab, - old_scroll_pos=tab.scroller.pos_px(), - options=options, text=text, prev=False) - else: - cb = None - + cb = functools.partial(self._search_cb, tab=tab, + old_scroll_pos=tab.scroller.pos_px(), + options=options, text=text, prev=False) options['result_cb'] = cb + tab.search.search(text, **options) @cmdutils.register(instance='command-dispatcher', hide=True, @@ -1955,33 +1952,20 @@ class CommandDispatcher: def debug_webaction(self, action, count=1): """Execute a webaction. - See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the - available actions. + Available actions: + http://doc.qt.io/archives/qt-5.5/qwebpage.html#WebAction-enum (WebKit) + http://doc.qt.io/qt-5/qwebenginepage.html#WebAction-enum (WebEngine) Args: action: The action to execute, e.g. MoveToNextChar. count: How many times to repeat the action. """ tab = self._current_widget() - - if tab.backend == usertypes.Backend.QtWebKit: - assert QWebPage is not None - member = getattr(QWebPage, action, None) - base = QWebPage.WebAction - elif tab.backend == usertypes.Backend.QtWebEngine: - assert QWebEnginePage is not None - member = getattr(QWebEnginePage, action, None) - base = QWebEnginePage.WebAction - - if not isinstance(member, base): - raise cmdexc.CommandError("{} is not a valid web action!".format( - action)) - for _ in range(count): - # This whole command is backend-specific anyways, so it makes no - # sense to introduce some API for this. - # pylint: disable=protected-access - tab._widget.triggerPageAction(member) + try: + tab.action.run_string(action) + except browsertab.WebTabError as e: + raise cmdexc.CommandError(str(e)) @cmdutils.register(instance='command-dispatcher', scope='window', maxsplit=0, no_cmd_split=True) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 0e6a7af4f..c7cb5ad8e 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -19,11 +19,13 @@ """Shared QtWebKit/QtWebEngine code for downloads.""" +import re import sys import html import os.path import collections import functools +import pathlib import tempfile import sip @@ -161,6 +163,25 @@ def get_filename_question(*, suggested_filename, url, parent=None): return q +def transform_path(path): + r"""Do platform-specific transformations, like changing E: to E:\. + + Returns None if the path is invalid on the current platform. + """ + if sys.platform != "win32": + return path + path = utils.expand_windows_drive(path) + # Drive dependent working directories are not supported, e.g. + # E:filename is invalid + if re.match(r'[A-Z]:[^\\]', path, re.IGNORECASE): + return None + # Paths like COM1, ... + # See https://github.com/qutebrowser/qutebrowser/issues/82 + if pathlib.Path(path).is_reserved(): + return None + return path + + class NoFilenameError(Exception): """Raised when we can't find out a filename in DownloadTarget.""" @@ -507,6 +528,14 @@ class AbstractDownloadItem(QObject): """Retry a failed download.""" raise NotImplementedError + @pyqtSlot() + def try_retry(self): + """Try to retry a download and show an error if it's unsupported.""" + try: + self.retry() + except UnsupportedOperationError as e: + message.error(str(e)) + def _get_open_filename(self): """Get the filename to open a download. @@ -923,7 +952,7 @@ class DownloadModel(QAbstractListModel): @cmdutils.register(instance='download-model', scope='window', maxsplit=0) @cmdutils.argument('count', count=True) - def download_open(self, cmdline: str=None, count=0): + def download_open(self, cmdline: str = None, count=0): """Open the last/[count]th download. If no specific command is given, this will use the system's default @@ -968,7 +997,7 @@ class DownloadModel(QAbstractListModel): raise cmdexc.CommandError("No failed downloads!") else: download = to_retry[0] - download.retry() + download.try_retry() def can_clear(self): """Check if there are finished downloads to clear.""" diff --git a/qutebrowser/browser/downloadview.py b/qutebrowser/browser/downloadview.py index f2a380114..199f4d1d8 100644 --- a/qutebrowser/browser/downloadview.py +++ b/qutebrowser/browser/downloadview.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -23,7 +23,7 @@ import functools import sip from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer -from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu +from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu, QStyleFactory from qutebrowser.browser import downloads from qutebrowser.config import style @@ -75,6 +75,7 @@ class DownloadView(QListView): def __init__(self, win_id, parent=None): super().__init__(parent) + self.setStyle(QStyleFactory.create('Fusion')) style.set_register_stylesheet(self) self.setResizeMode(QListView.Adjust) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) @@ -134,7 +135,7 @@ class DownloadView(QListView): if item.successful: actions.append(("Open", item.open_file)) else: - actions.append(("Retry", item.retry)) + actions.append(("Retry", item.try_retry)) actions.append(("Remove", item.remove)) else: actions.append(("Cancel", item.cancel)) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 144b13f76..a7a5941f9 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 2615fd393..294425549 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/inspector.py b/qutebrowser/browser/inspector.py index a18d1ecf2..e225c31da 100644 --- a/qutebrowser/browser/inspector.py +++ b/qutebrowser/browser/inspector.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/mouse.py b/qutebrowser/browser/mouse.py index 79b6816bb..d1cd889d3 100644 --- a/qutebrowser/browser/mouse.py +++ b/qutebrowser/browser/mouse.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py index aacec9d3c..b1ab6f9b1 100644 --- a/qutebrowser/browser/navigate.py +++ b/qutebrowser/browser/navigate.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/network/pac.py b/qutebrowser/browser/network/pac.py index c19b09880..217cf21d9 100644 --- a/qutebrowser/browser/network/pac.py +++ b/qutebrowser/browser/network/pac.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -155,7 +155,7 @@ class PACResolver: raise ParseProxyError("Invalid number of parameters for PROXY") host, port = PACResolver._parse_proxy_host(config[1]) return QNetworkProxy(QNetworkProxy.HttpProxy, host, port) - elif config[0] == "SOCKS": + elif config[0] in ["SOCKS", "SOCKS5"]: if len(config) != 2: raise ParseProxyError("Invalid number of parameters for SOCKS") host, port = PACResolver._parse_proxy_host(config[1]) diff --git a/qutebrowser/browser/network/proxy.py b/qutebrowser/browser/network/proxy.py index 719c33178..1bdbc7b0b 100644 --- a/qutebrowser/browser/network/proxy.py +++ b/qutebrowser/browser/network/proxy.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/pdfjs.py b/qutebrowser/browser/pdfjs.py index d11cf3098..d003cefb1 100644 --- a/qutebrowser/browser/pdfjs.py +++ b/qutebrowser/browser/pdfjs.py @@ -1,6 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2015 Daniel Schadt +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py index 5ac689e29..f3bc4dfe2 100644 --- a/qutebrowser/browser/qtnetworkdownloads.py +++ b/qutebrowser/browser/qtnetworkdownloads.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -110,6 +110,9 @@ class DownloadItem(downloads.AbstractDownloadItem): def _do_die(self): """Abort the download and emit an error.""" self._read_timer.stop() + if self._reply is None: + log.downloads.debug("Reply gone while dying") + return self._reply.downloadProgress.disconnect() self._reply.finished.disconnect() self._reply.error.disconnect() @@ -270,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 018582abf..919e6e186 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,23 +17,26 @@ # 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. _HANDLERS: The handlers registered via decorators. """ +import json +import os import sys import time -import datetime import urllib.parse +import datetime -from PyQt5.QtCore import QUrlQuery +from PyQt5.QtCore import QUrlQuery, QUrl import qutebrowser +from qutebrowser.config import config from qutebrowser.utils import (version, utils, jinja, log, message, docutils, - objreg) + objreg, usertypes, qtutils) from qutebrowser.misc import objects @@ -75,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. """ @@ -103,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()), @@ -125,13 +141,19 @@ 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) + new_url.setPath('/') + if new_url.host(): # path was a valid host + 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: @@ -150,7 +172,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(), @@ -163,90 +185,160 @@ def qute_bookmarks(_url): return 'text/html', html -@add_handler('history') # noqa -def qute_history(url): - """Handler for qute:history. Display history.""" - # Get current date from query parameter, if not given choose today. - curr_date = datetime.date.today() - try: - query_date = QUrlQuery(url).queryItemValue("date") - if query_date: - curr_date = datetime.datetime.strptime(query_date, "%Y-%m-%d") - curr_date = curr_date.date() - except ValueError: - log.misc.debug("Invalid date passed to qute:history: " + query_date) +def history_data(start_time): # noqa + """Return history data - one_day = datetime.timedelta(days=1) - next_date = curr_date + one_day - prev_date = curr_date - one_day + Arguments: + start_time -- select history starting from this timestamp. + """ + def history_iter(start_time, reverse=False): + """Iterate through the history and get items we're interested. - def history_iter(reverse): - """Iterate through the history and get items we're interested in.""" - curr_timestamp = time.mktime(curr_date.timetuple()) + Arguments: + reverse -- whether to reverse the history_dict before iterating. + """ history = objreg.get('web-history').history_dict.values() if reverse: history = reversed(history) + # when history_dict is not reversed, we need to keep track of last item + # so that we can yield its atime + last_item = None + + # end is 24hrs earlier than start + end_time = start_time - 24*60*60 + for item in history: - # If we can't apply the reverse performance trick below, - # at least continue as early as possible with old items. - # This gets us down from 550ms to 123ms with 500k old items on my - # machine. - if item.atime < curr_timestamp and not reverse: - continue - - # Convert timestamp - try: - item_atime = datetime.datetime.fromtimestamp(item.atime) - except (ValueError, OSError, OverflowError): - log.misc.debug("Invalid timestamp {}.".format(item.atime)) - continue - - if reverse and item_atime.date() < curr_date: - # If we could reverse the history in-place, and this entry is - # older than today, only older entries will follow, so we can - # abort here. - return - - # Skip items not on curr_date # Skip redirects # Skip qute:// links - is_internal = item.url.scheme() == 'qute' - is_not_today = item_atime.date() != curr_date - if item.redirect or is_internal or is_not_today: + if item.redirect or item.url.scheme() == 'qute': continue + # Skip items out of time window + item_newer = item.atime > start_time + item_older = item.atime <= end_time + if reverse: + # history_dict is reversed, we are going back in history. + # so: + # abort if item is older than start_time+24hr + # skip if item is newer than start + if item_older: + yield {"next": int(item.atime)} + return + if item_newer: + continue + else: + # history_dict isn't reversed, we are going forward in history. + # so: + # abort if item is newer than start_time + # skip if item is older than start_time+24hrs + if item_older: + last_item = item + continue + if item_newer: + yield {"next": int(last_item.atime if last_item else -1)} + return + # Use item's url as title if there's no title. item_url = item.url.toDisplayString() item_title = item.title if item.title else item_url - display_atime = item_atime.strftime("%X") + item_time = int(item.atime * 1000) - yield (item_url, item_title, display_atime) + yield {"url": item_url, "title": item_title, "time": item_time} + + # if we reached here, we had reached the end of history + yield {"next": int(last_item.atime if last_item else -1)} if sys.hexversion >= 0x03050000: # On Python >= 3.5 we can reverse the ordereddict in-place and thus # apply an additional performance improvement in history_iter. # On my machine, this gets us down from 550ms to 72us with 500k old # items. - history = list(history_iter(reverse=True)) + history = history_iter(start_time, reverse=True) else: # On Python 3.4, we can't do that, so we'd need to copy the entire # history to a list. There, filter first and then reverse it here. - history = reversed(list(history_iter(reverse=False))) + history = reversed(list(history_iter(start_time, reverse=False))) - html = jinja.render('history.html', - title='History', - history=history, - curr_date=curr_date, - next_date=next_date, - prev_date=prev_date, - today=datetime.date.today()) - return 'text/html', html + return list(history) + + +@add_handler('history') +def qute_history(url): + """Handler for qute://history. Display and serve history.""" + if url.path() == '/data': + # Use start_time in query or current time. + try: + start_time = QUrlQuery(url).queryItemValue("start_time") + start_time = float(start_time) if start_time else time.time() + except ValueError as e: + raise QuteSchemeError("Query parameter start_time is invalid", e) + + return 'text/html', json.dumps(history_data(start_time)) + else: + if ( + config.get('content', 'allow-javascript') and + (objects.backend == usertypes.Backend.QtWebEngine or + qtutils.is_qtwebkit_ng()) + ): + return 'text/html', jinja.render( + 'history.html', + title='History', + session_interval=config.get('ui', 'history-session-interval') + ) + else: + # Get current date from query parameter, if not given choose today. + curr_date = datetime.date.today() + try: + query_date = QUrlQuery(url).queryItemValue("date") + if query_date: + curr_date = datetime.datetime.strptime(query_date, + "%Y-%m-%d").date() + except ValueError: + log.misc.debug("Invalid date passed to qute:history: " + + query_date) + + one_day = datetime.timedelta(days=1) + next_date = curr_date + one_day + prev_date = curr_date - one_day + + # start_time is the last second of curr_date + start_time = time.mktime(next_date.timetuple()) - 1 + history = [ + (i["url"], i["title"], + datetime.datetime.fromtimestamp(i["time"]/1000), + QUrl(i["url"]).host()) + for i in history_data(start_time) if "next" not in i + ] + + return 'text/html', jinja.render( + 'history_nojs.html', + title='History', + history=history, + curr_date=curr_date, + next_date=next_date, + prev_date=prev_date, + today=datetime.date.today(), + ) + + +@add_handler('javascript') +def qute_javascript(url): + """Handler for qute://javascript. + + Return content of file given as query parameter. + """ + path = url.path() + if path: + path = "javascript" + os.sep.join(path.split('/')) + return 'text/html', utils.read_file(path, binary=False) + else: + raise QuteSchemeError("No file specified", ValueError()) @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 @@ -254,7 +346,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__) @@ -263,7 +355,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. @@ -283,7 +375,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. @@ -304,13 +396,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/shared.py b/qutebrowser/browser/shared.py index 885e2809d..005d652a3 100644 --- a/qutebrowser/browser/shared.py +++ b/qutebrowser/browser/shared.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/signalfilter.py b/qutebrowser/browser/signalfilter.py index 78aa18b42..90b85c586 100644 --- a/qutebrowser/browser/signalfilter.py +++ b/qutebrowser/browser/signalfilter.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/urlmarks.py b/qutebrowser/browser/urlmarks.py index 991ba7fc5..013de408c 100644 --- a/qutebrowser/browser/urlmarks.py +++ b/qutebrowser/browser/urlmarks.py @@ -1,7 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) -# Copyright 2015-2016 Antoni Boucher +# Copyright 2014-2017 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Antoni Boucher # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index 5dd263da3..dce808871 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -306,6 +306,11 @@ class AbstractWebElement(collections.abc.MutableMapping): qtutils.ensure_valid(url) return url + def is_link(self): + """Return True if this AbstractWebElement is a link.""" + href_tags = ['a', 'area', 'link'] + return self.tag_name() in href_tags + def _mouse_pos(self): """Get the position to click/hover.""" # Click the center of the largest square fitting into the top/left @@ -403,9 +408,8 @@ class AbstractWebElement(collections.abc.MutableMapping): self._click_fake_event(click_target) return - href_tags = ['a', 'area', 'link'] if click_target == usertypes.ClickTarget.normal: - if self.tag_name() in href_tags: + if self.is_link(): log.webelem.debug("Clicking via JS click()") self._click_js(click_target) elif self.is_editable(strict=True): @@ -418,7 +422,7 @@ class AbstractWebElement(collections.abc.MutableMapping): elif click_target in [usertypes.ClickTarget.tab, usertypes.ClickTarget.tab_bg, usertypes.ClickTarget.window]: - if self.tag_name() in href_tags: + if self.is_link(): self._click_href(click_target) else: self._click_fake_event(click_target) diff --git a/qutebrowser/browser/webengine/__init__.py b/qutebrowser/browser/webengine/__init__.py index d7c910b36..60d140540 100644 --- a/qutebrowser/browser/webengine/__init__.py +++ b/qutebrowser/browser/webengine/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webengine/certificateerror.py b/qutebrowser/browser/webengine/certificateerror.py index 19b59c522..07c4bbe1b 100644 --- a/qutebrowser/browser/webengine/certificateerror.py +++ b/qutebrowser/browser/webengine/certificateerror.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webengine/interceptor.py b/qutebrowser/browser/webengine/interceptor.py index 350378147..e02f621a3 100644 --- a/qutebrowser/browser/webengine/interceptor.py +++ b/qutebrowser/browser/webengine/interceptor.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -57,8 +57,7 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor): info: QWebEngineUrlRequestInfo &info """ # FIXME:qtwebengine only block ads for NavigationTypeOther? - if (bytes(info.requestMethod()) == b'GET' and - self._host_blocker.is_blocked(info.requestUrl())): + if self._host_blocker.is_blocked(info.requestUrl()): log.webview.info("Request to {} blocked by host blocker.".format( info.requestUrl().host())) info.block(True) diff --git a/qutebrowser/browser/webengine/tabhistory.py b/qutebrowser/browser/webengine/tabhistory.py index ec43aa07b..5db6faeb1 100644 --- a/qutebrowser/browser/webengine/tabhistory.py +++ b/qutebrowser/browser/webengine/tabhistory.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 070bcf8c0..f1ca2e6e3 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -78,32 +78,34 @@ class DownloadItem(downloads.AbstractDownloadItem): self.stats.finish() elif state == QWebEngineDownloadItem.DownloadInterrupted: self.successful = False - self.done = True # https://bugreports.qt.io/browse/QTBUG-56839 - self.error.emit("Download failed") - self.stats.finish() + self._die("Download failed") else: raise ValueError("_on_state_changed was called with unknown state " "{}".format(state_name)) def _do_die(self): self._qt_item.downloadProgress.disconnect() - self._qt_item.cancel() + if self._qt_item.state() != QWebEngineDownloadItem.DownloadInterrupted: + self._qt_item.cancel() def _do_cancel(self): self._qt_item.cancel() def retry(self): # https://bugreports.qt.io/browse/QTBUG-56840 - raise downloads.UnsupportedOperationError + raise downloads.UnsupportedOperationError( + "Retrying downloads is unsupported with QtWebEngine") def _get_open_filename(self): return self._filename - def _set_fileobj(self, fileobj): + def _set_fileobj(self, fileobj, *, + autoclose=True): # pylint: disable=unused-argument raise downloads.UnsupportedOperationError def _set_tempfile(self, fileobj): + fileobj.close() self._set_filename(fileobj.name, force_overwrite=True, remember_directory=False) @@ -145,7 +147,7 @@ def _get_suggested_filename(path): """ filename = os.path.basename(path) filename = re.sub(r'\([0-9]+\)(?=\.|$)', '', filename) - if not qtutils.version_check('5.8.1'): + if not qtutils.version_check('5.9'): # https://bugreports.qt.io/browse/QTBUG-58155 filename = urllib.parse.unquote(filename) # Doing basename a *second* time because there could be a %2F in diff --git a/qutebrowser/browser/webengine/webengineelem.py b/qutebrowser/browser/webengine/webengineelem.py index 3e145468b..dc170115d 100644 --- a/qutebrowser/browser/webengine/webengineelem.py +++ b/qutebrowser/browser/webengine/webengineelem.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -18,7 +18,7 @@ # along with qutebrowser. If not, see . # FIXME:qtwebengine remove this once the stubs are gone -# pylint: disable=unused-variable +# pylint: disable=unused-argument """QtWebEngine specific part of the web element API.""" @@ -39,6 +39,38 @@ class WebEngineElement(webelem.AbstractWebElement): def __init__(self, js_dict, tab): super().__init__(tab) + # Do some sanity checks on the data we get from JS + js_dict_types = { + 'id': int, + 'text': str, + 'value': (str, int, float), + 'tag_name': str, + 'outer_xml': str, + 'class_name': str, + 'rects': list, + 'attributes': dict, + } + assert set(js_dict.keys()).issubset(js_dict_types.keys()) + for name, typ in js_dict_types.items(): + if name in js_dict and not isinstance(js_dict[name], typ): + raise TypeError("Got {} for {} from JS but expected {}: " + "{}".format(type(js_dict[name]), name, typ, + js_dict)) + for name, value in js_dict['attributes'].items(): + if not isinstance(name, str): + raise TypeError("Got {} ({}) for attribute name from JS: " + "{}".format(name, type(name), js_dict)) + if not isinstance(value, str): + raise TypeError("Got {} ({}) for attribute {} from JS: " + "{}".format(value, type(value), name, js_dict)) + for rect in js_dict['rects']: + assert set(rect.keys()) == {'top', 'right', 'bottom', 'left', + 'height', 'width'}, rect.keys() + for value in rect.values(): + if not isinstance(value, (int, float)): + raise TypeError("Got {} ({}) for rect from JS: " + "{}".format(value, type(value), js_dict)) + self._id = js_dict['id'] self._js_dict = js_dict @@ -88,7 +120,9 @@ class WebEngineElement(webelem.AbstractWebElement): The returned name will always be lower-case. """ - return self._js_dict['tag_name'].lower() + tag = self._js_dict['tag_name'] + assert isinstance(tag, str), tag + return tag.lower() def outer_xml(self): """Get the full HTML representation of this element.""" diff --git a/qutebrowser/browser/webengine/webengineinspector.py b/qutebrowser/browser/webengine/webengineinspector.py index b822bd253..cbb258039 100644 --- a/qutebrowser/browser/webengine/webengineinspector.py +++ b/qutebrowser/browser/webengine/webengineinspector.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webengine/webenginequtescheme.py b/qutebrowser/browser/webengine/webenginequtescheme.py index 6bc31f9be..5acf6fcba 100644 --- a/qutebrowser/browser/webengine/webenginequtescheme.py +++ b/qutebrowser/browser/webengine/webenginequtescheme.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -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/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index b029b4fd5..e1f4a22c9 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -34,7 +34,8 @@ from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile, from qutebrowser.browser import shared from qutebrowser.config import config, websettings -from qutebrowser.utils import objreg, utils, standarddir, javascript, log +from qutebrowser.utils import (objreg, utils, standarddir, javascript, log, + qtutils) class Attribute(websettings.Attribute): @@ -177,7 +178,8 @@ def init(args): _init_stylesheet(profile) # We need to do this here as a WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-58650 - PersistentCookiePolicy().set(config.get('content', 'cookies-store')) + if not qtutils.version_check('5.9'): + PersistentCookiePolicy().set(config.get('content', 'cookies-store')) Attribute(QWebEngineSettings.FullScreenSupportEnabled).set(True) @@ -221,9 +223,6 @@ MAPPINGS = { Attribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls), 'local-content-can-access-file-urls': Attribute(QWebEngineSettings.LocalContentCanAccessFileUrls), - # https://bugreports.qt.io/browse/QTBUG-58650 - # 'cookies-store': - # PersistentCookiePolicy(), 'webgl': Attribute(QWebEngineSettings.WebGLEnabled), }, @@ -301,3 +300,8 @@ try: except AttributeError: # Added in Qt 5.8 pass + + +if qtutils.version_check('5.9'): + # https://bugreports.qt.io/browse/QTBUG-58650 + MAPPINGS['content']['cookies-store'] = PersistentCookiePolicy() diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 88c693244..af93786d6 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,9 +17,6 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -# FIXME:qtwebengine remove this once the stubs are gone -# pylint: disable=unused-variable - """Wrapper over a QWebEngineView.""" import functools @@ -40,7 +37,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory, webenginedownloads) from qutebrowser.misc import miscwidgets from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils, - objreg, jinja) + objreg, jinja, debug) _qute_scheme_handler = None @@ -55,7 +52,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) @@ -82,17 +79,17 @@ _JS_WORLD_MAP = { class WebEngineAction(browsertab.AbstractAction): - """QtWebKit implementations related to web actions.""" + """QtWebEngine implementations related to web actions.""" - def _action(self, action): - self._widget.triggerPageAction(action) + action_class = QWebEnginePage + action_base = QWebEnginePage.WebAction def exit_fullscreen(self): - self._action(QWebEnginePage.ExitFullScreen) + self._widget.triggerPageAction(QWebEnginePage.ExitFullScreen) def save_page(self): """Save the current page.""" - self._action(QWebEnginePage.SavePage) + self._widget.triggerPageAction(QWebEnginePage.SavePage) class WebEnginePrinting(browsertab.AbstractPrinting): @@ -128,12 +125,23 @@ class WebEngineSearch(browsertab.AbstractSearch): super().__init__(parent) self._flags = QWebEnginePage.FindFlags(0) - def _find(self, text, flags, cb=None): - """Call findText on the widget with optional callback.""" - if cb is None: - self._widget.findText(text, flags) - else: - self._widget.findText(text, flags, cb) + def _find(self, text, flags, callback, caller): + """Call findText on the widget.""" + self.search_displayed = True + + def wrapped_callback(found): + """Wrap the callback to do debug logging.""" + found_text = 'found' if found else "didn't find" + if flags: + flag_text = 'with flags {}'.format(debug.qflags_key( + QWebEnginePage, flags, klass=QWebEnginePage.FindFlag)) + else: + flag_text = '' + log.webview.debug(' '.join([caller, found_text, text, flag_text]) + .strip()) + if callback is not None: + callback(found) + self._widget.findText(text, flags, wrapped_callback) def search(self, text, *, ignore_case=False, reverse=False, result_cb=None): @@ -148,9 +156,10 @@ class WebEngineSearch(browsertab.AbstractSearch): self.text = text self._flags = flags - self._find(text, flags, result_cb) + self._find(text, flags, result_cb, 'search') def clear(self): + self.search_displayed = False self._widget.findText('') def prev_result(self, *, result_cb=None): @@ -160,10 +169,10 @@ class WebEngineSearch(browsertab.AbstractSearch): flags &= ~QWebEnginePage.FindBackward else: flags |= QWebEnginePage.FindBackward - self._find(self.text, flags, result_cb) + self._find(self.text, flags, result_cb, 'prev_result') def next_result(self, *, result_cb=None): - self._find(self.text, self._flags, result_cb) + self._find(self.text, self._flags, result_cb, 'next_result') class WebEngineCaret(browsertab.AbstractCaret): @@ -237,8 +246,47 @@ class WebEngineCaret(browsertab.AbstractCaret): raise browsertab.UnsupportedOperationError return self._widget.selectedText() + def _follow_selected_cb(self, js_elem, tab=False): + """Callback for javascript which clicks the selected element. + + Args: + js_elem: The element serialized from javascript. + tab: Open in a new tab. + """ + if js_elem is None: + return + assert isinstance(js_elem, dict), js_elem + elem = webengineelem.WebEngineElement(js_elem, tab=self._tab) + if tab: + click_type = usertypes.ClickTarget.tab + else: + click_type = usertypes.ClickTarget.normal + + # Only click if we see a link + if elem.is_link(): + log.webview.debug("Found link in selection, clicking. ClickTarget " + "{}, elem {}".format(click_type, elem)) + elem.click(click_type) + def follow_selected(self, *, tab=False): - log.stub() + if self._tab.search.search_displayed: + # We are currently in search mode. + # let's click the link via a fake-click + # https://bugreports.qt.io/browse/QTBUG-60673 + self._tab.search.clear() + + log.webview.debug("Clicking a searched link via fake key press.") + # send a fake enter, clicking the orange selection box + if tab: + self._tab.key_press(Qt.Key_Enter, modifier=Qt.ControlModifier) + else: + self._tab.key_press(Qt.Key_Enter) + + else: + # click an existing blue selection + js_code = javascript.assemble('webelem', 'find_selected_link') + self._tab.run_js_async(js_code, lambda jsret: + self._follow_selected_cb(jsret, tab)) class WebEngineScroller(browsertab.AbstractScroller): @@ -256,13 +304,10 @@ class WebEngineScroller(browsertab.AbstractScroller): page = widget.page() page.scrollPositionChanged.connect(self._update_pos) - def _key_press(self, key, count=1): + def _repeated_key_press(self, key, count=1, modifier=Qt.NoModifier): + """Send count fake key presses to this scroller's WebEngineTab.""" for _ in range(min(count, 5000)): - press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0) - release_evt = QKeyEvent(QEvent.KeyRelease, key, Qt.NoModifier, - 0, 0, 0) - self._tab.send_event(press_evt) - self._tab.send_event(release_evt) + self._tab.key_press(key, modifier) @pyqtSlot() def _update_pos(self): @@ -319,28 +364,28 @@ class WebEngineScroller(browsertab.AbstractScroller): self._tab.run_js_async(js_code) def up(self, count=1): - self._key_press(Qt.Key_Up, count) + self._repeated_key_press(Qt.Key_Up, count) def down(self, count=1): - self._key_press(Qt.Key_Down, count) + self._repeated_key_press(Qt.Key_Down, count) def left(self, count=1): - self._key_press(Qt.Key_Left, count) + self._repeated_key_press(Qt.Key_Left, count) def right(self, count=1): - self._key_press(Qt.Key_Right, count) + self._repeated_key_press(Qt.Key_Right, count) def top(self): - self._key_press(Qt.Key_Home) + self._tab.key_press(Qt.Key_Home) def bottom(self): - self._key_press(Qt.Key_End) + self._tab.key_press(Qt.Key_End) def page_up(self, count=1): - self._key_press(Qt.Key_PageUp, count) + self._repeated_key_press(Qt.Key_PageUp, count) def page_down(self, count=1): - self._key_press(Qt.Key_PageDown, count) + self._repeated_key_press(Qt.Key_PageDown, count) def at_top(self): return self.pos_px().y() == 0 @@ -369,11 +414,15 @@ class WebEngineHistory(browsertab.AbstractHistory): return self._history.canGoForward() def serialize(self): - # WORKAROUND for https://github.com/qutebrowser/qutebrowser/issues/2289 - # FIXME:qtwebengine can we get rid of this with Qt 5.8.1? - scheme = self._history.currentItem().url().scheme() - if scheme in ['view-source', 'chrome']: - raise browsertab.WebTabError("Can't serialize special URL!") + if not qtutils.version_check('5.9'): + # WORKAROUND for + # https://github.com/qutebrowser/qutebrowser/issues/2289 + # Don't use the history's currentItem here, because of + # https://bugreports.qt.io/browse/QTBUG-59599 and because it doesn't + # contain view-source. + scheme = self._tab.url().scheme() + if scheme in ['view-source', 'chrome']: + raise browsertab.WebTabError("Can't serialize special URL!") return qtutils.serialize(self._history) def deserialize(self, data): @@ -596,6 +645,13 @@ class WebEngineTab(browsertab.AbstractTab): def clear_ssl_errors(self): raise browsertab.UnsupportedOperationError + def key_press(self, key, modifier=Qt.NoModifier): + press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0) + release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier, + 0, 0, 0) + self.send_event(press_evt) + self.send_event(release_evt) + @pyqtSlot() def _on_history_trigger(self): url = self.url() diff --git a/qutebrowser/browser/webengine/webview.py b/qutebrowser/browser/webengine/webview.py index 67e5fc259..ee6e099bf 100644 --- a/qutebrowser/browser/webengine/webview.py +++ b/qutebrowser/browser/webengine/webview.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/__init__.py b/qutebrowser/browser/webkit/__init__.py index b67442be7..93b53cdba 100644 --- a/qutebrowser/browser/webkit/__init__.py +++ b/qutebrowser/browser/webkit/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/cache.py b/qutebrowser/browser/webkit/cache.py index 860a532b0..8ae9aa0f2 100644 --- a/qutebrowser/browser/webkit/cache.py +++ b/qutebrowser/browser/webkit/cache.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -25,7 +25,7 @@ from PyQt5.QtCore import pyqtSlot from PyQt5.QtNetwork import QNetworkDiskCache, QNetworkCacheMetaData from qutebrowser.config import config -from qutebrowser.utils import utils, objreg +from qutebrowser.utils import utils, objreg, qtutils class DiskCache(QNetworkDiskCache): @@ -53,6 +53,10 @@ class DiskCache(QNetworkDiskCache): size = config.get('storage', 'cache-size') if size is None: size = 1024 * 1024 * 50 # default from QNetworkDiskCachePrivate + # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-59909 + if (qtutils.version_check('5.7.1') and + not qtutils.version_check('5.9')): # pragma: no cover + size = 0 self.setMaximumCacheSize(size) def _maybe_activate(self): diff --git a/qutebrowser/browser/webkit/certificateerror.py b/qutebrowser/browser/webkit/certificateerror.py index 41cf2866f..d02ded76c 100644 --- a/qutebrowser/browser/webkit/certificateerror.py +++ b/qutebrowser/browser/webkit/certificateerror.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -41,7 +41,7 @@ class CertificateErrorWrapper(usertypes.AbstractCertificateErrorWrapper): try: # Qt >= 5.4 return hash(self._error) - except TypeError: + except TypeError: # pragma: no cover return hash((self._error.certificate().toDer(), self._error.error())) diff --git a/qutebrowser/browser/webkit/cookies.py b/qutebrowser/browser/webkit/cookies.py index 113eb661a..79f7a67fa 100644 --- a/qutebrowser/browser/webkit/cookies.py +++ b/qutebrowser/browser/webkit/cookies.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/http.py b/qutebrowser/browser/webkit/http.py index f55542c67..08cad7a44 100644 --- a/qutebrowser/browser/webkit/http.py +++ b/qutebrowser/browser/webkit/http.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/mhtml.py b/qutebrowser/browser/webkit/mhtml.py index bc9ea2695..ed357f2bd 100644 --- a/qutebrowser/browser/webkit/mhtml.py +++ b/qutebrowser/browser/webkit/mhtml.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Daniel Schadt +# Copyright 2015-2017 Daniel Schadt # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/network/filescheme.py b/qutebrowser/browser/webkit/network/filescheme.py index cd0a6d489..2f3a3ff18 100644 --- a/qutebrowser/browser/webkit/network/filescheme.py +++ b/qutebrowser/browser/webkit/network/filescheme.py @@ -1,7 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) -# Copyright 2015-2016 Antoni Boucher (antoyo) +# Copyright 2014-2017 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Antoni Boucher (antoyo) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/network/networkmanager.py b/qutebrowser/browser/webkit/network/networkmanager.py index 779d778bc..30adf3170 100644 --- a/qutebrowser/browser/webkit/network/networkmanager.py +++ b/qutebrowser/browser/webkit/network/networkmanager.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -416,8 +416,7 @@ class NetworkManager(QNetworkAccessManager): req.setRawHeader(header, value) host_blocker = objreg.get('host-blocker') - if (op == QNetworkAccessManager.GetOperation and - host_blocker.is_blocked(req.url())): + if host_blocker.is_blocked(req.url()): log.webview.info("Request to {} blocked by host blocker.".format( req.url().host())) return networkreply.ErrorNetworkReply( diff --git a/qutebrowser/browser/webkit/network/networkreply.py b/qutebrowser/browser/webkit/network/networkreply.py index 2cc5727be..a4a4f59ca 100644 --- a/qutebrowser/browser/webkit/network/networkreply.py +++ b/qutebrowser/browser/webkit/network/networkreply.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # Based on the Eric5 helpviewer, # Copyright (c) 2009 - 2014 Detlev Offenbach @@ -19,6 +19,10 @@ # # 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..""" @@ -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,20 @@ 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 abort(self): + """Called when there's e.g. a redirection limit.""" + pass + + def readData(self, _maxlen): + return bytes() diff --git a/qutebrowser/browser/webkit/network/schemehandler.py b/qutebrowser/browser/webkit/network/schemehandler.py index 9975db121..c6337efa3 100644 --- a/qutebrowser/browser/webkit/network/schemehandler.py +++ b/qutebrowser/browser/webkit/network/schemehandler.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # Based on the Eric5 helpviewer, # Copyright (c) 2009 - 2014 Detlev Offenbach diff --git a/qutebrowser/browser/webkit/network/webkitqutescheme.py b/qutebrowser/browser/webkit/network/webkitqutescheme.py index 61ef760bc..6e83e60a0 100644 --- a/qutebrowser/browser/webkit/network/webkitqutescheme.py +++ b/qutebrowser/browser/webkit/network/webkitqutescheme.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -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/rfc6266.py b/qutebrowser/browser/webkit/rfc6266.py index 89f4c78c6..c3c8f7a4b 100644 --- a/qutebrowser/browser/webkit/rfc6266.py +++ b/qutebrowser/browser/webkit/rfc6266.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -286,9 +286,6 @@ def normalize_ws(text): def parse_headers(content_disposition): """Build a _ContentDisposition from header values.""" - # WORKAROUND for https://bitbucket.org/logilab/pylint/issue/492/ - # pylint: disable=no-member - # We allow non-ascii here (it will only be parsed inside of qdtext, and # rejected by the grammar if it appears in other places), although parsing # it can be ambiguous. Parsing it ensures that a non-ambiguous filename* diff --git a/qutebrowser/browser/webkit/tabhistory.py b/qutebrowser/browser/webkit/tabhistory.py index d2efca257..19e4ef15c 100644 --- a/qutebrowser/browser/webkit/tabhistory.py +++ b/qutebrowser/browser/webkit/tabhistory.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -21,7 +21,6 @@ from PyQt5.QtCore import QByteArray, QDataStream, QIODevice, QUrl -from PyQt5.QtWebKit import qWebKitVersion from qutebrowser.utils import qtutils @@ -181,7 +180,7 @@ def serialize(items): else: current_idx = 0 - if qtutils.is_qtwebkit_ng(qWebKitVersion()): + if qtutils.is_qtwebkit_ng(): _serialize_ng(items, current_idx, stream) else: _serialize_old(items, current_idx, stream) diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py index 34209c290..a6fa1ba4f 100644 --- a/qutebrowser/browser/webkit/webkitelem.py +++ b/qutebrowser/browser/webkit/webkitelem.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -112,7 +112,9 @@ class WebKitElement(webelem.AbstractWebElement): def value(self): self._check_vanished() - return self._elem.evaluateJavaScript('this.value') + val = self._elem.evaluateJavaScript('this.value') + assert isinstance(val, (int, float, str)), val + return val def set_value(self, value): self._check_vanished() @@ -283,8 +285,7 @@ class WebKitElement(webelem.AbstractWebElement): for _ in range(5): if elem is None: break - tag = elem.tag_name() - if tag == 'a' or tag == 'area': + if elem.is_link(): if elem.get('target', None) == '_blank': elem['target'] = '_top' break diff --git a/qutebrowser/browser/webkit/webkithistory.py b/qutebrowser/browser/webkit/webkithistory.py index abd80eb39..0f9d64460 100644 --- a/qutebrowser/browser/webkit/webkithistory.py +++ b/qutebrowser/browser/webkit/webkithistory.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/webkitinspector.py b/qutebrowser/browser/webkit/webkitinspector.py index 9e612b28e..9a056a896 100644 --- a/qutebrowser/browser/webkit/webkitinspector.py +++ b/qutebrowser/browser/webkit/webkitinspector.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/webkitsettings.py b/qutebrowser/browser/webkit/webkitsettings.py index caeef296f..0d4564e7e 100644 --- a/qutebrowser/browser/webkit/webkitsettings.py +++ b/qutebrowser/browser/webkit/webkitsettings.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -26,7 +26,7 @@ Module attributes: import os.path -from PyQt5.QtWebKit import QWebSettings, qWebKitVersion +from PyQt5.QtWebKit import QWebSettings from qutebrowser.config import config, websettings from qutebrowser.utils import standarddir, objreg, urlutils, qtutils, message @@ -90,7 +90,7 @@ def _set_user_stylesheet(): def _init_private_browsing(): if config.get('general', 'private-browsing'): - if qtutils.is_qtwebkit_ng(qWebKitVersion()): + if qtutils.is_qtwebkit_ng(): message.warning("Private browsing is not fully implemented by " "QtWebKit-NG!") QWebSettings.setIconDatabasePath('') diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index ac9eea466..daf46a503 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -23,6 +23,7 @@ import sys import functools import xml.etree.ElementTree +import sip from PyQt5.QtCore import (pyqtSlot, Qt, QEvent, QUrl, QPoint, QTimer, QSizeF, QSize) from PyQt5.QtGui import QKeyEvent @@ -35,7 +36,7 @@ from qutebrowser.browser import browsertab from qutebrowser.browser.network import proxy from qutebrowser.browser.webkit import webview, tabhistory, webkitelem from qutebrowser.browser.webkit.network import webkitqutescheme -from qutebrowser.utils import qtutils, objreg, usertypes, utils, log +from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug def init(): @@ -56,6 +57,9 @@ class WebKitAction(browsertab.AbstractAction): """QtWebKit implementations related to web actions.""" + action_class = QWebPage + action_base = QWebPage.WebAction + def exit_fullscreen(self): raise browsertab.UnsupportedOperationError @@ -103,7 +107,7 @@ class WebKitSearch(browsertab.AbstractSearch): super().__init__(parent) self._flags = QWebPage.FindFlags(0) - def _call_cb(self, callback, found): + def _call_cb(self, callback, found, text, flags, caller): """Call the given callback if it's non-None. Delays the call via a QTimer so the website is re-rendered in between. @@ -111,17 +115,34 @@ class WebKitSearch(browsertab.AbstractSearch): Args: callback: What to call found: If the text was found + text: The text searched for + flags: The flags searched with + caller: Name of the caller. """ + found_text = 'found' if found else "didn't find" + # Removing FindWrapsAroundDocument to get the same logging as with + # QtWebEngine + debug_flags = debug.qflags_key( + QWebPage, flags & ~QWebPage.FindWrapsAroundDocument, + klass=QWebPage.FindFlag) + if debug_flags != '0x0000': + flag_text = 'with flags {}'.format(debug_flags) + else: + flag_text = '' + log.webview.debug(' '.join([caller, found_text, text, flag_text]) + .strip()) if callback is not None: QTimer.singleShot(0, functools.partial(callback, found)) def clear(self): + self.search_displayed = False # We first clear the marked text, then the highlights self._widget.findText('') self._widget.findText('', QWebPage.HighlightAllOccurrences) def search(self, text, *, ignore_case=False, reverse=False, result_cb=None): + self.search_displayed = True flags = QWebPage.FindWrapsAroundDocument if ignore_case == 'smart': if not text.islower(): @@ -136,13 +157,15 @@ class WebKitSearch(browsertab.AbstractSearch): self._widget.findText(text, flags | QWebPage.HighlightAllOccurrences) self.text = text self._flags = flags - self._call_cb(result_cb, found) + self._call_cb(result_cb, found, text, flags, 'search') def next_result(self, *, result_cb=None): + self.search_displayed = True found = self._widget.findText(self.text, self._flags) - self._call_cb(result_cb, found) + self._call_cb(result_cb, found, self.text, self._flags, 'next_result') def prev_result(self, *, result_cb=None): + self.search_displayed = True # The int() here makes sure we get a copy of the flags. flags = QWebPage.FindFlags(int(self._flags)) if flags & QWebPage.FindBackward: @@ -150,7 +173,7 @@ class WebKitSearch(browsertab.AbstractSearch): else: flags |= QWebPage.FindBackward found = self._widget.findText(self.text, flags) - self._call_cb(result_cb, found) + self._call_cb(result_cb, found, self.text, flags, 'prev_result') class WebKitCaret(browsertab.AbstractCaret): @@ -444,15 +467,11 @@ class WebKitScroller(browsertab.AbstractScroller): # self._widget.setFocus() for _ in range(min(count, 5000)): - press_evt = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, 0, 0, 0) - release_evt = QKeyEvent(QEvent.KeyRelease, key, Qt.NoModifier, - 0, 0, 0) # Abort scrolling if the minimum/maximum was reached. if (getter is not None and frame.scrollBarValue(direction) == getter(direction)): return - self._widget.keyPressEvent(press_evt) - self._widget.keyReleaseEvent(release_evt) + self._tab.key_press(key) def up(self, count=1): self._key_press(Qt.Key_Up, count, 'scrollBarMinimum', Qt.Vertical) @@ -678,6 +697,13 @@ class WebKitTab(browsertab.AbstractTab): def clear_ssl_errors(self): self.networkaccessmanager().clear_all_ssl_errors() + def key_press(self, key, modifier=Qt.NoModifier): + press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0) + release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier, + 0, 0, 0) + self.send_event(press_evt) + self.send_event(release_evt) + @pyqtSlot() def _on_history_trigger(self): url = self.url() @@ -707,6 +733,9 @@ class WebKitTab(browsertab.AbstractTab): @pyqtSlot() def _on_webkit_icon_changed(self): """Emit iconChanged with a QIcon like QWebEngineView does.""" + if sip.isdeleted(self._widget): + log.webview.debug("Got _on_webkit_icon_changed for deleted view!") + return self.icon_changed.emit(self._widget.icon()) @pyqtSlot(QWebFrame) diff --git a/qutebrowser/browser/webkit/webpage.py b/qutebrowser/browser/webkit/webpage.py index 7fc891c4c..a687dd5e2 100644 --- a/qutebrowser/browser/webkit/webpage.py +++ b/qutebrowser/browser/webkit/webpage.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index 12670be4f..01abca639 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -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/commands/__init__.py b/qutebrowser/commands/__init__.py index 70f058d82..7bc59ae40 100644 --- a/qutebrowser/commands/__init__.py +++ b/qutebrowser/commands/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py index e4c6378bd..9dfe841ce 100644 --- a/qutebrowser/commands/argparser.py +++ b/qutebrowser/commands/argparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -76,11 +76,11 @@ class ArgumentParser(argparse.ArgumentParser): self.name = name super().__init__(*args, add_help=False, prog=name, **kwargs) - def exit(self, status=0, msg=None): - raise ArgumentParserExit(status, msg) + def exit(self, status=0, message=None): + raise ArgumentParserExit(status, message) - def error(self, msg): - raise ArgumentParserError(msg.capitalize()) + def error(self, message): + raise ArgumentParserError(message.capitalize()) def arg_name(name): diff --git a/qutebrowser/commands/cmdexc.py b/qutebrowser/commands/cmdexc.py index 766d20620..51e20fec7 100644 --- a/qutebrowser/commands/cmdexc.py +++ b/qutebrowser/commands/cmdexc.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/commands/cmdutils.py b/qutebrowser/commands/cmdutils.py index 3641c3cb9..9b1539b71 100644 --- a/qutebrowser/commands/cmdutils.py +++ b/qutebrowser/commands/cmdutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index d46cc5c77..b40f7ab16 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index f6a8e07e2..cc967ce86 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -133,7 +133,8 @@ class CommandRunner(QObject): Yields: ParseResult tuples. """ - if not text.strip(): + text = text.strip().lstrip(':').strip() + if not text: raise cmdexc.NoSuchCommandError("No command given") if aliases: diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index 4533e86b1..f3802d04c 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/__init__.py b/qutebrowser/completion/__init__.py index d3caf8703..8b8b9d88d 100644 --- a/qutebrowser/completion/__init__.py +++ b/qutebrowser/completion/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py index 74c759c0d..96d937829 100644 --- a/qutebrowser/completion/completer.py +++ b/qutebrowser/completion/completer.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/completiondelegate.py b/qutebrowser/completion/completiondelegate.py index dfb479b3f..1d5dfadf0 100644 --- a/qutebrowser/completion/completiondelegate.py +++ b/qutebrowser/completion/completiondelegate.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py index bd6b8c5be..490fcd6c0 100644 --- a/qutebrowser/completion/completionwidget.py +++ b/qutebrowser/completion/completionwidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -23,7 +23,7 @@ Defines a CompletionView which uses CompletionFiterModel and CompletionModel subclasses to provide completions. """ -from PyQt5.QtWidgets import QStyle, QTreeView, QSizePolicy +from PyQt5.QtWidgets import QStyle, QTreeView, QSizePolicy, QStyleFactory from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel, QSize from qutebrowser.config import config, style @@ -117,6 +117,7 @@ class CompletionView(QTreeView): self._delegate = completiondelegate.CompletionItemDelegate(self) self.setItemDelegate(self._delegate) + self.setStyle(QStyleFactory.create('Fusion')) style.set_register_stylesheet(self) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.setHeaderHidden(True) @@ -124,6 +125,7 @@ class CompletionView(QTreeView): self.setIndentation(0) self.setItemsExpandable(False) self.setExpandsOnDoubleClick(False) + self.setAnimated(False) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # WORKAROUND # This is a workaround for weird race conditions with invalid diff --git a/qutebrowser/completion/models/__init__.py b/qutebrowser/completion/models/__init__.py index 99f1954fe..5812545eb 100644 --- a/qutebrowser/completion/models/__init__.py +++ b/qutebrowser/completion/models/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/models/base.py b/qutebrowser/completion/models/base.py index 88b06a4e0..b1cad276a 100644 --- a/qutebrowser/completion/models/base.py +++ b/qutebrowser/completion/models/base.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -103,7 +103,7 @@ class BaseCompletionModel(QStandardItemModel): nameitem.setData(userdata, Role.userdata) return nameitem, descitem, miscitem - def delete_cur_item(self, win_id): + def delete_cur_item(self, completion): """Delete the selected item.""" raise NotImplementedError diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py index 4058a5f00..c9e9850d1 100644 --- a/qutebrowser/completion/models/configmodel.py +++ b/qutebrowser/completion/models/configmodel.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/models/instances.py b/qutebrowser/completion/models/instances.py index 6359d3771..f7eaaca86 100644 --- a/qutebrowser/completion/models/instances.py +++ b/qutebrowser/completion/models/instances.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index 76c1a8997..5ab381c43 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/models/sortfilter.py b/qutebrowser/completion/models/sortfilter.py index 3df787539..2bb454bf9 100644 --- a/qutebrowser/completion/models/sortfilter.py +++ b/qutebrowser/completion/models/sortfilter.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/completion/models/urlmodel.py b/qutebrowser/completion/models/urlmodel.py index a222df4f6..98f68c08c 100644 --- a/qutebrowser/completion/models/urlmodel.py +++ b/qutebrowser/completion/models/urlmodel.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/__init__.py b/qutebrowser/config/__init__.py index e2a04ee47..bf0bce0ec 100644 --- a/qutebrowser/config/__init__.py +++ b/qutebrowser/config/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 05d71605f..ccd5afd56 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -186,7 +186,8 @@ def _init_key_config(parent): key_config = keyconf.KeyConfigParser(standarddir.config(), 'keys.conf', args.relaxed_config, parent=parent) - except (keyconf.KeyConfigError, UnicodeDecodeError) as e: + except (keyconf.KeyConfigError, cmdexc.CommandError, + UnicodeDecodeError) as e: log.init.exception(e) errstr = "Error while reading key config:\n" if e.lineno is not None: @@ -471,10 +472,9 @@ class ConfigManager(QObject): """Get the whole config as a string.""" lines = configdata.FIRST_COMMENT.strip('\n').splitlines() for sectname, sect in self.sections.items(): - lines.append('\n[{}]'.format(sectname)) - lines += self._str_section_desc(sectname) - lines += self._str_option_desc(sectname, sect) - lines += self._str_items(sect) + lines += ['\n'] + self._str_section_desc(sectname) + lines.append('[{}]'.format(sectname)) + lines += self._str_items(sectname, sect) return '\n'.join(lines) + '\n' def _str_section_desc(self, sectname): @@ -489,42 +489,7 @@ class ConfigManager(QObject): lines += wrapper.wrap(secline) return lines - def _str_option_desc(self, sectname, sect): - """Get the option description strings for sect/sectname.""" - wrapper = textwrapper.TextWrapper(initial_indent='#' + ' ' * 5, - subsequent_indent='#' + ' ' * 5) - lines = [] - if not getattr(sect, 'descriptions', None): - return lines - - for optname, option in sect.items(): - - lines.append('#') - typestr = ' ({})'.format(option.typ.get_name()) - lines.append("# {}{}:".format(optname, typestr)) - - try: - desc = self.sections[sectname].descriptions[optname] - except KeyError: - log.config.exception("No description for {}.{}!".format( - sectname, optname)) - continue - for descline in desc.splitlines(): - lines += wrapper.wrap(descline) - valid_values = option.typ.get_valid_values() - if valid_values is not None: - if valid_values.descriptions: - for val in valid_values: - desc = valid_values.descriptions[val] - lines += wrapper.wrap(" {}: {}".format(val, desc)) - else: - lines += wrapper.wrap("Valid values: {}".format(', '.join( - valid_values))) - lines += wrapper.wrap("Default: {}".format( - option.values['default'])) - return lines - - def _str_items(self, sect): + def _str_items(self, sectname, sect): """Get the option items as string for sect.""" lines = [] for optname, option in sect.items(): @@ -535,9 +500,43 @@ class ConfigManager(QObject): # configparser can't handle = in keys :( optname = optname.replace('=', '') keyval = '{} = {}'.format(optname, value) + lines += self._str_option_desc(sectname, sect, optname, option) lines.append(keyval) return lines + def _str_option_desc(self, sectname, sect, optname, option): + """Get the option description strings for a single option.""" + wrapper = textwrapper.TextWrapper(initial_indent='#' + ' ' * 5, + subsequent_indent='#' + ' ' * 5) + lines = [] + if not getattr(sect, 'descriptions', None): + return lines + + lines.append('') + typestr = ' ({})'.format(option.typ.get_name()) + lines.append("# {}{}:".format(optname, typestr)) + + try: + desc = self.sections[sectname].descriptions[optname] + except KeyError: + log.config.exception("No description for {}.{}!".format( + sectname, optname)) + return [] + for descline in desc.splitlines(): + lines += wrapper.wrap(descline) + valid_values = option.typ.get_valid_values() + if valid_values is not None: + if valid_values.descriptions: + for val in valid_values: + desc = valid_values.descriptions[val] + lines += wrapper.wrap(" {}: {}".format(val, desc)) + else: + lines += wrapper.wrap("Valid values: {}".format(', '.join( + valid_values))) + lines += wrapper.wrap("Default: {}".format( + option.values['default'])) + return lines + def _get_real_sectname(self, cp, sectname): """Get an old or new section name based on a configparser. @@ -644,8 +643,7 @@ class ConfigManager(QObject): def _after_set(self, changed_sect, changed_opt): """Clean up caches and emit signals after an option has been set.""" - # WORKAROUND for https://bitbucket.org/logilab/pylint/issues/659/ - self.get.cache_clear() # pylint: disable=no-member + self.get.cache_clear() self._changed(changed_sect, changed_opt) # Options in the same section and ${optname} interpolation. for optname, option in self.sections[changed_sect].items(): @@ -716,8 +714,7 @@ class ConfigManager(QObject): existed = optname in sectdict if existed: sectdict.delete(optname) - # WORKAROUND for https://bitbucket.org/logilab/pylint/issues/659/ - self.get.cache_clear() # pylint: disable=no-member + self.get.cache_clear() return existed @functools.lru_cache() @@ -806,7 +803,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 fe75c3bcc..480e97a8d 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -292,6 +292,12 @@ def data(readonly=False): )), ('ui', sect.KeyValue( + ('history-session-interval', + SettingValue(typ.Int(), '30'), + "The maximum time in minutes between two history items for them " + "to be considered being from the same session. Use -1 to " + "disable separation."), + ('zoom-levels', SettingValue(typ.List(typ.Perc(minval=0)), '25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,' @@ -311,8 +317,9 @@ def data(readonly=False): "The position of the status bar."), ('message-timeout', - SettingValue(typ.Int(), '2000'), - "Time (in ms) to show messages in the statusbar for."), + SettingValue(typ.Int(minval=0), '2000'), + "Time (in ms) to show messages in the statusbar for.\n" + "Set to 0 to never clear messages."), ('message-unfocused', SettingValue(typ.Bool(), 'false'), @@ -400,6 +407,10 @@ def data(readonly=False): "Globs are supported, so ';*' will blacklist all keychains" "starting with ';'. Use '*' to disable keyhints"), + ('keyhint-delay', + SettingValue(typ.Int(minval=0), '500'), + "Time from pressing a key to seeing the keyhint dialog (ms)"), + ('prompt-radius', SettingValue(typ.Int(minval=0), '8'), "The rounding radius for the edges of prompts."), @@ -669,6 +680,11 @@ def data(readonly=False): SettingValue(typ.Bool(), 'true'), "Whether to show favicons in the tab bar."), + ('favicon-scale', + SettingValue(typ.Float(minval=0.0), '1.0'), + "Scale for favicons in the tab bar. The tab size is unchanged, " + "so big favicons also require extra `tabs->padding`."), + ('width', SettingValue(typ.PercOrInt(minperc=0, maxperc=100, minint=1), '20%'), @@ -912,7 +928,7 @@ def data(readonly=False): ('cookies-store', SettingValue(typ.Bool(), 'true'), "Whether to store cookies. Note this option needs a restart with " - "QtWebEngine."), + "QtWebEngine on Qt < 5.9."), ('host-block-lists', SettingValue( @@ -1361,7 +1377,7 @@ def data(readonly=False): ('fonts', sect.KeyValue( ('_monospace', - SettingValue(typ.Font(), 'Terminus, Monospace, ' + SettingValue(typ.Font(), 'xos4 Terminus, Terminus, Monospace, ' '"DejaVu Sans Mono", Monaco, ' '"Bitstream Vera Sans Mono", "Andale Mono", ' '"Courier New", Courier, "Liberation Mono", ' @@ -1692,7 +1708,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/config/configexc.py b/qutebrowser/config/configexc.py index a8fd0af2e..b19d45d7b 100644 --- a/qutebrowser/config/configexc.py +++ b/qutebrowser/config/configexc.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 2dad85117..9c390b2b0 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/parsers/__init__.py b/qutebrowser/config/parsers/__init__.py index 5e2183794..1c316078d 100644 --- a/qutebrowser/config/parsers/__init__.py +++ b/qutebrowser/config/parsers/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/parsers/ini.py b/qutebrowser/config/parsers/ini.py index 56640e299..0ae485f4b 100644 --- a/qutebrowser/config/parsers/ini.py +++ b/qutebrowser/config/parsers/ini.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/parsers/keyconf.py b/qutebrowser/config/parsers/keyconf.py index 6ca5f72b7..53f23d7c0 100644 --- a/qutebrowser/config/parsers/keyconf.py +++ b/qutebrowser/config/parsers/keyconf.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -142,9 +142,14 @@ class KeyConfigParser(QObject): def save(self): """Save the key config file.""" log.destroy.debug("Saving key config to {}".format(self._configfile)) - with qtutils.savefile_open(self._configfile, encoding='utf-8') as f: - data = str(self) - f.write(data) + + try: + with qtutils.savefile_open(self._configfile, + encoding='utf-8') as f: + data = str(self) + f.write(data) + except OSError as e: + message.error("Could not save key config: {}".format(e)) @cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True, no_replace_variables=True) @@ -252,6 +257,7 @@ class KeyConfigParser(QObject): """ # {'sectname': {'keychain1': 'command', 'keychain2': 'command'}, ...} bindings_to_add = collections.OrderedDict() + mark_dirty = False for sectname, sect in configdata.KEY_DATA.items(): sectname = self._normalize_sectname(sectname) @@ -261,6 +267,7 @@ class KeyConfigParser(QObject): if not only_new or self._is_new(sectname, command, e): assert e not in bindings_to_add[sectname] bindings_to_add[sectname][e] = command + mark_dirty = True for sectname, sect in bindings_to_add.items(): if not sect: @@ -271,7 +278,7 @@ class KeyConfigParser(QObject): self._add_binding(sectname, keychain, command) self.changed.emit(sectname) - if bindings_to_add: + if mark_dirty: self._mark_config_dirty() def _is_new(self, sectname, command, keychain): @@ -315,7 +322,7 @@ class KeyConfigParser(QObject): else: line = line.strip() self._read_command(line) - except KeyConfigError as e: + except (KeyConfigError, cmdexc.CommandError) as e: if relaxed: continue else: diff --git a/qutebrowser/config/sections.py b/qutebrowser/config/sections.py index 254348fe9..04a735647 100644 --- a/qutebrowser/config/sections.py +++ b/qutebrowser/config/sections.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/style.py b/qutebrowser/config/style.py index b2697daac..15215c398 100644 --- a/qutebrowser/config/style.py +++ b/qutebrowser/config/style.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/textwrapper.py b/qutebrowser/config/textwrapper.py index a36a73b6f..b5744f60b 100644 --- a/qutebrowser/config/textwrapper.py +++ b/qutebrowser/config/textwrapper.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/value.py b/qutebrowser/config/value.py index 388d8febc..b23674606 100644 --- a/qutebrowser/config/value.py +++ b/qutebrowser/config/value.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/config/websettings.py b/qutebrowser/config/websettings.py index b6e010499..b2a54e58b 100644 --- a/qutebrowser/config/websettings.py +++ b/qutebrowser/config/websettings.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/html/history.html b/qutebrowser/html/history.html index 94f82182f..2c6c0a2f1 100644 --- a/qutebrowser/html/history.html +++ b/qutebrowser/html/history.html @@ -16,43 +16,61 @@ td.time { white-space: nowrap; } +table { + margin-bottom: 30px; +} + .date { - color: #888; - font-size: 14pt; - padding-left: 25px; -} - -.pagination-link { - display: inline-block; - margin-bottom: 10px; - margin-top: 10px; - padding-right: 10px; -} - -.pagination-link > a { - color: #333; + color: #555; + font-size: 12pt; + padding-bottom: 15px; font-weight: bold; + text-align: left; +} + +#load { + color: #555; + font-weight: bold; + text-decoration: none; +} + +#eof { + color: #aaa; + margin-bottom: 30px; + text-align: center; + width: 100%; +} + +.session-separator { + color: #aaa; + height: 40px; + text-align: center; } {% endblock %} - {% block content %} +

Browsing history

+
+ + + + {% endblock %} diff --git a/qutebrowser/html/history_nojs.html b/qutebrowser/html/history_nojs.html new file mode 100644 index 000000000..bcc5663c1 --- /dev/null +++ b/qutebrowser/html/history_nojs.html @@ -0,0 +1,61 @@ +{% extends "styled.html" %} + +{% block style %} +{{super()}} +body { + max-width: 1440px; +} + +td.title { + word-break: break-all; +} + +td.time { + color: #555; + text-align: right; + white-space: nowrap; +} + +table { + margin-bottom: 30px; +} + +.date { + color: #555; + font-size: 12pt; + padding-bottom: 15px; + font-weight: bold; + text-align: left; +} + +.pagination-link { + color: #555; + font-weight: bold; + margn-bottom: 15px; + text-decoration: none; +} +{% endblock %} +{% block content %} + +

Browsing history

+ + + + + {% for url, title, time, host in history %} + + + + + {% endfor %} + +
{{curr_date.strftime("%a, %d %B %Y")}}
+ {{title}} + {{host}} + {{time.strftime("%X")}}
+ + +{% if today >= next_date %} + +{% endif %} +{% endblock %} diff --git a/qutebrowser/html/styled.html b/qutebrowser/html/styled.html index e2a608538..f4d256422 100644 --- a/qutebrowser/html/styled.html +++ b/qutebrowser/html/styled.html @@ -38,4 +38,11 @@ td { padding: 2px 5px; text-align: left; } + +.hostname { + color: #858585; + font-size: 0.9em; + margin-left: 10px; + text-decoration: none; +} {% endblock %} diff --git a/qutebrowser/javascript/.eslintrc.yaml b/qutebrowser/javascript/.eslintrc.yaml index ef7ed00b5..0fbeea904 100644 --- a/qutebrowser/javascript/.eslintrc.yaml +++ b/qutebrowser/javascript/.eslintrc.yaml @@ -38,3 +38,5 @@ rules: max-len: ["error", {"ignoreUrls": true}] capitalized-comments: "off" prefer-destructuring: "off" + line-comment-position: "off" + no-inline-comments: "off" diff --git a/qutebrowser/javascript/history.js b/qutebrowser/javascript/history.js new file mode 100644 index 000000000..f46ceb49d --- /dev/null +++ b/qutebrowser/javascript/history.js @@ -0,0 +1,194 @@ +/** + * Copyright 2017 Imran Sobir + * + * This file is part of qutebrowser. + * + * qutebrowser is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qutebrowser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qutebrowser. If not, see . + */ + +"use strict"; + +window.loadHistory = (function() { + // Date of last seen item. + var lastItemDate = null; + + // The time to load next. + var nextTime = null; + + // The URL to fetch data from. + var DATA_URL = "qute://history/data"; + + // Various fixed elements + var EOF_MESSAGE = document.getElementById("eof"); + var LOAD_LINK = document.getElementById("load"); + var HIST_CONTAINER = document.getElementById("hist-container"); + + /** + * Finds or creates the session table>tbody to which item with given date + * should be added. + * + * @param {Date} date - the date of the item being added. + * @returns {Element} the element to which new rows should be added. + */ + function getSessionNode(date) { + // Find/create table + var tableId = ["hist", date.getDate(), date.getMonth(), + date.getYear()].join("-"); + var table = document.getElementById(tableId); + if (table === null) { + table = document.createElement("table"); + table.id = tableId; + + // Caption contains human-readable date + var caption = document.createElement("caption"); + caption.className = "date"; + var options = { + "weekday": "long", + "year": "numeric", + "month": "long", + "day": "numeric", + }; + caption.innerHTML = date.toLocaleDateString("en-US", options); + table.appendChild(caption); + + // Add table to page + HIST_CONTAINER.appendChild(table); + } + + // Find/create tbody + var tbody = table.lastChild; + if (tbody.tagName !== "TBODY") { + tbody = document.createElement("tbody"); + table.appendChild(tbody); + } + + // Create session-separator and new tbody if necessary + if (tbody.lastChild !== null && lastItemDate !== null && + window.SESSION_INTERVAL > 0) { + var interval = lastItemDate.getTime() - date.getTime(); + if (interval > window.SESSION_INTERVAL) { + // Add session-separator + var sessionSeparator = document.createElement("td"); + sessionSeparator.className = "session-separator"; + sessionSeparator.colSpan = 2; + sessionSeparator.innerHTML = "§"; + table.appendChild(document.createElement("tr")); + table.lastChild.appendChild(sessionSeparator); + + // Create new tbody + tbody = document.createElement("tbody"); + table.appendChild(tbody); + } + } + + return tbody; + } + + /** + * Given a history item, create and return for it. + * + * @param {string} itemUrl - The url for this item. + * @param {string} itemTitle - The title for this item. + * @param {string} itemTime - The formatted time for this item. + * @returns {Element} the completed tr. + */ + function makeHistoryRow(itemUrl, itemTitle, itemTime) { + var row = document.createElement("tr"); + + var title = document.createElement("td"); + title.className = "title"; + var link = document.createElement("a"); + link.href = itemUrl; + link.innerHTML = itemTitle; + var host = document.createElement("span"); + host.className = "hostname"; + host.innerHTML = link.hostname; + title.appendChild(link); + title.appendChild(host); + + var time = document.createElement("td"); + time.className = "time"; + time.innerHTML = itemTime; + + row.appendChild(title); + row.appendChild(time); + + return row; + } + + /** + * Get JSON from given URL. + * + * @param {string} url - the url to fetch data from. + * @param {function} callback - the function to callback with data. + * @returns {void} + */ + function getJSON(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, true); + xhr.responseType = "json"; + xhr.onload = function() { + var status = xhr.status; + callback(status, xhr.response); + }; + xhr.send(); + } + + /** + * Receive history data from qute://history/data. + * + * @param {Number} status - The status of the query. + * @param {Array} history - History data. + * @returns {void} + */ + function receiveHistory(status, history) { + if (history === null) { + return; + } + + for (var i = 0, len = history.length - 1; i < len; i++) { + var item = history[i]; + var currentItemDate = new Date(item.time); + getSessionNode(currentItemDate).appendChild(makeHistoryRow( + item.url, item.title, currentItemDate.toLocaleTimeString() + )); + lastItemDate = currentItemDate; + } + + var next = history[history.length - 1].next; + if (next === -1) { + // Reached end of history + window.onscroll = null; + EOF_MESSAGE.style.display = "block"; + LOAD_LINK.style.display = "none"; + } else { + nextTime = next; + } + } + + /** + * Load new history. + * @return {void} + */ + function loadHistory() { + if (nextTime === null) { + getJSON(DATA_URL, receiveHistory); + } else { + var url = DATA_URL.concat("?start_time=", nextTime.toString()); + getJSON(url, receiveHistory); + } + } + + return loadHistory; +})(); diff --git a/qutebrowser/javascript/position_caret.js b/qutebrowser/javascript/position_caret.js index 09d2301ad..4f6c32380 100644 --- a/qutebrowser/javascript/position_caret.js +++ b/qutebrowser/javascript/position_caret.js @@ -1,6 +1,6 @@ /** * Copyright 2015 Artur Shaik -* Copyright 2015-2016 Florian Bruhin (The Compiler) +* Copyright 2015-2017 Florian Bruhin (The Compiler) * * This file is part of qutebrowser. * diff --git a/qutebrowser/javascript/scroll.js b/qutebrowser/javascript/scroll.js index ac19d175c..35f412783 100644 --- a/qutebrowser/javascript/scroll.js +++ b/qutebrowser/javascript/scroll.js @@ -1,5 +1,5 @@ /** - * Copyright 2016 Florian Bruhin (The Compiler) + * Copyright 2016-2017 Florian Bruhin (The Compiler) * * This file is part of qutebrowser. * diff --git a/qutebrowser/javascript/webelem.js b/qutebrowser/javascript/webelem.js index 206cdf129..6d763b8df 100644 --- a/qutebrowser/javascript/webelem.js +++ b/qutebrowser/javascript/webelem.js @@ -1,5 +1,5 @@ /** - * Copyright 2016 Florian Bruhin (The Compiler) + * Copyright 2016-2017 Florian Bruhin (The Compiler) * * This file is part of qutebrowser. * @@ -50,14 +50,33 @@ window._qutebrowser.webelem = (function() { var out = { "id": id, - "text": elem.text, "value": elem.value, - "tag_name": elem.tagName, "outer_xml": elem.outerHTML, - "class_name": elem.className, "rects": [], // Gets filled up later }; + // https://github.com/qutebrowser/qutebrowser/issues/2569 + if (typeof elem.tagName === "string") { + out.tag_name = elem.tagName; + } else if (typeof elem.nodeName === "string") { + out.tag_name = elem.nodeName; + } else { + out.tag_name = ""; + } + + if (typeof elem.className === "string") { + out.class_name = elem.className; + } else { + // e.g. SVG elements + out.class_name = ""; + } + + if (typeof elem.textContent === "string") { + out.text = elem.textContent; + } else if (typeof elem.text === "string") { + out.text = elem.text; + } // else: don't add the text at all + var attributes = {}; for (var i = 0; i < elem.attributes.length; ++i) { var attr = elem.attributes[i]; @@ -150,6 +169,15 @@ window._qutebrowser.webelem = (function() { return serialize_elem(elem); }; + // Function for returning a selection to python (so we can click it) + funcs.find_selected_link = function() { + var elem = window.getSelection().anchorNode; + if (!elem) { + return null; + } + return serialize_elem(elem.parentNode); + }; + funcs.set_value = function(id, value) { elements[id].value = value; }; diff --git a/qutebrowser/keyinput/__init__.py b/qutebrowser/keyinput/__init__.py index 6fb35e5d6..c6b95b8a9 100644 --- a/qutebrowser/keyinput/__init__.py +++ b/qutebrowser/keyinput/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 7325223a3..670cde853 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/keyinput/keyparser.py b/qutebrowser/keyinput/keyparser.py index 0b46dffc4..f9a64edca 100644 --- a/qutebrowser/keyinput/keyparser.py +++ b/qutebrowser/keyinput/keyparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/keyinput/macros.py b/qutebrowser/keyinput/macros.py index 8176e5652..9e3667590 100644 --- a/qutebrowser/keyinput/macros.py +++ b/qutebrowser/keyinput/macros.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Jan Verbeek (blyxxyz) +# Copyright 2016-2017 Jan Verbeek (blyxxyz) # # This file is part of qutebrowser. # diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 542081719..5ce55e670 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index db540b58e..4ef393b03 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/__init__.py b/qutebrowser/mainwindow/__init__.py index 178413514..43eb563a9 100644 --- a/qutebrowser/mainwindow/__init__.py +++ b/qutebrowser/mainwindow/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 9858abf7d..96489fbac 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -184,8 +184,6 @@ class MainWindow(QWidget): self._keyhint = keyhintwidget.KeyHintView(self.win_id, self) self._add_overlay(self._keyhint, self._keyhint.update_geometry) - self._messageview = messageview.MessageView(parent=self) - self._add_overlay(self._messageview, self._messageview.update_geometry) self._prompt_container = prompt.PromptContainer(self.win_id, self) self._add_overlay(self._prompt_container, @@ -195,6 +193,9 @@ class MainWindow(QWidget): scope='window', window=self.win_id) self._prompt_container.hide() + self._messageview = messageview.MessageView(parent=self) + self._add_overlay(self._messageview, self._messageview.update_geometry) + if geometry is not None: self._load_geometry(geometry) elif self.win_id == 0: diff --git a/qutebrowser/mainwindow/messageview.py b/qutebrowser/mainwindow/messageview.py index 6565a2a44..7d0d2b682 100644 --- a/qutebrowser/mainwindow/messageview.py +++ b/qutebrowser/mainwindow/messageview.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -98,7 +98,9 @@ class MessageView(QWidget): @config.change_filter('ui', 'message-timeout') def _set_clear_timer_interval(self): """Configure self._clear_timer according to the config.""" - self._clear_timer.setInterval(config.get('ui', 'message-timeout')) + interval = config.get('ui', 'message-timeout') + if interval != 0: + self._clear_timer.setInterval(interval) @pyqtSlot() def clear_messages(self): @@ -125,8 +127,14 @@ class MessageView(QWidget): widget = Message(level, text, replace=replace, parent=self) self._vbox.addWidget(widget) widget.show() - self._clear_timer.start() + if config.get('ui', 'message-timeout') != 0: + self._clear_timer.start() self._messages.append(widget) self._last_text = text self.show() self.update_geometry.emit() + + def mousePressEvent(self, e): + """Clear messages when they are clicked on.""" + if e.button() in [Qt.LeftButton, Qt.MiddleButton, Qt.RightButton]: + self.clear_messages() diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index afe8a72dd..c38a41caa 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -387,7 +387,7 @@ class PromptContainer(QWidget): @cmdutils.register(instance='prompt-container', hide=True, scope='window', modes=[usertypes.KeyMode.prompt], maxsplit=0) - def prompt_open_download(self, cmdline: str=None): + def prompt_open_download(self, cmdline: str = None): """Immediately open a download. If no specific command is given, this will use the system's default @@ -437,13 +437,13 @@ class LineEdit(QLineEdit): """Override keyPressEvent to paste primary selection on Shift + Ins.""" if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier: try: - text = utils.get_clipboard(selection=True) + text = utils.get_clipboard(selection=True, fallback=True) except utils.ClipboardError: # pragma: no cover - pass + e.ignore() else: e.accept() self.insert(text) - return + return super().keyPressEvent(e) def __repr__(self): @@ -475,6 +475,7 @@ class _BasePrompt(QWidget): if question.text is not None: # Not doing any HTML escaping here as the text can be formatted text_label = QLabel(question.text) + text_label.setTextInteractionFlags(Qt.TextSelectableByMouse) self._vbox.addWidget(text_label) def _init_key_label(self): @@ -644,6 +645,10 @@ class FilenamePrompt(_BasePrompt): def accept(self, value=None): text = value if value is not None else self._lineedit.text() + text = downloads.transform_path(text) + if text is None: + message.error("Invalid filename") + return False self.question.answer = text return True @@ -694,9 +699,11 @@ class DownloadFilenamePrompt(FilenamePrompt): self._file_model.setFilter(QDir.AllDirs | QDir.Drives | QDir.NoDot) def accept(self, value=None): - text = value if value is not None else self._lineedit.text() - self.question.answer = downloads.FileDownloadTarget(text) - return True + done = super().accept(value) + answer = self.question.answer + if answer is not None: + self.question.answer = downloads.FileDownloadTarget(answer) + return done def download_open(self, cmdline): self.question.answer = downloads.OpenFileDownloadTarget(cmdline) diff --git a/qutebrowser/mainwindow/statusbar/__init__.py b/qutebrowser/mainwindow/statusbar/__init__.py index c6a25fe0c..eb3ed7193 100644 --- a/qutebrowser/mainwindow/statusbar/__init__.py +++ b/qutebrowser/mainwindow/statusbar/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index eaf8e6ffc..cddf4ec35 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py index a5abaa290..d59c87dbc 100644 --- a/qutebrowser/mainwindow/statusbar/command.py +++ b/qutebrowser/mainwindow/statusbar/command.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/keystring.py b/qutebrowser/mainwindow/statusbar/keystring.py index 0baa8137c..dd9825ab2 100644 --- a/qutebrowser/mainwindow/statusbar/keystring.py +++ b/qutebrowser/mainwindow/statusbar/keystring.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/percentage.py b/qutebrowser/mainwindow/statusbar/percentage.py index 9c2d2dcd9..050b0747a 100644 --- a/qutebrowser/mainwindow/statusbar/percentage.py +++ b/qutebrowser/mainwindow/statusbar/percentage.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/progress.py b/qutebrowser/mainwindow/statusbar/progress.py index 17892fe33..e78d5307c 100644 --- a/qutebrowser/mainwindow/statusbar/progress.py +++ b/qutebrowser/mainwindow/statusbar/progress.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/tabindex.py b/qutebrowser/mainwindow/statusbar/tabindex.py index 7dda5f806..6a4cc987c 100644 --- a/qutebrowser/mainwindow/statusbar/tabindex.py +++ b/qutebrowser/mainwindow/statusbar/tabindex.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/text.py b/qutebrowser/mainwindow/statusbar/text.py index 2791385b7..e99891ecd 100644 --- a/qutebrowser/mainwindow/statusbar/text.py +++ b/qutebrowser/mainwindow/statusbar/text.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/textbase.py b/qutebrowser/mainwindow/statusbar/textbase.py index eb3064286..0ae271191 100644 --- a/qutebrowser/mainwindow/statusbar/textbase.py +++ b/qutebrowser/mainwindow/statusbar/textbase.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/mainwindow/statusbar/url.py b/qutebrowser/mainwindow/statusbar/url.py index bca1f0458..b54e020fa 100644 --- a/qutebrowser/mainwindow/statusbar/url.py +++ b/qutebrowser/mainwindow/statusbar/url.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -24,7 +24,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt, QUrl from qutebrowser.browser import browsertab from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.config import style -from qutebrowser.utils import usertypes +from qutebrowser.utils import usertypes, urlutils # Note this has entries for success/error/warn from widgets.webview:LoadStatus @@ -138,8 +138,10 @@ class UrlText(textbase.TextBase): """ if url is None: self._normal_url = None + elif not url.isValid(): + self._normal_url = "Invalid URL!" else: - self._normal_url = url.toDisplayString() + self._normal_url = urlutils.safe_display_string(url) self._normal_url_type = UrlType.normal self._update_url() @@ -156,9 +158,9 @@ class UrlText(textbase.TextBase): if link: qurl = QUrl(link) if qurl.isValid(): - self._hover_url = qurl.toDisplayString() + self._hover_url = urlutils.safe_display_string(qurl) else: - self._hover_url = link + self._hover_url = '(invalid URL!) {}'.format(link) else: self._hover_url = None self._update_url() @@ -167,6 +169,9 @@ class UrlText(textbase.TextBase): def on_tab_changed(self, tab): """Update URL if the tab changed.""" self._hover_url = None - self._normal_url = tab.url().toDisplayString() + if tab.url().isValid(): + self._normal_url = urlutils.safe_display_string(tab.url()) + else: + self._normal_url = '' self.on_load_status_changed(tab.load_status().name) self._update_url() diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 116befac5..6bce2166a 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -147,10 +147,14 @@ class TabbedBrowser(tabwidget.TabWidget): We don't implement this as generator so we can delete tabs while iterating over the list. """ - w = [] + widgets = [] for i in range(self.count()): - w.append(self.widget(i)) - return w + widget = self.widget(i) + if widget is None: + log.webview.debug("Got None-widget in tabbedbrowser!") + else: + widgets.append(widget) + return widgets @config.change_filter('ui', 'window-title-format') def update_window_title(self): @@ -670,11 +674,11 @@ class TabbedBrowser(tabwidget.TabWidget): else: raise ValueError("Invalid status {}".format(status)) - # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58698 - # FIXME:qtwebengine can we disable this with Qt 5.8.1? - self._remove_tab(tab, crashed=True) - if self.count() == 0: - self.tabopen(QUrl('about:blank')) + if not qtutils.version_check('5.9'): + # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58698 + self._remove_tab(tab, crashed=True) + if self.count() == 0: + self.tabopen(QUrl('about:blank')) def resizeEvent(self, e): """Extend resizeEvent of QWidget to emit a resized signal afterwards. diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index d2da192c9..b8eb8a226 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -22,9 +22,11 @@ import collections import functools -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QSize, QRect, QTimer, QUrl +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint, + QTimer, QUrl) from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle, - QStyle, QStylePainter, QStyleOptionTab) + QStyle, QStylePainter, QStyleOptionTab, + QStyleFactory) from PyQt5.QtGui import QIcon, QPalette, QColor from qutebrowser.utils import qtutils, objreg, utils, usertypes, log @@ -51,7 +53,7 @@ class TabWidget(QTabWidget): def __init__(self, win_id, parent=None): super().__init__(parent) bar = TabBar(win_id) - self.setStyle(TabBarStyle(self.style())) + self.setStyle(TabBarStyle()) self.setTabBar(bar) bar.tabCloseRequested.connect(self.tabCloseRequested) bar.tabMoved.connect(functools.partial( @@ -143,6 +145,9 @@ class TabWidget(QTabWidget): def get_tab_fields(self, idx): """Get the tab field data.""" tab = self.widget(idx) + if tab is None: + log.misc.debug("Got None-tab in get_tab_fields!") + page_title = self.page_title(idx) fields = {} @@ -297,10 +302,11 @@ class TabBar(QTabBar): def __init__(self, win_id, parent=None): super().__init__(parent) self._win_id = win_id - self.setStyle(TabBarStyle(self.style())) + self.setStyle(TabBarStyle()) self.set_font() config_obj = objreg.get('config') config_obj.changed.connect(self.set_font) + config_obj.changed.connect(self.set_icon_size) self.vertical = False self._page_fullscreen = False self._auto_hide_timer = QTimer() @@ -403,7 +409,13 @@ class TabBar(QTabBar): def set_font(self): """Set the tab bar font.""" self.setFont(config.get('fonts', 'tabbar')) + self.set_icon_size() + + @config.change_filter('tabs', 'favicon-scale') + def set_icon_size(self): + """Set the tab bar favicon size.""" size = self.fontMetrics().height() - 2 + size *= config.get('tabs', 'favicon-scale') self.setIconSize(QSize(size, size)) @config.change_filter('colors', 'tabs.bg.bar') @@ -424,11 +436,12 @@ class TabBar(QTabBar): button = config.get('tabs', 'close-mouse-button') if (e.button() == Qt.RightButton and button == 'right' or e.button() == Qt.MiddleButton and button == 'middle'): + e.accept() idx = self.tabAt(e.pos()) - if idx != -1: - e.accept() - self.tabCloseRequested.emit(idx) - return + if idx == -1: + idx = self.currentIndex() + self.tabCloseRequested.emit(idx) + return super().mousePressEvent(e) def minimumTabSizeHint(self, index): @@ -604,20 +617,14 @@ class TabBarStyle(QCommonStyle): http://stackoverflow.com/a/17294081 https://code.google.com/p/makehuman/source/browse/trunk/makehuman/lib/qtgui.py - - Attributes: - _style: The base/"parent" style. """ - def __init__(self, style): + def __init__(self): """Initialize all functions we're not overriding. This simply calls the corresponding function in self._style. - - Args: - style: The base/"parent" style. """ - self._style = style + self._style = QStyleFactory.create('Fusion') for method in ['drawComplexControl', 'drawItemPixmap', 'generatedIconPixmap', 'hitTestComplexControl', 'itemPixmapRect', 'itemTextRect', 'polish', 'styleHint', @@ -826,7 +833,7 @@ class TabBarStyle(QCommonStyle): 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_top = text_rect.center().y() + 1 - tab_icon_size.height() / 2 + icon_rect = QRect(QPoint(text_rect.left(), icon_top), tab_icon_size) icon_rect = self._style.visualRect(opt.direction, opt.rect, icon_rect) return icon_rect diff --git a/qutebrowser/misc/__init__.py b/qutebrowser/misc/__init__.py index 3dc51e6a9..03ad27aa8 100644 --- a/qutebrowser/misc/__init__.py +++ b/qutebrowser/misc/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/autoupdate.py b/qutebrowser/misc/autoupdate.py index 860064672..15a3b6670 100644 --- a/qutebrowser/misc/autoupdate.py +++ b/qutebrowser/misc/autoupdate.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/checkpyver.py b/qutebrowser/misc/checkpyver.py index 364b8bb57..34183041b 100644 --- a/qutebrowser/misc/checkpyver.py +++ b/qutebrowser/misc/checkpyver.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The-Compiler) +# Copyright 2014-2017 Florian Bruhin (The-Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/cmdhistory.py b/qutebrowser/misc/cmdhistory.py index 7f3dfd52f..b0990a67e 100644 --- a/qutebrowser/misc/cmdhistory.py +++ b/qutebrowser/misc/cmdhistory.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/consolewidget.py b/qutebrowser/misc/consolewidget.py index 485da0fe4..537621b4d 100644 --- a/qutebrowser/misc/consolewidget.py +++ b/qutebrowser/misc/consolewidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/crashdialog.py b/qutebrowser/misc/crashdialog.py index 0add7932a..045406c13 100644 --- a/qutebrowser/misc/crashdialog.py +++ b/qutebrowser/misc/crashdialog.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/crashsignal.py b/qutebrowser/misc/crashsignal.py index 35c39bf2e..5f161312a 100644 --- a/qutebrowser/misc/crashsignal.py +++ b/qutebrowser/misc/crashsignal.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -171,14 +171,14 @@ class CrashHandler(QObject): """ try: pages = self._recover_pages(forgiving=True) - except Exception: - log.destroy.exception("Error while recovering pages") + except Exception as e: + log.destroy.exception("Error while recovering pages: {}".format(e)) pages = [] try: cmd_history = objreg.get('command-history')[-5:] - except Exception: - log.destroy.exception("Error while getting history: {}") + except Exception as e: + log.destroy.exception("Error while getting history: {}".format(e)) cmd_history = [] try: @@ -207,10 +207,10 @@ class CrashHandler(QObject): is_ignored_exception = (exctype is bdb.BdbQuit or not issubclass(exctype, Exception)) - if self._args.pdb_postmortem: + if 'pdb-postmortem' in self._args.debug_flags: pdb.post_mortem(tb) - if is_ignored_exception or self._args.pdb_postmortem: + if is_ignored_exception or 'pdb-postmortem' in self._args.debug_flags: # pdb exit, KeyboardInterrupt, ... status = 0 if is_ignored_exception else 2 try: diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index 3622dc36a..99fd3e1a3 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The-Compiler) +# Copyright 2014-2017 Florian Bruhin (The-Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/editor.py b/qutebrowser/misc/editor.py index a6f2854d8..58a08daf1 100644 --- a/qutebrowser/misc/editor.py +++ b/qutebrowser/misc/editor.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index eb0a036e5..e8d224f2e 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -142,6 +142,7 @@ class GUIProcess(QObject): self._proc.start(cmd, args) else: self._proc.start(cmd, args, mode) + self._proc.closeWriteChannel() def start_detached(self, cmd, args, cwd=None): """Convenience wrapper around QProcess::startDetached.""" diff --git a/qutebrowser/misc/httpclient.py b/qutebrowser/misc/httpclient.py index fc6f94f59..4d33d487e 100644 --- a/qutebrowser/misc/httpclient.py +++ b/qutebrowser/misc/httpclient.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/ipc.py b/qutebrowser/misc/ipc.py index 58bea9ca7..eb9aa4a3b 100644 --- a/qutebrowser/misc/ipc.py +++ b/qutebrowser/misc/ipc.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -27,7 +27,7 @@ import getpass import binascii import hashlib -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt, QTimer +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket import qutebrowser @@ -182,6 +182,7 @@ class IPCServer(QObject): self._server.newConnection.connect(self.handle_connection) self._socket = None + self._old_socket = None self._socketopts_ok = os.name == 'nt' if self._socketopts_ok: # pragma: no cover # If we use setSocketOptions on Unix with Qt < 5.4, we get a @@ -278,15 +279,8 @@ class IPCServer(QObject): log.ipc.debug("Client disconnected from socket 0x{:x}.".format( id(self._socket))) self._timer.stop() - if self._socket is None: - log.ipc.debug("In on_disconnected with None socket!") - else: - # For some reason Qt can still get delayed canReadNotifications - # internally, so if we call deleteLater() right away and then call - # QApplication::processEvents() somewhere in the code, we can get a - # segfault. - QTimer.singleShot(500, self._socket.deleteLater) - self._socket = None + self._old_socket = self._socket + self._socket = None # Maybe another connection is waiting. self.handle_connection() @@ -349,17 +343,23 @@ class IPCServer(QObject): @pyqtSlot() def on_ready_read(self): """Read json data from the client.""" - if self._socket is None: + if self._socket is None: # pragma: no cover # This happens when doing a connection while another one is already # active for some reason. - log.ipc.warning("In on_ready_read with None socket!") - return + if self._old_socket is None: + log.ipc.warning("In on_ready_read with None socket and " + "old_socket!") + return + log.ipc.debug("In on_ready_read with None socket!") + socket = self._old_socket + else: + socket = self._socket self._timer.stop() - while self._socket is not None and self._socket.canReadLine(): - data = bytes(self._socket.readLine()) + while socket is not None and socket.canReadLine(): + data = bytes(socket.readLine()) self.got_raw.emit(data) log.ipc.debug("Read from socket 0x{:x}: {!r}".format( - id(self._socket), data)) + id(socket), data)) self._handle_data(data) self._timer.start() diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py index c66712eb9..b42612b1b 100644 --- a/qutebrowser/misc/keyhintwidget.py +++ b/qutebrowser/misc/keyhintwidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # @@ -67,7 +67,6 @@ class KeyHintView(QLabel): self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum) self.hide() self._show_timer = usertypes.Timer(self, 'keyhint_show') - self._show_timer.setInterval(500) self._show_timer.timeout.connect(self.show) style.set_register_stylesheet(self) @@ -108,6 +107,7 @@ class KeyHintView(QLabel): return # delay so a quickly typed keychain doesn't display hints + self._show_timer.setInterval(config.get('ui', 'keyhint-delay')) self._show_timer.start() suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix')) diff --git a/qutebrowser/misc/lineparser.py b/qutebrowser/misc/lineparser.py index 55bae7142..ea9d100b7 100644 --- a/qutebrowser/misc/lineparser.py +++ b/qutebrowser/misc/lineparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index 0337269ea..48f775b85 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -46,13 +46,13 @@ class MinimalLineEditMixin: """Override keyPressEvent to paste primary selection on Shift + Ins.""" if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier: try: - text = utils.get_clipboard(selection=True) + text = utils.get_clipboard(selection=True, fallback=True) except utils.ClipboardError: - pass + e.ignore() else: e.accept() self.insert(text) - return + return super().keyPressEvent(e) def __repr__(self): diff --git a/qutebrowser/misc/msgbox.py b/qutebrowser/misc/msgbox.py index f6f29c38d..2c8aaf85e 100644 --- a/qutebrowser/misc/msgbox.py +++ b/qutebrowser/misc/msgbox.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/pastebin.py b/qutebrowser/misc/pastebin.py index 40dd77f33..9c30ca067 100644 --- a/qutebrowser/misc/pastebin.py +++ b/qutebrowser/misc/pastebin.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/readline.py b/qutebrowser/misc/readline.py index 0089ebe7c..2bc999c4d 100644 --- a/qutebrowser/misc/readline.py +++ b/qutebrowser/misc/readline.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/savemanager.py b/qutebrowser/misc/savemanager.py index 7e85b013e..509e5489a 100644 --- a/qutebrowser/misc/savemanager.py +++ b/qutebrowser/misc/savemanager.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index da7b9bf27..94c5855bd 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -451,7 +451,7 @@ class SessionManager(QObject): @cmdutils.register(name=['session-save', 'w'], instance='session-manager') @cmdutils.argument('name', completion=usertypes.Completion.sessions) @cmdutils.argument('win_id', win_id=True) - def session_save(self, name: str=default, current=False, quiet=False, + def session_save(self, name: str = default, current=False, quiet=False, force=False, only_active_window=False, win_id=None): """Save a session. @@ -463,9 +463,7 @@ class SessionManager(QObject): force: Force saving internal sessions (starting with an underline). only_active_window: Saves only tabs of the currently active window. """ - if (name is not default and - name.startswith('_') and # pylint: disable=no-member - not force): + if name is not default and name.startswith('_') and not force: raise cmdexc.CommandError("{} is an internal session, use --force " "to save anyways.".format(name)) if current: diff --git a/qutebrowser/misc/split.py b/qutebrowser/misc/split.py index cb7a38d81..3f3b2d362 100644 --- a/qutebrowser/misc/split.py +++ b/qutebrowser/misc/split.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index e617c1af2..41b44de1f 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -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) @@ -293,15 +293,24 @@ def debug_log_filter(filters: str): """Change the log filter for console logging. Args: - filters: A comma separated list of logger names. + filters: A comma separated list of logger names. Can also be "none" to + clear any existing filters. """ - if set(filters.split(',')).issubset(log.LOGGER_NAMES): - log.console_filter.names = filters.split(',') - else: + if log.console_filter is None: + raise cmdexc.CommandError("No log.console_filter. Not attached " + "to a console?") + + if filters.strip().lower() == 'none': + log.console_filter.names = None + return + + if not set(filters.split(',')).issubset(log.LOGGER_NAMES): raise cmdexc.CommandError("filters: Invalid value {} - expected one " "of: {}".format(filters, ', '.join(log.LOGGER_NAMES))) + log.console_filter.names = filters.split(',') + @cmdutils.register() @cmdutils.argument('current_win_id', win_id=True) diff --git a/qutebrowser/qutebrowser.py b/qutebrowser/qutebrowser.py index 8321fb04b..b4673ecff 100644 --- a/qutebrowser/qutebrowser.py +++ b/qutebrowser/qutebrowser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -23,7 +23,6 @@ import sys import json import qutebrowser -from qutebrowser.utils import log try: from qutebrowser.misc.checkpyver import check_python_version except ImportError: @@ -38,6 +37,7 @@ except ImportError: sys.stderr.flush() sys.exit(100) check_python_version() +from qutebrowser.utils import log import argparse from qutebrowser.misc import earlyinit @@ -103,10 +103,6 @@ def get_argparser(): help="Silently remove unknown config options.") debug.add_argument('--nowindow', action='store_true', help="Don't show " "the main window.") - debug.add_argument('--debug-exit', help="Turn on debugging of late exit.", - action='store_true') - debug.add_argument('--pdb-postmortem', action='store_true', - help="Drop into pdb on exceptions.") debug.add_argument('--temp-basedir', action='store_true', help="Use a " "temporary basedir.") debug.add_argument('--no-err-windows', action='store_true', help="Don't " @@ -118,6 +114,9 @@ def get_argparser(): action='append') debug.add_argument('--qt-flag', help="Pass an argument to Qt as flag.", nargs=1, action='append') + debug.add_argument('--debug-flag', type=debug_flag_error, default=[], + help="Pass name of debugging feature to be turned on.", + action='append', dest='debug_flags') parser.add_argument('command', nargs='*', help="Commands to execute on " "startup.", metavar=':command') # URLs will actually be in command @@ -131,7 +130,7 @@ def directory(arg): raise argparse.ArgumentTypeError("Invalid empty value") -def logfilter_error(logfilter: str): +def logfilter_error(logfilter): """Validate logger names passed to --logfilter. Args: @@ -145,6 +144,22 @@ def logfilter_error(logfilter: str): logfilter, ', '.join(log.LOGGER_NAMES))) +def debug_flag_error(flag): + """Validate flags passed to --debug-flag. + + Available flags: + debug-exit: Turn on debugging of late exit. + pdb-postmortem: Drop into pdb on exceptions. + """ + valid_flags = ['debug-exit', 'pdb-postmortem'] + + if flag in valid_flags: + return flag + else: + raise argparse.ArgumentTypeError("Invalid debug flag - valid flags: {}" + .format(', '.join(valid_flags))) + + def main(): parser = get_argparser() if sys.platform == 'darwin' and getattr(sys, 'frozen', False): diff --git a/qutebrowser/utils/__init__.py b/qutebrowser/utils/__init__.py index 763345a7b..c28a40b10 100644 --- a/qutebrowser/utils/__init__.py +++ b/qutebrowser/utils/__init__.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/debug.py b/qutebrowser/utils/debug.py index 89ae62faf..5da5234a9 100644 --- a/qutebrowser/utils/debug.py +++ b/qutebrowser/utils/debug.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -156,6 +156,10 @@ def qflags_key(base, value, add_base=False, klass=None): klass = value.__class__ if klass == int: raise TypeError("Can't guess enum class of an int!") + + if not value: + return qenum_key(base, value, add_base, klass) + bits = [] names = [] mask = 0x01 diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py index 40cb0cb70..1a3b4312d 100644 --- a/qutebrowser/utils/docutils.py +++ b/qutebrowser/utils/docutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/error.py b/qutebrowser/utils/error.py index 6a818857f..0d045bf19 100644 --- a/qutebrowser/utils/error.py +++ b/qutebrowser/utils/error.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/javascript.py b/qutebrowser/utils/javascript.py index 4fc7e546c..f536fed1f 100644 --- a/qutebrowser/utils/javascript.py +++ b/qutebrowser/utils/javascript.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index 2ad2be448..731dec8e4 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 7c96d4072..1cea959ee 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -161,11 +161,6 @@ def stub(suffix=''): misc.warning(text) -class CriticalQtWarning(Exception): - - """Exception raised when there's a critical Qt warning.""" - - def init_log(args): """Init loggers based on the argparse namespace passed.""" level = args.loglevel.upper() @@ -182,9 +177,10 @@ def init_log(args): root = logging.getLogger() global console_filter if console is not None: + console_filter = LogFilter(None) if args.logfilter is not None: - console_filter = LogFilter(args.logfilter.split(',')) - console.addFilter(console_filter) + console_filter.names = args.logfilter.split(',') + console.addFilter(console_filter) root.addHandler(console) if ram is not None: root.addHandler(ram) @@ -423,17 +419,7 @@ def qt_message_handler(msg_type, context, msg): 'with: -9805', # flake8: disable=E131 ] - # Messages which will trigger an exception immediately - critical_msgs = [ - 'Could not parse stylesheet of object', - ] - - if any(msg.strip().startswith(pattern) for pattern in critical_msgs): - # For some reason, the stack gets lost when raising here... - logger = logging.getLogger('misc') - logger.error("Got critical Qt warning!", stack_info=True) - raise CriticalQtWarning(msg) - elif any(msg.strip().startswith(pattern) for pattern in suppressed_msgs): + if any(msg.strip().startswith(pattern) for pattern in suppressed_msgs): level = logging.DEBUG else: level = qt_to_logging[msg_type] diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index bb758d78a..35ab604b0 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -18,7 +18,7 @@ # along with qutebrowser. If not, see . # Because every method needs to have a log_stack argument -# pylint: disable=unused-variable +# pylint: disable=unused-argument """Message singleton so we don't have to define unneeded signals.""" diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index e54502ef8..4df08ba0d 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/qtutils.py b/qutebrowser/utils/qtutils.py index e36fd0ffb..15c9e72ce 100644 --- a/qutebrowser/utils/qtutils.py +++ b/qutebrowser/utils/qtutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -36,6 +36,10 @@ import contextlib from PyQt5.QtCore import (qVersion, QEventLoop, QDataStream, QByteArray, QIODevice, QSaveFile, QT_VERSION_STR) from PyQt5.QtWidgets import QApplication +try: + from PyQt5.QtWebKit import qWebKitVersion +except ImportError: # pragma: no cover + qWebKitVersion = None from qutebrowser.utils import log @@ -100,13 +104,10 @@ def version_check(version, exact=False, strict=False): return result -def is_qtwebkit_ng(version): - """Check if the given version is QtWebKit-NG. - - This is typically used as is_webkit_ng(qWebKitVersion) but we don't want to - have QtWebKit imports in here. - """ - return (pkg_resources.parse_version(version) > +def is_qtwebkit_ng(): + """Check if the given version is QtWebKit-NG.""" + assert qWebKitVersion is not None + return (pkg_resources.parse_version(qWebKitVersion()) > pkg_resources.parse_version('538.1')) @@ -174,12 +175,6 @@ def ensure_valid(obj): raise QtValueError(obj) -def ensure_not_null(obj): - """Ensure a Qt object with an .isNull() method is not null.""" - if obj.isNull(): - raise QtValueError(obj, null=True) - - def check_qdatastream(stream): """Check the status of a QDataStream and raise OSError if it's not ok.""" status_to_str = { @@ -412,15 +407,12 @@ class QtValueError(ValueError): """Exception which gets raised by ensure_valid.""" - def __init__(self, obj, null=False): + def __init__(self, obj): try: self.reason = obj.errorString() except AttributeError: self.reason = None - if null: - err = "{} is null".format(obj) - else: - err = "{} is not valid".format(obj) + err = "{} is not valid".format(obj) if self.reason: err += ": {}".format(self.reason) super().__init__(err) diff --git a/qutebrowser/utils/standarddir.py b/qutebrowser/utils/standarddir.py index 7c756b805..a1cedddd0 100644 --- a/qutebrowser/utils/standarddir.py +++ b/qutebrowser/utils/standarddir.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/typing.py b/qutebrowser/utils/typing.py index dca42acd6..358a1a5a3 100644 --- a/qutebrowser/utils/typing.py +++ b/qutebrowser/utils/typing.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index 1beebbe92..4cd0e94d0 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -592,6 +592,30 @@ def data_url(mimetype, data): return url +def safe_display_string(qurl): + """Get a IDN-homograph phishing safe form of the given QUrl. + + If we're dealing with a Punycode-encoded URL, this prepends the hostname in + its encoded form, to make sure those URLs are distinguishable. + + See https://github.com/qutebrowser/qutebrowser/issues/2547 + and https://bugreports.qt.io/browse/QTBUG-60365 + """ + if not qurl.isValid(): + raise InvalidUrlError(qurl) + + host = qurl.host(QUrl.FullyEncoded) + if '..' in host: + # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-60364 + return '(unparseable URL!) {}'.format(qurl.toDisplayString()) + + for part in host.split('.'): + if part.startswith('xn--') and host != qurl.host(QUrl.FullyDecoded): + return '({}) {}'.format(host, qurl.toDisplayString()) + + return qurl.toDisplayString() + + class InvalidProxyTypeError(Exception): """Error raised when proxy_from_url gets an unknown proxy type.""" diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 34148f9af..7d31ba6ac 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index fd4466e2a..d2de174a8 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -20,6 +20,7 @@ """Other utilities which don't fit anywhere else.""" import io +import re import sys import enum import json @@ -53,6 +54,10 @@ class SelectionUnsupportedError(ClipboardError): """Raised if [gs]et_clipboard is used and selection=True is unsupported.""" + def __init__(self): + super().__init__("Primary selection is not supported on this " + "platform!") + class ClipboardEmptyError(ClipboardError): @@ -762,11 +767,23 @@ def set_clipboard(data, selection=False): QApplication.clipboard().setText(data, mode=mode) -def get_clipboard(selection=False): - """Get data from the clipboard.""" +def get_clipboard(selection=False, fallback=False): + """Get data from the clipboard. + + Args: + selection: Use the primary selection. + fallback: Fall back to the clipboard if primary selection is + unavailable. + """ global fake_clipboard + if fallback and not selection: + raise ValueError("fallback given without selection!") + if selection and not supports_selection(): - raise SelectionUnsupportedError + if fallback: + selection = False + else: + raise SelectionUnsupportedError if fake_clipboard is not None: data = fake_clipboard @@ -845,3 +862,21 @@ def open_file(filename, cmdline=None): def unused(_arg): """Function which does nothing to avoid pylint complaining.""" pass + + +def expand_windows_drive(path): + r"""Expand a drive-path like E: into E:\. + + Does nothing for other paths. + + Args: + path: The path to expand. + """ + # Usually, "E:" on Windows refers to the current working directory on drive + # E:\. The correct way to specifify drive E: is "E:\", but most users + # probably don't use the "multiple working directories" feature and expect + # "E:" and "E:\" to be equal. + if re.match(r'[A-Z]:$', path, re.IGNORECASE): + return path + "\\" + else: + return path diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index e6a011f44..a8e115ca5 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -257,8 +257,7 @@ def _backend(): """Get the backend line with relevant information.""" if objects.backend == usertypes.Backend.QtWebKit: return 'QtWebKit{} (WebKit {})'.format( - '-NG' if qtutils.is_qtwebkit_ng(qWebKitVersion()) else '', - qWebKitVersion()) + '-NG' if qtutils.is_qtwebkit_ng() else '', qWebKitVersion()) else: webengine = usertypes.Backend.QtWebEngine assert objects.backend == webengine, objects.backend diff --git a/requirements.txt b/requirements.txt index a62285a08..b2cc93c1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -colorama==0.3.7 +colorama==0.3.9 cssutils==1.0.2 -Jinja2==2.9.5 +Jinja2==2.9.6 MarkupSafe==1.0 Pygments==2.2.0 pyPEG2==2.15.2 diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py index 613f622a8..cfdc1b8d5 100755 --- a/scripts/asciidoc2html.py +++ b/scripts/asciidoc2html.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/dev/build_release.py b/scripts/dev/build_release.py index fb8fe000d..aca2d0ef0 100755 --- a/scripts/dev/build_release.py +++ b/scripts/dev/build_release.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py index c876acd1a..4abb43f8b 100644 --- a/scripts/dev/check_coverage.py +++ b/scripts/dev/check_coverage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -41,132 +41,132 @@ MsgType = enum.Enum('MsgType', 'insufficent_coverage, perfect_file') # A list of (test_file, tested_file) tuples. test_file can be None. PERFECT_FILES = [ (None, - 'qutebrowser/commands/cmdexc.py'), + 'commands/cmdexc.py'), ('tests/unit/commands/test_cmdutils.py', - 'qutebrowser/commands/cmdutils.py'), + 'commands/cmdutils.py'), ('tests/unit/commands/test_argparser.py', - 'qutebrowser/commands/argparser.py'), + 'commands/argparser.py'), ('tests/unit/browser/webkit/test_cache.py', - 'qutebrowser/browser/webkit/cache.py'), + 'browser/webkit/cache.py'), ('tests/unit/browser/webkit/test_cookies.py', - 'qutebrowser/browser/webkit/cookies.py'), + 'browser/webkit/cookies.py'), ('tests/unit/browser/webkit/test_history.py', - 'qutebrowser/browser/history.py'), + 'browser/history.py'), ('tests/unit/browser/webkit/test_history.py', - 'qutebrowser/browser/webkit/webkithistory.py'), + 'browser/webkit/webkithistory.py'), ('tests/unit/browser/webkit/http/test_http.py', - 'qutebrowser/browser/webkit/http.py'), + 'browser/webkit/http.py'), ('tests/unit/browser/webkit/http/test_content_disposition.py', - 'qutebrowser/browser/webkit/rfc6266.py'), + 'browser/webkit/rfc6266.py'), # ('tests/unit/browser/webkit/test_webkitelem.py', - # 'qutebrowser/browser/webkit/webkitelem.py'), + # 'browser/webkit/webkitelem.py'), # ('tests/unit/browser/webkit/test_webkitelem.py', - # 'qutebrowser/browser/webelem.py'), + # 'browser/webelem.py'), ('tests/unit/browser/webkit/network/test_schemehandler.py', - 'qutebrowser/browser/webkit/network/schemehandler.py'), + 'browser/webkit/network/schemehandler.py'), ('tests/unit/browser/webkit/network/test_filescheme.py', - 'qutebrowser/browser/webkit/network/filescheme.py'), + 'browser/webkit/network/filescheme.py'), ('tests/unit/browser/webkit/network/test_networkreply.py', - 'qutebrowser/browser/webkit/network/networkreply.py'), + 'browser/webkit/network/networkreply.py'), ('tests/unit/browser/test_signalfilter.py', - 'qutebrowser/browser/signalfilter.py'), + 'browser/signalfilter.py'), (None, - 'qutebrowser/browser/webkit/certificateerror.py'), + 'browser/webkit/certificateerror.py'), # ('tests/unit/browser/test_tab.py', - # 'qutebrowser/browser/tab.py'), + # 'browser/tab.py'), ('tests/unit/keyinput/test_basekeyparser.py', - 'qutebrowser/keyinput/basekeyparser.py'), + 'keyinput/basekeyparser.py'), ('tests/unit/misc/test_autoupdate.py', - 'qutebrowser/misc/autoupdate.py'), + 'misc/autoupdate.py'), ('tests/unit/misc/test_readline.py', - 'qutebrowser/misc/readline.py'), + 'misc/readline.py'), ('tests/unit/misc/test_split.py', - 'qutebrowser/misc/split.py'), + 'misc/split.py'), ('tests/unit/misc/test_msgbox.py', - 'qutebrowser/misc/msgbox.py'), + 'misc/msgbox.py'), ('tests/unit/misc/test_checkpyver.py', - 'qutebrowser/misc/checkpyver.py'), + 'misc/checkpyver.py'), ('tests/unit/misc/test_guiprocess.py', - 'qutebrowser/misc/guiprocess.py'), + 'misc/guiprocess.py'), ('tests/unit/misc/test_editor.py', - 'qutebrowser/misc/editor.py'), + 'misc/editor.py'), ('tests/unit/misc/test_cmdhistory.py', - 'qutebrowser/misc/cmdhistory.py'), + 'misc/cmdhistory.py'), ('tests/unit/misc/test_ipc.py', - 'qutebrowser/misc/ipc.py'), + 'misc/ipc.py'), ('tests/unit/misc/test_keyhints.py', - 'qutebrowser/misc/keyhintwidget.py'), + 'misc/keyhintwidget.py'), ('tests/unit/misc/test_pastebin.py', - 'qutebrowser/misc/pastebin.py'), + 'misc/pastebin.py'), (None, - 'qutebrowser/misc/objects.py'), + 'misc/objects.py'), (None, - 'qutebrowser/mainwindow/statusbar/keystring.py'), + 'mainwindow/statusbar/keystring.py'), ('tests/unit/mainwindow/statusbar/test_percentage.py', - 'qutebrowser/mainwindow/statusbar/percentage.py'), + 'mainwindow/statusbar/percentage.py'), ('tests/unit/mainwindow/statusbar/test_progress.py', - 'qutebrowser/mainwindow/statusbar/progress.py'), + 'mainwindow/statusbar/progress.py'), ('tests/unit/mainwindow/statusbar/test_tabindex.py', - 'qutebrowser/mainwindow/statusbar/tabindex.py'), + 'mainwindow/statusbar/tabindex.py'), ('tests/unit/mainwindow/statusbar/test_textbase.py', - 'qutebrowser/mainwindow/statusbar/textbase.py'), + 'mainwindow/statusbar/textbase.py'), ('tests/unit/mainwindow/statusbar/test_url.py', - 'qutebrowser/mainwindow/statusbar/url.py'), + 'mainwindow/statusbar/url.py'), ('tests/unit/mainwindow/test_messageview.py', - 'qutebrowser/mainwindow/messageview.py'), + 'mainwindow/messageview.py'), ('tests/unit/config/test_configtypes.py', - 'qutebrowser/config/configtypes.py'), + 'config/configtypes.py'), ('tests/unit/config/test_configdata.py', - 'qutebrowser/config/configdata.py'), + 'config/configdata.py'), ('tests/unit/config/test_configexc.py', - 'qutebrowser/config/configexc.py'), + 'config/configexc.py'), ('tests/unit/config/test_textwrapper.py', - 'qutebrowser/config/textwrapper.py'), + 'config/textwrapper.py'), ('tests/unit/config/test_style.py', - 'qutebrowser/config/style.py'), + 'config/style.py'), ('tests/unit/utils/test_qtutils.py', - 'qutebrowser/utils/qtutils.py'), + 'utils/qtutils.py'), ('tests/unit/utils/test_standarddir.py', - 'qutebrowser/utils/standarddir.py'), + 'utils/standarddir.py'), ('tests/unit/utils/test_urlutils.py', - 'qutebrowser/utils/urlutils.py'), + 'utils/urlutils.py'), ('tests/unit/utils/usertypes', - 'qutebrowser/utils/usertypes.py'), + 'utils/usertypes.py'), ('tests/unit/utils/test_utils.py', - 'qutebrowser/utils/utils.py'), + 'utils/utils.py'), ('tests/unit/utils/test_version.py', - 'qutebrowser/utils/version.py'), + 'utils/version.py'), ('tests/unit/utils/test_debug.py', - 'qutebrowser/utils/debug.py'), + 'utils/debug.py'), ('tests/unit/utils/test_jinja.py', - 'qutebrowser/utils/jinja.py'), + 'utils/jinja.py'), ('tests/unit/utils/test_error.py', - 'qutebrowser/utils/error.py'), + 'utils/error.py'), ('tests/unit/utils/test_typing.py', - 'qutebrowser/utils/typing.py'), + 'utils/typing.py'), ('tests/unit/utils/test_javascript.py', - 'qutebrowser/utils/javascript.py'), + 'utils/javascript.py'), ('tests/unit/completion/test_models.py', - 'qutebrowser/completion/models/base.py'), + 'completion/models/base.py'), ('tests/unit/completion/test_sortfilter.py', - 'qutebrowser/completion/models/sortfilter.py'), + 'completion/models/sortfilter.py'), ] # 100% coverage because of end2end tests, but no perfect unit tests yet. WHITELISTED_FILES = [ - 'qutebrowser/browser/webkit/webkitinspector.py', - 'qutebrowser/keyinput/macros.py', - 'qutebrowser/browser/webkit/webkitelem.py', + 'browser/webkit/webkitinspector.py', + 'keyinput/macros.py', + 'browser/webkit/webkitelem.py', ] @@ -187,6 +187,8 @@ def _get_filename(filename): common_path = os.path.commonprefix([basedir, filename]) if common_path: filename = filename[len(common_path):].lstrip('/') + if filename.startswith('qutebrowser/'): + filename = filename.split('/', maxsplit=1)[1] return filename diff --git a/scripts/dev/check_doc_changes.py b/scripts/dev/check_doc_changes.py index d072bfb65..ab879b5ac 100755 --- a/scripts/dev/check_doc_changes.py +++ b/scripts/dev/check_doc_changes.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/dev/ci/appveyor_install.py b/scripts/dev/ci/appveyor_install.py index 2c04304d5..60c07bad6 100644 --- a/scripts/dev/ci/appveyor_install.py +++ b/scripts/dev/ci/appveyor_install.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -28,6 +28,7 @@ CI machines. from __future__ import print_function +import os import time import subprocess import urllib @@ -44,23 +45,6 @@ def pip_install(pkg): pkg]) -print("Getting PyQt5...") -qt_version = '5.5.1' -pyqt_version = '5.5.1' -pyqt_url = ('https://www.qutebrowser.org/pyqt/' - 'PyQt5-{}-gpl-Py3.4-Qt{}-x32.exe'.format( - pyqt_version, qt_version)) - -try: - urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe') -except (OSError, IOError): - print("Downloading PyQt failed, trying again in 10 seconds...") - time.sleep(10) - urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe') - -print("Installing PyQt5...") -subprocess.check_call([r'C:\install-PyQt5.exe', '/S']) - print("Installing tox") pip_install('pip') pip_install(r'-rmisc\requirements\requirements-tox.txt') @@ -69,4 +53,23 @@ print("Linking Python...") with open(r'C:\Windows\system32\python3.bat', 'w') as f: f.write(r'@C:\Python34\python %*') -check_setup(r'C:\Python34\python') + +if '-pyqt' not in os.environ['TESTENV']: + print("Getting PyQt5...") + qt_version = '5.5.1' + pyqt_version = '5.5.1' + pyqt_url = ('https://www.qutebrowser.org/pyqt/' + 'PyQt5-{}-gpl-Py3.4-Qt{}-x32.exe'.format( + pyqt_version, qt_version)) + + try: + urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe') + except (OSError, IOError): + print("Downloading PyQt failed, trying again in 10 seconds...") + time.sleep(10) + urllib.urlretrieve(pyqt_url, r'C:\install-PyQt5.exe') + + print("Installing PyQt5...") + subprocess.check_call([r'C:\install-PyQt5.exe', '/S']) + + check_setup(r'C:\Python34\python') diff --git a/scripts/dev/ci/travis_install.sh b/scripts/dev/ci/travis_install.sh index e752685f5..0ada134c1 100644 --- a/scripts/dev/ci/travis_install.sh +++ b/scripts/dev/ci/travis_install.sh @@ -1,6 +1,6 @@ # vim: ft=sh fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/dev/cleanup.py b/scripts/dev/cleanup.py index f08292017..49832eb3d 100755 --- a/scripts/dev/cleanup.py +++ b/scripts/dev/cleanup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/freeze.py b/scripts/dev/freeze.py index f254f4d90..8f99d2d35 100755 --- a/scripts/dev/freeze.py +++ b/scripts/dev/freeze.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/freeze_tests.py b/scripts/dev/freeze_tests.py index 9f9e2bbd2..97405b446 100755 --- a/scripts/dev/freeze_tests.py +++ b/scripts/dev/freeze_tests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/get_coredumpctl_traces.py b/scripts/dev/get_coredumpctl_traces.py index d1e962834..8cae2a190 100644 --- a/scripts/dev/get_coredumpctl_traces.py +++ b/scripts/dev/get_coredumpctl_traces.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/dev/misc_checks.py b/scripts/dev/misc_checks.py index 49ca8e48f..1bb263d15 100644 --- a/scripts/dev/misc_checks.py +++ b/scripts/dev/misc_checks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/pylint_checkers/qute_pylint/config.py b/scripts/dev/pylint_checkers/qute_pylint/config.py index b93b211f1..be8ac8da8 100644 --- a/scripts/dev/pylint_checkers/qute_pylint/config.py +++ b/scripts/dev/pylint_checkers/qute_pylint/config.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/pylint_checkers/qute_pylint/modeline.py b/scripts/dev/pylint_checkers/qute_pylint/modeline.py index 580837b34..ee3de13c9 100644 --- a/scripts/dev/pylint_checkers/qute_pylint/modeline.py +++ b/scripts/dev/pylint_checkers/qute_pylint/modeline.py @@ -1,4 +1,4 @@ -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # This file is part of qutebrowser. diff --git a/scripts/dev/pylint_checkers/qute_pylint/openencoding.py b/scripts/dev/pylint_checkers/qute_pylint/openencoding.py index 83926fc5e..eccc152ba 100644 --- a/scripts/dev/pylint_checkers/qute_pylint/openencoding.py +++ b/scripts/dev/pylint_checkers/qute_pylint/openencoding.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/dev/pylint_checkers/qute_pylint/settrace.py b/scripts/dev/pylint_checkers/qute_pylint/settrace.py index 9c1196daa..2bfa9f06f 100644 --- a/scripts/dev/pylint_checkers/qute_pylint/settrace.py +++ b/scripts/dev/pylint_checkers/qute_pylint/settrace.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/pylint_checkers/setup.py b/scripts/dev/pylint_checkers/setup.py index eaecf406e..960fdd2b7 100644 --- a/scripts/dev/pylint_checkers/setup.py +++ b/scripts/dev/pylint_checkers/setup.py @@ -2,7 +2,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/recompile_requirements.py b/scripts/dev/recompile_requirements.py index 1e3181344..44c56202a 100644 --- a/scripts/dev/recompile_requirements.py +++ b/scripts/dev/recompile_requirements.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/run_frozen_tests.py b/scripts/dev/run_frozen_tests.py index e64325417..1684884a3 100644 --- a/scripts/dev/run_frozen_tests.py +++ b/scripts/dev/run_frozen_tests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/run_profile.py b/scripts/dev/run_profile.py index 87d2f0ed7..31fe539aa 100755 --- a/scripts/dev/run_profile.py +++ b/scripts/dev/run_profile.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/run_pylint_on_tests.py b/scripts/dev/run_pylint_on_tests.py index 01dd14ad7..b9fb4845d 100644 --- a/scripts/dev/run_pylint_on_tests.py +++ b/scripts/dev/run_pylint_on_tests.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -54,7 +54,8 @@ def main(): 'missing-docstring', 'protected-access', # https://bitbucket.org/logilab/pylint/issue/511/ - 'undefined-variable', + #'undefined-variable', + 'len-as-condition', # directories without __init__.py... 'import-error', ] @@ -66,7 +67,8 @@ def main(): no_docstring_rgx = ['^__.*__$', '^setup$'] args = (['--disable={}'.format(','.join(disabled)), - '--no-docstring-rgx=({})'.format('|'.join(no_docstring_rgx))] + + '--no-docstring-rgx=({})'.format('|'.join(no_docstring_rgx)), + '--ignored-modules=helpers,pytest'] + sys.argv[2:] + files) env = os.environ.copy() env['PYTHONPATH'] = os.pathsep.join(pythonpath) diff --git a/scripts/dev/run_vulture.py b/scripts/dev/run_vulture.py index 55f4d9c86..40fe2f591 100755 --- a/scripts/dev/run_vulture.py +++ b/scripts/dev/run_vulture.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -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/scripts/dev/segfault_test.py b/scripts/dev/segfault_test.py index cac8c6a14..c5e7c106f 100755 --- a/scripts/dev/segfault_test.py +++ b/scripts/dev/segfault_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index a8f019d9d..7445a99c6 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -437,6 +437,7 @@ def _get_authors(): 'Claire C.C': 'Claire Cavanaugh', 'Rahid': 'Maciej Wołczyk', 'Fritz V155 Reichwald': 'Fritz Reichwald', + 'Spreadyy': 'sandrosc', } ignored = ['pyup-bot'] commits = subprocess.check_output(['git', 'log', '--format=%aN']) diff --git a/scripts/dev/ua_fetch.py b/scripts/dev/ua_fetch.py index f37797041..14f71d525 100644 --- a/scripts/dev/ua_fetch.py +++ b/scripts/dev/ua_fetch.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 lamarpavel -# Copyright 2015-2016 Alexey Nabrodov (Averrin) +# Copyright 2015-2017 lamarpavel +# Copyright 2015-2017 Alexey Nabrodov (Averrin) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/dev/update_3rdparty.py b/scripts/dev/update_3rdparty.py index 4b88b165f..722ce2316 100755 --- a/scripts/dev/update_3rdparty.py +++ b/scripts/dev/update_3rdparty.py @@ -2,6 +2,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # Copyright 2015 Daniel Schadt +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/hostblock_blame.py b/scripts/hostblock_blame.py index 7e444793b..dde83d91f 100644 --- a/scripts/hostblock_blame.py +++ b/scripts/hostblock_blame.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/scripts/importer.py b/scripts/importer.py index 5e0883cd2..1b3be4d32 100755 --- a/scripts/importer.py +++ b/scripts/importer.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Claude (longneck) +# Copyright 2014-2017 Claude (longneck) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/keytester.py b/scripts/keytester.py index ebed5f62c..b147599b6 100644 --- a/scripts/keytester.py +++ b/scripts/keytester.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/link_pyqt.py b/scripts/link_pyqt.py index fffe27fb3..a7de598cd 100644 --- a/scripts/link_pyqt.py +++ b/scripts/link_pyqt.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/setupcommon.py b/scripts/setupcommon.py index 9a447d280..494ab4c46 100644 --- a/scripts/setupcommon.py +++ b/scripts/setupcommon.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/scripts/testbrowser.py b/scripts/testbrowser.py index 24ce2a130..fbe48c451 100755 --- a/scripts/testbrowser.py +++ b/scripts/testbrowser.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -46,7 +46,7 @@ def parse_args(): if QWebEngineView is not None: parser.add_argument('--webengine', help='Use QtWebEngine', default=False, action='store_true') - return parser.parse_args() + return parser.parse_known_args()[0] if __name__ == '__main__': diff --git a/scripts/utils.py b/scripts/utils.py index 55eb679bd..6793bb2a7 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/setup.py b/setup.py index 6e594af12..f594009a0 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2015 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/conftest.py b/tests/conftest.py index bcb67924b..2fdf8ab9a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -# pylint: disable=unused-import +# pylint: disable=unused-import,wildcard-import,unused-wildcard-import """The qutebrowser test suite conftest file.""" @@ -34,7 +34,7 @@ pytest.register_assert_rewrite('helpers') from helpers import logfail from helpers.logfail import fail_on_logging from helpers.messagemock import message_mock -from helpers.fixtures import * # pylint: disable=wildcard-import +from helpers.fixtures import * from qutebrowser.utils import qtutils @@ -44,7 +44,7 @@ hypothesis.settings.register_profile('default', hypothesis.settings.load_profile('default') -def _apply_platform_markers(item): +def _apply_platform_markers(config, item): """Apply a skip marker to a given item.""" markers = [ ('posix', os.name != 'posix', "Requires a POSIX os"), @@ -57,6 +57,8 @@ def _apply_platform_markers(item): ('frozen', not getattr(sys, 'frozen', False), "Can only run when frozen"), ('ci', 'CI' not in os.environ, "Only runs on CI."), + ('issue2478', os.name == 'nt' and config.webengine, + "Broken with QtWebEngine on Windows"), ] for searched_marker, condition, default_reason in markers: @@ -117,7 +119,7 @@ def pytest_collection_modifyitems(config, items): if module_root_dir == 'end2end': item.add_marker(pytest.mark.end2end) - _apply_platform_markers(item) + _apply_platform_markers(config, item) if item.get_marker('xfail_norun'): item.add_marker(pytest.mark.xfail(run=False)) if item.get_marker('js_prompt'): diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py index 5dcff3fb5..8dac6a41d 100644 --- a/tests/end2end/conftest.py +++ b/tests/end2end/conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -68,7 +68,7 @@ def _get_version_tag(tag): """ version_re = re.compile(r""" (?Pqt|pyqt) - (?P==|>=|!=) + (?P==|>=|!=|<) (?P\d+\.\d+(\.\d+)?) """, re.VERBOSE) @@ -84,6 +84,7 @@ def _get_version_tag(tag): do_skip = { '==': not qtutils.version_check(version, exact=True), '>=': not qtutils.version_check(version), + '<': qtutils.version_check(version), '!=': qtutils.version_check(version, exact=True), } return pytest.mark.skipif(do_skip[op], reason='Needs ' + tag) @@ -135,16 +136,6 @@ if not getattr(sys, 'frozen', False): def pytest_collection_modifyitems(config, items): """Apply @qtwebengine_* markers; skip unittests with QUTE_BDD_WEBENGINE.""" - if config.webengine: - qtwebkit_ng_used = False - else: - try: - from PyQt5.QtWebKit import qWebKitVersion - except ImportError: - qtwebkit_ng_used = False - else: - qtwebkit_ng_used = qtutils.is_qtwebkit_ng(qWebKitVersion()) - markers = [ ('qtwebengine_todo', 'QtWebEngine TODO', pytest.mark.xfail, config.webengine), @@ -153,9 +144,9 @@ def pytest_collection_modifyitems(config, items): ('qtwebkit_skip', 'Skipped with QtWebKit', pytest.mark.skipif, not config.webengine), ('qtwebkit_ng_xfail', 'Failing with QtWebKit-NG', pytest.mark.xfail, - qtwebkit_ng_used), + not config.webengine and qtutils.is_qtwebkit_ng()), ('qtwebkit_ng_skip', 'Skipped with QtWebKit-NG', pytest.mark.skipif, - qtwebkit_ng_used), + not config.webengine and qtutils.is_qtwebkit_ng()), ('qtwebengine_flaky', 'Flaky with QtWebEngine', pytest.mark.skipif, config.webengine), ('qtwebengine_osx_xfail', 'Fails on OS X with QtWebEngine', diff --git a/tests/end2end/data/downloads/issue2298.html b/tests/end2end/data/downloads/issue2298.html new file mode 100644 index 000000000..eb4a592c5 --- /dev/null +++ b/tests/end2end/data/downloads/issue2298.html @@ -0,0 +1,10 @@ + + + + + Retrying failing download with QtWebEngine + + + download + + diff --git a/tests/end2end/data/issue2569.html b/tests/end2end/data/issue2569.html new file mode 100644 index 000000000..8f613be2d --- /dev/null +++ b/tests/end2end/data/issue2569.html @@ -0,0 +1,26 @@ + + + + Form with tagName child + + + +
+ +
+ +
+ +
+ + + + + + +
  • List item
+ + diff --git a/tests/end2end/data/search.html b/tests/end2end/data/search.html index 2eec560d6..003751cfd 100644 --- a/tests/end2end/data/search.html +++ b/tests/end2end/data/search.html @@ -16,6 +16,7 @@ BAZ
space travel
/slash
+ follow me!

diff --git a/tests/end2end/data/search_select.js b/tests/end2end/data/search_select.js new file mode 100644 index 000000000..874e9e9fe --- /dev/null +++ b/tests/end2end/data/search_select.js @@ -0,0 +1,12 @@ +/* Select all elements marked with toselect */ + +var toSelect = document.getElementsByClassName("toselect"); +var s = window.getSelection(); + +if(s.rangeCount > 0) s.removeAllRanges(); + +for(var i = 0; i < toSelect.length; i++) { + var range = document.createRange(); + range.selectNode(toSelect[i]); + s.addRange(range); +} diff --git a/tests/end2end/data/sessions/history_replace_state.html b/tests/end2end/data/sessions/history_replace_state.html index c221187ae..b7f56f217 100644 --- a/tests/end2end/data/sessions/history_replace_state.html +++ b/tests/end2end/data/sessions/history_replace_state.html @@ -5,8 +5,8 @@ Test title diff --git a/tests/end2end/data/userscripts/stdinclose.py b/tests/end2end/data/userscripts/stdinclose.py new file mode 100755 index 000000000..fa0676f73 --- /dev/null +++ b/tests/end2end/data/userscripts/stdinclose.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2017 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""A userscript to check if the stdin gets closed.""" + +import sys +import os +sys.stdin.read() +with open(os.environ['QUTE_FIFO'], 'wb') as fifo: + fifo.write(b':message-info "stdin closed"\n') diff --git a/tests/end2end/features/adblock.feature b/tests/end2end/features/adblock.feature index c33df30fe..c400df25f 100644 --- a/tests/end2end/features/adblock.feature +++ b/tests/end2end/features/adblock.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Ad blocking Scenario: Simple adblock update diff --git a/tests/end2end/features/backforward.feature b/tests/end2end/features/backforward.feature index 8f970837b..413ee9d95 100644 --- a/tests/end2end/features/backforward.feature +++ b/tests/end2end/features/backforward.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Going back and forward. Testing the :back/:forward commands. diff --git a/tests/end2end/features/caret.feature b/tests/end2end/features/caret.feature index 47b00622d..79a7b9d8f 100644 --- a/tests/end2end/features/caret.feature +++ b/tests/end2end/features/caret.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Caret mode In caret mode, the user can select and yank text using the keyboard. diff --git a/tests/end2end/features/completion.feature b/tests/end2end/features/completion.feature index 87db94e8d..b6c62336c 100644 --- a/tests/end2end/features/completion.feature +++ b/tests/end2end/features/completion.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Using completion Scenario: No warnings when completing with one entry (#1600) diff --git a/tests/end2end/features/conftest.py b/tests/end2end/features/conftest.py index f93ac901f..2aa897dfa 100644 --- a/tests/end2end/features/conftest.py +++ b/tests/end2end/features/conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -161,7 +161,7 @@ def clean_open_tabs(quteproc): quteproc.send_cmd(':window-only') quteproc.send_cmd(':tab-only') quteproc.send_cmd(':tab-close --force') - + quteproc.wait_for_load_finished_url('about:blank') @bdd.given('pdfjs is available') def pdfjs_available(): @@ -173,13 +173,15 @@ def pdfjs_available(): @bdd.when(bdd.parsers.parse("I open {path}")) -def open_path(quteproc, path): +def open_path(quteproc, httpbin, path): """Open a URL. If used like "When I open ... in a new tab", the URL is opened in a new tab. With "... in a new window", it's opened in a new window. With "... as a URL", it's opened according to new-instance-open-target. """ + path = path.replace('(port)', str(httpbin.port)) + new_tab = False new_bg_tab = False new_window = False diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index b55673344..b1dd5155b 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Downloading things from a website. Background: @@ -78,6 +80,7 @@ Feature: Downloading things from a website. Scenario: Downloading with SSL errors (issue 1413) When I clear SSL errors And I set network -> ssl-strict to ask + And I set storage -> prompt-download-directory to false And I download an SSL page And I wait for "Entering mode KeyMode.* (reason: question asked)" in the log And I run :prompt-accept @@ -118,14 +121,50 @@ Feature: Downloading things from a website. And I wait until the download is finished Then the downloaded file download with spaces.bin should exist - @qtwebkit_skip - Scenario: Downloading a file with evil content-disposition header + @qtwebkit_skip @qt<5.9 + Scenario: Downloading a file with evil content-disposition header (Qt 5.8 or older) # Content-Disposition: download; filename=..%2Ffoo When I open response-headers?Content-Disposition=download;%20filename%3D..%252Ffoo without waiting And I wait until the download is finished Then the downloaded file ../foo should not exist And the downloaded file foo should exist + @qtwebkit_skip @qt>=5.9 + Scenario: Downloading a file with evil content-disposition header (Qt 5.9 or newer) + # Content-Disposition: download; filename=..%2Ffoo + When I open response-headers?Content-Disposition=download;%20filename%3D..%252Ffoo without waiting + And I wait until the download is finished + Then the downloaded file ../foo should not exist + And the downloaded file ..%2Ffoo should exist + + @windows + Scenario: Downloading a file to a reserved path + When I set storage -> prompt-download-directory to true + And I open data/downloads/download.bin without waiting + And I wait for "Asking question text='Please enter a location for http://localhost:*/data/downloads/download.bin' title='Save file to:'>, *" in the log + And I run :prompt-accept COM1 + And I run :leave-mode + Then the error "Invalid filename" should be shown + + @windows + Scenario: Downloading a file to a drive-relative working directory + When I set storage -> prompt-download-directory to true + And I open data/downloads/download.bin without waiting + And I wait for "Asking question text='Please enter a location for http://localhost:*/data/downloads/download.bin' title='Save file to:'>, *" in the log + And I run :prompt-accept C:foobar + And I run :leave-mode + Then the error "Invalid filename" should be shown + + @windows + Scenario: Downloading a file to a reserved path with :download + When I run :download data/downloads/download.bin --dest=COM1 + Then the error "Invalid target filename" should be shown + + @windows + Scenario: Download a file to a drive-relative working directory with :download + When I run :download data/downloads/download.bin --dest=C:foobar + Then the error "Invalid target filename" should be shown + ## :download-retry Scenario: Retrying a failed download @@ -137,6 +176,14 @@ Feature: Downloading things from a website. does-not-exist does-not-exist + @qtwebkit_skip + Scenario: Retrying a failed download with QtWebEngine + When I open data/downloads/issue2298.html + And I run :click-element id download + And I wait for "Download error: *" in the log + And I run :download-retry + Then the error "Retrying downloads is unsupported with QtWebEngine" should be shown + Scenario: Retrying with count When I run :download http://localhost:(port)/data/downloads/download.bin And I run :download http://localhost:(port)/does-not-exist @@ -603,3 +650,9 @@ Feature: Downloading things from a website. And I run :follow-hint 0 And I wait until the download is finished Then the downloaded file user-agent should contain Safari/ + + @qtwebengine_skip: Handled by QtWebEngine, not by us + Scenario: Downloading a "Internal server error" with disposition: inline (#2304) + When I set storage -> prompt-download-directory to false + And I open custom/500-inline + Then the error "Download error: *INTERNAL SERVER ERROR" should be shown diff --git a/tests/end2end/features/editor.feature b/tests/end2end/features/editor.feature index 947dba0b0..e94e8b63c 100644 --- a/tests/end2end/features/editor.feature +++ b/tests/end2end/features/editor.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Opening external editors ## :edit-url diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index 5fac84402..bdcf9b9d8 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Using hints # https://bugreports.qt.io/browse/QTBUG-58381 @@ -45,17 +47,17 @@ Feature: Using hints Scenario: Using :hint spawn with flags and -- (issue 797) When I open data/hints/html/simple.html - And I hint with args "-- all spawn -v echo" and follow a + And I hint with args "-- all spawn -v python -c ''" and follow a Then the message "Command exited successfully." should be shown Scenario: Using :hint spawn with flags (issue 797) When I open data/hints/html/simple.html - And I hint with args "all spawn -v echo" and follow a + And I hint with args "all spawn -v python -c ''" and follow a Then the message "Command exited successfully." should be shown Scenario: Using :hint spawn with flags and --rapid (issue 797) When I open data/hints/html/simple.html - And I hint with args "--rapid all spawn -v echo" and follow a + And I hint with args "--rapid all spawn -v python -c ''" and follow a Then the message "Command exited successfully." should be shown @posix diff --git a/tests/end2end/features/history.feature b/tests/end2end/features/history.feature index bc7f537e6..613b660af 100644 --- a/tests/end2end/features/history.feature +++ b/tests/end2end/features/history.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Page history Make sure the global page history is saved correctly. @@ -77,7 +79,15 @@ Feature: Page history Scenario: Listing history When I open data/numbers/3.txt And I open data/numbers/4.txt - And I open qute:history + And I open qute://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" diff --git a/tests/end2end/features/invoke.feature b/tests/end2end/features/invoke.feature index 4b50f2af7..95ff3ab5e 100644 --- a/tests/end2end/features/invoke.feature +++ b/tests/end2end/features/invoke.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Invoking a new process Simulate what happens when running qutebrowser with an existing instance diff --git a/tests/end2end/features/javascript.feature b/tests/end2end/features/javascript.feature index 13ab0d96a..7ea712d15 100644 --- a/tests/end2end/features/javascript.feature +++ b/tests/end2end/features/javascript.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Javascript stuff Integration with javascript. @@ -71,3 +73,30 @@ Feature: Javascript stuff When I set content -> allow-javascript to false And I run :jseval console.log('jseval executed') Then the javascript message "jseval executed" should be logged + + ## webelement issues (mostly with QtWebEngine) + + # https://github.com/qutebrowser/qutebrowser/issues/2569 + Scenario: Clicking on form element with tagName child + When I open data/issue2569.html + And I run :click-element id tagnameform + And I wait for "Sending fake click to *" in the log + Then no crash should happen + + Scenario: Clicking on form element with text child + When I open data/issue2569.html + And I run :click-element id textform + And I wait for "Sending fake click to *" in the log + Then no crash should happen + + Scenario: Clicking on svg element + When I open data/issue2569.html + And I run :click-element id icon + And I wait for "Sending fake click to *" in the log + Then no crash should happen + + Scenario: Clicking on li element + When I open data/issue2569.html + And I run :click-element id listitem + And I wait for "Sending fake click to *" in the log + Then no crash should happen diff --git a/tests/end2end/features/keyinput.feature b/tests/end2end/features/keyinput.feature index 6777056e8..93fbb1b93 100644 --- a/tests/end2end/features/keyinput.feature +++ b/tests/end2end/features/keyinput.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Keyboard input Tests for :bind and :unbind, :clear-keychain and other keyboard input @@ -200,34 +202,30 @@ Feature: Keyboard input # Macros Scenario: Recording a simple macro - Given I open data/scroll/simple.html - And I run :tab-only - When I run :scroll down with count 6 - And I wait until the scroll position changed - And I run :record-macro + When I run :record-macro And I press the key "a" - And I run :scroll up - And I run :scroll up - And I wait until the scroll position changed + And I run :message-info "foo 1" + And I run :message-info "bar 1" And I run :record-macro And I run :run-macro with count 2 And I press the key "a" - And I wait until the scroll position changed to 0/0 - Then the page should not be scrolled + Then the message "foo 1" should be shown + And the message "bar 1" should be shown + And the message "foo 1" should be shown + And the message "bar 1" should be shown + And the message "foo 1" should be shown + And the message "bar 1" should be shown Scenario: Recording a named macro - Given I open data/scroll/simple.html - And I run :tab-only - When I run :scroll down with count 6 - And I wait until the scroll position changed + When I run :record-macro foo + And I run :message-info "foo 2" + And I run :message-info "bar 2" And I run :record-macro foo - And I run :scroll up - And I run :scroll up - And I wait until the scroll position changed - And I run :record-macro foo - And I run :run-macro foo with count 2 - And I wait until the scroll position changed to 0/0 - Then the page should not be scrolled + And I run :run-macro foo + Then the message "foo 2" should be shown + And the message "bar 2" should be shown + And the message "foo 2" should be shown + And the message "bar 2" should be shown Scenario: Running an invalid macro Given I open data/scroll/simple.html @@ -262,17 +260,12 @@ Feature: Keyboard input Then "Leaving mode KeyMode.record_macro (reason: leave current)" should be logged Scenario: Ignoring non-register keys - Given I open data/scroll/simple.html - And I run :tab-only - When I run :scroll down with count 2 - And I wait until the scroll position changed - And I run :record-macro + When I run :record-macro And I press the key "" And I press the key "c" - And I run :scroll up - And I wait until the scroll position changed + And I run :message-info "foo 3" And I run :record-macro And I run :run-macro And I press the key "c" - And I wait until the scroll position changed to 0/0 - Then the page should not be scrolled + Then the message "foo 3" should be shown + And the message "foo 3" should be shown diff --git a/tests/end2end/features/marks.feature b/tests/end2end/features/marks.feature index e2738e23f..31ddd034d 100644 --- a/tests/end2end/features/marks.feature +++ b/tests/end2end/features/marks.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Setting positional marks Background: @@ -25,6 +27,7 @@ Feature: Setting positional marks And I wait until the scroll position changed to 10/20 Then the page should be scrolled to 10 20 + @qtwebengine_flaky Scenario: Setting the same local mark on another page When I run :scroll-px 5 10 And I wait until the scroll position changed to 5/10 diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 5e13e1feb..83de1c822 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Various utility commands. ## :set-cmd-text @@ -79,6 +81,7 @@ Feature: Various utility commands. And I run :jseval --world=1 console.log("Hello from JS!"); And I wait for the javascript message "Hello from JS!" Then "Ignoring world ID 1" should be logged + And "No output or error" should be logged @qtwebkit_skip Scenario: :jseval uses separate world without --world @@ -87,6 +90,7 @@ Feature: Various utility commands. And I run :jseval do_log() Then the javascript message "Hello from the page!" should not be logged And the javascript message "Uncaught ReferenceError: do_log is not defined" should be logged + And "No output or error" should be logged @qtwebkit_skip Scenario: :jseval using the main world @@ -94,6 +98,7 @@ Feature: Various utility commands. And I open data/misc/jseval.html And I run :jseval --world 0 do_log() Then the javascript message "Hello from the page!" should be logged + And "No output or error" should be logged @qtwebkit_skip Scenario: :jseval using the main world as name @@ -101,12 +106,14 @@ Feature: Various utility commands. And I open data/misc/jseval.html And I run :jseval --world main do_log() Then the javascript message "Hello from the page!" should be logged + And "No output or error" should be logged Scenario: :jseval --file using a file that exists as js-code When I set general -> log-javascript-console to info And I run :jseval --file (testdata)/misc/jseval_file.js Then the javascript message "Hello from JS!" should be logged And the javascript message "Hello again from JS!" should be logged + And "No output or error" should be logged Scenario: :jseval --file using a file that doesn't exist as js-code When I run :jseval --file nonexistentfile @@ -301,6 +308,15 @@ Feature: Various utility commands. - about:blank - qute://help/index.html (active) + # https://github.com/qutebrowser/qutebrowser/issues/2513 + Scenario: Opening link with qute:help + When the documentation is up to date + And I run :tab-only + And I open qute:help without waiting + And I wait for "Changing title for idx 0 to 'qutebrowser help'" in the log + And I hint with args "links normal" and follow a + Then qute://help/quickstart.html should be loaded + # :history Scenario: :history without arguments @@ -398,12 +414,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 @@ -505,12 +521,16 @@ 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 without waiting + # With Qt 5.9, we don't get a loaded message? + And I wait for "Changing title for idx * to 'log'" in the 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 without waiting + # With Qt 5.9, we don't get a loaded message? + And I wait for "Changing title for idx * to 'log'" in the log Then no crash should happen Scenario: Using :messages without messages @@ -531,6 +551,16 @@ Feature: Various utility commands. When I run :message-i "Hello World" (invalid command) Then the error "message-i: no such command" should be shown + Scenario: Multiple leading : in command + When I run :::::set-cmd-text ::::message-i "Hello World" + And I run :command-accept + Then the message "Hello World" should be shown + + Scenario: Whitespace in command + When I run : : set-cmd-text : : message-i "Hello World" + And I run :command-accept + Then the message "Hello World" should be shown + # We can't run :message-i as startup command, so we use # :set-cmd-text @@ -625,7 +655,7 @@ Feature: Various utility commands. 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 @@ -637,7 +667,7 @@ Feature: Various utility commands. 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 : @@ -673,7 +703,8 @@ Feature: Various utility commands. ## Renderer crashes - @qtwebkit_skip @no_invalid_lines + # Skipped on Windows as "... has stopped working" hangs. + @qtwebkit_skip @no_invalid_lines @posix Scenario: Renderer crash When I run :open -t chrome://crash Then the error "Renderer process crashed" should be shown diff --git a/tests/end2end/features/navigate.feature b/tests/end2end/features/navigate.feature index 5153400a4..cb57a4a15 100644 --- a/tests/end2end/features/navigate.feature +++ b/tests/end2end/features/navigate.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Using :navigate Scenario: :navigate with invalid argument diff --git a/tests/end2end/features/open.feature b/tests/end2end/features/open.feature index 89d6c9aa2..efc132466 100644 --- a/tests/end2end/features/open.feature +++ b/tests/end2end/features/open.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Opening pages Scenario: :open with URL diff --git a/tests/end2end/features/prompts.feature b/tests/end2end/features/prompts.feature index 8b687abcc..9b5d7aae5 100644 --- a/tests/end2end/features/prompts.feature +++ b/tests/end2end/features/prompts.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Prompts Various prompts (javascript, SSL errors, authentification, etc.) @@ -150,12 +152,13 @@ Feature: Prompts Scenario: Pasting via shift-insert without it being supported When selection is not supported And I put "insert test" into the primary selection + And I put "clipboard test" into the clipboard And I open data/prompt/jsprompt.html And I run :click-element id button And I wait for a prompt And I press the keys "" And I run :prompt-accept - Then the javascript message "Prompt reply: " should be logged + Then the javascript message "Prompt reply: clipboard test" should be logged @js_prompt Scenario: Using content -> ignore-javascript-prompt @@ -174,6 +177,7 @@ Feature: Prompts Then the error "Certificate error: *" should be shown And the page should contain the plaintext "Hello World via SSL!" + @issue2478 Scenario: SSL error with ssl-strict = true When I clear SSL errors And I set network -> ssl-strict to true @@ -189,6 +193,7 @@ Feature: Prompts And I wait until the SSL page finished loading Then the page should contain the plaintext "Hello World via SSL!" + @issue2478 Scenario: SSL error with ssl-strict = ask -> no When I clear SSL errors And I set network -> ssl-strict to ask @@ -197,6 +202,7 @@ Feature: Prompts And I run :prompt-accept no Then a SSL error page should be shown + @issue2478 Scenario: SSL error with ssl-strict = ask -> abort When I clear SSL errors And I set network -> ssl-strict to ask diff --git a/tests/end2end/features/scroll.feature b/tests/end2end/features/scroll.feature index 05f9c22e8..4e2643b6d 100644 --- a/tests/end2end/features/scroll.feature +++ b/tests/end2end/features/scroll.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Scrolling Tests the various scroll commands. @@ -37,6 +39,7 @@ Feature: Scrolling And I wait until the scroll position changed to 0/0 Then the page should not be scrolled + @qtwebengine_flaky Scenario: Scrolling left and right with count When I run :scroll-px 10 0 with count 2 And I wait until the scroll position changed to 20/0 @@ -144,7 +147,6 @@ Feature: Scrolling Scenario: Scrolling down with a very big count When I run :scroll down with count 99999999999 - And I wait until the scroll position changed # Make sure it doesn't hang And I run :message-info "Still alive!" Then the message "Still alive!" should be shown diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature index 87e51fc2c..7e0151976 100644 --- a/tests/end2end/features/search.feature +++ b/tests/end2end/features/search.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Searching on a page Searching text on the page (like /foo) with different options. @@ -9,42 +11,45 @@ Feature: Searching on a page Scenario: Searching text When I run :search foo - And I run :yank selection - Then the clipboard should contain "foo" + And I wait for "search found foo" in the log + Then "foo" should be found Scenario: Searching twice When I run :search foo + And I wait for "search found foo" in the log And I run :search bar - And I run :yank selection - Then the clipboard should contain "Bar" + And I wait for "search found bar" in the log + Then "Bar" should be found Scenario: Searching with --reverse When I set general -> ignore-case to true And I run :search -r foo - And I run :yank selection - Then the clipboard should contain "Foo" + And I wait for "search found foo with flags FindBackward" in the log + Then "Foo" should be found Scenario: Searching without matches When I run :search doesnotmatch + And I wait for "search didn't find doesnotmatch" in the log Then the warning "Text 'doesnotmatch' not found on page!" should be shown @xfail_norun Scenario: Searching with / and spaces at the end (issue 874) When I run :set-cmd-text -s /space And I run :command-accept - And I run :yank selection - Then the clipboard should contain "space " + And I wait for "search found space " in the log + Then "space " should be found Scenario: Searching with / and slash in search term (issue 507) When I run :set-cmd-text -s //slash And I run :command-accept - And I run :yank selection - Then the clipboard should contain "/slash" + And I wait for "search found /slash" in the log + Then "/slash" should be found # This doesn't work because this is QtWebKit behavior. @xfail_norun Scenario: Searching text with umlauts When I run :search blub + And I wait for "search didn't find blub" in the log Then the warning "Text 'blub' not found on page!" should be shown ## ignore-case @@ -52,49 +57,52 @@ Feature: Searching on a page Scenario: Searching text with ignore-case = true When I set general -> ignore-case to true And I run :search bar - And I run :yank selection - Then the clipboard should contain "Bar" + And I wait for "search found bar" in the log + Then "Bar" should be found Scenario: Searching text with ignore-case = false When I set general -> ignore-case to false And I run :search bar - And I run :yank selection - Then the clipboard should contain "bar" + And I wait for "search found bar with flags FindCaseSensitively" in the log + Then "bar" should be found Scenario: Searching text with ignore-case = smart (lower-case) When I set general -> ignore-case to smart And I run :search bar - And I run :yank selection - Then the clipboard should contain "Bar" + And I wait for "search found bar" in the log + Then "Bar" should be found Scenario: Searching text with ignore-case = smart (upper-case) When I set general -> ignore-case to smart And I run :search Foo - And I run :yank selection - Then the clipboard should contain "Foo" # even though foo was first + And I wait for "search found Foo with flags FindCaseSensitively" in the log + Then "Foo" should be found # even though foo was first ## :search-next Scenario: Jumping to next match When I set general -> ignore-case to true And I run :search foo + And I wait for "search found foo" in the log And I run :search-next - And I run :yank selection - Then the clipboard should contain "Foo" + And I wait for "next_result found foo" in the log + Then "Foo" should be found Scenario: Jumping to next match with count When I set general -> ignore-case to true And I run :search baz + And I wait for "search found baz" in the log And I run :search-next with count 2 - And I run :yank selection - Then the clipboard should contain "BAZ" + And I wait for "next_result found baz" in the log + Then "BAZ" should be found Scenario: Jumping to next match with --reverse When I set general -> ignore-case to true And I run :search --reverse foo + And I wait for "search found foo with flags FindBackward" in the log And I run :search-next - And I run :yank selection - Then the clipboard should contain "foo" + And I wait for "next_result found foo with flags FindBackward" in the log + Then "foo" should be found Scenario: Jumping to next match without search # Make sure there was no search in the same window before @@ -105,37 +113,55 @@ Feature: Searching on a page Scenario: Repeating search in a second tab (issue #940) When I open data/search.html in a new tab And I run :search foo + And I wait for "search found foo" in the log And I run :tab-prev And I run :search-next - And I run :yank selection - Then the clipboard should contain "foo" + And I wait for "search found foo" in the log + Then "foo" should be found + + # https://github.com/qutebrowser/qutebrowser/issues/2438 + Scenario: Jumping to next match after clearing + When I set general -> ignore-case to true + And I run :search foo + And I wait for "search found foo" in the log + And I run :search + And I run :search-next + And I wait for "next_result found foo" in the log + Then "foo" should be found ## :search-prev Scenario: Jumping to previous match When I set general -> ignore-case to true And I run :search foo + And I wait for "search found foo" in the log And I run :search-next + And I wait for "next_result found foo" in the log And I run :search-prev - And I run :yank selection - Then the clipboard should contain "foo" + And I wait for "prev_result found foo with flags FindBackward" in the log + Then "foo" should be found Scenario: Jumping to previous match with count When I set general -> ignore-case to true And I run :search baz + And I wait for "search found baz" in the log And I run :search-next + And I wait for "next_result found baz" in the log And I run :search-next + And I wait for "next_result found baz" in the log And I run :search-prev with count 2 - And I run :yank selection - Then the clipboard should contain "baz" + And I wait for "prev_result found baz with flags FindBackward" in the log + Then "baz" should be found Scenario: Jumping to previous match with --reverse When I set general -> ignore-case to true And I run :search --reverse foo + And I wait for "search found foo with flags FindBackward" in the log And I run :search-next + And I wait for "next_result found foo with flags FindBackward" in the log And I run :search-prev - And I run :yank selection - Then the clipboard should contain "Foo" + And I wait for "prev_result found foo" in the log + Then "Foo" should be found Scenario: Jumping to previous match without search # Make sure there was no search in the same window before @@ -147,17 +173,68 @@ Feature: Searching on a page Scenario: Wrapping around page When I run :search foo + And I wait for "search found foo" in the log And I run :search-next + And I wait for "next_result found foo" in the log And I run :search-next - And I run :yank selection - Then the clipboard should contain "foo" + And I wait for "next_result found foo" in the log + Then "foo" should be found Scenario: Wrapping around page with --reverse When I run :search --reverse foo + And I wait for "search found foo with flags FindBackward" in the log And I run :search-next + And I wait for "next_result found foo with flags FindBackward" in the log And I run :search-next - And I run :yank selection - Then the clipboard should contain "Foo" + And I wait for "next_result found foo with flags FindBackward" in the log + Then "Foo" should be found # TODO: wrapping message with scrolling # TODO: wrapping message without scrolling + + ## follow searched links + Scenario: Follow a searched link + When I run :search follow + And I wait for "search found follow" in the log + And I run :follow-selected + Then data/hello.txt should be loaded + + Scenario: Follow a searched link in a new tab + When I run :window-only + And I run :search follow + And I wait for "search found follow" in the log + And I run :follow-selected -t + And I wait until data/hello.txt is loaded + Then the following tabs should be open: + - data/search.html + - data/hello.txt (active) + + Scenario: Don't follow searched text + When I run :window-only + And I run :search foo + And I wait for "search found foo" in the log + And I run :follow-selected + Then the following tabs should be open: + - data/search.html (active) + + Scenario: Don't follow searched text in a new tab + When I run :window-only + And I run :search foo + And I wait for "search found foo" in the log + And I run :follow-selected -t + Then the following tabs should be open: + - data/search.html (active) + + Scenario: Follow a manually selected link + When I run :jseval --file (testdata)/search_select.js + And I run :follow-selected + Then data/hello.txt should be loaded + + Scenario: Follow a manually selected link in a new tab + When I run :window-only + And I run :jseval --file (testdata)/search_select.js + And I run :follow-selected -t + And I wait until data/hello.txt is loaded + Then the following tabs should be open: + - data/search.html + - data/hello.txt (active) diff --git a/tests/end2end/features/sessions.feature b/tests/end2end/features/sessions.feature index 34a545841..226d3107d 100644 --- a/tests/end2end/features/sessions.feature +++ b/tests/end2end/features/sessions.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Saving and loading sessions Background: @@ -198,7 +200,7 @@ Feature: Saving and loading sessions Scenario: Saving a session with a page using history.replaceState() When I open data/sessions/history_replace_state.html without waiting - Then the javascript message "Calling history.replaceState" should be logged + Then the javascript message "Called history.replaceState" should be logged And the session should look like: windows: - tabs: @@ -212,7 +214,7 @@ Feature: Saving and loading sessions Scenario: Saving a session with a page using history.replaceState() and navigating away (qtwebkit) When I open data/sessions/history_replace_state.html And I open data/hello.txt - Then the javascript message "Calling history.replaceState" should be logged + Then the javascript message "Called history.replaceState" should be logged And the session should look like: windows: - tabs: @@ -229,7 +231,7 @@ Feature: Saving and loading sessions @qtwebkit_skip Scenario: Saving a session with a page using history.replaceState() and navigating away When I open data/sessions/history_replace_state.html without waiting - And I wait for "* Calling history.replaceState" in the log + And I wait for "* Called history.replaceState" in the log And I open data/hello.txt Then the session should look like: windows: diff --git a/tests/end2end/features/set.feature b/tests/end2end/features/set.feature index 769605c3e..bc144eb6e 100644 --- a/tests/end2end/features/set.feature +++ b/tests/end2end/features/set.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Setting settings. Background: @@ -76,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 @@ -99,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/spawn.feature b/tests/end2end/features/spawn.feature index 445920924..dc0485391 100644 --- a/tests/end2end/features/spawn.feature +++ b/tests/end2end/features/spawn.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: :spawn Scenario: Running :spawn @@ -53,3 +55,13 @@ Feature: :spawn Then the following tabs should be open: - about:blank - about:blank (active) + + @posix + Scenario: Running :spawn with userscript that expects the stdin getting closed + When I run :spawn -u (testdata)/userscripts/stdinclose.py + Then the message "stdin closed" should be shown + + @posix + Scenario: Running :spawn -d with userscript that expects the stdin getting closed + When I run :spawn -d -u (testdata)/userscripts/stdinclose.py + Then the message "stdin closed" should be shown diff --git a/tests/end2end/features/tabs.feature b/tests/end2end/features/tabs.feature index 894d3cda9..9e14e6ca6 100644 --- a/tests/end2end/features/tabs.feature +++ b/tests/end2end/features/tabs.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Tab management Tests for various :tab-* commands. @@ -605,12 +607,25 @@ Feature: Tab management title: Test title # https://github.com/qutebrowser/qutebrowser/issues/2289 - @qtwebkit_skip @qt>=5.8 + + @qtwebkit_skip @qt==5.8.0 Scenario: Cloning a tab with a special URL When I open chrome://gpu And I run :tab-clone Then the error "Can't serialize special URL!" should be shown + @qtwebkit_skip @qt<5.9 + Scenario: Cloning a tab with a view-source URL + When I open view-source:http://localhost:(port) + And I run :tab-clone + Then the error "Can't serialize special URL!" should be shown + + @qtwebkit_skip @qt>=5.9 + Scenario: Cloning a tab with a special URL (Qt 5.9) + When I open chrome://gpu + And I run :tab-clone + Then no crash should happen + # :tab-detach Scenario: Detaching a tab @@ -766,18 +781,6 @@ Feature: Tab management - data/numbers/2.txt - data/numbers/3.txt - # https://github.com/qutebrowser/qutebrowser/issues/2289 - @qtwebkit_skip @qt>=5.8 - Scenario: Undoing a tab with a special URL - Given I have a fresh instance - When I open data/numbers/1.txt - And I open chrome://gpu in a new tab - And I run :tab-close - And I run :undo - Then the error "Nothing to undo!" should be shown - And the following tabs should be open: - - data/numbers/1.txt (active) - # last-close # FIXME:qtwebengine diff --git a/tests/end2end/features/test_adblock_bdd.py b/tests/end2end/features/test_adblock_bdd.py index 9f4ae63b3..069c127aa 100644 --- a/tests/end2end/features/test_adblock_bdd.py +++ b/tests/end2end/features/test_adblock_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_backforward_bdd.py b/tests/end2end/features/test_backforward_bdd.py index ede51988a..187882b67 100644 --- a/tests/end2end/features/test_backforward_bdd.py +++ b/tests/end2end/features/test_backforward_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_caret_bdd.py b/tests/end2end/features/test_caret_bdd.py index aa42241c4..9e4e1dedd 100644 --- a/tests/end2end/features/test_caret_bdd.py +++ b/tests/end2end/features/test_caret_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_completion_bdd.py b/tests/end2end/features/test_completion_bdd.py index 030a16ffc..f4ada848f 100644 --- a/tests/end2end/features/test_completion_bdd.py +++ b/tests/end2end/features/test_completion_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_downloads_bdd.py b/tests/end2end/features/test_downloads_bdd.py index cb0388ea4..25eb52aad 100644 --- a/tests/end2end/features/test_downloads_bdd.py +++ b/tests/end2end/features/test_downloads_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -68,6 +68,8 @@ def wait_for_download_finished_name(quteproc, name): def wait_for_download_prompt(tmpdir, quteproc, path): full_path = path.replace('(tmpdir)', str(tmpdir)).replace('/', os.sep) quteproc.wait_for(message=PROMPT_MSG.format(full_path)) + quteproc.wait_for(message="Entering mode KeyMode.prompt " + "(reason: question asked)") @bdd.when("I download an SSL page") diff --git a/tests/end2end/features/test_editor_bdd.py b/tests/end2end/features/test_editor_bdd.py index f26e2956f..7d38be5af 100644 --- a/tests/end2end/features/test_editor_bdd.py +++ b/tests/end2end/features/test_editor_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_hints_bdd.py b/tests/end2end/features/test_hints_bdd.py index b5304cb74..f39a15391 100644 --- a/tests/end2end/features/test_hints_bdd.py +++ b/tests/end2end/features/test_hints_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_history_bdd.py b/tests/end2end/features/test_history_bdd.py index 871dbcb98..1fee533eb 100644 --- a/tests/end2end/features/test_history_bdd.py +++ b/tests/end2end/features/test_history_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_invoke_bdd.py b/tests/end2end/features/test_invoke_bdd.py index 86faf8107..5d463608e 100644 --- a/tests/end2end/features/test_invoke_bdd.py +++ b/tests/end2end/features/test_invoke_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_javascript_bdd.py b/tests/end2end/features/test_javascript_bdd.py index 78d45ab5a..ca65a9d1d 100644 --- a/tests/end2end/features/test_javascript_bdd.py +++ b/tests/end2end/features/test_javascript_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_keyinput_bdd.py b/tests/end2end/features/test_keyinput_bdd.py index d6b3134a3..ef5be0ee9 100644 --- a/tests/end2end/features/test_keyinput_bdd.py +++ b/tests/end2end/features/test_keyinput_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_marks_bdd.py b/tests/end2end/features/test_marks_bdd.py index 5e0e623f2..5b8e352dd 100644 --- a/tests/end2end/features/test_marks_bdd.py +++ b/tests/end2end/features/test_marks_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_misc_bdd.py b/tests/end2end/features/test_misc_bdd.py index 177f7f383..e78840d68 100644 --- a/tests/end2end/features/test_misc_bdd.py +++ b/tests/end2end/features/test_misc_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -38,11 +38,13 @@ def update_documentation(): doc_path = os.path.join(base_path, 'html', 'doc') script_path = os.path.join(base_path, '..', 'scripts') - if not os.path.exists(doc_path): - # On CI, we can test this without actually building the docs - return + try: + os.mkdir(doc_path) + except FileExistsError: + pass - if all(docutils.docs_up_to_date(p) for p in os.listdir(doc_path)): + files = os.listdir(doc_path) + if files and all(docutils.docs_up_to_date(p) for p in files): return try: diff --git a/tests/end2end/features/test_navigate_bdd.py b/tests/end2end/features/test_navigate_bdd.py index 5f922fedc..03812df83 100644 --- a/tests/end2end/features/test_navigate_bdd.py +++ b/tests/end2end/features/test_navigate_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_open_bdd.py b/tests/end2end/features/test_open_bdd.py index 4a609decb..e5692c615 100644 --- a/tests/end2end/features/test_open_bdd.py +++ b/tests/end2end/features/test_open_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,5 +17,18 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import logging + import pytest_bdd as bdd bdd.scenarios('open.feature') + + +def test_open_s(quteproc, ssl_server): + """Test :open with -s.""" + quteproc.set_setting('network', 'ssl-strict', 'false') + quteproc.send_cmd(':open -s http://localhost:{}/'.format(ssl_server.port)) + quteproc.mark_expected(category='message', + loglevel=logging.ERROR, + message="Certificate error: *") + quteproc.wait_for_load_finished('/', port=ssl_server.port, https=True, + load_status='warn') diff --git a/tests/end2end/features/test_prompts_bdd.py b/tests/end2end/features/test_prompts_bdd.py index 3ebd53e8c..7a95d48c8 100644 --- a/tests/end2end/features/test_prompts_bdd.py +++ b/tests/end2end/features/test_prompts_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_scroll_bdd.py b/tests/end2end/features/test_scroll_bdd.py index de1ed3e0a..69199ebf3 100644 --- a/tests/end2end/features/test_scroll_bdd.py +++ b/tests/end2end/features/test_scroll_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_search_bdd.py b/tests/end2end/features/test_search_bdd.py index 12e5d9480..1b0c81488 100644 --- a/tests/end2end/features/test_search_bdd.py +++ b/tests/end2end/features/test_search_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,13 +17,26 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -import pytest +import json + import pytest_bdd as bdd # pylint: disable=unused-import from end2end.features.test_yankpaste_bdd import init_fake_clipboard -bdd.scenarios('search.feature') +@bdd.then(bdd.parsers.parse('"{text}" should be found')) +def check_found_text(request, quteproc, text): + if request.config.webengine: + # WORKAROUND + # This probably should work with Qt 5.9: + # https://codereview.qt-project.org/#/c/192920/ + # https://codereview.qt-project.org/#/c/192921/ + # https://bugreports.qt.io/browse/QTBUG-53134 + return + quteproc.send_cmd(':yank selection') + quteproc.wait_for(message='Setting fake clipboard: {}'.format( + json.dumps(text))) -pytestmark = pytest.mark.qtwebengine_skip("Searched text is not selected...") + +bdd.scenarios('search.feature') diff --git a/tests/end2end/features/test_sessions_bdd.py b/tests/end2end/features/test_sessions_bdd.py index d05b4d434..052748f90 100644 --- a/tests/end2end/features/test_sessions_bdd.py +++ b/tests/end2end/features/test_sessions_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_set_bdd.py b/tests/end2end/features/test_set_bdd.py index 2eabd8c56..fa94de88f 100644 --- a/tests/end2end/features/test_set_bdd.py +++ b/tests/end2end/features/test_set_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_spawn_bdd.py b/tests/end2end/features/test_spawn_bdd.py index e9ddf0301..432f95a53 100644 --- a/tests/end2end/features/test_spawn_bdd.py +++ b/tests/end2end/features/test_spawn_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_tabs_bdd.py b/tests/end2end/features/test_tabs_bdd.py index d53241288..3b106d7b8 100644 --- a/tests/end2end/features/test_tabs_bdd.py +++ b/tests/end2end/features/test_tabs_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_urlmarks_bdd.py b/tests/end2end/features/test_urlmarks_bdd.py index 170fdd30b..554ede3ec 100644 --- a/tests/end2end/features/test_urlmarks_bdd.py +++ b/tests/end2end/features/test_urlmarks_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_utilcmds_bdd.py b/tests/end2end/features/test_utilcmds_bdd.py index f90d587f6..01bfb1440 100644 --- a/tests/end2end/features/test_utilcmds_bdd.py +++ b/tests/end2end/features/test_utilcmds_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_yankpaste_bdd.py b/tests/end2end/features/test_yankpaste_bdd.py index 9deb4b3cf..8f2f56938 100644 --- a/tests/end2end/features/test_yankpaste_bdd.py +++ b/tests/end2end/features/test_yankpaste_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/test_zoom_bdd.py b/tests/end2end/features/test_zoom_bdd.py index 3f8728222..3dc94a8cd 100644 --- a/tests/end2end/features/test_zoom_bdd.py +++ b/tests/end2end/features/test_zoom_bdd.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/features/urlmarks.feature b/tests/end2end/features/urlmarks.feature index 800b6f794..b41359885 100644 --- a/tests/end2end/features/urlmarks.feature +++ b/tests/end2end/features/urlmarks.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: quickmarks and bookmarks ## bookmarks @@ -5,12 +7,12 @@ Feature: quickmarks and bookmarks Scenario: Saving a bookmark When I open data/title.html And I run :bookmark-add - Then the message "Bookmarked http://localhost:*/data/title.html!" should be shown + Then the message "Bookmarked http://localhost:*/data/title.html" should be shown And the bookmark file should contain "http://localhost:*/data/title.html Test title" Scenario: Saving a bookmark with a provided url and title When I run :bookmark-add http://example.com "some example title" - Then the message "Bookmarked http://example.com!" should be shown + Then the message "Bookmarked http://example.com" should be shown And the bookmark file should contain "http://example.com some example title" Scenario: Saving a bookmark with a url but no title @@ -223,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 + 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 5804dd8cd..7fd5952a8 100644 --- a/tests/end2end/features/utilcmds.feature +++ b/tests/end2end/features/utilcmds.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Miscellaneous utility commands exposed to the user. Background: @@ -129,6 +131,7 @@ Feature: Miscellaneous utility commands exposed to the user. And I hint with args "all tab-fg" And I run :leave-mode And I run :repeat-command + And I wait for "hints: *" in the log And I run :follow-hint a And I wait until data/hello.txt is loaded Then the following tabs should be open: @@ -142,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" @@ -161,3 +164,10 @@ Feature: Miscellaneous utility commands exposed to the user. Scenario: Using debug-log-filter with invalid filter When I run :debug-log-filter blah Then the error "filters: Invalid value blah - expected one of: statusbar, *" should be shown + + Scenario: Using debug-log-filter + When I run :debug-log-filter commands,ipc,webview + And I run :enter-mode insert + And I run :debug-log-filter none + And I run :leave-mode + Then "Entering mode KeyMode.insert *" should not be logged diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index 52c11ed22..9cf917c2c 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Yanking and pasting. :yank, {clipboard} and {primary} can be used to copy/paste the URL or title from/to the clipboard and primary selection. @@ -99,6 +101,11 @@ Feature: Yanking and pasting. And I run :open {primary} (invalid command) Then the error "Primary selection is empty." should be shown + Scenario: Pasting without primary selection being supported + When selection is not supported + And I run :open {primary} (invalid command) + Then the error "Primary selection is not supported on this platform!" should be shown + Scenario: Pasting with a space in clipboard When I put " " into the clipboard And I run :open {clipboard} (invalid command) diff --git a/tests/end2end/features/zoom.feature b/tests/end2end/features/zoom.feature index 3aa39df8b..015b85b17 100644 --- a/tests/end2end/features/zoom.feature +++ b/tests/end2end/features/zoom.feature @@ -1,3 +1,5 @@ +# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et: + Feature: Zooming in and out Background: diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index f57c3c6d5..040110e77 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -75,6 +75,12 @@ def is_ignored_lowlevel_message(message): "not supported by protocol" in message): # Makes tests fail on Quantumcross' machine return True + elif 'Unable to locate theme engine in module_path:' in message: + return True + elif 'getrlimit(RLIMIT_NOFILE) failed' in message: + return True + elif 'Could not bind NETLINK socket: Address already in use' in message: + return True return False @@ -308,7 +314,7 @@ class QuteProc(testprocess.Process): URLs like about:... and qute:... are handled specially and returned verbatim. """ - special_schemes = ['about:', 'qute:', 'chrome:'] + special_schemes = ['about:', 'qute:', 'chrome:', 'view-source:'] if any(path.startswith(scheme) for scheme in special_schemes): return path else: @@ -350,7 +356,8 @@ class QuteProc(testprocess.Process): self.wait_for(category='webview', message='Scroll position changed to ' + point) - def wait_for(self, timeout=None, **kwargs): + def wait_for(self, timeout=None, # pylint: disable=arguments-differ + **kwargs): """Extend wait_for to add divisor if a test is xfailing.""" __tracebackhide__ = (lambda e: e.errisinstance(testprocess.WaitForTimeout)) diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 80235e233..b40b5ebb6 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -149,11 +149,11 @@ def test_quteprocess_quitting(qtbot, quteproc_process): @pytest.mark.parametrize('data, attrs', [ ( # Normal message - '{"created": 0, "msecs": 0, "levelname": "DEBUG", "name": "init", ' + '{"created": 86400, "msecs": 0, "levelname": "DEBUG", "name": "init", ' '"module": "earlyinit", "funcName": "init_log", "lineno": 280, ' '"levelno": 10, "message": "Log initialized."}', { - 'timestamp': datetime.datetime.fromtimestamp(0), + 'timestamp': datetime.datetime.fromtimestamp(86400), 'loglevel': logging.DEBUG, 'category': 'init', 'module': 'earlyinit', @@ -165,28 +165,28 @@ def test_quteprocess_quitting(qtbot, quteproc_process): ), ( # VDEBUG - '{"created": 0, "msecs": 0, "levelname": "VDEBUG", "name": "foo", ' + '{"created": 86400, "msecs": 0, "levelname": "VDEBUG", "name": "foo", ' '"module": "foo", "funcName": "foo", "lineno": 0, "levelno": 9, ' '"message": ""}', {'loglevel': log.VDEBUG_LEVEL} ), ( # Unknown module - '{"created": 0, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' + '{"created": 86400, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' '"module": null, "funcName": null, "lineno": 0, "levelno": 10, ' '"message": "test"}', {'module': None, 'function': None, 'line': None}, ), ( # Expected message - '{"created": 0, "msecs": 0, "levelname": "VDEBUG", "name": "foo", ' + '{"created": 86400, "msecs": 0, "levelname": "VDEBUG", "name": "foo", ' '"module": "foo", "funcName": "foo", "lineno": 0, "levelno": 9, ' '"message": "SpellCheck: test"}', {'expected': True}, ), ( # Weird Qt location - '{"created": 0, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' + '{"created": 86400, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' '"module": "qnetworkreplyhttpimpl", "funcName": ' '"void QNetworkReplyHttpImplPrivate::error(' 'QNetworkReply::NetworkError, const QString&)", "lineno": 1929, ' @@ -200,7 +200,7 @@ def test_quteprocess_quitting(qtbot, quteproc_process): } ), ( - '{"created": 0, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' + '{"created": 86400, "msecs": 0, "levelname": "DEBUG", "name": "qt", ' '"module": "qxcbxsettings", "funcName": "QXcbXSettings::QXcbXSettings(' 'QXcbScreen*)", "lineno": 233, "levelno": 10, "message": ' '"QXcbXSettings::QXcbXSettings(QXcbScreen*) Failed to get selection ' @@ -213,7 +213,7 @@ def test_quteprocess_quitting(qtbot, quteproc_process): ), ( # ResourceWarning - '{"created": 0, "msecs": 0, "levelname": "WARNING", ' + '{"created": 86400, "msecs": 0, "levelname": "WARNING", ' '"name": "py.warnings", "module": "app", "funcName": "qt_mainloop", ' '"lineno": 121, "levelno": 30, "message": ' '".../app.py:121: ResourceWarning: unclosed file <_io.TextIOWrapper ' @@ -231,7 +231,7 @@ def test_log_line_parse(data, attrs): @pytest.mark.parametrize('data, colorized, expect_error, expected', [ ( - {'created': 0, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 10, 'message': 'quux'}, False, False, @@ -239,7 +239,7 @@ def test_log_line_parse(data, attrs): ), # Traceback attached ( - {'created': 0, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 10, 'message': 'quux', 'traceback': 'Traceback (most recent call ' 'last):\n here be dragons'}, @@ -250,7 +250,7 @@ def test_log_line_parse(data, attrs): ), # Colorized ( - {'created': 0, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 10, 'message': 'quux'}, True, False, @@ -259,7 +259,7 @@ def test_log_line_parse(data, attrs): ), # Expected error ( - {'created': 0, 'msecs': 0, 'levelname': 'ERROR', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'ERROR', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 40, 'message': 'quux'}, False, True, @@ -267,7 +267,7 @@ def test_log_line_parse(data, attrs): ), # Expected other message (i.e. should make no difference) ( - {'created': 0, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'DEBUG', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 10, 'message': 'quux'}, False, True, @@ -275,7 +275,7 @@ def test_log_line_parse(data, attrs): ), # Expected error colorized (shouldn't be red) ( - {'created': 0, 'msecs': 0, 'levelname': 'ERROR', 'name': 'foo', + {'created': 86400, 'msecs': 0, 'levelname': 'ERROR', 'name': 'foo', 'module': 'bar', 'funcName': 'qux', 'lineno': 10, 'levelno': 40, 'message': 'quux'}, True, True, diff --git a/tests/end2end/fixtures/test_testprocess.py b/tests/end2end/fixtures/test_testprocess.py index ef5b445ca..b38e79f56 100644 --- a/tests/end2end/fixtures/test_testprocess.py +++ b/tests/end2end/fixtures/test_testprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/fixtures/test_webserver.py b/tests/end2end/fixtures/test_webserver.py index 5f0fa8ab0..c885ea5b6 100644 --- a/tests/end2end/fixtures/test_webserver.py +++ b/tests/end2end/fixtures/test_webserver.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/fixtures/testprocess.py b/tests/end2end/fixtures/testprocess.py index c6f4edd22..4c3a6972e 100644 --- a/tests/end2end/fixtures/testprocess.py +++ b/tests/end2end/fixtures/testprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/fixtures/webserver.py b/tests/end2end/fixtures/webserver.py index f6e0f8ff2..8fc674e42 100644 --- a/tests/end2end/fixtures/webserver.py +++ b/tests/end2end/fixtures/webserver.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -74,6 +74,8 @@ class Request(testprocess.Line): '/redirect-to': [http.client.FOUND], '/cookies/set': [http.client.FOUND], + + '/custom/500-inline': [http.client.INTERNAL_SERVER_ERROR], } for i in range(15): path_to_statuses['/redirect/{}'.format(i)] = [http.client.FOUND] diff --git a/tests/end2end/fixtures/webserver_sub.py b/tests/end2end/fixtures/webserver_sub.py index df84d26d6..53c033f5b 100644 --- a/tests/end2end/fixtures/webserver_sub.py +++ b/tests/end2end/fixtures/webserver_sub.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -122,6 +122,17 @@ def redirect_self(): return app.make_response(flask.redirect(flask.url_for('redirect_self'))) +@app.route('/custom/500-inline') +def internal_error_attachment(): + """A 500 error with Content-Disposition: inline.""" + response = flask.Response(b"", headers={ + "Content-Type": "application/octet-stream", + "Content-Disposition": 'inline; filename="attachment.jpg"', + }) + response.status_code = 500 + return response + + @app.after_request def log_request(response): """Log a webserver request.""" diff --git a/tests/end2end/fixtures/webserver_sub_ssl.py b/tests/end2end/fixtures/webserver_sub_ssl.py index dadcb510e..d8a8f1025 100644 --- a/tests/end2end/fixtures/webserver_sub_ssl.py +++ b/tests/end2end/fixtures/webserver_sub_ssl.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/test_dirbrowser.py b/tests/end2end/test_dirbrowser.py index 2359020d7..3b27eebb3 100644 --- a/tests/end2end/test_dirbrowser.py +++ b/tests/end2end/test_dirbrowser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Daniel Schadt +# Copyright 2015-2017 Daniel Schadt # # This file is part of qutebrowser. # diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index c2d5143bc..d155ffcc4 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/test_insert_mode.py b/tests/end2end/test_insert_mode.py index 8b9ed3aca..3bd5d87fd 100644 --- a/tests/end2end/test_insert_mode.py +++ b/tests/end2end/test_insert_mode.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index bcb8b04e6..e4470ac3a 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -19,6 +19,7 @@ """Test starting qutebrowser with special arguments/environments.""" +import subprocess import socket import sys import logging @@ -28,7 +29,6 @@ import pytest from PyQt5.QtCore import QProcess -from end2end.fixtures import quteprocess, testprocess from qutebrowser.utils import qtutils @@ -77,8 +77,6 @@ def test_ascii_locale(request, httpbin, tmpdir, quteproc_new): https://github.com/qutebrowser/qutebrowser/issues/908 https://github.com/qutebrowser/qutebrowser/issues/1726 """ - if request.config.webengine: - pytest.skip("Downloads are not implemented with QtWebEngine yet") args = ['--temp-basedir'] + _base_args(request.config) quteproc_new.start(args, env={'LC_ALL': 'C'}) quteproc_new.set_setting('storage', 'download-directory', str(tmpdir)) @@ -113,9 +111,6 @@ def test_misconfigured_user_dirs(request, httpbin, temp_basedir_env, https://github.com/qutebrowser/qutebrowser/issues/866 https://github.com/qutebrowser/qutebrowser/issues/1269 """ - if request.config.webengine: - pytest.skip("Downloads are not implemented with QtWebEngine yet") - home = tmpdir / 'home' home.ensure(dir=True) temp_basedir_env['HOME'] = str(home) @@ -143,12 +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.""" - if request.config.webengine: - pytest.skip("qute:log is not implemented with QtWebEngine yet") + """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.' @@ -168,23 +161,24 @@ def test_optimize(request, quteproc_new, capfd, level): quteproc_new.wait_for_quit() +@pytest.mark.not_frozen def test_version(request): """Test invocation with --version argument.""" - args = ['--version'] + _base_args(request.config) + args = ['-m', 'qutebrowser', '--version'] + _base_args(request.config) # can't use quteproc_new here because it's confused by # early process termination - proc = quteprocess.QuteProc(request) - proc.proc.setProcessChannelMode(QProcess.SeparateChannels) + proc = QProcess() + proc.setProcessChannelMode(QProcess.SeparateChannels) - try: - proc.start(args) - proc.wait_for_quit() - except testprocess.ProcessExited: - assert proc.proc.exitStatus() == QProcess.NormalExit - else: - pytest.fail("Process did not exit!") + proc.start(sys.executable, args) + ok = proc.waitForStarted(2000) + assert ok + ok = proc.waitForFinished(2000) + assert ok + assert proc.exitStatus() == QProcess.NormalExit - output = bytes(proc.proc.readAllStandardOutput()).decode('utf-8') + output = bytes(proc.readAllStandardOutput()).decode('utf-8') + print(output) assert re.search(r'^qutebrowser\s+v\d+(\.\d+)', output) is not None @@ -261,3 +255,15 @@ def test_command_on_start(request, quteproc_new): quteproc_new.start(args) quteproc_new.send_cmd(':quit') quteproc_new.wait_for_quit() + + +def test_launching_with_python2(): + try: + proc = subprocess.Popen(['python2', '-m', 'qutebrowser', + '--no-err-windows'], stderr=subprocess.PIPE) + except FileNotFoundError: + pytest.skip("python2 not found") + _stdout, stderr = proc.communicate() + assert proc.returncode == 1 + error = "At least Python 3.4 is required to run qutebrowser" + assert stderr.decode('ascii').startswith(error) diff --git a/tests/end2end/test_mhtml_e2e.py b/tests/end2end/test_mhtml_e2e.py index 46ece8f0e..f753e6fd4 100644 --- a/tests/end2end/test_mhtml_e2e.py +++ b/tests/end2end/test_mhtml_e2e.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/fixtures.py b/tests/helpers/fixtures.py index f9e829e40..3af7195f5 100644 --- a/tests/helpers/fixtures.py +++ b/tests/helpers/fixtures.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/logfail.py b/tests/helpers/logfail.py index 2967e3ccf..3d8e3afb8 100644 --- a/tests/helpers/logfail.py +++ b/tests/helpers/logfail.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/messagemock.py b/tests/helpers/messagemock.py index 7854aabcc..77116115f 100644 --- a/tests/helpers/messagemock.py +++ b/tests/helpers/messagemock.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index dfbcc550d..df598e4ed 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -376,9 +376,6 @@ class InstaTimer(QObject): timeout = pyqtSignal() - def __init__(self, parent=None): - super().__init__(parent) - def start(self): self.timeout.emit() @@ -410,9 +407,6 @@ class StatusBarCommandStub(QLineEdit): show_cmd = pyqtSignal() hide_cmd = pyqtSignal() - def __init__(self, parent=None): - super().__init__(parent) - def prefix(self): return self.text()[0] @@ -594,6 +588,3 @@ class ApplicationStub(QObject): """Stub to insert as the app object in objreg.""" new_window = pyqtSignal(mainwindow.MainWindow) - - def __init__(self): - super().__init__() diff --git a/tests/helpers/test_helper_utils.py b/tests/helpers/test_helper_utils.py index 65c7c29a1..d7eadb894 100644 --- a/tests/helpers/test_helper_utils.py +++ b/tests/helpers/test_helper_utils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/test_logfail.py b/tests/helpers/test_logfail.py index 6bc4f364c..b95dec1d6 100644 --- a/tests/helpers/test_logfail.py +++ b/tests/helpers/test_logfail.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/test_stubs.py b/tests/helpers/test_stubs.py index d542afe54..10fa9e5db 100644 --- a/tests/helpers/test_stubs.py +++ b/tests/helpers/test_stubs.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py index faafefa82..1ef469144 100644 --- a/tests/helpers/utils.py +++ b/tests/helpers/utils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -17,8 +17,6 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -# pylint: disable=unused-variable - """Partial comparison of dicts/lists.""" diff --git a/tests/test_conftest.py b/tests/test_conftest.py index 24fb67097..74db4bf78 100644 --- a/tests/test_conftest.py +++ b/tests/test_conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/test_commands.py b/tests/unit/browser/test_commands.py index e7654a2d7..5a9d7a8da 100644 --- a/tests/unit/browser/test_commands.py +++ b/tests/unit/browser/test_commands.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/test_qutescheme.py b/tests/unit/browser/test_qutescheme.py index 5ceecb7f8..92ad30574 100644 --- a/tests/unit/browser/test_qutescheme.py +++ b/tests/unit/browser/test_qutescheme.py @@ -17,8 +17,9 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . -import datetime -import collections +import json +import os +import time from PyQt5.QtCore import QUrl import pytest @@ -27,30 +28,72 @@ from qutebrowser.browser import history, qutescheme from qutebrowser.utils import objreg -Dates = collections.namedtuple('Dates', ['yesterday', 'today', 'tomorrow']) +class TestJavascriptHandler: + + """Test the qute://javascript endpoint.""" + + # Tuples of fake JS files and their content. + js_files = [ + ('foo.js', "var a = 'foo';"), + ('bar.js', "var a = 'bar';"), + ] + + @pytest.fixture(autouse=True) + def patch_read_file(self, monkeypatch): + """Patch utils.read_file to return few fake JS files.""" + def _read_file(path, binary=False): + """Faked utils.read_file.""" + assert not binary + for filename, content in self.js_files: + if path == os.path.join('javascript', filename): + return content + raise OSError("File not found {}!".format(path)) + + monkeypatch.setattr('qutebrowser.utils.utils.read_file', _read_file) + + @pytest.mark.parametrize("filename, content", js_files) + def test_qutejavascript(self, filename, content): + url = QUrl("qute://javascript/{}".format(filename)) + _mimetype, data = qutescheme.qute_javascript(url) + + assert data == content + + def test_qutejavascript_404(self): + url = QUrl("qute://javascript/404.js") + + with pytest.raises(qutescheme.QuteSchemeOSError): + qutescheme.data_for_url(url) + + def test_qutejavascript_empty_query(self): + url = QUrl("qute://javascript") + + with pytest.raises(qutescheme.QuteSchemeError): + qutescheme.qute_javascript(url) class TestHistoryHandler: """Test the qute://history endpoint.""" - @pytest.fixture - def dates(self): - one_day = datetime.timedelta(days=1) - today = datetime.datetime.today() - tomorrow = today + one_day - yesterday = today - one_day - return Dates(yesterday, today, tomorrow) + @pytest.fixture(scope="module") + def now(self): + return int(time.time()) @pytest.fixture - def entries(self, dates): - today = history.Entry(atime=str(dates.today.timestamp()), - url=QUrl('www.today.com'), title='today') - tomorrow = history.Entry(atime=str(dates.tomorrow.timestamp()), - url=QUrl('www.tomorrow.com'), title='tomorrow') - yesterday = history.Entry(atime=str(dates.yesterday.timestamp()), - url=QUrl('www.yesterday.com'), title='yesterday') - return Dates(yesterday, today, tomorrow) + def entries(self, now): + """Create fake history entries.""" + # create 12 history items spaced 6 hours apart, starting from now + entry_count = 12 + interval = 6 * 60 * 60 + + items = [] + for i in range(entry_count): + entry_atime = now - i * interval + entry = history.Entry(atime=str(entry_atime), + url=QUrl("www.x.com/" + str(i)), title="Page " + str(i)) + items.insert(0, entry) + + return items @pytest.fixture def fake_web_history(self, fake_save_manager, tmpdir): @@ -62,78 +105,63 @@ class TestHistoryHandler: @pytest.fixture(autouse=True) def fake_history(self, fake_web_history, entries): - """Create fake history for three different days.""" - fake_web_history._add_entry(entries.yesterday) - fake_web_history._add_entry(entries.today) - fake_web_history._add_entry(entries.tomorrow) + """Create fake history.""" + for item in entries: + fake_web_history._add_entry(item) fake_web_history.save() - def test_history_without_query(self): - """Ensure qute://history shows today's history without any query.""" - _mimetype, data = qutescheme.qute_history(QUrl("qute://history")) - key = "{}".format( - datetime.date.today().strftime("%a, %d %B %Y")) - assert key in data - - def test_history_with_bad_query(self): - """Ensure qute://history shows today's history with bad query.""" - url = QUrl("qute://history?date=204-blaah") + @pytest.mark.parametrize("start_time_offset, expected_item_count", [ + (0, 4), + (24*60*60, 4), + (48*60*60, 4), + (72*60*60, 0) + ]) + def test_qutehistory_data(self, start_time_offset, expected_item_count, + now): + """Ensure qute://history/data returns correct items.""" + start_time = now - start_time_offset + url = QUrl("qute://history/data?start_time=" + str(start_time)) _mimetype, data = qutescheme.qute_history(url) - key = "{}".format( - datetime.date.today().strftime("%a, %d %B %Y")) - assert key in data + items = json.loads(data) + items = [item for item in items if 'time' in item] # skip 'next' item - def test_history_today(self): - """Ensure qute://history shows history for today.""" - url = QUrl("qute://history") + assert len(items) == expected_item_count + + # test times + end_time = start_time - 24*60*60 + for item in items: + assert item['time'] <= start_time * 1000 + assert item['time'] > end_time * 1000 + + @pytest.mark.parametrize("start_time_offset, next_time", [ + (0, 24*60*60), + (24*60*60, 48*60*60), + (48*60*60, -1), + (72*60*60, -1) + ]) + def test_qutehistory_next(self, start_time_offset, next_time, now): + """Ensure qute://history/data returns correct items.""" + start_time = now - start_time_offset + url = QUrl("qute://history/data?start_time=" + str(start_time)) _mimetype, data = qutescheme.qute_history(url) - assert "today" in data - assert "tomorrow" not in data - assert "yesterday" not in data + items = json.loads(data) + items = [item for item in items if 'next' in item] # 'next' items + assert len(items) == 1 - def test_history_yesterday(self, dates): - """Ensure qute://history shows history for yesterday.""" - url = QUrl("qute://history?date=" + - dates.yesterday.strftime("%Y-%m-%d")) - _mimetype, data = qutescheme.qute_history(url) - assert "today" not in data - assert "tomorrow" not in data - assert "yesterday" in data + if next_time == -1: + assert items[0]["next"] == -1 + else: + assert items[0]["next"] == now - next_time - def test_history_tomorrow(self, dates): - """Ensure qute://history shows history for tomorrow.""" - url = QUrl("qute://history?date=" + - dates.tomorrow.strftime("%Y-%m-%d")) - _mimetype, data = qutescheme.qute_history(url) - assert "today" not in data - assert "tomorrow" in data - assert "yesterday" not in data - - def test_no_next_link_to_future(self, dates): - """Ensure there's no next link pointing to the future.""" - url = QUrl("qute://history") - _mimetype, data = qutescheme.qute_history(url) - assert "Next" not in data - - url = QUrl("qute://history?date=" + - dates.tomorrow.strftime("%Y-%m-%d")) - _mimetype, data = qutescheme.qute_history(url) - assert "Next" not in data - - def test_qute_history_benchmark(self, dates, entries, fake_web_history, - benchmark): - for i in range(100000): + def test_qute_history_benchmark(self, fake_web_history, benchmark, now): + # items must be earliest-first to ensure history is sorted properly + for t in range(100000, 0, -1): # one history per second entry = history.Entry( - atime=str(dates.yesterday.timestamp()), - url=QUrl('www.yesterday.com/{}'.format(i)), - title='yesterday') + atime=str(now - t), + url=QUrl('www.x.com/{}'.format(t)), + title='x at {}'.format(t)) fake_web_history._add_entry(entry) - fake_web_history._add_entry(entries.today) - fake_web_history._add_entry(entries.tomorrow) - url = QUrl("qute://history") + url = QUrl("qute://history/data?start_time={}".format(now)) _mimetype, data = benchmark(qutescheme.qute_history, url) - - assert "today" in data - assert "tomorrow" not in data - assert "yesterday" not in data + assert len(json.loads(data)) > 1 diff --git a/tests/unit/browser/test_shared.py b/tests/unit/browser/test_shared.py index da81c2059..8cd1d6704 100644 --- a/tests/unit/browser/test_shared.py +++ b/tests/unit/browser/test_shared.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/test_signalfilter.py b/tests/unit/browser/test_signalfilter.py index bf8651d7f..4c02f89c4 100644 --- a/tests/unit/browser/test_signalfilter.py +++ b/tests/unit/browser/test_signalfilter.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/test_tab.py b/tests/unit/browser/test_tab.py index 7ff1edbde..bfd060956 100644 --- a/tests/unit/browser/test_tab.py +++ b/tests/unit/browser/test_tab.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webengine/test_webenginedownloads.py b/tests/unit/browser/webengine/test_webenginedownloads.py index ee5b8e22a..4ad146306 100644 --- a/tests/unit/browser/webengine/test_webenginedownloads.py +++ b/tests/unit/browser/webengine/test_webenginedownloads.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -24,6 +24,12 @@ import pytest pytest.importorskip('PyQt5.QtWebEngineWidgets') from qutebrowser.browser.webengine import webenginedownloads +from qutebrowser.utils import qtutils + +qt58 = pytest.mark.skipif( + qtutils.version_check('5.9'), reason="Needs Qt 5.8 or earlier") +qt59 = pytest.mark.skipif( + not qtutils.version_check('5.9'), reason="Needs Qt 5.9 or newer") @pytest.mark.parametrize('path, expected', [ @@ -31,8 +37,10 @@ from qutebrowser.browser.webengine import webenginedownloads ('foo(1)', 'foo'), ('foo(a)', 'foo(a)'), ('foo1', 'foo1'), - ('foo%20bar', 'foo bar'), - ('foo%2Fbar', 'bar'), + qt58(('foo%20bar', 'foo bar')), + qt58(('foo%2Fbar', 'bar')), + qt59(('foo%20bar', 'foo%20bar')), + qt59(('foo%2Fbar', 'foo%2Fbar')), ]) def test_get_suggested_filename(path, expected): assert webenginedownloads._get_suggested_filename(path) == expected diff --git a/tests/unit/browser/webkit/http/test_content_disposition.py b/tests/unit/browser/webkit/http/test_content_disposition.py index 7b817c5e9..e1f78eb74 100644 --- a/tests/unit/browser/webkit/http/test_content_disposition.py +++ b/tests/unit/browser/webkit/http/test_content_disposition.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/http/test_http.py b/tests/unit/browser/webkit/http/test_http.py index ee80baed7..1f9b7d75d 100644 --- a/tests/unit/browser/webkit/http/test_http.py +++ b/tests/unit/browser/webkit/http/test_http.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/http/test_http_hypothesis.py b/tests/unit/browser/webkit/http/test_http_hypothesis.py index 1fb4dd835..42290a8f4 100644 --- a/tests/unit/browser/webkit/http/test_http_hypothesis.py +++ b/tests/unit/browser/webkit/http/test_http_hypothesis.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/network/test_filescheme.py b/tests/unit/browser/webkit/network/test_filescheme.py index c3ef870d6..7a1bd9b19 100644 --- a/tests/unit/browser/webkit/network/test_filescheme.py +++ b/tests/unit/browser/webkit/network/test_filescheme.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Antoni Boucher (antoyo) +# Copyright 2015-2017 Antoni Boucher (antoyo) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/network/test_networkmanager.py b/tests/unit/browser/webkit/network/test_networkmanager.py index 4ac93435d..3e8dd4d13 100644 --- a/tests/unit/browser/webkit/network/test_networkmanager.py +++ b/tests/unit/browser/webkit/network/test_networkmanager.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/network/test_networkreply.py b/tests/unit/browser/webkit/network/test_networkreply.py index 7a9c89393..8d24ebab8 100644 --- a/tests/unit/browser/webkit/network/test_networkreply.py +++ b/tests/unit/browser/webkit/network/test_networkreply.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -91,3 +91,11 @@ 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 + reply.abort() # shouldn't do anything diff --git a/tests/unit/browser/webkit/network/test_pac.py b/tests/unit/browser/webkit/network/test_pac.py index 2ad63a496..bc914bbfc 100644 --- a/tests/unit/browser/webkit/network/test_pac.py +++ b/tests/unit/browser/webkit/network/test_pac.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/network/test_schemehandler.py b/tests/unit/browser/webkit/network/test_schemehandler.py index d981b8412..1f5464a85 100644 --- a/tests/unit/browser/webkit/network/test_schemehandler.py +++ b/tests/unit/browser/webkit/network/test_schemehandler.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/network/test_webkitqutescheme.py b/tests/unit/browser/webkit/network/test_webkitqutescheme.py index f941abb6d..d45f1a31f 100644 --- a/tests/unit/browser/webkit/network/test_webkitqutescheme.py +++ b/tests/unit/browser/webkit/network/test_webkitqutescheme.py @@ -1,6 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Daniel Schadt +# Copyright 2016-2017 Daniel Schadt +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_cache.py b/tests/unit/browser/webkit/test_cache.py index c716b9b82..f084a9869 100644 --- a/tests/unit/browser/webkit/test_cache.py +++ b/tests/unit/browser/webkit/test_cache.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 lamarpavel +# Copyright 2015-2017 lamarpavel # # This file is part of qutebrowser. # @@ -17,10 +17,17 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +import pytest from PyQt5.QtCore import QUrl, QDateTime from PyQt5.QtNetwork import QNetworkDiskCache, QNetworkCacheMetaData from qutebrowser.browser.webkit import cache +from qutebrowser.utils import qtutils + + +pytestmark = pytest.mark.skipif( + qtutils.version_check('5.7.1') and not qtutils.version_check('5.9'), + reason="QNetworkDiskCache is broken on Qt 5.7.1 and 5.8") def preload_cache(cache, url='http://www.example.com/', content=b'foobar'): diff --git a/tests/unit/browser/webkit/test_cookies.py b/tests/unit/browser/webkit/test_cookies.py index bcee01e60..85d045763 100644 --- a/tests/unit/browser/webkit/test_cookies.py +++ b/tests/unit/browser/webkit/test_cookies.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Alexander Cogneau (acogneau) : +# Copyright 2015-2017 Alexander Cogneau (acogneau) : # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_downloads.py b/tests/unit/browser/webkit/test_downloads.py index acee916b2..72b533cc7 100644 --- a/tests/unit/browser/webkit/test_downloads.py +++ b/tests/unit/browser/webkit/test_downloads.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_history.py b/tests/unit/browser/webkit/test_history.py index cc35dc4e4..37176dffe 100644 --- a/tests/unit/browser/webkit/test_history.py +++ b/tests/unit/browser/webkit/test_history.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_mhtml.py b/tests/unit/browser/webkit/test_mhtml.py index e5fdf4ffd..ad554cf3d 100644 --- a/tests/unit/browser/webkit/test_mhtml.py +++ b/tests/unit/browser/webkit/test_mhtml.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Daniel Schadt +# Copyright 2015-2017 Daniel Schadt # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_qt_javascript.py b/tests/unit/browser/webkit/test_qt_javascript.py index cb7c3a2ae..fd8a0c496 100644 --- a/tests/unit/browser/webkit/test_qt_javascript.py +++ b/tests/unit/browser/webkit/test_qt_javascript.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_tabhistory.py b/tests/unit/browser/webkit/test_tabhistory.py index b99d8376d..07b334771 100644 --- a/tests/unit/browser/webkit/test_tabhistory.py +++ b/tests/unit/browser/webkit/test_tabhistory.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/browser/webkit/test_webkitelem.py b/tests/unit/browser/webkit/test_webkitelem.py index c4384fa61..6258508ff 100644 --- a/tests/unit/browser/webkit/test_webkitelem.py +++ b/tests/unit/browser/webkit/test_webkitelem.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/commands/test_argparser.py b/tests/unit/commands/test_argparser.py index fc577fd15..54a584e71 100644 --- a/tests/unit/commands/test_argparser.py +++ b/tests/unit/commands/test_argparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/commands/test_cmdutils.py b/tests/unit/commands/test_cmdutils.py index 577a85540..06740031c 100644 --- a/tests/unit/commands/test_cmdutils.py +++ b/tests/unit/commands/test_cmdutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/commands/test_runners.py b/tests/unit/commands/test_runners.py index a7370081b..720fa5209 100644 --- a/tests/unit/commands/test_runners.py +++ b/tests/unit/commands/test_runners.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/commands/test_userscripts.py b/tests/unit/commands/test_userscripts.py index 5212b0d32..c6348d028 100644 --- a/tests/unit/commands/test_userscripts.py +++ b/tests/unit/commands/test_userscripts.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/completion/test_column_widths.py b/tests/unit/completion/test_column_widths.py index aae9c71aa..21456ed37 100644 --- a/tests/unit/completion/test_column_widths.py +++ b/tests/unit/completion/test_column_widths.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Alexander Cogneau +# Copyright 2015-2017 Alexander Cogneau # # This file is part of qutebrowser. # diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index a2b3bc7f0..9feff4655 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # @@ -113,7 +113,6 @@ def cmdutils_patch(monkeypatch, stubs): @cmdutils.argument('command', completion=usertypes.Completion.command) def bind(key, win_id, command=None, *, mode='normal', force=False): """docstring.""" - # pylint: disable=unused-variable pass def tab_detach(): diff --git a/tests/unit/completion/test_completionwidget.py b/tests/unit/completion/test_completionwidget.py index 5a4ee87c0..9a8de3cad 100644 --- a/tests/unit/completion/test_completionwidget.py +++ b/tests/unit/completion/test_completionwidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 84c87b270..ff00a11a9 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # @@ -26,7 +26,8 @@ import pytest from PyQt5.QtCore import QUrl from PyQt5.QtWidgets import QTreeView -from qutebrowser.completion.models import miscmodels, urlmodel, configmodel +from qutebrowser.completion.models import (miscmodels, urlmodel, configmodel, + sortfilter) from qutebrowser.browser import history from qutebrowser.config import sections, value @@ -498,3 +499,42 @@ def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub, ('rock', "Alias for 'roll'", 'ro'), ] }) + + +def test_url_completion_benchmark(benchmark, config_stub, + quickmark_manager_stub, + bookmark_manager_stub, + web_history_stub): + """Benchmark url completion.""" + config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d', + 'web-history-max-items': 1000} + + entries = [history.Entry( + atime=i, + url=QUrl('http://example.com/{}'.format(i)), + title='title{}'.format(i)) + for i in range(100000)] + + web_history_stub.history_dict = collections.OrderedDict( + ((e.url_str(), e) for e in entries)) + + quickmark_manager_stub.marks = collections.OrderedDict( + (e.title, e.url_str()) + for e in entries[0:1000]) + + bookmark_manager_stub.marks = collections.OrderedDict( + (e.url_str(), e.title) + for e in entries[0:1000]) + + def bench(): + model = urlmodel.UrlCompletionModel() + filtermodel = sortfilter.CompletionFilterModel(model) + filtermodel.set_pattern('') + filtermodel.set_pattern('e') + filtermodel.set_pattern('ex') + filtermodel.set_pattern('ex ') + filtermodel.set_pattern('ex 1') + filtermodel.set_pattern('ex 12') + filtermodel.set_pattern('ex 123') + + benchmark(bench) diff --git a/tests/unit/completion/test_sortfilter.py b/tests/unit/completion/test_sortfilter.py index cef394226..2d4a4e25d 100644 --- a/tests/unit/completion/test_sortfilter.py +++ b/tests/unit/completion/test_sortfilter.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index e98302604..c897b9f16 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_configdata.py b/tests/unit/config/test_configdata.py index 3d0d046b2..50509820e 100644 --- a/tests/unit/config/test_configdata.py +++ b/tests/unit/config/test_configdata.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_configexc.py b/tests/unit/config/test_configexc.py index 3cb8397d4..330ad7b07 100644 --- a/tests/unit/config/test_configexc.py +++ b/tests/unit/config/test_configexc.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index d44303278..4cfb9100c 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_configtypes_hypothesis.py b/tests/unit/config/test_configtypes_hypothesis.py index ed45ccc0b..96f47622e 100644 --- a/tests/unit/config/test_configtypes_hypothesis.py +++ b/tests/unit/config/test_configtypes_hypothesis.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_style.py b/tests/unit/config/test_style.py index 6b55aebae..2e4d8c1ce 100644 --- a/tests/unit/config/test_style.py +++ b/tests/unit/config/test_style.py @@ -1,5 +1,5 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/config/test_textwrapper.py b/tests/unit/config/test_textwrapper.py index 9fb337091..a654f9367 100644 --- a/tests/unit/config/test_textwrapper.py +++ b/tests/unit/config/test_textwrapper.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/javascript/conftest.py b/tests/unit/javascript/conftest.py index 88cfecdc4..b9f013ecf 100644 --- a/tests/unit/javascript/conftest.py +++ b/tests/unit/javascript/conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/javascript/position_caret/test_position_caret.py b/tests/unit/javascript/position_caret/test_position_caret.py index 2691c1afe..7be62e3cc 100644 --- a/tests/unit/javascript/position_caret/test_position_caret.py +++ b/tests/unit/javascript/position_caret/test_position_caret.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/keyinput/conftest.py b/tests/unit/keyinput/conftest.py index 75f318b53..28c24b2e2 100644 --- a/tests/unit/keyinput/conftest.py +++ b/tests/unit/keyinput/conftest.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) : +# Copyright 2015-2017 Florian Bruhin (The Compiler) : # # This file is part of qutebrowser. # diff --git a/tests/unit/keyinput/test_basekeyparser.py b/tests/unit/keyinput/test_basekeyparser.py index da1ecfbdf..0cb0763e7 100644 --- a/tests/unit/keyinput/test_basekeyparser.py +++ b/tests/unit/keyinput/test_basekeyparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) : +# Copyright 2014-2017 Florian Bruhin (The Compiler) : # # This file is part of qutebrowser. # diff --git a/tests/unit/keyinput/test_modeman.py b/tests/unit/keyinput/test_modeman.py index aa3a2753a..d00ce09f7 100644 --- a/tests/unit/keyinput/test_modeman.py +++ b/tests/unit/keyinput/test_modeman.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/keyinput/test_modeparsers.py b/tests/unit/keyinput/test_modeparsers.py index c01e0e5ba..d7da17129 100644 --- a/tests/unit/keyinput/test_modeparsers.py +++ b/tests/unit/keyinput/test_modeparsers.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) : +# Copyright 2015-2017 Florian Bruhin (The Compiler) : # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/statusbar/test_percentage.py b/tests/unit/mainwindow/statusbar/test_percentage.py index 0974e34ed..6622b06f4 100644 --- a/tests/unit/mainwindow/statusbar/test_percentage.py +++ b/tests/unit/mainwindow/statusbar/test_percentage.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/statusbar/test_progress.py b/tests/unit/mainwindow/statusbar/test_progress.py index 70865e104..f553f8c22 100644 --- a/tests/unit/mainwindow/statusbar/test_progress.py +++ b/tests/unit/mainwindow/statusbar/test_progress.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/statusbar/test_tabindex.py b/tests/unit/mainwindow/statusbar/test_tabindex.py index ca5673da2..99b5d9114 100644 --- a/tests/unit/mainwindow/statusbar/test_tabindex.py +++ b/tests/unit/mainwindow/statusbar/test_tabindex.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/statusbar/test_textbase.py b/tests/unit/mainwindow/statusbar/test_textbase.py index 1fb661296..b976f5c6f 100644 --- a/tests/unit/mainwindow/statusbar/test_textbase.py +++ b/tests/unit/mainwindow/statusbar/test_textbase.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/statusbar/test_url.py b/tests/unit/mainwindow/statusbar/test_url.py index 1fe201bff..fc8ffb705 100644 --- a/tests/unit/mainwindow/statusbar/test_url.py +++ b/tests/unit/mainwindow/statusbar/test_url.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Clayton Craft (craftyguy) +# Copyright 2016-2017 Clayton Craft (craftyguy) # # This file is part of qutebrowser. # @@ -22,7 +22,7 @@ import pytest -from qutebrowser.utils import usertypes +from qutebrowser.utils import usertypes, urlutils from qutebrowser.mainwindow.statusbar import url from PyQt5.QtCore import QUrl @@ -51,49 +51,42 @@ def url_widget(qtbot, monkeypatch, config_stub): return widget -@pytest.mark.parametrize('qurl', [ - QUrl('http://abc123.com/this/awesome/url.html'), - QUrl('https://supersecret.gov/nsa/files.txt'), - None -]) -def test_set_url(url_widget, qurl): - """Test text displayed by the widget.""" - url_widget.set_url(qurl) - if qurl is not None: - assert url_widget.text() == qurl.toDisplayString() - else: - assert url_widget.text() == "" - - -@pytest.mark.parametrize('url_text', [ - 'http://abc123.com/this/awesome/url.html', - 'https://supersecret.gov/nsa/files.txt', - None, -]) -def test_set_hover_url(url_widget, url_text): - """Test text when hovering over a link.""" - url_widget.set_hover_url(url_text) - if url_text is not None: - assert url_widget.text() == url_text - assert url_widget._urltype == url.UrlType.hover - else: - assert url_widget.text() == '' - assert url_widget._urltype == url.UrlType.normal - - @pytest.mark.parametrize('url_text, expected', [ + ('http://example.com/foo/bar.html', 'http://example.com/foo/bar.html'), ('http://test.gr/%CE%B1%CE%B2%CE%B3%CE%B4.txt', 'http://test.gr/αβγδ.txt'), ('http://test.ru/%D0%B0%D0%B1%D0%B2%D0%B3.txt', 'http://test.ru/абвг.txt'), ('http://test.com/s%20p%20a%20c%20e.txt', 'http://test.com/s p a c e.txt'), ('http://test.com/%22quotes%22.html', 'http://test.com/%22quotes%22.html'), ('http://username:secret%20password@test.com', 'http://username@test.com'), - ('http://example.com%5b/', 'http://example.com%5b/'), # invalid url + ('http://example.com%5b/', '(invalid URL!) http://example.com%5b/'), + # https://bugreports.qt.io/browse/QTBUG-60364 + ('http://www.xn--80ak6aa92e.com', + '(unparseable URL!) http://www.аррӏе.com'), + # IDN URL + ('http://www.ä.com', '(www.xn--4ca.com) http://www.ä.com'), + (None, ''), ]) -def test_set_hover_url_encoded(url_widget, url_text, expected): +@pytest.mark.parametrize('which', ['normal', 'hover']) +def test_set_url(url_widget, url_text, expected, which): """Test text when hovering over a percent encoded link.""" - url_widget.set_hover_url(url_text) + if which == 'normal': + if url_text is None: + qurl = None + else: + qurl = QUrl(url_text) + if not qurl.isValid(): + # Special case for the invalid URL above + expected = "Invalid URL!" + url_widget.set_url(qurl) + else: + url_widget.set_hover_url(url_text) + assert url_widget.text() == expected - assert url_widget._urltype == url.UrlType.hover + + if which == 'hover' and expected: + assert url_widget._urltype == url.UrlType.hover + else: + assert url_widget._urltype == url.UrlType.normal @pytest.mark.parametrize('status, expected', [ @@ -114,16 +107,23 @@ def test_on_load_status_changed(url_widget, status, expected): @pytest.mark.parametrize('load_status, qurl', [ (url.UrlType.success, QUrl('http://abc123.com/this/awesome/url.html')), (url.UrlType.success, QUrl('http://reddit.com/r/linux')), + (url.UrlType.success, QUrl('http://ä.com/')), (url.UrlType.success_https, QUrl('www.google.com')), (url.UrlType.success_https, QUrl('https://supersecret.gov/nsa/files.txt')), (url.UrlType.warn, QUrl('www.shadysite.org/some/file/with/issues.htm')), (url.UrlType.error, QUrl('invalid::/url')), + (url.UrlType.error, QUrl()), ]) def test_on_tab_changed(url_widget, fake_web_tab, load_status, qurl): tab_widget = fake_web_tab(load_status=load_status, url=qurl) url_widget.on_tab_changed(tab_widget) + assert url_widget._urltype == load_status - assert url_widget.text() == qurl.toDisplayString() + if not qurl.isValid(): + expected = '' + else: + expected = urlutils.safe_display_string(qurl) + assert url_widget.text() == expected @pytest.mark.parametrize('qurl, load_status, expected_status', [ diff --git a/tests/unit/mainwindow/test_messageview.py b/tests/unit/mainwindow/test_messageview.py index 740c090b6..ebecc9398 100644 --- a/tests/unit/mainwindow/test_messageview.py +++ b/tests/unit/mainwindow/test_messageview.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -18,6 +18,7 @@ # along with qutebrowser. If not, see . import pytest +from PyQt5.QtCore import Qt from qutebrowser.mainwindow import messageview from qutebrowser.utils import usertypes @@ -114,3 +115,17 @@ def test_replaced_messages(view, replace1, replace2, length): view.show_message(usertypes.MessageLevel.info, 'test', replace=replace1) view.show_message(usertypes.MessageLevel.info, 'test 2', replace=replace2) assert len(view._messages) == length + + +@pytest.mark.parametrize('button, count', [ + (Qt.LeftButton, 0), + (Qt.MiddleButton, 0), + (Qt.RightButton, 0), + (Qt.BackButton, 2), +]) +def test_click_messages(qtbot, view, button, count): + """Messages should dissappear when we click on them.""" + view.show_message(usertypes.MessageLevel.info, 'test mouse click') + view.show_message(usertypes.MessageLevel.info, 'test mouse click 2') + qtbot.mousePress(view, button) + assert len(view._messages) == count diff --git a/tests/unit/mainwindow/test_prompt.py b/tests/unit/mainwindow/test_prompt.py index dcc5461a5..6fa8592af 100644 --- a/tests/unit/mainwindow/test_prompt.py +++ b/tests/unit/mainwindow/test_prompt.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index b9cfb5d89..01a0c0ec8 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Daniel Schadt +# Copyright 2015-2017 Daniel Schadt # # This file is part of qutebrowser. # @@ -44,6 +44,7 @@ class TestTabWidget: 'select-on-remove': 1, 'show': 'always', 'show-favicons': True, + 'favicon-scale': 1.0, 'padding': configtypes.PaddingValues(0, 0, 5, 5), 'indicator-width': 3, 'indicator-padding': configtypes.PaddingValues(2, 2, 0, 4), diff --git a/tests/unit/misc/test_autoupdate.py b/tests/unit/misc/test_autoupdate.py index eb7ed6f91..d803e4877 100644 --- a/tests/unit/misc/test_autoupdate.py +++ b/tests/unit/misc/test_autoupdate.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Alexander Cogneau (acogneau) : +# Copyright 2015-2017 Alexander Cogneau (acogneau) : # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_checkpyver.py b/tests/unit/misc/test_checkpyver.py index 4c2cf3b7d..aeab032e7 100644 --- a/tests/unit/misc/test_checkpyver.py +++ b/tests/unit/misc/test_checkpyver.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # This file is part of qutebrowser. diff --git a/tests/unit/misc/test_cmdhistory.py b/tests/unit/misc/test_cmdhistory.py index 47284c075..7da721c2b 100644 --- a/tests/unit/misc/test_cmdhistory.py +++ b/tests/unit/misc/test_cmdhistory.py @@ -1,7 +1,7 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Alexander Cogneau (acogneau) -# Copyright 2015-2016 Florian Bruhin (The-Compiler) +# Copyright 2015-2017 Alexander Cogneau (acogneau) +# Copyright 2015-2017 Florian Bruhin (The-Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_crashdialog.py b/tests/unit/misc/test_crashdialog.py index dd2ebd35e..89ca342ee 100644 --- a/tests/unit/misc/test_crashdialog.py +++ b/tests/unit/misc/test_crashdialog.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_earlyinit.py b/tests/unit/misc/test_earlyinit.py index e61b06475..840936853 100644 --- a/tests/unit/misc/test_earlyinit.py +++ b/tests/unit/misc/test_earlyinit.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The-Compiler) +# Copyright 2016-2017 Florian Bruhin (The-Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_editor.py b/tests/unit/misc/test_editor.py index 193f5fa30..de9125c8b 100644 --- a/tests/unit/misc/test_editor.py +++ b/tests/unit/misc/test_editor.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py index 865e91a84..3101b7427 100644 --- a/tests/unit/misc/test_guiprocess.py +++ b/tests/unit/misc/test_guiprocess.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -41,6 +41,7 @@ def proc(qtbot, caplog): p._proc.terminate() if not blocker.signal_triggered: p._proc.kill() + p._proc.waitForFinished() @pytest.fixture() diff --git a/tests/unit/misc/test_ipc.py b/tests/unit/misc/test_ipc.py index a942229bd..cf5cc9a3f 100644 --- a/tests/unit/misc/test_ipc.py +++ b/tests/unit/misc/test_ipc.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -587,22 +587,20 @@ def test_timeout(qtbot, caplog, qlocalsocket, ipc_server): assert caplog.records[-1].message.startswith("IPC connection timed out") -@pytest.mark.parametrize('method, args, is_warning', [ - pytest.mark.posix(('on_error', [0], False)), - ('on_disconnected', [], False), - ('on_ready_read', [], True), -]) -def test_ipcserver_socket_none(ipc_server, caplog, method, args, is_warning): - func = getattr(ipc_server, method) +def test_ipcserver_socket_none_readyread(ipc_server, caplog): assert ipc_server._socket is None + assert ipc_server._old_socket is None + with caplog.at_level(logging.WARNING): + ipc_server.on_ready_read() + msg = "In on_ready_read with None socket and old_socket!" + assert msg in [r.message for r in caplog.records] - if is_warning: - with caplog.at_level(logging.WARNING): - func(*args) - else: - func(*args) - msg = "In {} with None socket!".format(method) +@pytest.mark.posix +def test_ipcserver_socket_none_error(ipc_server, caplog): + assert ipc_server._socket is None + ipc_server.on_error(0) + msg = "In on_error with None socket!" assert msg in [r.message for r in caplog.records] diff --git a/tests/unit/misc/test_keyhints.py b/tests/unit/misc/test_keyhints.py index a4093382c..b1255eb2c 100644 --- a/tests/unit/misc/test_keyhints.py +++ b/tests/unit/misc/test_keyhints.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Ryan Roden-Corrent (rcorre) +# Copyright 2016-2017 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # @@ -44,8 +44,8 @@ def expected_text(*args): @pytest.fixture -def keyhint(qtbot, config_stub, key_config_stub): - """Fixture to initialize a KeyHintView.""" +def keyhint_config(config_stub): + """Fixture providing the necessary config settings for the KeyHintView.""" config_stub.data = { 'colors': { 'keyhint.fg': 'white', @@ -55,9 +55,16 @@ def keyhint(qtbot, config_stub, key_config_stub): 'fonts': {'keyhint': 'Comic Sans'}, 'ui': { 'keyhint-blacklist': '', + 'keyhint-delay': 500, 'status-position': 'bottom', }, } + return config_stub + + +@pytest.fixture +def keyhint(qtbot, keyhint_config, key_config_stub): + """Fixture to initialize a KeyHintView.""" keyhint = KeyHintView(0, None) qtbot.add_widget(keyhint) assert keyhint.text() == '' @@ -161,3 +168,16 @@ def test_blacklist_all(keyhint, config_stub, key_config_stub): keyhint.update_keyhint('normal', 'a') assert not keyhint.text() + + +def test_delay(qtbot, stubs, monkeypatch, keyhint_config, key_config_stub): + timer = stubs.FakeTimer() + monkeypatch.setattr( + 'qutebrowser.misc.keyhintwidget.usertypes.Timer', + lambda *_: timer) + interval = 200 + keyhint_config.set('ui', 'keyhint-delay', interval) + key_config_stub.set_bindings_for('normal', OrderedDict([('aa', 'cmd-aa')])) + keyhint = KeyHintView(0, None) + keyhint.update_keyhint('normal', 'a') + assert timer.interval() == interval diff --git a/tests/unit/misc/test_lineparser.py b/tests/unit/misc/test_lineparser.py index 546d53cf9..0d2b3a52e 100644 --- a/tests/unit/misc/test_lineparser.py +++ b/tests/unit/misc/test_lineparser.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_miscwidgets.py b/tests/unit/misc/test_miscwidgets.py index dd17ac254..03441d9f2 100644 --- a/tests/unit/misc/test_miscwidgets.py +++ b/tests/unit/misc/test_miscwidgets.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_msgbox.py b/tests/unit/misc/test_msgbox.py index 311680266..2c1268bd8 100644 --- a/tests/unit/misc/test_msgbox.py +++ b/tests/unit/misc/test_msgbox.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # This file is part of qutebrowser. diff --git a/tests/unit/misc/test_pastebin.py b/tests/unit/misc/test_pastebin.py index b60b319a6..5591bfd0f 100644 --- a/tests/unit/misc/test_pastebin.py +++ b/tests/unit/misc/test_pastebin.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Anna Kobak (avk) : +# Copyright 2016-2017 Anna Kobak (avk) : # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_readline.py b/tests/unit/misc/test_readline.py index b0c1403a1..6c168774c 100644 --- a/tests/unit/misc/test_readline.py +++ b/tests/unit/misc/test_readline.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_sessions.py b/tests/unit/misc/test_sessions.py index 4bef9b9a4..c30ac6bdc 100644 --- a/tests/unit/misc/test_sessions.py +++ b/tests/unit/misc/test_sessions.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_split.py b/tests/unit/misc/test_split.py index 7b3c8015a..179084849 100644 --- a/tests/unit/misc/test_split.py +++ b/tests/unit/misc/test_split.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_split_hypothesis.py b/tests/unit/misc/test_split_hypothesis.py index 52eb8cb11..c63ddff09 100644 --- a/tests/unit/misc/test_split_hypothesis.py +++ b/tests/unit/misc/test_split_hypothesis.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/misc/test_utilcmds.py b/tests/unit/misc/test_utilcmds.py index 41c2f1f15..e4b686e31 100644 --- a/tests/unit/misc/test_utilcmds.py +++ b/tests/unit/misc/test_utilcmds.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/scripts/test_check_coverage.py b/tests/unit/scripts/test_check_coverage.py index 70f429aa2..8b80d6e44 100644 --- a/tests/unit/scripts/test_check_coverage.py +++ b/tests/unit/scripts/test_check_coverage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # @@ -185,11 +185,12 @@ def test_skipped_windows(covtest, monkeypatch): def _generate_files(): """Get filenames from WHITELISTED_/PERFECT_FILES.""" - yield from iter(check_coverage.WHITELISTED_FILES) + for src_file in check_coverage.WHITELISTED_FILES: + yield os.path.join('qutebrowser', src_file) for test_file, src_file in check_coverage.PERFECT_FILES: if test_file is not None: yield test_file - yield src_file + yield os.path.join('qutebrowser', src_file) @pytest.mark.parametrize('filename', list(_generate_files())) diff --git a/tests/unit/scripts/test_run_vulture.py b/tests/unit/scripts/test_run_vulture.py index 91f7a0744..c19d70b29 100644 --- a/tests/unit/scripts/test_run_vulture.py +++ b/tests/unit/scripts/test_run_vulture.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # This file is part of qutebrowser. # diff --git a/tests/unit/test_app.py b/tests/unit/test_app.py index ecb3ec46e..080399ecc 100644 --- a/tests/unit/test_app.py +++ b/tests/unit/test_app.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/overflow_test_cases.py b/tests/unit/utils/overflow_test_cases.py index 308e5f9a3..450522d6b 100644 --- a/tests/unit/utils/overflow_test_cases.py +++ b/tests/unit/utils/overflow_test_cases.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/test_debug.py b/tests/unit/utils/test_debug.py index 04a2fa436..76c84fee1 100644 --- a/tests/unit/utils/test_debug.py +++ b/tests/unit/utils/test_debug.py @@ -1,4 +1,4 @@ -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # @@ -138,6 +138,7 @@ class TestQEnumKey: (QFrame, QFrame.Sunken, None, 'Sunken'), (QFrame, 0x0030, QFrame.Shadow, 'Sunken'), (QFrame, 0x1337, QFrame.Shadow, '0x1337'), + (Qt, Qt.AnchorLeft, None, 'AnchorLeft'), ]) def test_qenum_key(self, base, value, klass, expected): key = debug.qenum_key(base, value, klass=klass) @@ -168,6 +169,8 @@ class TestQFlagsKey: (Qt, Qt.AlignCenter, None, 'AlignHCenter|AlignVCenter'), fixme((Qt, 0x0021, Qt.Alignment, 'AlignLeft|AlignTop')), (Qt, 0x1100, Qt.Alignment, '0x0100|0x1000'), + (Qt, Qt.DockWidgetAreas(0), Qt.DockWidgetArea, 'NoDockWidgetArea'), + (Qt, Qt.DockWidgetAreas(0), None, '0x0000'), ]) def test_qflags_key(self, base, value, klass, expected): flags = debug.qflags_key(base, value, klass=klass) diff --git a/tests/unit/utils/test_error.py b/tests/unit/utils/test_error.py index abcc72c44..4f4905365 100644 --- a/tests/unit/utils/test_error.py +++ b/tests/unit/utils/test_error.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # This file is part of qutebrowser. diff --git a/tests/unit/utils/test_javascript.py b/tests/unit/utils/test_javascript.py index 298a75312..54c32ee92 100644 --- a/tests/unit/utils/test_javascript.py +++ b/tests/unit/utils/test_javascript.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 289b2c26b..5d8798b96 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -1,4 +1,4 @@ -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # diff --git a/tests/unit/utils/test_log.py b/tests/unit/utils/test_log.py index 3c1e82fc0..147e760bb 100644 --- a/tests/unit/utils/test_log.py +++ b/tests/unit/utils/test_log.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/test_qtutils.py b/tests/unit/utils/test_qtutils.py index b2de7b58d..45daa36d8 100644 --- a/tests/unit/utils/test_qtutils.py +++ b/tests/unit/utils/test_qtutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -88,8 +88,9 @@ def test_version_check(monkeypatch, qversion, compiled, version, exact, ('538.1', False), # Qt 5.8 ('602.1', True) # QtWebKit-NG TP5 ]) -def test_is_qtwebkit_ng(version, ng): - assert qtutils.is_qtwebkit_ng(version) == ng +def test_is_qtwebkit_ng(monkeypatch, version, ng): + monkeypatch.setattr(qtutils, 'qWebKitVersion', lambda: version) + assert qtutils.is_qtwebkit_ng() == ng class TestCheckOverflow: @@ -209,48 +210,32 @@ class QtObject: return self._null -@pytest.mark.parametrize('func_name, obj, raising, exc_reason, exc_str', [ - # ensure_valid, good examples - ('ensure_valid', QtObject(valid=True, null=True), False, None, None), - ('ensure_valid', QtObject(valid=True, null=False), False, None, None), - # ensure_valid, bad examples - ('ensure_valid', QtObject(valid=False, null=True), True, None, - ' is not valid'), - ('ensure_valid', QtObject(valid=False, null=False), True, None, - ' is not valid'), - ('ensure_valid', QtObject(valid=False, null=True, error='Test'), True, - 'Test', ' is not valid: Test'), - # ensure_not_null, good examples - ('ensure_not_null', QtObject(valid=True, null=False), False, None, None), - ('ensure_not_null', QtObject(valid=False, null=False), False, None, None), - # ensure_not_null, bad examples - ('ensure_not_null', QtObject(valid=True, null=True), True, None, - ' is null'), - ('ensure_not_null', QtObject(valid=False, null=True), True, None, - ' is null'), - ('ensure_not_null', QtObject(valid=False, null=True, error='Test'), True, - 'Test', ' is null: Test'), +@pytest.mark.parametrize('obj, raising, exc_reason, exc_str', [ + # good examples + (QtObject(valid=True, null=True), False, None, None), + (QtObject(valid=True, null=False), False, None, None), + # bad examples + (QtObject(valid=False, null=True), True, None, ' is not valid'), + (QtObject(valid=False, null=False), True, None, ' is not valid'), + (QtObject(valid=False, null=True, error='Test'), True, 'Test', + ' is not valid: Test'), ]) -def test_ensure(func_name, obj, raising, exc_reason, exc_str): - """Test ensure_valid and ensure_not_null. - - The function is parametrized as they do nearly the same. +def test_ensure_valid(obj, raising, exc_reason, exc_str): + """Test ensure_valid. Args: - func_name: The name of the function to call. obj: The object to test with. raising: Whether QtValueError is expected to be raised. exc_reason: The expected .reason attribute of the exception. exc_str: The expected string of the exception. """ - func = getattr(qtutils, func_name) if raising: with pytest.raises(qtutils.QtValueError) as excinfo: - func(obj) + qtutils.ensure_valid(obj) assert excinfo.value.reason == exc_reason assert str(excinfo.value) == exc_str else: - func(obj) + qtutils.ensure_valid(obj) @pytest.mark.parametrize('status, raising, message', [ diff --git a/tests/unit/utils/test_standarddir.py b/tests/unit/utils/test_standarddir.py index 69acd2884..bc7a58975 100644 --- a/tests/unit/utils/test_standarddir.py +++ b/tests/unit/utils/test_standarddir.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/test_typing.py b/tests/unit/utils/test_typing.py index fc507299c..4a3359a45 100644 --- a/tests/unit/utils/test_typing.py +++ b/tests/unit/utils/test_typing.py @@ -1,4 +1,4 @@ -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: # diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py index f944f95b2..74c244b7b 100644 --- a/tests/unit/utils/test_urlutils.py +++ b/tests/unit/utils/test_urlutils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -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:'), @@ -739,6 +742,31 @@ def test_data_url(): assert url == QUrl('data:text/plain;base64,Zm9v') +@pytest.mark.parametrize('url, expected', [ + # No IDN + (QUrl('http://www.example.com'), 'http://www.example.com'), + # IDN in domain + (QUrl('http://www.ä.com'), '(www.xn--4ca.com) http://www.ä.com'), + # IDN with non-whitelisted TLD + (QUrl('http://www.ä.foo'), 'http://www.xn--4ca.foo'), + # Unicode only in path + (QUrl('http://www.example.com/ä'), 'http://www.example.com/ä'), + # Unicode only in TLD (looks like Qt shows Punycode with рф...) + (QUrl('http://www.example.xn--p1ai'), + '(www.example.xn--p1ai) http://www.example.рф'), + # https://bugreports.qt.io/browse/QTBUG-60364 + (QUrl('http://www.xn--80ak6aa92e.com'), + '(unparseable URL!) http://www.аррӏе.com'), +]) +def test_safe_display_string(url, expected): + assert urlutils.safe_display_string(url) == expected + + +def test_safe_display_string_invalid(): + with pytest.raises(urlutils.InvalidUrlError): + urlutils.safe_display_string(QUrl()) + + class TestProxyFromUrl: @pytest.mark.parametrize('url, expected', [ @@ -762,7 +790,7 @@ class TestProxyFromUrl: assert urlutils.proxy_from_url(QUrl(url)) == expected @pytest.mark.parametrize('scheme', ['pac+http', 'pac+https']) - def test_proxy_from_url_pac(self, scheme): + def test_proxy_from_url_pac(self, scheme, qapp): fetcher = urlutils.proxy_from_url(QUrl('{}://foo'.format(scheme))) assert isinstance(fetcher, pac.PACFetcher) diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py index 12330aa73..7eb4008a6 100644 --- a/tests/unit/utils/test_utils.py +++ b/tests/unit/utils/test_utils.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -836,6 +836,11 @@ class TestGetSetClipboard: with pytest.raises(utils.SelectionUnsupportedError): utils.get_clipboard(selection=True) + def test_get_unsupported_selection_fallback(self, clipboard_mock): + clipboard_mock.supportsSelection.return_value = False + clipboard_mock.text.return_value = 'text' + assert utils.get_clipboard(selection=True, fallback=True) == 'text' + @pytest.mark.parametrize('selection', [True, False]) def test_get_fake_clipboard(self, selection): utils.fake_clipboard = 'fake clipboard text' @@ -847,6 +852,10 @@ class TestGetSetClipboard: clipboard_mock.supportsSelection.return_value = selection assert utils.supports_selection() == selection + def test_fallback_without_selection(self): + with pytest.raises(ValueError): + utils.get_clipboard(fallback=True) + @pytest.mark.parametrize('keystr, expected', [ ('', True), @@ -916,3 +925,16 @@ class TestOpenFile: def test_unused(): utils.unused(None) + + +@pytest.mark.parametrize('path, expected', [ + ('E:', 'E:\\'), + ('e:', 'e:\\'), + ('E:foo', 'E:foo'), + ('E:\\', 'E:\\'), + ('E:\\foo', 'E:\\foo'), + ('foo:', 'foo:'), + ('foo:bar', 'foo:bar'), +]) +def test_expand_windows_drive(path, expected): + assert utils.expand_windows_drive(path) == expected diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index c6e629ef1..96f4bf88f 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # @@ -725,13 +725,13 @@ def test_version_output(git_commit, frozen, style, with_webkit, stubs, patches['objects.backend'] = usertypes.Backend.QtWebKit patches['QWebEngineProfile'] = None if with_webkit == 'ng': - patches['qtutils.is_qtwebkit_ng'] = lambda v: True + patches['qtutils.is_qtwebkit_ng'] = lambda: True substitutions['backend'] = 'QtWebKit-NG (WebKit WEBKIT VERSION)' else: - patches['qtutils.is_qtwebkit_ng'] = lambda v: False + patches['qtutils.is_qtwebkit_ng'] = lambda: False substitutions['backend'] = 'QtWebKit (WebKit WEBKIT VERSION)' else: - patches['qWebKitVersion'] = None + monkeypatch.delattr(version, 'qtutils.qWebKitVersion', raising=False) patches['objects.backend'] = usertypes.Backend.QtWebEngine patches['QWebEngineProfile'] = FakeWebEngineProfile substitutions['backend'] = 'QtWebEngine (Chromium CHROMIUMVERSION)' diff --git a/tests/unit/utils/usertypes/test_enum.py b/tests/unit/utils/usertypes/test_enum.py index b78251171..a5d2f33e5 100644 --- a/tests/unit/utils/usertypes/test_enum.py +++ b/tests/unit/utils/usertypes/test_enum.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/usertypes/test_misc.py b/tests/unit/utils/usertypes/test_misc.py index c2c0738e5..d21816ace 100644 --- a/tests/unit/utils/usertypes/test_misc.py +++ b/tests/unit/utils/usertypes/test_misc.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2016 Florian Bruhin (The Compiler) +# Copyright 2016-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/usertypes/test_neighborlist.py b/tests/unit/utils/usertypes/test_neighborlist.py index 7bfbe3e2c..751f940a3 100644 --- a/tests/unit/utils/usertypes/test_neighborlist.py +++ b/tests/unit/utils/usertypes/test_neighborlist.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2014-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/usertypes/test_question.py b/tests/unit/utils/usertypes/test_question.py index 119d4ee3f..031e304cc 100644 --- a/tests/unit/utils/usertypes/test_question.py +++ b/tests/unit/utils/usertypes/test_question.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tests/unit/utils/usertypes/test_timer.py b/tests/unit/utils/usertypes/test_timer.py index e48bd332b..d120d82e6 100644 --- a/tests/unit/utils/usertypes/test_timer.py +++ b/tests/unit/utils/usertypes/test_timer.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2015-2016 Florian Bruhin (The Compiler) +# Copyright 2015-2017 Florian Bruhin (The Compiler) # # This file is part of qutebrowser. # diff --git a/tox.ini b/tox.ini index 731a07831..29e38f8fd 100644 --- a/tox.ini +++ b/tox.ini @@ -97,18 +97,18 @@ setenv = passenv = {[testenv]passenv} deps = {[testenv]deps} - PyQt5==5.8.1.1 + PyQt5==5.8.2 commands = {envpython} -bb -m pytest {posargs:tests} [testenv:py36-pyqt58] -basepython = python3.6 +basepython = {env:PYTHON:python3.6} setenv = {[testenv]setenv} QUTE_BDD_WEBENGINE=true passenv = {[testenv]passenv} deps = {[testenv]deps} - PyQt5==5.8.1.1 + PyQt5==5.8.2 commands = {envpython} -bb -m pytest {posargs:tests} # other envs @@ -148,6 +148,16 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/misc/requirements/requirements-pyqt.txt +# This is used for Windows, since binary name is different +[testenv:mkvenv-win-pypi] +basepython = python.exe +commands = {envpython} -c "" +envdir = {toxinidir}/.venv +usedevelop = true +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/misc/requirements/requirements-pyqt.txt + [testenv:unittests-frozen] # cx_Freeze doesn't support Python 3.5 yet basepython = python3.4 diff --git a/www/header.asciidoc b/www/header.asciidoc index dc80a5d00..ff5b26d9e 100644 --- a/www/header.asciidoc +++ b/www/header.asciidoc @@ -17,4 +17,8 @@ Releases Blog +
+qutebrowser is currently running a crowdfunding campaign for its new configuration system, allowing for per-domain settings and much more. +See the Kickstarter campaign for more information! +
+++ diff --git a/www/qute.css b/www/qute.css index 2de633b18..9a47b8122 100644 --- a/www/qute.css +++ b/www/qute.css @@ -53,6 +53,12 @@ p { color: #666666; } +#crowdfunding { + padding: 10px 10px; + background-color: #a6dfff; + margin-bottom: 10px; +} + #menu { padding: 0px 20px; background-color: #555555;