diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index f17a0656d319..ce49f528f6e9 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -1286,180 +1286,199 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) } // Step 15. and later in the HTML5 spec + nsresult rv = NS_OK; + RefPtr request; + mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); if (aElement->GetScriptExternal()) { - return ProcessExternalScript(aElement, scriptKind, type, scriptContent); - } - - return ProcessInlineScript(aElement, scriptKind); -} - -bool -ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement, - ScriptKind aScriptKind, - nsAutoString aTypeAttr, - nsIContent* aScriptContent) -{ - nsCOMPtr scriptURI = aElement->GetScriptURI(); - if (!scriptURI) { - // Asynchronously report the failure to create a URI object - NS_DispatchToCurrentThread( - NewRunnableMethod("nsIScriptElement::FireErrorEvent", - aElement, - &nsIScriptElement::FireErrorEvent)); - return false; - } - - RefPtr request = LookupPreloadRequest(aElement, aScriptKind); - - if (request && NS_FAILED(CheckContentPolicy(mDocument, aElement, request->mURI, - aTypeAttr, false))) { - // Probably plans have changed; even though the preload was allowed seems - // like the actual load is not; let's cancel the preload request. - request->Cancel(); - return false; - } - - if (request) { - // Use the preload request. - request->mElement = aElement; - - // It's possible these attributes changed since we started the preload so - // update them here. - request->SetScriptMode(aElement->GetScriptDeferred(), - aElement->GetScriptAsync()); - } else { - // No usable preload found. - - SRIMetadata sriMetadata; - { - nsAutoString integrity; - aScriptContent->AsElement()->GetAttr(kNameSpaceID_None, - nsGkAtoms::integrity, - integrity); - GetSRIMetadata(integrity, &sriMetadata); - } - - nsCOMPtr principal = aElement->GetScriptURITriggeringPrincipal(); - if (!principal) { - principal = aScriptContent->NodePrincipal(); - } - - CORSMode ourCORSMode = aElement->GetCORSMode(); - mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy(); - request = CreateLoadRequest(aScriptKind, scriptURI, aElement, - ourCORSMode, sriMetadata, ourRefPolicy); - request->mTriggeringPrincipal = Move(principal); - request->mIsInline = false; - request->SetScriptMode(aElement->GetScriptDeferred(), - aElement->GetScriptAsync()); - // keep request->mScriptFromHead to false so we don't treat non preloaded - // scripts as blockers for full page load. See bug 792438. - - nsresult rv = StartLoad(request); - if (NS_FAILED(rv)) { - ReportErrorToConsole(request, rv); - - // Asynchronously report the load failure + // external script + nsCOMPtr scriptURI = aElement->GetScriptURI(); + if (!scriptURI) { + // Asynchronously report the failure to create a URI object NS_DispatchToCurrentThread( NewRunnableMethod("nsIScriptElement::FireErrorEvent", aElement, &nsIScriptElement::FireErrorEvent)); return false; } - } - // Should still be in loading stage of script. - NS_ASSERTION(!request->InCompilingStage(), - "Request should not yet be in compiling stage."); + // Double-check that the preload matches what we're asked to load now. + CORSMode ourCORSMode = aElement->GetCORSMode(); + nsTArray::index_type i = + mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator()); + if (i != nsTArray::NoIndex) { + // preloaded + // note that a script-inserted script can steal a preload! + request = mPreloads[i].mRequest; + request->mElement = aElement; + nsString preloadCharset(mPreloads[i].mCharset); + mPreloads.RemoveElementAt(i); - if (request->IsAsyncScript()) { - AddAsyncRequest(request); - if (request->IsReadyToRun()) { - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. + // Double-check that the charset the preload used is the same as + // the charset we have now. + nsAutoString elementCharset; + aElement->GetScriptCharset(elementCharset); + if (elementCharset.Equals(preloadCharset) && + ourCORSMode == request->mCORSMode && + ourRefPolicy == request->mReferrerPolicy && + scriptKind == request->mKind) { + rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false); + if (NS_FAILED(rv)) { + // probably plans have changed; even though the preload was allowed seems + // like the actual load is not; let's cancel the preload request. + request->Cancel(); + return false; + } + } else { + // Drop the preload + request = nullptr; + } + } - // KVKV TODO: Instead of processing immediately, try off-thread-parsing - // it and only schedule a pending ProcessRequest if that fails. + if (request) { + // Use a preload request. + + // It's possible these attributes changed since we started the preload so + // update them here. + request->SetScriptMode(aElement->GetScriptDeferred(), + aElement->GetScriptAsync()); + } else { + // No usable preload found. + + SRIMetadata sriMetadata; + { + nsAutoString integrity; + scriptContent->AsElement()->GetAttr(kNameSpaceID_None, + nsGkAtoms::integrity, + integrity); + if (!integrity.IsEmpty()) { + MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, + ("ScriptLoader::ProcessScriptElement, integrity=%s", + NS_ConvertUTF16toUTF8(integrity).get())); + nsAutoCString sourceUri; + if (mDocument->GetDocumentURI()) { + mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); + } + SRICheck::IntegrityMetadata(integrity, sourceUri, mReporter, + &sriMetadata); + } + } + + nsCOMPtr principal = aElement->GetScriptURITriggeringPrincipal(); + if (!principal) { + principal = scriptContent->NodePrincipal(); + } + + request = CreateLoadRequest(scriptKind, scriptURI, aElement, ourCORSMode, + sriMetadata, ourRefPolicy); + request->mTriggeringPrincipal = Move(principal); + request->mIsInline = false; + request->SetScriptMode(aElement->GetScriptDeferred(), + aElement->GetScriptAsync()); + // keep request->mScriptFromHead to false so we don't treat non preloaded + // scripts as blockers for full page load. See bug 792438. + + rv = StartLoad(request); + if (NS_FAILED(rv)) { + ReportErrorToConsole(request, rv); + + // Asynchronously report the load failure + NS_DispatchToCurrentThread( + NewRunnableMethod("nsIScriptElement::FireErrorEvent", + aElement, + &nsIScriptElement::FireErrorEvent)); + return false; + } + } + + // Should still be in loading stage of script. + NS_ASSERTION(!request->InCompilingStage(), + "Request should not yet be in compiling stage."); + + if (request->IsAsyncScript()) { + AddAsyncRequest(request); + if (request->IsReadyToRun()) { + // The script is available already. Run it ASAP when the event + // loop gets a chance to spin. + + // KVKV TODO: Instead of processing immediately, try off-thread-parsing + // it and only schedule a pending ProcessRequest if that fails. + ProcessPendingRequestsAsync(); + } + return false; + } + if (!aElement->GetParserCreated()) { + // Violate the HTML5 spec in order to make LABjs and the "order" plug-in + // for RequireJS work with their Gecko-sniffed code path. See + // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html + request->mIsNonAsyncScriptInserted = true; + mNonAsyncExternalScriptInsertedRequests.AppendElement(request); + if (request->IsReadyToRun()) { + // The script is available already. Run it ASAP when the event + // loop gets a chance to spin. + ProcessPendingRequestsAsync(); + } + return false; + } + // we now have a parser-inserted request that may or may not be still + // loading + if (request->IsDeferredScript()) { + // We don't want to run this yet. + // If we come here, the script is a parser-created script and it has + // the defer attribute but not the async attribute. Since a + // a parser-inserted script is being run, we came here by the parser + // running the script, which means the parser is still alive and the + // parse is ongoing. + NS_ASSERTION(mDocument->GetCurrentContentSink() || + aElement->GetParserCreated() == FROM_PARSER_XSLT, + "Non-XSLT Defer script on a document without an active parser; bug 592366."); + AddDeferRequest(request); + return false; + } + + if (aElement->GetParserCreated() == FROM_PARSER_XSLT) { + // Need to maintain order for XSLT-inserted scripts + NS_ASSERTION(!mParserBlockingRequest, + "Parser-blocking scripts and XSLT scripts in the same doc!"); + request->mIsXSLT = true; + mXSLTRequests.AppendElement(request); + if (request->IsReadyToRun()) { + // The script is available already. Run it ASAP when the event + // loop gets a chance to spin. + ProcessPendingRequestsAsync(); + } + return true; + } + + if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) { + // The request has already been loaded and there are no pending style + // sheets. If the script comes from the network stream, cheat for + // performance reasons and avoid a trip through the event loop. + if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) { + return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; + } + // Otherwise, we've got a document.written script, make a trip through + // the event loop to hide the preload effects from the scripts on the + // Web page. + NS_ASSERTION(!mParserBlockingRequest, + "There can be only one parser-blocking script at a time"); + NS_ASSERTION(mXSLTRequests.isEmpty(), + "Parser-blocking scripts and XSLT scripts in the same doc!"); + mParserBlockingRequest = request; ProcessPendingRequestsAsync(); + return true; } - return false; - } - if (!aElement->GetParserCreated()) { - // Violate the HTML5 spec in order to make LABjs and the "order" plug-in - // for RequireJS work with their Gecko-sniffed code path. See - // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html - request->mIsNonAsyncScriptInserted = true; - mNonAsyncExternalScriptInsertedRequests.AppendElement(request); - if (request->IsReadyToRun()) { - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. - ProcessPendingRequestsAsync(); - } - return false; - } - // we now have a parser-inserted request that may or may not be still - // loading - if (request->IsDeferredScript()) { - // We don't want to run this yet. - // If we come here, the script is a parser-created script and it has - // the defer attribute but not the async attribute. Since a - // a parser-inserted script is being run, we came here by the parser - // running the script, which means the parser is still alive and the - // parse is ongoing. - NS_ASSERTION(mDocument->GetCurrentContentSink() || - aElement->GetParserCreated() == FROM_PARSER_XSLT, - "Non-XSLT Defer script on a document without an active parser; bug 592366."); - AddDeferRequest(request); - return false; - } - if (aElement->GetParserCreated() == FROM_PARSER_XSLT) { - // Need to maintain order for XSLT-inserted scripts - NS_ASSERTION(!mParserBlockingRequest, - "Parser-blocking scripts and XSLT scripts in the same doc!"); - request->mIsXSLT = true; - mXSLTRequests.AppendElement(request); - if (request->IsReadyToRun()) { - // The script is available already. Run it ASAP when the event - // loop gets a chance to spin. - ProcessPendingRequestsAsync(); - } - return true; - } - - if (request->IsReadyToRun() && ReadyToExecuteParserBlockingScripts()) { - // The request has already been loaded and there are no pending style - // sheets. If the script comes from the network stream, cheat for - // performance reasons and avoid a trip through the event loop. - if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) { - return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; - } - // Otherwise, we've got a document.written script, make a trip through - // the event loop to hide the preload effects from the scripts on the - // Web page. + // The script hasn't loaded yet or there's a style sheet blocking it. + // The script will be run when it loads or the style sheet loads. NS_ASSERTION(!mParserBlockingRequest, "There can be only one parser-blocking script at a time"); NS_ASSERTION(mXSLTRequests.isEmpty(), "Parser-blocking scripts and XSLT scripts in the same doc!"); mParserBlockingRequest = request; - ProcessPendingRequestsAsync(); return true; } - // The script hasn't loaded yet or there's a style sheet blocking it. - // The script will be run when it loads or the style sheet loads. - NS_ASSERTION(!mParserBlockingRequest, - "There can be only one parser-blocking script at a time"); - NS_ASSERTION(mXSLTRequests.isEmpty(), - "Parser-blocking scripts and XSLT scripts in the same doc!"); - mParserBlockingRequest = request; - return true; -} - -bool -ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement, - ScriptKind aScriptKind) -{ + // inline script // Is this document sandboxed without 'allow-scripts'? if (mDocument->HasScriptsBlockedBySandbox()) { return false; @@ -1472,15 +1491,14 @@ ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement, // Inline classic scripts ignore their CORS mode and are always CORS_NONE. CORSMode corsMode = CORS_NONE; - if (aScriptKind == ScriptKind::eModule) { + if (scriptKind == ScriptKind::eModule) { corsMode = aElement->GetCORSMode(); } - RefPtr request = - CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement, - corsMode, - SRIMetadata(), // SRI doesn't apply - mDocument->GetReferrerPolicy()); + request = CreateLoadRequest(scriptKind, mDocument->GetDocumentURI(), aElement, + corsMode, + SRIMetadata(), // SRI doesn't apply + ourRefPolicy); request->mIsInline = true; request->mTriggeringPrincipal = mDocument->NodePrincipal(); request->mLineNo = aElement->GetScriptLineNumber(); @@ -1556,59 +1574,6 @@ ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement, return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK; } -ScriptLoadRequest* -ScriptLoader::LookupPreloadRequest(nsIScriptElement* aElement, - ScriptKind aScriptKind) -{ - nsTArray::index_type i = - mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator()); - if (i == nsTArray::NoIndex) { - return nullptr; - } - - // Found preloaded request. Note that a script-inserted script can steal a - // preload! - RefPtr request = mPreloads[i].mRequest; - nsString preloadCharset(mPreloads[i].mCharset); - mPreloads.RemoveElementAt(i); - - // Double-check that the charset the preload used is the same as the charset - // we have now. - nsAutoString elementCharset; - aElement->GetScriptCharset(elementCharset); - if (!elementCharset.Equals(preloadCharset) || - aElement->GetCORSMode() != request->mCORSMode || - mDocument->GetReferrerPolicy() != request->mReferrerPolicy || - aScriptKind != request->mKind) { - // Drop the preload. - return nullptr; - } - - return request; -} - -void -ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr, - SRIMetadata *aMetadataOut) -{ - MOZ_ASSERT(aMetadataOut->IsEmpty()); - - if (aIntegrityAttr.IsEmpty()) { - return; - } - - MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, - ("ScriptLoader::GetSRIMetadata, integrity=%s", - NS_ConvertUTF16toUTF8(aIntegrityAttr).get())); - - nsAutoCString sourceUri; - if (mDocument->GetDocumentURI()) { - mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); - } - SRICheck::IntegrityMetadata(aIntegrityAttr, sourceUri, mReporter, - aMetadataOut); -} - namespace { class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable @@ -3164,7 +3129,16 @@ ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset, } SRIMetadata sriMetadata; - GetSRIMetadata(aIntegrity, &sriMetadata); + if (!aIntegrity.IsEmpty()) { + MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug, + ("ScriptLoader::PreloadURI, integrity=%s", + NS_ConvertUTF16toUTF8(aIntegrity).get())); + nsAutoCString sourceUri; + if (mDocument->GetDocumentURI()) { + mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); + } + SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata); + } RefPtr request = CreateLoadRequest(ScriptKind::eClassic, aURI, nullptr, diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index f2cf1d376cf7..0aafa9a63003 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -357,20 +357,6 @@ private: void ContinueParserAsync(ScriptLoadRequest* aParserBlockingRequest); - bool ProcessExternalScript(nsIScriptElement* aElement, - ScriptKind aScriptKind, - nsAutoString aTypeAttr, - nsIContent* aScriptContent); - - bool ProcessInlineScript(nsIScriptElement* aElement, - ScriptKind aScriptKind); - - ScriptLoadRequest* LookupPreloadRequest(nsIScriptElement* aElement, - ScriptKind aScriptKind); - - void GetSRIMetadata(const nsAString& aIntegrityAttr, - SRIMetadata *aMetadataOut); - /** * Helper function to check the content policy for a given request. */