Issue #618 - (async) Implement async attribute for inline module scripts.

This commit does several things:
- Moves the pref check from ScriptLoader to ns[I]Document so it can be called on
the document.
- Changes the atrribute freezing function to a better name that takes the
document as a parameter.
- Sets the proper async/defer attributes on HTML script elements based on
keywords and whether they are module scripts or not.
This commit is contained in:
Moonchild 2020-08-25 07:06:43 +00:00 • committed by Roy Tam
commit 3781c4a6dd
11 changed files with 73 additions and 41 deletions

View file

@ -12446,3 +12446,16 @@ nsIDocument::GetSelection(ErrorResult& aRv)
return nsGlobalWindow::Cast(window)->GetSelection(aRv); return nsGlobalWindow::Cast(window)->GetSelection(aRv);
} }
bool
nsIDocument::ModuleScriptsEnabled()
{
static bool sEnabledForContent = false;
static bool sCachedPref = false;
if (!sCachedPref) {
sCachedPref = true;
Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
}
return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
}

View file

@ -2851,6 +2851,8 @@ public:
return mIsWebComponentsEnabled; return mIsWebComponentsEnabled;
} }
bool ModuleScriptsEnabled();
protected: protected:
bool GetUseCounter(mozilla::UseCounter aUseCounter) bool GetUseCounter(mozilla::UseCounter aUseCounter)
{ {

View file

@ -280,12 +280,20 @@ HTMLScriptElement::GetScriptCharset(nsAString& charset)
} }
void void
HTMLScriptElement::FreezeUriAsyncDefer() HTMLScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
{ {
if (mFrozen) { if (mFrozen) {
return; return;
} }
MOZ_ASSERT(!mIsModule && !mAsync && !mDefer && !mExternal);
// Determine whether this is a classic script or a module script.
nsAutoString type;
GetScriptType(type);
mIsModule = aOwnerDoc->ModuleScriptsEnabled() &&
!type.IsEmpty() && type.LowerCaseEqualsASCII("module");
// variation of this code in nsSVGScriptElement - check if changes // variation of this code in nsSVGScriptElement - check if changes
// need to be transfered when modifying. Note that we don't use GetSrc here // need to be transfered when modifying. Note that we don't use GetSrc here
// because it will return the base URL when the attr value is "". // because it will return the base URL when the attr value is "".
@ -300,15 +308,14 @@ HTMLScriptElement::FreezeUriAsyncDefer()
// At this point mUri will be null for invalid URLs. // At this point mUri will be null for invalid URLs.
mExternal = true; mExternal = true;
bool defer, async;
GetAsync(&async);
GetDefer(&defer);
mDefer = !async && defer;
mAsync = async;
} }
bool defer, async;
GetAsync(&async);
mAsync = (mExternal || mIsModule) && async;
GetDefer(&defer);
mDefer = mExternal && !async && defer;
mFrozen = true; mFrozen = true;
} }

View file

@ -41,7 +41,7 @@ public:
virtual bool GetScriptType(nsAString& type) override; virtual bool GetScriptType(nsAString& type) override;
virtual void GetScriptText(nsAString& text) override; virtual void GetScriptText(nsAString& text) override;
virtual void GetScriptCharset(nsAString& charset) override; virtual void GetScriptCharset(nsAString& charset) override;
virtual void FreezeUriAsyncDefer() override; virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) override;
virtual CORSMode GetCORSMode() const override; virtual CORSMode GetCORSMode() const override;
// nsIContent // nsIContent

View file

@ -128,11 +128,11 @@ ScriptElement::MaybeProcessScript()
return false; return false;
} }
FreezeUriAsyncDefer(); nsIDocument* ownerDoc = cont->OwnerDoc();
FreezeExecutionAttrs(ownerDoc);
mAlreadyStarted = true; mAlreadyStarted = true;
nsIDocument* ownerDoc = cont->OwnerDoc();
nsCOMPtr<nsIParser> parser = ((nsIScriptElement*) this)->GetCreatorParser(); nsCOMPtr<nsIParser> parser = ((nsIScriptElement*) this)->GetCreatorParser();
if (parser) { if (parser) {
nsCOMPtr<nsIContentSink> sink = parser->GetContentSink(); nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();

View file

@ -390,19 +390,6 @@ ScriptLoader::CheckContentPolicy(nsIDocument* aDocument,
return NS_OK; return NS_OK;
} }
bool
ScriptLoader::ModuleScriptsEnabled()
{
static bool sEnabledForContent = false;
static bool sCachedPref = false;
if (!sCachedPref) {
sCachedPref = true;
Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
}
return nsContentUtils::IsChromeDoc(mDocument) || sEnabledForContent;
}
bool bool
ScriptLoader::ModuleMapContainsURL(nsIURI* aURL) const ScriptLoader::ModuleMapContainsURL(nsIURI* aURL) const
{ {
@ -1227,14 +1214,12 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement); nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);
// Determine whether this is a classic script or a module script.
nsAutoString type; nsAutoString type;
bool hasType = aElement->GetScriptType(type); bool hasType = aElement->GetScriptType(type);
ScriptKind scriptKind = ScriptKind::Classic;
if (ModuleScriptsEnabled() && ScriptKind scriptKind = aElement->GetScriptIsModule() ?
!type.IsEmpty() && type.LowerCaseEqualsASCII("module")) { ScriptKind::Module :
scriptKind = ScriptKind::Module; ScriptKind::Classic;
}
// Step 13. Check that the script is not an eventhandler // Step 13. Check that the script is not an eventhandler
if (IsScriptEventHandler(scriptKind, scriptContent)) { if (IsScriptEventHandler(scriptKind, scriptContent)) {
@ -1268,7 +1253,7 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
// the nomodule attribute will be ignored". // the nomodule attribute will be ignored".
// "The nomodule attribute must not be specified on module scripts (and will // "The nomodule attribute must not be specified on module scripts (and will
// be ignored if it is)." // be ignored if it is)."
if (ModuleScriptsEnabled() && if (mDocument->ModuleScriptsEnabled() &&
scriptKind == ScriptKind::Classic && scriptKind == ScriptKind::Classic &&
scriptContent->IsHTMLElement() && scriptContent->IsHTMLElement() &&
scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) { scriptContent->HasAttr(kNameSpaceID_None, nsGkAtoms::nomodule)) {
@ -1480,6 +1465,12 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
request->mIsInline = true; request->mIsInline = true;
request->mLineNo = aElement->GetScriptLineNumber(); request->mLineNo = aElement->GetScriptLineNumber();
// Only the 'async' attribute is heeded on an inline module script and
// inline classic scripts ignore both these attributes.
MOZ_ASSERT(!aElement->GetScriptDeferred());
MOZ_ASSERT_IF(!request->IsModuleRequest(), !aElement->GetScriptAsync());
request->SetScriptMode(false, aElement->GetScriptAsync());
if (request->IsModuleRequest()) { if (request->IsModuleRequest()) {
ModuleLoadRequest* modReq = request->AsModuleRequest(); ModuleLoadRequest* modReq = request->AsModuleRequest();
modReq->mBaseURL = mDocument->GetDocBaseURI(); modReq->mBaseURL = mDocument->GetDocBaseURI();
@ -2601,7 +2592,7 @@ ScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
} }
// TODO: Preload module scripts. // TODO: Preload module scripts.
if (ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) { if (mDocument->ModuleScriptsEnabled() && aType.LowerCaseEqualsASCII("module")) {
return; return;
} }

View file

@ -606,8 +606,6 @@ private:
JS::SourceBufferHolder GetScriptSource(ScriptLoadRequest* aRequest, JS::SourceBufferHolder GetScriptSource(ScriptLoadRequest* aRequest,
nsAutoString& inlineData); nsAutoString& inlineData);
bool ModuleScriptsEnabled();
void SetModuleFetchStarted(ModuleLoadRequest *aRequest); void SetModuleFetchStarted(ModuleLoadRequest *aRequest);
void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest, void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest,
nsresult aResult); nsresult aResult);

View file

@ -13,6 +13,7 @@
#include "nsIScriptLoaderObserver.h" #include "nsIScriptLoaderObserver.h"
#include "nsWeakPtr.h" #include "nsWeakPtr.h"
#include "nsIParser.h" #include "nsIParser.h"
#include "nsIDocument.h"
#include "nsContentCreatorFunctions.h" #include "nsContentCreatorFunctions.h"
#include "nsIDOMHTMLScriptElement.h" #include "nsIDOMHTMLScriptElement.h"
#include "mozilla/CORSMode.h" #include "mozilla/CORSMode.h"
@ -37,6 +38,7 @@ public:
mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER || mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER ||
aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT), aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT),
mFrozen(false), mFrozen(false),
mIsModule(false),
mDefer(false), mDefer(false),
mAsync(false), mAsync(false),
mExternal(false), mExternal(false),
@ -73,11 +75,25 @@ public:
virtual void GetScriptCharset(nsAString& charset) = 0; virtual void GetScriptCharset(nsAString& charset) = 0;
/** /**
* Freezes the return values of GetScriptDeferred(), GetScriptAsync() and * Freezes the return values of the following methods so that subsequent
* GetScriptURI() so that subsequent modifications to the attributes don't * modifications to the attributes don't change execution behavior:
* change execution behavior. * - GetScriptIsModule()
* - GetScriptDeferred()
* - GetScriptAsync()
* - GetScriptURI()
* - GetScriptExternal()
*/ */
virtual void FreezeUriAsyncDefer() = 0; virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) = 0;
/**
* Is the script a module script?
* Currently only supported by HTML scripts.
*/
bool GetScriptIsModule()
{
NS_PRECONDITION(mFrozen, "Execution attributes not yet frozen: Not ready for this call!");
return mIsModule;
}
/** /**
* Is the script deferred. Currently only supported by HTML scripts. * Is the script deferred. Currently only supported by HTML scripts.
@ -292,6 +308,11 @@ protected:
*/ */
bool mFrozen; bool mFrozen;
/**
* The effective moduleness.
*/
bool mIsModule;
/** /**
* The effective deferredness. * The effective deferredness.
*/ */

View file

@ -137,7 +137,7 @@ SVGScriptElement::GetScriptCharset(nsAString& charset)
} }
void void
SVGScriptElement::FreezeUriAsyncDefer() SVGScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
{ {
if (mFrozen) { if (mFrozen) {
return; return;

View file

@ -44,7 +44,7 @@ public:
virtual bool GetScriptType(nsAString& type) override; virtual bool GetScriptType(nsAString& type) override;
virtual void GetScriptText(nsAString& text) override; virtual void GetScriptText(nsAString& text) override;
virtual void GetScriptCharset(nsAString& charset) override; virtual void GetScriptCharset(nsAString& charset) override;
virtual void FreezeUriAsyncDefer() override; virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) override;
virtual CORSMode GetCORSMode() const override; virtual CORSMode GetCORSMode() const override;
// ScriptElement // ScriptElement

View file

@ -1076,7 +1076,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node); nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node);
NS_ASSERTION(sele, "Node didn't QI to script."); NS_ASSERTION(sele, "Node didn't QI to script.");
sele->SetScriptLineNumber(mFour.integer); sele->SetScriptLineNumber(mFour.integer);
sele->FreezeUriAsyncDefer(); sele->FreezeExecutionAttrs(node->OwnerDoc());
return NS_OK; return NS_OK;
} }
case eTreeOpSvgLoad: { case eTreeOpSvgLoad: {