Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsStyleLinkElement.cpp
blob7cf7ab455996300e32008935f42aeecf24a2bba6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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 "nsStyleLinkElement.h"
15 #include "mozilla/CSSStyleSheet.h"
16 #include "mozilla/css/Loader.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/FragmentOrElement.h"
19 #include "mozilla/dom/ShadowRoot.h"
20 #include "mozilla/Preferences.h"
21 #include "nsIContent.h"
22 #include "nsIDocument.h"
23 #include "nsIDOMComment.h"
24 #include "nsIDOMNode.h"
25 #include "nsIDOMStyleSheet.h"
26 #include "nsNetUtil.h"
27 #include "nsUnicharUtils.h"
28 #include "nsCRT.h"
29 #include "nsXPCOMCIDInternal.h"
30 #include "nsUnicharInputStream.h"
31 #include "nsContentUtils.h"
32 #include "nsStyleUtil.h"
34 using namespace mozilla;
35 using namespace mozilla::dom;
37 nsStyleLinkElement::nsStyleLinkElement()
38 : mDontLoadStyle(false)
39 , mUpdatesEnabled(true)
40 , mLineNumber(1)
44 nsStyleLinkElement::~nsStyleLinkElement()
46 nsStyleLinkElement::SetStyleSheet(nullptr);
49 void
50 nsStyleLinkElement::Unlink()
52 mStyleSheet = nullptr;
55 void
56 nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
58 nsStyleLinkElement* tmp = this;
59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
62 NS_IMETHODIMP
63 nsStyleLinkElement::SetStyleSheet(CSSStyleSheet* aStyleSheet)
65 if (mStyleSheet) {
66 mStyleSheet->SetOwningNode(nullptr);
69 mStyleSheet = aStyleSheet;
70 if (mStyleSheet) {
71 nsCOMPtr<nsINode> node = do_QueryObject(this);
72 if (node) {
73 mStyleSheet->SetOwningNode(node);
77 return NS_OK;
80 NS_IMETHODIMP_(CSSStyleSheet*)
81 nsStyleLinkElement::GetStyleSheet()
83 return mStyleSheet;
86 NS_IMETHODIMP
87 nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle)
89 mDontLoadStyle = aDontLoadStyle;
91 return NS_OK;
94 NS_IMETHODIMP
95 nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates)
97 mUpdatesEnabled = aEnableUpdates;
99 return NS_OK;
102 NS_IMETHODIMP
103 nsStyleLinkElement::GetCharset(nsAString& aCharset)
105 // descendants have to implement this themselves
106 return NS_ERROR_NOT_IMPLEMENTED;
109 /* virtual */ void
110 nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI)
112 NS_NOTREACHED("Base URI can't be overriden in this implementation "
113 "of nsIStyleSheetLinkingElement.");
116 /* virtual */ void
117 nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
119 mLineNumber = aLineNumber;
122 /* static */ bool
123 nsStyleLinkElement::IsImportEnabled(nsIPrincipal* aPrincipal)
125 static bool sAdded = false;
126 static bool sWebComponentsEnabled;
127 if (!sAdded) {
128 // This part runs only once because of the static flag.
129 Preferences::AddBoolVarCache(&sWebComponentsEnabled,
130 "dom.webcomponents.enabled",
131 false);
132 sAdded = true;
135 if (sWebComponentsEnabled) {
136 return true;
139 // If the web components pref is not enabled, check
140 // if we are in a certified app because imports is enabled
141 // for certified apps.
142 return aPrincipal &&
143 aPrincipal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
146 static uint32_t ToLinkMask(const nsAString& aLink, nsIPrincipal* aPrincipal)
148 if (aLink.EqualsLiteral("prefetch"))
149 return nsStyleLinkElement::ePREFETCH;
150 else if (aLink.EqualsLiteral("dns-prefetch"))
151 return nsStyleLinkElement::eDNS_PREFETCH;
152 else if (aLink.EqualsLiteral("stylesheet"))
153 return nsStyleLinkElement::eSTYLESHEET;
154 else if (aLink.EqualsLiteral("next"))
155 return nsStyleLinkElement::eNEXT;
156 else if (aLink.EqualsLiteral("alternate"))
157 return nsStyleLinkElement::eALTERNATE;
158 else if (aLink.EqualsLiteral("import") && aPrincipal &&
159 nsStyleLinkElement::IsImportEnabled(aPrincipal))
160 return nsStyleLinkElement::eHTMLIMPORT;
161 else
162 return 0;
165 uint32_t nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes, nsIPrincipal* aPrincipal)
167 uint32_t linkMask = 0;
168 nsAString::const_iterator start, done;
169 aTypes.BeginReading(start);
170 aTypes.EndReading(done);
171 if (start == done)
172 return linkMask;
174 nsAString::const_iterator current(start);
175 bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
176 nsAutoString subString;
178 while (current != done) {
179 if (nsContentUtils::IsHTMLWhitespace(*current)) {
180 if (inString) {
181 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
182 linkMask |= ToLinkMask(subString, aPrincipal);
183 inString = false;
186 else {
187 if (!inString) {
188 start = current;
189 inString = true;
192 ++current;
194 if (inString) {
195 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
196 linkMask |= ToLinkMask(subString, aPrincipal);
198 return linkMask;
201 NS_IMETHODIMP
202 nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
203 bool* aWillNotify,
204 bool* aIsAlternate,
205 bool aForceReload)
207 if (aForceReload) {
208 // We remove this stylesheet from the cache to load a new version.
209 nsCOMPtr<nsIContent> thisContent;
210 CallQueryInterface(this, getter_AddRefs(thisContent));
211 nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
212 thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
213 if (doc && doc->CSSLoader()->GetEnabled() &&
214 mStyleSheet && mStyleSheet->GetOriginalURI()) {
215 doc->CSSLoader()->ObsoleteSheet(mStyleSheet->GetOriginalURI());
218 return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify,
219 aIsAlternate, aForceReload);
222 nsresult
223 nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
224 ShadowRoot *aOldShadowRoot,
225 bool aForceUpdate)
227 bool notify, alternate;
228 return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, &notify,
229 &alternate, aForceUpdate);
232 static bool
233 IsScopedStyleElement(nsIContent* aContent)
235 // This is quicker than, say, QIing aContent to nsStyleLinkElement
236 // and then calling its virtual GetStyleSheetInfo method to find out
237 // if it is scoped.
238 return (aContent->IsHTML(nsGkAtoms::style) ||
239 aContent->IsSVG(nsGkAtoms::style)) &&
240 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
243 static bool
244 HasScopedStyleSheetChild(nsIContent* aContent)
246 for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) {
247 if (IsScopedStyleElement(n)) {
248 return true;
251 return false;
254 // Called when aElement has had a <style scoped> child removed.
255 static void
256 UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement)
258 NS_ASSERTION(aElement->IsElementInStyleScope(),
259 "only call UpdateIsElementInStyleScopeFlagOnSubtree on a "
260 "subtree that has IsElementInStyleScope boolean flag set");
262 if (HasScopedStyleSheetChild(aElement)) {
263 return;
266 aElement->ClearIsElementInStyleScope();
268 nsIContent* n = aElement->GetNextNode(aElement);
269 while (n) {
270 if (HasScopedStyleSheetChild(n)) {
271 n = n->GetNextNonChildNode(aElement);
272 } else {
273 if (n->IsElement()) {
274 n->ClearIsElementInStyleScope();
276 n = n->GetNextNode(aElement);
281 static Element*
282 GetScopeElement(nsIStyleSheet* aSheet)
284 nsRefPtr<CSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet);
285 if (!cssStyleSheet) {
286 return nullptr;
289 return cssStyleSheet->GetScopeElement();
292 nsresult
293 nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
294 ShadowRoot* aOldShadowRoot,
295 nsICSSLoaderObserver* aObserver,
296 bool* aWillNotify,
297 bool* aIsAlternate,
298 bool aForceUpdate)
300 *aWillNotify = false;
302 nsCOMPtr<nsIContent> thisContent;
303 CallQueryInterface(this, getter_AddRefs(thisContent));
305 // All instances of nsStyleLinkElement should implement nsIContent.
306 NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
308 if (thisContent->IsInAnonymousSubtree() &&
309 thisContent->IsAnonymousContentInSVGUseSubtree()) {
310 // Stylesheets in <use>-cloned subtrees are disabled until we figure out
311 // how they should behave.
312 return NS_OK;
315 // Check for a ShadowRoot because link elements are inert in a
316 // ShadowRoot.
317 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
318 if (thisContent->IsHTML(nsGkAtoms::link) &&
319 (aOldShadowRoot || containingShadow)) {
320 return NS_OK;
323 Element* oldScopeElement = GetScopeElement(mStyleSheet);
325 if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
326 MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
327 "ShadowRoot content is never in document, thus "
328 "there should not be a old document and old "
329 "ShadowRoot simultaneously.");
331 // We're removing the link element from the document or shadow tree,
332 // unload the stylesheet. We want to do this even if updates are
333 // disabled, since otherwise a sheet with a stale linking element pointer
334 // will be hanging around -- not good!
335 if (aOldShadowRoot) {
336 aOldShadowRoot->RemoveSheet(mStyleSheet);
337 } else {
338 aOldDocument->BeginUpdate(UPDATE_STYLE);
339 aOldDocument->RemoveStyleSheet(mStyleSheet);
340 aOldDocument->EndUpdate(UPDATE_STYLE);
343 nsStyleLinkElement::SetStyleSheet(nullptr);
344 if (oldScopeElement) {
345 UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
349 // When static documents are created, stylesheets are cloned manually.
350 if (mDontLoadStyle || !mUpdatesEnabled ||
351 thisContent->OwnerDoc()->IsStaticDocument()) {
352 return NS_OK;
355 nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
356 thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
357 if (!doc || !doc->CSSLoader()->GetEnabled()) {
358 return NS_OK;
361 bool isInline;
362 nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline);
364 if (!aForceUpdate && mStyleSheet && !isInline && uri) {
365 nsIURI* oldURI = mStyleSheet->GetSheetURI();
366 if (oldURI) {
367 bool equal;
368 nsresult rv = oldURI->Equals(uri, &equal);
369 if (NS_SUCCEEDED(rv) && equal) {
370 return NS_OK; // We already loaded this stylesheet
375 if (mStyleSheet) {
376 if (thisContent->IsInShadowTree()) {
377 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
378 containingShadow->RemoveSheet(mStyleSheet);
379 } else {
380 doc->BeginUpdate(UPDATE_STYLE);
381 doc->RemoveStyleSheet(mStyleSheet);
382 doc->EndUpdate(UPDATE_STYLE);
385 nsStyleLinkElement::SetStyleSheet(nullptr);
388 if (!uri && !isInline) {
389 return NS_OK; // If href is empty and this is not inline style then just bail
392 nsAutoString title, type, media;
393 bool isScoped;
394 bool isAlternate;
396 GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
398 if (!type.LowerCaseEqualsLiteral("text/css")) {
399 return NS_OK;
402 Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
403 if (scopeElement) {
404 NS_ASSERTION(isInline, "non-inline style must not have scope element");
405 scopeElement->SetIsElementInStyleScopeFlagOnSubtree(true);
408 bool doneLoading = false;
409 nsresult rv = NS_OK;
410 if (isInline) {
411 nsAutoString text;
412 if (!nsContentUtils::GetNodeTextContent(thisContent, false, text)) {
413 return NS_ERROR_OUT_OF_MEMORY;
416 MOZ_ASSERT(thisContent->Tag() != nsGkAtoms::link,
417 "<link> is not 'inline', and needs different CSP checks");
418 if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent,
419 thisContent->NodePrincipal(),
420 doc->GetDocumentURI(),
421 mLineNumber, text, &rv))
422 return rv;
424 // Parse the style sheet.
425 rv = doc->CSSLoader()->
426 LoadInlineStyle(thisContent, text, mLineNumber, title, media,
427 scopeElement, aObserver, &doneLoading, &isAlternate);
429 else {
430 // XXXbz clone the URI here to work around content policies modifying URIs.
431 nsCOMPtr<nsIURI> clonedURI;
432 uri->Clone(getter_AddRefs(clonedURI));
433 NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
434 rv = doc->CSSLoader()->
435 LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
436 GetCORSMode(), doc->GetReferrerPolicy(),
437 aObserver, &isAlternate);
438 if (NS_FAILED(rv)) {
439 // Don't propagate LoadStyleLink() errors further than this, since some
440 // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
441 // things like a stylesheet load being blocked by the security system.
442 doneLoading = true;
443 isAlternate = false;
444 rv = NS_OK;
448 NS_ENSURE_SUCCESS(rv, rv);
450 *aWillNotify = !doneLoading;
451 *aIsAlternate = isAlternate;
453 return NS_OK;
456 void
457 nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped)
459 if (!mStyleSheet) {
460 return;
463 nsCOMPtr<nsIContent> thisContent;
464 CallQueryInterface(this, getter_AddRefs(thisContent));
466 Element* oldScopeElement = mStyleSheet->GetScopeElement();
467 Element* newScopeElement = aIsNowScoped ?
468 thisContent->GetParentElement() :
469 nullptr;
471 if (oldScopeElement == newScopeElement) {
472 return;
475 nsIDocument* document = thisContent->GetOwnerDocument();
477 if (thisContent->IsInShadowTree()) {
478 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
479 containingShadow->RemoveSheet(mStyleSheet);
481 mStyleSheet->SetScopeElement(newScopeElement);
483 containingShadow->InsertSheet(mStyleSheet, thisContent);
484 } else {
485 document->BeginUpdate(UPDATE_STYLE);
486 document->RemoveStyleSheet(mStyleSheet);
488 mStyleSheet->SetScopeElement(newScopeElement);
490 document->AddStyleSheet(mStyleSheet);
491 document->EndUpdate(UPDATE_STYLE);
494 if (oldScopeElement) {
495 UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
497 if (newScopeElement) {
498 newScopeElement->SetIsElementInStyleScopeFlagOnSubtree(true);