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:
Honza Bambas
2020-05-11 14:07:24 +00:00
parent 27e8bc155e
commit ff3778f1fb
12 changed files with 168 additions and 25 deletions

18
dom/script/ScriptKind.h Normal file
View 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

View File

@@ -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);

View File

@@ -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

View File

@@ -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
//////////////////////////////////////////////////////////////

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -25,6 +25,7 @@
#include "mozilla/MozPromise.h"
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include "mozilla/Vector.h"
#include "ScriptKind.h"
class nsIURI;

View File

@@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [
'LoadedScript.h',
'ScriptDecoding.h',
'ScriptElement.h',
'ScriptKind.h',
'ScriptLoader.h',
'ScriptLoadRequest.h',
'ScriptSettings.h',

View File

@@ -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;

View File

@@ -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

View File

@@ -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()) {

View File

@@ -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: