HTMLScriptElement.cpp (12954B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/HTMLScriptElement.h" 8 9 #include "mozilla/StaticPrefs_dom.h" 10 #include "mozilla/dom/Document.h" 11 #include "mozilla/dom/FetchPriority.h" 12 #include "mozilla/dom/HTMLScriptElementBinding.h" 13 #include "mozilla/dom/TrustedTypeUtils.h" 14 #include "mozilla/dom/TrustedTypesConstants.h" 15 #include "nsAttrValue.h" 16 #include "nsAttrValueOrString.h" 17 #include "nsContentUtils.h" 18 #include "nsDOMJSUtils.h" 19 #include "nsDOMTokenList.h" 20 #include "nsError.h" 21 #include "nsGenericHTMLElement.h" 22 #include "nsGkAtoms.h" 23 #include "nsIScriptContext.h" 24 #include "nsIScriptError.h" 25 #include "nsIScriptGlobalObject.h" 26 #include "nsISupportsImpl.h" 27 #include "nsNetUtil.h" 28 #include "nsServiceManagerUtils.h" 29 #include "nsStyleConsts.h" 30 #include "nsTArray.h" 31 #include "nsUnicharUtils.h" // for nsCaseInsensitiveStringComparator() 32 33 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Script) 34 35 using JS::loader::ScriptKind; 36 37 namespace mozilla::dom { 38 39 JSObject* HTMLScriptElement::WrapNode(JSContext* aCx, 40 JS::Handle<JSObject*> aGivenProto) { 41 return HTMLScriptElement_Binding::Wrap(aCx, this, aGivenProto); 42 } 43 44 HTMLScriptElement::HTMLScriptElement( 45 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, 46 FromParser aFromParser) 47 : nsGenericHTMLElement(std::move(aNodeInfo)), ScriptElement(aFromParser) { 48 AddMutationObserver(this); 49 } 50 51 HTMLScriptElement::~HTMLScriptElement() = default; 52 53 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLScriptElement, 54 nsGenericHTMLElement, 55 nsIScriptLoaderObserver, 56 nsIScriptElement, 57 nsIMutationObserver) 58 59 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLScriptElement, nsGenericHTMLElement, 60 mBlocking) 61 62 nsresult HTMLScriptElement::BindToTree(BindContext& aContext, 63 nsINode& aParent) { 64 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent); 65 NS_ENSURE_SUCCESS(rv, rv); 66 67 if (IsInComposedDoc()) { 68 MaybeProcessScript(nullptr /* aParser */); 69 } 70 71 return NS_OK; 72 } 73 74 bool HTMLScriptElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 75 const nsAString& aValue, 76 nsIPrincipal* aMaybeScriptedPrincipal, 77 nsAttrValue& aResult) { 78 if (aNamespaceID == kNameSpaceID_None) { 79 if (aAttribute == nsGkAtoms::crossorigin) { 80 ParseCORSValue(aValue, aResult); 81 return true; 82 } 83 84 if (aAttribute == nsGkAtoms::integrity) { 85 aResult.ParseStringOrAtom(aValue); 86 return true; 87 } 88 89 if (aAttribute == nsGkAtoms::fetchpriority) { 90 ParseFetchPriority(aValue, aResult); 91 return true; 92 } 93 94 if (aAttribute == nsGkAtoms::blocking && 95 StaticPrefs::dom_element_blocking_enabled()) { 96 aResult.ParseAtomArray(aValue); 97 return true; 98 } 99 } 100 101 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, 102 aMaybeScriptedPrincipal, aResult); 103 } 104 105 nsresult HTMLScriptElement::Clone(dom::NodeInfo* aNodeInfo, 106 nsINode** aResult) const { 107 *aResult = nullptr; 108 109 HTMLScriptElement* it = new (aNodeInfo->NodeInfoManager()) 110 HTMLScriptElement(do_AddRef(aNodeInfo), NOT_FROM_PARSER); 111 112 nsCOMPtr<nsINode> kungFuDeathGrip = it; 113 nsresult rv = const_cast<HTMLScriptElement*>(this)->CopyInnerTo(it); 114 NS_ENSURE_SUCCESS(rv, rv); 115 116 // The clone should be marked evaluated if we are. 117 it->mAlreadyStarted = mAlreadyStarted; 118 it->mLineNumber = mLineNumber; 119 it->mMalformed = mMalformed; 120 121 kungFuDeathGrip.swap(*aResult); 122 123 return NS_OK; 124 } 125 126 void HTMLScriptElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 127 const nsAttrValue* aValue, 128 const nsAttrValue* aOldValue, 129 nsIPrincipal* aMaybeScriptedPrincipal, 130 bool aNotify) { 131 if (nsGkAtoms::async == aName && kNameSpaceID_None == aNamespaceID) { 132 mForceAsync = false; 133 } 134 if (nsGkAtoms::src == aName && kNameSpaceID_None == aNamespaceID) { 135 mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal( 136 this, nsAttrValueOrString(aValue).String(), aMaybeScriptedPrincipal); 137 } 138 return nsGenericHTMLElement::AfterSetAttr( 139 aNamespaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify); 140 } 141 142 void HTMLScriptElement::GetInnerHTML(nsAString& aInnerHTML, 143 OOMReporter& aError) { 144 if (!nsContentUtils::GetNodeTextContent(this, false, aInnerHTML, fallible)) { 145 aError.ReportOOM(); 146 } 147 } 148 149 void HTMLScriptElement::SetInnerHTMLTrusted(const nsAString& aInnerHTML, 150 nsIPrincipal* aSubjectPrincipal, 151 ErrorResult& aError) { 152 // aInnerHTML is trusted HTML, but not trusted script so we must not preserve 153 // trustworthiness. 154 aError = nsContentUtils::SetNodeTextContent(this, aInnerHTML, true); 155 } 156 157 void HTMLScriptElement::GetText(nsAString& aValue, ErrorResult& aRv) const { 158 if (!nsContentUtils::GetNodeTextContent(this, false, aValue, fallible)) { 159 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 160 } 161 } 162 163 void HTMLScriptElement::GetText(OwningTrustedScriptOrString& aValue, 164 ErrorResult& aRv) const { 165 GetText(aValue.SetAsString(), aRv); 166 } 167 168 void HTMLScriptElement::SetText(const TrustedScriptOrString& aValue, 169 nsIPrincipal* aSubjectPrincipal, 170 ErrorResult& aRv) { 171 constexpr nsLiteralString sink = u"HTMLScriptElement text"_ns; 172 173 Maybe<nsAutoString> compliantStringHolder; 174 const nsAString* compliantString = 175 TrustedTypeUtils::GetTrustedTypesCompliantString( 176 aValue, sink, kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal, 177 compliantStringHolder, aRv); 178 if (aRv.Failed()) { 179 return; 180 } 181 aRv = nsContentUtils::SetNodeTextContent( 182 this, *compliantString, true, 183 MutationEffectOnScript::KeepTrustWorthiness); 184 } 185 186 void HTMLScriptElement::GetInnerText( 187 OwningTrustedScriptOrNullIsEmptyString& aValue, ErrorResult& aError) { 188 DOMString innerText; 189 nsGenericHTMLElement::GetInnerText(innerText, aError); 190 if (aError.Failed()) { 191 return; 192 } 193 aValue.SetAsNullIsEmptyString() = innerText.AsAString(); 194 } 195 196 void HTMLScriptElement::SetInnerText( 197 const TrustedScriptOrNullIsEmptyString& aValue, 198 nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) { 199 constexpr nsLiteralString sink = u"HTMLScriptElement innerText"_ns; 200 201 Maybe<nsAutoString> compliantStringHolder; 202 const nsAString* compliantString = 203 TrustedTypeUtils::GetTrustedTypesCompliantString( 204 aValue, sink, kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal, 205 compliantStringHolder, aError); 206 if (aError.Failed()) { 207 return; 208 } 209 nsGenericHTMLElement::SetInnerTextInternal( 210 *compliantString, MutationEffectOnScript::KeepTrustWorthiness); 211 } 212 213 void HTMLScriptElement::GetTrustedScriptOrStringTextContent( 214 Nullable<OwningTrustedScriptOrString>& aTextContent, 215 mozilla::OOMReporter& aError) { 216 FragmentOrElement::GetTextContentInternal( 217 aTextContent.SetValue().SetAsString(), aError); 218 } 219 220 void HTMLScriptElement::SetTrustedScriptOrStringTextContent( 221 const Nullable<TrustedScriptOrString>& aTextContent, 222 nsIPrincipal* aSubjectPrincipal, mozilla::ErrorResult& aError) { 223 constexpr nsLiteralString sink = u"HTMLScriptElement textContent"_ns; 224 Maybe<nsAutoString> compliantStringHolder; 225 if (aTextContent.IsNull()) { 226 Nullable<TrustedScriptOrString> emptyString; 227 emptyString.SetValue().SetStringLiteral(u""); 228 SetTrustedScriptOrStringTextContent(emptyString, aSubjectPrincipal, aError); 229 return; 230 } 231 const nsAString* compliantString = 232 TrustedTypeUtils::GetTrustedTypesCompliantString( 233 aTextContent.Value(), sink, kTrustedTypesOnlySinkGroup, *this, 234 aSubjectPrincipal, compliantStringHolder, aError); 235 if (aError.Failed()) { 236 return; 237 } 238 SetTextContentInternal(*compliantString, aSubjectPrincipal, aError, 239 MutationEffectOnScript::KeepTrustWorthiness); 240 } 241 242 void HTMLScriptElement::GetSrc(OwningTrustedScriptURLOrUSVString& aSrc) { 243 GetURIAttr(nsGkAtoms::src, nullptr, aSrc.SetAsUSVString()); 244 } 245 246 void HTMLScriptElement::SetSrc(const TrustedScriptURLOrUSVString& aSrc, 247 nsIPrincipal* aSubjectPrincipal, 248 ErrorResult& aRv) { 249 constexpr nsLiteralString sink = u"HTMLScriptElement src"_ns; 250 251 Maybe<nsAutoString> compliantStringHolder; 252 const nsAString* compliantString = 253 TrustedTypeUtils::GetTrustedTypesCompliantString( 254 aSrc, sink, kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal, 255 compliantStringHolder, aRv); 256 if (aRv.Failed()) { 257 return; 258 } 259 260 SetHTMLAttr(nsGkAtoms::src, *compliantString, aSubjectPrincipal, aRv); 261 } 262 263 // variation of this code in SVGScriptElement - check if changes 264 // need to be transfered when modifying 265 266 void HTMLScriptElement::GetScriptText(nsAString& text) const { 267 GetText(text, IgnoreErrors()); 268 } 269 270 void HTMLScriptElement::GetScriptCharset(nsAString& charset) { 271 GetCharset(charset); 272 } 273 274 void HTMLScriptElement::FreezeExecutionAttrs(const Document* aOwnerDoc) { 275 if (mFrozen) { 276 return; 277 } 278 279 // Determine whether this is a(n) classic/module/importmap script. 280 DetermineKindFromType(aOwnerDoc); 281 282 // variation of this code in SVGScriptElement - check if changes 283 // need to be transfered when modifying. Note that we don't use GetSrc here 284 // because it will return the base URL when the attr value is "". 285 nsAutoString src; 286 if (GetAttr(nsGkAtoms::src, src)) { 287 SourceLocation loc{OwnerDoc()->GetDocumentURI(), GetScriptLineNumber(), 288 GetScriptColumnNumber().oneOriginValue()}; 289 // Empty src should be treated as invalid URL. 290 if (!src.IsEmpty()) { 291 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(mUri), src, 292 OwnerDoc(), GetBaseURI()); 293 294 if (!mUri) { 295 AutoTArray<nsString, 2> params = {u"src"_ns, src}; 296 297 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "HTML"_ns, 298 OwnerDoc(), 299 nsContentUtils::eDOM_PROPERTIES, 300 "ScriptSourceInvalidUri", params, loc); 301 } 302 } else { 303 AutoTArray<nsString, 1> params = {u"src"_ns}; 304 nsContentUtils::ReportToConsole( 305 nsIScriptError::warningFlag, "HTML"_ns, OwnerDoc(), 306 nsContentUtils::eDOM_PROPERTIES, "ScriptSourceEmpty", params, loc); 307 } 308 309 // At this point mUri will be null for invalid URLs. 310 mExternal = true; 311 } 312 313 bool async = (mExternal || mKind == ScriptKind::eModule) && Async(); 314 bool defer = mExternal && Defer(); 315 316 mDefer = !async && defer; 317 mAsync = async; 318 319 mFrozen = true; 320 } 321 322 CORSMode HTMLScriptElement::GetCORSMode() const { 323 return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); 324 } 325 326 FetchPriority HTMLScriptElement::GetFetchPriority() const { 327 return Element::GetFetchPriority(); 328 } 329 330 mozilla::dom::ReferrerPolicy HTMLScriptElement::GetReferrerPolicy() { 331 return GetReferrerPolicyAsEnum(); 332 } 333 334 bool HTMLScriptElement::HasExternalScriptContent() { 335 return mFrozen ? mExternal : HasAttr(nsGkAtoms::src); 336 } 337 338 // https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports 339 /* static */ 340 bool HTMLScriptElement::Supports(const GlobalObject& aGlobal, 341 const nsAString& aType) { 342 nsAutoString type(aType); 343 return aType.EqualsLiteral("classic") || aType.EqualsLiteral("module") || 344 345 aType.EqualsLiteral("importmap"); 346 } 347 348 nsDOMTokenList* HTMLScriptElement::Blocking() { 349 if (!mBlocking) { 350 mBlocking = 351 new nsDOMTokenList(this, nsGkAtoms::blocking, sSupportedBlockingValues); 352 } 353 return mBlocking; 354 } 355 356 bool HTMLScriptElement::IsPotentiallyRenderBlocking() { 357 return BlockingContainsRender(); 358 359 // TODO: handle implicitly potentially render blocking 360 // https://html.spec.whatwg.org/#implicitly-potentially-render-blocking 361 // A script element el is implicitly potentially render-blocking if el's type 362 // is "classic", el is parser-inserted, and el does not have an async or defer 363 // attribute. 364 } 365 366 } // namespace mozilla::dom