1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsStyleLinkElement.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,471 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * A base class which implements nsIStyleSheetLinkingElement and can 1.12 + * be subclassed by various content nodes that want to load 1.13 + * stylesheets (<style>, <link>, processing instructions, etc). 1.14 + */ 1.15 + 1.16 +#include "nsStyleLinkElement.h" 1.17 + 1.18 +#include "mozilla/css/Loader.h" 1.19 +#include "mozilla/dom/Element.h" 1.20 +#include "mozilla/dom/ShadowRoot.h" 1.21 +#include "nsCSSStyleSheet.h" 1.22 +#include "nsIContent.h" 1.23 +#include "nsIDocument.h" 1.24 +#include "nsIDOMComment.h" 1.25 +#include "nsIDOMNode.h" 1.26 +#include "nsIDOMStyleSheet.h" 1.27 +#include "nsNetUtil.h" 1.28 +#include "nsUnicharUtils.h" 1.29 +#include "nsCRT.h" 1.30 +#include "nsXPCOMCIDInternal.h" 1.31 +#include "nsUnicharInputStream.h" 1.32 +#include "nsContentUtils.h" 1.33 +#include "nsStyleUtil.h" 1.34 + 1.35 +using namespace mozilla; 1.36 +using namespace mozilla::dom; 1.37 + 1.38 +nsStyleLinkElement::nsStyleLinkElement() 1.39 + : mDontLoadStyle(false) 1.40 + , mUpdatesEnabled(true) 1.41 + , mLineNumber(1) 1.42 +{ 1.43 +} 1.44 + 1.45 +nsStyleLinkElement::~nsStyleLinkElement() 1.46 +{ 1.47 + nsStyleLinkElement::SetStyleSheet(nullptr); 1.48 +} 1.49 + 1.50 +void 1.51 +nsStyleLinkElement::Unlink() 1.52 +{ 1.53 + mStyleSheet = nullptr; 1.54 +} 1.55 + 1.56 +void 1.57 +nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb) 1.58 +{ 1.59 + nsStyleLinkElement* tmp = this; 1.60 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet); 1.61 +} 1.62 + 1.63 +NS_IMETHODIMP 1.64 +nsStyleLinkElement::SetStyleSheet(nsCSSStyleSheet* aStyleSheet) 1.65 +{ 1.66 + if (mStyleSheet) { 1.67 + mStyleSheet->SetOwningNode(nullptr); 1.68 + } 1.69 + 1.70 + mStyleSheet = aStyleSheet; 1.71 + if (mStyleSheet) { 1.72 + nsCOMPtr<nsINode> node = do_QueryObject(this); 1.73 + if (node) { 1.74 + mStyleSheet->SetOwningNode(node); 1.75 + } 1.76 + } 1.77 + 1.78 + return NS_OK; 1.79 +} 1.80 + 1.81 +NS_IMETHODIMP 1.82 +nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet) 1.83 +{ 1.84 + aStyleSheet = mStyleSheet; 1.85 + NS_IF_ADDREF(aStyleSheet); 1.86 + 1.87 + return NS_OK; 1.88 +} 1.89 + 1.90 +NS_IMETHODIMP 1.91 +nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle) 1.92 +{ 1.93 + mDontLoadStyle = aDontLoadStyle; 1.94 + 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +NS_IMETHODIMP 1.99 +nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates) 1.100 +{ 1.101 + mUpdatesEnabled = aEnableUpdates; 1.102 + 1.103 + return NS_OK; 1.104 +} 1.105 + 1.106 +NS_IMETHODIMP 1.107 +nsStyleLinkElement::GetCharset(nsAString& aCharset) 1.108 +{ 1.109 + // descendants have to implement this themselves 1.110 + return NS_ERROR_NOT_IMPLEMENTED; 1.111 +} 1.112 + 1.113 +/* virtual */ void 1.114 +nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI) 1.115 +{ 1.116 + NS_NOTREACHED("Base URI can't be overriden in this implementation " 1.117 + "of nsIStyleSheetLinkingElement."); 1.118 +} 1.119 + 1.120 +/* virtual */ void 1.121 +nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber) 1.122 +{ 1.123 + mLineNumber = aLineNumber; 1.124 +} 1.125 + 1.126 +static uint32_t ToLinkMask(const nsAString& aLink) 1.127 +{ 1.128 + if (aLink.EqualsLiteral("prefetch")) 1.129 + return nsStyleLinkElement::ePREFETCH; 1.130 + else if (aLink.EqualsLiteral("dns-prefetch")) 1.131 + return nsStyleLinkElement::eDNS_PREFETCH; 1.132 + else if (aLink.EqualsLiteral("stylesheet")) 1.133 + return nsStyleLinkElement::eSTYLESHEET; 1.134 + else if (aLink.EqualsLiteral("next")) 1.135 + return nsStyleLinkElement::eNEXT; 1.136 + else if (aLink.EqualsLiteral("alternate")) 1.137 + return nsStyleLinkElement::eALTERNATE; 1.138 + else 1.139 + return 0; 1.140 +} 1.141 + 1.142 +uint32_t nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes) 1.143 +{ 1.144 + uint32_t linkMask = 0; 1.145 + nsAString::const_iterator start, done; 1.146 + aTypes.BeginReading(start); 1.147 + aTypes.EndReading(done); 1.148 + if (start == done) 1.149 + return linkMask; 1.150 + 1.151 + nsAString::const_iterator current(start); 1.152 + bool inString = !nsContentUtils::IsHTMLWhitespace(*current); 1.153 + nsAutoString subString; 1.154 + 1.155 + while (current != done) { 1.156 + if (nsContentUtils::IsHTMLWhitespace(*current)) { 1.157 + if (inString) { 1.158 + nsContentUtils::ASCIIToLower(Substring(start, current), subString); 1.159 + linkMask |= ToLinkMask(subString); 1.160 + inString = false; 1.161 + } 1.162 + } 1.163 + else { 1.164 + if (!inString) { 1.165 + start = current; 1.166 + inString = true; 1.167 + } 1.168 + } 1.169 + ++current; 1.170 + } 1.171 + if (inString) { 1.172 + nsContentUtils::ASCIIToLower(Substring(start, current), subString); 1.173 + linkMask |= ToLinkMask(subString); 1.174 + } 1.175 + return linkMask; 1.176 +} 1.177 + 1.178 +NS_IMETHODIMP 1.179 +nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver, 1.180 + bool* aWillNotify, 1.181 + bool* aIsAlternate) 1.182 +{ 1.183 + return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify, 1.184 + aIsAlternate, false); 1.185 +} 1.186 + 1.187 +nsresult 1.188 +nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument, 1.189 + ShadowRoot *aOldShadowRoot, 1.190 + bool aForceUpdate) 1.191 +{ 1.192 + bool notify, alternate; 1.193 + return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, ¬ify, 1.194 + &alternate, aForceUpdate); 1.195 +} 1.196 + 1.197 +static bool 1.198 +IsScopedStyleElement(nsIContent* aContent) 1.199 +{ 1.200 + // This is quicker than, say, QIing aContent to nsStyleLinkElement 1.201 + // and then calling its virtual GetStyleSheetInfo method to find out 1.202 + // if it is scoped. 1.203 + return (aContent->IsHTML(nsGkAtoms::style) || 1.204 + aContent->IsSVG(nsGkAtoms::style)) && 1.205 + aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped); 1.206 +} 1.207 + 1.208 +static void 1.209 +SetIsElementInStyleScopeFlagOnSubtree(Element* aElement) 1.210 +{ 1.211 + if (aElement->IsElementInStyleScope()) { 1.212 + return; 1.213 + } 1.214 + 1.215 + aElement->SetIsElementInStyleScope(); 1.216 + 1.217 + nsIContent* n = aElement->GetNextNode(aElement); 1.218 + while (n) { 1.219 + if (n->IsElementInStyleScope()) { 1.220 + n = n->GetNextNonChildNode(aElement); 1.221 + } else { 1.222 + if (n->IsElement()) { 1.223 + n->SetIsElementInStyleScope(); 1.224 + } 1.225 + n = n->GetNextNode(aElement); 1.226 + } 1.227 + } 1.228 +} 1.229 + 1.230 +static bool 1.231 +HasScopedStyleSheetChild(nsIContent* aContent) 1.232 +{ 1.233 + for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) { 1.234 + if (IsScopedStyleElement(n)) { 1.235 + return true; 1.236 + } 1.237 + } 1.238 + return false; 1.239 +} 1.240 + 1.241 +// Called when aElement has had a <style scoped> child removed. 1.242 +static void 1.243 +UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement) 1.244 +{ 1.245 + NS_ASSERTION(aElement->IsElementInStyleScope(), 1.246 + "only call UpdateIsElementInStyleScopeFlagOnSubtree on a " 1.247 + "subtree that has IsElementInStyleScope boolean flag set"); 1.248 + 1.249 + if (HasScopedStyleSheetChild(aElement)) { 1.250 + return; 1.251 + } 1.252 + 1.253 + aElement->ClearIsElementInStyleScope(); 1.254 + 1.255 + nsIContent* n = aElement->GetNextNode(aElement); 1.256 + while (n) { 1.257 + if (HasScopedStyleSheetChild(n)) { 1.258 + n = n->GetNextNonChildNode(aElement); 1.259 + } else { 1.260 + if (n->IsElement()) { 1.261 + n->ClearIsElementInStyleScope(); 1.262 + } 1.263 + n = n->GetNextNode(aElement); 1.264 + } 1.265 + } 1.266 +} 1.267 + 1.268 +static Element* 1.269 +GetScopeElement(nsIStyleSheet* aSheet) 1.270 +{ 1.271 + nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet); 1.272 + if (!cssStyleSheet) { 1.273 + return nullptr; 1.274 + } 1.275 + 1.276 + return cssStyleSheet->GetScopeElement(); 1.277 +} 1.278 + 1.279 +nsresult 1.280 +nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument, 1.281 + ShadowRoot* aOldShadowRoot, 1.282 + nsICSSLoaderObserver* aObserver, 1.283 + bool* aWillNotify, 1.284 + bool* aIsAlternate, 1.285 + bool aForceUpdate) 1.286 +{ 1.287 + *aWillNotify = false; 1.288 + 1.289 + nsCOMPtr<nsIContent> thisContent; 1.290 + CallQueryInterface(this, getter_AddRefs(thisContent)); 1.291 + 1.292 + // All instances of nsStyleLinkElement should implement nsIContent. 1.293 + NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE); 1.294 + 1.295 + // Check for a ShadowRoot because link elements are inert in a 1.296 + // ShadowRoot. 1.297 + ShadowRoot* containingShadow = thisContent->GetContainingShadow(); 1.298 + if (thisContent->IsHTML(nsGkAtoms::link) && 1.299 + (aOldShadowRoot || containingShadow)) { 1.300 + return NS_OK; 1.301 + } 1.302 + 1.303 + Element* oldScopeElement = GetScopeElement(mStyleSheet); 1.304 + 1.305 + if (mStyleSheet && aOldDocument) { 1.306 + // We're removing the link element from the document, unload the 1.307 + // stylesheet. We want to do this even if updates are disabled, since 1.308 + // otherwise a sheet with a stale linking element pointer will be hanging 1.309 + // around -- not good! 1.310 + if (aOldShadowRoot) { 1.311 + aOldShadowRoot->RemoveSheet(mStyleSheet); 1.312 + } else { 1.313 + aOldDocument->BeginUpdate(UPDATE_STYLE); 1.314 + aOldDocument->RemoveStyleSheet(mStyleSheet); 1.315 + aOldDocument->EndUpdate(UPDATE_STYLE); 1.316 + } 1.317 + 1.318 + nsStyleLinkElement::SetStyleSheet(nullptr); 1.319 + if (oldScopeElement) { 1.320 + UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement); 1.321 + } 1.322 + } 1.323 + 1.324 + // When static documents are created, stylesheets are cloned manually. 1.325 + if (mDontLoadStyle || !mUpdatesEnabled || 1.326 + thisContent->OwnerDoc()->IsStaticDocument()) { 1.327 + return NS_OK; 1.328 + } 1.329 + 1.330 + nsCOMPtr<nsIDocument> doc = thisContent->GetDocument(); 1.331 + 1.332 + if (!doc || !doc->CSSLoader()->GetEnabled()) { 1.333 + return NS_OK; 1.334 + } 1.335 + 1.336 + bool isInline; 1.337 + nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline); 1.338 + 1.339 + if (!aForceUpdate && mStyleSheet && !isInline && uri) { 1.340 + nsIURI* oldURI = mStyleSheet->GetSheetURI(); 1.341 + if (oldURI) { 1.342 + bool equal; 1.343 + nsresult rv = oldURI->Equals(uri, &equal); 1.344 + if (NS_SUCCEEDED(rv) && equal) { 1.345 + return NS_OK; // We already loaded this stylesheet 1.346 + } 1.347 + } 1.348 + } 1.349 + 1.350 + if (mStyleSheet) { 1.351 + if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { 1.352 + ShadowRoot* containingShadow = thisContent->GetContainingShadow(); 1.353 + containingShadow->RemoveSheet(mStyleSheet); 1.354 + } else { 1.355 + doc->BeginUpdate(UPDATE_STYLE); 1.356 + doc->RemoveStyleSheet(mStyleSheet); 1.357 + doc->EndUpdate(UPDATE_STYLE); 1.358 + } 1.359 + 1.360 + nsStyleLinkElement::SetStyleSheet(nullptr); 1.361 + } 1.362 + 1.363 + if (!uri && !isInline) { 1.364 + return NS_OK; // If href is empty and this is not inline style then just bail 1.365 + } 1.366 + 1.367 + nsAutoString title, type, media; 1.368 + bool isScoped; 1.369 + bool isAlternate; 1.370 + 1.371 + GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate); 1.372 + 1.373 + if (!type.LowerCaseEqualsLiteral("text/css")) { 1.374 + return NS_OK; 1.375 + } 1.376 + 1.377 + Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr; 1.378 + if (scopeElement) { 1.379 + NS_ASSERTION(isInline, "non-inline style must not have scope element"); 1.380 + SetIsElementInStyleScopeFlagOnSubtree(scopeElement); 1.381 + } 1.382 + 1.383 + bool doneLoading = false; 1.384 + nsresult rv = NS_OK; 1.385 + if (isInline) { 1.386 + nsAutoString text; 1.387 + if (!nsContentUtils::GetNodeTextContent(thisContent, false, text)) { 1.388 + return NS_ERROR_OUT_OF_MEMORY; 1.389 + } 1.390 + 1.391 + MOZ_ASSERT(thisContent->Tag() != nsGkAtoms::link, 1.392 + "<link> is not 'inline', and needs different CSP checks"); 1.393 + if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent, 1.394 + thisContent->NodePrincipal(), 1.395 + doc->GetDocumentURI(), 1.396 + mLineNumber, text, &rv)) 1.397 + return rv; 1.398 + 1.399 + // Parse the style sheet. 1.400 + rv = doc->CSSLoader()-> 1.401 + LoadInlineStyle(thisContent, text, mLineNumber, title, media, 1.402 + scopeElement, aObserver, &doneLoading, &isAlternate); 1.403 + } 1.404 + else { 1.405 + // XXXbz clone the URI here to work around content policies modifying URIs. 1.406 + nsCOMPtr<nsIURI> clonedURI; 1.407 + uri->Clone(getter_AddRefs(clonedURI)); 1.408 + NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY); 1.409 + rv = doc->CSSLoader()-> 1.410 + LoadStyleLink(thisContent, clonedURI, title, media, isAlternate, 1.411 + GetCORSMode(), aObserver, &isAlternate); 1.412 + if (NS_FAILED(rv)) { 1.413 + // Don't propagate LoadStyleLink() errors further than this, since some 1.414 + // consumers (e.g. nsXMLContentSink) will completely abort on innocuous 1.415 + // things like a stylesheet load being blocked by the security system. 1.416 + doneLoading = true; 1.417 + isAlternate = false; 1.418 + rv = NS_OK; 1.419 + } 1.420 + } 1.421 + 1.422 + NS_ENSURE_SUCCESS(rv, rv); 1.423 + 1.424 + *aWillNotify = !doneLoading; 1.425 + *aIsAlternate = isAlternate; 1.426 + 1.427 + return NS_OK; 1.428 +} 1.429 + 1.430 +void 1.431 +nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped) 1.432 +{ 1.433 + if (!mStyleSheet) { 1.434 + return; 1.435 + } 1.436 + 1.437 + nsCOMPtr<nsIContent> thisContent; 1.438 + CallQueryInterface(this, getter_AddRefs(thisContent)); 1.439 + 1.440 + Element* oldScopeElement = mStyleSheet->GetScopeElement(); 1.441 + Element* newScopeElement = aIsNowScoped ? 1.442 + thisContent->GetParentElement() : 1.443 + nullptr; 1.444 + 1.445 + if (oldScopeElement == newScopeElement) { 1.446 + return; 1.447 + } 1.448 + 1.449 + nsIDocument* document = thisContent->GetOwnerDocument(); 1.450 + 1.451 + if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { 1.452 + ShadowRoot* containingShadow = thisContent->GetContainingShadow(); 1.453 + containingShadow->RemoveSheet(mStyleSheet); 1.454 + 1.455 + mStyleSheet->SetScopeElement(newScopeElement); 1.456 + 1.457 + containingShadow->InsertSheet(mStyleSheet, thisContent); 1.458 + } else { 1.459 + document->BeginUpdate(UPDATE_STYLE); 1.460 + document->RemoveStyleSheet(mStyleSheet); 1.461 + 1.462 + mStyleSheet->SetScopeElement(newScopeElement); 1.463 + 1.464 + document->AddStyleSheet(mStyleSheet); 1.465 + document->EndUpdate(UPDATE_STYLE); 1.466 + } 1.467 + 1.468 + if (oldScopeElement) { 1.469 + UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement); 1.470 + } 1.471 + if (newScopeElement) { 1.472 + SetIsElementInStyleScopeFlagOnSubtree(newScopeElement); 1.473 + } 1.474 +}