Skip to content
Permalink
Browse files
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.
  • Loading branch information
The-Compiler committed Aug 9, 2016
1 parent 00673ef commit 6b7a39685e43a8728eaf24567b407b0f625cff7a
@@ -33,7 +33,7 @@

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 @@ def _init_widget(self, widget):
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 @@ def _key_press(self, key, count=1):
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 @@ def update_scroll_pos(jsret):
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 @@ def pos_perc(self):
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 @@ def delta(self, x=0, y=0):
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 @@ def __init__(self, win_id, mode_manager, parent=None):
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 @@ def _find_all_elements_js_cb(self, callback, js_elems):
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);
}
window.scroll(x_px, y_px);
};

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;

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 (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;
}

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},
};

var pos = {
"perc": {"x": perc_x, "y": perc_y},
"px": {"x": window.scrollX, "y": window.scrollY},
// console.log(JSON.stringify(pos));
return pos;
};

// console.log(JSON.stringify(pos));
return pos;
}
return funcs;
})();
@@ -19,60 +19,62 @@

"use strict";

document._qutebrowser_elements = [];


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;
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,
};

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;
}
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;

function _qutebrowser_find_all_elements(selector) {
var elems = document.querySelectorAll(selector);
var out = [];
var id = document._qutebrowser_elements.length;
for (var i = 0; i < elems.length; ++i) {
var elem = elems[i];
out.push(serialize_elem(elem, id));
elements[id] = elem;
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;
}
return out;
};

funcs.focus_element = function() {
var elem = document.activeElement;

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;
}

if (!elem || elem === document.body) {
// "When there is no selection, the active element is the page's <body>
// or null."
return null;
}
var id = elements.length;
return serialize_elem(elem, id);
};

var id = document._qutebrowser_elements.length;
return _qutebrowser_serialize_elem(elem, id);
}

funcs.get_element = function(id) {
return elements[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),
)

0 comments on commit 6b7a396

Please sign in to comment.