summaryrefslogtreecommitdiff
path: root/overrides/endless_private/injectable_webview.js
blob: 41772d73066ee0ee3197507ae15f91d168c3b598 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const WebKit2 = imports.gi.WebKit2;

/**
 * Class: InjectableWebview
 * WebKit WebView subclass which provides a clean interface for injecting
 * custom JS and CSS from gresource files.
 *
 * Calling <inject_js_from_resource()> or <inject_css_from_resource()> will
 * ensure that the current page (if any) as well as all subsequently loaded
 * pages in this webview will have the given JS or CSS injected only when the
 * HTML document has finished loading
 *
 * Parent class:
 *     WebKit2.WebView
 */
const InjectableWebview = new Lang.Class({
    Name: 'InjectableWebview',
    GTypeName: 'EknInjectableWebview',
    Extends: WebKit2.WebView,

    _init: function (params) {
        this._injection_handlers = [];
        this.parent(params);
    },

    /**
     * Method: inject_js_from_resource
     * Injects the given JS file into the current page, as well as all
     * subsequent pages. Note that this will be an asynchronous call, and will
     * not block on the injection/execution of the JS. If you want more control
     * over the order in which JS files are injected, or want to inspect
     * returned results, use <load_js_from_gresource> instead.
     *
     * Parameters:
     *     uri - the JS file's "resource://" URI
     */
    inject_js_from_resource: function (js_uri) {
        let js_str = this._read_gresource_file(js_uri);
        this._run_js_on_loaded_page(js_str, WebKit2.LoadEvent.FINISHED);
    },

    /**
     * Method: inject_css_from_resource
     * Injects the given CSS file into the current page, as well as all
     * subsequent pages
     *
     * Parameters:
     *     uri - the CSS file's "resource://" URI
     */
    inject_css_from_resource: function (css_uri) {
        let css_str = this._read_gresource_file(css_uri);

        // generate a javascript string which creates a CSS Style tag whose
        // innerText is the same as the CSS file in the gresource
        let inject_css_script = [
            'var link = document.createElement("style");',
            'link.type = "text/css";',
            'link.rel = "stylesheet";',
            'var css_text = CSS_TEXT',
            'link.innerText = css_text;',
            'document.getElementsByTagName("head")[0].appendChild(link);'
        ].join('\n').replace('CSS_TEXT', css_str.toSource());

        // exec the javascript
        this._run_js_on_loaded_page(inject_css_script, WebKit2.LoadEvent.COMMITTED);
    },

    /**
     * Method: clear_injections
     * Clear all injection handlers from the webview. All future pages will not
     * be affected by any injected documents prior to calling this.
     */
    clear_injections: function () {
        this._injection_handlers.forEach(function (handler) {
            this.disconnect(handler);
        }.bind(this));
        this._injection_handlers = [];
    },

    // just return the string contents of the file provided by the URI
    _read_gresource_file: function (resource_uri) {
        let file = Gio.file_new_for_uri(resource_uri);
        let [success, resource_str] = file.load_contents(null);
        return resource_str.toString();
    },

    // first, if the webview isn't loading something, attempt to run the
    // javascript on the page. Also attach a handler to run the javascript
    // whenever the webview's load-changed indicates it's finished loading
    // something
    _run_js_on_loaded_page: function (script, event) {
        if (this.uri !== null && !this.is_loading) {
            this.run_javascript(script, null, null);
        }
        let handler = this.connect('load-changed', function (webview, status) {
            if (status == event) {
                this.run_javascript(script, null, null);
            }
        }.bind(this));

        this._injection_handlers.push(handler);
    }
});