-
Notifications
You must be signed in to change notification settings - Fork 20.5k
Description
Description
I'm toying with an extension to chrome, and during this i have encountered issues when loading jQuery for use in content scripts. The content scripts execute in what chrome calls a 'isolated world', i.e., it may see all DOM object, but it is isolated from any scripts that was on the page they are injected into (and vice versa).
Currently i have isolated my problems down to a race condition, that seems(?) to arise due to the fact that my content script (currently only jQuery) is injected into the page before the DOM is fully initialised(?) or something like that.
-
What do you expect to happen?
The jQuery script being injected into the browsed page without any errors, such that it may be referenced from other content scripts. -
What actually happens
I have tried to copy/paste and format the output i get from the chrome console. As noted before, this error only comes about 80% of the times. So if you try to reproduce, make sure to refresh the site a few times. It seems to always work on small pages like google.com, however on 'big' pages like newspapers, twitter, facebook, etc it seems to always fail with the below error:-
jQuery 1.12.4
jquery-1.12.4.js:940 Uncaught TypeError: Cannot read property 'createElement' of undefined assert @ jquery-1.12.4.js:940 (anonymous function) @ jquery-1.12.4.js:2662 (anonymous function) @ jquery-1.12.4.js:2713 (anonymous function) @ jquery-1.12.4.js:34 (anonymous function) @ jquery-1.12.4.js:38 -
jQuery 2.2.4
jquery-2.2.4.js:906 Uncaught TypeError: Cannot read property 'createElement' of undefined assert @ jquery-2.2.4.js:906 (anonymous function) @ jquery-2.2.4.js:2628 (anonymous function) @ jquery-2.2.4.js:2679 (anonymous function) @ jquery-2.2.4.js:34 (anonymous function) @ jquery-2.2.4.js:38 -
jQuery 3.1.1
jquery-3.1.1.js:927 Uncaught TypeError: Cannot read property 'createElement' of undefined assert @ jquery-3.1.1.js:927 (anonymous function) @ jquery-3.1.1.js:2746 (anonymous function) @ jquery-3.1.1.js:2797 (anonymous function) @ jquery-3.1.1.js:36 (anonymous function) @ jquery-3.1.1.js:40
I did some debugging in the 2.2.4 version
From the above errors, it is kinda clear (and i debugged it), that
documentis somehow (sometimes) ending up as undefined.905: function assert( fn ) { 906: var div = document.createElement("div"); 907:
I boiled it down to the internals of Sizzle, where it should set the internal
documentvariable, which is used in the above assert function. However, thissetDocument()function sometimes returns early if "doc is invalid or already selected"2623: // Initialize against the default document 2624: setDocument();
By printing the
documentvariable before and after, i noticed that it is sometimes still undefined after this function, which leads to the above mentioned uncaught type errors.From what i can debug, it all comes down to this
1042: // Return early if doc is invalid or already selected 1043: if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { 1044: return document; 1045: }
Printing
doc,document,doc.nodeTypeanddoc.documentElementto the console, always yielded indocbeing aHTMLDocumentobject,documentbeing undefined, as it hasn't been initialised yet (we are currently doing it),doc.nodeTypeequal to 9,
butdoc.documentElementwas either aHTMLHtmlElement(then obviously it worked), ornulland then obviously it didn't work, as it would 'return early' withdocumentstill being undefined.
-
-
Which browsers are affected?
Currently I have tested this on a Debian machine (Linux evelin 4.6.0-1-amd64 Accumulated, not yet pulled commits #1 SMP Debian 4.6.4-1 (2016-07-18) x86_64 GNU/Linux) with Google Chrome (stable 53.0.2785.116-1). As this is done using a Chrome extension to inject jQuery into the running page i don't really have a means to reproduce this in any other browser.
I don't know if this is a Chrome bug or jQuery, as it seems timing related and that I don't know enough about javascript to say for sure what can be 100% expected to be initialized by the browser before any script is being run. However currently i have fixed it by placing some busy looping code in the top of the jQuery script, to make sure it is delayed just a few fractions of a sec, that it loads without any errors.
Link to test case
You need a empty directory. In here you place this "manifest.json" file:
{
"manifest_version": 2,
"name": "jQuery MWE",
"version": "0.1",
"background": {
"persistent": false,
"scripts": [
"eventPage.js"
]
},
"permissions": [
"declarativeContent",
"<all_urls>"
]
}and you then place this "eventPage.js" file along side (which is responsible for injecting the jQuery file as a content script into the maching web page, which here is any page you browse).
var show_page_action = {
conditions: [
// https://developer.chrome.com/extensions/declarativeContent#type-PageStateMatcher
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
urlMatches: '.*',
//schemes: ['https'],
}
})
],
actions: [
new chrome.declarativeContent.RequestContentScript({
js: ["jquery-2.2.4.js",],
})
],
};
// Make sure our declarativeContent rules are up to date.
chrome.runtime.onInstalled.addListener(function (details) {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([show_page_action])
})
});Then of cause you need the jQuery file (above i have used "jquery-2.2.4.js") and then you just need to load the directory as a chrome extension.
When the extension is loaded, go browse any webpage with the chrome developer tools/console open, and refresh the page a few times until you see the above mention uncaught type error. Most of the times google.com will work just fine as it seems to load fast enough, so try something with a bit more content on.