Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / base / LinkStyle.cpp
blob61eb5fb6ea7cfe6a32c71d92e562ae2ca6765541
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/. */
7 /*
8 * A base class which implements nsIStyleSheetLinkingElement and can
9 * be subclassed by various content nodes that want to load
10 * stylesheets (<style>, <link>, processing instructions, etc).
13 #include "mozilla/dom/LinkStyle.h"
15 #include "mozilla/StyleSheet.h"
16 #include "mozilla/StyleSheetInlines.h"
17 #include "mozilla/css/Loader.h"
18 #include "mozilla/dom/Element.h"
19 #include "mozilla/dom/FragmentOrElement.h"
20 #include "mozilla/dom/HTMLLinkElement.h"
21 #include "mozilla/dom/HTMLStyleElement.h"
22 #include "mozilla/dom/SVGStyleElement.h"
23 #include "mozilla/dom/ShadowRoot.h"
24 #include "mozilla/dom/SRILogHelper.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/StaticPrefs_dom.h"
27 #include "nsIContent.h"
28 #include "mozilla/dom/Document.h"
29 #include "nsUnicharUtils.h"
30 #include "nsCRT.h"
31 #include "nsXPCOMCIDInternal.h"
32 #include "nsUnicharInputStream.h"
33 #include "nsContentUtils.h"
34 #include "nsStyleUtil.h"
35 #include "nsQueryObject.h"
37 namespace mozilla::dom {
39 LinkStyle::SheetInfo::SheetInfo(
40 const Document& aDocument, nsIContent* aContent,
41 already_AddRefed<nsIURI> aURI,
42 already_AddRefed<nsIPrincipal> aTriggeringPrincipal,
43 already_AddRefed<nsIReferrerInfo> aReferrerInfo,
44 mozilla::CORSMode aCORSMode, const nsAString& aTitle,
45 const nsAString& aMedia, const nsAString& aIntegrity,
46 const nsAString& aNonce, HasAlternateRel aHasAlternateRel,
47 IsInline aIsInline, IsExplicitlyEnabled aIsExplicitlyEnabled,
48 FetchPriority aFetchPriority)
49 : mContent(aContent),
50 mURI(aURI),
51 mTriggeringPrincipal(aTriggeringPrincipal),
52 mReferrerInfo(aReferrerInfo),
53 mCORSMode(aCORSMode),
54 mTitle(aTitle),
55 mMedia(aMedia),
56 mIntegrity(aIntegrity),
57 mNonce(aNonce),
58 mFetchPriority(aFetchPriority),
59 mHasAlternateRel(aHasAlternateRel == HasAlternateRel::Yes),
60 mIsInline(aIsInline == IsInline::Yes),
61 mIsExplicitlyEnabled(aIsExplicitlyEnabled) {
62 MOZ_ASSERT(!mIsInline || aContent);
63 MOZ_ASSERT_IF(aContent, aContent->OwnerDoc() == &aDocument);
64 MOZ_ASSERT(mReferrerInfo);
65 MOZ_ASSERT(mIntegrity.IsEmpty() || !mIsInline,
66 "Integrity only applies to <link>");
69 LinkStyle::SheetInfo::~SheetInfo() = default;
70 LinkStyle::LinkStyle() = default;
72 LinkStyle::~LinkStyle() { LinkStyle::SetStyleSheet(nullptr); }
74 StyleSheet* LinkStyle::GetSheetForBindings() const {
75 if (mStyleSheet && mStyleSheet->IsComplete()) {
76 return mStyleSheet;
78 return nullptr;
81 void LinkStyle::GetTitleAndMediaForElement(const Element& aSelf,
82 nsString& aTitle, nsString& aMedia) {
83 // Only honor title as stylesheet name for elements in the document (that is,
84 // ignore for Shadow DOM), per [1] and [2]. See [3].
86 // [1]: https://html.spec.whatwg.org/#attr-link-title
87 // [2]: https://html.spec.whatwg.org/#attr-style-title
88 // [3]: https://github.com/w3c/webcomponents/issues/535
89 if (aSelf.IsInUncomposedDoc()) {
90 aSelf.GetAttr(nsGkAtoms::title, aTitle);
91 aTitle.CompressWhitespace();
94 aSelf.GetAttr(nsGkAtoms::media, aMedia);
95 // The HTML5 spec is formulated in terms of the CSSOM spec, which specifies
96 // that media queries should be ASCII lowercased during serialization.
98 // FIXME(emilio): How does it matter? This is going to be parsed anyway, CSS
99 // should take care of serializing it properly.
100 nsContentUtils::ASCIIToLower(aMedia);
103 bool LinkStyle::IsCSSMimeTypeAttributeForStyleElement(const Element& aSelf) {
104 // Per
105 // https://html.spec.whatwg.org/multipage/semantics.html#the-style-element:update-a-style-block
106 // step 4, for style elements we should only accept empty and "text/css" type
107 // attribute values.
108 nsAutoString type;
109 aSelf.GetAttr(nsGkAtoms::type, type);
110 return type.IsEmpty() || type.LowerCaseEqualsLiteral("text/css");
113 void LinkStyle::Unlink() { LinkStyle::SetStyleSheet(nullptr); }
115 void LinkStyle::Traverse(nsCycleCollectionTraversalCallback& cb) {
116 LinkStyle* tmp = this;
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
120 void LinkStyle::SetStyleSheet(StyleSheet* aStyleSheet) {
121 if (mStyleSheet) {
122 mStyleSheet->SetOwningNode(nullptr);
125 mStyleSheet = aStyleSheet;
126 if (mStyleSheet) {
127 mStyleSheet->SetOwningNode(&AsContent());
131 void LinkStyle::GetCharset(nsAString& aCharset) { aCharset.Truncate(); }
133 static uint32_t ToLinkMask(const nsAString& aLink) {
134 // Keep this in sync with sSupportedRelValues in HTMLLinkElement.cpp
135 uint32_t mask = 0;
136 if (aLink.EqualsLiteral("prefetch")) {
137 mask = LinkStyle::ePREFETCH;
138 } else if (aLink.EqualsLiteral("dns-prefetch")) {
139 mask = LinkStyle::eDNS_PREFETCH;
140 } else if (aLink.EqualsLiteral("stylesheet")) {
141 mask = LinkStyle::eSTYLESHEET;
142 } else if (aLink.EqualsLiteral("next")) {
143 mask = LinkStyle::eNEXT;
144 } else if (aLink.EqualsLiteral("alternate")) {
145 mask = LinkStyle::eALTERNATE;
146 } else if (aLink.EqualsLiteral("preconnect")) {
147 mask = LinkStyle::ePRECONNECT;
148 } else if (aLink.EqualsLiteral("preload")) {
149 mask = LinkStyle::ePRELOAD;
150 } else if (aLink.EqualsLiteral("modulepreload")) {
151 mask = LinkStyle::eMODULE_PRELOAD;
154 return mask;
157 uint32_t LinkStyle::ParseLinkTypes(const nsAString& aTypes) {
158 uint32_t linkMask = 0;
159 nsAString::const_iterator start, done;
160 aTypes.BeginReading(start);
161 aTypes.EndReading(done);
162 if (start == done) return linkMask;
164 nsAString::const_iterator current(start);
165 bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
166 nsAutoString subString;
168 while (current != done) {
169 if (nsContentUtils::IsHTMLWhitespace(*current)) {
170 if (inString) {
171 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
172 linkMask |= ToLinkMask(subString);
173 inString = false;
175 } else {
176 if (!inString) {
177 start = current;
178 inString = true;
181 ++current;
183 if (inString) {
184 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
185 linkMask |= ToLinkMask(subString);
187 return linkMask;
190 Result<LinkStyle::Update, nsresult> LinkStyle::UpdateStyleSheetInternal(
191 Document* aOldDocument, ShadowRoot* aOldShadowRoot,
192 ForceUpdate aForceUpdate) {
193 return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr,
194 aForceUpdate);
197 LinkStyle* LinkStyle::FromNode(Element& aElement) {
198 nsAtom* name = aElement.NodeInfo()->NameAtom();
199 if (name == nsGkAtoms::link) {
200 MOZ_ASSERT(aElement.IsHTMLElement() == !!aElement.AsLinkStyle());
201 return aElement.IsHTMLElement() ? static_cast<HTMLLinkElement*>(&aElement)
202 : nullptr;
204 if (name == nsGkAtoms::style) {
205 if (aElement.IsHTMLElement()) {
206 MOZ_ASSERT(aElement.AsLinkStyle());
207 return static_cast<HTMLStyleElement*>(&aElement);
209 if (aElement.IsSVGElement()) {
210 MOZ_ASSERT(aElement.AsLinkStyle());
211 return static_cast<SVGStyleElement*>(&aElement);
214 MOZ_ASSERT(!aElement.AsLinkStyle());
215 return nullptr;
218 void LinkStyle::BindToTree() {
219 if (mUpdatesEnabled) {
220 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
221 "LinkStyle::BindToTree",
222 [this, pin = RefPtr{&AsContent()}] { UpdateStyleSheetInternal(); }));
226 Result<LinkStyle::Update, nsresult> LinkStyle::DoUpdateStyleSheet(
227 Document* aOldDocument, ShadowRoot* aOldShadowRoot,
228 nsICSSLoaderObserver* aObserver, ForceUpdate aForceUpdate) {
229 nsIContent& thisContent = AsContent();
230 if (thisContent.IsInSVGUseShadowTree()) {
231 // Stylesheets in <use>-cloned subtrees are disabled until we figure out
232 // how they should behave.
233 return Update{};
236 if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
237 MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
238 "ShadowRoot content is never in document, thus "
239 "there should not be a old document and old "
240 "ShadowRoot simultaneously.");
242 // We're removing the link element from the document or shadow tree, unload
243 // the stylesheet.
245 // We want to do this even if updates are disabled, since otherwise a sheet
246 // with a stale linking element pointer will be hanging around -- not good!
247 if (mStyleSheet->IsComplete()) {
248 if (aOldShadowRoot) {
249 aOldShadowRoot->RemoveStyleSheet(*mStyleSheet);
250 } else {
251 aOldDocument->RemoveStyleSheet(*mStyleSheet);
255 SetStyleSheet(nullptr);
258 Document* doc = thisContent.GetComposedDoc();
260 // Loader could be null during unlink, see bug 1425866.
261 if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
262 return Update{};
265 // When static documents are created, stylesheets are cloned manually.
266 if (!mUpdatesEnabled || doc->IsStaticDocument()) {
267 return Update{};
270 Maybe<SheetInfo> info = GetStyleSheetInfo();
271 if (aForceUpdate == ForceUpdate::No && mStyleSheet && info &&
272 !info->mIsInline && info->mURI) {
273 if (nsIURI* oldURI = mStyleSheet->GetSheetURI()) {
274 bool equal;
275 nsresult rv = oldURI->Equals(info->mURI, &equal);
276 if (NS_SUCCEEDED(rv) && equal) {
277 return Update{};
282 if (mStyleSheet) {
283 if (mStyleSheet->IsComplete()) {
284 if (thisContent.IsInShadowTree()) {
285 ShadowRoot* containingShadow = thisContent.GetContainingShadow();
286 // Could be null only during unlink.
287 if (MOZ_LIKELY(containingShadow)) {
288 containingShadow->RemoveStyleSheet(*mStyleSheet);
290 } else {
291 doc->RemoveStyleSheet(*mStyleSheet);
295 SetStyleSheet(nullptr);
298 if (!info) {
299 return Update{};
302 if (!info->mURI && !info->mIsInline) {
303 // If href is empty and this is not inline style then just bail
304 return Update{};
307 if (info->mIsInline) {
308 nsAutoString text;
309 if (!nsContentUtils::GetNodeTextContent(&thisContent, false, text,
310 fallible)) {
311 return Err(NS_ERROR_OUT_OF_MEMORY);
314 MOZ_ASSERT(thisContent.NodeInfo()->NameAtom() != nsGkAtoms::link,
315 "<link> is not 'inline', and needs different CSP checks");
316 MOZ_ASSERT(thisContent.IsElement());
317 nsresult rv = NS_OK;
318 if (!nsStyleUtil::CSPAllowsInlineStyle(
319 thisContent.AsElement(), doc, info->mTriggeringPrincipal,
320 mLineNumber, mColumnNumber, text, &rv)) {
321 if (NS_FAILED(rv)) {
322 return Err(rv);
324 return Update{};
327 // Parse the style sheet.
328 return doc->CSSLoader()->LoadInlineStyle(*info, text, aObserver);
330 if (thisContent.IsElement()) {
331 nsAutoString integrity;
332 thisContent.AsElement()->GetAttr(nsGkAtoms::integrity, integrity);
333 if (!integrity.IsEmpty()) {
334 MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
335 ("LinkStyle::DoUpdateStyleSheet, integrity=%s",
336 NS_ConvertUTF16toUTF8(integrity).get()));
339 auto resultOrError = doc->CSSLoader()->LoadStyleLink(*info, aObserver);
340 if (resultOrError.isErr()) {
341 // Don't propagate LoadStyleLink() errors further than this, since some
342 // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
343 // things like a stylesheet load being blocked by the security system.
344 return Update{};
346 return resultOrError;
349 } // namespace mozilla::dom