Bug 1618298 - Update PreloadService to provide APIs to integrate with link DOM element, r=smaug

Depends on D67480

Differential Revision: https://phabricator.services.mozilla.com/D67482
This commit is contained in:
Honza Bambas 2020-05-11 14:05:22 +00:00
parent 0f372316ea
commit 2d65fbb465
5 changed files with 160 additions and 2 deletions

View File

@ -1366,7 +1366,8 @@ Document::Document(const char* aContentType)
mGeneration(0),
mCachedTabSizeGeneration(0),
mNextFormNumber(0),
mNextControlNumber(0) {
mNextControlNumber(0),
mPreloadService(this) {
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
SetIsInDocument();

View File

@ -1054,6 +1054,8 @@ void nsHtml5TreeOpExecutor::SetSpeculationBase(const nsAString& aURL) {
DebugOnly<nsresult> rv = NS_NewURI(getter_AddRefs(mSpeculationBaseURI), aURL,
encoding, mDocument->GetDocumentURI());
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to create a URI");
mDocument->Preloads().SetSpeculationBase(mSpeculationBaseURI);
}
void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(

View File

@ -5,6 +5,12 @@
#include "PreloadService.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/StaticPrefs_network.h"
#include "nsNetUtil.h"
namespace mozilla {
bool PreloadService::RegisterPreload(PreloadHashKey* aKey,
@ -34,4 +40,120 @@ already_AddRefed<PreloaderBase> PreloadService::LookupPreload(
return mPreloads.Get(aKey);
}
already_AddRefed<nsIURI> PreloadService::GetPreloadURI(const nsAString& aURL) {
nsIURI* base = BaseURIForPreload();
auto encoding = mDocument->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, encoding, base);
if (NS_FAILED(rv)) {
return nullptr;
}
return uri.forget();
}
already_AddRefed<PreloaderBase> PreloadService::PreloadLinkElement(
dom::HTMLLinkElement* aLinkElement, nsContentPolicyType aPolicyType,
nsIReferrerInfo* aReferrerInfo) {
if (!StaticPrefs::network_preload_experimental()) {
return nullptr;
}
if (!CheckReferrerURIScheme(aReferrerInfo)) {
return nullptr;
}
if (aPolicyType == nsIContentPolicy::TYPE_INVALID) {
NotifyNodeEvent(aLinkElement, false);
return nullptr;
}
nsCOMPtr<nsIURI> uri = aLinkElement->GetURI();
nsAutoString as, charset, crossOrigin, integrity, referrerPolicyAttr, srcset,
sizes, type;
aLinkElement->GetAs(as);
aLinkElement->GetCharset(charset);
aLinkElement->GetCrossOrigin(crossOrigin);
aLinkElement->GetIntegrity(integrity);
aLinkElement->GetReferrerPolicy(referrerPolicyAttr);
PreloadHashKey preloadKey;
// * Branch according the "as" value and build the `preloadKey` value.
if (true) {
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.
preload = LookupPreload(&preloadKey);
if (!preload) {
NotifyNodeEvent(aLinkElement, false);
return nullptr;
}
}
preload->AddLinkPreloadNode(aLinkElement);
return preload.forget();
}
// static
void PreloadService::NotifyNodeEvent(nsINode* aNode, bool aSuccess) {
if (!aNode->IsInComposedDoc()) {
return;
}
// We don't dispatch synchronously since |node| might be in a DocGroup
// that we're not allowed to touch. (Our network request happens in the
// DocGroup of one of the mSources nodes--not necessarily this one).
RefPtr<AsyncEventDispatcher> dispatcher = new AsyncEventDispatcher(
aNode, aSuccess ? NS_LITERAL_STRING("load") : NS_LITERAL_STRING("error"),
CanBubble::eNo);
dispatcher->RequireNodeInDocument();
dispatcher->PostDOMEvent();
}
dom::ReferrerPolicy PreloadService::PreloadReferrerPolicy(
const nsAString& aReferrerPolicy) {
dom::ReferrerPolicy referrerPolicy =
dom::ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy);
if (referrerPolicy == dom::ReferrerPolicy::_empty) {
referrerPolicy = mDocument->GetPreloadReferrerInfo()->ReferrerPolicy();
}
return referrerPolicy;
}
bool PreloadService::CheckReferrerURIScheme(nsIReferrerInfo* aReferrerInfo) {
if (!aReferrerInfo) {
return false;
}
nsCOMPtr<nsIURI> referrer = aReferrerInfo->GetOriginalReferrer();
if (!referrer) {
return false;
}
if (!referrer->SchemeIs("http") && !referrer->SchemeIs("https")) {
return false;
}
return true;
}
nsIURI* PreloadService::BaseURIForPreload() {
nsIURI* documentURI = mDocument->GetDocumentURI();
nsIURI* documentBaseURI = mDocument->GetDocBaseURI();
return (documentURI == documentBaseURI)
? (mSpeculationBaseURI ? mSpeculationBaseURI.get() : documentURI)
: documentBaseURI;
}
} // namespace mozilla

View File

@ -6,11 +6,21 @@
#ifndef PreloadService_h__
#define PreloadService_h__
#include "nsIContentPolicy.h"
#include "nsIReferrerInfo.h"
#include "nsIURI.h"
#include "nsRefPtrHashtable.h"
#include "PreloaderBase.h"
namespace mozilla {
namespace dom {
class HTMLLinkElement;
class Document;
} // namespace dom
/**
* Intended to scope preloads and speculative loads under one roof. This class
* is intended to be a member of dom::Document. Provides registration of
@ -20,6 +30,8 @@ namespace mozilla {
*/
class PreloadService {
public:
explicit PreloadService(dom::Document* aDocument) : mDocument(aDocument) {}
// Called by resource loaders to register a running resource load. This is
// called for a speculative load when it's started the first time, being it
// either regular speculative load or a preload.
@ -42,8 +54,28 @@ class PreloadService {
// registered under the key.
already_AddRefed<PreloaderBase> LookupPreload(PreloadHashKey* aKey) const;
void SetSpeculationBase(nsIURI* aURI) { mSpeculationBaseURI = aURI; }
already_AddRefed<nsIURI> GetPreloadURI(const nsAString& aURL);
already_AddRefed<PreloaderBase> PreloadLinkElement(
dom::HTMLLinkElement* aLinkElement, nsContentPolicyType aPolicyType,
nsIReferrerInfo* aReferrerInfo);
static void NotifyNodeEvent(nsINode* aNode, bool aSuccess);
private:
dom::ReferrerPolicy PreloadReferrerPolicy(const nsAString& aReferrerPolicy);
bool CheckReferrerURIScheme(nsIReferrerInfo* aReferrerInfo);
nsIURI* BaseURIForPreload();
private:
nsRefPtrHashtable<PreloadHashKey, PreloaderBase> mPreloads;
// Raw pointer only, we are intended to be a direct member of dom::Document
dom::Document* mDocument;
// Set by `nsHtml5TreeOpExecutor::SetSpeculationBase`.
nsCOMPtr<nsIURI> mSpeculationBaseURI;
};
} // namespace mozilla

View File

@ -229,7 +229,8 @@ void PreloaderBase::RemoveLinkPreloadNode(nsINode* aNode) {
}
void PreloaderBase::NotifyNodeEvent(nsINode* aNode) {
// * Notify load or error event on the node
PreloadService::NotifyNodeEvent(
aNode, mShouldFireLoadEvent || NS_SUCCEEDED(*mOnStopStatus));
}
nsresult PreloaderBase::AsyncConsume(nsIStreamListener* aListener) {