Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome content scripts aren't working: DOMContentLoaded listener does not execute

I am trying to code extension that corrects misspellings on 1 forum.

I am trying to access <p> tag, with content script, but it doesn't change anything (using the code below):

document.addEventListener("DOMContentLoaded", function() {
    document.getElementsByTagName("P")[4].innerHTML = "correct_word"; 
}); 

It doesn't change anything when added as an extension, apparently if I wget the page, and put the script there, all works. Any thoughts?

My manifest.json file:

{
    "manifest_version": 2,
    "name": "Extension",
    "description": "Description",
    "version": "1.0",
    "content_scripts": [{
        "run_at": "document_end",
        "matches": ["http://example.com/"],
        "js": ["script.js"]
    }],
    "web_accessible_resources": ["Filedeleted(really).html"]
}

I know content scripts and WWW pages have different sandboxes, maybe content script can't access page (and tag)?

like image 435
J morris Avatar asked Apr 05 '17 13:04

J morris


People also ask

What does DOMContentLoaded mean?

The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. A different event, load , should be used only to detect a fully-loaded page.

What happens after DOMContentLoaded?

The DOMContentLoaded event fires when the HTML document has been completely parsed, and all deferred scripts ( <script defer src="…"> and <script type="module"> ) have downloaded and executed. It doesn't wait for other things like images, subframes, and async scripts to finish loading.

What is the difference between DOMContentLoaded and load?

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets and images. This is in contrast to DOMContentLoaded , which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.


1 Answers

You are injecting your script after the event you are listening for fires (in this case, DOMContentLoaded). Thus, any code that you have in the listener will not be executed, because the event never fires after you have added your listener.

In Chrome extensions and Firefox WebExtensions, when specifying a time for a content script to be injected, you can specify "document_start", "document_end", or "document_idle".1 In a manifest.json this is the value stated for the run_at property. For tabs.executeScript(), it is the runAt property.

  • document_start
    The injection takes place prior to the DOM being created, or scripts from the page being run. This means that document.body and document.head do not yet exist. The DOMContentLoaded and window load events have not yet fired. You can add things to the DOM by adding them to the document.documentElement. You may need to use a MutationObserver to watch for the elements you are interested in being added to the DOM, or wait for an event like DOMContentLoaded to indicate that the DOM is available.
  • document_end (default)
    The injection takes place after the DOM is complete, but before subresources (e.g. images and frames) are loaded. This is usually after DOMContentLoaded has fired, but before the window load event fires.
  • document_idle
    The injection takes place sometime after document_end and immediately after the window load event fires. The answer to "When does a run_at: document_idle content script run?" indicates that this is the earlier of:

    • After the window load event fires, or
    • 200ms after the DOMContentLoaded event fired.

    This means that your content script will be injected after DOMContentLoaded has fired, but the window load event may, or may not, have already fired.

When listening for DOMContentLoaded, or window load, you should check document.readyState first

Any time you use a DOMContentLoaded listener, or a window load listener, you should always check the document.readyState prior to adding the listener to make sure that you are adding the listener prior to the DOMContentLoaded event being fired (or prior to the load event being fired, if that is what you are listening for). This should be normal habit when you want to listen for these events. If you add the listener after the event has fired, the listener will never be run.

For adding a DOMContentLoaded listener, you should use something like:

if(document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded',afterDOMLoaded);
} else {
    afterDOMLoaded();
}

function afterDOMLoaded(){
    //Everything that needs to happen after the DOM has initially loaded.
}

For adding a window load listener, you could use something like:

if(document.readyState !== 'complete') {
    window.addEventListener('load',afterWindowLoaded);
} else {
    afterWindowLoaded();
}

function afterWindowLoaded(){
    //Everything that needs to happen after the window is fully loaded.
}

  1. If you are using tabs.executeScript(), the value that you provide for runAt only indicates the earliest you want the script to be injected. If you are executing tabs.executeScript() prior to that time, then the injection is delayed until the specified time. Note that for document_start the point when executing tabs.executeScript() will be valid for a new page is a complex topic, which deserves its own question/answer.
  2. Portions of this answer were copied from my answer to "Detect and handle a button click in the active HTML page".
like image 187
Makyen Avatar answered Sep 19 '22 10:09

Makyen