1

I have read this question Is it possible to inject a javascript code that OVERRIDES the one existing in a DOM? and the solution works well most of time, but sometimes, seems JavaScript code in page will run first and then content_scripts.

This will make the 'OVVERRIDES' failed sometimes.

My test code as below:

//manifest.json
"content_scripts": [
{
    "matches": ["http://*/*"],
    "js": ["js_injector.js"],
    "run_at": "document_start"
}

//js_injector.js
function injectJs(link) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=link;

document.documentElement.appendChild(scr);
}
injectJs(chrome.extension.getURL('my.js'));

//my.js
console.log("in my.js");
function foo() {
    console.log("in my.js foo");
}

//test.html (view this page in chrome)
<html>
<script type="text/javascript">
    console.log("I am here 1110");
    foo()
    console.log("I am here 1111");
</script>
</html>

Usually, I will get below logs:

in my.js my.js:114
I am here 1110 test.html:4
in my.js foo my.js:116
I am here 1111 test.html:6

But sometimes, below logs will be got:

I am here 1110 test.html:4
Uncaught ReferenceError: foo is not defined test.html:5
in my.js test.js:114

Seems the order to run code in pages and content scripts are random? And my purpose is let target pages run js apis that defines in content scripts. Does someone know how to fix it? Thanks!

Community
  • 1
  • 1
Hui.Li
  • 399
  • 4
  • 18

2 Answers2

0

I found My injected <script> runs after the target-page's javascript, despite using run_at: document_start? which has pointed out the reason and gave a work-around solution.

But it is really difficult to write js code into a string like that. So I wrote a python code to read and generate them.

import sys

s_injector_header = """
function injectJs(text) {
    var scr = document.createElement('script');
    scr.type="text/javascript";
    scr.textContent=text;
    document.documentElement.appendChild(scr);
}

"""

def openInjector():
    fd = open(sys.path[0]+"/js_injector.js","w")
    fd.write(s_injector_header)
    return fd

def closeInjector(fd):
    fd.close()

def injectJs(fd,filename):
    jsfd = open(sys.path[0]+"/"+filename,"r")
    text = jsfd.read()
    text = text.replace("\\","\\\\")
    text = text.replace("'","\\'")
    text = text.replace('"','\\"')
    text = text.replace('\n','\\n')
    fd.write("injectJs('"+text+"');\n\n")

def main():
    fd = openInjector()
    injectJs(fd,"md5.js")
    injectJs(fd,"dessrc.js")
    injectJs(fd,"my_code.js")
    closeInjector(fd)

if __name__ == '__main__':
    main()
Community
  • 1
  • 1
Hui.Li
  • 399
  • 4
  • 18
0

The order of execution is certainly not random.

What your function injectJS is doing is creating a tag with a "src" field of my.js. Then injecting that into your HTML. Which means my.js is included asynchronously.

Here is what test.html sees:

<script src="my.js"></script>


Because of it being asynchronous, the contents of my.js are not always loaded when you want. To mitigate this issue, you do not want to include my.js, you want to include the contents of my.js.

Using Broswerify's brfs, you can put a require() statement into your browser side code and have it inject the actual file contents. After the right installs, your js_injector.js would need the following:

//js_injector.js

var fs = require('fs');

function injectJs(link) {
    //Create script tag with full file content as the content
    var scr = document.createElement('script');
    link = fs.readFileSync(__dirname + '/inject.js', 'utf-8');
    scr.textContent = link;

    (document.head || document.documentElement).appendChild(src);
}

injectJs('my.js');

Which would give you the following and the behavior you expect:

//test.html

<script> 
    console.log("in my.js");
    function foo() {
        console.log("in my.js foo");
    }
</script>
Dawson B
  • 1,224
  • 12
  • 16