Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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).
11 */
13 #include "nsStyleLinkElement.h"
15 #include "mozilla/css/Loader.h"
16 #include "mozilla/dom/Element.h"
17 #include "mozilla/dom/ShadowRoot.h"
18 #include "nsCSSStyleSheet.h"
19 #include "nsIContent.h"
20 #include "nsIDocument.h"
21 #include "nsIDOMComment.h"
22 #include "nsIDOMNode.h"
23 #include "nsIDOMStyleSheet.h"
24 #include "nsNetUtil.h"
25 #include "nsUnicharUtils.h"
26 #include "nsCRT.h"
27 #include "nsXPCOMCIDInternal.h"
28 #include "nsUnicharInputStream.h"
29 #include "nsContentUtils.h"
30 #include "nsStyleUtil.h"
32 using namespace mozilla;
33 using namespace mozilla::dom;
35 nsStyleLinkElement::nsStyleLinkElement()
36 : mDontLoadStyle(false)
37 , mUpdatesEnabled(true)
38 , mLineNumber(1)
39 {
40 }
42 nsStyleLinkElement::~nsStyleLinkElement()
43 {
44 nsStyleLinkElement::SetStyleSheet(nullptr);
45 }
47 void
48 nsStyleLinkElement::Unlink()
49 {
50 mStyleSheet = nullptr;
51 }
53 void
54 nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
55 {
56 nsStyleLinkElement* tmp = this;
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
58 }
60 NS_IMETHODIMP
61 nsStyleLinkElement::SetStyleSheet(nsCSSStyleSheet* aStyleSheet)
62 {
63 if (mStyleSheet) {
64 mStyleSheet->SetOwningNode(nullptr);
65 }
67 mStyleSheet = aStyleSheet;
68 if (mStyleSheet) {
69 nsCOMPtr<nsINode> node = do_QueryObject(this);
70 if (node) {
71 mStyleSheet->SetOwningNode(node);
72 }
73 }
75 return NS_OK;
76 }
78 NS_IMETHODIMP
79 nsStyleLinkElement::GetStyleSheet(nsIStyleSheet*& aStyleSheet)
80 {
81 aStyleSheet = mStyleSheet;
82 NS_IF_ADDREF(aStyleSheet);
84 return NS_OK;
85 }
87 NS_IMETHODIMP
88 nsStyleLinkElement::InitStyleLinkElement(bool aDontLoadStyle)
89 {
90 mDontLoadStyle = aDontLoadStyle;
92 return NS_OK;
93 }
95 NS_IMETHODIMP
96 nsStyleLinkElement::SetEnableUpdates(bool aEnableUpdates)
97 {
98 mUpdatesEnabled = aEnableUpdates;
100 return NS_OK;
101 }
103 NS_IMETHODIMP
104 nsStyleLinkElement::GetCharset(nsAString& aCharset)
105 {
106 // descendants have to implement this themselves
107 return NS_ERROR_NOT_IMPLEMENTED;
108 }
110 /* virtual */ void
111 nsStyleLinkElement::OverrideBaseURI(nsIURI* aNewBaseURI)
112 {
113 NS_NOTREACHED("Base URI can't be overriden in this implementation "
114 "of nsIStyleSheetLinkingElement.");
115 }
117 /* virtual */ void
118 nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
119 {
120 mLineNumber = aLineNumber;
121 }
123 static uint32_t ToLinkMask(const nsAString& aLink)
124 {
125 if (aLink.EqualsLiteral("prefetch"))
126 return nsStyleLinkElement::ePREFETCH;
127 else if (aLink.EqualsLiteral("dns-prefetch"))
128 return nsStyleLinkElement::eDNS_PREFETCH;
129 else if (aLink.EqualsLiteral("stylesheet"))
130 return nsStyleLinkElement::eSTYLESHEET;
131 else if (aLink.EqualsLiteral("next"))
132 return nsStyleLinkElement::eNEXT;
133 else if (aLink.EqualsLiteral("alternate"))
134 return nsStyleLinkElement::eALTERNATE;
135 else
136 return 0;
137 }
139 uint32_t nsStyleLinkElement::ParseLinkTypes(const nsAString& aTypes)
140 {
141 uint32_t linkMask = 0;
142 nsAString::const_iterator start, done;
143 aTypes.BeginReading(start);
144 aTypes.EndReading(done);
145 if (start == done)
146 return linkMask;
148 nsAString::const_iterator current(start);
149 bool inString = !nsContentUtils::IsHTMLWhitespace(*current);
150 nsAutoString subString;
152 while (current != done) {
153 if (nsContentUtils::IsHTMLWhitespace(*current)) {
154 if (inString) {
155 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
156 linkMask |= ToLinkMask(subString);
157 inString = false;
158 }
159 }
160 else {
161 if (!inString) {
162 start = current;
163 inString = true;
164 }
165 }
166 ++current;
167 }
168 if (inString) {
169 nsContentUtils::ASCIIToLower(Substring(start, current), subString);
170 linkMask |= ToLinkMask(subString);
171 }
172 return linkMask;
173 }
175 NS_IMETHODIMP
176 nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver,
177 bool* aWillNotify,
178 bool* aIsAlternate)
179 {
180 return DoUpdateStyleSheet(nullptr, nullptr, aObserver, aWillNotify,
181 aIsAlternate, false);
182 }
184 nsresult
185 nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
186 ShadowRoot *aOldShadowRoot,
187 bool aForceUpdate)
188 {
189 bool notify, alternate;
190 return DoUpdateStyleSheet(aOldDocument, aOldShadowRoot, nullptr, ¬ify,
191 &alternate, aForceUpdate);
192 }
194 static bool
195 IsScopedStyleElement(nsIContent* aContent)
196 {
197 // This is quicker than, say, QIing aContent to nsStyleLinkElement
198 // and then calling its virtual GetStyleSheetInfo method to find out
199 // if it is scoped.
200 return (aContent->IsHTML(nsGkAtoms::style) ||
201 aContent->IsSVG(nsGkAtoms::style)) &&
202 aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped);
203 }
205 static void
206 SetIsElementInStyleScopeFlagOnSubtree(Element* aElement)
207 {
208 if (aElement->IsElementInStyleScope()) {
209 return;
210 }
212 aElement->SetIsElementInStyleScope();
214 nsIContent* n = aElement->GetNextNode(aElement);
215 while (n) {
216 if (n->IsElementInStyleScope()) {
217 n = n->GetNextNonChildNode(aElement);
218 } else {
219 if (n->IsElement()) {
220 n->SetIsElementInStyleScope();
221 }
222 n = n->GetNextNode(aElement);
223 }
224 }
225 }
227 static bool
228 HasScopedStyleSheetChild(nsIContent* aContent)
229 {
230 for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) {
231 if (IsScopedStyleElement(n)) {
232 return true;
233 }
234 }
235 return false;
236 }
238 // Called when aElement has had a <style scoped> child removed.
239 static void
240 UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement)
241 {
242 NS_ASSERTION(aElement->IsElementInStyleScope(),
243 "only call UpdateIsElementInStyleScopeFlagOnSubtree on a "
244 "subtree that has IsElementInStyleScope boolean flag set");
246 if (HasScopedStyleSheetChild(aElement)) {
247 return;
248 }
250 aElement->ClearIsElementInStyleScope();
252 nsIContent* n = aElement->GetNextNode(aElement);
253 while (n) {
254 if (HasScopedStyleSheetChild(n)) {
255 n = n->GetNextNonChildNode(aElement);
256 } else {
257 if (n->IsElement()) {
258 n->ClearIsElementInStyleScope();
259 }
260 n = n->GetNextNode(aElement);
261 }
262 }
263 }
265 static Element*
266 GetScopeElement(nsIStyleSheet* aSheet)
267 {
268 nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet);
269 if (!cssStyleSheet) {
270 return nullptr;
271 }
273 return cssStyleSheet->GetScopeElement();
274 }
276 nsresult
277 nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
278 ShadowRoot* aOldShadowRoot,
279 nsICSSLoaderObserver* aObserver,
280 bool* aWillNotify,
281 bool* aIsAlternate,
282 bool aForceUpdate)
283 {
284 *aWillNotify = false;
286 nsCOMPtr<nsIContent> thisContent;
287 CallQueryInterface(this, getter_AddRefs(thisContent));
289 // All instances of nsStyleLinkElement should implement nsIContent.
290 NS_ENSURE_TRUE(thisContent, NS_ERROR_FAILURE);
292 // Check for a ShadowRoot because link elements are inert in a
293 // ShadowRoot.
294 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
295 if (thisContent->IsHTML(nsGkAtoms::link) &&
296 (aOldShadowRoot || containingShadow)) {
297 return NS_OK;
298 }
300 Element* oldScopeElement = GetScopeElement(mStyleSheet);
302 if (mStyleSheet && aOldDocument) {
303 // We're removing the link element from the document, unload the
304 // stylesheet. We want to do this even if updates are disabled, since
305 // otherwise a sheet with a stale linking element pointer will be hanging
306 // around -- not good!
307 if (aOldShadowRoot) {
308 aOldShadowRoot->RemoveSheet(mStyleSheet);
309 } else {
310 aOldDocument->BeginUpdate(UPDATE_STYLE);
311 aOldDocument->RemoveStyleSheet(mStyleSheet);
312 aOldDocument->EndUpdate(UPDATE_STYLE);
313 }
315 nsStyleLinkElement::SetStyleSheet(nullptr);
316 if (oldScopeElement) {
317 UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
318 }
319 }
321 // When static documents are created, stylesheets are cloned manually.
322 if (mDontLoadStyle || !mUpdatesEnabled ||
323 thisContent->OwnerDoc()->IsStaticDocument()) {
324 return NS_OK;
325 }
327 nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
329 if (!doc || !doc->CSSLoader()->GetEnabled()) {
330 return NS_OK;
331 }
333 bool isInline;
334 nsCOMPtr<nsIURI> uri = GetStyleSheetURL(&isInline);
336 if (!aForceUpdate && mStyleSheet && !isInline && uri) {
337 nsIURI* oldURI = mStyleSheet->GetSheetURI();
338 if (oldURI) {
339 bool equal;
340 nsresult rv = oldURI->Equals(uri, &equal);
341 if (NS_SUCCEEDED(rv) && equal) {
342 return NS_OK; // We already loaded this stylesheet
343 }
344 }
345 }
347 if (mStyleSheet) {
348 if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
349 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
350 containingShadow->RemoveSheet(mStyleSheet);
351 } else {
352 doc->BeginUpdate(UPDATE_STYLE);
353 doc->RemoveStyleSheet(mStyleSheet);
354 doc->EndUpdate(UPDATE_STYLE);
355 }
357 nsStyleLinkElement::SetStyleSheet(nullptr);
358 }
360 if (!uri && !isInline) {
361 return NS_OK; // If href is empty and this is not inline style then just bail
362 }
364 nsAutoString title, type, media;
365 bool isScoped;
366 bool isAlternate;
368 GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
370 if (!type.LowerCaseEqualsLiteral("text/css")) {
371 return NS_OK;
372 }
374 Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
375 if (scopeElement) {
376 NS_ASSERTION(isInline, "non-inline style must not have scope element");
377 SetIsElementInStyleScopeFlagOnSubtree(scopeElement);
378 }
380 bool doneLoading = false;
381 nsresult rv = NS_OK;
382 if (isInline) {
383 nsAutoString text;
384 if (!nsContentUtils::GetNodeTextContent(thisContent, false, text)) {
385 return NS_ERROR_OUT_OF_MEMORY;
386 }
388 MOZ_ASSERT(thisContent->Tag() != nsGkAtoms::link,
389 "<link> is not 'inline', and needs different CSP checks");
390 if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent,
391 thisContent->NodePrincipal(),
392 doc->GetDocumentURI(),
393 mLineNumber, text, &rv))
394 return rv;
396 // Parse the style sheet.
397 rv = doc->CSSLoader()->
398 LoadInlineStyle(thisContent, text, mLineNumber, title, media,
399 scopeElement, aObserver, &doneLoading, &isAlternate);
400 }
401 else {
402 // XXXbz clone the URI here to work around content policies modifying URIs.
403 nsCOMPtr<nsIURI> clonedURI;
404 uri->Clone(getter_AddRefs(clonedURI));
405 NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
406 rv = doc->CSSLoader()->
407 LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
408 GetCORSMode(), aObserver, &isAlternate);
409 if (NS_FAILED(rv)) {
410 // Don't propagate LoadStyleLink() errors further than this, since some
411 // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
412 // things like a stylesheet load being blocked by the security system.
413 doneLoading = true;
414 isAlternate = false;
415 rv = NS_OK;
416 }
417 }
419 NS_ENSURE_SUCCESS(rv, rv);
421 *aWillNotify = !doneLoading;
422 *aIsAlternate = isAlternate;
424 return NS_OK;
425 }
427 void
428 nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped)
429 {
430 if (!mStyleSheet) {
431 return;
432 }
434 nsCOMPtr<nsIContent> thisContent;
435 CallQueryInterface(this, getter_AddRefs(thisContent));
437 Element* oldScopeElement = mStyleSheet->GetScopeElement();
438 Element* newScopeElement = aIsNowScoped ?
439 thisContent->GetParentElement() :
440 nullptr;
442 if (oldScopeElement == newScopeElement) {
443 return;
444 }
446 nsIDocument* document = thisContent->GetOwnerDocument();
448 if (thisContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
449 ShadowRoot* containingShadow = thisContent->GetContainingShadow();
450 containingShadow->RemoveSheet(mStyleSheet);
452 mStyleSheet->SetScopeElement(newScopeElement);
454 containingShadow->InsertSheet(mStyleSheet, thisContent);
455 } else {
456 document->BeginUpdate(UPDATE_STYLE);
457 document->RemoveStyleSheet(mStyleSheet);
459 mStyleSheet->SetScopeElement(newScopeElement);
461 document->AddStyleSheet(mStyleSheet);
462 document->EndUpdate(UPDATE_STYLE);
463 }
465 if (oldScopeElement) {
466 UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
467 }
468 if (newScopeElement) {
469 SetIsElementInStyleScopeFlagOnSubtree(newScopeElement);
470 }
471 }