Bumping manifests a=b2g-bump
[gecko.git] / layout / style / CSSStyleSheet.cpp
blobf418e969e76b7c9913de624a61dddc2daf7155f0
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 "mozilla/CSSStyleSheet.h"
11 #include "nsIAtom.h"
12 #include "nsCSSRuleProcessor.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/ShadowRoot.h"
16 #include "mozilla/dom/MediaListBinding.h"
17 #include "mozilla/css/NameSpaceRule.h"
18 #include "mozilla/css/GroupRule.h"
19 #include "mozilla/css/ImportRule.h"
20 #include "nsIMediaList.h"
21 #include "nsIDocument.h"
22 #include "nsPresContext.h"
23 #include "nsGkAtoms.h"
24 #include "nsString.h"
25 #include "nsTArray.h"
26 #include "nsIDOMCSSStyleSheet.h"
27 #include "mozilla/dom/CSSRuleList.h"
28 #include "nsIDOMMediaList.h"
29 #include "nsIDOMNode.h"
30 #include "nsError.h"
31 #include "nsCSSParser.h"
32 #include "mozilla/css/Loader.h"
33 #include "nsICSSLoaderObserver.h"
34 #include "nsNameSpaceManager.h"
35 #include "nsXMLNameSpaceMap.h"
36 #include "nsCOMPtr.h"
37 #include "nsContentUtils.h"
38 #include "nsIScriptSecurityManager.h"
39 #include "mozAutoDocUpdate.h"
40 #include "nsRuleNode.h"
41 #include "nsMediaFeatures.h"
42 #include "nsDOMClassInfoID.h"
43 #include "mozilla/Likely.h"
44 #include "mozilla/dom/CSSStyleSheetBinding.h"
45 #include "nsComponentManagerUtils.h"
47 using namespace mozilla;
48 using namespace mozilla::dom;
50 // -------------------------------
51 // Style Rule List for the DOM
53 class CSSRuleListImpl MOZ_FINAL : public CSSRuleList
55 public:
56 explicit CSSRuleListImpl(CSSStyleSheet *aStyleSheet);
58 virtual CSSStyleSheet* GetParentObject() MOZ_OVERRIDE;
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 CSSStyleSheet* mStyleSheet;
73 CSSRuleListImpl::CSSRuleListImpl(CSSStyleSheet *aStyleSheet)
75 // Not reference counted to avoid circular references.
76 // The style sheet will tell us when its going away.
77 mStyleSheet = aStyleSheet;
80 CSSRuleListImpl::~CSSRuleListImpl()
84 CSSStyleSheet*
85 CSSRuleListImpl::GetParentObject()
87 return mStyleSheet;
90 uint32_t
91 CSSRuleListImpl::Length()
93 if (!mStyleSheet) {
94 return 0;
97 return AssertedCast<uint32_t>(mStyleSheet->StyleRuleCount());
100 nsIDOMCSSRule*
101 CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound)
103 aFound = false;
105 if (mStyleSheet) {
106 // ensure rules have correct parent
107 mStyleSheet->EnsureUniqueInner();
108 css::Rule* rule = mStyleSheet->GetStyleRuleAt(aIndex);
109 if (rule) {
110 aFound = true;
111 return rule->GetDOMRule();
115 // Per spec: "Return Value ... null if ... not a valid index."
116 return nullptr;
119 template <class Numeric>
120 int32_t DoCompare(Numeric a, Numeric b)
122 if (a == b)
123 return 0;
124 if (a < b)
125 return -1;
126 return 1;
129 bool
130 nsMediaExpression::Matches(nsPresContext *aPresContext,
131 const nsCSSValue& aActualValue) const
133 const nsCSSValue& actual = aActualValue;
134 const nsCSSValue& required = mValue;
136 // If we don't have the feature, the match fails.
137 if (actual.GetUnit() == eCSSUnit_Null) {
138 return false;
141 // If the expression had no value to match, the match succeeds,
142 // unless the value is an integer 0 or a zero length.
143 if (required.GetUnit() == eCSSUnit_Null) {
144 if (actual.GetUnit() == eCSSUnit_Integer)
145 return actual.GetIntValue() != 0;
146 if (actual.IsLengthUnit())
147 return actual.GetFloatValue() != 0;
148 return true;
151 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxAllowed ||
152 mRange == nsMediaExpression::eEqual, "yikes");
153 int32_t cmp; // -1 (actual < required)
154 // 0 (actual == required)
155 // 1 (actual > required)
156 switch (mFeature->mValueType) {
157 case nsMediaFeature::eLength:
159 NS_ASSERTION(actual.IsLengthUnit(), "bad actual value");
160 NS_ASSERTION(required.IsLengthUnit(), "bad required value");
161 nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont(
162 aPresContext, actual);
163 nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont(
164 aPresContext, required);
165 cmp = DoCompare(actualCoord, requiredCoord);
167 break;
168 case nsMediaFeature::eInteger:
169 case nsMediaFeature::eBoolInteger:
171 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Integer,
172 "bad actual value");
173 NS_ASSERTION(required.GetUnit() == eCSSUnit_Integer,
174 "bad required value");
175 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
176 actual.GetIntValue() == 0 || actual.GetIntValue() == 1,
177 "bad actual bool integer value");
178 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
179 required.GetIntValue() == 0 || required.GetIntValue() == 1,
180 "bad required bool integer value");
181 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
183 break;
184 case nsMediaFeature::eFloat:
186 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Number,
187 "bad actual value");
188 NS_ASSERTION(required.GetUnit() == eCSSUnit_Number,
189 "bad required value");
190 cmp = DoCompare(actual.GetFloatValue(), required.GetFloatValue());
192 break;
193 case nsMediaFeature::eIntRatio:
195 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array &&
196 actual.GetArrayValue()->Count() == 2 &&
197 actual.GetArrayValue()->Item(0).GetUnit() ==
198 eCSSUnit_Integer &&
199 actual.GetArrayValue()->Item(1).GetUnit() ==
200 eCSSUnit_Integer,
201 "bad actual value");
202 NS_ASSERTION(required.GetUnit() == eCSSUnit_Array &&
203 required.GetArrayValue()->Count() == 2 &&
204 required.GetArrayValue()->Item(0).GetUnit() ==
205 eCSSUnit_Integer &&
206 required.GetArrayValue()->Item(1).GetUnit() ==
207 eCSSUnit_Integer,
208 "bad required value");
209 // Convert to int64_t so we can multiply without worry. Note
210 // that while the spec requires that both halves of |required|
211 // be positive, the numerator or denominator of |actual| might
212 // be zero (e.g., when testing 'aspect-ratio' on a 0-width or
213 // 0-height iframe).
214 int64_t actualNum = actual.GetArrayValue()->Item(0).GetIntValue(),
215 actualDen = actual.GetArrayValue()->Item(1).GetIntValue(),
216 requiredNum = required.GetArrayValue()->Item(0).GetIntValue(),
217 requiredDen = required.GetArrayValue()->Item(1).GetIntValue();
218 cmp = DoCompare(actualNum * requiredDen, requiredNum * actualDen);
220 break;
221 case nsMediaFeature::eResolution:
223 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch ||
224 actual.GetUnit() == eCSSUnit_Pixel ||
225 actual.GetUnit() == eCSSUnit_Centimeter,
226 "bad actual value");
227 NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch ||
228 required.GetUnit() == eCSSUnit_Pixel ||
229 required.GetUnit() == eCSSUnit_Centimeter,
230 "bad required value");
231 float actualDPI = actual.GetFloatValue();
232 if (actual.GetUnit() == eCSSUnit_Centimeter) {
233 actualDPI = actualDPI * 2.54f;
234 } else if (actual.GetUnit() == eCSSUnit_Pixel) {
235 actualDPI = actualDPI * 96.0f;
237 float requiredDPI = required.GetFloatValue();
238 if (required.GetUnit() == eCSSUnit_Centimeter) {
239 requiredDPI = requiredDPI * 2.54f;
240 } else if (required.GetUnit() == eCSSUnit_Pixel) {
241 requiredDPI = requiredDPI * 96.0f;
243 cmp = DoCompare(actualDPI, requiredDPI);
245 break;
246 case nsMediaFeature::eEnumerated:
248 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated,
249 "bad actual value");
250 NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated,
251 "bad required value");
252 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed,
253 "bad range"); // we asserted above about mRange
254 // We don't really need DoCompare, but it doesn't hurt (and
255 // maybe the compiler will condense this case with eInteger).
256 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
258 break;
259 case nsMediaFeature::eIdent:
261 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Ident,
262 "bad actual value");
263 NS_ASSERTION(required.GetUnit() == eCSSUnit_Ident,
264 "bad required value");
265 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed,
266 "bad range");
267 cmp = !(actual == required); // string comparison
269 break;
271 switch (mRange) {
272 case nsMediaExpression::eMin:
273 return cmp != -1;
274 case nsMediaExpression::eMax:
275 return cmp != 1;
276 case nsMediaExpression::eEqual:
277 return cmp == 0;
279 NS_NOTREACHED("unexpected mRange");
280 return false;
283 void
284 nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression* aExpression,
285 bool aExpressionMatches)
287 const nsMediaFeature *feature = aExpression->mFeature;
288 FeatureEntry *entry = nullptr;
289 for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) {
290 if (mFeatureCache[i].mFeature == feature) {
291 entry = &mFeatureCache[i];
292 break;
295 if (!entry) {
296 entry = mFeatureCache.AppendElement();
297 if (!entry) {
298 return; /* out of memory */
300 entry->mFeature = feature;
303 ExpressionEntry eentry = { *aExpression, aExpressionMatches };
304 entry->mExpressions.AppendElement(eentry);
307 bool
308 nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const
310 if (aPresContext->Medium() != mMedium) {
311 return false;
314 for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) {
315 const FeatureEntry *entry = &mFeatureCache[i];
316 nsCSSValue actual;
317 nsresult rv =
318 (entry->mFeature->mGetter)(aPresContext, entry->mFeature, actual);
319 NS_ENSURE_SUCCESS(rv, false); // any better ideas?
321 for (uint32_t j = 0; j < entry->mExpressions.Length(); ++j) {
322 const ExpressionEntry &eentry = entry->mExpressions[j];
323 if (eentry.mExpression.Matches(aPresContext, actual) !=
324 eentry.mExpressionMatches) {
325 return false;
330 return true;
333 void
334 nsMediaQuery::AppendToString(nsAString& aString) const
336 if (mHadUnknownExpression) {
337 aString.AppendLiteral("not all");
338 return;
341 NS_ASSERTION(!mNegated || !mHasOnly, "can't have not and only");
342 NS_ASSERTION(!mTypeOmitted || (!mNegated && !mHasOnly),
343 "can't have not or only when type is omitted");
344 if (!mTypeOmitted) {
345 if (mNegated) {
346 aString.AppendLiteral("not ");
347 } else if (mHasOnly) {
348 aString.AppendLiteral("only ");
350 aString.Append(nsDependentAtomString(mMediaType));
353 for (uint32_t i = 0, i_end = mExpressions.Length(); i < i_end; ++i) {
354 if (i > 0 || !mTypeOmitted)
355 aString.AppendLiteral(" and ");
356 aString.Append('(');
358 const nsMediaExpression &expr = mExpressions[i];
359 if (expr.mRange == nsMediaExpression::eMin) {
360 aString.AppendLiteral("min-");
361 } else if (expr.mRange == nsMediaExpression::eMax) {
362 aString.AppendLiteral("max-");
365 const nsMediaFeature *feature = expr.mFeature;
366 aString.Append(nsDependentAtomString(*feature->mName));
368 if (expr.mValue.GetUnit() != eCSSUnit_Null) {
369 aString.AppendLiteral(": ");
370 switch (feature->mValueType) {
371 case nsMediaFeature::eLength:
372 NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit");
373 // Use 'width' as a property that takes length values
374 // written in the normal way.
375 expr.mValue.AppendToString(eCSSProperty_width, aString,
376 nsCSSValue::eNormalized);
377 break;
378 case nsMediaFeature::eInteger:
379 case nsMediaFeature::eBoolInteger:
380 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer,
381 "bad unit");
382 // Use 'z-index' as a property that takes integer values
383 // written without anything extra.
384 expr.mValue.AppendToString(eCSSProperty_z_index, aString,
385 nsCSSValue::eNormalized);
386 break;
387 case nsMediaFeature::eFloat:
389 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Number,
390 "bad unit");
391 // Use 'line-height' as a property that takes float values
392 // written in the normal way.
393 expr.mValue.AppendToString(eCSSProperty_line_height, aString,
394 nsCSSValue::eNormalized);
396 break;
397 case nsMediaFeature::eIntRatio:
399 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array,
400 "bad unit");
401 nsCSSValue::Array *array = expr.mValue.GetArrayValue();
402 NS_ASSERTION(array->Count() == 2, "unexpected length");
403 NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
404 "bad unit");
405 NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer,
406 "bad unit");
407 array->Item(0).AppendToString(eCSSProperty_z_index, aString,
408 nsCSSValue::eNormalized);
409 aString.Append('/');
410 array->Item(1).AppendToString(eCSSProperty_z_index, aString,
411 nsCSSValue::eNormalized);
413 break;
414 case nsMediaFeature::eResolution:
416 aString.AppendFloat(expr.mValue.GetFloatValue());
417 if (expr.mValue.GetUnit() == eCSSUnit_Inch) {
418 aString.AppendLiteral("dpi");
419 } else if (expr.mValue.GetUnit() == eCSSUnit_Pixel) {
420 aString.AppendLiteral("dppx");
421 } else {
422 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter,
423 "bad unit");
424 aString.AppendLiteral("dpcm");
427 break;
428 case nsMediaFeature::eEnumerated:
429 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Enumerated,
430 "bad unit");
431 AppendASCIItoUTF16(
432 nsCSSProps::ValueToKeyword(expr.mValue.GetIntValue(),
433 feature->mData.mKeywordTable),
434 aString);
435 break;
436 case nsMediaFeature::eIdent:
437 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Ident,
438 "bad unit");
439 aString.Append(expr.mValue.GetStringBufferValue());
440 break;
444 aString.Append(')');
448 nsMediaQuery*
449 nsMediaQuery::Clone() const
451 return new nsMediaQuery(*this);
454 bool
455 nsMediaQuery::Matches(nsPresContext* aPresContext,
456 nsMediaQueryResultCacheKey* aKey) const
458 if (mHadUnknownExpression)
459 return false;
461 bool match =
462 mMediaType == aPresContext->Medium() || mMediaType == nsGkAtoms::all;
463 for (uint32_t i = 0, i_end = mExpressions.Length(); match && i < i_end; ++i) {
464 const nsMediaExpression &expr = mExpressions[i];
465 nsCSSValue actual;
466 nsresult rv =
467 (expr.mFeature->mGetter)(aPresContext, expr.mFeature, actual);
468 NS_ENSURE_SUCCESS(rv, false); // any better ideas?
470 match = expr.Matches(aPresContext, actual);
471 if (aKey) {
472 aKey->AddExpression(&expr, match);
476 return match == !mNegated;
479 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMediaList)
480 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
481 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList)
482 NS_INTERFACE_MAP_ENTRY(nsISupports)
483 NS_INTERFACE_MAP_END
485 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMediaList)
486 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMediaList)
488 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsMediaList)
490 nsMediaList::nsMediaList()
491 : mStyleSheet(nullptr)
493 SetIsDOMBinding();
496 nsMediaList::~nsMediaList()
500 /* virtual */ JSObject*
501 nsMediaList::WrapObject(JSContext* aCx)
503 return MediaListBinding::Wrap(aCx, this);
506 void
507 nsMediaList::GetText(nsAString& aMediaText)
509 aMediaText.Truncate();
511 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
512 nsMediaQuery* query = mArray[i];
514 query->AppendToString(aMediaText);
516 if (i + 1 < i_end) {
517 aMediaText.AppendLiteral(", ");
522 // XXXbz this is so ill-defined in the spec, it's not clear quite what
523 // it should be doing....
524 void
525 nsMediaList::SetText(const nsAString& aMediaText)
527 nsCSSParser parser;
529 bool htmlMode = mStyleSheet && mStyleSheet->GetOwnerNode();
531 parser.ParseMediaList(aMediaText, nullptr, 0, this, htmlMode);
534 bool
535 nsMediaList::Matches(nsPresContext* aPresContext,
536 nsMediaQueryResultCacheKey* aKey)
538 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
539 if (mArray[i]->Matches(aPresContext, aKey)) {
540 return true;
543 return mArray.IsEmpty();
546 void
547 nsMediaList::SetStyleSheet(CSSStyleSheet* aSheet)
549 NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
550 "multiple style sheets competing for one media list");
551 mStyleSheet = aSheet;
554 already_AddRefed<nsMediaList>
555 nsMediaList::Clone()
557 nsRefPtr<nsMediaList> result = new nsMediaList();
558 result->mArray.AppendElements(mArray.Length());
559 for (uint32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
560 result->mArray[i] = mArray[i]->Clone();
561 MOZ_ASSERT(result->mArray[i]);
563 return result.forget();
566 NS_IMETHODIMP
567 nsMediaList::GetMediaText(nsAString& aMediaText)
569 GetText(aMediaText);
570 return NS_OK;
573 // "sheet" should be a CSSStyleSheet and "doc" should be an
574 // nsCOMPtr<nsIDocument>
575 #define BEGIN_MEDIA_CHANGE(sheet, doc) \
576 if (sheet) { \
577 doc = sheet->GetOwningDocument(); \
579 mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); \
580 if (sheet) { \
581 sheet->WillDirty(); \
584 #define END_MEDIA_CHANGE(sheet, doc) \
585 if (sheet) { \
586 sheet->DidDirty(); \
588 /* XXXldb Pass something meaningful? */ \
589 if (doc) { \
590 doc->StyleRuleChanged(sheet, nullptr, nullptr); \
594 NS_IMETHODIMP
595 nsMediaList::SetMediaText(const nsAString& aMediaText)
597 nsCOMPtr<nsIDocument> doc;
599 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
601 SetText(aMediaText);
603 END_MEDIA_CHANGE(mStyleSheet, doc)
605 return NS_OK;
608 NS_IMETHODIMP
609 nsMediaList::GetLength(uint32_t* aLength)
611 NS_ENSURE_ARG_POINTER(aLength);
613 *aLength = Length();
614 return NS_OK;
617 NS_IMETHODIMP
618 nsMediaList::Item(uint32_t aIndex, nsAString& aReturn)
620 bool dummy;
621 IndexedGetter(aIndex, dummy, aReturn);
622 return NS_OK;
625 void
626 nsMediaList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aReturn)
628 if (aIndex < Length()) {
629 aFound = true;
630 aReturn.Truncate();
631 mArray[aIndex]->AppendToString(aReturn);
632 } else {
633 aFound = false;
634 SetDOMStringToNull(aReturn);
638 NS_IMETHODIMP
639 nsMediaList::DeleteMedium(const nsAString& aOldMedium)
641 nsresult rv = NS_OK;
642 nsCOMPtr<nsIDocument> doc;
644 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
646 rv = Delete(aOldMedium);
647 if (NS_FAILED(rv))
648 return rv;
650 END_MEDIA_CHANGE(mStyleSheet, doc)
652 return rv;
655 NS_IMETHODIMP
656 nsMediaList::AppendMedium(const nsAString& aNewMedium)
658 nsresult rv = NS_OK;
659 nsCOMPtr<nsIDocument> doc;
661 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
663 rv = Append(aNewMedium);
664 if (NS_FAILED(rv))
665 return rv;
667 END_MEDIA_CHANGE(mStyleSheet, doc)
669 return rv;
672 nsresult
673 nsMediaList::Delete(const nsAString& aOldMedium)
675 if (aOldMedium.IsEmpty())
676 return NS_ERROR_DOM_NOT_FOUND_ERR;
678 for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) {
679 nsMediaQuery* query = mArray[i];
681 nsAutoString buf;
682 query->AppendToString(buf);
683 if (buf == aOldMedium) {
684 mArray.RemoveElementAt(i);
685 return NS_OK;
689 return NS_ERROR_DOM_NOT_FOUND_ERR;
692 nsresult
693 nsMediaList::Append(const nsAString& aNewMedium)
695 if (aNewMedium.IsEmpty())
696 return NS_ERROR_DOM_NOT_FOUND_ERR;
698 Delete(aNewMedium);
700 nsresult rv = NS_OK;
701 nsTArray<nsAutoPtr<nsMediaQuery> > buf;
702 mArray.SwapElements(buf);
703 SetText(aNewMedium);
704 if (mArray.Length() == 1) {
705 nsMediaQuery *query = mArray[0].forget();
706 if (!buf.AppendElement(query)) {
707 delete query;
708 rv = NS_ERROR_OUT_OF_MEMORY;
712 mArray.SwapElements(buf);
713 return rv;
716 namespace mozilla {
718 // -------------------------------
719 // CSS Style Sheet Inner Data Container
723 CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet,
724 CORSMode aCORSMode)
725 : mSheets(),
726 mCORSMode(aCORSMode),
727 mComplete(false)
728 #ifdef DEBUG
729 , mPrincipalSet(false)
730 #endif
732 MOZ_COUNT_CTOR(CSSStyleSheetInner);
733 mSheets.AppendElement(aPrimarySheet);
735 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
736 if (!mPrincipal) {
737 NS_RUNTIMEABORT("OOM");
741 static bool SetStyleSheetReference(css::Rule* aRule, void* aSheet)
743 if (aRule) {
744 aRule->SetStyleSheet(static_cast<CSSStyleSheet*>(aSheet));
746 return true;
749 struct ChildSheetListBuilder {
750 nsRefPtr<CSSStyleSheet>* sheetSlot;
751 CSSStyleSheet* parent;
753 void SetParentLinks(CSSStyleSheet* aSheet) {
754 aSheet->mParent = parent;
755 aSheet->SetOwningDocument(parent->mDocument);
758 static void ReparentChildList(CSSStyleSheet* aPrimarySheet,
759 CSSStyleSheet* aFirstChild)
761 for (CSSStyleSheet *child = aFirstChild; child; child = child->mNext) {
762 child->mParent = aPrimarySheet;
763 child->SetOwningDocument(aPrimarySheet->mDocument);
768 bool
769 CSSStyleSheet::RebuildChildList(css::Rule* aRule, void* aBuilder)
771 int32_t type = aRule->GetType();
772 if (type < css::Rule::IMPORT_RULE) {
773 // Keep going till we get to the import rules.
774 return true;
777 if (type != css::Rule::IMPORT_RULE) {
778 // We're past all the import rules; stop the enumeration.
779 return false;
782 ChildSheetListBuilder* builder =
783 static_cast<ChildSheetListBuilder*>(aBuilder);
785 // XXXbz We really need to decomtaminate all this stuff. Is there a reason
786 // that I can't just QI to ImportRule and get a CSSStyleSheet
787 // directly from it?
788 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(aRule));
789 NS_ASSERTION(importRule, "GetType lied");
791 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
792 importRule->GetStyleSheet(getter_AddRefs(childSheet));
794 // Have to do this QI to be safe, since XPConnect can fake
795 // nsIDOMCSSStyleSheets
796 nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(childSheet);
797 if (!cssSheet) {
798 return true;
801 (*builder->sheetSlot) = cssSheet;
802 builder->SetParentLinks(*builder->sheetSlot);
803 builder->sheetSlot = &(*builder->sheetSlot)->mNext;
804 return true;
807 size_t
808 CSSStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
810 size_t n = 0;
811 const CSSStyleSheet* s = this;
812 while (s) {
813 n += aMallocSizeOf(s);
815 // Each inner can be shared by multiple sheets. So we only count the inner
816 // if this sheet is the first one in the list of those sharing it. As a
817 // result, the first such sheet takes all the blame for the memory
818 // consumption of the inner, which isn't ideal but it's better than
819 // double-counting the inner.
820 if (s->mInner->mSheets[0] == s) {
821 n += s->mInner->SizeOfIncludingThis(aMallocSizeOf);
824 // Measurement of the following members may be added later if DMD finds it
825 // is worthwhile:
826 // - s->mTitle
827 // - s->mMedia
828 // - s->mRuleCollection
829 // - s->mRuleProcessors
831 // The following members are not measured:
832 // - s->mOwnerRule, because it's non-owning
834 s = s->mNext;
836 return n;
839 CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy,
840 CSSStyleSheet* aPrimarySheet)
841 : mSheets(),
842 mSheetURI(aCopy.mSheetURI),
843 mOriginalSheetURI(aCopy.mOriginalSheetURI),
844 mBaseURI(aCopy.mBaseURI),
845 mPrincipal(aCopy.mPrincipal),
846 mCORSMode(aCopy.mCORSMode),
847 mComplete(aCopy.mComplete)
848 #ifdef DEBUG
849 , mPrincipalSet(aCopy.mPrincipalSet)
850 #endif
852 MOZ_COUNT_CTOR(CSSStyleSheetInner);
853 AddSheet(aPrimarySheet);
854 aCopy.mOrderedRules.EnumerateForwards(css::GroupRule::CloneRuleInto, &mOrderedRules);
855 mOrderedRules.EnumerateForwards(SetStyleSheetReference, aPrimarySheet);
857 ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet };
858 mOrderedRules.EnumerateForwards(CSSStyleSheet::RebuildChildList, &builder);
860 RebuildNameSpaces();
863 CSSStyleSheetInner::~CSSStyleSheetInner()
865 MOZ_COUNT_DTOR(CSSStyleSheetInner);
866 mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr);
869 CSSStyleSheetInner*
870 CSSStyleSheetInner::CloneFor(CSSStyleSheet* aPrimarySheet)
872 return new CSSStyleSheetInner(*this, aPrimarySheet);
875 void
876 CSSStyleSheetInner::AddSheet(CSSStyleSheet* aSheet)
878 mSheets.AppendElement(aSheet);
881 void
882 CSSStyleSheetInner::RemoveSheet(CSSStyleSheet* aSheet)
884 if (1 == mSheets.Length()) {
885 NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
886 delete this;
887 return;
889 if (aSheet == mSheets.ElementAt(0)) {
890 mSheets.RemoveElementAt(0);
891 NS_ASSERTION(mSheets.Length(), "no parents");
892 mOrderedRules.EnumerateForwards(SetStyleSheetReference,
893 mSheets.ElementAt(0));
895 ChildSheetListBuilder::ReparentChildList(mSheets[0], mFirstChild);
897 else {
898 mSheets.RemoveElement(aSheet);
902 static void
903 AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap)
905 NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type");
907 nsRefPtr<css::NameSpaceRule> nameSpaceRule = do_QueryObject(aRule);
909 nsAutoString urlSpec;
910 nameSpaceRule->GetURLSpec(urlSpec);
912 aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec);
915 static bool
916 CreateNameSpace(css::Rule* aRule, void* aNameSpacePtr)
918 int32_t type = aRule->GetType();
919 if (css::Rule::NAMESPACE_RULE == type) {
920 AddNamespaceRuleToMap(aRule,
921 static_cast<nsXMLNameSpaceMap*>(aNameSpacePtr));
922 return true;
924 // stop if not namespace, import or charset because namespace can't follow
925 // anything else
926 return (css::Rule::CHARSET_RULE == type || css::Rule::IMPORT_RULE == type);
929 void
930 CSSStyleSheetInner::RebuildNameSpaces()
932 // Just nuke our existing namespace map, if any
933 if (NS_SUCCEEDED(CreateNamespaceMap())) {
934 mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap);
938 nsresult
939 CSSStyleSheetInner::CreateNamespaceMap()
941 mNameSpaceMap = nsXMLNameSpaceMap::Create(false);
942 NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
943 // Override the default namespace map behavior for the null prefix to
944 // return the wildcard namespace instead of the null namespace.
945 mNameSpaceMap->AddPrefix(nullptr, kNameSpaceID_Unknown);
946 return NS_OK;
949 size_t
950 CSSStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
952 size_t n = aMallocSizeOf(this);
953 n += mOrderedRules.SizeOfExcludingThis(css::Rule::SizeOfCOMArrayElementIncludingThis,
954 aMallocSizeOf);
955 n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0;
957 // Measurement of the following members may be added later if DMD finds it is
958 // worthwhile:
959 // - mSheetURI
960 // - mOriginalSheetURI
961 // - mBaseURI
962 // - mPrincipal
963 // - mNameSpaceMap
965 // The following members are not measured:
966 // - mSheets, because it's non-owning
968 return n;
971 // -------------------------------
972 // CSS Style Sheet
975 CSSStyleSheet::CSSStyleSheet(CORSMode aCORSMode)
976 : mTitle(),
977 mParent(nullptr),
978 mOwnerRule(nullptr),
979 mDocument(nullptr),
980 mOwningNode(nullptr),
981 mDisabled(false),
982 mDirty(false),
983 mScopeElement(nullptr),
984 mRuleProcessors(nullptr)
986 mInner = new CSSStyleSheetInner(this, aCORSMode);
988 SetIsDOMBinding();
991 CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
992 CSSStyleSheet* aParentToUse,
993 css::ImportRule* aOwnerRuleToUse,
994 nsIDocument* aDocumentToUse,
995 nsINode* aOwningNodeToUse)
996 : mTitle(aCopy.mTitle),
997 mParent(aParentToUse),
998 mOwnerRule(aOwnerRuleToUse),
999 mDocument(aDocumentToUse),
1000 mOwningNode(aOwningNodeToUse),
1001 mDisabled(aCopy.mDisabled),
1002 mDirty(aCopy.mDirty),
1003 mScopeElement(nullptr),
1004 mInner(aCopy.mInner),
1005 mRuleProcessors(nullptr)
1008 mInner->AddSheet(this);
1010 if (mDirty) { // CSSOM's been there, force full copy now
1011 NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
1012 // FIXME: handle failure?
1013 EnsureUniqueInner();
1016 if (aCopy.mMedia) {
1017 // XXX This is wrong; we should be keeping @import rules and
1018 // sheets in sync!
1019 mMedia = aCopy.mMedia->Clone();
1022 SetIsDOMBinding();
1025 CSSStyleSheet::~CSSStyleSheet()
1027 for (CSSStyleSheet* child = mInner->mFirstChild;
1028 child;
1029 child = child->mNext) {
1030 // XXXbz this is a little bogus; see the XXX comment where we
1031 // declare mFirstChild.
1032 if (child->mParent == this) {
1033 child->mParent = nullptr;
1034 child->mDocument = nullptr;
1037 DropRuleCollection();
1038 DropMedia();
1039 mInner->RemoveSheet(this);
1040 // XXX The document reference is not reference counted and should
1041 // not be released. The document will let us know when it is going
1042 // away.
1043 if (mRuleProcessors) {
1044 NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference");
1045 delete mRuleProcessors; // weak refs, should be empty here anyway
1049 void
1050 CSSStyleSheet::DropRuleCollection()
1052 if (mRuleCollection) {
1053 mRuleCollection->DropReference();
1054 mRuleCollection = nullptr;
1058 void
1059 CSSStyleSheet::DropMedia()
1061 if (mMedia) {
1062 mMedia->SetStyleSheet(nullptr);
1063 mMedia = nullptr;
1067 void
1068 CSSStyleSheet::UnlinkInner()
1070 // We can only have a cycle through our inner if we have a unique inner,
1071 // because otherwise there are no JS wrappers for anything in the inner.
1072 if (mInner->mSheets.Length() != 1) {
1073 return;
1076 mInner->mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr);
1077 mInner->mOrderedRules.Clear();
1079 // Have to be a bit careful with child sheets, because we want to
1080 // drop their mNext pointers and null out their mParent and
1081 // mDocument, but don't want to work with deleted objects. And we
1082 // don't want to do any addrefing in the process, just to make sure
1083 // we don't confuse the cycle collector (though on the face of it,
1084 // addref/release pairs during unlink should probably be ok).
1085 nsRefPtr<CSSStyleSheet> child;
1086 child.swap(mInner->mFirstChild);
1087 while (child) {
1088 MOZ_ASSERT(child->mParent == this, "We have a unique inner!");
1089 child->mParent = nullptr;
1090 child->mDocument = nullptr;
1091 nsRefPtr<CSSStyleSheet> next;
1092 // Null out child->mNext, but don't let it die yet
1093 next.swap(child->mNext);
1094 // Switch to looking at the old value of child->mNext next iteration
1095 child.swap(next);
1096 // "next" is now our previous value of child; it'll get released
1097 // as we loop around.
1101 void
1102 CSSStyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb)
1104 // We can only have a cycle through our inner if we have a unique inner,
1105 // because otherwise there are no JS wrappers for anything in the inner.
1106 if (mInner->mSheets.Length() != 1) {
1107 return;
1110 nsRefPtr<CSSStyleSheet>* childSheetSlot = &mInner->mFirstChild;
1111 while (*childSheetSlot) {
1112 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
1113 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIStyleSheet*, childSheetSlot->get()));
1114 childSheetSlot = &(*childSheetSlot)->mNext;
1117 const nsCOMArray<css::Rule>& rules = mInner->mOrderedRules;
1118 for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
1119 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]");
1120 cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule());
1124 // QueryInterface implementation for CSSStyleSheet
1125 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSStyleSheet)
1126 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1127 NS_INTERFACE_MAP_ENTRY(nsIStyleSheet)
1128 NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
1129 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
1130 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
1131 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleSheet)
1132 if (aIID.Equals(NS_GET_IID(CSSStyleSheet)))
1133 foundInterface = reinterpret_cast<nsISupports*>(this);
1134 else
1135 NS_INTERFACE_MAP_END
1138 NS_IMPL_CYCLE_COLLECTING_ADDREF(CSSStyleSheet)
1139 NS_IMPL_CYCLE_COLLECTING_RELEASE(CSSStyleSheet)
1141 NS_IMPL_CYCLE_COLLECTION_CLASS(CSSStyleSheet)
1143 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSStyleSheet)
1144 tmp->DropMedia();
1145 // We do not unlink mNext; our parent will handle that. If we
1146 // unlinked it here, our parent would not be able to walk its list
1147 // of child sheets and null out the back-references to it, if we got
1148 // unlinked before it does.
1149 tmp->DropRuleCollection();
1150 tmp->UnlinkInner();
1151 tmp->mScopeElement = nullptr;
1152 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1153 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CSSStyleSheet)
1155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
1156 // We do not traverse mNext; our parent will handle that. See
1157 // comments in Unlink for why.
1158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection)
1159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement)
1160 tmp->TraverseInner(cb);
1161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1162 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1163 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CSSStyleSheet)
1165 nsresult
1166 CSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor)
1168 if (! mRuleProcessors) {
1169 mRuleProcessors = new nsAutoTArray<nsCSSRuleProcessor*, 8>();
1170 if (!mRuleProcessors)
1171 return NS_ERROR_OUT_OF_MEMORY;
1173 NS_ASSERTION(mRuleProcessors->NoIndex == mRuleProcessors->IndexOf(aProcessor),
1174 "processor already registered");
1175 mRuleProcessors->AppendElement(aProcessor); // weak ref
1176 return NS_OK;
1179 nsresult
1180 CSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor)
1182 if (!mRuleProcessors)
1183 return NS_ERROR_FAILURE;
1184 return mRuleProcessors->RemoveElement(aProcessor)
1185 ? NS_OK
1186 : NS_ERROR_FAILURE;
1190 void
1191 CSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
1192 nsIURI* aBaseURI)
1194 NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
1196 NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete,
1197 "Can't call SetURL on sheets that are complete or have rules");
1199 mInner->mSheetURI = aSheetURI;
1200 mInner->mOriginalSheetURI = aOriginalSheetURI;
1201 mInner->mBaseURI = aBaseURI;
1204 void
1205 CSSStyleSheet::SetPrincipal(nsIPrincipal* aPrincipal)
1207 NS_PRECONDITION(!mInner->mPrincipalSet,
1208 "Should have an inner whose principal has not yet been set");
1209 if (aPrincipal) {
1210 mInner->mPrincipal = aPrincipal;
1211 #ifdef DEBUG
1212 mInner->mPrincipalSet = true;
1213 #endif
1217 /* virtual */ nsIURI*
1218 CSSStyleSheet::GetSheetURI() const
1220 return mInner->mSheetURI;
1223 /* virtual */ nsIURI*
1224 CSSStyleSheet::GetBaseURI() const
1226 return mInner->mBaseURI;
1229 /* virtual */ void
1230 CSSStyleSheet::GetType(nsString& aType) const
1232 aType.AssignLiteral("text/css");
1235 bool
1236 CSSStyleSheet::UseForPresentation(nsPresContext* aPresContext,
1237 nsMediaQueryResultCacheKey& aKey) const
1239 if (mMedia) {
1240 return mMedia->Matches(aPresContext, &aKey);
1242 return true;
1246 void
1247 CSSStyleSheet::SetMedia(nsMediaList* aMedia)
1249 mMedia = aMedia;
1252 /* virtual */ bool
1253 CSSStyleSheet::HasRules() const
1255 return StyleRuleCount() != 0;
1258 /* virtual */ bool
1259 CSSStyleSheet::IsApplicable() const
1261 return !mDisabled && mInner->mComplete;
1264 /* virtual */ void
1265 CSSStyleSheet::SetEnabled(bool aEnabled)
1267 // Internal method, so callers must handle BeginUpdate/EndUpdate
1268 bool oldDisabled = mDisabled;
1269 mDisabled = !aEnabled;
1271 if (mInner->mComplete && oldDisabled != mDisabled) {
1272 ClearRuleCascades();
1274 if (mDocument) {
1275 mDocument->SetStyleSheetApplicableState(this, !mDisabled);
1280 /* virtual */ bool
1281 CSSStyleSheet::IsComplete() const
1283 return mInner->mComplete;
1286 /* virtual */ void
1287 CSSStyleSheet::SetComplete()
1289 NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
1290 mInner->mComplete = true;
1291 if (mDocument && !mDisabled) {
1292 // Let the document know
1293 mDocument->BeginUpdate(UPDATE_STYLE);
1294 mDocument->SetStyleSheetApplicableState(this, true);
1295 mDocument->EndUpdate(UPDATE_STYLE);
1298 if (mOwningNode && !mDisabled &&
1299 mOwningNode->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
1300 mOwningNode->IsContent()) {
1301 ShadowRoot* shadowRoot = mOwningNode->AsContent()->GetContainingShadow();
1302 shadowRoot->StyleSheetChanged();
1306 /* virtual */ nsIStyleSheet*
1307 CSSStyleSheet::GetParentSheet() const
1309 return mParent;
1312 /* virtual */ nsIDocument*
1313 CSSStyleSheet::GetOwningDocument() const
1315 return mDocument;
1318 /* virtual */ void
1319 CSSStyleSheet::SetOwningDocument(nsIDocument* aDocument)
1320 { // not ref counted
1321 mDocument = aDocument;
1322 // Now set the same document on all our child sheets....
1323 // XXXbz this is a little bogus; see the XXX comment where we
1324 // declare mFirstChild.
1325 for (CSSStyleSheet* child = mInner->mFirstChild;
1326 child; child = child->mNext) {
1327 if (child->mParent == this) {
1328 child->SetOwningDocument(aDocument);
1333 uint64_t
1334 CSSStyleSheet::FindOwningWindowInnerID() const
1336 uint64_t windowID = 0;
1337 if (mDocument) {
1338 windowID = mDocument->InnerWindowID();
1341 if (windowID == 0 && mOwningNode) {
1342 windowID = mOwningNode->OwnerDoc()->InnerWindowID();
1345 if (windowID == 0 && mOwnerRule) {
1346 nsCOMPtr<nsIStyleSheet> sheet = static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet();
1347 if (sheet) {
1348 nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(sheet);
1349 if (cssSheet) {
1350 windowID = cssSheet->FindOwningWindowInnerID();
1355 if (windowID == 0 && mParent) {
1356 windowID = mParent->FindOwningWindowInnerID();
1359 return windowID;
1362 void
1363 CSSStyleSheet::AppendStyleSheet(CSSStyleSheet* aSheet)
1365 NS_PRECONDITION(nullptr != aSheet, "null arg");
1367 WillDirty();
1368 nsRefPtr<CSSStyleSheet>* tail = &mInner->mFirstChild;
1369 while (*tail) {
1370 tail = &(*tail)->mNext;
1372 *tail = aSheet;
1374 // This is not reference counted. Our parent tells us when
1375 // it's going away.
1376 aSheet->mParent = this;
1377 aSheet->mDocument = mDocument;
1378 DidDirty();
1381 void
1382 CSSStyleSheet::InsertStyleSheetAt(CSSStyleSheet* aSheet, int32_t aIndex)
1384 NS_PRECONDITION(nullptr != aSheet, "null arg");
1386 WillDirty();
1387 nsRefPtr<CSSStyleSheet>* tail = &mInner->mFirstChild;
1388 while (*tail && aIndex) {
1389 --aIndex;
1390 tail = &(*tail)->mNext;
1392 aSheet->mNext = *tail;
1393 *tail = aSheet;
1395 // This is not reference counted. Our parent tells us when
1396 // it's going away.
1397 aSheet->mParent = this;
1398 aSheet->mDocument = mDocument;
1399 DidDirty();
1402 void
1403 CSSStyleSheet::PrependStyleRule(css::Rule* aRule)
1405 NS_PRECONDITION(nullptr != aRule, "null arg");
1407 WillDirty();
1408 mInner->mOrderedRules.InsertObjectAt(aRule, 0);
1409 aRule->SetStyleSheet(this);
1410 DidDirty();
1412 if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
1413 // no api to prepend a namespace (ugh), release old ones and re-create them all
1414 mInner->RebuildNameSpaces();
1418 void
1419 CSSStyleSheet::AppendStyleRule(css::Rule* aRule)
1421 NS_PRECONDITION(nullptr != aRule, "null arg");
1423 WillDirty();
1424 mInner->mOrderedRules.AppendObject(aRule);
1425 aRule->SetStyleSheet(this);
1426 DidDirty();
1428 if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
1429 #ifdef DEBUG
1430 nsresult rv =
1431 #endif
1432 RegisterNamespaceRule(aRule);
1433 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1434 "RegisterNamespaceRule returned error");
1438 void
1439 CSSStyleSheet::ReplaceStyleRule(css::Rule* aOld, css::Rule* aNew)
1441 NS_PRECONDITION(mInner->mOrderedRules.Count() != 0, "can't have old rule");
1442 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
1444 WillDirty();
1445 int32_t index = mInner->mOrderedRules.IndexOf(aOld);
1446 if (MOZ_UNLIKELY(index == -1)) {
1447 NS_NOTREACHED("Couldn't find old rule");
1448 return;
1450 mInner->mOrderedRules.ReplaceObjectAt(aNew, index);
1452 aNew->SetStyleSheet(this);
1453 aOld->SetStyleSheet(nullptr);
1454 DidDirty();
1455 NS_ASSERTION(css::Rule::NAMESPACE_RULE != aNew->GetType(), "not yet implemented");
1456 NS_ASSERTION(css::Rule::NAMESPACE_RULE != aOld->GetType(), "not yet implemented");
1459 int32_t
1460 CSSStyleSheet::StyleRuleCount() const
1462 return mInner->mOrderedRules.Count();
1465 css::Rule*
1466 CSSStyleSheet::GetStyleRuleAt(int32_t aIndex) const
1468 // Important: If this function is ever made scriptable, we must add
1469 // a security check here. See GetCssRules below for an example.
1470 return mInner->mOrderedRules.SafeObjectAt(aIndex);
1473 int32_t
1474 CSSStyleSheet::StyleSheetCount() const
1476 // XXX Far from an ideal way to do this, but the hope is that
1477 // it won't be done too often. If it is, we might want to
1478 // consider storing the children in an array.
1479 int32_t count = 0;
1481 const CSSStyleSheet* child = mInner->mFirstChild;
1482 while (child) {
1483 count++;
1484 child = child->mNext;
1487 return count;
1490 CSSStyleSheet::EnsureUniqueInnerResult
1491 CSSStyleSheet::EnsureUniqueInner()
1493 mDirty = true;
1495 NS_ABORT_IF_FALSE(mInner->mSheets.Length() != 0,
1496 "unexpected number of outers");
1497 if (mInner->mSheets.Length() == 1) {
1498 return eUniqueInner_AlreadyUnique;
1500 CSSStyleSheetInner* clone = mInner->CloneFor(this);
1501 MOZ_ASSERT(clone);
1502 mInner->RemoveSheet(this);
1503 mInner = clone;
1505 // otherwise the rule processor has pointers to the old rules
1506 ClearRuleCascades();
1508 return eUniqueInner_ClonedInner;
1511 void
1512 CSSStyleSheet::AppendAllChildSheets(nsTArray<CSSStyleSheet*>& aArray)
1514 for (CSSStyleSheet* child = mInner->mFirstChild; child;
1515 child = child->mNext) {
1516 aArray.AppendElement(child);
1520 already_AddRefed<CSSStyleSheet>
1521 CSSStyleSheet::Clone(CSSStyleSheet* aCloneParent,
1522 css::ImportRule* aCloneOwnerRule,
1523 nsIDocument* aCloneDocument,
1524 nsINode* aCloneOwningNode) const
1526 nsRefPtr<CSSStyleSheet> clone = new CSSStyleSheet(*this,
1527 aCloneParent,
1528 aCloneOwnerRule,
1529 aCloneDocument,
1530 aCloneOwningNode);
1531 return clone.forget();
1534 #ifdef DEBUG
1535 static void
1536 ListRules(const nsCOMArray<css::Rule>& aRules, FILE* aOut, int32_t aIndent)
1538 for (int32_t index = aRules.Count() - 1; index >= 0; --index) {
1539 aRules.ObjectAt(index)->List(aOut, aIndent);
1543 struct ListEnumData {
1544 ListEnumData(FILE* aOut, int32_t aIndent)
1545 : mOut(aOut),
1546 mIndent(aIndent)
1549 FILE* mOut;
1550 int32_t mIndent;
1553 /* virtual */ void
1554 CSSStyleSheet::List(FILE* out, int32_t aIndent) const
1557 int32_t index;
1559 // Indent
1560 for (index = aIndent; --index >= 0; ) fputs(" ", out);
1562 fputs("CSS Style Sheet: ", out);
1563 nsAutoCString urlSpec;
1564 nsresult rv = mInner->mSheetURI->GetSpec(urlSpec);
1565 if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
1566 fputs(urlSpec.get(), out);
1569 if (mMedia) {
1570 fputs(" media: ", out);
1571 nsAutoString buffer;
1572 mMedia->GetText(buffer);
1573 fputs(NS_ConvertUTF16toUTF8(buffer).get(), out);
1575 fputs("\n", out);
1577 for (const CSSStyleSheet* child = mInner->mFirstChild;
1578 child;
1579 child = child->mNext) {
1580 child->List(out, aIndent + 1);
1583 fputs("Rules in source order:\n", out);
1584 ListRules(mInner->mOrderedRules, out, aIndent);
1586 #endif
1588 void
1589 CSSStyleSheet::ClearRuleCascades()
1591 if (mRuleProcessors) {
1592 nsCSSRuleProcessor **iter = mRuleProcessors->Elements(),
1593 **end = iter + mRuleProcessors->Length();
1594 for(; iter != end; ++iter) {
1595 (*iter)->ClearRuleCascades();
1598 if (mParent) {
1599 CSSStyleSheet* parent = (CSSStyleSheet*)mParent;
1600 parent->ClearRuleCascades();
1604 void
1605 CSSStyleSheet::WillDirty()
1607 if (mInner->mComplete) {
1608 EnsureUniqueInner();
1612 void
1613 CSSStyleSheet::DidDirty()
1615 NS_ABORT_IF_FALSE(!mInner->mComplete || mDirty,
1616 "caller must have called WillDirty()");
1617 ClearRuleCascades();
1620 nsresult
1621 CSSStyleSheet::SubjectSubsumesInnerPrincipal()
1623 nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
1624 if (subjectPrincipal->Subsumes(mInner->mPrincipal)) {
1625 return NS_OK;
1628 // Allow access only if CORS mode is not NONE
1629 if (GetCORSMode() == CORS_NONE) {
1630 return NS_ERROR_DOM_SECURITY_ERR;
1633 // Now make sure we set the principal of our inner to the
1634 // subjectPrincipal. That means we need a unique inner, of
1635 // course. But we don't want to do that if we're not complete
1636 // yet. Luckily, all the callers of this method throw anyway if
1637 // not complete, so we can just do that here too.
1638 if (!mInner->mComplete) {
1639 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1642 WillDirty();
1644 mInner->mPrincipal = subjectPrincipal;
1646 DidDirty();
1648 return NS_OK;
1651 nsresult
1652 CSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule)
1654 if (!mInner->mNameSpaceMap) {
1655 nsresult rv = mInner->CreateNamespaceMap();
1656 NS_ENSURE_SUCCESS(rv, rv);
1659 AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap);
1660 return NS_OK;
1663 // nsIDOMStyleSheet interface
1664 NS_IMETHODIMP
1665 CSSStyleSheet::GetType(nsAString& aType)
1667 aType.AssignLiteral("text/css");
1668 return NS_OK;
1671 NS_IMETHODIMP
1672 CSSStyleSheet::GetDisabled(bool* aDisabled)
1674 *aDisabled = Disabled();
1675 return NS_OK;
1678 NS_IMETHODIMP
1679 CSSStyleSheet::SetDisabled(bool aDisabled)
1681 // DOM method, so handle BeginUpdate/EndUpdate
1682 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
1683 CSSStyleSheet::SetEnabled(!aDisabled);
1684 return NS_OK;
1687 NS_IMETHODIMP
1688 CSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
1690 nsCOMPtr<nsIDOMNode> ownerNode = do_QueryInterface(GetOwnerNode());
1691 ownerNode.forget(aOwnerNode);
1692 return NS_OK;
1695 NS_IMETHODIMP
1696 CSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
1698 NS_ENSURE_ARG_POINTER(aParentStyleSheet);
1700 NS_IF_ADDREF(*aParentStyleSheet = mParent);
1702 return NS_OK;
1705 NS_IMETHODIMP
1706 CSSStyleSheet::GetHref(nsAString& aHref)
1708 if (mInner->mOriginalSheetURI) {
1709 nsAutoCString str;
1710 mInner->mOriginalSheetURI->GetSpec(str);
1711 CopyUTF8toUTF16(str, aHref);
1712 } else {
1713 SetDOMStringToNull(aHref);
1716 return NS_OK;
1719 /* virtual */ void
1720 CSSStyleSheet::GetTitle(nsString& aTitle) const
1722 aTitle = mTitle;
1725 NS_IMETHODIMP
1726 CSSStyleSheet::GetTitle(nsAString& aTitle)
1728 aTitle.Assign(mTitle);
1729 return NS_OK;
1732 NS_IMETHODIMP
1733 CSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia)
1735 NS_ADDREF(*aMedia = Media());
1736 return NS_OK;
1739 nsMediaList*
1740 CSSStyleSheet::Media()
1742 if (!mMedia) {
1743 mMedia = new nsMediaList();
1744 mMedia->SetStyleSheet(this);
1747 return mMedia;
1750 NS_IMETHODIMP
1751 CSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
1753 NS_IF_ADDREF(*aOwnerRule = GetOwnerRule());
1754 return NS_OK;
1757 nsIDOMCSSRule*
1758 CSSStyleSheet::GetDOMOwnerRule() const
1760 return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr;
1763 NS_IMETHODIMP
1764 CSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
1766 ErrorResult rv;
1767 nsCOMPtr<nsIDOMCSSRuleList> rules = GetCssRules(rv);
1768 rules.forget(aCssRules);
1769 return rv.ErrorCode();
1772 CSSRuleList*
1773 CSSStyleSheet::GetCssRules(ErrorResult& aRv)
1775 // No doing this on incomplete sheets!
1776 if (!mInner->mComplete) {
1777 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
1778 return nullptr;
1781 //-- Security check: Only scripts whose principal subsumes that of the
1782 // style sheet can access rule collections.
1783 nsresult rv = SubjectSubsumesInnerPrincipal();
1784 if (NS_FAILED(rv)) {
1785 aRv.Throw(rv);
1786 return nullptr;
1789 // OK, security check passed, so get the rule collection
1790 if (!mRuleCollection) {
1791 mRuleCollection = new CSSRuleListImpl(this);
1794 return mRuleCollection;
1797 NS_IMETHODIMP
1798 CSSStyleSheet::InsertRule(const nsAString& aRule,
1799 uint32_t aIndex,
1800 uint32_t* aReturn)
1802 //-- Security check: Only scripts whose principal subsumes that of the
1803 // style sheet can modify rule collections.
1804 nsresult rv = SubjectSubsumesInnerPrincipal();
1805 NS_ENSURE_SUCCESS(rv, rv);
1807 return InsertRuleInternal(aRule, aIndex, aReturn);
1810 static bool
1811 RuleHasPendingChildSheet(css::Rule *cssRule)
1813 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
1814 NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
1815 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
1816 importRule->GetStyleSheet(getter_AddRefs(childSheet));
1817 nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(childSheet);
1818 return cssSheet != nullptr && !cssSheet->IsComplete();
1821 nsresult
1822 CSSStyleSheet::InsertRuleInternal(const nsAString& aRule,
1823 uint32_t aIndex,
1824 uint32_t* aReturn)
1826 // No doing this if the sheet is not complete!
1827 if (!mInner->mComplete) {
1828 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1831 WillDirty();
1833 if (aIndex > uint32_t(mInner->mOrderedRules.Count()))
1834 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1836 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX,
1837 "Too many style rules!");
1839 // Hold strong ref to the CSSLoader in case the document update
1840 // kills the document
1841 nsRefPtr<css::Loader> loader;
1842 if (mDocument) {
1843 loader = mDocument->CSSLoader();
1844 NS_ASSERTION(loader, "Document with no CSS loader!");
1847 nsCSSParser css(loader, this);
1849 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1851 nsRefPtr<css::Rule> rule;
1852 nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
1853 mInner->mPrincipal, getter_AddRefs(rule));
1854 if (NS_FAILED(result))
1855 return result;
1857 // Hierarchy checking.
1858 int32_t newType = rule->GetType();
1860 // check that we're not inserting before a charset rule
1861 css::Rule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
1862 if (nextRule) {
1863 int32_t nextType = nextRule->GetType();
1864 if (nextType == css::Rule::CHARSET_RULE) {
1865 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1868 if (nextType == css::Rule::IMPORT_RULE &&
1869 newType != css::Rule::CHARSET_RULE &&
1870 newType != css::Rule::IMPORT_RULE) {
1871 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1874 if (nextType == css::Rule::NAMESPACE_RULE &&
1875 newType != css::Rule::CHARSET_RULE &&
1876 newType != css::Rule::IMPORT_RULE &&
1877 newType != css::Rule::NAMESPACE_RULE) {
1878 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1882 if (aIndex != 0) {
1883 // no inserting charset at nonzero position
1884 if (newType == css::Rule::CHARSET_RULE) {
1885 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1888 css::Rule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1);
1889 int32_t prevType = prevRule->GetType();
1891 if (newType == css::Rule::IMPORT_RULE &&
1892 prevType != css::Rule::CHARSET_RULE &&
1893 prevType != css::Rule::IMPORT_RULE) {
1894 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1897 if (newType == css::Rule::NAMESPACE_RULE &&
1898 prevType != css::Rule::CHARSET_RULE &&
1899 prevType != css::Rule::IMPORT_RULE &&
1900 prevType != css::Rule::NAMESPACE_RULE) {
1901 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1905 bool insertResult = mInner->mOrderedRules.InsertObjectAt(rule, aIndex);
1906 NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY);
1907 DidDirty();
1909 rule->SetStyleSheet(this);
1911 int32_t type = rule->GetType();
1912 if (type == css::Rule::NAMESPACE_RULE) {
1913 // XXXbz does this screw up when inserting a namespace rule before
1914 // another namespace rule that binds the same prefix to a different
1915 // namespace?
1916 result = RegisterNamespaceRule(rule);
1917 NS_ENSURE_SUCCESS(result, result);
1920 // We don't notify immediately for @import rules, but rather when
1921 // the sheet the rule is importing is loaded (see StyleSheetLoaded)
1922 if ((type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) &&
1923 mDocument) {
1924 mDocument->StyleRuleAdded(this, rule);
1927 *aReturn = aIndex;
1928 return NS_OK;
1931 NS_IMETHODIMP
1932 CSSStyleSheet::DeleteRule(uint32_t aIndex)
1934 // No doing this if the sheet is not complete!
1935 if (!mInner->mComplete) {
1936 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1939 //-- Security check: Only scripts whose principal subsumes that of the
1940 // style sheet can modify rule collections.
1941 nsresult rv = SubjectSubsumesInnerPrincipal();
1942 NS_ENSURE_SUCCESS(rv, rv);
1944 // XXX TBI: handle @rule types
1945 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1947 WillDirty();
1949 if (aIndex >= uint32_t(mInner->mOrderedRules.Count()))
1950 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1952 NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX,
1953 "Too many style rules!");
1955 // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
1956 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(aIndex);
1957 if (rule) {
1958 mInner->mOrderedRules.RemoveObjectAt(aIndex);
1959 if (mDocument && mDocument->StyleSheetChangeEventsEnabled()) {
1960 // Force creation of the DOM rule, so that it can be put on the
1961 // StyleRuleRemoved event object.
1962 rule->GetDOMRule();
1964 rule->SetStyleSheet(nullptr);
1965 DidDirty();
1967 if (mDocument) {
1968 mDocument->StyleRuleRemoved(this, rule);
1972 return NS_OK;
1975 nsresult
1976 CSSStyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex)
1978 NS_ENSURE_ARG_POINTER(aGroup);
1979 NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!");
1980 nsRefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
1981 NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
1983 // check that the rule actually belongs to this sheet!
1984 if (this != rule->GetStyleSheet()) {
1985 return NS_ERROR_INVALID_ARG;
1988 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1990 WillDirty();
1992 nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
1993 NS_ENSURE_SUCCESS(result, result);
1995 rule->SetStyleSheet(nullptr);
1997 DidDirty();
1999 if (mDocument) {
2000 mDocument->StyleRuleRemoved(this, rule);
2003 return NS_OK;
2006 nsresult
2007 CSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule,
2008 css::GroupRule* aGroup,
2009 uint32_t aIndex,
2010 uint32_t* _retval)
2012 NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!");
2013 // check that the group actually belongs to this sheet!
2014 if (this != aGroup->GetStyleSheet()) {
2015 return NS_ERROR_INVALID_ARG;
2018 // Hold strong ref to the CSSLoader in case the document update
2019 // kills the document
2020 nsRefPtr<css::Loader> loader;
2021 if (mDocument) {
2022 loader = mDocument->CSSLoader();
2023 NS_ASSERTION(loader, "Document with no CSS loader!");
2026 nsCSSParser css(loader, this);
2028 // parse and grab the rule
2029 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2031 WillDirty();
2033 nsRefPtr<css::Rule> rule;
2034 nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
2035 mInner->mPrincipal, getter_AddRefs(rule));
2036 if (NS_FAILED(result))
2037 return result;
2039 switch (rule->GetType()) {
2040 case css::Rule::STYLE_RULE:
2041 case css::Rule::MEDIA_RULE:
2042 case css::Rule::FONT_FACE_RULE:
2043 case css::Rule::PAGE_RULE:
2044 case css::Rule::KEYFRAMES_RULE:
2045 case css::Rule::COUNTER_STYLE_RULE:
2046 case css::Rule::DOCUMENT_RULE:
2047 case css::Rule::SUPPORTS_RULE:
2048 // these types are OK to insert into a group
2049 break;
2050 case css::Rule::CHARSET_RULE:
2051 case css::Rule::IMPORT_RULE:
2052 case css::Rule::NAMESPACE_RULE:
2053 // these aren't
2054 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2055 default:
2056 NS_NOTREACHED("unexpected rule type");
2057 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2060 result = aGroup->InsertStyleRuleAt(aIndex, rule);
2061 NS_ENSURE_SUCCESS(result, result);
2062 DidDirty();
2064 if (mDocument) {
2065 mDocument->StyleRuleAdded(this, rule);
2068 *_retval = aIndex;
2069 return NS_OK;
2072 nsresult
2073 CSSStyleSheet::ReplaceRuleInGroup(css::GroupRule* aGroup,
2074 css::Rule* aOld, css::Rule* aNew)
2076 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
2077 NS_ASSERTION(this == aGroup->GetStyleSheet(), "group doesn't belong to this sheet");
2079 WillDirty();
2081 nsresult result = aGroup->ReplaceStyleRule(aOld, aNew);
2082 DidDirty();
2083 return result;
2086 // nsICSSLoaderObserver implementation
2087 NS_IMETHODIMP
2088 CSSStyleSheet::StyleSheetLoaded(CSSStyleSheet* aSheet,
2089 bool aWasAlternate,
2090 nsresult aStatus)
2092 if (aSheet->GetParentSheet() == nullptr) {
2093 return NS_OK; // ignore if sheet has been detached already (see parseSheet)
2095 NS_ASSERTION(this == aSheet->GetParentSheet(),
2096 "We are being notified of a sheet load for a sheet that is not our child!");
2098 if (mDocument && NS_SUCCEEDED(aStatus)) {
2099 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2101 // XXXldb @import rules shouldn't even implement nsIStyleRule (but
2102 // they do)!
2103 mDocument->StyleRuleAdded(this, aSheet->GetOwnerRule());
2106 return NS_OK;
2109 nsresult
2110 CSSStyleSheet::ParseSheet(const nsAString& aInput)
2112 // Not doing this if the sheet is not complete!
2113 if (!mInner->mComplete) {
2114 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2117 // Hold strong ref to the CSSLoader in case the document update
2118 // kills the document
2119 nsRefPtr<css::Loader> loader;
2120 if (mDocument) {
2121 loader = mDocument->CSSLoader();
2122 NS_ASSERTION(loader, "Document with no CSS loader!");
2123 } else {
2124 loader = new css::Loader();
2127 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2129 WillDirty();
2131 // detach existing rules (including child sheets via import rules)
2132 int ruleCount;
2133 while ((ruleCount = mInner->mOrderedRules.Count()) != 0) {
2134 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(ruleCount - 1);
2135 mInner->mOrderedRules.RemoveObjectAt(ruleCount - 1);
2136 rule->SetStyleSheet(nullptr);
2137 if (mDocument) {
2138 mDocument->StyleRuleRemoved(this, rule);
2142 // nuke child sheets list and current namespace map
2143 for (CSSStyleSheet* child = mInner->mFirstChild; child; child = child->mNext) {
2144 NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!");
2145 child->mParent = nullptr;
2146 child->mDocument = nullptr;
2148 mInner->mFirstChild = nullptr;
2149 mInner->mNameSpaceMap = nullptr;
2151 // allow unsafe rules if the style sheet's principal is the system principal
2152 bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal);
2154 nsCSSParser parser(loader, this);
2155 nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI,
2156 mInner->mPrincipal, 1, allowUnsafeRules);
2157 DidDirty(); // we are always 'dirty' here since we always remove rules first
2158 NS_ENSURE_SUCCESS(rv, rv);
2160 // notify document of all new rules
2161 if (mDocument) {
2162 for (int32_t index = 0; index < mInner->mOrderedRules.Count(); ++index) {
2163 nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(index);
2164 if (rule->GetType() == css::Rule::IMPORT_RULE &&
2165 RuleHasPendingChildSheet(rule)) {
2166 continue; // notify when loaded (see StyleSheetLoaded)
2168 mDocument->StyleRuleAdded(this, rule);
2171 return NS_OK;
2174 /* virtual */ nsIURI*
2175 CSSStyleSheet::GetOriginalURI() const
2177 return mInner->mOriginalSheetURI;
2180 /* virtual */
2181 JSObject*
2182 CSSStyleSheet::WrapObject(JSContext* aCx)
2184 return CSSStyleSheetBinding::Wrap(aCx, this);
2187 } // namespace mozilla