From 13c353e453f125cba6fe1b042c36f41968df04c4 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Sun, 16 Jul 2023 00:53:29 +0000 Subject: [PATCH] Bug 1839954 - support async defer and module scripts r=emilio https://www.w3.org/2012/01/05-svg-irc#T21-07-03 says that async and defer should be supported as does the specification https://www.w3.org/TR/SVG2/interact.html#ScriptElement https://github.com/w3c/svgwg/issues/918 tracks getting the SVGScriptElement webidl updated. Differential Revision: https://phabricator.services.mozilla.com/D181936 UltraBlame original commit: 21669d226d640b0b8d4b44449e512e0942b113a1 --- dom/base/Document.cpp | 4 +-- dom/base/Document.h | 4 +-- dom/html/HTMLScriptElement.cpp | 24 ++------------- dom/html/HTMLScriptElement.h | 2 +- dom/script/nsIScriptElement.cpp | 29 +++++++++++++++++++ dom/script/nsIScriptElement.h | 7 ++++- dom/svg/SVGScriptElement.cpp | 13 ++++++++- dom/svg/SVGScriptElement.h | 9 +++++- dom/webidl/SVGScriptElement.webidl | 5 ++-- .../execution-timing/136.html.ini | 5 ---- .../tests/svg/interact/scripted/async-01.svg | 29 +++++++++++++++++++ .../tests/svg/interact/scripted/defer-01.svg | 25 ++++++++++++++++ .../tests/svg/interact/scripted/defer.js | 4 +++ .../tests/svg/interact/scripted/log.py | 13 +++++++++ .../tests/svg/interact/scripted/module-01.svg | 20 +++++++++++++ 15 files changed, 156 insertions(+), 37 deletions(-) delete mode 100644 testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/136.html.ini create mode 100644 testing/web-platform/tests/svg/interact/scripted/async-01.svg create mode 100644 testing/web-platform/tests/svg/interact/scripted/defer-01.svg create mode 100644 testing/web-platform/tests/svg/interact/scripted/defer.js create mode 100644 testing/web-platform/tests/svg/interact/scripted/log.py create mode 100644 testing/web-platform/tests/svg/interact/scripted/module-01.svg diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index dea2c37557e14..c7bd71a0207be 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -18114,12 +18114,12 @@ void Document::RecordNavigationTiming(ReadyState aReadyState) { } } -bool Document::ModuleScriptsEnabled() { +bool Document::ModuleScriptsEnabled() const { return nsContentUtils::IsChromeDoc(this) || StaticPrefs::dom_moduleScripts_enabled(); } -bool Document::ImportMapsEnabled() { +bool Document::ImportMapsEnabled() const { return nsContentUtils::IsChromeDoc(this) || StaticPrefs::dom_importMaps_enabled(); } diff --git a/dom/base/Document.h b/dom/base/Document.h index 9b108c1e5a608..484fb6ce0567d 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -4029,9 +4029,9 @@ class Document : public nsINode, mozilla::dom::FeaturePolicy* FeaturePolicy() const; - bool ModuleScriptsEnabled(); + bool ModuleScriptsEnabled() const; - bool ImportMapsEnabled(); + bool ImportMapsEnabled() const; diff --git a/dom/html/HTMLScriptElement.cpp b/dom/html/HTMLScriptElement.cpp index ce0e15a4c2483..824b06851172b 100644 --- a/dom/html/HTMLScriptElement.cpp +++ b/dom/html/HTMLScriptElement.cpp @@ -150,33 +150,13 @@ void HTMLScriptElement::GetScriptCharset(nsAString& charset) { GetCharset(charset); } -void HTMLScriptElement::FreezeExecutionAttrs(Document* aOwnerDoc) { +void HTMLScriptElement::FreezeExecutionAttrs(const Document* aOwnerDoc) { if (mFrozen) { return; } - MOZ_ASSERT((mKind != ScriptKind::eModule) && - (mKind != ScriptKind::eImportMap) && !mAsync && !mDefer && - !mExternal); - - nsAutoString type; - GetScriptType(type); - if (!type.IsEmpty()) { - if (aOwnerDoc->ModuleScriptsEnabled() && - type.LowerCaseEqualsASCII("module")) { - mKind = ScriptKind::eModule; - } - - - - - - if (aOwnerDoc->ImportMapsEnabled() && - type.LowerCaseEqualsASCII("importmap")) { - mKind = ScriptKind::eImportMap; - } - } + DetermineKindFromType(aOwnerDoc); diff --git a/dom/html/HTMLScriptElement.h b/dom/html/HTMLScriptElement.h index 9514cfe605efb..ac1310838437e 100644 --- a/dom/html/HTMLScriptElement.h +++ b/dom/html/HTMLScriptElement.h @@ -32,7 +32,7 @@ class HTMLScriptElement final : public nsGenericHTMLElement, virtual void GetScriptText(nsAString& text) const override; virtual void GetScriptCharset(nsAString& charset) override; - virtual void FreezeExecutionAttrs(Document* aOwnerDoc) override; + virtual void FreezeExecutionAttrs(const Document* aOwnerDoc) override; virtual CORSMode GetCORSMode() const override; virtual mozilla::dom::ReferrerPolicy GetReferrerPolicy() override; diff --git a/dom/script/nsIScriptElement.cpp b/dom/script/nsIScriptElement.cpp index 2b9f020bb2305..413066d8ad155 100644 --- a/dom/script/nsIScriptElement.cpp +++ b/dom/script/nsIScriptElement.cpp @@ -7,10 +7,13 @@ #include "nsIScriptElement.h" +#include "mozilla/dom/Document.h" #include "mozilla/dom/ReferrerPolicyBinding.h" #include "nsIParser.h" #include "nsIWeakReference.h" +using JS::loader::ScriptKind; + void nsIScriptElement::SetCreatorParser(nsIParser* aParser) { mCreatorParser = do_GetWeakReference(aParser); } @@ -51,3 +54,29 @@ already_AddRefed nsIScriptElement::GetCreatorParser() { mozilla::dom::ReferrerPolicy nsIScriptElement::GetReferrerPolicy() { return mozilla::dom::ReferrerPolicy::_empty; } + +void nsIScriptElement::DetermineKindFromType( + const mozilla::dom::Document* aOwnerDoc) { + MOZ_ASSERT((mKind != ScriptKind::eModule) && + (mKind != ScriptKind::eImportMap) && !mAsync && !mDefer && + !mExternal); + + nsAutoString type; + GetScriptType(type); + + if (!type.IsEmpty()) { + if (aOwnerDoc->ModuleScriptsEnabled() && + type.LowerCaseEqualsASCII("module")) { + mKind = ScriptKind::eModule; + } + + + + + + if (aOwnerDoc->ImportMapsEnabled() && + type.LowerCaseEqualsASCII("importmap")) { + mKind = ScriptKind::eImportMap; + } + } +} diff --git a/dom/script/nsIScriptElement.h b/dom/script/nsIScriptElement.h index 75b6c4f404d52..1fb6bd6fad78d 100644 --- a/dom/script/nsIScriptElement.h +++ b/dom/script/nsIScriptElement.h @@ -106,7 +106,7 @@ class nsIScriptElement : public nsIScriptLoaderObserver { - virtual void FreezeExecutionAttrs(mozilla::dom::Document*) = 0; + virtual void FreezeExecutionAttrs(const mozilla::dom::Document*) = 0; @@ -279,6 +279,11 @@ class nsIScriptElement : public nsIScriptLoaderObserver { + void DetermineKindFromType(const mozilla::dom::Document* aOwnerDoc); + + + + uint32_t mLineNumber; diff --git a/dom/svg/SVGScriptElement.cpp b/dom/svg/SVGScriptElement.cpp index 46f655b8b2e83..bc90bb1e8b36b 100644 --- a/dom/svg/SVGScriptElement.cpp +++ b/dom/svg/SVGScriptElement.cpp @@ -14,6 +14,8 @@ NS_IMPL_NS_NEW_SVG_ELEMENT_CHECK_PARSER(Script) +using JS::loader::ScriptKind; + namespace mozilla::dom { JSObject* SVGScriptElement::WrapNode(JSContext* aCx, @@ -106,11 +108,14 @@ void SVGScriptElement::GetScriptCharset(nsAString& charset) { charset.Truncate(); } -void SVGScriptElement::FreezeExecutionAttrs(Document* aOwnerDoc) { +void SVGScriptElement::FreezeExecutionAttrs(const Document* aOwnerDoc) { if (mFrozen) { return; } + + DetermineKindFromType(aOwnerDoc); + if (mStringAttributes[HREF].IsExplicitlySet() || mStringAttributes[XLINK_HREF].IsExplicitlySet()) { @@ -150,6 +155,12 @@ void SVGScriptElement::FreezeExecutionAttrs(Document* aOwnerDoc) { mExternal = true; } + bool async = (mExternal || mKind == ScriptKind::eModule) && Async(); + bool defer = mExternal && Defer(); + + mDefer = !async && defer; + mAsync = async; + mFrozen = true; } diff --git a/dom/svg/SVGScriptElement.h b/dom/svg/SVGScriptElement.h index 45db7de017e75..f7db3deb2fac4 100644 --- a/dom/svg/SVGScriptElement.h +++ b/dom/svg/SVGScriptElement.h @@ -41,7 +41,7 @@ class SVGScriptElement final : public SVGScriptElementBase, void GetScriptText(nsAString& text) const override; void GetScriptCharset(nsAString& charset) override; - void FreezeExecutionAttrs(Document* aOwnerDoc) override; + void FreezeExecutionAttrs(const Document* aOwnerDoc) override; CORSMode GetCORSMode() const override; @@ -59,6 +59,13 @@ class SVGScriptElement final : public SVGScriptElementBase, void GetType(nsAString& aType); void SetType(const nsAString& aType, ErrorResult& rv); + bool Async() { return mForceAsync || GetBoolAttr(nsGkAtoms::async); } + void SetAsync(bool aValue) { + mForceAsync = false; + SetBoolAttr(nsGkAtoms::async, aValue); + } + bool Defer() { return GetBoolAttr(nsGkAtoms::defer); } + void SetDefer(bool aDefer) { SetBoolAttr(nsGkAtoms::defer, aDefer); } void GetCrossOrigin(nsAString& aCrossOrigin); void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError); already_AddRefed Href(); diff --git a/dom/webidl/SVGScriptElement.webidl b/dom/webidl/SVGScriptElement.webidl index 7baa74ac16c24..914bfd606ed36 100644 --- a/dom/webidl/SVGScriptElement.webidl +++ b/dom/webidl/SVGScriptElement.webidl @@ -14,8 +14,9 @@ interface SVGScriptElement : SVGElement { [SetterThrows] attribute DOMString type; - - // CORS attribute + // See https://github.com/w3c/svgwg/issues/918 + attribute boolean async; + attribute boolean defer; [SetterThrows] attribute DOMString? crossOrigin; }; diff --git a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/136.html.ini b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/136.html.ini deleted file mode 100644 index e85710b9a2e44..0000000000000 --- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/execution-timing/136.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[136.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [scheduler: DOM added external SVG script, force-async? ] - expected: FAIL diff --git a/testing/web-platform/tests/svg/interact/scripted/async-01.svg b/testing/web-platform/tests/svg/interact/scripted/async-01.svg new file mode 100644 index 0000000000000..a56ef34ae547a --- /dev/null +++ b/testing/web-platform/tests/svg/interact/scripted/async-01.svg @@ -0,0 +1,29 @@ + + + async on script + + + + + + + + + + + diff --git a/testing/web-platform/tests/svg/interact/scripted/defer-01.svg b/testing/web-platform/tests/svg/interact/scripted/defer-01.svg new file mode 100644 index 0000000000000..93e54c3f0ef64 --- /dev/null +++ b/testing/web-platform/tests/svg/interact/scripted/defer-01.svg @@ -0,0 +1,25 @@ + + + defer on script + + + + + + + + + + + diff --git a/testing/web-platform/tests/svg/interact/scripted/defer.js b/testing/web-platform/tests/svg/interact/scripted/defer.js new file mode 100644 index 0000000000000..0c0bfb1c23ea7 --- /dev/null +++ b/testing/web-platform/tests/svg/interact/scripted/defer.js @@ -0,0 +1,4 @@ +t.step(() => { + assert_equals(script_run_status, "deferred", "script run status"); +}); +t.done(); diff --git a/testing/web-platform/tests/svg/interact/scripted/log.py b/testing/web-platform/tests/svg/interact/scripted/log.py new file mode 100644 index 0000000000000..2a6cc3302962e --- /dev/null +++ b/testing/web-platform/tests/svg/interact/scripted/log.py @@ -0,0 +1,13 @@ +import time + +def main(request, response): + response.headers.append(b"Content-Type", b"text/javascript") + try: + script_id = int(request.GET.first(b"id")) + delay = int(request.GET.first(b"sec")) + except: + response.set_error(400, u"Invalid parameter") + + time.sleep(int(delay)) + + return u"log('%s')" % script_id diff --git a/testing/web-platform/tests/svg/interact/scripted/module-01.svg b/testing/web-platform/tests/svg/interact/scripted/module-01.svg new file mode 100644 index 0000000000000..ea70391e922aa --- /dev/null +++ b/testing/web-platform/tests/svg/interact/scripted/module-01.svg @@ -0,0 +1,20 @@ + + + script type="module" + + + + + + + +