From b184d2f94dc40f07e1c857348264c0e3dbfcde3a Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 16 Nov 2017 19:22:08 +0600 Subject: [PATCH 001/229] dirty initial port of chrome caretbrowser extension --- qutebrowser/browser/webengine/webenginetab.py | 5 +- qutebrowser/javascript/axs_testing.js | 2643 +++++++++++++++++ qutebrowser/javascript/webengine_caret.js | 2188 ++++++++++++++ qutebrowser/keyinput/modeman.py | 4 - 4 files changed, 4835 insertions(+), 5 deletions(-) create mode 100644 qutebrowser/javascript/axs_testing.js create mode 100644 qutebrowser/javascript/webengine_caret.js diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 7339cd422..3a779f480 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -179,7 +179,8 @@ class WebEngineCaret(browsertab.AbstractCaret): @pyqtSlot(usertypes.KeyMode) def _on_mode_entered(self, mode): - pass + js_code = javascript.assemble('caret', 'setInitialCursor') + self._tab.run_js_async(js_code) @pyqtSlot(usertypes.KeyMode) def _on_mode_left(self): @@ -547,6 +548,8 @@ class WebEngineTab(browsertab.AbstractTab): 'window._qutebrowser = {};', utils.read_file('javascript/scroll.js'), utils.read_file('javascript/webelem.js'), + utils.read_file('javascript/webengine_caret.js'), + utils.read_file('javascript/axs_testing.js'), ]) script = QWebEngineScript() script.setInjectionPoint(QWebEngineScript.DocumentCreation) diff --git a/qutebrowser/javascript/axs_testing.js b/qutebrowser/javascript/axs_testing.js new file mode 100644 index 000000000..e6b1a3755 --- /dev/null +++ b/qutebrowser/javascript/axs_testing.js @@ -0,0 +1,2643 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Generated from http://github.com/GoogleChrome/accessibility-developer-tools/tree/7d778f7da58af341a47b3a6f6457c2842b24d4d8 + * + * See project README for build steps. + */ + +// AUTO-GENERATED CONTENT BELOW: DO NOT EDIT! See above for details. + +var fn = (function() { + var COMPILED = !0, goog = goog || {}; +goog.global = this; +goog.isDef = function(a) { + return void 0 !== a; +}; +goog.exportPath_ = function(a, b, c) { + a = a.split("."); + c = c || goog.global; + a[0] in c || !c.execScript || c.execScript("var " + a[0]); + for (var d;a.length && (d = a.shift());) { + !a.length && goog.isDef(b) ? c[d] = b : c = c[d] ? c[d] : c[d] = {}; + } +}; +goog.define = function(a, b) { + var c = b; + COMPILED || (goog.global.CLOSURE_UNCOMPILED_DEFINES && Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_UNCOMPILED_DEFINES, a) ? c = goog.global.CLOSURE_UNCOMPILED_DEFINES[a] : goog.global.CLOSURE_DEFINES && Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES, a) && (c = goog.global.CLOSURE_DEFINES[a])); + goog.exportPath_(a, c); +}; +goog.DEBUG = !0; +goog.LOCALE = "en"; +goog.TRUSTED_SITE = !0; +goog.STRICT_MODE_COMPATIBLE = !1; +goog.DISALLOW_TEST_ONLY_CODE = COMPILED && !goog.DEBUG; +goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = !1; +goog.provide = function(a) { + if (goog.isInModuleLoader_()) { + throw Error("goog.provide can not be used within a goog.module."); + } + if (!COMPILED && goog.isProvided_(a)) { + throw Error('Namespace "' + a + '" already declared.'); + } + goog.constructNamespace_(a); +}; +goog.constructNamespace_ = function(a, b) { + if (!COMPILED) { + delete goog.implicitNamespaces_[a]; + for (var c = a;(c = c.substring(0, c.lastIndexOf("."))) && !goog.getObjectByName(c);) { + goog.implicitNamespaces_[c] = !0; + } + } + goog.exportPath_(a, b); +}; +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; +goog.module = function(a) { + if (!goog.isString(a) || !a || -1 == a.search(goog.VALID_MODULE_RE_)) { + throw Error("Invalid module identifier"); + } + if (!goog.isInModuleLoader_()) { + throw Error("Module " + a + " has been loaded incorrectly."); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error("goog.module may only be called once per module."); + } + goog.moduleLoaderState_.moduleName = a; + if (!COMPILED) { + if (goog.isProvided_(a)) { + throw Error('Namespace "' + a + '" already declared.'); + } + delete goog.implicitNamespaces_[a]; + } +}; +goog.module.get = function(a) { + return goog.module.getInternal_(a); +}; +goog.module.getInternal_ = function(a) { + if (!COMPILED) { + return goog.isProvided_(a) ? a in goog.loadedModules_ ? goog.loadedModules_[a] : goog.getObjectByName(a) : null; + } +}; +goog.moduleLoaderState_ = null; +goog.isInModuleLoader_ = function() { + return null != goog.moduleLoaderState_; +}; +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw Error("goog.module.declareLegacyNamespace must be called from within a goog.module"); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error("goog.module must be called prior to goog.module.declareLegacyNamespace."); + } + goog.moduleLoaderState_.declareLegacyNamespace = !0; +}; +goog.setTestOnly = function(a) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + throw a = a || "", Error("Importing test-only code into non-debug environment" + (a ? ": " + a : ".")); + } +}; +goog.forwardDeclare = function(a) { +}; +COMPILED || (goog.isProvided_ = function(a) { + return a in goog.loadedModules_ || !goog.implicitNamespaces_[a] && goog.isDefAndNotNull(goog.getObjectByName(a)); +}, goog.implicitNamespaces_ = {"goog.module":!0}); +goog.getObjectByName = function(a, b) { + for (var c = a.split("."), d = b || goog.global, e;e = c.shift();) { + if (goog.isDefAndNotNull(d[e])) { + d = d[e]; + } else { + return null; + } + } + return d; +}; +goog.globalize = function(a, b) { + var c = b || goog.global, d; + for (d in a) { + c[d] = a[d]; + } +}; +goog.addDependency = function(a, b, c, d) { + if (goog.DEPENDENCIES_ENABLED) { + var e; + a = a.replace(/\\/g, "/"); + var f = goog.dependencies_; + d && "boolean" !== typeof d || (d = d ? {module:"goog"} : {}); + for (var g = 0;e = b[g];g++) { + f.nameToPath[e] = a, f.loadFlags[a] = d; + } + for (d = 0;b = c[d];d++) { + a in f.requires || (f.requires[a] = {}), f.requires[a][b] = !0; + } + } +}; +goog.ENABLE_DEBUG_LOADER = !0; +goog.logToConsole_ = function(a) { + goog.global.console && goog.global.console.error(a); +}; +goog.require = function(a) { + if (!COMPILED) { + goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_ && goog.maybeProcessDeferredDep_(a); + if (goog.isProvided_(a)) { + return goog.isInModuleLoader_() ? goog.module.getInternal_(a) : null; + } + if (goog.ENABLE_DEBUG_LOADER) { + var b = goog.getPathFromDeps_(a); + if (b) { + return goog.writeScripts_(b), null; + } + } + a = "goog.require could not find: " + a; + goog.logToConsole_(a); + throw Error(a); + } +}; +goog.basePath = ""; +goog.nullFunction = function() { +}; +goog.abstractMethod = function() { + throw Error("unimplemented abstract method"); +}; +goog.addSingletonGetter = function(a) { + a.getInstance = function() { + if (a.instance_) { + return a.instance_; + } + goog.DEBUG && (goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = a); + return a.instance_ = new a; + }; +}; +goog.instantiatedSingletons_ = []; +goog.LOAD_MODULE_USING_EVAL = !0; +goog.SEAL_MODULE_EXPORTS = goog.DEBUG; +goog.loadedModules_ = {}; +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; +goog.ALWAYS_TRANSPILE = !1; +goog.NEVER_TRANSPILE = !1; +goog.DEPENDENCIES_ENABLED && (goog.dependencies_ = {loadFlags:{}, nameToPath:{}, requires:{}, visited:{}, written:{}, deferred:{}}, goog.inHtmlDocument_ = function() { + var a = goog.global.document; + return null != a && "write" in a; +}, goog.findBasePath_ = function() { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + } else { + if (goog.inHtmlDocument_()) { + for (var a = goog.global.document.getElementsByTagName("SCRIPT"), b = a.length - 1;0 <= b;--b) { + var c = a[b].src, d = c.lastIndexOf("?"), d = -1 == d ? c.length : d; + if ("base.js" == c.substr(d - 7, 7)) { + goog.basePath = c.substr(0, d - 7); + break; + } + } + } + } +}, goog.importScript_ = function(a, b) { + (goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_)(a, b) && (goog.dependencies_.written[a] = !0); +}, goog.IS_OLD_IE_ = !(goog.global.atob || !goog.global.document || !goog.global.document.all), goog.importProcessedScript_ = function(a, b, c) { + goog.importScript_("", 'goog.retrieveAndExec_("' + a + '", ' + b + ", " + c + ");"); +}, goog.queuedModules_ = [], goog.wrapModule_ = function(a, b) { + return goog.LOAD_MODULE_USING_EVAL && goog.isDef(goog.global.JSON) ? "goog.loadModule(" + goog.global.JSON.stringify(b + "\n//# sourceURL=" + a + "\n") + ");" : 'goog.loadModule(function(exports) {"use strict";' + b + "\n;return exports});\n//# sourceURL=" + a + "\n"; +}, goog.loadQueuedModules_ = function() { + var a = goog.queuedModules_.length; + if (0 < a) { + var b = goog.queuedModules_; + goog.queuedModules_ = []; + for (var c = 0;c < a;c++) { + goog.maybeProcessDeferredPath_(b[c]); + } + } +}, goog.maybeProcessDeferredDep_ = function(a) { + goog.isDeferredModule_(a) && goog.allDepsAreAvailable_(a) && (a = goog.getPathFromDeps_(a), goog.maybeProcessDeferredPath_(goog.basePath + a)); +}, goog.isDeferredModule_ = function(a) { + var b = (a = goog.getPathFromDeps_(a)) && goog.dependencies_.loadFlags[a] || {}; + return a && ("goog" == b.module || goog.needsTranspile_(b.lang)) ? goog.basePath + a in goog.dependencies_.deferred : !1; +}, goog.allDepsAreAvailable_ = function(a) { + if ((a = goog.getPathFromDeps_(a)) && a in goog.dependencies_.requires) { + for (var b in goog.dependencies_.requires[a]) { + if (!goog.isProvided_(b) && !goog.isDeferredModule_(b)) { + return !1; + } + } + } + return !0; +}, goog.maybeProcessDeferredPath_ = function(a) { + if (a in goog.dependencies_.deferred) { + var b = goog.dependencies_.deferred[a]; + delete goog.dependencies_.deferred[a]; + goog.globalEval(b); + } +}, goog.loadModuleFromUrl = function(a) { + goog.retrieveAndExec_(a, !0, !1); +}, goog.loadModule = function(a) { + var b = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = {moduleName:void 0, declareLegacyNamespace:!1}; + var c; + if (goog.isFunction(a)) { + c = a.call(goog.global, {}); + } else { + if (goog.isString(a)) { + c = goog.loadModuleFromSource_.call(goog.global, a); + } else { + throw Error("Invalid module definition"); + } + } + var d = goog.moduleLoaderState_.moduleName; + if (!goog.isString(d) || !d) { + throw Error('Invalid module name "' + d + '"'); + } + goog.moduleLoaderState_.declareLegacyNamespace ? goog.constructNamespace_(d, c) : goog.SEAL_MODULE_EXPORTS && Object.seal && Object.seal(c); + goog.loadedModules_[d] = c; + } finally { + goog.moduleLoaderState_ = b; + } +}, goog.loadModuleFromSource_ = function(a) { + eval(a); + return {}; +}, goog.writeScriptSrcNode_ = function(a) { + goog.global.document.write(' + + diff --git a/tests/end2end/data/hints/iframe_button.html b/tests/end2end/data/hints/iframe_button.html new file mode 100644 index 000000000..cd7bf203c --- /dev/null +++ b/tests/end2end/data/hints/iframe_button.html @@ -0,0 +1,11 @@ + + + + + + Hinting inside an iframe + + + + + diff --git a/tests/end2end/data/hints/iframe_input.html b/tests/end2end/data/hints/iframe_input.html new file mode 100644 index 000000000..143577747 --- /dev/null +++ b/tests/end2end/data/hints/iframe_input.html @@ -0,0 +1,11 @@ + + + + + + Hinting inside an iframe + + + + + diff --git a/tests/end2end/data/iframe_search.html b/tests/end2end/data/iframe_search.html new file mode 100644 index 000000000..a06c0ef3e --- /dev/null +++ b/tests/end2end/data/iframe_search.html @@ -0,0 +1,11 @@ + + + + + + Hinting inside an iframe + + + + + diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index 0f7868e31..f575d0423 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -209,6 +209,19 @@ Feature: Using hints And I hint with args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged + Scenario: Using :follow-hint inside an iframe button + When I open data/hints/iframe_button.html + And I hint with args "all normal" and follow s + Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged + + Scenario: Hinting inputs in an iframe without type + When I open data/hints/iframe_input.html + And I hint with args "inputs" and follow a + And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log + And I run :leave-mode + # The actual check is already done above + Then no crash should happen + ### FIXME currenly skipped, see https://github.com/qutebrowser/qutebrowser/issues/1525 @xfail_norun Scenario: Using :follow-hint inside a scrolled iframe @@ -218,7 +231,6 @@ Feature: Using hints And I hint wht args "links normal" and follow a Then "navigation request: url http://localhost:*/data/hello2.txt, type NavigationTypeLinkClicked, *" should be logged - @qtwebengine_skip: Opens in new tab due to Chromium bug Scenario: Opening a link inside a specific iframe When I open data/hints/iframe_target.html And I hint with args "links normal" and follow a diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature index 3778f963d..483bcc29f 100644 --- a/tests/end2end/features/search.feature +++ b/tests/end2end/features/search.feature @@ -238,3 +238,22 @@ Feature: Searching on a page Then the following tabs should be open: - data/search.html - data/hello.txt (active) + + Scenario: Follow a searched link in an iframe + When I open data/iframe_search.html + And I run :tab-only + And I run :search follow + And I wait for "search found follow" in the log + And I run :follow-selected + Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, is_main_frame False" should be logged + + Scenario: Follow a tabbed searched link in an iframe + When I open data/iframe_search.html + And I run :tab-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/iframe_search.html + - data/hello.txt (active) From b87f0b6f65d5ca7d05426ee3397fb724e667834a Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Mon, 18 Dec 2017 17:04:50 -0800 Subject: [PATCH 025/229] Add support for non-link buttons to test_hints --- tests/end2end/test_hints_html.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index abc106505..ff4837020 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -106,7 +106,11 @@ def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, quteproc.set_setting('hints.find_implementation', find_implementation) quteproc.send_cmd(':zoom {}'.format(zoom_level)) # follow hint - quteproc.send_cmd(':hint links normal') + if 'button' in test_name: + # We are hinting buttons, link hinting will not work + quteproc.send_cmd(':hint all normal') + else: + quteproc.send_cmd(':hint links normal') quteproc.wait_for(message='hints: a', category='hints') quteproc.send_cmd(':follow-hint a') quteproc.wait_for_load_finished('data/' + parsed.target) From 012e63520f5ba90b9ab8c91754aa160010825954 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Mon, 18 Dec 2017 18:20:25 -0800 Subject: [PATCH 026/229] Blacklist non-implemented qtwebkit frame features --- tests/end2end/features/hints.feature | 3 ++- tests/end2end/features/search.feature | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index f575d0423..5da03a085 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -212,7 +212,7 @@ Feature: Using hints Scenario: Using :follow-hint inside an iframe button When I open data/hints/iframe_button.html And I hint with args "all normal" and follow s - Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, *" should be logged + Then "navigation request: url http://localhost:*/data/hello.txt, *" should be logged Scenario: Hinting inputs in an iframe without type When I open data/hints/iframe_input.html @@ -241,6 +241,7 @@ Feature: Using hints And I run :tab-only And I hint with args "links tab" and follow a And I wait until data/hello.txt is loaded + And I wait 0.5s Then the following tabs should be open: - data/hints/iframe_target.html - data/hello.txt (active) diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature index 483bcc29f..7779ff28e 100644 --- a/tests/end2end/features/search.feature +++ b/tests/end2end/features/search.feature @@ -239,6 +239,7 @@ Feature: Searching on a page - data/search.html - data/hello.txt (active) + @qtwebkit_skip: Not supported in qtwebkit Scenario: Follow a searched link in an iframe When I open data/iframe_search.html And I run :tab-only @@ -247,6 +248,7 @@ Feature: Searching on a page And I run :follow-selected Then "navigation request: url http://localhost:*/data/hello.txt, type NavigationTypeLinkClicked, is_main_frame False" should be logged + @qtwebkit_skip: Not supported in qtwebkit Scenario: Follow a tabbed searched link in an iframe When I open data/iframe_search.html And I run :tab-only From a01566ed15f3f93ff034810e3d74d36d0d3657a1 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 28 Dec 2017 19:17:25 +0600 Subject: [PATCH 027/229] Fix loosed variable --- qutebrowser/browser/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index cb744b23d..5c051a3ff 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -862,6 +862,7 @@ class CommandDispatcher: else: # pragma: no cover raise ValueError("Invalid value {!r} for `what'.".format(what)) + self.yank_object['what'] = what self._yank_to_target(s) def _yank_callback(self, s): From 9728e90401a884a45de7f1bd72441f7ee5a63f37 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 28 Dec 2017 20:05:10 +0600 Subject: [PATCH 028/229] Enable test in webengine --- tests/end2end/features/editor.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/end2end/features/editor.feature b/tests/end2end/features/editor.feature index d3c634b0d..f1b53e90d 100644 --- a/tests/end2end/features/editor.feature +++ b/tests/end2end/features/editor.feature @@ -115,7 +115,6 @@ Feature: Opening external editors And I run :click-element id qute-button Then the javascript message "text: foobar" should be logged - @qtwebengine_todo: Caret mode is not implemented yet Scenario: Spawning an editor in caret mode When I set up a fake editor returning "foobar" And I open data/editor.html From 63658d3a1e618e81687827d68961e7829aa3bd7b Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 28 Dec 2017 20:40:37 +0600 Subject: [PATCH 029/229] Catch userscript exception and show error message --- qutebrowser/browser/commands.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 5c051a3ff..53fb03830 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1247,7 +1247,10 @@ class CommandDispatcher: self.openurl(config.val.url.start_pages[0]) def _selection_callback(self, s): - self._run_userscript(s) + try: + self._run_userscript(s) + except cmdexc.CommandError as e: + message.error(str(e)) def _run_userscript(self, selection): """Run a userscript given as argument. From 83f8d840128248231a98cd3776d4436c4e58cfbf Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 28 Dec 2017 22:27:47 +0600 Subject: [PATCH 030/229] Finally enable webengine test --- tests/end2end/features/editor.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/end2end/features/editor.feature b/tests/end2end/features/editor.feature index 15da4a6cd..d01e3d229 100644 --- a/tests/end2end/features/editor.feature +++ b/tests/end2end/features/editor.feature @@ -129,7 +129,6 @@ Feature: Opening external editors And I kill the waiting editor Then the error "Edited element vanished" should be shown - @qtwebengine_todo: Caret mode is not implemented yet Scenario: Spawning an editor in caret mode When I set up a fake editor returning "foobar" And I open data/editor.html From 7d181ee4b53edb6064778802f427b52fe91004d5 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Thu, 28 Dec 2017 23:04:04 +0600 Subject: [PATCH 031/229] Check if document body exists --- qutebrowser/javascript/webengine_caret.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 7235c9772..af381f529 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -965,7 +965,8 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, window.caretBrowsingLoaded = true; CaretBrowsing.init(); - if (document.body.getAttribute("caretbrowsing") === "on") { + if (document.body && + document.body.getAttribute("caretbrowsing") === "on") { CaretBrowsing.forceEnabled = true; CaretBrowsing.isEnabled = true; CaretBrowsing.updateIsCaretVisible(); From 3b836d34836e90d4cdf7ddbadc91c89c27ea51fa Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Fri, 29 Dec 2017 17:56:16 +0600 Subject: [PATCH 032/229] Fix lint warnings --- qutebrowser/browser/commands.py | 6 ++++-- qutebrowser/javascript/webengine_caret.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index d3f865a52..653d70014 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1293,9 +1293,11 @@ class CommandDispatcher: env['QUTE_URL'] = url.toString(QUrl.FullyEncoded) try: + args = self.userscript_object['args'] + verbose = self.userscript_object['verbose'] userscripts.run_async(tab, self.userscript_object['cmd'], - *self.userscript_object['args'], win_id=self._win_id, - env=env, verbose=self.userscript_object['verbose']) + *args, win_id=self._win_id, + env=env, verbose=verbose) except userscripts.Error as e: raise cmdexc.CommandError(e) finally: diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index af381f529..be0d6eaa5 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -965,7 +965,7 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, window.caretBrowsingLoaded = true; CaretBrowsing.init(); - if (document.body && + if (document.body && document.body.getAttribute("caretbrowsing") === "on") { CaretBrowsing.forceEnabled = true; CaretBrowsing.isEnabled = true; From db16a87e68ca10cac2fcf17e8d4481b7999e7a26 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Fri, 29 Dec 2017 18:39:29 +0600 Subject: [PATCH 033/229] Removed unused import --- qutebrowser/keyinput/modeman.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 719d29863..2c21908de 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -29,7 +29,6 @@ from qutebrowser.keyinput import modeparsers, keyparser from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.utils import usertypes, log, objreg, utils -from qutebrowser.misc import objects @attr.s(frozen=True) From 882beab3f244ef67742fb8285905af880444cf37 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Fri, 29 Dec 2017 19:01:12 +0600 Subject: [PATCH 034/229] Try to fix Windows caret ussues --- qutebrowser/javascript/webengine_caret.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index be0d6eaa5..4c2771451 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -468,6 +468,9 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, CaretBrowsing.blinkFlag = true; + CaretBrowsing.isWindows = + window.navigator.userAgent.indexOf("Windows") !== -1; + CaretBrowsing.isControlThatNeedsArrowKeys = function(node) { // eslint-disable-line complexity,max-len if (!node) { return false; @@ -860,9 +863,15 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, getSelection(). modify(action, direction, granularity); - window.setTimeout(() => { - CaretBrowsing.updateCaretOrSelection(true); - }, 0); + if (CaretBrowsing.isWindows && + (direction === "forward" || + direction === "right")) { + CaretBrowsing.move("left", "character"); + } else { + window.setTimeout(() => { + CaretBrowsing.updateCaretOrSelection(true); + }, 0); + } }; CaretBrowsing.moveToBlock = function(paragraph, boundary) { From d04a087c2bd9132603b704ed0c5edfefe3f43e02 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Fri, 29 Dec 2017 20:10:18 +0600 Subject: [PATCH 035/229] Try fix Windows caret issues 2 --- qutebrowser/javascript/webengine_caret.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 4c2771451..79fb01dcb 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -865,7 +865,8 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, if (CaretBrowsing.isWindows && (direction === "forward" || - direction === "right")) { + direction === "right") && + granularity === "word") { CaretBrowsing.move("left", "character"); } else { window.setTimeout(() => { From e254ea2fa7e617432c656ad7f9055c72af64e2bc Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Fri, 29 Dec 2017 23:43:39 +0600 Subject: [PATCH 036/229] Add license and description --- qutebrowser/javascript/webengine_caret.js | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 79fb01dcb..e7075cd33 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -1,3 +1,28 @@ +/** + * Copyright 2016-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 . + */ + +/** + * Ported chrome-caretbrowsing extension. + * Create and control div caret, which listen commands from qutebrowser, + * change document selection model and div caret position. + */ + "use strict"; // eslint-disable-line max-lines window._qutebrowser.caret = (function() { // eslint-disable-line max-statements,max-len From 25436e2544d8224e3422efb7952180e732de122e Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 09:43:16 +0600 Subject: [PATCH 037/229] Fix eslint warnings --- qutebrowser/javascript/webengine_caret.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index e7075cd33..001afa8a7 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -1,3 +1,4 @@ +// eslint-disable-line max-lines /** * Copyright 2016-2017 Florian Bruhin (The Compiler) * @@ -23,7 +24,7 @@ * change document selection model and div caret position. */ -"use strict"; // eslint-disable-line max-lines +"use strict"; window._qutebrowser.caret = (function() { // eslint-disable-line max-statements,max-len function isElementInViewport(node) { // eslint-disable-line complexity From 981f5fd09b93a194f2e768697e6d260dfa078ece Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 23:20:29 +0600 Subject: [PATCH 038/229] Remove unused import --- tests/end2end/features/test_caret_bdd.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/end2end/features/test_caret_bdd.py b/tests/end2end/features/test_caret_bdd.py index ee6cda82c..abd1821d8 100644 --- a/tests/end2end/features/test_caret_bdd.py +++ b/tests/end2end/features/test_caret_bdd.py @@ -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-import -import pytest import pytest_bdd as bdd from end2end.features.test_yankpaste_bdd import init_fake_clipboard From 6dc31087477e496d3be9b4cf8fc7d6b81adb994b Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 23:35:12 +0600 Subject: [PATCH 039/229] Get OS name using python --- qutebrowser/browser/webengine/webenginetab.py | 3 ++- qutebrowser/javascript/webengine_caret.js | 15 ++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index b816e8f2a..42cbc3d2a 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -21,6 +21,7 @@ import math import functools +import platform import html as html_utils import sip @@ -202,7 +203,7 @@ class WebEngineCaret(browsertab.AbstractCaret): @pyqtSlot(usertypes.KeyMode) def _on_mode_entered(self, mode): self._tab.run_js_async( - javascript.assemble('caret', 'setInitialCursor')) + javascript.assemble('caret', 'setInitialCursor', platform.platform())) @pyqtSlot(usertypes.KeyMode) def _on_mode_left(self): diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 001afa8a7..89eb9969f 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -494,9 +494,6 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, CaretBrowsing.blinkFlag = true; - CaretBrowsing.isWindows = - window.navigator.userAgent.indexOf("Windows") !== -1; - CaretBrowsing.isControlThatNeedsArrowKeys = function(node) { // eslint-disable-line complexity,max-len if (!node) { return false; @@ -590,9 +587,9 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, document.body.appendChild(node); }; - CaretBrowsing.setInitialCursor = function() { - const sel = window.getSelection(); - if (sel.rangeCount > 0) { + CaretBrowsing.setInitialCursor = function(platform) { + CaretBrowsing.isWindows = platform === "Windows"; + if (window.getSelection().rangeCount > 0) { return; } @@ -960,7 +957,7 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, CaretBrowsing.isCaretVisible = (CaretBrowsing.isEnabled && CaretBrowsing.isWindowFocused); if (CaretBrowsing.isCaretVisible && !CaretBrowsing.caretElement) { - CaretBrowsing.setInitialCursor(); + CaretBrowsing.setInitialCursor(CaretBrowsing.isWindows); CaretBrowsing.updateCaretOrSelection(true); if (CaretBrowsing.caretElement) { CaretBrowsing.blinkFunctionId = window.setInterval( @@ -1012,9 +1009,9 @@ window._qutebrowser.caret = (function() { // eslint-disable-line max-statements, const funcs = {}; - funcs.setInitialCursor = () => { + funcs.setInitialCursor = (platform) => { if (!CaretBrowsing.initiated) { - CaretBrowsing.setInitialCursor(); + CaretBrowsing.setInitialCursor(platform); return; } From fe4eb19ecfa43badf9c39cdeebff1ddbe0a38058 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 23:37:17 +0600 Subject: [PATCH 040/229] Add link to chrome-caretbrowsing extension --- qutebrowser/javascript/webengine_caret.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 89eb9969f..9e217fe36 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -20,6 +20,8 @@ /** * Ported chrome-caretbrowsing extension. + * https://cs.chromium.org/chromium/src/ui/accessibility/extensions/caretbrowsing/ + * * Create and control div caret, which listen commands from qutebrowser, * change document selection model and div caret position. */ From e22dc1b5c6cd8720fde7bfd60762988a157c196a Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 23:37:57 +0600 Subject: [PATCH 041/229] Update copyright year --- qutebrowser/javascript/webengine_caret.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/javascript/webengine_caret.js b/qutebrowser/javascript/webengine_caret.js index 9e217fe36..99f6bd76a 100644 --- a/qutebrowser/javascript/webengine_caret.js +++ b/qutebrowser/javascript/webengine_caret.js @@ -1,6 +1,6 @@ // eslint-disable-line max-lines /** - * Copyright 2016-2017 Florian Bruhin (The Compiler) + * Copyright 2018 Florian Bruhin (The Compiler) * * This file is part of qutebrowser. * From 695f7341429b2b4e68b8aeb08051363f0261af82 Mon Sep 17 00:00:00 2001 From: Artur Shaikhullin Date: Sat, 30 Dec 2017 23:46:04 +0600 Subject: [PATCH 042/229] Extract js call method --- qutebrowser/browser/webengine/webenginetab.py | 58 +++++++------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 42cbc3d2a..38a16b323 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -208,87 +208,69 @@ class WebEngineCaret(browsertab.AbstractCaret): @pyqtSlot(usertypes.KeyMode) def _on_mode_left(self): self.drop_selection() - self._tab.run_js_async( - javascript.assemble('caret', 'toggle')) + self._js_call('toggle') def move_to_next_line(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveDown')) + self._js_call('moveDown') def move_to_prev_line(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveUp')) + self._js_call('moveUp') def move_to_next_char(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveRight')) + self._js_call('moveRight') def move_to_prev_char(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveLeft')) + self._js_call('moveLeft') def move_to_end_of_word(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfWord')) + self._js_call('moveToEndOfWord') def move_to_next_word(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToNextWord')) + self._js_call('moveToNextWord') def move_to_prev_word(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToPreviousWord')) + self._js_call('moveToPreviousWord') def move_to_start_of_line(self): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfLine')) + self._js_call('moveToStartOfLine') def move_to_end_of_line(self): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfLine')) + self._js_call('moveToEndOfLine') def move_to_start_of_next_block(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfNextBlock')) + self._js_call('moveToStartOfNextBlock') def move_to_start_of_prev_block(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfPrevBlock')) + self._js_call('moveToStartOfPrevBlock') def move_to_end_of_next_block(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfNextBlock')) + self._js_call('moveToEndOfNextBlock') def move_to_end_of_prev_block(self, count=1): for _ in range(count): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfPrevBlock')) + self._js_call('moveToEndOfPrevBlock') def move_to_start_of_document(self): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToStartOfDocument')) + self._js_call('moveToStartOfDocument') def move_to_end_of_document(self): - self._tab.run_js_async( - javascript.assemble('caret', 'moveToEndOfDocument')) + self._js_call('moveToEndOfDocument') def toggle_selection(self): - self._tab.run_js_async( - javascript.assemble('caret', 'toggleSelection')) + self._js_call('toggleSelection') def drop_selection(self): - self._tab.run_js_async( - javascript.assemble('caret', 'dropSelection')) + self._js_call('dropSelection') def has_selection(self): if qtutils.version_check('5.10'): @@ -351,6 +333,10 @@ class WebEngineCaret(browsertab.AbstractCaret): self._tab.run_js_async(js_code, lambda jsret: self._follow_selected_cb(jsret, tab)) + def _js_call(self, command): + self._tab.run_js_async( + javascript.assemble('caret', command)) + class WebEngineScroller(browsertab.AbstractScroller): From e989d948edd81a7e6afd45592fe6764d35124ae0 Mon Sep 17 00:00:00 2001 From: Slackhead Date: Sun, 31 Dec 2017 14:47:42 +0000 Subject: [PATCH 043/229] Add cycle-inputs.js to userscripts --- misc/userscripts/cycle-inputs.js | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 misc/userscripts/cycle-inputs.js diff --git a/misc/userscripts/cycle-inputs.js b/misc/userscripts/cycle-inputs.js new file mode 100644 index 000000000..c395f6de2 --- /dev/null +++ b/misc/userscripts/cycle-inputs.js @@ -0,0 +1,43 @@ +/* Cycle text boxes. + * works with the types defined in 'types'. + * Note: Does not work for