Modularize javascript code
We now load the JS code as a QWebEngineScript, which sets up window._qutebrowser with various "modules". That means we don't have to pass the whole module every time we want to execute something.
This commit is contained in:
parent
00673ef7da
commit
6b7a39685e
@ -33,7 +33,7 @@ from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
|
||||
|
||||
from qutebrowser.browser import browsertab
|
||||
from qutebrowser.browser.webengine import webview, webengineelem
|
||||
from qutebrowser.utils import usertypes, qtutils, log, javascript
|
||||
from qutebrowser.utils import usertypes, qtutils, log, javascript, utils
|
||||
|
||||
|
||||
class WebEnginePrinting(browsertab.AbstractPrinting):
|
||||
@ -191,11 +191,9 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
||||
super()._init_widget(widget)
|
||||
page = widget.page()
|
||||
try:
|
||||
page.scrollPositionChanged.connect(
|
||||
self._on_scroll_pos_changed)
|
||||
page.scrollPositionChanged.connect(self._update_pos)
|
||||
except AttributeError:
|
||||
log.stub('scrollPositionChanged, on Qt < 5.7')
|
||||
self._on_scroll_pos_changed()
|
||||
|
||||
def _key_press(self, key, count=1):
|
||||
# FIXME:qtwebengine Abort scrolling if the minimum/maximum was reached.
|
||||
@ -209,9 +207,9 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
||||
QApplication.postEvent(recipient, release_evt)
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_scroll_pos_changed(self):
|
||||
def _update_pos(self):
|
||||
"""Update the scroll position attributes when it changed."""
|
||||
def update_scroll_pos(jsret):
|
||||
def update_pos_cb(jsret):
|
||||
"""Callback after getting scroll position via JS."""
|
||||
if jsret is None:
|
||||
# This can happen when the callback would get called after
|
||||
@ -222,8 +220,8 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
||||
self._pos_px = QPoint(jsret['px']['x'], jsret['px']['y'])
|
||||
self.perc_changed.emit(*self._pos_perc)
|
||||
|
||||
js_code = javascript.assemble('scroll', 'scroll_pos')
|
||||
self._tab.run_js_async(js_code, update_scroll_pos)
|
||||
js_code = javascript.assemble('scroll', 'pos')
|
||||
self._tab.run_js_async(js_code, update_pos_cb)
|
||||
|
||||
def pos_px(self):
|
||||
return self._pos_px
|
||||
@ -232,7 +230,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
||||
return self._pos_perc
|
||||
|
||||
def to_perc(self, x=None, y=None):
|
||||
js_code = javascript.assemble('scroll', 'scroll_to_perc', x, y)
|
||||
js_code = javascript.assemble('scroll', 'to_perc', x, y)
|
||||
self._tab.run_js_async(js_code)
|
||||
|
||||
def to_point(self, point):
|
||||
@ -243,7 +241,7 @@ class WebEngineScroller(browsertab.AbstractScroller):
|
||||
self._tab.run_js_async("window.scrollBy({x}, {y});".format(x=x, y=y))
|
||||
|
||||
def delta_page(self, x=0, y=0):
|
||||
js_code = javascript.assemble('scroll', 'scroll_delta_page', x, y)
|
||||
js_code = javascript.assemble('scroll', 'delta_page', x, y)
|
||||
self._tab.run_js_async(js_code)
|
||||
|
||||
def up(self, count=1):
|
||||
@ -334,6 +332,31 @@ class WebEngineTab(browsertab.AbstractTab):
|
||||
self._set_widget(widget)
|
||||
self._connect_signals()
|
||||
self.backend = usertypes.Backend.QtWebEngine
|
||||
# init js stuff
|
||||
self._init_js()
|
||||
|
||||
def _init_js(self):
|
||||
js_code = '\n'.join([
|
||||
'"use strict";',
|
||||
'window._qutebrowser = {};',
|
||||
utils.read_file('javascript/scroll.js'),
|
||||
utils.read_file('javascript/webelem.js'),
|
||||
])
|
||||
script = QWebEngineScript()
|
||||
script.setInjectionPoint(QWebEngineScript.DocumentCreation)
|
||||
page = self._widget.page()
|
||||
script.setSourceCode(js_code)
|
||||
|
||||
try:
|
||||
page.runJavaScript("", QWebEngineScript.ApplicationWorld)
|
||||
except TypeError:
|
||||
# We're unable to pass a world to runJavaScript
|
||||
script.setWorldId(QWebEngineScript.MainWorld)
|
||||
else:
|
||||
script.setWorldId(QWebEngineScript.ApplicationWorld)
|
||||
|
||||
# FIXME:qtwebengine what about runsOnSubFrames?
|
||||
page.scripts().insert(script)
|
||||
|
||||
def openurl(self, url):
|
||||
self._openurl_prepare(url)
|
||||
@ -427,7 +450,7 @@ class WebEngineTab(browsertab.AbstractTab):
|
||||
callback(elems)
|
||||
|
||||
def find_all_elements(self, selector, callback, *, only_visible=False):
|
||||
js_code = javascript.assemble('webelem', 'find_all_elements', selector)
|
||||
js_code = javascript.assemble('webelem', 'find_all', selector)
|
||||
js_cb = functools.partial(self._find_all_elements_js_cb, callback)
|
||||
self.run_js_async(js_code, js_cb)
|
||||
|
||||
|
@ -33,7 +33,3 @@ rules:
|
||||
no-undefined: "off"
|
||||
wrap-iife: ["error", "inside"]
|
||||
func-names: "off"
|
||||
|
||||
# FIXME turn these on again after using a _qutebrowser object
|
||||
no-unused-vars: "off"
|
||||
no-implicit-globals: "off"
|
||||
|
@ -19,51 +19,57 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
function _qutebrowser_scroll_to_perc(x, y) {
|
||||
var elem = document.documentElement;
|
||||
var x_px = window.scrollX;
|
||||
var y_px = window.scrollY;
|
||||
window._qutebrowser.scroll = (function() {
|
||||
var funcs = {};
|
||||
|
||||
if (x !== undefined) {
|
||||
x_px = (elem.scrollWidth - elem.clientWidth) / 100 * x;
|
||||
}
|
||||
funcs.to_perc = function(x, y) {
|
||||
var elem = document.documentElement;
|
||||
var x_px = window.scrollX;
|
||||
var y_px = window.scrollY;
|
||||
|
||||
if (y !== undefined) {
|
||||
y_px = (elem.scrollHeight - elem.clientHeight) / 100 * y;
|
||||
}
|
||||
if (x !== undefined) {
|
||||
x_px = (elem.scrollWidth - elem.clientWidth) / 100 * x;
|
||||
}
|
||||
|
||||
window.scroll(x_px, y_px);
|
||||
}
|
||||
if (y !== undefined) {
|
||||
y_px = (elem.scrollHeight - elem.clientHeight) / 100 * y;
|
||||
}
|
||||
|
||||
function _qutebrowser_scroll_delta_page(x, y) {
|
||||
var dx = document.documentElement.clientWidth * x;
|
||||
var dy = document.documentElement.clientHeight * y;
|
||||
window.scrollBy(dx, dy);
|
||||
}
|
||||
|
||||
function _qutebrowser_scroll_pos() {
|
||||
var elem = document.documentElement;
|
||||
var dx = elem.scrollWidth - elem.clientWidth;
|
||||
var dy = elem.scrollHeight - elem.clientHeight;
|
||||
var perc_x, perc_y;
|
||||
|
||||
if (dx === 0) {
|
||||
perc_x = 0;
|
||||
} else {
|
||||
perc_x = 100 / dx * window.scrollX;
|
||||
}
|
||||
|
||||
if (dy === 0) {
|
||||
perc_y = 0;
|
||||
} else {
|
||||
perc_y = 100 / dy * window.scrollY;
|
||||
}
|
||||
|
||||
var pos = {
|
||||
"perc": {"x": perc_x, "y": perc_y},
|
||||
"px": {"x": window.scrollX, "y": window.scrollY},
|
||||
window.scroll(x_px, y_px);
|
||||
};
|
||||
|
||||
// console.log(JSON.stringify(pos));
|
||||
return pos;
|
||||
}
|
||||
funcs.delta_page = function(x, y) {
|
||||
var dx = document.documentElement.clientWidth * x;
|
||||
var dy = document.documentElement.clientHeight * y;
|
||||
window.scrollBy(dx, dy);
|
||||
};
|
||||
|
||||
funcs.pos = function() {
|
||||
var elem = document.documentElement;
|
||||
var dx = elem.scrollWidth - elem.clientWidth;
|
||||
var dy = elem.scrollHeight - elem.clientHeight;
|
||||
var perc_x, perc_y;
|
||||
|
||||
if (dx === 0) {
|
||||
perc_x = 0;
|
||||
} else {
|
||||
perc_x = 100 / dx * window.scrollX;
|
||||
}
|
||||
|
||||
if (dy === 0) {
|
||||
perc_y = 0;
|
||||
} else {
|
||||
perc_y = 100 / dy * window.scrollY;
|
||||
}
|
||||
|
||||
var pos = {
|
||||
"perc": {"x": perc_x, "y": perc_y},
|
||||
"px": {"x": window.scrollX, "y": window.scrollY},
|
||||
};
|
||||
|
||||
// console.log(JSON.stringify(pos));
|
||||
return pos;
|
||||
};
|
||||
|
||||
return funcs;
|
||||
})();
|
||||
|
@ -19,60 +19,62 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
document._qutebrowser_elements = [];
|
||||
window._qutebrowser.webelem = (function() {
|
||||
var funcs = {};
|
||||
var elements = [];
|
||||
|
||||
function serialize_elem(elem, id) {
|
||||
var out = {
|
||||
"id": id,
|
||||
"text": elem.text,
|
||||
"tag_name": elem.tagName,
|
||||
"outer_xml": elem.outerHTML,
|
||||
};
|
||||
|
||||
function _qutebrowser_serialize_elem(elem, id) {
|
||||
var out = {
|
||||
"id": id,
|
||||
"text": elem.text,
|
||||
"tag_name": elem.tagName,
|
||||
"outer_xml": elem.outerHTML,
|
||||
var attributes = {};
|
||||
for (var i = 0; i < elem.attributes.length; ++i) {
|
||||
var attr = elem.attributes[i];
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
out.attributes = attributes;
|
||||
|
||||
// console.log(JSON.stringify(out));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
funcs.find_all = function(selector) {
|
||||
var elems = document.querySelectorAll(selector);
|
||||
var out = [];
|
||||
var id = elements.length;
|
||||
|
||||
for (var i = 0; i < elems.length; ++i) {
|
||||
var elem = elems[i];
|
||||
out.push(serialize_elem(elem, id));
|
||||
elements[id] = elem;
|
||||
id++;
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
var attributes = {};
|
||||
for (var i = 0; i < elem.attributes.length; ++i) {
|
||||
var attr = elem.attributes[i];
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
out.attributes = attributes;
|
||||
funcs.focus_element = function() {
|
||||
var elem = document.activeElement;
|
||||
|
||||
// console.log(JSON.stringify(out));
|
||||
if (!elem || elem === document.body) {
|
||||
// "When there is no selection, the active element is the page's
|
||||
// <body> or null."
|
||||
return null;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
var id = elements.length;
|
||||
return serialize_elem(elem, id);
|
||||
};
|
||||
|
||||
|
||||
function _qutebrowser_find_all_elements(selector) {
|
||||
var elems = document.querySelectorAll(selector);
|
||||
var out = [];
|
||||
var id = document._qutebrowser_elements.length;
|
||||
funcs.get_element = function(id) {
|
||||
return elements[id];
|
||||
};
|
||||
|
||||
for (var i = 0; i < elems.length; ++i) {
|
||||
var elem = elems[i];
|
||||
out.push(_qutebrowser_serialize_elem(elem, id));
|
||||
document._qutebrowser_elements[id] = elem;
|
||||
id++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
function _qutebrowser_focus_element() {
|
||||
var elem = document.activeElement;
|
||||
|
||||
if (!elem || elem === document.body) {
|
||||
// "When there is no selection, the active element is the page's <body>
|
||||
// or null."
|
||||
return null;
|
||||
}
|
||||
|
||||
var id = document._qutebrowser_elements.length;
|
||||
return _qutebrowser_serialize_elem(elem, id);
|
||||
}
|
||||
|
||||
|
||||
function _qutebrowser_get_element(id) {
|
||||
return document._qutebrowser_elements[id];
|
||||
}
|
||||
return funcs;
|
||||
})();
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
"""Utilities related to javascript interaction."""
|
||||
|
||||
import textwrap
|
||||
|
||||
from qutebrowser.utils import utils
|
||||
|
||||
@ -62,10 +63,13 @@ def _convert_js_arg(arg):
|
||||
arg, type(arg).__name__))
|
||||
|
||||
|
||||
def assemble(name, function, *args):
|
||||
def assemble(module, function, *args):
|
||||
"""Assemble a javascript file and a function call."""
|
||||
code = "{code}\n_qutebrowser_{function}({args});".format(
|
||||
code=utils.read_file('javascript/{}.js'.format(name)),
|
||||
code = textwrap.dedent("""
|
||||
"use strict";
|
||||
window._qutebrowser.{module}.{function}({args});
|
||||
""").format(
|
||||
module=module,
|
||||
function=function,
|
||||
args=', '.join(_convert_js_arg(arg) for arg in args),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user