|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 // vim:cindent:tabstop=2:expandtab:shiftwidth=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/. */ |
|
6 |
|
7 /* representation of a CSS style sheet */ |
|
8 |
|
9 #include "nsCSSStyleSheet.h" |
|
10 |
|
11 #include "nsIAtom.h" |
|
12 #include "nsCSSRuleProcessor.h" |
|
13 #include "mozilla/MemoryReporting.h" |
|
14 #include "mozilla/dom/Element.h" |
|
15 #include "mozilla/dom/MediaListBinding.h" |
|
16 #include "mozilla/css/NameSpaceRule.h" |
|
17 #include "mozilla/css/GroupRule.h" |
|
18 #include "mozilla/css/ImportRule.h" |
|
19 #include "nsIMediaList.h" |
|
20 #include "nsIDocument.h" |
|
21 #include "nsPresContext.h" |
|
22 #include "nsGkAtoms.h" |
|
23 #include "nsString.h" |
|
24 #include "nsTArray.h" |
|
25 #include "nsIDOMCSSStyleSheet.h" |
|
26 #include "nsICSSRuleList.h" |
|
27 #include "nsIDOMMediaList.h" |
|
28 #include "nsIDOMNode.h" |
|
29 #include "nsError.h" |
|
30 #include "nsCSSParser.h" |
|
31 #include "mozilla/css/Loader.h" |
|
32 #include "nsICSSLoaderObserver.h" |
|
33 #include "nsNameSpaceManager.h" |
|
34 #include "nsXMLNameSpaceMap.h" |
|
35 #include "nsCOMPtr.h" |
|
36 #include "nsContentUtils.h" |
|
37 #include "nsIScriptSecurityManager.h" |
|
38 #include "mozAutoDocUpdate.h" |
|
39 #include "nsRuleNode.h" |
|
40 #include "nsMediaFeatures.h" |
|
41 #include "nsDOMClassInfoID.h" |
|
42 #include "mozilla/Likely.h" |
|
43 #include "mozilla/dom/CSSStyleSheetBinding.h" |
|
44 #include "nsComponentManagerUtils.h" |
|
45 |
|
46 using namespace mozilla; |
|
47 using namespace mozilla::dom; |
|
48 |
|
49 |
|
50 // ------------------------------- |
|
51 // Style Rule List for the DOM |
|
52 // |
|
53 class CSSRuleListImpl : public nsICSSRuleList |
|
54 { |
|
55 public: |
|
56 CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet); |
|
57 |
|
58 NS_DECL_ISUPPORTS |
|
59 |
|
60 virtual nsIDOMCSSRule* |
|
61 IndexedGetter(uint32_t aIndex, bool& aFound) MOZ_OVERRIDE; |
|
62 virtual uint32_t |
|
63 Length() MOZ_OVERRIDE; |
|
64 |
|
65 void DropReference() { mStyleSheet = nullptr; } |
|
66 |
|
67 protected: |
|
68 virtual ~CSSRuleListImpl(); |
|
69 |
|
70 nsCSSStyleSheet* mStyleSheet; |
|
71 }; |
|
72 |
|
73 CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet) |
|
74 { |
|
75 // Not reference counted to avoid circular references. |
|
76 // The style sheet will tell us when its going away. |
|
77 mStyleSheet = aStyleSheet; |
|
78 } |
|
79 |
|
80 CSSRuleListImpl::~CSSRuleListImpl() |
|
81 { |
|
82 } |
|
83 |
|
84 DOMCI_DATA(CSSRuleList, CSSRuleListImpl) |
|
85 |
|
86 // QueryInterface implementation for CSSRuleList |
|
87 NS_INTERFACE_MAP_BEGIN(CSSRuleListImpl) |
|
88 NS_INTERFACE_MAP_ENTRY(nsICSSRuleList) |
|
89 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList) |
|
90 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
91 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSRuleList) |
|
92 NS_INTERFACE_MAP_END |
|
93 |
|
94 |
|
95 NS_IMPL_ADDREF(CSSRuleListImpl) |
|
96 NS_IMPL_RELEASE(CSSRuleListImpl) |
|
97 |
|
98 |
|
99 uint32_t |
|
100 CSSRuleListImpl::Length() |
|
101 { |
|
102 if (!mStyleSheet) { |
|
103 return 0; |
|
104 } |
|
105 |
|
106 return SafeCast<uint32_t>(mStyleSheet->StyleRuleCount()); |
|
107 } |
|
108 |
|
109 nsIDOMCSSRule* |
|
110 CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound) |
|
111 { |
|
112 aFound = false; |
|
113 |
|
114 if (mStyleSheet) { |
|
115 // ensure rules have correct parent |
|
116 mStyleSheet->EnsureUniqueInner(); |
|
117 css::Rule* rule = mStyleSheet->GetStyleRuleAt(aIndex); |
|
118 if (rule) { |
|
119 aFound = true; |
|
120 return rule->GetDOMRule(); |
|
121 } |
|
122 } |
|
123 |
|
124 // Per spec: "Return Value ... null if ... not a valid index." |
|
125 return nullptr; |
|
126 } |
|
127 |
|
128 template <class Numeric> |
|
129 int32_t DoCompare(Numeric a, Numeric b) |
|
130 { |
|
131 if (a == b) |
|
132 return 0; |
|
133 if (a < b) |
|
134 return -1; |
|
135 return 1; |
|
136 } |
|
137 |
|
138 bool |
|
139 nsMediaExpression::Matches(nsPresContext *aPresContext, |
|
140 const nsCSSValue& aActualValue) const |
|
141 { |
|
142 const nsCSSValue& actual = aActualValue; |
|
143 const nsCSSValue& required = mValue; |
|
144 |
|
145 // If we don't have the feature, the match fails. |
|
146 if (actual.GetUnit() == eCSSUnit_Null) { |
|
147 return false; |
|
148 } |
|
149 |
|
150 // If the expression had no value to match, the match succeeds, |
|
151 // unless the value is an integer 0 or a zero length. |
|
152 if (required.GetUnit() == eCSSUnit_Null) { |
|
153 if (actual.GetUnit() == eCSSUnit_Integer) |
|
154 return actual.GetIntValue() != 0; |
|
155 if (actual.IsLengthUnit()) |
|
156 return actual.GetFloatValue() != 0; |
|
157 return true; |
|
158 } |
|
159 |
|
160 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxAllowed || |
|
161 mRange == nsMediaExpression::eEqual, "yikes"); |
|
162 int32_t cmp; // -1 (actual < required) |
|
163 // 0 (actual == required) |
|
164 // 1 (actual > required) |
|
165 switch (mFeature->mValueType) { |
|
166 case nsMediaFeature::eLength: |
|
167 { |
|
168 NS_ASSERTION(actual.IsLengthUnit(), "bad actual value"); |
|
169 NS_ASSERTION(required.IsLengthUnit(), "bad required value"); |
|
170 nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont( |
|
171 aPresContext, actual); |
|
172 nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont( |
|
173 aPresContext, required); |
|
174 cmp = DoCompare(actualCoord, requiredCoord); |
|
175 } |
|
176 break; |
|
177 case nsMediaFeature::eInteger: |
|
178 case nsMediaFeature::eBoolInteger: |
|
179 { |
|
180 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Integer, |
|
181 "bad actual value"); |
|
182 NS_ASSERTION(required.GetUnit() == eCSSUnit_Integer, |
|
183 "bad required value"); |
|
184 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger || |
|
185 actual.GetIntValue() == 0 || actual.GetIntValue() == 1, |
|
186 "bad actual bool integer value"); |
|
187 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger || |
|
188 required.GetIntValue() == 0 || required.GetIntValue() == 1, |
|
189 "bad required bool integer value"); |
|
190 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue()); |
|
191 } |
|
192 break; |
|
193 case nsMediaFeature::eFloat: |
|
194 { |
|
195 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Number, |
|
196 "bad actual value"); |
|
197 NS_ASSERTION(required.GetUnit() == eCSSUnit_Number, |
|
198 "bad required value"); |
|
199 cmp = DoCompare(actual.GetFloatValue(), required.GetFloatValue()); |
|
200 } |
|
201 break; |
|
202 case nsMediaFeature::eIntRatio: |
|
203 { |
|
204 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array && |
|
205 actual.GetArrayValue()->Count() == 2 && |
|
206 actual.GetArrayValue()->Item(0).GetUnit() == |
|
207 eCSSUnit_Integer && |
|
208 actual.GetArrayValue()->Item(1).GetUnit() == |
|
209 eCSSUnit_Integer, |
|
210 "bad actual value"); |
|
211 NS_ASSERTION(required.GetUnit() == eCSSUnit_Array && |
|
212 required.GetArrayValue()->Count() == 2 && |
|
213 required.GetArrayValue()->Item(0).GetUnit() == |
|
214 eCSSUnit_Integer && |
|
215 required.GetArrayValue()->Item(1).GetUnit() == |
|
216 eCSSUnit_Integer, |
|
217 "bad required value"); |
|
218 // Convert to int64_t so we can multiply without worry. Note |
|
219 // that while the spec requires that both halves of |required| |
|
220 // be positive, the numerator or denominator of |actual| might |
|
221 // be zero (e.g., when testing 'aspect-ratio' on a 0-width or |
|
222 // 0-height iframe). |
|
223 int64_t actualNum = actual.GetArrayValue()->Item(0).GetIntValue(), |
|
224 actualDen = actual.GetArrayValue()->Item(1).GetIntValue(), |
|
225 requiredNum = required.GetArrayValue()->Item(0).GetIntValue(), |
|
226 requiredDen = required.GetArrayValue()->Item(1).GetIntValue(); |
|
227 cmp = DoCompare(actualNum * requiredDen, requiredNum * actualDen); |
|
228 } |
|
229 break; |
|
230 case nsMediaFeature::eResolution: |
|
231 { |
|
232 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch || |
|
233 actual.GetUnit() == eCSSUnit_Pixel || |
|
234 actual.GetUnit() == eCSSUnit_Centimeter, |
|
235 "bad actual value"); |
|
236 NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch || |
|
237 required.GetUnit() == eCSSUnit_Pixel || |
|
238 required.GetUnit() == eCSSUnit_Centimeter, |
|
239 "bad required value"); |
|
240 float actualDPI = actual.GetFloatValue(); |
|
241 if (actual.GetUnit() == eCSSUnit_Centimeter) { |
|
242 actualDPI = actualDPI * 2.54f; |
|
243 } else if (actual.GetUnit() == eCSSUnit_Pixel) { |
|
244 actualDPI = actualDPI * 96.0f; |
|
245 } |
|
246 float requiredDPI = required.GetFloatValue(); |
|
247 if (required.GetUnit() == eCSSUnit_Centimeter) { |
|
248 requiredDPI = requiredDPI * 2.54f; |
|
249 } else if (required.GetUnit() == eCSSUnit_Pixel) { |
|
250 requiredDPI = requiredDPI * 96.0f; |
|
251 } |
|
252 cmp = DoCompare(actualDPI, requiredDPI); |
|
253 } |
|
254 break; |
|
255 case nsMediaFeature::eEnumerated: |
|
256 { |
|
257 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated, |
|
258 "bad actual value"); |
|
259 NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated, |
|
260 "bad required value"); |
|
261 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed, |
|
262 "bad range"); // we asserted above about mRange |
|
263 // We don't really need DoCompare, but it doesn't hurt (and |
|
264 // maybe the compiler will condense this case with eInteger). |
|
265 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue()); |
|
266 } |
|
267 break; |
|
268 case nsMediaFeature::eIdent: |
|
269 { |
|
270 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Ident, |
|
271 "bad actual value"); |
|
272 NS_ASSERTION(required.GetUnit() == eCSSUnit_Ident, |
|
273 "bad required value"); |
|
274 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed, |
|
275 "bad range"); |
|
276 cmp = !(actual == required); // string comparison |
|
277 } |
|
278 break; |
|
279 } |
|
280 switch (mRange) { |
|
281 case nsMediaExpression::eMin: |
|
282 return cmp != -1; |
|
283 case nsMediaExpression::eMax: |
|
284 return cmp != 1; |
|
285 case nsMediaExpression::eEqual: |
|
286 return cmp == 0; |
|
287 } |
|
288 NS_NOTREACHED("unexpected mRange"); |
|
289 return false; |
|
290 } |
|
291 |
|
292 void |
|
293 nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression* aExpression, |
|
294 bool aExpressionMatches) |
|
295 { |
|
296 const nsMediaFeature *feature = aExpression->mFeature; |
|
297 FeatureEntry *entry = nullptr; |
|
298 for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) { |
|
299 if (mFeatureCache[i].mFeature == feature) { |
|
300 entry = &mFeatureCache[i]; |
|
301 break; |
|
302 } |
|
303 } |
|
304 if (!entry) { |
|
305 entry = mFeatureCache.AppendElement(); |
|
306 if (!entry) { |
|
307 return; /* out of memory */ |
|
308 } |
|
309 entry->mFeature = feature; |
|
310 } |
|
311 |
|
312 ExpressionEntry eentry = { *aExpression, aExpressionMatches }; |
|
313 entry->mExpressions.AppendElement(eentry); |
|
314 } |
|
315 |
|
316 bool |
|
317 nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const |
|
318 { |
|
319 if (aPresContext->Medium() != mMedium) { |
|
320 return false; |
|
321 } |
|
322 |
|
323 for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) { |
|
324 const FeatureEntry *entry = &mFeatureCache[i]; |
|
325 nsCSSValue actual; |
|
326 nsresult rv = |
|
327 (entry->mFeature->mGetter)(aPresContext, entry->mFeature, actual); |
|
328 NS_ENSURE_SUCCESS(rv, false); // any better ideas? |
|
329 |
|
330 for (uint32_t j = 0; j < entry->mExpressions.Length(); ++j) { |
|
331 const ExpressionEntry &eentry = entry->mExpressions[j]; |
|
332 if (eentry.mExpression.Matches(aPresContext, actual) != |
|
333 eentry.mExpressionMatches) { |
|
334 return false; |
|
335 } |
|
336 } |
|
337 } |
|
338 |
|
339 return true; |
|
340 } |
|
341 |
|
342 void |
|
343 nsMediaQuery::AppendToString(nsAString& aString) const |
|
344 { |
|
345 if (mHadUnknownExpression) { |
|
346 aString.AppendLiteral("not all"); |
|
347 return; |
|
348 } |
|
349 |
|
350 NS_ASSERTION(!mNegated || !mHasOnly, "can't have not and only"); |
|
351 NS_ASSERTION(!mTypeOmitted || (!mNegated && !mHasOnly), |
|
352 "can't have not or only when type is omitted"); |
|
353 if (!mTypeOmitted) { |
|
354 if (mNegated) { |
|
355 aString.AppendLiteral("not "); |
|
356 } else if (mHasOnly) { |
|
357 aString.AppendLiteral("only "); |
|
358 } |
|
359 aString.Append(nsDependentAtomString(mMediaType)); |
|
360 } |
|
361 |
|
362 for (uint32_t i = 0, i_end = mExpressions.Length(); i < i_end; ++i) { |
|
363 if (i > 0 || !mTypeOmitted) |
|
364 aString.AppendLiteral(" and "); |
|
365 aString.AppendLiteral("("); |
|
366 |
|
367 const nsMediaExpression &expr = mExpressions[i]; |
|
368 if (expr.mRange == nsMediaExpression::eMin) { |
|
369 aString.AppendLiteral("min-"); |
|
370 } else if (expr.mRange == nsMediaExpression::eMax) { |
|
371 aString.AppendLiteral("max-"); |
|
372 } |
|
373 |
|
374 const nsMediaFeature *feature = expr.mFeature; |
|
375 aString.Append(nsDependentAtomString(*feature->mName)); |
|
376 |
|
377 if (expr.mValue.GetUnit() != eCSSUnit_Null) { |
|
378 aString.AppendLiteral(": "); |
|
379 switch (feature->mValueType) { |
|
380 case nsMediaFeature::eLength: |
|
381 NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit"); |
|
382 // Use 'width' as a property that takes length values |
|
383 // written in the normal way. |
|
384 expr.mValue.AppendToString(eCSSProperty_width, aString, |
|
385 nsCSSValue::eNormalized); |
|
386 break; |
|
387 case nsMediaFeature::eInteger: |
|
388 case nsMediaFeature::eBoolInteger: |
|
389 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer, |
|
390 "bad unit"); |
|
391 // Use 'z-index' as a property that takes integer values |
|
392 // written without anything extra. |
|
393 expr.mValue.AppendToString(eCSSProperty_z_index, aString, |
|
394 nsCSSValue::eNormalized); |
|
395 break; |
|
396 case nsMediaFeature::eFloat: |
|
397 { |
|
398 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Number, |
|
399 "bad unit"); |
|
400 // Use 'line-height' as a property that takes float values |
|
401 // written in the normal way. |
|
402 expr.mValue.AppendToString(eCSSProperty_line_height, aString, |
|
403 nsCSSValue::eNormalized); |
|
404 } |
|
405 break; |
|
406 case nsMediaFeature::eIntRatio: |
|
407 { |
|
408 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array, |
|
409 "bad unit"); |
|
410 nsCSSValue::Array *array = expr.mValue.GetArrayValue(); |
|
411 NS_ASSERTION(array->Count() == 2, "unexpected length"); |
|
412 NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer, |
|
413 "bad unit"); |
|
414 NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer, |
|
415 "bad unit"); |
|
416 array->Item(0).AppendToString(eCSSProperty_z_index, aString, |
|
417 nsCSSValue::eNormalized); |
|
418 aString.AppendLiteral("/"); |
|
419 array->Item(1).AppendToString(eCSSProperty_z_index, aString, |
|
420 nsCSSValue::eNormalized); |
|
421 } |
|
422 break; |
|
423 case nsMediaFeature::eResolution: |
|
424 { |
|
425 aString.AppendFloat(expr.mValue.GetFloatValue()); |
|
426 if (expr.mValue.GetUnit() == eCSSUnit_Inch) { |
|
427 aString.AppendLiteral("dpi"); |
|
428 } else if (expr.mValue.GetUnit() == eCSSUnit_Pixel) { |
|
429 aString.AppendLiteral("dppx"); |
|
430 } else { |
|
431 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter, |
|
432 "bad unit"); |
|
433 aString.AppendLiteral("dpcm"); |
|
434 } |
|
435 } |
|
436 break; |
|
437 case nsMediaFeature::eEnumerated: |
|
438 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Enumerated, |
|
439 "bad unit"); |
|
440 AppendASCIItoUTF16( |
|
441 nsCSSProps::ValueToKeyword(expr.mValue.GetIntValue(), |
|
442 feature->mData.mKeywordTable), |
|
443 aString); |
|
444 break; |
|
445 case nsMediaFeature::eIdent: |
|
446 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Ident, |
|
447 "bad unit"); |
|
448 aString.Append(expr.mValue.GetStringBufferValue()); |
|
449 break; |
|
450 } |
|
451 } |
|
452 |
|
453 aString.AppendLiteral(")"); |
|
454 } |
|
455 } |
|
456 |
|
457 nsMediaQuery* |
|
458 nsMediaQuery::Clone() const |
|
459 { |
|
460 return new nsMediaQuery(*this); |
|
461 } |
|
462 |
|
463 bool |
|
464 nsMediaQuery::Matches(nsPresContext* aPresContext, |
|
465 nsMediaQueryResultCacheKey* aKey) const |
|
466 { |
|
467 if (mHadUnknownExpression) |
|
468 return false; |
|
469 |
|
470 bool match = |
|
471 mMediaType == aPresContext->Medium() || mMediaType == nsGkAtoms::all; |
|
472 for (uint32_t i = 0, i_end = mExpressions.Length(); match && i < i_end; ++i) { |
|
473 const nsMediaExpression &expr = mExpressions[i]; |
|
474 nsCSSValue actual; |
|
475 nsresult rv = |
|
476 (expr.mFeature->mGetter)(aPresContext, expr.mFeature, actual); |
|
477 NS_ENSURE_SUCCESS(rv, false); // any better ideas? |
|
478 |
|
479 match = expr.Matches(aPresContext, actual); |
|
480 if (aKey) { |
|
481 aKey->AddExpression(&expr, match); |
|
482 } |
|
483 } |
|
484 |
|
485 return match == !mNegated; |
|
486 } |
|
487 |
|
488 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMediaList) |
|
489 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
490 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList) |
|
491 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
492 NS_INTERFACE_MAP_END |
|
493 |
|
494 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMediaList) |
|
495 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMediaList) |
|
496 |
|
497 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsMediaList) |
|
498 |
|
499 nsMediaList::nsMediaList() |
|
500 : mStyleSheet(nullptr) |
|
501 { |
|
502 SetIsDOMBinding(); |
|
503 } |
|
504 |
|
505 nsMediaList::~nsMediaList() |
|
506 { |
|
507 } |
|
508 |
|
509 /* virtual */ JSObject* |
|
510 nsMediaList::WrapObject(JSContext* aCx) |
|
511 { |
|
512 return MediaListBinding::Wrap(aCx, this); |
|
513 } |
|
514 |
|
515 void |
|
516 nsMediaList::GetText(nsAString& aMediaText) |
|
517 { |
|
518 aMediaText.Truncate(); |
|
519 |
|
520 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { |
|
521 nsMediaQuery* query = mArray[i]; |
|
522 |
|
523 query->AppendToString(aMediaText); |
|
524 |
|
525 if (i + 1 < i_end) { |
|
526 aMediaText.AppendLiteral(", "); |
|
527 } |
|
528 } |
|
529 } |
|
530 |
|
531 // XXXbz this is so ill-defined in the spec, it's not clear quite what |
|
532 // it should be doing.... |
|
533 void |
|
534 nsMediaList::SetText(const nsAString& aMediaText) |
|
535 { |
|
536 nsCSSParser parser; |
|
537 |
|
538 bool htmlMode = mStyleSheet && mStyleSheet->GetOwnerNode(); |
|
539 |
|
540 parser.ParseMediaList(aMediaText, nullptr, 0, this, htmlMode); |
|
541 } |
|
542 |
|
543 bool |
|
544 nsMediaList::Matches(nsPresContext* aPresContext, |
|
545 nsMediaQueryResultCacheKey* aKey) |
|
546 { |
|
547 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { |
|
548 if (mArray[i]->Matches(aPresContext, aKey)) { |
|
549 return true; |
|
550 } |
|
551 } |
|
552 return mArray.IsEmpty(); |
|
553 } |
|
554 |
|
555 nsresult |
|
556 nsMediaList::SetStyleSheet(nsCSSStyleSheet *aSheet) |
|
557 { |
|
558 NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet, |
|
559 "multiple style sheets competing for one media list"); |
|
560 mStyleSheet = aSheet; |
|
561 return NS_OK; |
|
562 } |
|
563 |
|
564 already_AddRefed<nsMediaList> |
|
565 nsMediaList::Clone() |
|
566 { |
|
567 nsRefPtr<nsMediaList> result = new nsMediaList(); |
|
568 result->mArray.AppendElements(mArray.Length()); |
|
569 for (uint32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { |
|
570 result->mArray[i] = mArray[i]->Clone(); |
|
571 MOZ_ASSERT(result->mArray[i]); |
|
572 } |
|
573 return result.forget(); |
|
574 } |
|
575 |
|
576 NS_IMETHODIMP |
|
577 nsMediaList::GetMediaText(nsAString& aMediaText) |
|
578 { |
|
579 GetText(aMediaText); |
|
580 return NS_OK; |
|
581 } |
|
582 |
|
583 // "sheet" should be an nsCSSStyleSheet and "doc" should be an |
|
584 // nsCOMPtr<nsIDocument> |
|
585 #define BEGIN_MEDIA_CHANGE(sheet, doc) \ |
|
586 if (sheet) { \ |
|
587 doc = sheet->GetOwningDocument(); \ |
|
588 } \ |
|
589 mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); \ |
|
590 if (sheet) { \ |
|
591 sheet->WillDirty(); \ |
|
592 } |
|
593 |
|
594 #define END_MEDIA_CHANGE(sheet, doc) \ |
|
595 if (sheet) { \ |
|
596 sheet->DidDirty(); \ |
|
597 } \ |
|
598 /* XXXldb Pass something meaningful? */ \ |
|
599 if (doc) { \ |
|
600 doc->StyleRuleChanged(sheet, nullptr, nullptr); \ |
|
601 } |
|
602 |
|
603 |
|
604 NS_IMETHODIMP |
|
605 nsMediaList::SetMediaText(const nsAString& aMediaText) |
|
606 { |
|
607 nsCOMPtr<nsIDocument> doc; |
|
608 |
|
609 BEGIN_MEDIA_CHANGE(mStyleSheet, doc) |
|
610 |
|
611 SetText(aMediaText); |
|
612 |
|
613 END_MEDIA_CHANGE(mStyleSheet, doc) |
|
614 |
|
615 return NS_OK; |
|
616 } |
|
617 |
|
618 NS_IMETHODIMP |
|
619 nsMediaList::GetLength(uint32_t* aLength) |
|
620 { |
|
621 NS_ENSURE_ARG_POINTER(aLength); |
|
622 |
|
623 *aLength = Length(); |
|
624 return NS_OK; |
|
625 } |
|
626 |
|
627 NS_IMETHODIMP |
|
628 nsMediaList::Item(uint32_t aIndex, nsAString& aReturn) |
|
629 { |
|
630 bool dummy; |
|
631 IndexedGetter(aIndex, dummy, aReturn); |
|
632 return NS_OK; |
|
633 } |
|
634 |
|
635 void |
|
636 nsMediaList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aReturn) |
|
637 { |
|
638 if (aIndex < Length()) { |
|
639 aFound = true; |
|
640 aReturn.Truncate(); |
|
641 mArray[aIndex]->AppendToString(aReturn); |
|
642 } else { |
|
643 aFound = false; |
|
644 SetDOMStringToNull(aReturn); |
|
645 } |
|
646 } |
|
647 |
|
648 NS_IMETHODIMP |
|
649 nsMediaList::DeleteMedium(const nsAString& aOldMedium) |
|
650 { |
|
651 nsresult rv = NS_OK; |
|
652 nsCOMPtr<nsIDocument> doc; |
|
653 |
|
654 BEGIN_MEDIA_CHANGE(mStyleSheet, doc) |
|
655 |
|
656 rv = Delete(aOldMedium); |
|
657 if (NS_FAILED(rv)) |
|
658 return rv; |
|
659 |
|
660 END_MEDIA_CHANGE(mStyleSheet, doc) |
|
661 |
|
662 return rv; |
|
663 } |
|
664 |
|
665 NS_IMETHODIMP |
|
666 nsMediaList::AppendMedium(const nsAString& aNewMedium) |
|
667 { |
|
668 nsresult rv = NS_OK; |
|
669 nsCOMPtr<nsIDocument> doc; |
|
670 |
|
671 BEGIN_MEDIA_CHANGE(mStyleSheet, doc) |
|
672 |
|
673 rv = Append(aNewMedium); |
|
674 if (NS_FAILED(rv)) |
|
675 return rv; |
|
676 |
|
677 END_MEDIA_CHANGE(mStyleSheet, doc) |
|
678 |
|
679 return rv; |
|
680 } |
|
681 |
|
682 nsresult |
|
683 nsMediaList::Delete(const nsAString& aOldMedium) |
|
684 { |
|
685 if (aOldMedium.IsEmpty()) |
|
686 return NS_ERROR_DOM_NOT_FOUND_ERR; |
|
687 |
|
688 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { |
|
689 nsMediaQuery* query = mArray[i]; |
|
690 |
|
691 nsAutoString buf; |
|
692 query->AppendToString(buf); |
|
693 if (buf == aOldMedium) { |
|
694 mArray.RemoveElementAt(i); |
|
695 return NS_OK; |
|
696 } |
|
697 } |
|
698 |
|
699 return NS_ERROR_DOM_NOT_FOUND_ERR; |
|
700 } |
|
701 |
|
702 nsresult |
|
703 nsMediaList::Append(const nsAString& aNewMedium) |
|
704 { |
|
705 if (aNewMedium.IsEmpty()) |
|
706 return NS_ERROR_DOM_NOT_FOUND_ERR; |
|
707 |
|
708 Delete(aNewMedium); |
|
709 |
|
710 nsresult rv = NS_OK; |
|
711 nsTArray<nsAutoPtr<nsMediaQuery> > buf; |
|
712 mArray.SwapElements(buf); |
|
713 SetText(aNewMedium); |
|
714 if (mArray.Length() == 1) { |
|
715 nsMediaQuery *query = mArray[0].forget(); |
|
716 if (!buf.AppendElement(query)) { |
|
717 delete query; |
|
718 rv = NS_ERROR_OUT_OF_MEMORY; |
|
719 } |
|
720 } |
|
721 |
|
722 mArray.SwapElements(buf); |
|
723 return rv; |
|
724 } |
|
725 |
|
726 // ------------------------------- |
|
727 // CSS Style Sheet Inner Data Container |
|
728 // |
|
729 |
|
730 |
|
731 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheet* aPrimarySheet, |
|
732 CORSMode aCORSMode) |
|
733 : mSheets(), |
|
734 mCORSMode(aCORSMode), |
|
735 mComplete(false) |
|
736 #ifdef DEBUG |
|
737 , mPrincipalSet(false) |
|
738 #endif |
|
739 { |
|
740 MOZ_COUNT_CTOR(nsCSSStyleSheetInner); |
|
741 mSheets.AppendElement(aPrimarySheet); |
|
742 |
|
743 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1"); |
|
744 if (!mPrincipal) { |
|
745 NS_RUNTIMEABORT("OOM"); |
|
746 } |
|
747 } |
|
748 |
|
749 static bool SetStyleSheetReference(css::Rule* aRule, void* aSheet) |
|
750 { |
|
751 if (aRule) { |
|
752 aRule->SetStyleSheet(static_cast<nsCSSStyleSheet*>(aSheet)); |
|
753 } |
|
754 return true; |
|
755 } |
|
756 |
|
757 struct ChildSheetListBuilder { |
|
758 nsRefPtr<nsCSSStyleSheet>* sheetSlot; |
|
759 nsCSSStyleSheet* parent; |
|
760 |
|
761 void SetParentLinks(nsCSSStyleSheet* aSheet) { |
|
762 aSheet->mParent = parent; |
|
763 aSheet->SetOwningDocument(parent->mDocument); |
|
764 } |
|
765 |
|
766 static void ReparentChildList(nsCSSStyleSheet* aPrimarySheet, |
|
767 nsCSSStyleSheet* aFirstChild) |
|
768 { |
|
769 for (nsCSSStyleSheet *child = aFirstChild; child; child = child->mNext) { |
|
770 child->mParent = aPrimarySheet; |
|
771 child->SetOwningDocument(aPrimarySheet->mDocument); |
|
772 } |
|
773 } |
|
774 }; |
|
775 |
|
776 bool |
|
777 nsCSSStyleSheet::RebuildChildList(css::Rule* aRule, void* aBuilder) |
|
778 { |
|
779 int32_t type = aRule->GetType(); |
|
780 if (type < css::Rule::IMPORT_RULE) { |
|
781 // Keep going till we get to the import rules. |
|
782 return true; |
|
783 } |
|
784 |
|
785 if (type != css::Rule::IMPORT_RULE) { |
|
786 // We're past all the import rules; stop the enumeration. |
|
787 return false; |
|
788 } |
|
789 |
|
790 ChildSheetListBuilder* builder = |
|
791 static_cast<ChildSheetListBuilder*>(aBuilder); |
|
792 |
|
793 // XXXbz We really need to decomtaminate all this stuff. Is there a reason |
|
794 // that I can't just QI to ImportRule and get an nsCSSStyleSheet |
|
795 // directly from it? |
|
796 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(aRule)); |
|
797 NS_ASSERTION(importRule, "GetType lied"); |
|
798 |
|
799 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet; |
|
800 importRule->GetStyleSheet(getter_AddRefs(childSheet)); |
|
801 |
|
802 // Have to do this QI to be safe, since XPConnect can fake |
|
803 // nsIDOMCSSStyleSheets |
|
804 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(childSheet); |
|
805 if (!cssSheet) { |
|
806 return true; |
|
807 } |
|
808 |
|
809 (*builder->sheetSlot) = cssSheet; |
|
810 builder->SetParentLinks(*builder->sheetSlot); |
|
811 builder->sheetSlot = &(*builder->sheetSlot)->mNext; |
|
812 return true; |
|
813 } |
|
814 |
|
815 size_t |
|
816 nsCSSStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
817 { |
|
818 size_t n = 0; |
|
819 const nsCSSStyleSheet* s = this; |
|
820 while (s) { |
|
821 n += aMallocSizeOf(s); |
|
822 |
|
823 // Each inner can be shared by multiple sheets. So we only count the inner |
|
824 // if this sheet is the first one in the list of those sharing it. As a |
|
825 // result, the first such sheet takes all the blame for the memory |
|
826 // consumption of the inner, which isn't ideal but it's better than |
|
827 // double-counting the inner. |
|
828 if (s->mInner->mSheets[0] == s) { |
|
829 n += s->mInner->SizeOfIncludingThis(aMallocSizeOf); |
|
830 } |
|
831 |
|
832 // Measurement of the following members may be added later if DMD finds it |
|
833 // is worthwhile: |
|
834 // - s->mTitle |
|
835 // - s->mMedia |
|
836 // - s->mRuleCollection |
|
837 // - s->mRuleProcessors |
|
838 // |
|
839 // The following members are not measured: |
|
840 // - s->mOwnerRule, because it's non-owning |
|
841 |
|
842 s = s->mNext; |
|
843 } |
|
844 return n; |
|
845 } |
|
846 |
|
847 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy, |
|
848 nsCSSStyleSheet* aPrimarySheet) |
|
849 : mSheets(), |
|
850 mSheetURI(aCopy.mSheetURI), |
|
851 mOriginalSheetURI(aCopy.mOriginalSheetURI), |
|
852 mBaseURI(aCopy.mBaseURI), |
|
853 mPrincipal(aCopy.mPrincipal), |
|
854 mCORSMode(aCopy.mCORSMode), |
|
855 mComplete(aCopy.mComplete) |
|
856 #ifdef DEBUG |
|
857 , mPrincipalSet(aCopy.mPrincipalSet) |
|
858 #endif |
|
859 { |
|
860 MOZ_COUNT_CTOR(nsCSSStyleSheetInner); |
|
861 AddSheet(aPrimarySheet); |
|
862 aCopy.mOrderedRules.EnumerateForwards(css::GroupRule::CloneRuleInto, &mOrderedRules); |
|
863 mOrderedRules.EnumerateForwards(SetStyleSheetReference, aPrimarySheet); |
|
864 |
|
865 ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet }; |
|
866 mOrderedRules.EnumerateForwards(nsCSSStyleSheet::RebuildChildList, &builder); |
|
867 |
|
868 RebuildNameSpaces(); |
|
869 } |
|
870 |
|
871 nsCSSStyleSheetInner::~nsCSSStyleSheetInner() |
|
872 { |
|
873 MOZ_COUNT_DTOR(nsCSSStyleSheetInner); |
|
874 mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr); |
|
875 } |
|
876 |
|
877 nsCSSStyleSheetInner* |
|
878 nsCSSStyleSheetInner::CloneFor(nsCSSStyleSheet* aPrimarySheet) |
|
879 { |
|
880 return new nsCSSStyleSheetInner(*this, aPrimarySheet); |
|
881 } |
|
882 |
|
883 void |
|
884 nsCSSStyleSheetInner::AddSheet(nsCSSStyleSheet* aSheet) |
|
885 { |
|
886 mSheets.AppendElement(aSheet); |
|
887 } |
|
888 |
|
889 void |
|
890 nsCSSStyleSheetInner::RemoveSheet(nsCSSStyleSheet* aSheet) |
|
891 { |
|
892 if (1 == mSheets.Length()) { |
|
893 NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent"); |
|
894 delete this; |
|
895 return; |
|
896 } |
|
897 if (aSheet == mSheets.ElementAt(0)) { |
|
898 mSheets.RemoveElementAt(0); |
|
899 NS_ASSERTION(mSheets.Length(), "no parents"); |
|
900 mOrderedRules.EnumerateForwards(SetStyleSheetReference, |
|
901 mSheets.ElementAt(0)); |
|
902 |
|
903 ChildSheetListBuilder::ReparentChildList(mSheets[0], mFirstChild); |
|
904 } |
|
905 else { |
|
906 mSheets.RemoveElement(aSheet); |
|
907 } |
|
908 } |
|
909 |
|
910 static void |
|
911 AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap) |
|
912 { |
|
913 NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type"); |
|
914 |
|
915 nsRefPtr<css::NameSpaceRule> nameSpaceRule = do_QueryObject(aRule); |
|
916 |
|
917 nsAutoString urlSpec; |
|
918 nameSpaceRule->GetURLSpec(urlSpec); |
|
919 |
|
920 aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec); |
|
921 } |
|
922 |
|
923 static bool |
|
924 CreateNameSpace(css::Rule* aRule, void* aNameSpacePtr) |
|
925 { |
|
926 int32_t type = aRule->GetType(); |
|
927 if (css::Rule::NAMESPACE_RULE == type) { |
|
928 AddNamespaceRuleToMap(aRule, |
|
929 static_cast<nsXMLNameSpaceMap*>(aNameSpacePtr)); |
|
930 return true; |
|
931 } |
|
932 // stop if not namespace, import or charset because namespace can't follow |
|
933 // anything else |
|
934 return (css::Rule::CHARSET_RULE == type || css::Rule::IMPORT_RULE == type); |
|
935 } |
|
936 |
|
937 void |
|
938 nsCSSStyleSheetInner::RebuildNameSpaces() |
|
939 { |
|
940 // Just nuke our existing namespace map, if any |
|
941 if (NS_SUCCEEDED(CreateNamespaceMap())) { |
|
942 mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap); |
|
943 } |
|
944 } |
|
945 |
|
946 nsresult |
|
947 nsCSSStyleSheetInner::CreateNamespaceMap() |
|
948 { |
|
949 mNameSpaceMap = nsXMLNameSpaceMap::Create(false); |
|
950 NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY); |
|
951 // Override the default namespace map behavior for the null prefix to |
|
952 // return the wildcard namespace instead of the null namespace. |
|
953 mNameSpaceMap->AddPrefix(nullptr, kNameSpaceID_Unknown); |
|
954 return NS_OK; |
|
955 } |
|
956 |
|
957 size_t |
|
958 nsCSSStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
959 { |
|
960 size_t n = aMallocSizeOf(this); |
|
961 n += mOrderedRules.SizeOfExcludingThis(css::Rule::SizeOfCOMArrayElementIncludingThis, |
|
962 aMallocSizeOf); |
|
963 n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0; |
|
964 |
|
965 // Measurement of the following members may be added later if DMD finds it is |
|
966 // worthwhile: |
|
967 // - mSheetURI |
|
968 // - mOriginalSheetURI |
|
969 // - mBaseURI |
|
970 // - mPrincipal |
|
971 // - mNameSpaceMap |
|
972 // |
|
973 // The following members are not measured: |
|
974 // - mSheets, because it's non-owning |
|
975 |
|
976 return n; |
|
977 } |
|
978 |
|
979 // ------------------------------- |
|
980 // CSS Style Sheet |
|
981 // |
|
982 |
|
983 nsCSSStyleSheet::nsCSSStyleSheet(CORSMode aCORSMode) |
|
984 : mTitle(), |
|
985 mParent(nullptr), |
|
986 mOwnerRule(nullptr), |
|
987 mDocument(nullptr), |
|
988 mOwningNode(nullptr), |
|
989 mDisabled(false), |
|
990 mDirty(false), |
|
991 mScopeElement(nullptr), |
|
992 mRuleProcessors(nullptr) |
|
993 { |
|
994 mInner = new nsCSSStyleSheetInner(this, aCORSMode); |
|
995 |
|
996 SetIsDOMBinding(); |
|
997 } |
|
998 |
|
999 nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet& aCopy, |
|
1000 nsCSSStyleSheet* aParentToUse, |
|
1001 css::ImportRule* aOwnerRuleToUse, |
|
1002 nsIDocument* aDocumentToUse, |
|
1003 nsINode* aOwningNodeToUse) |
|
1004 : mTitle(aCopy.mTitle), |
|
1005 mParent(aParentToUse), |
|
1006 mOwnerRule(aOwnerRuleToUse), |
|
1007 mDocument(aDocumentToUse), |
|
1008 mOwningNode(aOwningNodeToUse), |
|
1009 mDisabled(aCopy.mDisabled), |
|
1010 mDirty(aCopy.mDirty), |
|
1011 mScopeElement(nullptr), |
|
1012 mInner(aCopy.mInner), |
|
1013 mRuleProcessors(nullptr) |
|
1014 { |
|
1015 |
|
1016 mInner->AddSheet(this); |
|
1017 |
|
1018 if (mDirty) { // CSSOM's been there, force full copy now |
|
1019 NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?"); |
|
1020 // FIXME: handle failure? |
|
1021 EnsureUniqueInner(); |
|
1022 } |
|
1023 |
|
1024 if (aCopy.mMedia) { |
|
1025 // XXX This is wrong; we should be keeping @import rules and |
|
1026 // sheets in sync! |
|
1027 mMedia = aCopy.mMedia->Clone(); |
|
1028 } |
|
1029 |
|
1030 SetIsDOMBinding(); |
|
1031 } |
|
1032 |
|
1033 nsCSSStyleSheet::~nsCSSStyleSheet() |
|
1034 { |
|
1035 for (nsCSSStyleSheet* child = mInner->mFirstChild; |
|
1036 child; |
|
1037 child = child->mNext) { |
|
1038 // XXXbz this is a little bogus; see the XXX comment where we |
|
1039 // declare mFirstChild. |
|
1040 if (child->mParent == this) { |
|
1041 child->mParent = nullptr; |
|
1042 child->mDocument = nullptr; |
|
1043 } |
|
1044 } |
|
1045 DropRuleCollection(); |
|
1046 DropMedia(); |
|
1047 mInner->RemoveSheet(this); |
|
1048 // XXX The document reference is not reference counted and should |
|
1049 // not be released. The document will let us know when it is going |
|
1050 // away. |
|
1051 if (mRuleProcessors) { |
|
1052 NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference"); |
|
1053 delete mRuleProcessors; // weak refs, should be empty here anyway |
|
1054 } |
|
1055 } |
|
1056 |
|
1057 void |
|
1058 nsCSSStyleSheet::DropRuleCollection() |
|
1059 { |
|
1060 if (mRuleCollection) { |
|
1061 mRuleCollection->DropReference(); |
|
1062 mRuleCollection = nullptr; |
|
1063 } |
|
1064 } |
|
1065 |
|
1066 void |
|
1067 nsCSSStyleSheet::DropMedia() |
|
1068 { |
|
1069 if (mMedia) { |
|
1070 mMedia->SetStyleSheet(nullptr); |
|
1071 mMedia = nullptr; |
|
1072 } |
|
1073 } |
|
1074 |
|
1075 void |
|
1076 nsCSSStyleSheet::UnlinkInner() |
|
1077 { |
|
1078 // We can only have a cycle through our inner if we have a unique inner, |
|
1079 // because otherwise there are no JS wrappers for anything in the inner. |
|
1080 if (mInner->mSheets.Length() != 1) { |
|
1081 return; |
|
1082 } |
|
1083 |
|
1084 mInner->mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr); |
|
1085 mInner->mOrderedRules.Clear(); |
|
1086 |
|
1087 // Have to be a bit careful with child sheets, because we want to |
|
1088 // drop their mNext pointers and null out their mParent and |
|
1089 // mDocument, but don't want to work with deleted objects. And we |
|
1090 // don't want to do any addrefing in the process, just to make sure |
|
1091 // we don't confuse the cycle collector (though on the face of it, |
|
1092 // addref/release pairs during unlink should probably be ok). |
|
1093 nsRefPtr<nsCSSStyleSheet> child; |
|
1094 child.swap(mInner->mFirstChild); |
|
1095 while (child) { |
|
1096 MOZ_ASSERT(child->mParent == this, "We have a unique inner!"); |
|
1097 child->mParent = nullptr; |
|
1098 child->mDocument = nullptr; |
|
1099 nsRefPtr<nsCSSStyleSheet> next; |
|
1100 // Null out child->mNext, but don't let it die yet |
|
1101 next.swap(child->mNext); |
|
1102 // Switch to looking at the old value of child->mNext next iteration |
|
1103 child.swap(next); |
|
1104 // "next" is now our previous value of child; it'll get released |
|
1105 // as we loop around. |
|
1106 } |
|
1107 } |
|
1108 |
|
1109 void |
|
1110 nsCSSStyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb) |
|
1111 { |
|
1112 // We can only have a cycle through our inner if we have a unique inner, |
|
1113 // because otherwise there are no JS wrappers for anything in the inner. |
|
1114 if (mInner->mSheets.Length() != 1) { |
|
1115 return; |
|
1116 } |
|
1117 |
|
1118 nsRefPtr<nsCSSStyleSheet>* childSheetSlot = &mInner->mFirstChild; |
|
1119 while (*childSheetSlot) { |
|
1120 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet"); |
|
1121 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, childSheetSlot->get())); |
|
1122 childSheetSlot = &(*childSheetSlot)->mNext; |
|
1123 } |
|
1124 |
|
1125 const nsCOMArray<css::Rule>& rules = mInner->mOrderedRules; |
|
1126 for (int32_t i = 0, count = rules.Count(); i < count; ++i) { |
|
1127 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]"); |
|
1128 cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule()); |
|
1129 } |
|
1130 } |
|
1131 |
|
1132 DOMCI_DATA(CSSStyleSheet, nsCSSStyleSheet) |
|
1133 |
|
1134 // QueryInterface implementation for nsCSSStyleSheet |
|
1135 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSStyleSheet) |
|
1136 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
1137 NS_INTERFACE_MAP_ENTRY(nsIStyleSheet) |
|
1138 NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet) |
|
1139 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet) |
|
1140 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) |
|
1141 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleSheet) |
|
1142 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleSheet) |
|
1143 if (aIID.Equals(NS_GET_IID(nsCSSStyleSheet))) |
|
1144 foundInterface = reinterpret_cast<nsISupports*>(this); |
|
1145 else |
|
1146 NS_INTERFACE_MAP_END |
|
1147 |
|
1148 |
|
1149 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSStyleSheet) |
|
1150 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSStyleSheet) |
|
1151 |
|
1152 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSStyleSheet) |
|
1153 |
|
1154 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSStyleSheet) |
|
1155 tmp->DropMedia(); |
|
1156 // We do not unlink mNext; our parent will handle that. If we |
|
1157 // unlinked it here, our parent would not be able to walk its list |
|
1158 // of child sheets and null out the back-references to it, if we got |
|
1159 // unlinked before it does. |
|
1160 tmp->DropRuleCollection(); |
|
1161 tmp->UnlinkInner(); |
|
1162 tmp->mScopeElement = nullptr; |
|
1163 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
1164 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
1165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSStyleSheet) |
|
1166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia) |
|
1167 // We do not traverse mNext; our parent will handle that. See |
|
1168 // comments in Unlink for why. |
|
1169 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection) |
|
1170 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement) |
|
1171 tmp->TraverseInner(cb); |
|
1172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
1173 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
1174 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsCSSStyleSheet) |
|
1175 |
|
1176 nsresult |
|
1177 nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor) |
|
1178 { |
|
1179 if (! mRuleProcessors) { |
|
1180 mRuleProcessors = new nsAutoTArray<nsCSSRuleProcessor*, 8>(); |
|
1181 if (!mRuleProcessors) |
|
1182 return NS_ERROR_OUT_OF_MEMORY; |
|
1183 } |
|
1184 NS_ASSERTION(mRuleProcessors->NoIndex == mRuleProcessors->IndexOf(aProcessor), |
|
1185 "processor already registered"); |
|
1186 mRuleProcessors->AppendElement(aProcessor); // weak ref |
|
1187 return NS_OK; |
|
1188 } |
|
1189 |
|
1190 nsresult |
|
1191 nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor) |
|
1192 { |
|
1193 if (!mRuleProcessors) |
|
1194 return NS_ERROR_FAILURE; |
|
1195 return mRuleProcessors->RemoveElement(aProcessor) |
|
1196 ? NS_OK |
|
1197 : NS_ERROR_FAILURE; |
|
1198 } |
|
1199 |
|
1200 |
|
1201 void |
|
1202 nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI, |
|
1203 nsIURI* aBaseURI) |
|
1204 { |
|
1205 NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr"); |
|
1206 |
|
1207 NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete, |
|
1208 "Can't call SetURL on sheets that are complete or have rules"); |
|
1209 |
|
1210 mInner->mSheetURI = aSheetURI; |
|
1211 mInner->mOriginalSheetURI = aOriginalSheetURI; |
|
1212 mInner->mBaseURI = aBaseURI; |
|
1213 } |
|
1214 |
|
1215 void |
|
1216 nsCSSStyleSheet::SetPrincipal(nsIPrincipal* aPrincipal) |
|
1217 { |
|
1218 NS_PRECONDITION(!mInner->mPrincipalSet, |
|
1219 "Should have an inner whose principal has not yet been set"); |
|
1220 if (aPrincipal) { |
|
1221 mInner->mPrincipal = aPrincipal; |
|
1222 #ifdef DEBUG |
|
1223 mInner->mPrincipalSet = true; |
|
1224 #endif |
|
1225 } |
|
1226 } |
|
1227 |
|
1228 /* virtual */ nsIURI* |
|
1229 nsCSSStyleSheet::GetSheetURI() const |
|
1230 { |
|
1231 return mInner->mSheetURI; |
|
1232 } |
|
1233 |
|
1234 /* virtual */ nsIURI* |
|
1235 nsCSSStyleSheet::GetBaseURI() const |
|
1236 { |
|
1237 return mInner->mBaseURI; |
|
1238 } |
|
1239 |
|
1240 /* virtual */ void |
|
1241 nsCSSStyleSheet::GetType(nsString& aType) const |
|
1242 { |
|
1243 aType.AssignLiteral("text/css"); |
|
1244 } |
|
1245 |
|
1246 bool |
|
1247 nsCSSStyleSheet::UseForPresentation(nsPresContext* aPresContext, |
|
1248 nsMediaQueryResultCacheKey& aKey) const |
|
1249 { |
|
1250 if (mMedia) { |
|
1251 return mMedia->Matches(aPresContext, &aKey); |
|
1252 } |
|
1253 return true; |
|
1254 } |
|
1255 |
|
1256 |
|
1257 void |
|
1258 nsCSSStyleSheet::SetMedia(nsMediaList* aMedia) |
|
1259 { |
|
1260 mMedia = aMedia; |
|
1261 } |
|
1262 |
|
1263 /* virtual */ bool |
|
1264 nsCSSStyleSheet::HasRules() const |
|
1265 { |
|
1266 return StyleRuleCount() != 0; |
|
1267 } |
|
1268 |
|
1269 /* virtual */ bool |
|
1270 nsCSSStyleSheet::IsApplicable() const |
|
1271 { |
|
1272 return !mDisabled && mInner->mComplete; |
|
1273 } |
|
1274 |
|
1275 /* virtual */ void |
|
1276 nsCSSStyleSheet::SetEnabled(bool aEnabled) |
|
1277 { |
|
1278 // Internal method, so callers must handle BeginUpdate/EndUpdate |
|
1279 bool oldDisabled = mDisabled; |
|
1280 mDisabled = !aEnabled; |
|
1281 |
|
1282 if (mInner->mComplete && oldDisabled != mDisabled) { |
|
1283 ClearRuleCascades(); |
|
1284 |
|
1285 if (mDocument) { |
|
1286 mDocument->SetStyleSheetApplicableState(this, !mDisabled); |
|
1287 } |
|
1288 } |
|
1289 } |
|
1290 |
|
1291 /* virtual */ bool |
|
1292 nsCSSStyleSheet::IsComplete() const |
|
1293 { |
|
1294 return mInner->mComplete; |
|
1295 } |
|
1296 |
|
1297 /* virtual */ void |
|
1298 nsCSSStyleSheet::SetComplete() |
|
1299 { |
|
1300 NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!"); |
|
1301 mInner->mComplete = true; |
|
1302 if (mDocument && !mDisabled) { |
|
1303 // Let the document know |
|
1304 mDocument->BeginUpdate(UPDATE_STYLE); |
|
1305 mDocument->SetStyleSheetApplicableState(this, true); |
|
1306 mDocument->EndUpdate(UPDATE_STYLE); |
|
1307 } |
|
1308 } |
|
1309 |
|
1310 /* virtual */ nsIStyleSheet* |
|
1311 nsCSSStyleSheet::GetParentSheet() const |
|
1312 { |
|
1313 return mParent; |
|
1314 } |
|
1315 |
|
1316 /* virtual */ nsIDocument* |
|
1317 nsCSSStyleSheet::GetOwningDocument() const |
|
1318 { |
|
1319 return mDocument; |
|
1320 } |
|
1321 |
|
1322 /* virtual */ void |
|
1323 nsCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument) |
|
1324 { // not ref counted |
|
1325 mDocument = aDocument; |
|
1326 // Now set the same document on all our child sheets.... |
|
1327 // XXXbz this is a little bogus; see the XXX comment where we |
|
1328 // declare mFirstChild. |
|
1329 for (nsCSSStyleSheet* child = mInner->mFirstChild; |
|
1330 child; child = child->mNext) { |
|
1331 if (child->mParent == this) { |
|
1332 child->SetOwningDocument(aDocument); |
|
1333 } |
|
1334 } |
|
1335 } |
|
1336 |
|
1337 uint64_t |
|
1338 nsCSSStyleSheet::FindOwningWindowInnerID() const |
|
1339 { |
|
1340 uint64_t windowID = 0; |
|
1341 if (mDocument) { |
|
1342 windowID = mDocument->InnerWindowID(); |
|
1343 } |
|
1344 |
|
1345 if (windowID == 0 && mOwningNode) { |
|
1346 windowID = mOwningNode->OwnerDoc()->InnerWindowID(); |
|
1347 } |
|
1348 |
|
1349 if (windowID == 0 && mOwnerRule) { |
|
1350 nsCOMPtr<nsIStyleSheet> sheet = static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet(); |
|
1351 if (sheet) { |
|
1352 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheet); |
|
1353 if (cssSheet) { |
|
1354 windowID = cssSheet->FindOwningWindowInnerID(); |
|
1355 } |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 if (windowID == 0 && mParent) { |
|
1360 windowID = mParent->FindOwningWindowInnerID(); |
|
1361 } |
|
1362 |
|
1363 return windowID; |
|
1364 } |
|
1365 |
|
1366 void |
|
1367 nsCSSStyleSheet::AppendStyleSheet(nsCSSStyleSheet* aSheet) |
|
1368 { |
|
1369 NS_PRECONDITION(nullptr != aSheet, "null arg"); |
|
1370 |
|
1371 WillDirty(); |
|
1372 nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild; |
|
1373 while (*tail) { |
|
1374 tail = &(*tail)->mNext; |
|
1375 } |
|
1376 *tail = aSheet; |
|
1377 |
|
1378 // This is not reference counted. Our parent tells us when |
|
1379 // it's going away. |
|
1380 aSheet->mParent = this; |
|
1381 aSheet->mDocument = mDocument; |
|
1382 DidDirty(); |
|
1383 } |
|
1384 |
|
1385 void |
|
1386 nsCSSStyleSheet::InsertStyleSheetAt(nsCSSStyleSheet* aSheet, int32_t aIndex) |
|
1387 { |
|
1388 NS_PRECONDITION(nullptr != aSheet, "null arg"); |
|
1389 |
|
1390 WillDirty(); |
|
1391 nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild; |
|
1392 while (*tail && aIndex) { |
|
1393 --aIndex; |
|
1394 tail = &(*tail)->mNext; |
|
1395 } |
|
1396 aSheet->mNext = *tail; |
|
1397 *tail = aSheet; |
|
1398 |
|
1399 // This is not reference counted. Our parent tells us when |
|
1400 // it's going away. |
|
1401 aSheet->mParent = this; |
|
1402 aSheet->mDocument = mDocument; |
|
1403 DidDirty(); |
|
1404 } |
|
1405 |
|
1406 void |
|
1407 nsCSSStyleSheet::PrependStyleRule(css::Rule* aRule) |
|
1408 { |
|
1409 NS_PRECONDITION(nullptr != aRule, "null arg"); |
|
1410 |
|
1411 WillDirty(); |
|
1412 mInner->mOrderedRules.InsertObjectAt(aRule, 0); |
|
1413 aRule->SetStyleSheet(this); |
|
1414 DidDirty(); |
|
1415 |
|
1416 if (css::Rule::NAMESPACE_RULE == aRule->GetType()) { |
|
1417 // no api to prepend a namespace (ugh), release old ones and re-create them all |
|
1418 mInner->RebuildNameSpaces(); |
|
1419 } |
|
1420 } |
|
1421 |
|
1422 void |
|
1423 nsCSSStyleSheet::AppendStyleRule(css::Rule* aRule) |
|
1424 { |
|
1425 NS_PRECONDITION(nullptr != aRule, "null arg"); |
|
1426 |
|
1427 WillDirty(); |
|
1428 mInner->mOrderedRules.AppendObject(aRule); |
|
1429 aRule->SetStyleSheet(this); |
|
1430 DidDirty(); |
|
1431 |
|
1432 if (css::Rule::NAMESPACE_RULE == aRule->GetType()) { |
|
1433 #ifdef DEBUG |
|
1434 nsresult rv = |
|
1435 #endif |
|
1436 RegisterNamespaceRule(aRule); |
|
1437 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), |
|
1438 "RegisterNamespaceRule returned error"); |
|
1439 } |
|
1440 } |
|
1441 |
|
1442 void |
|
1443 nsCSSStyleSheet::ReplaceStyleRule(css::Rule* aOld, css::Rule* aNew) |
|
1444 { |
|
1445 NS_PRECONDITION(mInner->mOrderedRules.Count() != 0, "can't have old rule"); |
|
1446 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!"); |
|
1447 |
|
1448 WillDirty(); |
|
1449 int32_t index = mInner->mOrderedRules.IndexOf(aOld); |
|
1450 if (MOZ_UNLIKELY(index == -1)) { |
|
1451 NS_NOTREACHED("Couldn't find old rule"); |
|
1452 return; |
|
1453 } |
|
1454 mInner->mOrderedRules.ReplaceObjectAt(aNew, index); |
|
1455 |
|
1456 aNew->SetStyleSheet(this); |
|
1457 aOld->SetStyleSheet(nullptr); |
|
1458 DidDirty(); |
|
1459 NS_ASSERTION(css::Rule::NAMESPACE_RULE != aNew->GetType(), "not yet implemented"); |
|
1460 NS_ASSERTION(css::Rule::NAMESPACE_RULE != aOld->GetType(), "not yet implemented"); |
|
1461 } |
|
1462 |
|
1463 int32_t |
|
1464 nsCSSStyleSheet::StyleRuleCount() const |
|
1465 { |
|
1466 return mInner->mOrderedRules.Count(); |
|
1467 } |
|
1468 |
|
1469 css::Rule* |
|
1470 nsCSSStyleSheet::GetStyleRuleAt(int32_t aIndex) const |
|
1471 { |
|
1472 // Important: If this function is ever made scriptable, we must add |
|
1473 // a security check here. See GetCssRules below for an example. |
|
1474 return mInner->mOrderedRules.SafeObjectAt(aIndex); |
|
1475 } |
|
1476 |
|
1477 int32_t |
|
1478 nsCSSStyleSheet::StyleSheetCount() const |
|
1479 { |
|
1480 // XXX Far from an ideal way to do this, but the hope is that |
|
1481 // it won't be done too often. If it is, we might want to |
|
1482 // consider storing the children in an array. |
|
1483 int32_t count = 0; |
|
1484 |
|
1485 const nsCSSStyleSheet* child = mInner->mFirstChild; |
|
1486 while (child) { |
|
1487 count++; |
|
1488 child = child->mNext; |
|
1489 } |
|
1490 |
|
1491 return count; |
|
1492 } |
|
1493 |
|
1494 nsCSSStyleSheet::EnsureUniqueInnerResult |
|
1495 nsCSSStyleSheet::EnsureUniqueInner() |
|
1496 { |
|
1497 mDirty = true; |
|
1498 |
|
1499 NS_ABORT_IF_FALSE(mInner->mSheets.Length() != 0, |
|
1500 "unexpected number of outers"); |
|
1501 if (mInner->mSheets.Length() == 1) { |
|
1502 return eUniqueInner_AlreadyUnique; |
|
1503 } |
|
1504 nsCSSStyleSheetInner* clone = mInner->CloneFor(this); |
|
1505 MOZ_ASSERT(clone); |
|
1506 mInner->RemoveSheet(this); |
|
1507 mInner = clone; |
|
1508 |
|
1509 // otherwise the rule processor has pointers to the old rules |
|
1510 ClearRuleCascades(); |
|
1511 |
|
1512 return eUniqueInner_ClonedInner; |
|
1513 } |
|
1514 |
|
1515 void |
|
1516 nsCSSStyleSheet::AppendAllChildSheets(nsTArray<nsCSSStyleSheet*>& aArray) |
|
1517 { |
|
1518 for (nsCSSStyleSheet* child = mInner->mFirstChild; child; |
|
1519 child = child->mNext) { |
|
1520 aArray.AppendElement(child); |
|
1521 } |
|
1522 } |
|
1523 |
|
1524 already_AddRefed<nsCSSStyleSheet> |
|
1525 nsCSSStyleSheet::Clone(nsCSSStyleSheet* aCloneParent, |
|
1526 css::ImportRule* aCloneOwnerRule, |
|
1527 nsIDocument* aCloneDocument, |
|
1528 nsINode* aCloneOwningNode) const |
|
1529 { |
|
1530 nsRefPtr<nsCSSStyleSheet> clone = new nsCSSStyleSheet(*this, |
|
1531 aCloneParent, |
|
1532 aCloneOwnerRule, |
|
1533 aCloneDocument, |
|
1534 aCloneOwningNode); |
|
1535 return clone.forget(); |
|
1536 } |
|
1537 |
|
1538 #ifdef DEBUG |
|
1539 static void |
|
1540 ListRules(const nsCOMArray<css::Rule>& aRules, FILE* aOut, int32_t aIndent) |
|
1541 { |
|
1542 for (int32_t index = aRules.Count() - 1; index >= 0; --index) { |
|
1543 aRules.ObjectAt(index)->List(aOut, aIndent); |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 struct ListEnumData { |
|
1548 ListEnumData(FILE* aOut, int32_t aIndent) |
|
1549 : mOut(aOut), |
|
1550 mIndent(aIndent) |
|
1551 { |
|
1552 } |
|
1553 FILE* mOut; |
|
1554 int32_t mIndent; |
|
1555 }; |
|
1556 |
|
1557 /* virtual */ void |
|
1558 nsCSSStyleSheet::List(FILE* out, int32_t aIndent) const |
|
1559 { |
|
1560 |
|
1561 int32_t index; |
|
1562 |
|
1563 // Indent |
|
1564 for (index = aIndent; --index >= 0; ) fputs(" ", out); |
|
1565 |
|
1566 fputs("CSS Style Sheet: ", out); |
|
1567 nsAutoCString urlSpec; |
|
1568 nsresult rv = mInner->mSheetURI->GetSpec(urlSpec); |
|
1569 if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) { |
|
1570 fputs(urlSpec.get(), out); |
|
1571 } |
|
1572 |
|
1573 if (mMedia) { |
|
1574 fputs(" media: ", out); |
|
1575 nsAutoString buffer; |
|
1576 mMedia->GetText(buffer); |
|
1577 fputs(NS_ConvertUTF16toUTF8(buffer).get(), out); |
|
1578 } |
|
1579 fputs("\n", out); |
|
1580 |
|
1581 for (const nsCSSStyleSheet* child = mInner->mFirstChild; |
|
1582 child; |
|
1583 child = child->mNext) { |
|
1584 child->List(out, aIndent + 1); |
|
1585 } |
|
1586 |
|
1587 fputs("Rules in source order:\n", out); |
|
1588 ListRules(mInner->mOrderedRules, out, aIndent); |
|
1589 } |
|
1590 #endif |
|
1591 |
|
1592 void |
|
1593 nsCSSStyleSheet::ClearRuleCascades() |
|
1594 { |
|
1595 if (mRuleProcessors) { |
|
1596 nsCSSRuleProcessor **iter = mRuleProcessors->Elements(), |
|
1597 **end = iter + mRuleProcessors->Length(); |
|
1598 for(; iter != end; ++iter) { |
|
1599 (*iter)->ClearRuleCascades(); |
|
1600 } |
|
1601 } |
|
1602 if (mParent) { |
|
1603 nsCSSStyleSheet* parent = (nsCSSStyleSheet*)mParent; |
|
1604 parent->ClearRuleCascades(); |
|
1605 } |
|
1606 } |
|
1607 |
|
1608 void |
|
1609 nsCSSStyleSheet::WillDirty() |
|
1610 { |
|
1611 if (mInner->mComplete) { |
|
1612 EnsureUniqueInner(); |
|
1613 } |
|
1614 } |
|
1615 |
|
1616 void |
|
1617 nsCSSStyleSheet::DidDirty() |
|
1618 { |
|
1619 NS_ABORT_IF_FALSE(!mInner->mComplete || mDirty, |
|
1620 "caller must have called WillDirty()"); |
|
1621 ClearRuleCascades(); |
|
1622 } |
|
1623 |
|
1624 nsresult |
|
1625 nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() |
|
1626 { |
|
1627 // Get the security manager and do the subsumes check |
|
1628 nsIScriptSecurityManager *securityManager = |
|
1629 nsContentUtils::GetSecurityManager(); |
|
1630 |
|
1631 nsCOMPtr<nsIPrincipal> subjectPrincipal; |
|
1632 nsresult rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); |
|
1633 NS_ENSURE_SUCCESS(rv, rv); |
|
1634 |
|
1635 if (!subjectPrincipal) { |
|
1636 return NS_ERROR_DOM_SECURITY_ERR; |
|
1637 } |
|
1638 |
|
1639 bool subsumes; |
|
1640 rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes); |
|
1641 NS_ENSURE_SUCCESS(rv, rv); |
|
1642 |
|
1643 if (subsumes) { |
|
1644 return NS_OK; |
|
1645 } |
|
1646 |
|
1647 if (!nsContentUtils::IsCallerChrome()) { |
|
1648 // Allow access only if CORS mode is not NONE |
|
1649 if (GetCORSMode() == CORS_NONE) { |
|
1650 return NS_ERROR_DOM_SECURITY_ERR; |
|
1651 } |
|
1652 |
|
1653 // Now make sure we set the principal of our inner to the |
|
1654 // subjectPrincipal. That means we need a unique inner, of |
|
1655 // course. But we don't want to do that if we're not complete |
|
1656 // yet. Luckily, all the callers of this method throw anyway if |
|
1657 // not complete, so we can just do that here too. |
|
1658 if (!mInner->mComplete) { |
|
1659 return NS_ERROR_DOM_INVALID_ACCESS_ERR; |
|
1660 } |
|
1661 |
|
1662 WillDirty(); |
|
1663 |
|
1664 mInner->mPrincipal = subjectPrincipal; |
|
1665 |
|
1666 DidDirty(); |
|
1667 } |
|
1668 |
|
1669 return NS_OK; |
|
1670 } |
|
1671 |
|
1672 nsresult |
|
1673 nsCSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule) |
|
1674 { |
|
1675 if (!mInner->mNameSpaceMap) { |
|
1676 nsresult rv = mInner->CreateNamespaceMap(); |
|
1677 NS_ENSURE_SUCCESS(rv, rv); |
|
1678 } |
|
1679 |
|
1680 AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap); |
|
1681 return NS_OK; |
|
1682 } |
|
1683 |
|
1684 // nsIDOMStyleSheet interface |
|
1685 NS_IMETHODIMP |
|
1686 nsCSSStyleSheet::GetType(nsAString& aType) |
|
1687 { |
|
1688 aType.AssignLiteral("text/css"); |
|
1689 return NS_OK; |
|
1690 } |
|
1691 |
|
1692 NS_IMETHODIMP |
|
1693 nsCSSStyleSheet::GetDisabled(bool* aDisabled) |
|
1694 { |
|
1695 *aDisabled = Disabled(); |
|
1696 return NS_OK; |
|
1697 } |
|
1698 |
|
1699 NS_IMETHODIMP |
|
1700 nsCSSStyleSheet::SetDisabled(bool aDisabled) |
|
1701 { |
|
1702 // DOM method, so handle BeginUpdate/EndUpdate |
|
1703 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true); |
|
1704 nsCSSStyleSheet::SetEnabled(!aDisabled); |
|
1705 return NS_OK; |
|
1706 } |
|
1707 |
|
1708 NS_IMETHODIMP |
|
1709 nsCSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode) |
|
1710 { |
|
1711 nsCOMPtr<nsIDOMNode> ownerNode = do_QueryInterface(GetOwnerNode()); |
|
1712 ownerNode.forget(aOwnerNode); |
|
1713 return NS_OK; |
|
1714 } |
|
1715 |
|
1716 NS_IMETHODIMP |
|
1717 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet) |
|
1718 { |
|
1719 NS_ENSURE_ARG_POINTER(aParentStyleSheet); |
|
1720 |
|
1721 NS_IF_ADDREF(*aParentStyleSheet = mParent); |
|
1722 |
|
1723 return NS_OK; |
|
1724 } |
|
1725 |
|
1726 NS_IMETHODIMP |
|
1727 nsCSSStyleSheet::GetHref(nsAString& aHref) |
|
1728 { |
|
1729 if (mInner->mOriginalSheetURI) { |
|
1730 nsAutoCString str; |
|
1731 mInner->mOriginalSheetURI->GetSpec(str); |
|
1732 CopyUTF8toUTF16(str, aHref); |
|
1733 } else { |
|
1734 SetDOMStringToNull(aHref); |
|
1735 } |
|
1736 |
|
1737 return NS_OK; |
|
1738 } |
|
1739 |
|
1740 /* virtual */ void |
|
1741 nsCSSStyleSheet::GetTitle(nsString& aTitle) const |
|
1742 { |
|
1743 aTitle = mTitle; |
|
1744 } |
|
1745 |
|
1746 NS_IMETHODIMP |
|
1747 nsCSSStyleSheet::GetTitle(nsAString& aTitle) |
|
1748 { |
|
1749 aTitle.Assign(mTitle); |
|
1750 return NS_OK; |
|
1751 } |
|
1752 |
|
1753 NS_IMETHODIMP |
|
1754 nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia) |
|
1755 { |
|
1756 NS_ADDREF(*aMedia = Media()); |
|
1757 return NS_OK; |
|
1758 } |
|
1759 |
|
1760 nsMediaList* |
|
1761 nsCSSStyleSheet::Media() |
|
1762 { |
|
1763 if (!mMedia) { |
|
1764 mMedia = new nsMediaList(); |
|
1765 mMedia->SetStyleSheet(this); |
|
1766 } |
|
1767 |
|
1768 return mMedia; |
|
1769 } |
|
1770 |
|
1771 NS_IMETHODIMP |
|
1772 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule) |
|
1773 { |
|
1774 NS_IF_ADDREF(*aOwnerRule = GetOwnerRule()); |
|
1775 return NS_OK; |
|
1776 } |
|
1777 |
|
1778 nsIDOMCSSRule* |
|
1779 nsCSSStyleSheet::GetDOMOwnerRule() const |
|
1780 { |
|
1781 return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr; |
|
1782 } |
|
1783 |
|
1784 NS_IMETHODIMP |
|
1785 nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules) |
|
1786 { |
|
1787 ErrorResult rv; |
|
1788 nsCOMPtr<nsIDOMCSSRuleList> rules = GetCssRules(rv); |
|
1789 rules.forget(aCssRules); |
|
1790 return rv.ErrorCode(); |
|
1791 } |
|
1792 |
|
1793 nsIDOMCSSRuleList* |
|
1794 nsCSSStyleSheet::GetCssRules(ErrorResult& aRv) |
|
1795 { |
|
1796 // No doing this on incomplete sheets! |
|
1797 if (!mInner->mComplete) { |
|
1798 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); |
|
1799 return nullptr; |
|
1800 } |
|
1801 |
|
1802 //-- Security check: Only scripts whose principal subsumes that of the |
|
1803 // style sheet can access rule collections. |
|
1804 nsresult rv = SubjectSubsumesInnerPrincipal(); |
|
1805 if (NS_FAILED(rv)) { |
|
1806 aRv.Throw(rv); |
|
1807 return nullptr; |
|
1808 } |
|
1809 |
|
1810 // OK, security check passed, so get the rule collection |
|
1811 if (!mRuleCollection) { |
|
1812 mRuleCollection = new CSSRuleListImpl(this); |
|
1813 } |
|
1814 |
|
1815 return mRuleCollection; |
|
1816 } |
|
1817 |
|
1818 NS_IMETHODIMP |
|
1819 nsCSSStyleSheet::InsertRule(const nsAString& aRule, |
|
1820 uint32_t aIndex, |
|
1821 uint32_t* aReturn) |
|
1822 { |
|
1823 //-- Security check: Only scripts whose principal subsumes that of the |
|
1824 // style sheet can modify rule collections. |
|
1825 nsresult rv = SubjectSubsumesInnerPrincipal(); |
|
1826 NS_ENSURE_SUCCESS(rv, rv); |
|
1827 |
|
1828 return InsertRuleInternal(aRule, aIndex, aReturn); |
|
1829 } |
|
1830 |
|
1831 static bool |
|
1832 RuleHasPendingChildSheet(css::Rule *cssRule) |
|
1833 { |
|
1834 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule)); |
|
1835 NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!"); |
|
1836 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet; |
|
1837 importRule->GetStyleSheet(getter_AddRefs(childSheet)); |
|
1838 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(childSheet); |
|
1839 return cssSheet != nullptr && !cssSheet->IsComplete(); |
|
1840 } |
|
1841 |
|
1842 nsresult |
|
1843 nsCSSStyleSheet::InsertRuleInternal(const nsAString& aRule, |
|
1844 uint32_t aIndex, |
|
1845 uint32_t* aReturn) |
|
1846 { |
|
1847 // No doing this if the sheet is not complete! |
|
1848 if (!mInner->mComplete) { |
|
1849 return NS_ERROR_DOM_INVALID_ACCESS_ERR; |
|
1850 } |
|
1851 |
|
1852 WillDirty(); |
|
1853 |
|
1854 if (aIndex > uint32_t(mInner->mOrderedRules.Count())) |
|
1855 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
|
1856 |
|
1857 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX, |
|
1858 "Too many style rules!"); |
|
1859 |
|
1860 // Hold strong ref to the CSSLoader in case the document update |
|
1861 // kills the document |
|
1862 nsRefPtr<css::Loader> loader; |
|
1863 if (mDocument) { |
|
1864 loader = mDocument->CSSLoader(); |
|
1865 NS_ASSERTION(loader, "Document with no CSS loader!"); |
|
1866 } |
|
1867 |
|
1868 nsCSSParser css(loader, this); |
|
1869 |
|
1870 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
1871 |
|
1872 nsRefPtr<css::Rule> rule; |
|
1873 nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, |
|
1874 mInner->mPrincipal, getter_AddRefs(rule)); |
|
1875 if (NS_FAILED(result)) |
|
1876 return result; |
|
1877 |
|
1878 // Hierarchy checking. |
|
1879 int32_t newType = rule->GetType(); |
|
1880 |
|
1881 // check that we're not inserting before a charset rule |
|
1882 css::Rule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex); |
|
1883 if (nextRule) { |
|
1884 int32_t nextType = nextRule->GetType(); |
|
1885 if (nextType == css::Rule::CHARSET_RULE) { |
|
1886 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1887 } |
|
1888 |
|
1889 if (nextType == css::Rule::IMPORT_RULE && |
|
1890 newType != css::Rule::CHARSET_RULE && |
|
1891 newType != css::Rule::IMPORT_RULE) { |
|
1892 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1893 } |
|
1894 |
|
1895 if (nextType == css::Rule::NAMESPACE_RULE && |
|
1896 newType != css::Rule::CHARSET_RULE && |
|
1897 newType != css::Rule::IMPORT_RULE && |
|
1898 newType != css::Rule::NAMESPACE_RULE) { |
|
1899 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1900 } |
|
1901 } |
|
1902 |
|
1903 if (aIndex != 0) { |
|
1904 // no inserting charset at nonzero position |
|
1905 if (newType == css::Rule::CHARSET_RULE) { |
|
1906 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1907 } |
|
1908 |
|
1909 css::Rule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1); |
|
1910 int32_t prevType = prevRule->GetType(); |
|
1911 |
|
1912 if (newType == css::Rule::IMPORT_RULE && |
|
1913 prevType != css::Rule::CHARSET_RULE && |
|
1914 prevType != css::Rule::IMPORT_RULE) { |
|
1915 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1916 } |
|
1917 |
|
1918 if (newType == css::Rule::NAMESPACE_RULE && |
|
1919 prevType != css::Rule::CHARSET_RULE && |
|
1920 prevType != css::Rule::IMPORT_RULE && |
|
1921 prevType != css::Rule::NAMESPACE_RULE) { |
|
1922 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
1923 } |
|
1924 } |
|
1925 |
|
1926 bool insertResult = mInner->mOrderedRules.InsertObjectAt(rule, aIndex); |
|
1927 NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY); |
|
1928 DidDirty(); |
|
1929 |
|
1930 rule->SetStyleSheet(this); |
|
1931 |
|
1932 int32_t type = rule->GetType(); |
|
1933 if (type == css::Rule::NAMESPACE_RULE) { |
|
1934 // XXXbz does this screw up when inserting a namespace rule before |
|
1935 // another namespace rule that binds the same prefix to a different |
|
1936 // namespace? |
|
1937 result = RegisterNamespaceRule(rule); |
|
1938 NS_ENSURE_SUCCESS(result, result); |
|
1939 } |
|
1940 |
|
1941 // We don't notify immediately for @import rules, but rather when |
|
1942 // the sheet the rule is importing is loaded (see StyleSheetLoaded) |
|
1943 if ((type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) && |
|
1944 mDocument) { |
|
1945 mDocument->StyleRuleAdded(this, rule); |
|
1946 } |
|
1947 |
|
1948 *aReturn = aIndex; |
|
1949 return NS_OK; |
|
1950 } |
|
1951 |
|
1952 NS_IMETHODIMP |
|
1953 nsCSSStyleSheet::DeleteRule(uint32_t aIndex) |
|
1954 { |
|
1955 // No doing this if the sheet is not complete! |
|
1956 if (!mInner->mComplete) { |
|
1957 return NS_ERROR_DOM_INVALID_ACCESS_ERR; |
|
1958 } |
|
1959 |
|
1960 //-- Security check: Only scripts whose principal subsumes that of the |
|
1961 // style sheet can modify rule collections. |
|
1962 nsresult rv = SubjectSubsumesInnerPrincipal(); |
|
1963 NS_ENSURE_SUCCESS(rv, rv); |
|
1964 |
|
1965 // XXX TBI: handle @rule types |
|
1966 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
1967 |
|
1968 WillDirty(); |
|
1969 |
|
1970 if (aIndex >= uint32_t(mInner->mOrderedRules.Count())) |
|
1971 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
|
1972 |
|
1973 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX, |
|
1974 "Too many style rules!"); |
|
1975 |
|
1976 // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt |
|
1977 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(aIndex); |
|
1978 if (rule) { |
|
1979 mInner->mOrderedRules.RemoveObjectAt(aIndex); |
|
1980 if (mDocument && mDocument->StyleSheetChangeEventsEnabled()) { |
|
1981 // Force creation of the DOM rule, so that it can be put on the |
|
1982 // StyleRuleRemoved event object. |
|
1983 rule->GetDOMRule(); |
|
1984 } |
|
1985 rule->SetStyleSheet(nullptr); |
|
1986 DidDirty(); |
|
1987 |
|
1988 if (mDocument) { |
|
1989 mDocument->StyleRuleRemoved(this, rule); |
|
1990 } |
|
1991 } |
|
1992 |
|
1993 return NS_OK; |
|
1994 } |
|
1995 |
|
1996 nsresult |
|
1997 nsCSSStyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex) |
|
1998 { |
|
1999 NS_ENSURE_ARG_POINTER(aGroup); |
|
2000 NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!"); |
|
2001 nsRefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex); |
|
2002 NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE); |
|
2003 |
|
2004 // check that the rule actually belongs to this sheet! |
|
2005 if (this != rule->GetStyleSheet()) { |
|
2006 return NS_ERROR_INVALID_ARG; |
|
2007 } |
|
2008 |
|
2009 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
2010 |
|
2011 WillDirty(); |
|
2012 |
|
2013 nsresult result = aGroup->DeleteStyleRuleAt(aIndex); |
|
2014 NS_ENSURE_SUCCESS(result, result); |
|
2015 |
|
2016 rule->SetStyleSheet(nullptr); |
|
2017 |
|
2018 DidDirty(); |
|
2019 |
|
2020 if (mDocument) { |
|
2021 mDocument->StyleRuleRemoved(this, rule); |
|
2022 } |
|
2023 |
|
2024 return NS_OK; |
|
2025 } |
|
2026 |
|
2027 nsresult |
|
2028 nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule, |
|
2029 css::GroupRule* aGroup, |
|
2030 uint32_t aIndex, |
|
2031 uint32_t* _retval) |
|
2032 { |
|
2033 NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!"); |
|
2034 // check that the group actually belongs to this sheet! |
|
2035 if (this != aGroup->GetStyleSheet()) { |
|
2036 return NS_ERROR_INVALID_ARG; |
|
2037 } |
|
2038 |
|
2039 // Hold strong ref to the CSSLoader in case the document update |
|
2040 // kills the document |
|
2041 nsRefPtr<css::Loader> loader; |
|
2042 if (mDocument) { |
|
2043 loader = mDocument->CSSLoader(); |
|
2044 NS_ASSERTION(loader, "Document with no CSS loader!"); |
|
2045 } |
|
2046 |
|
2047 nsCSSParser css(loader, this); |
|
2048 |
|
2049 // parse and grab the rule |
|
2050 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
2051 |
|
2052 WillDirty(); |
|
2053 |
|
2054 nsRefPtr<css::Rule> rule; |
|
2055 nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, |
|
2056 mInner->mPrincipal, getter_AddRefs(rule)); |
|
2057 if (NS_FAILED(result)) |
|
2058 return result; |
|
2059 |
|
2060 switch (rule->GetType()) { |
|
2061 case css::Rule::STYLE_RULE: |
|
2062 case css::Rule::MEDIA_RULE: |
|
2063 case css::Rule::FONT_FACE_RULE: |
|
2064 case css::Rule::PAGE_RULE: |
|
2065 case css::Rule::KEYFRAMES_RULE: |
|
2066 case css::Rule::DOCUMENT_RULE: |
|
2067 case css::Rule::SUPPORTS_RULE: |
|
2068 // these types are OK to insert into a group |
|
2069 break; |
|
2070 case css::Rule::CHARSET_RULE: |
|
2071 case css::Rule::IMPORT_RULE: |
|
2072 case css::Rule::NAMESPACE_RULE: |
|
2073 // these aren't |
|
2074 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
2075 default: |
|
2076 NS_NOTREACHED("unexpected rule type"); |
|
2077 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
|
2078 } |
|
2079 |
|
2080 result = aGroup->InsertStyleRuleAt(aIndex, rule); |
|
2081 NS_ENSURE_SUCCESS(result, result); |
|
2082 DidDirty(); |
|
2083 |
|
2084 if (mDocument) { |
|
2085 mDocument->StyleRuleAdded(this, rule); |
|
2086 } |
|
2087 |
|
2088 *_retval = aIndex; |
|
2089 return NS_OK; |
|
2090 } |
|
2091 |
|
2092 nsresult |
|
2093 nsCSSStyleSheet::ReplaceRuleInGroup(css::GroupRule* aGroup, |
|
2094 css::Rule* aOld, css::Rule* aNew) |
|
2095 { |
|
2096 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!"); |
|
2097 NS_ASSERTION(this == aGroup->GetStyleSheet(), "group doesn't belong to this sheet"); |
|
2098 |
|
2099 WillDirty(); |
|
2100 |
|
2101 nsresult result = aGroup->ReplaceStyleRule(aOld, aNew); |
|
2102 DidDirty(); |
|
2103 return result; |
|
2104 } |
|
2105 |
|
2106 // nsICSSLoaderObserver implementation |
|
2107 NS_IMETHODIMP |
|
2108 nsCSSStyleSheet::StyleSheetLoaded(nsCSSStyleSheet* aSheet, |
|
2109 bool aWasAlternate, |
|
2110 nsresult aStatus) |
|
2111 { |
|
2112 if (aSheet->GetParentSheet() == nullptr) { |
|
2113 return NS_OK; // ignore if sheet has been detached already (see parseSheet) |
|
2114 } |
|
2115 NS_ASSERTION(this == aSheet->GetParentSheet(), |
|
2116 "We are being notified of a sheet load for a sheet that is not our child!"); |
|
2117 |
|
2118 if (mDocument && NS_SUCCEEDED(aStatus)) { |
|
2119 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
2120 |
|
2121 // XXXldb @import rules shouldn't even implement nsIStyleRule (but |
|
2122 // they do)! |
|
2123 mDocument->StyleRuleAdded(this, aSheet->GetOwnerRule()); |
|
2124 } |
|
2125 |
|
2126 return NS_OK; |
|
2127 } |
|
2128 |
|
2129 nsresult |
|
2130 nsCSSStyleSheet::ParseSheet(const nsAString& aInput) |
|
2131 { |
|
2132 // Not doing this if the sheet is not complete! |
|
2133 if (!mInner->mComplete) { |
|
2134 return NS_ERROR_DOM_INVALID_ACCESS_ERR; |
|
2135 } |
|
2136 |
|
2137 // Hold strong ref to the CSSLoader in case the document update |
|
2138 // kills the document |
|
2139 nsRefPtr<css::Loader> loader; |
|
2140 if (mDocument) { |
|
2141 loader = mDocument->CSSLoader(); |
|
2142 NS_ASSERTION(loader, "Document with no CSS loader!"); |
|
2143 } else { |
|
2144 loader = new css::Loader(); |
|
2145 } |
|
2146 |
|
2147 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); |
|
2148 |
|
2149 WillDirty(); |
|
2150 |
|
2151 // detach existing rules (including child sheets via import rules) |
|
2152 int ruleCount; |
|
2153 while ((ruleCount = mInner->mOrderedRules.Count()) != 0) { |
|
2154 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(ruleCount - 1); |
|
2155 mInner->mOrderedRules.RemoveObjectAt(ruleCount - 1); |
|
2156 rule->SetStyleSheet(nullptr); |
|
2157 if (mDocument) { |
|
2158 mDocument->StyleRuleRemoved(this, rule); |
|
2159 } |
|
2160 } |
|
2161 |
|
2162 // nuke child sheets list and current namespace map |
|
2163 for (nsCSSStyleSheet* child = mInner->mFirstChild; child; child = child->mNext) { |
|
2164 NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!"); |
|
2165 child->mParent = nullptr; |
|
2166 child->mDocument = nullptr; |
|
2167 } |
|
2168 mInner->mFirstChild = nullptr; |
|
2169 mInner->mNameSpaceMap = nullptr; |
|
2170 |
|
2171 // allow unsafe rules if the style sheet's principal is the system principal |
|
2172 bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal); |
|
2173 |
|
2174 nsCSSParser parser(loader, this); |
|
2175 nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI, |
|
2176 mInner->mPrincipal, 1, allowUnsafeRules); |
|
2177 DidDirty(); // we are always 'dirty' here since we always remove rules first |
|
2178 NS_ENSURE_SUCCESS(rv, rv); |
|
2179 |
|
2180 // notify document of all new rules |
|
2181 if (mDocument) { |
|
2182 for (int32_t index = 0; index < mInner->mOrderedRules.Count(); ++index) { |
|
2183 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(index); |
|
2184 if (rule->GetType() == css::Rule::IMPORT_RULE && |
|
2185 RuleHasPendingChildSheet(rule)) { |
|
2186 continue; // notify when loaded (see StyleSheetLoaded) |
|
2187 } |
|
2188 mDocument->StyleRuleAdded(this, rule); |
|
2189 } |
|
2190 } |
|
2191 return NS_OK; |
|
2192 } |
|
2193 |
|
2194 /* virtual */ nsIURI* |
|
2195 nsCSSStyleSheet::GetOriginalURI() const |
|
2196 { |
|
2197 return mInner->mOriginalSheetURI; |
|
2198 } |
|
2199 |
|
2200 /* virtual */ |
|
2201 JSObject* |
|
2202 nsCSSStyleSheet::WrapObject(JSContext* aCx) |
|
2203 { |
|
2204 return CSSStyleSheetBinding::Wrap(aCx, this); |
|
2205 } |