11

Suppose there is a site that includes an external .js file in an html script tag like so:

<script src="somescript.js">

I want greasemonkey to intercept each of such scripts, and alter some of the values in them before they execute. For example, I want to change all occurrences of the value "400" to "300" within them, then continue to load the page as if the scripts used those values instead of the original ones. Currently I'm using the following code in greasemonkey:

function replaceTargetJavascript (scriptNode) {
    var scriptSrc   = scriptNode.textContent;
    scriptSrc       = scriptSrc.replace (
        /'400'/,
        "'300'"
    );

    addJS_Node (scriptSrc);
}

document.addEventListener("beforescriptexecute", function(e) {

    checkForBadJavascripts ( [
        [false, /'400'/, replaceTargetJavascript]
    ] );
}, true);

Which according to my sources is the right way to do it, but it is not working. Can anyone please help me figure this out?

Joey
  • 10,504
  • 16
  • 39
  • 54
  • 1
    That's not how you use [checkForBadJavascripts](https://gist.github.com/2620135). (EG, see [this answer](http://stackoverflow.com/a/11201555/331508).) ... Also you cannot do on-the-fly edits of the *contents* of scripts loaded via ` – Brock Adams Mar 03 '14 at 08:58
  • 1
    Would you be willing to write up how to do what you just described? Pretty please? – Joey Mar 03 '14 at 17:06
  • Cool, I actually solved it... I'll post the solution as an answer soon. – Joey Mar 04 '14 at 01:39
  • @Joey - Did you ever post your solution or find any good notes? – codeaperature Jan 06 '17 at 19:22
  • Just use mutation observer. For example https://stackoverflow.com/questions/17632475/watch-for-element-creation-in-greasemonkey-script – user202729 Nov 30 '19 at 16:30

2 Answers2

23

Old question, but I needed to do this recently. Here's how I did it using GreaseMonkey.

You add the beforescriptexecute listener, and wait for your target script to be loaded, checking the src tag to identify the correct script.

Then you stop that script from loading and get the script source yourself using GM_xmlhttpRequest.

Then you are free to modify the script as you please and insert it back into the DOM.

// ==UserScript==
// @name        Test
// @namespace   Test
// @description TEST
// @include     http://the.website.com/*
// @version     1
// @grant       GM_xmlhttpRequest
// @run-at      document-start
// ==/UserScript==

function addScript(text) {
    text = text.replace(/replaceThis();/g, "");
    var newScript = document.createElement('script');
    newScript.type = "text/javascript";
    newScript.textContent = text;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(newScript);
}

window.addEventListener('beforescriptexecute', function(e) {
    src = e.target.src;
    if (src.search(/script_to_modify\.js/) != -1) {
        e.preventDefault();
        e.stopPropagation();        
        GM_xmlhttpRequest({
            method: "GET",
            url: e.target.src,
            onload: function(response) {
                addScript(response.responseText);
            }
        });
    }
});
Jack Culhane
  • 763
  • 7
  • 11
  • Yep, this is the right way to do it. Almost forgot I ever asked this question! – Joey Apr 29 '17 at 18:23
  • 1
    I know, I always feel obliged to answer old unanswered questions like this that I come across! https://xkcd.com/979/ – Jack Culhane May 02 '17 at 09:11
  • 10
    This is not a standard feature https://developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute – Dainius Mar 15 '19 at 10:05
  • We should be able to do this cross-browser with `MutationObserver`, but that doesn't seem to work with GM for some reason... I gave up :-( – mindplay.dk Nov 18 '22 at 11:54
0

Old question but I needed to do this recently and the accepted answer uses the non-standard-feature beforescriptexecute.

It's basically the same as the accepted answer but using the MutationObserver as comments alluded to, also see this answer for a duplicate question.

new MutationObserver(async (mutations, observer) => {
    let oldScript = mutations
        .flatMap(e => [...e.addedNodes])
        .filter(e => e.tagName == 'SCRIPT')
        .find(e => e.src.match(/old-script.js/))

    if (oldScript) {
        observer.disconnect()
        oldScript.remove()

        let text = await fetch(oldScript.src).then(e => e.text())
            .then(e => e.replace(/hijack-part/g, "profit"))

        let newScript = document.createElement('script')
        newScript.type = 'module' // or text/javascript depending on what you hijack
        newScript.textContent = text
        document.querySelector('head').appendChild(newScript)
    }
}).observe(document, {
    childList: true,
    subtree: true,
})

I'm not 100% sure in what order things happen regarding mutations and script loading/executing though. So I don't know if there are corner cases where the script has already been executed before the observer gets mutation. AFAICT the MutationObserver is only an observer and can't strictly prevent nodes being added, just remove them immediately.

In my case it works though.

Alowaniak
  • 571
  • 3
  • 10