declarativeContent.RequestContentScript sometimes inject content script before document.documentElement is defined.
Reported by
jesper.r...@gmail.com,
Sep 28 2016
|
||
Issue description
Chrome Version : 53.0.2785.116
OS Version: Linux 4.6.0-1-amd64 #1 SMP Debian 4.6.4-1 (2016-07-18) x86_64 GNU/Linux
URLs (if applicable) :
Other browsers tested:
Add OK or FAIL after other browsers where you have tested this issue:
Safari 5: Not testet
Firefox 4.x: Not testet
IE 7/8/9: Note test
What steps will reproduce the problem?
1. Create a simple extension that uses declarativeContent to inject any version of jQuery, as the only content script, into any page you like.
2. Reload that page a few times, and at some point you should see an uncaught exception. It is usually faster to get the exception on a 'fat' page like facebook or twitter, google.com seems to load fast enough that it works 90% of the times.
What is the expected result?
No unhandled exception. The jQuery library should load flawlessly.
What happens instead of that?
On some occasions, it spits out an unhandled exception. The exception is due to the fact that there is no document.documentElement at the point in time that the script is executed.
Please provide any additional information below. Attach a screenshot if
possible.
I have debugged this quite a lot, and it can be simplified to a simple content script (instead of the jQuery library) that just does:
console.log("document: " + document + " documentElement: " + document.documentElement);
With the extension loaded, refresh the page a few times until the output shows that document.documentElement is null (see the two attached images, never mind the name of "delay.js", in the image both delay.js and jquery-2.2.4.js is loaded which also shows the error from jQuery).
As far as i can see from this, then the content script is
This seems to be a race condition, that sometimes executes the script before the page is is being "created", and thus the document.documentElement is still null. In the jQuery case, this is quite bad, as jQuery expects to be executed in an environment where document.documentElement exists.
I would as minimum have expected the RequestContentScript to be injected at a place in time that was equivalent to `run_at: document_start` for the content_scripts field in the manifest.json file (or the runAt field in the details argument to chrome.tabs.executeScript)
> In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.
So document.documentElement should be set, as at least some of the DOM is created.
Ideally one could specify the same as run_at, as an argument to declarativeContent.RequestContentScript.
I know that the API docs says that RequestContentScript is not in stable, and I have seen from other bug reports that you guys have chosen to let RequestContentScript stay in stable and still keep the warning on the API page, such that you may change the API if neede at some point.
Just so you don't shoot this down, because I'm testing this in stable.
Again I want to stress that testing this on google.com, is usually not that fruitful. I was just lucky that it failed quite fast on my attached photos. twitter.com is usually a great page to test against (or just any other page with lots of content, like a newspaper).
My MWE extension consists of the following three files:
manifest.json:
{
"manifest_version": 2,
"name": "MWE",
"version": "0.1",
"background": {
"persistent": false,
"scripts": [
"eventPage.js"
]
},
"permissions": [
"declarativeContent",
"<all_urls>"
]
}
script.js:
console.log("document: " + document + " documentElement: " + document.documentElement + " URL: " + document.URL);
eventPage.js:
var show_page_action = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: {
urlMatches: '.*',
//schemes: ['https'],
}
})
],
actions: [
new chrome.declarativeContent.RequestContentScript({
js: ["script.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])
})
});
UserAgentString: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
,
Oct 1 2016
Correction: it's easy to reproduce only when devtools (console) is open, otherwise it takes several/many tries of clicking links and going back/forward. An extension that shows an alert when documentElement is absent is attached.
,
Oct 5 2016
Perhaps I should have mentioned that it is easier to reproduce when jumping around to different pages, as you also noticed, I just thought it would complicate the things. Nice work around with the MutationObserver. I was porting it all back to not using declarativContent to inject my content scripts. But this lets me keep using it, and then hope this will be fixed(?) in the future.
,
Oct 26 2017
Issue has not been modified or commented on in the last 365 days, please re-open or file a new bug if this is still an issue. For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot |
||
►
Sign in to add a comment |
||
Comment 1 by woxxom@gmail.com
, Oct 1 2016Interesting problem. The easiest way to reproduce is to navigate back/forward between sites. I also found a workaround using MutationObserver: wrap jQuery code in a named function, inject it as the first script, call it in your main content script when documentElement appears. It takes 50ms here for documentElement to appear. =================================== jquery-2.2.4.js: =================================== function jQueryInitWrapper() { ......... the entire jQuery code ......... } =================================== script.js =================================== if (document.documentElement) { jQueryInitWrapper(); onJQueryLoaded(); } else { console.log('waiting for documentElement...'); new MutationObserver(function() { console.log('documentElement added'); this.disconnect(); jQueryInitWrapper(); onJQueryLoaded(); }).observe(document, {childList: true}); } function onJQueryLoaded() { $(function() { alert('jQuery says DOM is ready!'); }); } =================================== eventPage.js: =================================== ................... actions: [ new chrome.declarativeContent.RequestContentScript({ js: ["jquery-2.2.4.js", "script.js"], }) ], ...................