Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
7 /* representation of a CSS style sheet */
9 #include "nsCSSStyleSheet.h"
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"
46 using namespace mozilla;
47 using namespace mozilla::dom;
50 // -------------------------------
51 // Style Rule List for the DOM
52 //
53 class CSSRuleListImpl : public nsICSSRuleList
54 {
55 public:
56 CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet);
58 NS_DECL_ISUPPORTS
60 virtual nsIDOMCSSRule*
61 IndexedGetter(uint32_t aIndex, bool& aFound) MOZ_OVERRIDE;
62 virtual uint32_t
63 Length() MOZ_OVERRIDE;
65 void DropReference() { mStyleSheet = nullptr; }
67 protected:
68 virtual ~CSSRuleListImpl();
70 nsCSSStyleSheet* mStyleSheet;
71 };
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 }
80 CSSRuleListImpl::~CSSRuleListImpl()
81 {
82 }
84 DOMCI_DATA(CSSRuleList, CSSRuleListImpl)
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
95 NS_IMPL_ADDREF(CSSRuleListImpl)
96 NS_IMPL_RELEASE(CSSRuleListImpl)
99 uint32_t
100 CSSRuleListImpl::Length()
101 {
102 if (!mStyleSheet) {
103 return 0;
104 }
106 return SafeCast<uint32_t>(mStyleSheet->StyleRuleCount());
107 }
109 nsIDOMCSSRule*
110 CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound)
111 {
112 aFound = false;
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 }
124 // Per spec: "Return Value ... null if ... not a valid index."
125 return nullptr;
126 }
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 }
138 bool
139 nsMediaExpression::Matches(nsPresContext *aPresContext,
140 const nsCSSValue& aActualValue) const
141 {
142 const nsCSSValue& actual = aActualValue;
143 const nsCSSValue& required = mValue;
145 // If we don't have the feature, the match fails.
146 if (actual.GetUnit() == eCSSUnit_Null) {
147 return false;
148 }
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 }
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 }
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 }
312 ExpressionEntry eentry = { *aExpression, aExpressionMatches };
313 entry->mExpressions.AppendElement(eentry);
314 }
316 bool
317 nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const
318 {
319 if (aPresContext->Medium() != mMedium) {
320 return false;
321 }
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?
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 }
339 return true;
340 }
342 void
343 nsMediaQuery::AppendToString(nsAString& aString) const
344 {
345 if (mHadUnknownExpression) {
346 aString.AppendLiteral("not all");
347 return;
348 }
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 }
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("(");
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 }
374 const nsMediaFeature *feature = expr.mFeature;
375 aString.Append(nsDependentAtomString(*feature->mName));
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 }
453 aString.AppendLiteral(")");
454 }
455 }
457 nsMediaQuery*
458 nsMediaQuery::Clone() const
459 {
460 return new nsMediaQuery(*this);
461 }
463 bool
464 nsMediaQuery::Matches(nsPresContext* aPresContext,
465 nsMediaQueryResultCacheKey* aKey) const
466 {
467 if (mHadUnknownExpression)
468 return false;
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?
479 match = expr.Matches(aPresContext, actual);
480 if (aKey) {
481 aKey->AddExpression(&expr, match);
482 }
483 }
485 return match == !mNegated;
486 }
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
494 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMediaList)
495 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMediaList)
497 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsMediaList)
499 nsMediaList::nsMediaList()
500 : mStyleSheet(nullptr)
501 {
502 SetIsDOMBinding();
503 }
505 nsMediaList::~nsMediaList()
506 {
507 }
509 /* virtual */ JSObject*
510 nsMediaList::WrapObject(JSContext* aCx)
511 {
512 return MediaListBinding::Wrap(aCx, this);
513 }
515 void
516 nsMediaList::GetText(nsAString& aMediaText)
517 {
518 aMediaText.Truncate();
520 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
521 nsMediaQuery* query = mArray[i];
523 query->AppendToString(aMediaText);
525 if (i + 1 < i_end) {
526 aMediaText.AppendLiteral(", ");
527 }
528 }
529 }
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;
538 bool htmlMode = mStyleSheet && mStyleSheet->GetOwnerNode();
540 parser.ParseMediaList(aMediaText, nullptr, 0, this, htmlMode);
541 }
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 }
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 }
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 }
576 NS_IMETHODIMP
577 nsMediaList::GetMediaText(nsAString& aMediaText)
578 {
579 GetText(aMediaText);
580 return NS_OK;
581 }
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 }
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 }
604 NS_IMETHODIMP
605 nsMediaList::SetMediaText(const nsAString& aMediaText)
606 {
607 nsCOMPtr<nsIDocument> doc;
609 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
611 SetText(aMediaText);
613 END_MEDIA_CHANGE(mStyleSheet, doc)
615 return NS_OK;
616 }
618 NS_IMETHODIMP
619 nsMediaList::GetLength(uint32_t* aLength)
620 {
621 NS_ENSURE_ARG_POINTER(aLength);
623 *aLength = Length();
624 return NS_OK;
625 }
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 }
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 }
648 NS_IMETHODIMP
649 nsMediaList::DeleteMedium(const nsAString& aOldMedium)
650 {
651 nsresult rv = NS_OK;
652 nsCOMPtr<nsIDocument> doc;
654 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
656 rv = Delete(aOldMedium);
657 if (NS_FAILED(rv))
658 return rv;
660 END_MEDIA_CHANGE(mStyleSheet, doc)
662 return rv;
663 }
665 NS_IMETHODIMP
666 nsMediaList::AppendMedium(const nsAString& aNewMedium)
667 {
668 nsresult rv = NS_OK;
669 nsCOMPtr<nsIDocument> doc;
671 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
673 rv = Append(aNewMedium);
674 if (NS_FAILED(rv))
675 return rv;
677 END_MEDIA_CHANGE(mStyleSheet, doc)
679 return rv;
680 }
682 nsresult
683 nsMediaList::Delete(const nsAString& aOldMedium)
684 {
685 if (aOldMedium.IsEmpty())
686 return NS_ERROR_DOM_NOT_FOUND_ERR;
688 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
689 nsMediaQuery* query = mArray[i];
691 nsAutoString buf;
692 query->AppendToString(buf);
693 if (buf == aOldMedium) {
694 mArray.RemoveElementAt(i);
695 return NS_OK;
696 }
697 }
699 return NS_ERROR_DOM_NOT_FOUND_ERR;
700 }
702 nsresult
703 nsMediaList::Append(const nsAString& aNewMedium)
704 {
705 if (aNewMedium.IsEmpty())
706 return NS_ERROR_DOM_NOT_FOUND_ERR;
708 Delete(aNewMedium);
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 }
722 mArray.SwapElements(buf);
723 return rv;
724 }
726 // -------------------------------
727 // CSS Style Sheet Inner Data Container
728 //
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);
743 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
744 if (!mPrincipal) {
745 NS_RUNTIMEABORT("OOM");
746 }
747 }
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 }
757 struct ChildSheetListBuilder {
758 nsRefPtr<nsCSSStyleSheet>* sheetSlot;
759 nsCSSStyleSheet* parent;
761 void SetParentLinks(nsCSSStyleSheet* aSheet) {
762 aSheet->mParent = parent;
763 aSheet->SetOwningDocument(parent->mDocument);
764 }
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 };
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 }
785 if (type != css::Rule::IMPORT_RULE) {
786 // We're past all the import rules; stop the enumeration.
787 return false;
788 }
790 ChildSheetListBuilder* builder =
791 static_cast<ChildSheetListBuilder*>(aBuilder);
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");
799 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
800 importRule->GetStyleSheet(getter_AddRefs(childSheet));
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 }
809 (*builder->sheetSlot) = cssSheet;
810 builder->SetParentLinks(*builder->sheetSlot);
811 builder->sheetSlot = &(*builder->sheetSlot)->mNext;
812 return true;
813 }
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);
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 }
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
842 s = s->mNext;
843 }
844 return n;
845 }
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);
865 ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet };
866 mOrderedRules.EnumerateForwards(nsCSSStyleSheet::RebuildChildList, &builder);
868 RebuildNameSpaces();
869 }
871 nsCSSStyleSheetInner::~nsCSSStyleSheetInner()
872 {
873 MOZ_COUNT_DTOR(nsCSSStyleSheetInner);
874 mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr);
875 }
877 nsCSSStyleSheetInner*
878 nsCSSStyleSheetInner::CloneFor(nsCSSStyleSheet* aPrimarySheet)
879 {
880 return new nsCSSStyleSheetInner(*this, aPrimarySheet);
881 }
883 void
884 nsCSSStyleSheetInner::AddSheet(nsCSSStyleSheet* aSheet)
885 {
886 mSheets.AppendElement(aSheet);
887 }
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));
903 ChildSheetListBuilder::ReparentChildList(mSheets[0], mFirstChild);
904 }
905 else {
906 mSheets.RemoveElement(aSheet);
907 }
908 }
910 static void
911 AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap)
912 {
913 NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type");
915 nsRefPtr<css::NameSpaceRule> nameSpaceRule = do_QueryObject(aRule);
917 nsAutoString urlSpec;
918 nameSpaceRule->GetURLSpec(urlSpec);
920 aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec);
921 }
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 }
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 }
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 }
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;
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
976 return n;
977 }
979 // -------------------------------
980 // CSS Style Sheet
981 //
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);
996 SetIsDOMBinding();
997 }
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 {
1016 mInner->AddSheet(this);
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 }
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 }
1030 SetIsDOMBinding();
1031 }
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 }
1057 void
1058 nsCSSStyleSheet::DropRuleCollection()
1059 {
1060 if (mRuleCollection) {
1061 mRuleCollection->DropReference();
1062 mRuleCollection = nullptr;
1063 }
1064 }
1066 void
1067 nsCSSStyleSheet::DropMedia()
1068 {
1069 if (mMedia) {
1070 mMedia->SetStyleSheet(nullptr);
1071 mMedia = nullptr;
1072 }
1073 }
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 }
1084 mInner->mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr);
1085 mInner->mOrderedRules.Clear();
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 }
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 }
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 }
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 }
1132 DOMCI_DATA(CSSStyleSheet, nsCSSStyleSheet)
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
1149 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSStyleSheet)
1150 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSStyleSheet)
1152 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSStyleSheet)
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)
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 }
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 }
1201 void
1202 nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
1203 nsIURI* aBaseURI)
1204 {
1205 NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
1207 NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete,
1208 "Can't call SetURL on sheets that are complete or have rules");
1210 mInner->mSheetURI = aSheetURI;
1211 mInner->mOriginalSheetURI = aOriginalSheetURI;
1212 mInner->mBaseURI = aBaseURI;
1213 }
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 }
1228 /* virtual */ nsIURI*
1229 nsCSSStyleSheet::GetSheetURI() const
1230 {
1231 return mInner->mSheetURI;
1232 }
1234 /* virtual */ nsIURI*
1235 nsCSSStyleSheet::GetBaseURI() const
1236 {
1237 return mInner->mBaseURI;
1238 }
1240 /* virtual */ void
1241 nsCSSStyleSheet::GetType(nsString& aType) const
1242 {
1243 aType.AssignLiteral("text/css");
1244 }
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 }
1257 void
1258 nsCSSStyleSheet::SetMedia(nsMediaList* aMedia)
1259 {
1260 mMedia = aMedia;
1261 }
1263 /* virtual */ bool
1264 nsCSSStyleSheet::HasRules() const
1265 {
1266 return StyleRuleCount() != 0;
1267 }
1269 /* virtual */ bool
1270 nsCSSStyleSheet::IsApplicable() const
1271 {
1272 return !mDisabled && mInner->mComplete;
1273 }
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;
1282 if (mInner->mComplete && oldDisabled != mDisabled) {
1283 ClearRuleCascades();
1285 if (mDocument) {
1286 mDocument->SetStyleSheetApplicableState(this, !mDisabled);
1287 }
1288 }
1289 }
1291 /* virtual */ bool
1292 nsCSSStyleSheet::IsComplete() const
1293 {
1294 return mInner->mComplete;
1295 }
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 }
1310 /* virtual */ nsIStyleSheet*
1311 nsCSSStyleSheet::GetParentSheet() const
1312 {
1313 return mParent;
1314 }
1316 /* virtual */ nsIDocument*
1317 nsCSSStyleSheet::GetOwningDocument() const
1318 {
1319 return mDocument;
1320 }
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 }
1337 uint64_t
1338 nsCSSStyleSheet::FindOwningWindowInnerID() const
1339 {
1340 uint64_t windowID = 0;
1341 if (mDocument) {
1342 windowID = mDocument->InnerWindowID();
1343 }
1345 if (windowID == 0 && mOwningNode) {
1346 windowID = mOwningNode->OwnerDoc()->InnerWindowID();
1347 }
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 }
1359 if (windowID == 0 && mParent) {
1360 windowID = mParent->FindOwningWindowInnerID();
1361 }
1363 return windowID;
1364 }
1366 void
1367 nsCSSStyleSheet::AppendStyleSheet(nsCSSStyleSheet* aSheet)
1368 {
1369 NS_PRECONDITION(nullptr != aSheet, "null arg");
1371 WillDirty();
1372 nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild;
1373 while (*tail) {
1374 tail = &(*tail)->mNext;
1375 }
1376 *tail = aSheet;
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 }
1385 void
1386 nsCSSStyleSheet::InsertStyleSheetAt(nsCSSStyleSheet* aSheet, int32_t aIndex)
1387 {
1388 NS_PRECONDITION(nullptr != aSheet, "null arg");
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;
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 }
1406 void
1407 nsCSSStyleSheet::PrependStyleRule(css::Rule* aRule)
1408 {
1409 NS_PRECONDITION(nullptr != aRule, "null arg");
1411 WillDirty();
1412 mInner->mOrderedRules.InsertObjectAt(aRule, 0);
1413 aRule->SetStyleSheet(this);
1414 DidDirty();
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 }
1422 void
1423 nsCSSStyleSheet::AppendStyleRule(css::Rule* aRule)
1424 {
1425 NS_PRECONDITION(nullptr != aRule, "null arg");
1427 WillDirty();
1428 mInner->mOrderedRules.AppendObject(aRule);
1429 aRule->SetStyleSheet(this);
1430 DidDirty();
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 }
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!");
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);
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 }
1463 int32_t
1464 nsCSSStyleSheet::StyleRuleCount() const
1465 {
1466 return mInner->mOrderedRules.Count();
1467 }
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 }
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;
1485 const nsCSSStyleSheet* child = mInner->mFirstChild;
1486 while (child) {
1487 count++;
1488 child = child->mNext;
1489 }
1491 return count;
1492 }
1494 nsCSSStyleSheet::EnsureUniqueInnerResult
1495 nsCSSStyleSheet::EnsureUniqueInner()
1496 {
1497 mDirty = true;
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;
1509 // otherwise the rule processor has pointers to the old rules
1510 ClearRuleCascades();
1512 return eUniqueInner_ClonedInner;
1513 }
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 }
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 }
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 }
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 };
1557 /* virtual */ void
1558 nsCSSStyleSheet::List(FILE* out, int32_t aIndent) const
1559 {
1561 int32_t index;
1563 // Indent
1564 for (index = aIndent; --index >= 0; ) fputs(" ", out);
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 }
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);
1581 for (const nsCSSStyleSheet* child = mInner->mFirstChild;
1582 child;
1583 child = child->mNext) {
1584 child->List(out, aIndent + 1);
1585 }
1587 fputs("Rules in source order:\n", out);
1588 ListRules(mInner->mOrderedRules, out, aIndent);
1589 }
1590 #endif
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 }
1608 void
1609 nsCSSStyleSheet::WillDirty()
1610 {
1611 if (mInner->mComplete) {
1612 EnsureUniqueInner();
1613 }
1614 }
1616 void
1617 nsCSSStyleSheet::DidDirty()
1618 {
1619 NS_ABORT_IF_FALSE(!mInner->mComplete || mDirty,
1620 "caller must have called WillDirty()");
1621 ClearRuleCascades();
1622 }
1624 nsresult
1625 nsCSSStyleSheet::SubjectSubsumesInnerPrincipal()
1626 {
1627 // Get the security manager and do the subsumes check
1628 nsIScriptSecurityManager *securityManager =
1629 nsContentUtils::GetSecurityManager();
1631 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1632 nsresult rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1633 NS_ENSURE_SUCCESS(rv, rv);
1635 if (!subjectPrincipal) {
1636 return NS_ERROR_DOM_SECURITY_ERR;
1637 }
1639 bool subsumes;
1640 rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
1641 NS_ENSURE_SUCCESS(rv, rv);
1643 if (subsumes) {
1644 return NS_OK;
1645 }
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 }
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 }
1662 WillDirty();
1664 mInner->mPrincipal = subjectPrincipal;
1666 DidDirty();
1667 }
1669 return NS_OK;
1670 }
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 }
1680 AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap);
1681 return NS_OK;
1682 }
1684 // nsIDOMStyleSheet interface
1685 NS_IMETHODIMP
1686 nsCSSStyleSheet::GetType(nsAString& aType)
1687 {
1688 aType.AssignLiteral("text/css");
1689 return NS_OK;
1690 }
1692 NS_IMETHODIMP
1693 nsCSSStyleSheet::GetDisabled(bool* aDisabled)
1694 {
1695 *aDisabled = Disabled();
1696 return NS_OK;
1697 }
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 }
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 }
1716 NS_IMETHODIMP
1717 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
1718 {
1719 NS_ENSURE_ARG_POINTER(aParentStyleSheet);
1721 NS_IF_ADDREF(*aParentStyleSheet = mParent);
1723 return NS_OK;
1724 }
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 }
1737 return NS_OK;
1738 }
1740 /* virtual */ void
1741 nsCSSStyleSheet::GetTitle(nsString& aTitle) const
1742 {
1743 aTitle = mTitle;
1744 }
1746 NS_IMETHODIMP
1747 nsCSSStyleSheet::GetTitle(nsAString& aTitle)
1748 {
1749 aTitle.Assign(mTitle);
1750 return NS_OK;
1751 }
1753 NS_IMETHODIMP
1754 nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia)
1755 {
1756 NS_ADDREF(*aMedia = Media());
1757 return NS_OK;
1758 }
1760 nsMediaList*
1761 nsCSSStyleSheet::Media()
1762 {
1763 if (!mMedia) {
1764 mMedia = new nsMediaList();
1765 mMedia->SetStyleSheet(this);
1766 }
1768 return mMedia;
1769 }
1771 NS_IMETHODIMP
1772 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
1773 {
1774 NS_IF_ADDREF(*aOwnerRule = GetOwnerRule());
1775 return NS_OK;
1776 }
1778 nsIDOMCSSRule*
1779 nsCSSStyleSheet::GetDOMOwnerRule() const
1780 {
1781 return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr;
1782 }
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 }
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 }
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 }
1810 // OK, security check passed, so get the rule collection
1811 if (!mRuleCollection) {
1812 mRuleCollection = new CSSRuleListImpl(this);
1813 }
1815 return mRuleCollection;
1816 }
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);
1828 return InsertRuleInternal(aRule, aIndex, aReturn);
1829 }
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 }
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 }
1852 WillDirty();
1854 if (aIndex > uint32_t(mInner->mOrderedRules.Count()))
1855 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1857 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX,
1858 "Too many style rules!");
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 }
1868 nsCSSParser css(loader, this);
1870 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
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;
1878 // Hierarchy checking.
1879 int32_t newType = rule->GetType();
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 }
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 }
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 }
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 }
1909 css::Rule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1);
1910 int32_t prevType = prevRule->GetType();
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 }
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 }
1926 bool insertResult = mInner->mOrderedRules.InsertObjectAt(rule, aIndex);
1927 NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY);
1928 DidDirty();
1930 rule->SetStyleSheet(this);
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 }
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 }
1948 *aReturn = aIndex;
1949 return NS_OK;
1950 }
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 }
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);
1965 // XXX TBI: handle @rule types
1966 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1968 WillDirty();
1970 if (aIndex >= uint32_t(mInner->mOrderedRules.Count()))
1971 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1973 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX,
1974 "Too many style rules!");
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();
1988 if (mDocument) {
1989 mDocument->StyleRuleRemoved(this, rule);
1990 }
1991 }
1993 return NS_OK;
1994 }
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);
2004 // check that the rule actually belongs to this sheet!
2005 if (this != rule->GetStyleSheet()) {
2006 return NS_ERROR_INVALID_ARG;
2007 }
2009 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2011 WillDirty();
2013 nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
2014 NS_ENSURE_SUCCESS(result, result);
2016 rule->SetStyleSheet(nullptr);
2018 DidDirty();
2020 if (mDocument) {
2021 mDocument->StyleRuleRemoved(this, rule);
2022 }
2024 return NS_OK;
2025 }
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 }
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 }
2047 nsCSSParser css(loader, this);
2049 // parse and grab the rule
2050 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2052 WillDirty();
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;
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 }
2080 result = aGroup->InsertStyleRuleAt(aIndex, rule);
2081 NS_ENSURE_SUCCESS(result, result);
2082 DidDirty();
2084 if (mDocument) {
2085 mDocument->StyleRuleAdded(this, rule);
2086 }
2088 *_retval = aIndex;
2089 return NS_OK;
2090 }
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");
2099 WillDirty();
2101 nsresult result = aGroup->ReplaceStyleRule(aOld, aNew);
2102 DidDirty();
2103 return result;
2104 }
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!");
2118 if (mDocument && NS_SUCCEEDED(aStatus)) {
2119 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2121 // XXXldb @import rules shouldn't even implement nsIStyleRule (but
2122 // they do)!
2123 mDocument->StyleRuleAdded(this, aSheet->GetOwnerRule());
2124 }
2126 return NS_OK;
2127 }
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 }
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 }
2147 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2149 WillDirty();
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 }
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;
2171 // allow unsafe rules if the style sheet's principal is the system principal
2172 bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal);
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);
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 }
2194 /* virtual */ nsIURI*
2195 nsCSSStyleSheet::GetOriginalURI() const
2196 {
2197 return mInner->mOriginalSheetURI;
2198 }
2200 /* virtual */
2201 JSObject*
2202 nsCSSStyleSheet::WrapObject(JSContext* aCx)
2203 {
2204 return CSSStyleSheetBinding::Wrap(aCx, this);
2205 }