Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you detect that a script was loaded *and* executed in a chrome extension?

I've been tracking down a bug for days... then I realized the bug was me. :/

I had been using webRequest.onComplete, filtered for scripts. My error was that I made the incorrect association between the scripts being loaded and being executed. The get loaded in a different order than they get executed, and thus the timing of the events is not in the order I need them in. I need to inject between certain scripts so I need an event right after a file has been executed and before the next one.

The only solution I can think of at the moment is to alter the JS being loaded before it gets executed. But it makes my stomach turn. And the bfcache would wreak even more havoc, so not a great solution either.

I would use the HTML5 spec's afterscriptexecute, but that is not implemented in Chrome. Is there another API, perhaps an extension API that I can use?

like image 228
sroussey Avatar asked Aug 19 '13 21:08

sroussey


People also ask

What is Chrome scripting executeScript?

executeScript() Injects a script into a target context. The script is run at document_idle by default. Note: This method is available in Manifest V3 or higher in Chrome and Firefox 101. In Safari and Firefox 102+, this method is also available in Manifest V2.

What is scripting in Chrome?

As the name might suggest, chrome. scripting is a new namespace introduced in Manifest V3 responsible for script and style injection capabilities. Developers that have created Chrome extensions in the past may be familiar with Manifest V2 methods on the Tabs API like chrome. tabs. executeScript and chrome.


1 Answers

Note: This method no longer works as of Chrome 36. There are no direct alternatives.

Note: The answer below only applies to external scripts, i.e. those loaded with <script src>.

In Chrome (and Safari), the "beforeload" event is triggered right before a resource is loaded. This event allows one to block the resource, so that the script is never fetched. In this event, you can determine whether the loaded resource is a script, and check whether you want to perform some action

This event can be used to emulate beforescriptexecute / afterscriptexecute:

document.addEventListener('beforeload', function(event) {
    var target = event.target;
    if (target.nodeName.toUpperCase() !== 'SCRIPT') return;
    var dispatchEvent = function(name, bubbles, cancelable) {
        var evt = new CustomEvent(name, {
            bubbles: bubbles,
            cancelable: cancelable
        });
        target.dispatchEvent(evt);
        if (evt.defaultPrevented) {
            event.preventDefault();
        }
    };
    var onload = function() {
        cleanup();
        dispatchEvent('afterscriptexecute', true, false);
    };
    var cleanup = function() {
        target.removeEventListener('load', onload, true);
        target.removeEventListener('error', cleanup, true);
    }
    target.addEventListener('error', cleanup, true);
    target.addEventListener('load', onload, true);

    dispatchEvent('beforescriptexecute', true, true);
}, true);

The dispatch times are not 100% identical to the original ones, but it is sufficient for most cases. This is the time line for the (non-emulated) events:

beforeload             Before the network request is started
beforescriptexecute    Before a script executes
afterscriptexecute     After a script executes
onload                 After the script has executed

Here's an easy way to see that the events are working as expected:

window.addEventListener('afterscriptexecute', function() {
    alert(window.x);
});
document.head.appendChild(document.createElement('script')).src = 'data:,x=1';
document.head.appendChild(document.createElement('script')).src = 'data:,x=2';

The demo can be seen live at http://jsfiddle.net/sDaZt/

like image 172
Rob W Avatar answered Nov 15 '22 05:11

Rob W