Bug 1618292 - Make ScriptLoadRequest derive and use PreloaderBase to support new preload as speculative load feature, r=smaug
Depends on D67483 Differential Revision: https://phabricator.services.mozilla.com/D67481
This commit is contained in:
18
dom/script/ScriptKind.h
Normal file
18
dom/script/ScriptKind.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ScriptKind_h
|
||||
#define mozilla_dom_ScriptKind_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
enum class ScriptKind { eClassic, eModule };
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -94,6 +94,14 @@ ScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
uint32_t aDataLength, const uint8_t* aData,
|
||||
uint32_t* aConsumedLength) {
|
||||
nsCOMPtr<nsIRequest> channelRequest;
|
||||
aLoader->GetRequest(getter_AddRefs(channelRequest));
|
||||
|
||||
if (!mPreloadStartNotified) {
|
||||
mPreloadStartNotified = true;
|
||||
mRequest->NotifyStart(channelRequest);
|
||||
}
|
||||
|
||||
if (mRequest->IsCanceled()) {
|
||||
// If request cancelled, ignore any incoming data.
|
||||
*aConsumedLength = aDataLength;
|
||||
@@ -145,8 +153,6 @@ ScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
|
||||
*aConsumedLength = aDataLength;
|
||||
rv = MaybeDecodeSRI();
|
||||
if (NS_FAILED(rv)) {
|
||||
nsCOMPtr<nsIRequest> channelRequest;
|
||||
aLoader->GetRequest(getter_AddRefs(channelRequest));
|
||||
return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
|
||||
}
|
||||
}
|
||||
@@ -334,6 +340,14 @@ ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
|
||||
nsCOMPtr<nsIRequest> channelRequest;
|
||||
aLoader->GetRequest(getter_AddRefs(channelRequest));
|
||||
|
||||
if (!mPreloadStartNotified) {
|
||||
mPreloadStartNotified = true;
|
||||
mRequest->NotifyStart(channelRequest);
|
||||
}
|
||||
|
||||
auto notifyStop =
|
||||
MakeScopeExit([&] { mRequest->NotifyStop(channelRequest, rv); });
|
||||
|
||||
if (!mRequest->IsCanceled()) {
|
||||
if (mRequest->IsUnknownDataType()) {
|
||||
rv = EnsureKnownDataType(aLoader);
|
||||
|
||||
@@ -98,6 +98,9 @@ class ScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver {
|
||||
|
||||
// Unicode decoder for charset.
|
||||
mozilla::UniquePtr<mozilla::Decoder> mDecoder;
|
||||
|
||||
// Flipped to true after calling NotifyStart the first time
|
||||
bool mPreloadStartNotified = false;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIClassOfService.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "ScriptLoadRequest.h"
|
||||
#include "ScriptSettings.h"
|
||||
|
||||
@@ -232,6 +234,24 @@ void ScriptLoadRequest::SetScript(JSScript* aScript) {
|
||||
HoldJSObjects(this);
|
||||
}
|
||||
|
||||
// static
|
||||
void ScriptLoadRequest::PrioritizeAsPreload(nsIChannel* aChannel) {
|
||||
if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
|
||||
cos->AddClassFlags(nsIClassOfService::Unblocked);
|
||||
}
|
||||
if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
|
||||
sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptLoadRequest::PrioritizeAsPreload() {
|
||||
if (!IsLinkPreloadScript()) {
|
||||
// Do the prioritization only if this request has not already been created
|
||||
// as a preload.
|
||||
PrioritizeAsPreload(Channel());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// ScriptLoadRequestList
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -12,12 +12,14 @@
|
||||
#include "mozilla/dom/SRIMetadata.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PreloaderBase.h"
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIScriptElement.h"
|
||||
#include "ScriptKind.h"
|
||||
|
||||
class nsICacheInfoChannel;
|
||||
|
||||
@@ -27,8 +29,6 @@ namespace dom {
|
||||
class ModuleLoadRequest;
|
||||
class ScriptLoadRequestList;
|
||||
|
||||
enum class ScriptKind { eClassic, eModule };
|
||||
|
||||
/*
|
||||
* Some options used when fetching script resources. This only loosely
|
||||
* corresponds to HTML's "script fetch options".
|
||||
@@ -61,7 +61,7 @@ class ScriptFetchOptions {
|
||||
*/
|
||||
|
||||
class ScriptLoadRequest
|
||||
: public nsISupports,
|
||||
: public PreloaderBase,
|
||||
private mozilla::LinkedListElement<ScriptLoadRequest> {
|
||||
typedef LinkedListElement<ScriptLoadRequest> super;
|
||||
|
||||
@@ -80,6 +80,10 @@ class ScriptLoadRequest
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ScriptLoadRequest)
|
||||
|
||||
// PreloaderBase
|
||||
static void PrioritizeAsPreload(nsIChannel* aChannel);
|
||||
virtual void PrioritizeAsPreload() override;
|
||||
|
||||
bool IsModuleRequest() const { return mKind == ScriptKind::eModule; }
|
||||
|
||||
ModuleLoadRequest* AsModuleRequest();
|
||||
|
||||
@@ -1225,6 +1225,9 @@ nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) {
|
||||
aRequest->mScriptBytecode.clearAndFree();
|
||||
TRACE_FOR_TEST(aRequest->Element(), "scriptloader_fallback");
|
||||
|
||||
// Notify preload restart so that we can register this preload request again.
|
||||
aRequest->NotifyRestart(mDocument);
|
||||
|
||||
// Start a new channel from which we explicitly request to stream the source
|
||||
// instead of the bytecode.
|
||||
aRequest->mProgress = ScriptLoadRequest::Progress::eLoading_Source;
|
||||
@@ -1371,20 +1374,17 @@ nsresult ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) {
|
||||
LOG(("ScriptLoadRequest (%p): mode=%u tracking=%d", aRequest,
|
||||
unsigned(aRequest->mScriptMode), aRequest->IsTracking()));
|
||||
|
||||
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
|
||||
if (cos) {
|
||||
if (aRequest->IsLinkPreloadScript()) {
|
||||
// This is <link rel="preload" as="script"> initiated speculative load,
|
||||
// put it to the group that is not blocked by leaders and doesn't block
|
||||
// follower at the same time. Giving it a much higher priority will make
|
||||
// this request be processed ahead of other Unblocked requests, but with
|
||||
// the same weight as Leaders. This will make us behave similar way for
|
||||
// both http2 and http1.
|
||||
cos->AddClassFlags(nsIClassOfService::Unblocked);
|
||||
if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(channel)) {
|
||||
sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
|
||||
}
|
||||
} else if (aRequest->mScriptFromHead && aRequest->IsBlockingScript()) {
|
||||
if (aRequest->IsLinkPreloadScript()) {
|
||||
// This is <link rel="preload" as="script"> initiated speculative load,
|
||||
// put it to the group that is not blocked by leaders and doesn't block
|
||||
// follower at the same time. Giving it a much higher priority will make
|
||||
// this request be processed ahead of other Unblocked requests, but with
|
||||
// the same weight as Leaders. This will make us behave similar way for
|
||||
// both http2 and http1.
|
||||
ScriptLoadRequest::PrioritizeAsPreload(channel);
|
||||
ScriptLoadRequest::AddLoadBackgroundFlag(channel);
|
||||
} else if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
|
||||
if (aRequest->mScriptFromHead && aRequest->IsBlockingScript()) {
|
||||
// synchronous head scripts block loading of most other non js/css
|
||||
// content such as images, Leader implicitely disallows tailing
|
||||
cos->AddClassFlags(nsIClassOfService::Leader);
|
||||
@@ -1471,6 +1471,12 @@ nsresult ScriptLoader::StartLoad(ScriptLoadRequest* aRequest) {
|
||||
rv = channel->AsyncOpen(loader);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
auto key = PreloadHashKey::CreateAsScript(
|
||||
aRequest->mURI, aRequest->CORSMode(), aRequest->mKind,
|
||||
aRequest->ReferrerPolicy());
|
||||
aRequest->NotifyOpen(&key, channel, mDocument,
|
||||
aRequest->IsLinkPreloadScript());
|
||||
|
||||
if (aRequest->IsModuleRequest()) {
|
||||
// We successfully started fetching a module so put its URL in the module
|
||||
// map and mark it as fetching.
|
||||
@@ -1916,6 +1922,10 @@ ScriptLoadRequest* ScriptLoader::LookupPreloadRequest(
|
||||
// Report any errors that we skipped while preloading.
|
||||
ReportPreloadErrorsToConsole(request);
|
||||
|
||||
// This makes sure the pending preload (if exists) for this resource is
|
||||
// properly marked as used and thus not notified in the console as unused.
|
||||
request->NotifyUsage();
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
#include "mozilla/Vector.h"
|
||||
#include "ScriptKind.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [
|
||||
'LoadedScript.h',
|
||||
'ScriptDecoding.h',
|
||||
'ScriptElement.h',
|
||||
'ScriptKind.h',
|
||||
'ScriptLoader.h',
|
||||
'ScriptLoadRequest.h',
|
||||
'ScriptSettings.h',
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
|
||||
#include "PreloadHashKey.h"
|
||||
|
||||
#include "mozilla/dom/Element.h" // StringToCORSMode
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIReferrerInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
PreloadHashKey::PreloadHashKey(const nsIURI* aKey, ResourceType aAs)
|
||||
: nsURIHashKey(aKey), mAs(aAs) {}
|
||||
|
||||
PreloadHashKey::PreloadHashKey(const PreloadHashKey* aKey)
|
||||
: nsURIHashKey(aKey->mKey) {
|
||||
*this = *aKey;
|
||||
@@ -22,6 +26,7 @@ PreloadHashKey::PreloadHashKey(PreloadHashKey&& aToMove)
|
||||
|
||||
switch (mAs) {
|
||||
case ResourceType::SCRIPT:
|
||||
mScript = std::move(aToMove.mScript);
|
||||
break;
|
||||
case ResourceType::STYLE:
|
||||
break;
|
||||
@@ -48,6 +53,7 @@ PreloadHashKey& PreloadHashKey::operator=(const PreloadHashKey& aOther) {
|
||||
|
||||
switch (mAs) {
|
||||
case ResourceType::SCRIPT:
|
||||
mScript = aOther.mScript;
|
||||
break;
|
||||
case ResourceType::STYLE:
|
||||
break;
|
||||
@@ -64,6 +70,32 @@ PreloadHashKey& PreloadHashKey::operator=(const PreloadHashKey& aOther) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// static
|
||||
PreloadHashKey PreloadHashKey::CreateAsScript(
|
||||
nsIURI* aURI, const CORSMode& aCORSMode, const dom::ScriptKind& aScriptKind,
|
||||
const dom::ReferrerPolicy& aReferrerPolicy) {
|
||||
PreloadHashKey key(aURI, ResourceType::SCRIPT);
|
||||
key.mCORSMode = aCORSMode;
|
||||
key.mReferrerPolicy = aReferrerPolicy;
|
||||
|
||||
key.mScript.mScriptKind = aScriptKind;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
// static
|
||||
PreloadHashKey PreloadHashKey::CreateAsScript(
|
||||
nsIURI* aURI, const nsAString& aCrossOrigin, const nsAString& aType,
|
||||
const dom::ReferrerPolicy& aReferrerPolicy) {
|
||||
dom::ScriptKind scriptKind = dom::ScriptKind::eClassic;
|
||||
if (aType.LowerCaseEqualsASCII("module")) {
|
||||
scriptKind = dom::ScriptKind::eModule;
|
||||
}
|
||||
CORSMode crossOrigin = dom::Element::StringToCORSMode(aCrossOrigin);
|
||||
|
||||
return CreateAsScript(aURI, crossOrigin, scriptKind, aReferrerPolicy);
|
||||
}
|
||||
|
||||
bool PreloadHashKey::KeyEquals(KeyTypePointer aOther) const {
|
||||
if (mAs != aOther->mAs || mCORSMode != aOther->mCORSMode ||
|
||||
mReferrerPolicy != aOther->mReferrerPolicy) {
|
||||
@@ -77,6 +109,9 @@ bool PreloadHashKey::KeyEquals(KeyTypePointer aOther) const {
|
||||
|
||||
switch (mAs) {
|
||||
case ResourceType::SCRIPT:
|
||||
if (mScript.mScriptKind != aOther->mScript.mScriptKind) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ResourceType::STYLE:
|
||||
break;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "mozilla/CORSMode.h"
|
||||
#include "mozilla/dom/ReferrerPolicyBinding.h"
|
||||
#include "mozilla/dom/ScriptKind.h"
|
||||
#include "nsURIHashKey.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
@@ -32,14 +33,21 @@ class PreloadHashKey : public nsURIHashKey {
|
||||
typedef const PreloadHashKey* KeyTypePointer;
|
||||
|
||||
PreloadHashKey() = default;
|
||||
PreloadHashKey(const nsIURI* aKey, ResourceType aAs);
|
||||
explicit PreloadHashKey(const PreloadHashKey* aKey);
|
||||
PreloadHashKey(PreloadHashKey&& aToMove);
|
||||
~PreloadHashKey() = default;
|
||||
|
||||
PreloadHashKey& operator=(const PreloadHashKey& aOther);
|
||||
|
||||
static PreloadHashKey CreateAsScript(
|
||||
nsIURI* aURI, const CORSMode& aCORSMode,
|
||||
const dom::ScriptKind& aScriptKind,
|
||||
const dom::ReferrerPolicy& aReferrerPolicy);
|
||||
static PreloadHashKey CreateAsScript(
|
||||
nsIURI* aURI, const nsAString& aCrossOrigin, const nsAString& aType,
|
||||
const dom::ReferrerPolicy& aReferrerPolicy);
|
||||
|
||||
// TODO
|
||||
// static CreateAsScript(...);
|
||||
// static CreateAsStyle(...);
|
||||
// static CreateAsImage(...);
|
||||
// static CreateAsFont(...);
|
||||
@@ -67,6 +75,10 @@ class PreloadHashKey : public nsURIHashKey {
|
||||
|
||||
CORSMode mCORSMode = CORS_NONE;
|
||||
enum dom::ReferrerPolicy mReferrerPolicy = dom::ReferrerPolicy::_empty;
|
||||
|
||||
struct {
|
||||
dom::ScriptKind mScriptKind = dom::ScriptKind::eClassic;
|
||||
} mScript;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -78,18 +78,27 @@ already_AddRefed<PreloaderBase> PreloadService::PreloadLinkElement(
|
||||
aLinkElement->GetCrossOrigin(crossOrigin);
|
||||
aLinkElement->GetIntegrity(integrity);
|
||||
aLinkElement->GetReferrerPolicy(referrerPolicyAttr);
|
||||
auto referrerPolicy = PreloadReferrerPolicy(referrerPolicyAttr);
|
||||
|
||||
PreloadHashKey preloadKey;
|
||||
|
||||
// * Branch according the "as" value and build the `preloadKey` value.
|
||||
if (true) {
|
||||
if (as.LowerCaseEqualsASCII("script")) {
|
||||
dom::DOMString domType;
|
||||
aLinkElement->GetType(domType);
|
||||
domType.ToString(type);
|
||||
preloadKey =
|
||||
PreloadHashKey::CreateAsScript(uri, crossOrigin, type, referrerPolicy);
|
||||
} else {
|
||||
NotifyNodeEvent(aLinkElement, false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PreloaderBase> preload = LookupPreload(&preloadKey);
|
||||
if (!preload) {
|
||||
// * Start preload accordint the "as" value and various other per-type
|
||||
// specific attributes.
|
||||
if (as.LowerCaseEqualsASCII("script")) {
|
||||
PreloadScript(uri, type, charset, crossOrigin, referrerPolicyAttr,
|
||||
integrity, true /* isInHead - TODO */);
|
||||
}
|
||||
|
||||
preload = LookupPreload(&preloadKey);
|
||||
if (!preload) {
|
||||
@@ -103,6 +112,17 @@ already_AddRefed<PreloaderBase> PreloadService::PreloadLinkElement(
|
||||
return preload.forget();
|
||||
}
|
||||
|
||||
void PreloadService::PreloadScript(nsIURI* aURI, const nsAString& aType,
|
||||
const nsAString& aCharset,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aReferrerPolicy,
|
||||
const nsAString& aIntegrity,
|
||||
bool aScriptFromHead) {
|
||||
mDocument->ScriptLoader()->PreloadURI(
|
||||
aURI, aCharset, aType, aCrossOrigin, aIntegrity, aScriptFromHead, false,
|
||||
false, false, true, PreloadReferrerPolicy(aReferrerPolicy));
|
||||
}
|
||||
|
||||
// static
|
||||
void PreloadService::NotifyNodeEvent(nsINode* aNode, bool aSuccess) {
|
||||
if (!aNode->IsInComposedDoc()) {
|
||||
|
||||
@@ -61,6 +61,11 @@ class PreloadService {
|
||||
dom::HTMLLinkElement* aLinkElement, nsContentPolicyType aPolicyType,
|
||||
nsIReferrerInfo* aReferrerInfo);
|
||||
|
||||
void PreloadScript(nsIURI* aURI, const nsAString& aType,
|
||||
const nsAString& aCharset, const nsAString& aCrossOrigin,
|
||||
const nsAString& aReferrerPolicy,
|
||||
const nsAString& aIntegrity, bool aScriptFromHead);
|
||||
|
||||
static void NotifyNodeEvent(nsINode* aNode, bool aSuccess);
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user