Repository navigation
undefined document variable during initialisation of Sizzle when injected as content script of a Chrome extension #3333
Description
Activity
Thank you for opening an issue! However, your test case requires a lot of work on our end just to get to the point where we are debugging the issue. I think this needs to be fleshed out more before we can continue. It's very odd that
documentis undefined. That should be available even before the DOM is parsed. In other words, if you find out more about the issue and can confirm that there is something we can change in jQuery to fix it, we can reopen this ticket.@timmywil, Sure, you are right. It requires more than clicking a jsfiddle link, as you are probably used to.
But what do you want/expect from me, that I haven't already supplied you with?To be honest, I find i quite rude to just close the ticket, before giving me a chance to reply to your comment and/or before concluding that nothing new will be brought to the table.
I could perhaps check the files into a repository, but checking that out or copy/pasting the two snippets into their own files is almost equally easy for you or anyone else to do.
Putting the files into a directory, and loading the directory in Chrome as an extension can be done in 1-2 minutes.My knowledge of jQuery internals is quite limited. I could come up with quite a few things, but I would have no idea what consequences they would have.
I assume there must be a reason why all that elaborate testing is done in thesetDocumentfunction.
And a reason why the internaldocumentvariable isn't just initialised to be equal topreferredDoc, since this seems to be the desired side effect of callingsetDocumentwithout anodeargument` on line 2624.In all circumstances, I would say that it at least deserves a proper error response, instead of returning an undefined
documentvariable, which throws an exception further down the execution stack, as it assumes everything went fine on line 1044. In fact it seems that thedocumentvariable should never be able to be undefined.However even if one somehow changed the code to not erroneously "return early" in the case where we are trying to initialise against the default document. Then, as far as I can see, the undefined
document.documentElementis saved in the local variabledocElemjust below, which is used further down.
So i guess the only proper solution would be to somehow wait for thedocument.documentElementto come into existence? I guess it is bound to get around at some point, as even an empty page always seems to get some minimal html with an empty head and empty body.
The case where one would have to wait, for thedocument.documentElementto be populated by the browser, would not hurt anyone, as in theory there is "nothing" else in existence.@reenberg I had no intention of being rude. Maybe there really is nothing more you can provide, but closing a ticket does not mean it can't be reopened. It is a tool for triaging to narrow the tickets we can focus on and get fixed.
setDocumentin Sizzle has been used for a while, and I don't see anything obviously wrong with it. If thedocumentvariable isundefined, I would normally say we don't support environments that don't have a window with a document. Sizzle is only the first place that would throw an error when there's not a document, so many more changes would be required to work the way you're describing. And we can't make those changes without pinpointing the exact problem.I hope that's a better explanation. Thank you again for contributing. Even if we don't pinpoint the problem immediately, having this issue here for posterity is worthwhile.
You have to differentiate between which
documentvariable there is in play.Inside Sizzel (is that the right term to use?), there is defined a local
documentvariable at line 572, which (as I understand from the code) is updated through thesetDocumentfunction so that sizzle may work in different contexts.
The globaldocumentvariable (window.document) is defined, and it is even saved aspreferredDocat line 582.The issue at hand is that the global
document.documentElement(window.document.documentElement) is sometimes not present when the script is run.
This makes sense if Chrome injects the content scripts before the DOM has been initialized(again not sure if I'm using the right terms?). I believe this is the case as I can see (if I drop to the debugger at this stage) that the browser window/tab is all empty.I would argue that I have already pinpointed the exact issue:
setDocument()is called whenwindow.document.documentElementis undefined, resulting in it not initialising the localdocumentvariable because it "returns early", resulting in an uncaught TypeError later, as the code tries to do adocument.createElement("div");inside theassertfunction on the undefined localdocumentvariable.What can be argued is whether my reasoning for it happening is correct, and of cause whether this is a bug in Chrome or in jQuery.
I don't know which guarantees a script may expect, in regards to its execution environment.If one are always to expect that there is a global
document.documentElementwhen a script is being executed, then this is clearly a Chrome bug. But if this is not defined anywhere, then i would say that jQuery should handle this.No matter which of the above, I would still say that jQuery should fail more gracefully if the internal
documentis undefined. That would help in the future if anyone is ending up in the same kind of state as I.
I found #15014 in your old bugtracker(?) and i believe that he is actually experiencing the exact same problem as me: The internaldocumentvariable is undefined, most likely because of the same reason as im experiencing it (though i don't know anything about the ChromeEmbeddedFramework). Note that i don't agree in his solution at all (it is in fact wrong as far as i can see) or in any of his reasoning. I just linked it to underline that I'm most likely not the only one who has ended up with this issue, and thus jQuery ought to at least fail gracefully telling me thatdocument.documentElementis undefined.Perhaps it would make sense to do such a test, after the call to
setDocument()on line 2625, as at that point jQuery/Sizzle will fall apart if execution is continued on an undefined localdocumentvariable.
And as this is an initialisation, that should only be run once(?) and thus not a performance hit to anyone.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.
This isn't very firm information to use when asking for a change. In all the environments we formally support there is a
document. Anecdotally I know that others are using jQuery in Chrome extensions, I have never used it there myself. Perhaps you can research this and find out why their uses succeed and this one fails?Perhaps you can research this and find out why their uses succeed and this one fails?
I also know that many other people are using jQuery in their Chrome extensions. But I'm quite sure it is because they are not using the
chrome.declarativeContent.RequestContentScriptto inject the content script, but the old way of injecting by either specifying it in the manifest.json file or by programmatic injection throughchrome.tabs.executeScriptin the backend script/event handler.The
RequestContentScriptis quite new, and in the documentation it is marked as not being available in the stable release of chrome. Though my tests showed that it actually is available in the stable branch (as starting of this year or somewhere around that). As far as i can get from their internal bug tracker, they haven't removed those remarks from the API documentation since the API haven't been officially vetted or something, and as such they may decide to change it in the future once it actually gets looked through. If that is ever going to happen (their own bug reports on the matter dates 1-2 years back).Again, it is technically not the
documentthat is undefined, it is thedocument.documentElement, that has not yet been set.I guess it is fair enough to say that you only formally support environments where this is not undefined at the moment where jQuery is loaded. However I'm still missing a hard evidence that says that
document.documentElementmust exists when javascripts are executed, before I can go and report it as a bug to Chrome, else it would just be a feature request (I assume).Since you do have the option of injection the content scripts at various stages of the page loading, if done by the manifest.json file via the
run_atspecifyer: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.
In the case of "document_end", the files are injected immediately after the DOM is complete, but before subresources like images and frames have loaded.
In any case, @dmethvin, you don't address my point whether or not jQuery should fail more gracefully in this case where it is run in a non formally supported environment?
In any case, @dmethvin, you don't address my point whether or not jQuery should fail more gracefully in this case where it is run in a non formally supported environment?
I can address it by saying that it is very difficult to anticipate all the things that might go wrong when the code is used in untested environments. Doing it in the dark as a thought experiment isn't going to work that well. As said elsewhere, once you "solve" this by trying to get past the lack of
documenthere, it's likely you'll run into the same problem somewhere else in Sizzle or jQuery.I'm not suggesting to try to solve issue of a missing document. By failing gracefully i mean throwing an exception, with a proper error message describing the lack of a
document.documentElement, instead of just letting it keep executing in a bad state, just to have it throw an exception elsewhere that makes no sense as to what the original problem was.There are all sorts of assumptions the code makes about the state of the environment it is running within. Those assumptions are met by the supported environments. It's a waste of code to put in a bunch of checks and error messages for those when they are never supposed to happen. The reason the exception makes no sense is because the state of the environment makes no sense to the code. A clear error message won't help you here, it still won't make the code run.
document is not set in some places - even if it's defined on the top. it should be "window.document". it's not working in electron neither for me.
I have the same issue with a similar use-case as @reenberg ... Did you solve your issue with a specific workaround?
- locked as resolved and limited conversation to collaborators
on Jun 17, 2018
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 2.2.4
jQuery 3.1.1
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.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"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
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,but
doc.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:
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).
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.