1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim: ft=cpp tw=78 sw=2 et ts=2
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * A class that handles loading and evaluation of <script> elements.
11 #include "nsScriptLoader.h"
14 #include "jsfriendapi.h"
15 #include "nsIUnicodeDecoder.h"
16 #include "nsIContent.h"
17 #include "nsJSUtils.h"
18 #include "mozilla/dom/ScriptSettings.h"
19 #include "mozilla/dom/Element.h"
20 #include "nsGkAtoms.h"
21 #include "nsNetUtil.h"
22 #include "nsIJSRuntimeService.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsIScriptContext.h"
25 #include "nsIScriptSecurityManager.h"
26 #include "nsIPrincipal.h"
27 #include "nsJSPrincipals.h"
28 #include "nsContentPolicyUtils.h"
29 #include "nsIHttpChannel.h"
30 #include "nsIClassOfService.h"
31 #include "nsITimedChannel.h"
32 #include "nsIScriptElement.h"
33 #include "nsIDOMHTMLScriptElement.h"
34 #include "nsIDocShell.h"
35 #include "nsContentUtils.h"
36 #include "nsUnicharUtils.h"
37 #include "nsAutoPtr.h"
38 #include "nsIXPConnect.h"
40 #include "nsThreadUtils.h"
41 #include "nsDocShellCID.h"
42 #include "nsIContentSecurityPolicy.h"
45 #include "nsContentCreatorFunctions.h"
46 #include "nsCORSListenerProxy.h"
47 #include "nsSandboxFlags.h"
48 #include "nsContentTypeParser.h"
49 #include "nsINetworkPredictor.h"
50 #include "ImportManager.h"
51 #include "mozilla/dom/EncodingUtils.h"
53 #include "mozilla/CORSMode.h"
54 #include "mozilla/Attributes.h"
55 #include "mozilla/unused.h"
58 static PRLogModuleInfo
* gCspPRLog
;
61 using namespace mozilla
;
62 using namespace mozilla::dom
;
64 //////////////////////////////////////////////////////////////
65 // Per-request data structure
66 //////////////////////////////////////////////////////////////
68 class nsScriptLoadRequest MOZ_FINAL
: public nsISupports
{
69 ~nsScriptLoadRequest()
71 js_free(mScriptTextBuf
);
75 nsScriptLoadRequest(nsIScriptElement
* aElement
,
81 mHasSourceMapURL(false),
82 mScriptTextBuf(nullptr),
87 mReferrerPolicy(mozilla::net::RP_Default
)
91 NS_DECL_THREADSAFE_ISUPPORTS
93 void FireScriptAvailable(nsresult aResult
)
95 mElement
->ScriptAvailable(aResult
, mElement
, mIsInline
, mURI
, mLineNo
);
97 void FireScriptEvaluated(nsresult aResult
)
99 mElement
->ScriptEvaluated(aResult
, mElement
, mIsInline
);
104 return mElement
== nullptr;
107 nsCOMPtr
<nsIScriptElement
> mElement
;
108 bool mLoading
; // Are we still waiting for a load to complete?
109 bool mIsInline
; // Is the script inline or loaded?
110 bool mHasSourceMapURL
; // Does the HTTP header have a source map url?
111 nsString mSourceMapURL
; // Holds source map url for loaded scripts
112 char16_t
* mScriptTextBuf
; // Holds script text for non-inline scripts. Don't
113 size_t mScriptTextLength
; // use nsString so we can give ownership to jsapi.
115 nsCOMPtr
<nsIURI
> mURI
;
116 nsCOMPtr
<nsIPrincipal
> mOriginPrincipal
;
117 nsAutoCString mURL
; // Keep the URI's filename alive during off thread parsing.
119 const CORSMode mCORSMode
;
120 mozilla::net::ReferrerPolicy mReferrerPolicy
;
123 // The nsScriptLoadRequest is passed as the context to necko, and thus
124 // it needs to be threadsafe. Necko won't do anything with this
125 // context, but it will AddRef and Release it on other threads.
126 NS_IMPL_ISUPPORTS0(nsScriptLoadRequest
)
128 //////////////////////////////////////////////////////////////
130 //////////////////////////////////////////////////////////////
132 nsScriptLoader::nsScriptLoader(nsIDocument
*aDocument
)
133 : mDocument(aDocument
),
136 mDeferEnabled(false),
137 mDocumentParsingDone(false),
138 mBlockingDOMContentLoaded(false)
140 // enable logging for CSP
143 gCspPRLog
= PR_NewLogModule("CSP");
147 nsScriptLoader::~nsScriptLoader()
151 if (mParserBlockingRequest
) {
152 mParserBlockingRequest
->FireScriptAvailable(NS_ERROR_ABORT
);
155 for (uint32_t i
= 0; i
< mXSLTRequests
.Length(); i
++) {
156 mXSLTRequests
[i
]->FireScriptAvailable(NS_ERROR_ABORT
);
159 for (uint32_t i
= 0; i
< mDeferRequests
.Length(); i
++) {
160 mDeferRequests
[i
]->FireScriptAvailable(NS_ERROR_ABORT
);
163 for (uint32_t i
= 0; i
< mAsyncRequests
.Length(); i
++) {
164 mAsyncRequests
[i
]->FireScriptAvailable(NS_ERROR_ABORT
);
167 for (uint32_t i
= 0; i
< mNonAsyncExternalScriptInsertedRequests
.Length(); i
++) {
168 mNonAsyncExternalScriptInsertedRequests
[i
]->FireScriptAvailable(NS_ERROR_ABORT
);
171 // Unblock the kids, in case any of them moved to a different document
172 // subtree in the meantime and therefore aren't actually going away.
173 for (uint32_t j
= 0; j
< mPendingChildLoaders
.Length(); ++j
) {
174 mPendingChildLoaders
[j
]->RemoveExecuteBlocker();
178 NS_IMPL_ISUPPORTS(nsScriptLoader
, nsIStreamLoaderObserver
)
180 // Helper method for checking if the script element is an event-handler
181 // This means that it has both a for-attribute and a event-attribute.
182 // Also, if the for-attribute has a value that matches "\s*window\s*",
183 // and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
184 // eventhandler. (both matches are case insensitive).
185 // This is how IE seems to filter out a window's onload handler from a
186 // <script for=... event=...> element.
189 IsScriptEventHandler(nsIContent
* aScriptElement
)
191 if (!aScriptElement
->IsHTML()) {
195 nsAutoString forAttr
, eventAttr
;
196 if (!aScriptElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::_for
, forAttr
) ||
197 !aScriptElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::event
, eventAttr
)) {
201 const nsAString
& for_str
=
202 nsContentUtils::TrimWhitespace
<nsCRT::IsAsciiSpace
>(forAttr
);
203 if (!for_str
.LowerCaseEqualsLiteral("window")) {
207 // We found for="window", now check for event="onload".
208 const nsAString
& event_str
=
209 nsContentUtils::TrimWhitespace
<nsCRT::IsAsciiSpace
>(eventAttr
, false);
210 if (!StringBeginsWith(event_str
, NS_LITERAL_STRING("onload"),
211 nsCaseInsensitiveStringComparator())) {
212 // It ain't "onload.*".
217 nsAutoString::const_iterator start
, end
;
218 event_str
.BeginReading(start
);
219 event_str
.EndReading(end
);
221 start
.advance(6); // advance past "onload"
223 if (start
!= end
&& *start
!= '(' && *start
!= ' ') {
224 // We got onload followed by something other than space or
225 // '('. Not good enough.
234 nsScriptLoader::CheckContentPolicy(nsIDocument
* aDocument
,
235 nsISupports
*aContext
,
237 const nsAString
&aType
)
239 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
240 nsresult rv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT
,
242 aDocument
->NodePrincipal(),
244 NS_LossyConvertUTF16toASCII(aType
),
247 nsContentUtils::GetContentPolicy(),
248 nsContentUtils::GetSecurityManager());
249 if (NS_FAILED(rv
) || NS_CP_REJECTED(shouldLoad
)) {
250 if (NS_FAILED(rv
) || shouldLoad
!= nsIContentPolicy::REJECT_TYPE
) {
251 return NS_ERROR_CONTENT_BLOCKED
;
253 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT
;
260 nsScriptLoader::ShouldLoadScript(nsIDocument
* aDocument
,
261 nsISupports
* aContext
,
263 const nsAString
&aType
)
265 // Check that the containing page is allowed to load this URI.
266 nsresult rv
= nsContentUtils::GetSecurityManager()->
267 CheckLoadURIWithPrincipal(aDocument
->NodePrincipal(), aURI
,
268 nsIScriptSecurityManager::ALLOW_CHROME
);
270 NS_ENSURE_SUCCESS(rv
, rv
);
272 // After the security manager, the content-policy stuff gets a veto
273 rv
= CheckContentPolicy(aDocument
, aContext
, aURI
, aType
);
282 nsScriptLoader::StartLoad(nsScriptLoadRequest
*aRequest
, const nsAString
&aType
,
283 bool aScriptFromHead
)
285 nsISupports
*context
= aRequest
->mElement
.get()
286 ? static_cast<nsISupports
*>(aRequest
->mElement
.get())
287 : static_cast<nsISupports
*>(mDocument
);
288 nsresult rv
= ShouldLoadScript(mDocument
, context
, aRequest
->mURI
, aType
);
293 nsCOMPtr
<nsILoadGroup
> loadGroup
= mDocument
->GetDocumentLoadGroup();
295 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(mDocument
->MasterDocument()->GetWindow()));
298 return NS_ERROR_NULL_POINTER
;
301 nsIDocShell
*docshell
= window
->GetDocShell();
303 nsCOMPtr
<nsIInterfaceRequestor
> prompter(do_QueryInterface(docshell
));
305 // If this document is sandboxed without 'allow-scripts', abort.
306 if (mDocument
->GetSandboxFlags() & SANDBOXED_SCRIPTS
) {
310 nsCOMPtr
<nsIChannel
> channel
;
311 rv
= NS_NewChannel(getter_AddRefs(channel
),
314 nsILoadInfo::SEC_NORMAL
,
315 nsIContentPolicy::TYPE_SCRIPT
,
318 nsIRequest::LOAD_NORMAL
|
319 nsIChannel::LOAD_CLASSIFY_URI
);
321 NS_ENSURE_SUCCESS(rv
, rv
);
323 nsIScriptElement
*script
= aRequest
->mElement
;
324 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(channel
));
327 if (aScriptFromHead
&&
328 !(script
&& (script
->GetScriptAsync() || script
->GetScriptDeferred()))) {
329 // synchronous head scripts block lading of most other non js/css
330 // content such as images
331 cos
->AddClassFlags(nsIClassOfService::Leader
);
332 } else if (!(script
&& script
->GetScriptDeferred())) {
333 // other scripts are neither blocked nor prioritized unless marked deferred
334 cos
->AddClassFlags(nsIClassOfService::Unblocked
);
338 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
340 // HTTP content negotation has little value in this context.
341 httpChannel
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
342 NS_LITERAL_CSTRING("*/*"),
344 httpChannel
->SetReferrerWithPolicy(mDocument
->GetDocumentURI(),
345 aRequest
->mReferrerPolicy
);
348 nsCOMPtr
<nsILoadContext
> loadContext(do_QueryInterface(docshell
));
349 mozilla::net::PredictorLearn(aRequest
->mURI
, mDocument
->GetDocumentURI(),
350 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
, loadContext
);
352 // Set the initiator type
353 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChannel
));
355 timedChannel
->SetInitiatorType(NS_LITERAL_STRING("script"));
358 nsCOMPtr
<nsIStreamLoader
> loader
;
359 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), this);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 nsCOMPtr
<nsIStreamListener
> listener
= loader
.get();
364 if (aRequest
->mCORSMode
!= CORS_NONE
) {
365 bool withCredentials
= (aRequest
->mCORSMode
== CORS_USE_CREDENTIALS
);
366 nsRefPtr
<nsCORSListenerProxy
> corsListener
=
367 new nsCORSListenerProxy(listener
, mDocument
->NodePrincipal(),
369 rv
= corsListener
->Init(channel
);
370 NS_ENSURE_SUCCESS(rv
, rv
);
371 listener
= corsListener
;
374 rv
= channel
->AsyncOpen(listener
, aRequest
);
375 NS_ENSURE_SUCCESS(rv
, rv
);
381 nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo
&aPi
,
382 nsIURI
* const &aURI
) const
385 return NS_SUCCEEDED(aPi
.mRequest
->mURI
->Equals(aURI
, &same
)) &&
389 class nsScriptRequestProcessor
: public nsRunnable
392 nsRefPtr
<nsScriptLoader
> mLoader
;
393 nsRefPtr
<nsScriptLoadRequest
> mRequest
;
395 nsScriptRequestProcessor(nsScriptLoader
* aLoader
,
396 nsScriptLoadRequest
* aRequest
)
402 return mLoader
->ProcessRequest(mRequest
);
407 ParseTypeAttribute(const nsAString
& aType
, JSVersion
* aVersion
)
409 MOZ_ASSERT(!aType
.IsEmpty());
410 MOZ_ASSERT(aVersion
);
411 MOZ_ASSERT(*aVersion
== JSVERSION_DEFAULT
);
413 nsContentTypeParser
parser(aType
);
415 nsAutoString mimeType
;
416 nsresult rv
= parser
.GetType(mimeType
);
417 NS_ENSURE_SUCCESS(rv
, false);
419 if (!nsContentUtils::IsJavascriptMIMEType(mimeType
)) {
423 // Get the version string, and ensure the language supports it.
424 nsAutoString versionName
;
425 rv
= parser
.GetParameter("version", versionName
);
427 if (NS_SUCCEEDED(rv
)) {
428 *aVersion
= nsContentUtils::ParseJavascriptVersion(versionName
);
429 } else if (rv
!= NS_ERROR_INVALID_ARG
) {
437 CSPAllowsInlineScript(nsIScriptElement
*aElement
, nsIDocument
*aDocument
)
439 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
440 // Note: For imports NodePrincipal and the principal of the master are
442 nsresult rv
= aDocument
->NodePrincipal()->GetCsp(getter_AddRefs(csp
));
443 NS_ENSURE_SUCCESS(rv
, false);
450 // An inline script can be allowed because all inline scripts are allowed,
451 // or else because it is whitelisted by a nonce-source or hash-source. This
452 // is a logical OR between whitelisting methods, so the allowInlineScript
453 // outparam can be reused for each check as long as we stop checking as soon
454 // as it is set to true. This also optimizes performance by avoiding the
455 // overhead of unnecessary checks.
456 bool allowInlineScript
= true;
457 nsAutoTArray
<unsigned short, 3> violations
;
459 bool reportInlineViolation
= false;
460 rv
= csp
->GetAllowsInlineScript(&reportInlineViolation
, &allowInlineScript
);
461 NS_ENSURE_SUCCESS(rv
, false);
462 if (reportInlineViolation
) {
463 violations
.AppendElement(static_cast<unsigned short>(
464 nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT
));
468 if (!allowInlineScript
) {
469 nsCOMPtr
<nsIContent
> scriptContent
= do_QueryInterface(aElement
);
470 bool foundNonce
= scriptContent
->GetAttr(kNameSpaceID_None
,
471 nsGkAtoms::nonce
, nonce
);
473 bool reportNonceViolation
;
474 rv
= csp
->GetAllowsNonce(nonce
, nsIContentPolicy::TYPE_SCRIPT
,
475 &reportNonceViolation
, &allowInlineScript
);
476 NS_ENSURE_SUCCESS(rv
, false);
477 if (reportNonceViolation
) {
478 violations
.AppendElement(static_cast<unsigned short>(
479 nsIContentSecurityPolicy::VIOLATION_TYPE_NONCE_SCRIPT
));
484 if (!allowInlineScript
) {
485 bool reportHashViolation
;
486 nsAutoString scriptText
;
487 aElement
->GetScriptText(scriptText
);
488 rv
= csp
->GetAllowsHash(scriptText
, nsIContentPolicy::TYPE_SCRIPT
,
489 &reportHashViolation
, &allowInlineScript
);
490 NS_ENSURE_SUCCESS(rv
, false);
491 if (reportHashViolation
) {
492 violations
.AppendElement(static_cast<unsigned short>(
493 nsIContentSecurityPolicy::VIOLATION_TYPE_HASH_SCRIPT
));
497 // What violation(s) should be reported?
499 // 1. If the script tag has a nonce attribute, and the nonce does not match
500 // the policy, report VIOLATION_TYPE_NONCE_SCRIPT.
501 // 2. If the policy has at least one hash-source, and the hashed contents of
502 // the script tag did not match any of them, report VIOLATION_TYPE_HASH_SCRIPT
503 // 3. Otherwise, report VIOLATION_TYPE_INLINE_SCRIPT if appropriate.
505 // 1 and 2 may occur together, 3 should only occur by itself. Naturally,
506 // every VIOLATION_TYPE_NONCE_SCRIPT and VIOLATION_TYPE_HASH_SCRIPT are also
507 // VIOLATION_TYPE_INLINE_SCRIPT, but reporting the
508 // VIOLATION_TYPE_INLINE_SCRIPT is redundant and does not help the developer.
509 if (!violations
.IsEmpty()) {
510 MOZ_ASSERT(violations
[0] == nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT
,
511 "How did we get any violations without an initial inline script violation?");
512 // gather information to log with violation report
513 nsIURI
* uri
= aDocument
->GetDocumentURI();
514 nsAutoCString asciiSpec
;
515 uri
->GetAsciiSpec(asciiSpec
);
516 nsAutoString scriptText
;
517 aElement
->GetScriptText(scriptText
);
518 nsAutoString
scriptSample(scriptText
);
520 // cap the length of the script sample at 40 chars
521 if (scriptSample
.Length() > 40) {
522 scriptSample
.Truncate(40);
523 scriptSample
.AppendLiteral("...");
526 for (uint32_t i
= 0; i
< violations
.Length(); i
++) {
527 // Skip reporting the redundant inline script violation if there are
528 // other (nonce and/or hash violations) as well.
529 if (i
> 0 || violations
.Length() == 1) {
530 csp
->LogViolationDetails(violations
[i
], NS_ConvertUTF8toUTF16(asciiSpec
),
531 scriptSample
, aElement
->GetScriptLineNumber(),
537 if (!allowInlineScript
) {
538 NS_ASSERTION(!violations
.IsEmpty(),
539 "CSP blocked inline script but is not reporting a violation");
546 nsScriptLoader::ProcessScriptElement(nsIScriptElement
*aElement
)
548 // We need a document to evaluate scripts.
549 NS_ENSURE_TRUE(mDocument
, false);
551 // Check to see if scripts has been turned off.
552 if (!mEnabled
|| !mDocument
->IsScriptEnabled()) {
556 NS_ASSERTION(!aElement
->IsMalformed(), "Executing malformed script");
558 nsCOMPtr
<nsIContent
> scriptContent
= do_QueryInterface(aElement
);
560 // Step 12. Check that the script is not an eventhandler
561 if (IsScriptEventHandler(scriptContent
)) {
565 JSVersion version
= JSVERSION_DEFAULT
;
567 // Check the type attribute to determine language and version.
568 // If type exists, it trumps the deprecated 'language='
570 aElement
->GetScriptType(type
);
571 if (!type
.IsEmpty()) {
572 NS_ENSURE_TRUE(ParseTypeAttribute(type
, &version
), false);
574 // no 'type=' element
575 // "language" is a deprecated attribute of HTML, so we check it only for
576 // HTML script elements.
577 if (scriptContent
->IsHTML()) {
578 nsAutoString language
;
579 scriptContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::language
, language
);
580 if (!language
.IsEmpty()) {
581 if (!nsContentUtils::IsJavaScriptLanguage(language
)) {
588 // Step 14. in the HTML5 spec
590 nsRefPtr
<nsScriptLoadRequest
> request
;
591 if (aElement
->GetScriptExternal()) {
593 nsCOMPtr
<nsIURI
> scriptURI
= aElement
->GetScriptURI();
595 // Asynchronously report the failure to create a URI object
596 NS_DispatchToCurrentThread(
597 NS_NewRunnableMethod(aElement
,
598 &nsIScriptElement::FireErrorEvent
));
602 // Double-check that the preload matches what we're asked to load now.
603 mozilla::net::ReferrerPolicy ourRefPolicy
= mDocument
->GetReferrerPolicy();
604 CORSMode ourCORSMode
= aElement
->GetCORSMode();
605 nsTArray
<PreloadInfo
>::index_type i
=
606 mPreloads
.IndexOf(scriptURI
.get(), 0, PreloadURIComparator());
607 if (i
!= nsTArray
<PreloadInfo
>::NoIndex
) {
609 // note that a script-inserted script can steal a preload!
610 request
= mPreloads
[i
].mRequest
;
611 request
->mElement
= aElement
;
612 nsString
preloadCharset(mPreloads
[i
].mCharset
);
613 mPreloads
.RemoveElementAt(i
);
615 // Double-check that the charset the preload used is the same as
616 // the charset we have now.
617 nsAutoString elementCharset
;
618 aElement
->GetScriptCharset(elementCharset
);
619 if (elementCharset
.Equals(preloadCharset
) &&
620 ourCORSMode
== request
->mCORSMode
&&
621 ourRefPolicy
== request
->mReferrerPolicy
) {
622 rv
= CheckContentPolicy(mDocument
, aElement
, request
->mURI
, type
);
623 NS_ENSURE_SUCCESS(rv
, false);
632 request
= new nsScriptLoadRequest(aElement
, version
, ourCORSMode
);
633 request
->mURI
= scriptURI
;
634 request
->mIsInline
= false;
635 request
->mLoading
= true;
636 request
->mReferrerPolicy
= ourRefPolicy
;
638 // set aScriptFromHead to false so we don't treat non preloaded scripts as
639 // blockers for full page load. See bug 792438.
640 rv
= StartLoad(request
, type
, false);
642 // Asynchronously report the load failure
643 NS_DispatchToCurrentThread(
644 NS_NewRunnableMethod(aElement
,
645 &nsIScriptElement::FireErrorEvent
));
650 request
->mJSVersion
= version
;
652 if (aElement
->GetScriptAsync()) {
653 mAsyncRequests
.AppendElement(request
);
654 if (!request
->mLoading
) {
655 // The script is available already. Run it ASAP when the event
656 // loop gets a chance to spin.
657 ProcessPendingRequestsAsync();
661 if (!aElement
->GetParserCreated()) {
662 // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
663 // for RequireJS work with their Gecko-sniffed code path. See
664 // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
665 mNonAsyncExternalScriptInsertedRequests
.AppendElement(request
);
666 if (!request
->mLoading
) {
667 // The script is available already. Run it ASAP when the event
668 // loop gets a chance to spin.
669 ProcessPendingRequestsAsync();
673 // we now have a parser-inserted request that may or may not be still
675 if (aElement
->GetScriptDeferred()) {
676 // We don't want to run this yet.
677 // If we come here, the script is a parser-created script and it has
678 // the defer attribute but not the async attribute. Since a
679 // a parser-inserted script is being run, we came here by the parser
680 // running the script, which means the parser is still alive and the
682 NS_ASSERTION(mDocument
->GetCurrentContentSink() ||
683 aElement
->GetParserCreated() == FROM_PARSER_XSLT
,
684 "Non-XSLT Defer script on a document without an active parser; bug 592366.");
685 AddDeferRequest(request
);
689 if (aElement
->GetParserCreated() == FROM_PARSER_XSLT
) {
690 // Need to maintain order for XSLT-inserted scripts
691 NS_ASSERTION(!mParserBlockingRequest
,
692 "Parser-blocking scripts and XSLT scripts in the same doc!");
693 mXSLTRequests
.AppendElement(request
);
694 if (!request
->mLoading
) {
695 // The script is available already. Run it ASAP when the event
696 // loop gets a chance to spin.
697 ProcessPendingRequestsAsync();
701 if (!request
->mLoading
&& ReadyToExecuteScripts()) {
702 // The request has already been loaded and there are no pending style
703 // sheets. If the script comes from the network stream, cheat for
704 // performance reasons and avoid a trip through the event loop.
705 if (aElement
->GetParserCreated() == FROM_PARSER_NETWORK
) {
706 return ProcessRequest(request
) == NS_ERROR_HTMLPARSER_BLOCK
;
708 // Otherwise, we've got a document.written script, make a trip through
709 // the event loop to hide the preload effects from the scripts on the
711 NS_ASSERTION(!mParserBlockingRequest
,
712 "There can be only one parser-blocking script at a time");
713 NS_ASSERTION(mXSLTRequests
.IsEmpty(),
714 "Parser-blocking scripts and XSLT scripts in the same doc!");
715 mParserBlockingRequest
= request
;
716 ProcessPendingRequestsAsync();
719 // The script hasn't loaded yet or there's a style sheet blocking it.
720 // The script will be run when it loads or the style sheet loads.
721 NS_ASSERTION(!mParserBlockingRequest
,
722 "There can be only one parser-blocking script at a time");
723 NS_ASSERTION(mXSLTRequests
.IsEmpty(),
724 "Parser-blocking scripts and XSLT scripts in the same doc!");
725 mParserBlockingRequest
= request
;
730 // Is this document sandboxed without 'allow-scripts'?
731 if (mDocument
->GetSandboxFlags() & SANDBOXED_SCRIPTS
) {
735 // Does CSP allow this inline script to run?
736 if (!CSPAllowsInlineScript(aElement
, mDocument
)) {
740 // Inline scripts ignore ther CORS mode and are always CORS_NONE
741 request
= new nsScriptLoadRequest(aElement
, version
, CORS_NONE
);
742 request
->mJSVersion
= version
;
743 request
->mLoading
= false;
744 request
->mIsInline
= true;
745 request
->mURI
= mDocument
->GetDocumentURI();
746 request
->mLineNo
= aElement
->GetScriptLineNumber();
748 if (aElement
->GetParserCreated() == FROM_PARSER_XSLT
&&
749 (!ReadyToExecuteScripts() || !mXSLTRequests
.IsEmpty())) {
750 // Need to maintain order for XSLT-inserted scripts
751 NS_ASSERTION(!mParserBlockingRequest
,
752 "Parser-blocking scripts and XSLT scripts in the same doc!");
753 mXSLTRequests
.AppendElement(request
);
756 if (aElement
->GetParserCreated() == NOT_FROM_PARSER
) {
757 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
758 "A script-inserted script is inserted without an update batch?");
759 nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this,
763 if (aElement
->GetParserCreated() == FROM_PARSER_NETWORK
&&
764 !ReadyToExecuteScripts()) {
765 NS_ASSERTION(!mParserBlockingRequest
,
766 "There can be only one parser-blocking script at a time");
767 mParserBlockingRequest
= request
;
768 NS_ASSERTION(mXSLTRequests
.IsEmpty(),
769 "Parser-blocking scripts and XSLT scripts in the same doc!");
772 // We now have a document.written inline script or we have an inline script
773 // from the network but there is no style sheet that is blocking scripts.
774 // Don't check for style sheets blocking scripts in the document.write
775 // case to avoid style sheet network activity affecting when
776 // document.write returns. It's not really necessary to do this if
777 // there's no document.write currently on the call stack. However,
778 // this way matches IE more closely than checking if document.write
779 // is on the call stack.
780 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
781 "Not safe to run a parser-inserted script?");
782 return ProcessRequest(request
) == NS_ERROR_HTMLPARSER_BLOCK
;
787 class NotifyOffThreadScriptLoadCompletedRunnable
: public nsRunnable
789 nsRefPtr
<nsScriptLoadRequest
> mRequest
;
790 nsRefPtr
<nsScriptLoader
> mLoader
;
794 NotifyOffThreadScriptLoadCompletedRunnable(nsScriptLoadRequest
* aRequest
,
795 nsScriptLoader
* aLoader
)
796 : mRequest(aRequest
), mLoader(aLoader
), mToken(nullptr)
799 void SetToken(void* aToken
) {
800 MOZ_ASSERT(aToken
&& !mToken
);
807 } /* anonymous namespace */
810 nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest
* aRequest
, void **aOffThreadToken
)
812 nsresult rv
= ProcessRequest(aRequest
, aOffThreadToken
);
813 mDocument
->UnblockOnload(false);
818 NotifyOffThreadScriptLoadCompletedRunnable::Run()
820 MOZ_ASSERT(NS_IsMainThread());
822 // We want these to be dropped on the main thread, once we return from this
824 nsRefPtr
<nsScriptLoadRequest
> request
= mRequest
.forget();
825 nsRefPtr
<nsScriptLoader
> loader
= mLoader
.forget();
827 nsresult rv
= loader
->ProcessOffThreadRequest(request
, &mToken
);
830 // The result of the off thread parse was not actually needed to process
831 // the request (disappearing window, some other error, ...). Finish the
832 // request to avoid leaks in the JS engine.
833 nsCOMPtr
<nsIJSRuntimeService
> svc
= do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
834 NS_ENSURE_TRUE(svc
, NS_ERROR_FAILURE
);
836 svc
->GetRuntime(&rt
);
837 NS_ENSURE_TRUE(rt
, NS_ERROR_FAILURE
);
838 JS::FinishOffThreadScript(nullptr, rt
, mToken
);
845 OffThreadScriptLoaderCallback(void *aToken
, void *aCallbackData
)
847 nsRefPtr
<NotifyOffThreadScriptLoadCompletedRunnable
> aRunnable
=
848 dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable
*>(aCallbackData
));
849 aRunnable
->SetToken(aToken
);
850 NS_DispatchToMainThread(aRunnable
);
854 nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest
* aRequest
)
856 if (!aRequest
->mElement
->GetScriptAsync() || aRequest
->mIsInline
) {
857 return NS_ERROR_FAILURE
;
860 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
= GetScriptGlobalObject();
862 return NS_ERROR_FAILURE
;
866 if (!jsapi
.InitWithLegacyErrorReporting(globalObject
)) {
867 return NS_ERROR_FAILURE
;
870 JSContext
* cx
= jsapi
.cx();
871 JS::Rooted
<JSObject
*> global(cx
, globalObject
->GetGlobalJSObject());
872 JS::CompileOptions
options(cx
);
873 FillCompileOptionsForRequest(jsapi
, aRequest
, global
, &options
);
875 if (!JS::CanCompileOffThread(cx
, options
, aRequest
->mScriptTextLength
)) {
876 return NS_ERROR_FAILURE
;
879 nsRefPtr
<NotifyOffThreadScriptLoadCompletedRunnable
> runnable
=
880 new NotifyOffThreadScriptLoadCompletedRunnable(aRequest
, this);
882 if (!JS::CompileOffThread(cx
, options
,
883 aRequest
->mScriptTextBuf
, aRequest
->mScriptTextLength
,
884 OffThreadScriptLoaderCallback
,
885 static_cast<void*>(runnable
))) {
886 return NS_ERROR_OUT_OF_MEMORY
;
889 mDocument
->BlockOnload();
891 unused
<< runnable
.forget();
896 nsScriptLoader::ProcessRequest(nsScriptLoadRequest
* aRequest
, void **aOffThreadToken
)
898 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
899 "Processing requests when running scripts is unsafe.");
901 if (!aOffThreadToken
) {
902 nsresult rv
= AttemptAsyncScriptParse(aRequest
);
903 if (rv
!= NS_ERROR_FAILURE
)
907 NS_ENSURE_ARG(aRequest
);
908 nsAutoString textData
;
909 const char16_t
* scriptBuf
= nullptr;
910 size_t scriptLength
= 0;
911 JS::SourceBufferHolder::Ownership giveScriptOwnership
=
912 JS::SourceBufferHolder::NoOwnership
;
914 nsCOMPtr
<nsIDocument
> doc
;
916 nsCOMPtr
<nsINode
> scriptElem
= do_QueryInterface(aRequest
->mElement
);
918 // If there's no script text, we try to get it from the element
919 if (aRequest
->mIsInline
) {
920 // XXX This is inefficient - GetText makes multiple
922 aRequest
->mElement
->GetScriptText(textData
);
924 scriptBuf
= textData
.get();
925 scriptLength
= textData
.Length();
926 giveScriptOwnership
= JS::SourceBufferHolder::NoOwnership
;
929 scriptBuf
= aRequest
->mScriptTextBuf
;
930 scriptLength
= aRequest
->mScriptTextLength
;
932 giveScriptOwnership
= JS::SourceBufferHolder::GiveOwnership
;
933 aRequest
->mScriptTextBuf
= nullptr;
934 aRequest
->mScriptTextLength
= 0;
936 doc
= scriptElem
->OwnerDoc();
939 JS::SourceBufferHolder
srcBuf(scriptBuf
, scriptLength
, giveScriptOwnership
);
941 nsCOMPtr
<nsIScriptElement
> oldParserInsertedScript
;
942 uint32_t parserCreated
= aRequest
->mElement
->GetParserCreated();
944 oldParserInsertedScript
= mCurrentParserInsertedScript
;
945 mCurrentParserInsertedScript
= aRequest
->mElement
;
948 FireScriptAvailable(NS_OK
, aRequest
);
950 // The window may have gone away by this point, in which case there's no point
951 // in trying to run the script.
952 nsCOMPtr
<nsIDocument
> master
= mDocument
->MasterDocument();
953 nsPIDOMWindow
*pwin
= master
->GetInnerWindow();
954 bool runScript
= !!pwin
;
956 nsContentUtils::DispatchTrustedEvent(scriptElem
->OwnerDoc(),
958 NS_LITERAL_STRING("beforescriptexecute"),
959 true, true, &runScript
);
962 // Inner window could have gone away after firing beforescriptexecute
963 pwin
= master
->GetInnerWindow();
971 doc
->BeginEvaluatingExternalScript();
973 aRequest
->mElement
->BeginEvaluating();
974 rv
= EvaluateScript(aRequest
, srcBuf
, aOffThreadToken
);
975 aRequest
->mElement
->EndEvaluating();
977 doc
->EndEvaluatingExternalScript();
980 nsContentUtils::DispatchTrustedEvent(scriptElem
->OwnerDoc(),
982 NS_LITERAL_STRING("afterscriptexecute"),
986 FireScriptEvaluated(rv
, aRequest
);
989 mCurrentParserInsertedScript
= oldParserInsertedScript
;
996 nsScriptLoader::FireScriptAvailable(nsresult aResult
,
997 nsScriptLoadRequest
* aRequest
)
999 for (int32_t i
= 0; i
< mObservers
.Count(); i
++) {
1000 nsCOMPtr
<nsIScriptLoaderObserver
> obs
= mObservers
[i
];
1001 obs
->ScriptAvailable(aResult
, aRequest
->mElement
,
1002 aRequest
->mIsInline
, aRequest
->mURI
,
1006 aRequest
->FireScriptAvailable(aResult
);
1010 nsScriptLoader::FireScriptEvaluated(nsresult aResult
,
1011 nsScriptLoadRequest
* aRequest
)
1013 for (int32_t i
= 0; i
< mObservers
.Count(); i
++) {
1014 nsCOMPtr
<nsIScriptLoaderObserver
> obs
= mObservers
[i
];
1015 obs
->ScriptEvaluated(aResult
, aRequest
->mElement
,
1016 aRequest
->mIsInline
);
1019 aRequest
->FireScriptEvaluated(aResult
);
1022 already_AddRefed
<nsIScriptGlobalObject
>
1023 nsScriptLoader::GetScriptGlobalObject()
1025 nsCOMPtr
<nsIDocument
> master
= mDocument
->MasterDocument();
1026 nsPIDOMWindow
*pwin
= master
->GetInnerWindow();
1031 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
= do_QueryInterface(pwin
);
1032 NS_ASSERTION(globalObject
, "windows must be global objects");
1034 // and make sure we are setup for this type of script.
1035 nsresult rv
= globalObject
->EnsureScriptEnvironment();
1036 if (NS_FAILED(rv
)) {
1040 return globalObject
.forget();
1044 nsScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI
&jsapi
,
1045 nsScriptLoadRequest
*aRequest
,
1046 JS::Handle
<JSObject
*> aScopeChain
,
1047 JS::CompileOptions
*aOptions
)
1049 // It's very important to use aRequest->mURI, not the final URI of the channel
1050 // aRequest ended up getting script data from, as the script filename.
1051 nsContentUtils::GetWrapperSafeScriptFilename(mDocument
, aRequest
->mURI
, aRequest
->mURL
);
1053 aOptions
->setIntroductionType("scriptElement");
1054 aOptions
->setFileAndLine(aRequest
->mURL
.get(), aRequest
->mLineNo
);
1055 aOptions
->setVersion(JSVersion(aRequest
->mJSVersion
));
1056 aOptions
->setCompileAndGo(JS_IsGlobalObject(aScopeChain
));
1057 // We only need the setNoScriptRval bit when compiling off-thread here, since
1058 // otherwise nsJSUtils::EvaluateString will set it up for us.
1059 aOptions
->setNoScriptRval(true);
1060 if (aRequest
->mHasSourceMapURL
) {
1061 aOptions
->setSourceMapURL(aRequest
->mSourceMapURL
.get());
1063 if (aRequest
->mOriginPrincipal
) {
1064 nsIPrincipal
* scriptPrin
= nsContentUtils::ObjectPrincipal(aScopeChain
);
1065 bool subsumes
= scriptPrin
->Subsumes(aRequest
->mOriginPrincipal
);
1066 aOptions
->setMutedErrors(!subsumes
);
1069 JSContext
* cx
= jsapi
.cx();
1070 JS::Rooted
<JS::Value
> elementVal(cx
);
1071 MOZ_ASSERT(aRequest
->mElement
);
1072 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx
, aRequest
->mElement
,
1074 /* aAllowWrapping = */ true))) {
1075 MOZ_ASSERT(elementVal
.isObject());
1076 aOptions
->setElement(&elementVal
.toObject());
1081 nsScriptLoader::EvaluateScript(nsScriptLoadRequest
* aRequest
,
1082 JS::SourceBufferHolder
& aSrcBuf
,
1083 void** aOffThreadToken
)
1085 // We need a document to evaluate scripts.
1087 return NS_ERROR_FAILURE
;
1090 nsCOMPtr
<nsIContent
> scriptContent(do_QueryInterface(aRequest
->mElement
));
1091 nsIDocument
* ownerDoc
= scriptContent
->OwnerDoc();
1092 if (ownerDoc
!= mDocument
) {
1093 // Willful violation of HTML5 as of 2010-12-01
1094 return NS_ERROR_FAILURE
;
1097 // Get the script-type to be used by this element.
1098 NS_ASSERTION(scriptContent
, "no content - what is default script-type?");
1100 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
= GetScriptGlobalObject();
1101 if (!globalObject
) {
1102 return NS_ERROR_FAILURE
;
1105 // Make sure context is a strong reference since we access it after
1106 // we've executed a script, which may cause all other references to
1107 // the context to go away.
1108 nsCOMPtr
<nsIScriptContext
> context
= globalObject
->GetScriptContext();
1110 return NS_ERROR_FAILURE
;
1113 JSVersion version
= JSVersion(aRequest
->mJSVersion
);
1114 if (version
== JSVERSION_UNKNOWN
) {
1118 // New script entry point required, due to the "Create a script" sub-step of
1119 // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
1120 AutoEntryScript
entryScript(globalObject
, true, context
->GetNativeContext());
1121 JS::Rooted
<JSObject
*> global(entryScript
.cx(),
1122 globalObject
->GetGlobalJSObject());
1124 bool oldProcessingScriptTag
= context
->GetProcessingScriptTag();
1125 context
->SetProcessingScriptTag(true);
1128 // Update our current script.
1129 AutoCurrentScriptUpdater
scriptUpdater(this, aRequest
->mElement
);
1130 Maybe
<AutoCurrentScriptUpdater
> masterScriptUpdater
;
1131 nsCOMPtr
<nsIDocument
> master
= mDocument
->MasterDocument();
1132 if (master
!= mDocument
) {
1133 // If this script belongs to an import document, it will be
1134 // executed in the context of the master document. During the
1135 // execution currentScript of the master should refer to this
1136 // script. So let's update the mCurrentScript of the ScriptLoader
1137 // of the master document too.
1138 masterScriptUpdater
.emplace(master
->ScriptLoader(),
1139 aRequest
->mElement
);
1142 JS::CompileOptions
options(entryScript
.cx());
1143 FillCompileOptionsForRequest(entryScript
, aRequest
, global
, &options
);
1144 rv
= nsJSUtils::EvaluateString(entryScript
.cx(), aSrcBuf
, global
, options
,
1148 context
->SetProcessingScriptTag(oldProcessingScriptTag
);
1153 nsScriptLoader::ProcessPendingRequestsAsync()
1155 if (mParserBlockingRequest
|| !mPendingChildLoaders
.IsEmpty()) {
1156 nsCOMPtr
<nsIRunnable
> ev
= NS_NewRunnableMethod(this,
1157 &nsScriptLoader::ProcessPendingRequests
);
1159 NS_DispatchToCurrentThread(ev
);
1164 nsScriptLoader::ProcessPendingRequests()
1166 nsRefPtr
<nsScriptLoadRequest
> request
;
1167 if (mParserBlockingRequest
&&
1168 !mParserBlockingRequest
->mLoading
&&
1169 ReadyToExecuteScripts()) {
1170 request
.swap(mParserBlockingRequest
);
1171 UnblockParser(request
);
1172 ProcessRequest(request
);
1173 ContinueParserAsync(request
);
1176 while (ReadyToExecuteScripts() &&
1177 !mXSLTRequests
.IsEmpty() &&
1178 !mXSLTRequests
[0]->mLoading
) {
1179 request
.swap(mXSLTRequests
[0]);
1180 mXSLTRequests
.RemoveElementAt(0);
1181 ProcessRequest(request
);
1185 while (mEnabled
&& i
< mAsyncRequests
.Length()) {
1186 if (!mAsyncRequests
[i
]->mLoading
) {
1187 request
.swap(mAsyncRequests
[i
]);
1188 mAsyncRequests
.RemoveElementAt(i
);
1189 ProcessRequest(request
);
1195 while (mEnabled
&& !mNonAsyncExternalScriptInsertedRequests
.IsEmpty() &&
1196 !mNonAsyncExternalScriptInsertedRequests
[0]->mLoading
) {
1197 // Violate the HTML5 spec and execute these in the insertion order in
1198 // order to make LABjs and the "order" plug-in for RequireJS work with
1199 // their Gecko-sniffed code path. See
1200 // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
1201 request
.swap(mNonAsyncExternalScriptInsertedRequests
[0]);
1202 mNonAsyncExternalScriptInsertedRequests
.RemoveElementAt(0);
1203 ProcessRequest(request
);
1206 if (mDocumentParsingDone
&& mXSLTRequests
.IsEmpty()) {
1207 while (!mDeferRequests
.IsEmpty() && !mDeferRequests
[0]->mLoading
) {
1208 request
.swap(mDeferRequests
[0]);
1209 mDeferRequests
.RemoveElementAt(0);
1210 ProcessRequest(request
);
1214 while (!mPendingChildLoaders
.IsEmpty() && ReadyToExecuteScripts()) {
1215 nsRefPtr
<nsScriptLoader
> child
= mPendingChildLoaders
[0];
1216 mPendingChildLoaders
.RemoveElementAt(0);
1217 child
->RemoveExecuteBlocker();
1220 if (mDocumentParsingDone
&& mDocument
&&
1221 !mParserBlockingRequest
&& mAsyncRequests
.IsEmpty() &&
1222 mNonAsyncExternalScriptInsertedRequests
.IsEmpty() &&
1223 mXSLTRequests
.IsEmpty() && mDeferRequests
.IsEmpty()) {
1224 if (MaybeRemovedDeferRequests()) {
1225 return ProcessPendingRequests();
1227 // No more pending scripts; time to unblock onload.
1228 // OK to unblock onload synchronously here, since callers must be
1229 // prepared for the world changing anyway.
1230 mDocumentParsingDone
= false;
1231 mDocument
->UnblockOnload(true);
1236 nsScriptLoader::ReadyToExecuteScripts()
1238 // Make sure the SelfReadyToExecuteScripts check is first, so that
1239 // we don't block twice on an ancestor.
1240 if (!SelfReadyToExecuteScripts()) {
1244 for (nsIDocument
* doc
= mDocument
; doc
; doc
= doc
->GetParentDocument()) {
1245 nsScriptLoader
* ancestor
= doc
->ScriptLoader();
1246 if (!ancestor
->SelfReadyToExecuteScripts() &&
1247 ancestor
->AddPendingChildLoader(this)) {
1248 AddExecuteBlocker();
1253 if (mDocument
&& !mDocument
->IsMasterDocument()) {
1254 nsRefPtr
<ImportManager
> im
= mDocument
->ImportManager();
1255 nsRefPtr
<ImportLoader
> loader
= im
->Find(mDocument
);
1256 MOZ_ASSERT(loader
, "How can we have an import document without a loader?");
1258 // The referring link that counts in the execution order calculation
1259 // (in spec: flagged as branch)
1260 nsCOMPtr
<nsINode
> referrer
= loader
->GetMainReferrer();
1261 MOZ_ASSERT(referrer
, "There has to be a main referring link for each imports");
1263 // Import documents are blocked by their import predecessors. We need to
1264 // wait with script execution until all the predecessors are done.
1265 // Technically it means we have to wait for the last one to finish,
1266 // which is the neares one to us in the order.
1267 nsRefPtr
<ImportLoader
> lastPred
= im
->GetNearestPredecessor(referrer
);
1269 // If there is no predecessor we can run.
1273 nsCOMPtr
<nsIDocument
> doc
= lastPred
->GetDocument();
1274 if (lastPred
->IsBlocking() || !doc
|| (doc
&& !doc
->ScriptLoader()->SelfReadyToExecuteScripts())) {
1275 // Document has not been created yet or it was created but not ready.
1276 // Either case we are blocked by it. The ImportLoader will take care
1277 // of blocking us, and adding the pending child loader to the blocking
1278 // ScriptLoader when it's possible (at this point the blocking loader
1279 // might not have created the document/ScriptLoader)
1280 lastPred
->AddBlockedScriptLoader(this);
1281 // As more imports are parsed, this can change, let's cache what we
1282 // blocked, so it can be later updated if needed (see: ImportLoader::Updater).
1283 loader
->SetBlockingPredecessor(lastPred
);
1291 // This function was copied from nsParser.cpp. It was simplified a bit.
1293 DetectByteOrderMark(const unsigned char* aBytes
, int32_t aLen
, nsCString
& oCharset
)
1300 if (aLen
>= 3 && 0xBB == aBytes
[1] && 0xBF == aBytes
[2]) {
1303 oCharset
.AssignLiteral("UTF-8");
1307 if (0xFF == aBytes
[1]) {
1309 // UTF-16, big-endian
1310 oCharset
.AssignLiteral("UTF-16BE");
1314 if (0xFE == aBytes
[1]) {
1316 // UTF-16, little-endian
1317 oCharset
.AssignLiteral("UTF-16LE");
1321 return !oCharset
.IsEmpty();
1324 /* static */ nsresult
1325 nsScriptLoader::ConvertToUTF16(nsIChannel
* aChannel
, const uint8_t* aData
,
1326 uint32_t aLength
, const nsAString
& aHintCharset
,
1327 nsIDocument
* aDocument
,
1328 char16_t
*& aBufOut
, size_t& aLengthOut
)
1336 // The encoding info precedence is as follows from high to low:
1338 // HTTP Content-Type (if name recognized)
1339 // charset attribute (if name recognized)
1340 // The encoding of the document
1342 nsAutoCString charset
;
1344 nsCOMPtr
<nsIUnicodeDecoder
> unicodeDecoder
;
1346 if (DetectByteOrderMark(aData
, aLength
, charset
)) {
1347 // charset is now "UTF-8" or "UTF-16". The UTF-16 decoder will re-sniff
1348 // the BOM for endianness. Both the UTF-16 and the UTF-8 decoder will
1349 // take care of swallowing the BOM.
1350 unicodeDecoder
= EncodingUtils::DecoderForEncoding(charset
);
1353 if (!unicodeDecoder
&&
1355 NS_SUCCEEDED(aChannel
->GetContentCharset(charset
)) &&
1356 EncodingUtils::FindEncodingForLabel(charset
, charset
)) {
1357 unicodeDecoder
= EncodingUtils::DecoderForEncoding(charset
);
1360 if (!unicodeDecoder
&&
1361 EncodingUtils::FindEncodingForLabel(aHintCharset
, charset
)) {
1362 unicodeDecoder
= EncodingUtils::DecoderForEncoding(charset
);
1365 if (!unicodeDecoder
&& aDocument
) {
1366 charset
= aDocument
->GetDocumentCharacterSet();
1367 unicodeDecoder
= EncodingUtils::DecoderForEncoding(charset
);
1370 if (!unicodeDecoder
) {
1371 // Curiously, there are various callers that don't pass aDocument. The
1372 // fallback in the old code was ISO-8859-1, which behaved like
1373 // windows-1252. Saying windows-1252 for clarity and for compliance
1374 // with the Encoding Standard.
1375 unicodeDecoder
= EncodingUtils::DecoderForEncoding("windows-1252");
1378 int32_t unicodeLength
= 0;
1381 unicodeDecoder
->GetMaxLength(reinterpret_cast<const char*>(aData
),
1382 aLength
, &unicodeLength
);
1383 NS_ENSURE_SUCCESS(rv
, rv
);
1385 aBufOut
= static_cast<char16_t
*>(js_malloc(unicodeLength
* sizeof(char16_t
)));
1388 return NS_ERROR_OUT_OF_MEMORY
;
1390 aLengthOut
= unicodeLength
;
1392 rv
= unicodeDecoder
->Convert(reinterpret_cast<const char*>(aData
),
1393 (int32_t *) &aLength
, aBufOut
,
1395 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1396 aLengthOut
= unicodeLength
;
1397 if (NS_FAILED(rv
)) {
1406 nsScriptLoader::OnStreamComplete(nsIStreamLoader
* aLoader
,
1407 nsISupports
* aContext
,
1409 uint32_t aStringLen
,
1410 const uint8_t* aString
)
1412 nsScriptLoadRequest
* request
= static_cast<nsScriptLoadRequest
*>(aContext
);
1413 NS_ASSERTION(request
, "null request in stream complete handler");
1414 NS_ENSURE_TRUE(request
, NS_ERROR_FAILURE
);
1416 nsresult rv
= PrepareLoadedRequest(request
, aLoader
, aStatus
, aStringLen
,
1418 if (NS_FAILED(rv
)) {
1420 * Handle script not loading error because source was a tracking URL.
1421 * We make a note of this script node by including it in a dedicated
1422 * array of blocked tracking nodes under its parent document.
1424 if (rv
== NS_ERROR_TRACKING_URI
) {
1425 nsCOMPtr
<nsIContent
> cont
= do_QueryInterface(request
->mElement
);
1426 mDocument
->AddBlockedTrackingNode(cont
);
1429 if (mDeferRequests
.RemoveElement(request
) ||
1430 mAsyncRequests
.RemoveElement(request
) ||
1431 mNonAsyncExternalScriptInsertedRequests
.RemoveElement(request
) ||
1432 mXSLTRequests
.RemoveElement(request
)) {
1433 FireScriptAvailable(rv
, request
);
1434 } else if (mParserBlockingRequest
== request
) {
1435 mParserBlockingRequest
= nullptr;
1436 UnblockParser(request
);
1437 FireScriptAvailable(rv
, request
);
1438 ContinueParserAsync(request
);
1440 mPreloads
.RemoveElement(request
, PreloadRequestComparator());
1444 moz_free(const_cast<uint8_t *>(aString
));
1445 rv
= NS_SUCCESS_ADOPTED_DATA
;
1448 // Process our request and/or any pending ones
1449 ProcessPendingRequests();
1455 nsScriptLoader::UnblockParser(nsScriptLoadRequest
* aParserBlockingRequest
)
1457 aParserBlockingRequest
->mElement
->UnblockParser();
1461 nsScriptLoader::ContinueParserAsync(nsScriptLoadRequest
* aParserBlockingRequest
)
1463 aParserBlockingRequest
->mElement
->ContinueParserAsync();
1467 nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest
* aRequest
,
1468 nsIStreamLoader
* aLoader
,
1470 uint32_t aStringLen
,
1471 const uint8_t* aString
)
1473 if (NS_FAILED(aStatus
)) {
1477 // If we don't have a document, then we need to abort further
1480 return NS_ERROR_NOT_AVAILABLE
;
1483 // If the load returned an error page, then we need to abort
1484 nsCOMPtr
<nsIRequest
> req
;
1485 nsresult rv
= aLoader
->GetRequest(getter_AddRefs(req
));
1486 NS_ASSERTION(req
, "StreamLoader's request went away prematurely");
1487 NS_ENSURE_SUCCESS(rv
, rv
);
1489 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(req
);
1491 bool requestSucceeded
;
1492 rv
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
1493 if (NS_SUCCEEDED(rv
) && !requestSucceeded
) {
1494 return NS_ERROR_NOT_AVAILABLE
;
1497 nsAutoCString sourceMapURL
;
1498 rv
= httpChannel
->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL
);
1499 if (NS_SUCCEEDED(rv
)) {
1500 aRequest
->mHasSourceMapURL
= true;
1501 aRequest
->mSourceMapURL
= NS_ConvertUTF8toUTF16(sourceMapURL
);
1505 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(req
);
1506 // If this load was subject to a CORS check; don't flag it with a
1507 // separate origin principal, so that it will treat our document's
1508 // principal as the origin principal
1509 if (aRequest
->mCORSMode
== CORS_NONE
) {
1510 rv
= nsContentUtils::GetSecurityManager()->
1511 GetChannelResultPrincipal(channel
, getter_AddRefs(aRequest
->mOriginPrincipal
));
1512 NS_ENSURE_SUCCESS(rv
, rv
);
1516 // Check the charset attribute to determine script charset.
1517 nsAutoString hintCharset
;
1518 if (!aRequest
->IsPreload()) {
1519 aRequest
->mElement
->GetScriptCharset(hintCharset
);
1521 nsTArray
<PreloadInfo
>::index_type i
=
1522 mPreloads
.IndexOf(aRequest
, 0, PreloadRequestComparator());
1523 NS_ASSERTION(i
!= mPreloads
.NoIndex
, "Incorrect preload bookkeeping");
1524 hintCharset
= mPreloads
[i
].mCharset
;
1526 rv
= ConvertToUTF16(channel
, aString
, aStringLen
, hintCharset
, mDocument
,
1527 aRequest
->mScriptTextBuf
, aRequest
->mScriptTextLength
);
1529 NS_ENSURE_SUCCESS(rv
, rv
);
1532 // This assertion could fire errorously if we ran out of memory when
1533 // inserting the request in the array. However it's an unlikely case
1534 // so if you see this assertion it is likely something else that is
1535 // wrong, especially if you see it more than once.
1536 NS_ASSERTION(mDeferRequests
.Contains(aRequest
) ||
1537 mAsyncRequests
.Contains(aRequest
) ||
1538 mNonAsyncExternalScriptInsertedRequests
.Contains(aRequest
) ||
1539 mXSLTRequests
.Contains(aRequest
) ||
1540 mPreloads
.Contains(aRequest
, PreloadRequestComparator()) ||
1541 mParserBlockingRequest
,
1542 "aRequest should be pending!");
1544 // Mark this as loaded
1545 aRequest
->mLoading
= false;
1551 nsScriptLoader::ParsingComplete(bool aTerminated
)
1553 if (mDeferEnabled
) {
1554 // Have to check because we apparently get ParsingComplete
1555 // without BeginDeferringScripts in some cases
1556 mDocumentParsingDone
= true;
1558 mDeferEnabled
= false;
1560 mDeferRequests
.Clear();
1561 mAsyncRequests
.Clear();
1562 mNonAsyncExternalScriptInsertedRequests
.Clear();
1563 mXSLTRequests
.Clear();
1564 mParserBlockingRequest
= nullptr;
1567 // Have to call this even if aTerminated so we'll correctly unblock
1569 ProcessPendingRequests();
1573 nsScriptLoader::PreloadURI(nsIURI
*aURI
, const nsAString
&aCharset
,
1574 const nsAString
&aType
,
1575 const nsAString
&aCrossOrigin
,
1576 bool aScriptFromHead
,
1577 const mozilla::net::ReferrerPolicy aReferrerPolicy
)
1579 // Check to see if scripts has been turned off.
1580 if (!mEnabled
|| !mDocument
->IsScriptEnabled()) {
1584 nsRefPtr
<nsScriptLoadRequest
> request
=
1585 new nsScriptLoadRequest(nullptr, 0,
1586 Element::StringToCORSMode(aCrossOrigin
));
1587 request
->mURI
= aURI
;
1588 request
->mIsInline
= false;
1589 request
->mLoading
= true;
1590 request
->mReferrerPolicy
= aReferrerPolicy
;
1592 nsresult rv
= StartLoad(request
, aType
, aScriptFromHead
);
1593 if (NS_FAILED(rv
)) {
1597 PreloadInfo
*pi
= mPreloads
.AppendElement();
1598 pi
->mRequest
= request
;
1599 pi
->mCharset
= aCharset
;
1603 nsScriptLoader::AddDeferRequest(nsScriptLoadRequest
* aRequest
)
1605 mDeferRequests
.AppendElement(aRequest
);
1606 if (mDeferEnabled
&& mDeferRequests
.Length() == 1 && mDocument
&&
1607 !mBlockingDOMContentLoaded
) {
1608 MOZ_ASSERT(mDocument
->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING
);
1609 mBlockingDOMContentLoaded
= true;
1610 mDocument
->BlockDOMContentLoaded();
1615 nsScriptLoader::MaybeRemovedDeferRequests()
1617 if (mDeferRequests
.Length() == 0 && mDocument
&&
1618 mBlockingDOMContentLoaded
) {
1619 mBlockingDOMContentLoaded
= false;
1620 mDocument
->UnblockDOMContentLoaded();