6

I'm developing a chrome extension to make some changes on Github site while the URL postfix matches the source code type (for example, xxx.cpp)

I expect the content.js will run every time the tab finishes loading the DOM elements. However, it only runs when I press the refresh button. If I click the hyperlinks and go to another URL, content.js won't run.

What did I do wrong? Thanks.

Following are the code.

manifest.json

{
    "name": "My Chrome Ext",
    "description": "change appearance of source code on Github",
    "version": "0.1",
    "manifest_version": 3,
    "permissions": ["storage", "activeTab"],
    "action": {},
    "content_scripts": [
        {
            "matches": ["*://github.com/*cpp"],
            "all_frames": true,
            "js": ["content.js"],
            "run_at": "document_end"
        }
    ]
}

content.js

window.alert("start running content.js");
Qiao
  • 452
  • 5
  • 15
  • Remove `cpp` from `matches` and use MutationObserver to detect changes, [more info](https://stackoverflow.com/a/39508954). – wOxxOm Apr 20 '22 at 08:43

1 Answers1

6

The Chrome extension content script will only load when a completely new webpage matching the URL specified in your manifest is loaded. In this era of single page web applications, many websites, including GitHub, use Javascript frameworks and Ajax calls to only update parts of the existing webpage content as the user navigates around the site. Even though the address bar is being updated, most of the time no actual page loads are being executed, so your chrome extension won't trigger.

This improves the performance of the website, but it does mean that browser extensions can't always depend on their content scripts being loaded even if the browser is displaying a matching URL.

My solution? Enable the extension for the entire site and the gate the content script functionality behind a MutationObserver that checks window.location.href every time the callback is called:

function addLocationObserver(callback) {

  // Options for the observer (which mutations to observe)
  const config = { attributes: false, childList: true, subtree: false }

  // Create an observer instance linked to the callback function
  const observer = new MutationObserver(callback)

  // Start observing the target node for configured mutations
  observer.observe(document.body, config)
}

function observerCallback() {

  if (window.location.href.startsWith('https://github.com')) {
    initContentScript()
  }
}

addLocationObserver(observerCallback)
observerCallback()

I also invoke the callback immediately the content script is called to ensure the script triggers correctly on a page reload. You might have to play with the observer configuration to ensure the observer triggers the callback at all the right times.

Note, the target node for the observer be one that remains in the document unless the entire page is refreshed. If it's overwritten, you will lose the observer and your script won't be triggered. In my case the document.body works just fine.

Mike
  • 76
  • 3
  • 3
    I'm having a hard time figuring out where this goes. Does this go in a background script or content-script? Would you be able to share an example in context of a full chrome extension (manifest, content-script, etc)? – benny Aug 19 '22 at 16:16
  • This code would go in the content script. You would also need to adjust your manifest to use wildcards instead of specific URLs. – Brad Johnson Jan 08 '23 at 21:49