summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/scriptlets/dom-survey-scripts.js
blob: 30ef66e46b96f1656ae8887562b360de0c6dd69b (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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*******************************************************************************

    uBlock Origin - a comprehensive, efficient content blocker
    Copyright (C) 2015-present Raymond Hill

    This program 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.

    This program 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 this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

/******************************************************************************/

// Scriptlets to count the number of script tags in a document.

(( ) => {
    if ( typeof vAPI !== 'object' ) { return; }

    const t0 = Date.now();

    if ( vAPI.domSurveyScripts instanceof Object === false ) {
        vAPI.domSurveyScripts = {
            busy: false,
            scriptCount: -1,
            surveyTime: t0,
        };
    }
    const surveyResults = vAPI.domSurveyScripts;

    if ( surveyResults.busy ) { return; }
    surveyResults.busy = true;

    if ( surveyResults.surveyTime < vAPI.domMutationTime ) {
        surveyResults.scriptCount = -1;
    }
    surveyResults.surveyTime = t0;

    if ( surveyResults.scriptCount === -1 ) {
        const reInlineScript = /^(data:|blob:|$)/;
        let inlineScriptCount = 0;
        let scriptCount = 0;
        for ( const script of document.scripts ) {
            if ( reInlineScript.test(script.src) ) {
                inlineScriptCount = 1;
                continue;
            }
            scriptCount += 1;
            if ( scriptCount === 99 ) { break; }
        }
        scriptCount += inlineScriptCount;
        if ( scriptCount !== 0 ) {
            surveyResults.scriptCount = scriptCount;
        }
    }

    // https://github.com/uBlockOrigin/uBlock-issues/issues/756
    //   Keep trying to find inline script-like instances but only if we
    //   have the time-budget to do so.
    if ( surveyResults.scriptCount === -1 ) {
        if ( document.querySelector('a[href^="javascript:"]') !== null ) {
            surveyResults.scriptCount = 1;
        }
    }

    // https://github.com/uBlockOrigin/uBlock-issues/issues/1756
    //   Mind that there might be no body element.
    if ( surveyResults.scriptCount === -1 && document.body !== null ) {
        surveyResults.scriptCount = 0;
        const onHandlers = new Set([
            'onabort', 'onblur', 'oncancel', 'oncanplay',
            'oncanplaythrough', 'onchange', 'onclick', 'onclose',
            'oncontextmenu', 'oncuechange', 'ondblclick', 'ondrag',
            'ondragend', 'ondragenter', 'ondragexit', 'ondragleave',
            'ondragover', 'ondragstart', 'ondrop', 'ondurationchange',
            'onemptied', 'onended', 'onerror', 'onfocus',
            'oninput', 'oninvalid', 'onkeydown', 'onkeypress',
            'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata',
            'onloadstart', 'onmousedown', 'onmouseenter', 'onmouseleave',
            'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup',
            'onwheel', 'onpause', 'onplay', 'onplaying',
            'onprogress', 'onratechange', 'onreset', 'onresize',
            'onscroll', 'onseeked', 'onseeking', 'onselect',
            'onshow', 'onstalled', 'onsubmit', 'onsuspend',
            'ontimeupdate', 'ontoggle', 'onvolumechange', 'onwaiting',
            'onafterprint', 'onbeforeprint', 'onbeforeunload', 'onhashchange',
            'onlanguagechange', 'onmessage', 'onoffline', 'ononline',
            'onpagehide', 'onpageshow', 'onrejectionhandled', 'onpopstate',
            'onstorage', 'onunhandledrejection', 'onunload',
            'oncopy', 'oncut', 'onpaste'
        ]);
        const nodeIter = document.createNodeIterator(
            document.body,
            NodeFilter.SHOW_ELEMENT
        );
        for (;;) {
            const node = nodeIter.nextNode();
            if ( node === null ) { break; }
            if ( node.hasAttributes() === false ) { continue; }
            for ( const attr of node.getAttributeNames() ) {
                if ( onHandlers.has(attr) === false ) { continue; }
                surveyResults.scriptCount = 1;
                break;
            }
        }
    }

    surveyResults.busy = false;

    // IMPORTANT: This is returned to the injector, so this MUST be
    //            the last statement.
    if ( surveyResults.scriptCount !== -1 ) {
        return surveyResults.scriptCount;
    }
})();