Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / style / nsCSSStyleSheet.cpp
blob80b699c10d3e8b9eb55c561de9533ebc4aebaa12
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * L. David Baron <dbaron@dbaron.org>
25 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Daniel Glazman <glazman@netscape.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 /* representation of a CSS style sheet */
44 #include "nsCSSStyleSheet.h"
46 #include "nsCRT.h"
47 #include "nsIAtom.h"
48 #include "nsIServiceManager.h"
49 #include "nsCSSRuleProcessor.h"
50 #include "nsICSSStyleRule.h"
51 #include "nsICSSNameSpaceRule.h"
52 #include "nsICSSGroupRule.h"
53 #include "nsICSSImportRule.h"
54 #include "nsIMediaList.h"
55 #include "nsIDocument.h"
56 #include "nsPresContext.h"
57 #include "nsGkAtoms.h"
58 #include "nsString.h"
59 #include "nsVoidArray.h"
60 #include "nsIDOMStyleSheetList.h"
61 #include "nsIDOMCSSStyleSheet.h"
62 #include "nsIDOMCSSRule.h"
63 #include "nsIDOMCSSImportRule.h"
64 #include "nsICSSRuleList.h"
65 #include "nsIDOMMediaList.h"
66 #include "nsIDOMNode.h"
67 #include "nsDOMError.h"
68 #include "nsICSSParser.h"
69 #include "nsICSSLoader.h"
70 #include "nsICSSLoaderObserver.h"
71 #include "nsINameSpaceManager.h"
72 #include "nsXMLNameSpaceMap.h"
73 #include "nsCOMPtr.h"
74 #include "nsContentUtils.h"
75 #include "nsIJSContextStack.h"
76 #include "nsIScriptSecurityManager.h"
77 #include "mozAutoDocUpdate.h"
78 #include "nsCSSDeclaration.h"
79 #include "nsRuleNode.h"
81 // -------------------------------
82 // Style Rule List for the DOM
84 class CSSRuleListImpl : public nsICSSRuleList
86 public:
87 CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet);
89 NS_DECL_ISUPPORTS
91 // nsIDOMCSSRuleList interface
92 NS_IMETHOD GetLength(PRUint32* aLength);
93 NS_IMETHOD Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn);
95 virtual nsIDOMCSSRule* GetItemAt(PRUint32 aIndex, nsresult* aResult);
97 void DropReference() { mStyleSheet = nsnull; }
99 protected:
100 virtual ~CSSRuleListImpl();
102 nsCSSStyleSheet* mStyleSheet;
103 public:
104 PRBool mRulesAccessed;
107 CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet)
109 // Not reference counted to avoid circular references.
110 // The style sheet will tell us when its going away.
111 mStyleSheet = aStyleSheet;
112 mRulesAccessed = PR_FALSE;
115 CSSRuleListImpl::~CSSRuleListImpl()
119 // QueryInterface implementation for CSSRuleList
120 NS_INTERFACE_MAP_BEGIN(CSSRuleListImpl)
121 NS_INTERFACE_MAP_ENTRY(nsICSSRuleList)
122 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList)
123 NS_INTERFACE_MAP_ENTRY(nsISupports)
124 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSRuleList)
125 NS_INTERFACE_MAP_END
128 NS_IMPL_ADDREF(CSSRuleListImpl)
129 NS_IMPL_RELEASE(CSSRuleListImpl)
132 NS_IMETHODIMP
133 CSSRuleListImpl::GetLength(PRUint32* aLength)
135 if (nsnull != mStyleSheet) {
136 PRInt32 count;
137 mStyleSheet->StyleRuleCount(count);
138 *aLength = (PRUint32)count;
140 else {
141 *aLength = 0;
144 return NS_OK;
147 nsIDOMCSSRule*
148 CSSRuleListImpl::GetItemAt(PRUint32 aIndex, nsresult* aResult)
150 nsresult result = NS_OK;
152 if (mStyleSheet) {
153 result = mStyleSheet->EnsureUniqueInner(); // needed to ensure rules have correct parent
154 if (NS_SUCCEEDED(result)) {
155 nsCOMPtr<nsICSSRule> rule;
157 result = mStyleSheet->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
158 if (rule) {
159 mRulesAccessed = PR_TRUE; // signal to never share rules again
160 return rule->GetDOMRuleWeak(aResult);
162 if (result == NS_ERROR_ILLEGAL_VALUE) {
163 result = NS_OK; // per spec: "Return Value ... null if ... not a valid index."
168 *aResult = result;
169 return nsnull;
172 NS_IMETHODIMP
173 CSSRuleListImpl::Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn)
175 nsresult rv;
176 nsIDOMCSSRule* rule = GetItemAt(aIndex, &rv);
177 if (!rule) {
178 *aReturn = nsnull;
180 return rv;
183 return CallQueryInterface(rule, aReturn);
186 template <class Numeric>
187 PRInt32 DoCompare(Numeric a, Numeric b)
189 if (a == b)
190 return 0;
191 if (a < b)
192 return -1;
193 return 1;
196 PRBool
197 nsMediaExpression::Matches(nsPresContext *aPresContext,
198 const nsCSSValue& aActualValue) const
200 const nsCSSValue& actual = aActualValue;
201 const nsCSSValue& required = mValue;
203 // If we don't have the feature, the match fails.
204 if (actual.GetUnit() == eCSSUnit_Null) {
205 return PR_FALSE;
208 // If the expression had no value to match, the match succeeds,
209 // unless the value is an integer 0 or a zero length.
210 if (required.GetUnit() == eCSSUnit_Null) {
211 if (actual.GetUnit() == eCSSUnit_Integer)
212 return actual.GetIntValue() != 0;
213 if (actual.IsLengthUnit())
214 return actual.GetFloatValue() != 0;
215 return PR_TRUE;
218 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxAllowed ||
219 mRange == nsMediaExpression::eEqual, "yikes");
220 PRInt32 cmp; // -1 (actual < required)
221 // 0 (actual == required)
222 // 1 (actual > required)
223 switch (mFeature->mValueType) {
224 case nsMediaFeature::eLength:
226 NS_ASSERTION(actual.IsLengthUnit(), "bad actual value");
227 NS_ASSERTION(required.IsLengthUnit(), "bad required value");
228 nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont(
229 aPresContext, actual);
230 nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont(
231 aPresContext, required);
232 cmp = DoCompare(actualCoord, requiredCoord);
234 break;
235 case nsMediaFeature::eInteger:
236 case nsMediaFeature::eBoolInteger:
238 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Integer,
239 "bad actual value");
240 NS_ASSERTION(required.GetUnit() == eCSSUnit_Integer,
241 "bad required value");
242 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
243 actual.GetIntValue() == 0 || actual.GetIntValue() == 1,
244 "bad actual bool integer value");
245 NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
246 required.GetIntValue() == 0 || required.GetIntValue() == 1,
247 "bad required bool integer value");
248 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
250 break;
251 case nsMediaFeature::eIntRatio:
253 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array &&
254 actual.GetArrayValue()->Count() == 2 &&
255 actual.GetArrayValue()->Item(0).GetUnit() ==
256 eCSSUnit_Integer &&
257 actual.GetArrayValue()->Item(1).GetUnit() ==
258 eCSSUnit_Integer,
259 "bad actual value");
260 NS_ASSERTION(required.GetUnit() == eCSSUnit_Array &&
261 required.GetArrayValue()->Count() == 2 &&
262 required.GetArrayValue()->Item(0).GetUnit() ==
263 eCSSUnit_Integer &&
264 required.GetArrayValue()->Item(1).GetUnit() ==
265 eCSSUnit_Integer,
266 "bad required value");
267 // Convert to PRInt64 so we can multiply without worry. Note
268 // that while the spec requires that both halves of |required|
269 // be positive, the numerator or denominator of |actual| might
270 // be zero (e.g., when testing 'aspect-ratio' on a 0-width or
271 // 0-height iframe).
272 PRInt64 actualNum = actual.GetArrayValue()->Item(0).GetIntValue(),
273 actualDen = actual.GetArrayValue()->Item(1).GetIntValue(),
274 requiredNum = required.GetArrayValue()->Item(0).GetIntValue(),
275 requiredDen = required.GetArrayValue()->Item(1).GetIntValue();
276 cmp = DoCompare(actualNum * requiredDen, requiredNum * actualDen);
278 break;
279 case nsMediaFeature::eResolution:
281 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch ||
282 actual.GetUnit() == eCSSUnit_Centimeter,
283 "bad actual value");
284 NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch ||
285 required.GetUnit() == eCSSUnit_Centimeter,
286 "bad required value");
287 float actualDPI = actual.GetFloatValue();
288 if (actual.GetUnit() == eCSSUnit_Centimeter)
289 actualDPI = actualDPI * 2.54f;
290 float requiredDPI = required.GetFloatValue();
291 if (required.GetUnit() == eCSSUnit_Centimeter)
292 requiredDPI = requiredDPI * 2.54f;
293 cmp = DoCompare(actualDPI, requiredDPI);
295 break;
296 case nsMediaFeature::eEnumerated:
298 NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated,
299 "bad actual value");
300 NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated,
301 "bad required value");
302 NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed,
303 "bad range"); // we asserted above about mRange
304 // We don't really need DoCompare, but it doesn't hurt (and
305 // maybe the compiler will condense this case with eInteger).
306 cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
308 break;
310 switch (mRange) {
311 case nsMediaExpression::eMin:
312 return cmp != -1;
313 case nsMediaExpression::eMax:
314 return cmp != 1;
315 case nsMediaExpression::eEqual:
316 return cmp == 0;
318 NS_NOTREACHED("unexpected mRange");
319 return PR_FALSE;
322 void
323 nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression* aExpression,
324 PRBool aExpressionMatches)
326 const nsMediaFeature *feature = aExpression->mFeature;
327 FeatureEntry *entry = nsnull;
328 for (PRUint32 i = 0; i < mFeatureCache.Length(); ++i) {
329 if (mFeatureCache[i].mFeature == feature) {
330 entry = &mFeatureCache[i];
331 break;
334 if (!entry) {
335 entry = mFeatureCache.AppendElement();
336 if (!entry) {
337 return; /* out of memory */
339 entry->mFeature = feature;
342 ExpressionEntry eentry = { *aExpression, aExpressionMatches };
343 entry->mExpressions.AppendElement(eentry);
346 PRBool
347 nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const
349 if (aPresContext->Medium() != mMedium) {
350 return PR_FALSE;
353 for (PRUint32 i = 0; i < mFeatureCache.Length(); ++i) {
354 const FeatureEntry *entry = &mFeatureCache[i];
355 nsCSSValue actual;
356 nsresult rv = (entry->mFeature->mGetter)(aPresContext, actual);
357 NS_ENSURE_SUCCESS(rv, PR_FALSE); // any better ideas?
359 for (PRUint32 j = 0; j < entry->mExpressions.Length(); ++j) {
360 const ExpressionEntry &eentry = entry->mExpressions[j];
361 if (eentry.mExpression.Matches(aPresContext, actual) !=
362 eentry.mExpressionMatches) {
363 return PR_FALSE;
368 return PR_TRUE;
371 void
372 nsMediaQuery::AppendToString(nsAString& aString) const
374 nsAutoString buffer;
376 if (mHadUnknownExpression) {
377 aString.AppendLiteral("not all");
378 return;
381 NS_ASSERTION(!mNegated || !mHasOnly, "can't have not and only");
382 NS_ASSERTION(!mTypeOmitted || (!mNegated && !mHasOnly),
383 "can't have not or only when type is omitted");
384 if (!mTypeOmitted) {
385 if (mNegated) {
386 aString.AppendLiteral("not ");
387 } else if (mHasOnly) {
388 aString.AppendLiteral("only ");
390 mMediaType->ToString(buffer);
391 aString.Append(buffer);
392 buffer.Truncate();
395 for (PRUint32 i = 0, i_end = mExpressions.Length(); i < i_end; ++i) {
396 if (i > 0 || !mTypeOmitted)
397 aString.AppendLiteral(" and ");
398 aString.AppendLiteral("(");
400 const nsMediaExpression &expr = mExpressions[i];
401 if (expr.mRange == nsMediaExpression::eMin) {
402 aString.AppendLiteral("min-");
403 } else if (expr.mRange == nsMediaExpression::eMax) {
404 aString.AppendLiteral("max-");
407 const nsMediaFeature *feature = expr.mFeature;
408 (*feature->mName)->ToString(buffer);
409 aString.Append(buffer);
410 buffer.Truncate();
412 if (expr.mValue.GetUnit() != eCSSUnit_Null) {
413 aString.AppendLiteral(": ");
414 switch (feature->mValueType) {
415 case nsMediaFeature::eLength:
416 NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit");
417 // Use 'width' as a property that takes length values
418 // written in the normal way.
419 nsCSSDeclaration::AppendCSSValueToString(eCSSProperty_width,
420 expr.mValue, aString);
421 break;
422 case nsMediaFeature::eInteger:
423 case nsMediaFeature::eBoolInteger:
424 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer,
425 "bad unit");
426 // Use 'z-index' as a property that takes integer values
427 // written without anything extra.
428 nsCSSDeclaration::AppendCSSValueToString(eCSSProperty_z_index,
429 expr.mValue, aString);
430 break;
431 case nsMediaFeature::eIntRatio:
433 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array,
434 "bad unit");
435 nsCSSValue::Array *array = expr.mValue.GetArrayValue();
436 NS_ASSERTION(array->Count() == 2, "unexpected length");
437 NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
438 "bad unit");
439 NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer,
440 "bad unit");
441 nsCSSDeclaration::AppendCSSValueToString(eCSSProperty_z_index,
442 array->Item(0), aString);
443 aString.AppendLiteral("/");
444 nsCSSDeclaration::AppendCSSValueToString(eCSSProperty_z_index,
445 array->Item(1), aString);
447 break;
448 case nsMediaFeature::eResolution:
449 buffer.AppendFloat(expr.mValue.GetFloatValue());
450 aString.Append(buffer);
451 buffer.Truncate();
452 if (expr.mValue.GetUnit() == eCSSUnit_Inch) {
453 aString.AppendLiteral("dpi");
454 } else {
455 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter,
456 "bad unit");
457 aString.AppendLiteral("dpcm");
459 break;
460 case nsMediaFeature::eEnumerated:
461 NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Enumerated,
462 "bad unit");
463 AppendASCIItoUTF16(
464 nsCSSProps::ValueToKeyword(expr.mValue.GetIntValue(),
465 feature->mKeywordTable),
466 aString);
467 break;
471 aString.AppendLiteral(")");
475 nsMediaQuery*
476 nsMediaQuery::Clone() const
478 nsAutoPtr<nsMediaQuery> result(new nsMediaQuery(*this));
479 NS_ENSURE_TRUE(result &&
480 result->mExpressions.Length() == mExpressions.Length(),
481 nsnull);
482 return result.forget();
485 PRBool
486 nsMediaQuery::Matches(nsPresContext* aPresContext,
487 nsMediaQueryResultCacheKey& aKey) const
489 if (mHadUnknownExpression)
490 return PR_FALSE;
492 PRBool match =
493 mMediaType == aPresContext->Medium() || mMediaType == nsGkAtoms::all;
494 for (PRUint32 i = 0, i_end = mExpressions.Length(); match && i < i_end; ++i) {
495 const nsMediaExpression &expr = mExpressions[i];
496 nsCSSValue actual;
497 nsresult rv = (expr.mFeature->mGetter)(aPresContext, actual);
498 NS_ENSURE_SUCCESS(rv, PR_FALSE); // any better ideas?
500 match = expr.Matches(aPresContext, actual);
501 aKey.AddExpression(&expr, match);
504 return match == !mNegated;
507 NS_INTERFACE_MAP_BEGIN(nsMediaList)
508 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList)
509 NS_INTERFACE_MAP_ENTRY(nsISupports)
510 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(MediaList)
511 NS_INTERFACE_MAP_END
513 NS_IMPL_ADDREF(nsMediaList)
514 NS_IMPL_RELEASE(nsMediaList)
517 nsMediaList::nsMediaList()
518 : mIsEmpty(PR_TRUE)
519 , mStyleSheet(nsnull)
523 nsMediaList::~nsMediaList()
527 nsresult
528 nsMediaList::GetText(nsAString& aMediaText)
530 aMediaText.Truncate();
532 if (mArray.Length() == 0 && !mIsEmpty) {
533 aMediaText.AppendLiteral("not all");
536 for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
537 nsMediaQuery* query = mArray[i];
538 NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
540 query->AppendToString(aMediaText);
542 if (i + 1 < i_end) {
543 aMediaText.AppendLiteral(", ");
547 return NS_OK;
550 // XXXbz this is so ill-defined in the spec, it's not clear quite what
551 // it should be doing....
552 nsresult
553 nsMediaList::SetText(const nsAString& aMediaText)
555 nsCOMPtr<nsICSSParser> parser;
556 nsresult rv = NS_NewCSSParser(getter_AddRefs(parser));
557 NS_ENSURE_SUCCESS(rv, rv);
559 PRBool htmlMode = PR_FALSE;
560 nsCOMPtr<nsIDOMStyleSheet> domSheet =
561 do_QueryInterface(static_cast<nsICSSStyleSheet*>(mStyleSheet));
562 if (domSheet) {
563 nsCOMPtr<nsIDOMNode> node;
564 domSheet->GetOwnerNode(getter_AddRefs(node));
565 htmlMode = !!node;
568 return parser->ParseMediaList(nsString(aMediaText), nsnull, 0,
569 this, htmlMode);
572 PRBool
573 nsMediaList::Matches(nsPresContext* aPresContext,
574 nsMediaQueryResultCacheKey& aKey)
576 for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
577 if (mArray[i]->Matches(aPresContext, aKey)) {
578 return PR_TRUE;
581 return mIsEmpty;
584 nsresult
585 nsMediaList::SetStyleSheet(nsICSSStyleSheet *aSheet)
587 NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
588 "multiple style sheets competing for one media list");
589 mStyleSheet = static_cast<nsCSSStyleSheet*>(aSheet);
590 return NS_OK;
593 nsresult
594 nsMediaList::Clone(nsMediaList** aResult)
596 nsRefPtr<nsMediaList> result = new nsMediaList();
597 if (!result || !result->mArray.AppendElements(mArray.Length()))
598 return NS_ERROR_OUT_OF_MEMORY;
599 for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
600 if (!(result->mArray[i] = mArray[i]->Clone())) {
601 return NS_ERROR_OUT_OF_MEMORY;
604 NS_ADDREF(*aResult = result);
605 return NS_OK;
608 NS_IMETHODIMP
609 nsMediaList::GetMediaText(nsAString& aMediaText)
611 return GetText(aMediaText);
614 // "sheet" should be an nsCSSStyleSheet and "doc" should be an
615 // nsCOMPtr<nsIDocument>
616 #define BEGIN_MEDIA_CHANGE(sheet, doc) \
617 if (sheet) { \
618 rv = sheet->GetOwningDocument(*getter_AddRefs(doc)); \
619 NS_ENSURE_SUCCESS(rv, rv); \
621 mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, PR_TRUE); \
622 if (sheet) { \
623 rv = sheet->WillDirty(); \
624 NS_ENSURE_SUCCESS(rv, rv); \
627 #define END_MEDIA_CHANGE(sheet, doc) \
628 if (sheet) { \
629 sheet->DidDirty(); \
631 /* XXXldb Pass something meaningful? */ \
632 if (doc) { \
633 doc->StyleRuleChanged(sheet, nsnull, nsnull); \
637 NS_IMETHODIMP
638 nsMediaList::SetMediaText(const nsAString& aMediaText)
640 nsresult rv = NS_OK;
641 nsCOMPtr<nsIDocument> doc;
643 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
645 rv = SetText(aMediaText);
646 if (NS_FAILED(rv))
647 return rv;
649 END_MEDIA_CHANGE(mStyleSheet, doc)
651 return rv;
654 NS_IMETHODIMP
655 nsMediaList::GetLength(PRUint32* aLength)
657 NS_ENSURE_ARG_POINTER(aLength);
659 *aLength = mArray.Length();
660 return NS_OK;
663 NS_IMETHODIMP
664 nsMediaList::Item(PRUint32 aIndex, nsAString& aReturn)
666 PRInt32 index = aIndex;
667 if (0 <= index && index < Count()) {
668 nsMediaQuery* query = mArray[index];
669 NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
671 aReturn.Truncate();
672 query->AppendToString(aReturn);
673 } else {
674 SetDOMStringToNull(aReturn);
677 return NS_OK;
680 NS_IMETHODIMP
681 nsMediaList::DeleteMedium(const nsAString& aOldMedium)
683 nsresult rv = NS_OK;
684 nsCOMPtr<nsIDocument> doc;
686 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
688 rv = Delete(aOldMedium);
689 if (NS_FAILED(rv))
690 return rv;
692 END_MEDIA_CHANGE(mStyleSheet, doc)
694 return rv;
697 NS_IMETHODIMP
698 nsMediaList::AppendMedium(const nsAString& aNewMedium)
700 nsresult rv = NS_OK;
701 nsCOMPtr<nsIDocument> doc;
703 BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
705 rv = Append(aNewMedium);
706 if (NS_FAILED(rv))
707 return rv;
709 END_MEDIA_CHANGE(mStyleSheet, doc)
711 return rv;
714 nsresult
715 nsMediaList::Delete(const nsAString& aOldMedium)
717 if (aOldMedium.IsEmpty())
718 return NS_ERROR_DOM_NOT_FOUND_ERR;
720 for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
721 nsMediaQuery* query = mArray[i];
722 NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
724 nsAutoString buf;
725 query->AppendToString(buf);
726 if (buf == aOldMedium) {
727 mArray.RemoveElementAt(i);
728 return NS_OK;
732 return NS_ERROR_DOM_NOT_FOUND_ERR;
735 nsresult
736 nsMediaList::Append(const nsAString& aNewMedium)
738 if (aNewMedium.IsEmpty())
739 return NS_ERROR_DOM_NOT_FOUND_ERR;
741 Delete(aNewMedium);
743 nsresult rv = NS_OK;
744 nsTArray<nsAutoPtr<nsMediaQuery> > buf;
745 #ifdef DEBUG
746 PRBool ok =
747 #endif
748 mArray.SwapElements(buf);
749 NS_ASSERTION(ok, "SwapElements should never fail when neither array "
750 "is an auto array");
751 SetText(aNewMedium);
752 if (mArray.Length() == 1) {
753 nsMediaQuery *query = mArray[0].forget();
754 if (!buf.AppendElement(query)) {
755 delete query;
756 rv = NS_ERROR_OUT_OF_MEMORY;
759 #ifdef DEBUG
760 ok =
761 #endif
762 mArray.SwapElements(buf);
763 NS_ASSERTION(ok, "SwapElements should never fail when neither array "
764 "is an auto array");
765 return rv;
768 // -------------------------------
769 // CSS Style Sheet Inner Data Container
773 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsICSSStyleSheet* aPrimarySheet)
774 : mSheets(),
775 mComplete(PR_FALSE)
776 #ifdef DEBUG
777 , mPrincipalSet(PR_FALSE)
778 #endif
780 MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
781 mSheets.AppendElement(aPrimarySheet);
783 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
786 static PRBool SetStyleSheetReference(nsICSSRule* aRule, void* aSheet)
788 if (aRule) {
789 aRule->SetStyleSheet((nsICSSStyleSheet*)aSheet);
791 return PR_TRUE;
794 static PRBool
795 CloneRuleInto(nsICSSRule* aRule, void* aArray)
797 nsICSSRule* clone = nsnull;
798 aRule->Clone(clone);
799 if (clone) {
800 static_cast<nsCOMArray<nsICSSRule>*>(aArray)->AppendObject(clone);
801 NS_RELEASE(clone);
803 return PR_TRUE;
806 struct ChildSheetListBuilder {
807 nsRefPtr<nsCSSStyleSheet>* sheetSlot;
808 nsCSSStyleSheet* parent;
810 void SetParentLinks(nsCSSStyleSheet* aSheet) {
811 aSheet->mParent = parent;
812 aSheet->SetOwningDocument(parent->mDocument);
816 static PRBool
817 RebuildChildList(nsICSSRule* aRule, void* aBuilder)
819 PRInt32 type;
820 aRule->GetType(type);
821 if (type == nsICSSRule::CHARSET_RULE) {
822 return PR_TRUE;
825 if (type == nsICSSRule::NAMESPACE_RULE || type == nsICSSRule::MEDIA_RULE ||
826 type == nsICSSRule::STYLE_RULE) {
827 return PR_FALSE;
830 ChildSheetListBuilder* builder =
831 static_cast<ChildSheetListBuilder*>(aBuilder);
833 // XXXbz We really need to decomtaminate all this stuff. Is there a reason
834 // that I can't just QI to nsICSSImportRule and get an nsCSSStyleSheet
835 // directly from it?
836 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(aRule));
837 NS_ASSERTION(importRule, "GetType lied");
839 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
840 importRule->GetStyleSheet(getter_AddRefs(childSheet));
842 // Have to do this QI to be safe, since XPConnect can fake
843 // nsIDOMCSSStyleSheets
844 nsCOMPtr<nsICSSStyleSheet> cssSheet = do_QueryInterface(childSheet);
845 if (!cssSheet) {
846 return PR_TRUE;
849 (*builder->sheetSlot) = static_cast<nsCSSStyleSheet*>(cssSheet.get());
850 builder->SetParentLinks(*builder->sheetSlot);
851 return PR_TRUE;
854 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy,
855 nsCSSStyleSheet* aPrimarySheet)
856 : mSheets(),
857 mSheetURI(aCopy.mSheetURI),
858 mOriginalSheetURI(aCopy.mOriginalSheetURI),
859 mBaseURI(aCopy.mBaseURI),
860 mPrincipal(aCopy.mPrincipal),
861 mComplete(aCopy.mComplete)
862 #ifdef DEBUG
863 , mPrincipalSet(aCopy.mPrincipalSet)
864 #endif
866 MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
867 mSheets.AppendElement(aPrimarySheet);
868 aCopy.mOrderedRules.EnumerateForwards(CloneRuleInto, &mOrderedRules);
869 mOrderedRules.EnumerateForwards(SetStyleSheetReference, aPrimarySheet);
871 ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet };
872 mOrderedRules.EnumerateForwards(RebuildChildList, &builder);
874 RebuildNameSpaces();
877 nsCSSStyleSheetInner::~nsCSSStyleSheetInner()
879 MOZ_COUNT_DTOR(nsCSSStyleSheetInner);
880 mOrderedRules.EnumerateForwards(SetStyleSheetReference, nsnull);
883 nsCSSStyleSheetInner*
884 nsCSSStyleSheetInner::CloneFor(nsCSSStyleSheet* aPrimarySheet)
886 return new nsCSSStyleSheetInner(*this, aPrimarySheet);
889 void
890 nsCSSStyleSheetInner::AddSheet(nsICSSStyleSheet* aSheet)
892 mSheets.AppendElement(aSheet);
895 void
896 nsCSSStyleSheetInner::RemoveSheet(nsICSSStyleSheet* aSheet)
898 if (1 == mSheets.Count()) {
899 NS_ASSERTION(aSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0), "bad parent");
900 delete this;
901 return;
903 if (aSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0)) {
904 mSheets.RemoveElementAt(0);
905 NS_ASSERTION(mSheets.Count(), "no parents");
906 mOrderedRules.EnumerateForwards(SetStyleSheetReference,
907 (nsICSSStyleSheet*)mSheets.ElementAt(0));
909 else {
910 mSheets.RemoveElement(aSheet);
914 static void
915 AddNamespaceRuleToMap(nsICSSRule* aRule, nsXMLNameSpaceMap* aMap)
917 #ifdef DEBUG
918 PRInt32 type;
919 aRule->GetType(type);
920 NS_ASSERTION(type == nsICSSRule::NAMESPACE_RULE, "Bogus rule type");
921 #endif
923 nsCOMPtr<nsICSSNameSpaceRule> nameSpaceRule = do_QueryInterface(aRule);
925 nsCOMPtr<nsIAtom> prefix;
926 nsAutoString urlSpec;
927 nameSpaceRule->GetPrefix(*getter_AddRefs(prefix));
928 nameSpaceRule->GetURLSpec(urlSpec);
930 aMap->AddPrefix(prefix, urlSpec);
933 static PRBool
934 CreateNameSpace(nsICSSRule* aRule, void* aNameSpacePtr)
936 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
937 aRule->GetType(type);
938 if (nsICSSRule::NAMESPACE_RULE == type) {
939 AddNamespaceRuleToMap(aRule,
940 static_cast<nsXMLNameSpaceMap*>(aNameSpacePtr));
941 return PR_TRUE;
943 // stop if not namespace, import or charset because namespace can't follow
944 // anything else
945 return (nsICSSRule::CHARSET_RULE == type || nsICSSRule::IMPORT_RULE == type);
948 void
949 nsCSSStyleSheetInner::RebuildNameSpaces()
951 // Just nuke our existing namespace map, if any
952 if (NS_SUCCEEDED(CreateNamespaceMap())) {
953 mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap);
957 nsresult
958 nsCSSStyleSheetInner::CreateNamespaceMap()
960 mNameSpaceMap = nsXMLNameSpaceMap::Create();
961 NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
962 // Override the default namespace map behavior for the null prefix to
963 // return the wildcard namespace instead of the null namespace.
964 mNameSpaceMap->AddPrefix(nsnull, kNameSpaceID_Unknown);
965 return NS_OK;
968 // -------------------------------
969 // CSS Style Sheet
972 nsCSSStyleSheet::nsCSSStyleSheet()
973 : nsICSSStyleSheet(),
974 mRefCnt(0),
975 mTitle(),
976 mMedia(nsnull),
977 mParent(nsnull),
978 mOwnerRule(nsnull),
979 mRuleCollection(nsnull),
980 mDocument(nsnull),
981 mOwningNode(nsnull),
982 mDisabled(PR_FALSE),
983 mDirty(PR_FALSE),
984 mRuleProcessors(nsnull)
987 mInner = new nsCSSStyleSheetInner(this);
990 nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet& aCopy,
991 nsICSSStyleSheet* aParentToUse,
992 nsICSSImportRule* aOwnerRuleToUse,
993 nsIDocument* aDocumentToUse,
994 nsIDOMNode* aOwningNodeToUse)
995 : nsICSSStyleSheet(),
996 mRefCnt(0),
997 mTitle(aCopy.mTitle),
998 mMedia(nsnull),
999 mParent(aParentToUse),
1000 mOwnerRule(aOwnerRuleToUse),
1001 mRuleCollection(nsnull), // re-created lazily
1002 mDocument(aDocumentToUse),
1003 mOwningNode(aOwningNodeToUse),
1004 mDisabled(aCopy.mDisabled),
1005 mDirty(PR_FALSE),
1006 mInner(aCopy.mInner),
1007 mRuleProcessors(nsnull)
1010 mInner->AddSheet(this);
1012 if (aCopy.mRuleCollection &&
1013 aCopy.mRuleCollection->mRulesAccessed) { // CSSOM's been there, force full copy now
1014 NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
1015 EnsureUniqueInner();
1018 if (aCopy.mMedia) {
1019 // XXX This is wrong; we should be keeping @import rules and
1020 // sheets in sync!
1021 aCopy.mMedia->Clone(getter_AddRefs(mMedia));
1025 nsCSSStyleSheet::~nsCSSStyleSheet()
1027 for (nsCSSStyleSheet* 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 = nsnull;
1034 child->mDocument = nsnull;
1037 if (nsnull != mRuleCollection) {
1038 mRuleCollection->DropReference();
1039 NS_RELEASE(mRuleCollection);
1041 if (mMedia) {
1042 mMedia->SetStyleSheet(nsnull);
1043 mMedia = nsnull;
1045 mInner->RemoveSheet(this);
1046 // XXX The document reference is not reference counted and should
1047 // not be released. The document will let us know when it is going
1048 // away.
1049 if (mRuleProcessors) {
1050 NS_ASSERTION(mRuleProcessors->Count() == 0, "destructing sheet with rule processor reference");
1051 delete mRuleProcessors; // weak refs, should be empty here anyway
1056 // QueryInterface implementation for nsCSSStyleSheet
1057 NS_INTERFACE_MAP_BEGIN(nsCSSStyleSheet)
1058 NS_INTERFACE_MAP_ENTRY(nsICSSStyleSheet)
1059 NS_INTERFACE_MAP_ENTRY(nsIStyleSheet)
1060 NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
1061 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
1062 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
1063 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICSSStyleSheet)
1064 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSStyleSheet)
1065 NS_INTERFACE_MAP_END
1068 NS_IMPL_ADDREF(nsCSSStyleSheet)
1069 NS_IMPL_RELEASE(nsCSSStyleSheet)
1072 NS_IMETHODIMP
1073 nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor)
1075 if (! mRuleProcessors) {
1076 mRuleProcessors = new nsAutoVoidArray();
1077 if (!mRuleProcessors)
1078 return NS_ERROR_OUT_OF_MEMORY;
1080 NS_ASSERTION(-1 == mRuleProcessors->IndexOf(aProcessor),
1081 "processor already registered");
1082 mRuleProcessors->AppendElement(aProcessor); // weak ref
1083 return NS_OK;
1086 NS_IMETHODIMP
1087 nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor)
1089 if (!mRuleProcessors)
1090 return NS_ERROR_FAILURE;
1091 return mRuleProcessors->RemoveElement(aProcessor)
1092 ? NS_OK
1093 : NS_ERROR_FAILURE;
1097 NS_IMETHODIMP
1098 nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
1099 nsIURI* aBaseURI)
1101 NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
1103 NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete,
1104 "Can't call SetURL on sheets that are complete or have rules");
1106 mInner->mSheetURI = aSheetURI;
1107 mInner->mOriginalSheetURI = aOriginalSheetURI;
1108 mInner->mBaseURI = aBaseURI;
1109 return NS_OK;
1112 void
1113 nsCSSStyleSheet::SetPrincipal(nsIPrincipal* aPrincipal)
1115 NS_PRECONDITION(!mInner->mPrincipalSet,
1116 "Should have an inner whose principal has not yet been set");
1117 if (aPrincipal) {
1118 mInner->mPrincipal = aPrincipal;
1119 #ifdef DEBUG
1120 mInner->mPrincipalSet = PR_TRUE;
1121 #endif
1125 nsIPrincipal*
1126 nsCSSStyleSheet::Principal() const
1128 return mInner->mPrincipal;
1131 NS_IMETHODIMP
1132 nsCSSStyleSheet::GetSheetURI(nsIURI** aSheetURI) const
1134 NS_IF_ADDREF(*aSheetURI = mInner->mSheetURI.get());
1135 return NS_OK;
1138 NS_IMETHODIMP
1139 nsCSSStyleSheet::GetBaseURI(nsIURI** aBaseURI) const
1141 NS_IF_ADDREF(*aBaseURI = mInner->mBaseURI.get());
1142 return NS_OK;
1145 NS_IMETHODIMP
1146 nsCSSStyleSheet::SetTitle(const nsAString& aTitle)
1148 mTitle = aTitle;
1149 return NS_OK;
1152 NS_IMETHODIMP
1153 nsCSSStyleSheet::GetType(nsString& aType) const
1155 aType.AssignLiteral("text/css");
1156 return NS_OK;
1159 PRBool
1160 nsCSSStyleSheet::UseForPresentation(nsPresContext* aPresContext,
1161 nsMediaQueryResultCacheKey& aKey) const
1163 if (mMedia) {
1164 return mMedia->Matches(aPresContext, aKey);
1166 return PR_TRUE;
1170 NS_IMETHODIMP
1171 nsCSSStyleSheet::SetMedia(nsMediaList* aMedia)
1173 mMedia = aMedia;
1174 return NS_OK;
1177 NS_IMETHODIMP_(PRBool)
1178 nsCSSStyleSheet::HasRules() const
1180 PRInt32 count;
1181 StyleRuleCount(count);
1182 return count != 0;
1185 NS_IMETHODIMP
1186 nsCSSStyleSheet::GetApplicable(PRBool& aApplicable) const
1188 aApplicable = !mDisabled && mInner->mComplete;
1189 return NS_OK;
1192 NS_IMETHODIMP
1193 nsCSSStyleSheet::SetEnabled(PRBool aEnabled)
1195 // Internal method, so callers must handle BeginUpdate/EndUpdate
1196 PRBool oldDisabled = mDisabled;
1197 mDisabled = !aEnabled;
1199 if (mInner->mComplete && oldDisabled != mDisabled) {
1200 ClearRuleCascades();
1202 if (mDocument) {
1203 mDocument->SetStyleSheetApplicableState(this, !mDisabled);
1207 return NS_OK;
1210 NS_IMETHODIMP
1211 nsCSSStyleSheet::GetComplete(PRBool& aComplete) const
1213 aComplete = mInner->mComplete;
1214 return NS_OK;
1217 NS_IMETHODIMP
1218 nsCSSStyleSheet::SetComplete()
1220 NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
1221 mInner->mComplete = PR_TRUE;
1222 if (mDocument && !mDisabled) {
1223 // Let the document know
1224 mDocument->BeginUpdate(UPDATE_STYLE);
1225 mDocument->SetStyleSheetApplicableState(this, PR_TRUE);
1226 mDocument->EndUpdate(UPDATE_STYLE);
1228 return NS_OK;
1231 NS_IMETHODIMP
1232 nsCSSStyleSheet::GetParentSheet(nsIStyleSheet*& aParent) const
1234 aParent = mParent;
1235 NS_IF_ADDREF(aParent);
1236 return NS_OK;
1239 NS_IMETHODIMP
1240 nsCSSStyleSheet::GetOwningDocument(nsIDocument*& aDocument) const
1242 aDocument = mDocument;
1243 NS_IF_ADDREF(aDocument);
1244 return NS_OK;
1247 NS_IMETHODIMP
1248 nsCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument)
1249 { // not ref counted
1250 mDocument = aDocument;
1251 // Now set the same document on all our child sheets....
1252 // XXXbz this is a little bogus; see the XXX comment where we
1253 // declare mFirstChild.
1254 for (nsCSSStyleSheet* child = mInner->mFirstChild;
1255 child; child = child->mNext) {
1256 if (child->mParent == this) {
1257 child->SetOwningDocument(aDocument);
1260 return NS_OK;
1263 NS_IMETHODIMP
1264 nsCSSStyleSheet::SetOwningNode(nsIDOMNode* aOwningNode)
1265 { // not ref counted
1266 mOwningNode = aOwningNode;
1267 return NS_OK;
1270 NS_IMETHODIMP
1271 nsCSSStyleSheet::SetOwnerRule(nsICSSImportRule* aOwnerRule)
1272 { // not ref counted
1273 mOwnerRule = aOwnerRule;
1274 return NS_OK;
1277 NS_IMETHODIMP
1278 nsCSSStyleSheet::GetOwnerRule(nsICSSImportRule** aOwnerRule)
1280 *aOwnerRule = mOwnerRule;
1281 NS_IF_ADDREF(*aOwnerRule);
1282 return NS_OK;
1285 NS_IMETHODIMP
1286 nsCSSStyleSheet::AppendStyleSheet(nsICSSStyleSheet* aSheet)
1288 NS_PRECONDITION(nsnull != aSheet, "null arg");
1290 if (NS_SUCCEEDED(WillDirty())) {
1291 nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet;
1293 nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild;
1294 while (*tail) {
1295 tail = &(*tail)->mNext;
1297 *tail = sheet;
1299 // This is not reference counted. Our parent tells us when
1300 // it's going away.
1301 sheet->mParent = this;
1302 sheet->mDocument = mDocument;
1303 DidDirty();
1305 return NS_OK;
1308 NS_IMETHODIMP
1309 nsCSSStyleSheet::InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex)
1311 NS_PRECONDITION(nsnull != aSheet, "null arg");
1313 nsresult result = WillDirty();
1315 if (NS_SUCCEEDED(result)) {
1316 nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet;
1318 nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild;
1319 while (*tail && aIndex) {
1320 --aIndex;
1321 tail = &(*tail)->mNext;
1323 sheet->mNext = *tail;
1324 *tail = sheet;
1326 // This is not reference counted. Our parent tells us when
1327 // it's going away.
1328 sheet->mParent = this;
1329 sheet->mDocument = mDocument;
1330 DidDirty();
1332 return result;
1335 NS_IMETHODIMP
1336 nsCSSStyleSheet::PrependStyleRule(nsICSSRule* aRule)
1338 NS_PRECONDITION(nsnull != aRule, "null arg");
1340 if (NS_SUCCEEDED(WillDirty())) {
1341 mInner->mOrderedRules.InsertObjectAt(aRule, 0);
1342 aRule->SetStyleSheet(this);
1343 DidDirty();
1345 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
1346 aRule->GetType(type);
1347 if (nsICSSRule::NAMESPACE_RULE == type) {
1348 // no api to prepend a namespace (ugh), release old ones and re-create them all
1349 mInner->RebuildNameSpaces();
1352 return NS_OK;
1355 NS_IMETHODIMP
1356 nsCSSStyleSheet::AppendStyleRule(nsICSSRule* aRule)
1358 NS_PRECONDITION(nsnull != aRule, "null arg");
1360 if (NS_SUCCEEDED(WillDirty())) {
1361 mInner->mOrderedRules.AppendObject(aRule);
1362 aRule->SetStyleSheet(this);
1363 DidDirty();
1365 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
1366 aRule->GetType(type);
1367 if (nsICSSRule::NAMESPACE_RULE == type) {
1368 nsresult rv = RegisterNamespaceRule(aRule);
1369 NS_ENSURE_SUCCESS(rv, rv);
1372 return NS_OK;
1375 NS_IMETHODIMP
1376 nsCSSStyleSheet::ReplaceStyleRule(nsICSSRule* aOld, nsICSSRule* aNew)
1378 NS_PRECONDITION(mInner->mOrderedRules.Count() != 0, "can't have old rule");
1379 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
1381 if (NS_SUCCEEDED(WillDirty())) {
1382 PRInt32 index = mInner->mOrderedRules.IndexOf(aOld);
1383 NS_ENSURE_TRUE(index != -1, NS_ERROR_UNEXPECTED);
1384 mInner->mOrderedRules.ReplaceObjectAt(aNew, index);
1386 aNew->SetStyleSheet(this);
1387 aOld->SetStyleSheet(nsnull);
1388 DidDirty();
1389 #ifdef DEBUG
1390 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
1391 aNew->GetType(type);
1392 NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented");
1393 aOld->GetType(type);
1394 NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented");
1395 #endif
1397 return NS_OK;
1400 NS_IMETHODIMP
1401 nsCSSStyleSheet::StyleRuleCount(PRInt32& aCount) const
1403 aCount = mInner->mOrderedRules.Count();
1404 return NS_OK;
1407 NS_IMETHODIMP
1408 nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex, nsICSSRule*& aRule) const
1410 // Important: If this function is ever made scriptable, we must add
1411 // a security check here. See GetCssRules below for an example.
1412 aRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
1413 if (aRule) {
1414 NS_ADDREF(aRule);
1415 return NS_OK;
1418 return NS_ERROR_ILLEGAL_VALUE;
1421 nsXMLNameSpaceMap*
1422 nsCSSStyleSheet::GetNameSpaceMap() const
1424 return mInner->mNameSpaceMap;
1427 NS_IMETHODIMP
1428 nsCSSStyleSheet::StyleSheetCount(PRInt32& aCount) const
1430 // XXX Far from an ideal way to do this, but the hope is that
1431 // it won't be done too often. If it is, we might want to
1432 // consider storing the children in an array.
1433 aCount = 0;
1435 const nsCSSStyleSheet* child = mInner->mFirstChild;
1436 while (child) {
1437 aCount++;
1438 child = child->mNext;
1441 return NS_OK;
1444 NS_IMETHODIMP
1445 nsCSSStyleSheet::GetStyleSheetAt(PRInt32 aIndex, nsICSSStyleSheet*& aSheet) const
1447 // XXX Ughh...an O(n^2) method for doing iteration. Again, we hope
1448 // that this isn't done too often. If it is, we need to change the
1449 // underlying storage mechanism
1450 aSheet = nsnull;
1452 nsCSSStyleSheet* child = mInner->mFirstChild;
1453 while (child && (0 != aIndex)) {
1454 --aIndex;
1455 child = child->mNext;
1458 NS_IF_ADDREF(aSheet = child);
1460 return NS_OK;
1463 nsresult
1464 nsCSSStyleSheet::EnsureUniqueInner()
1466 if (1 < mInner->mSheets.Count()) {
1467 nsCSSStyleSheetInner* clone = mInner->CloneFor(this);
1468 if (clone) {
1469 mInner->RemoveSheet(this);
1470 mInner = clone;
1472 else {
1473 return NS_ERROR_OUT_OF_MEMORY;
1476 return NS_OK;
1479 NS_IMETHODIMP
1480 nsCSSStyleSheet::Clone(nsICSSStyleSheet* aCloneParent,
1481 nsICSSImportRule* aCloneOwnerRule,
1482 nsIDocument* aCloneDocument,
1483 nsIDOMNode* aCloneOwningNode,
1484 nsICSSStyleSheet** aClone) const
1486 NS_PRECONDITION(aClone, "Null out param!");
1487 nsCSSStyleSheet* clone = new nsCSSStyleSheet(*this,
1488 aCloneParent,
1489 aCloneOwnerRule,
1490 aCloneDocument,
1491 aCloneOwningNode);
1492 if (clone) {
1493 *aClone = static_cast<nsICSSStyleSheet*>(clone);
1494 NS_ADDREF(*aClone);
1496 return NS_OK;
1499 #ifdef DEBUG
1500 static void
1501 ListRules(const nsCOMArray<nsICSSRule>& aRules, FILE* aOut, PRInt32 aIndent)
1503 for (PRInt32 index = aRules.Count() - 1; index >= 0; --index) {
1504 aRules.ObjectAt(index)->List(aOut, aIndent);
1508 struct ListEnumData {
1509 ListEnumData(FILE* aOut, PRInt32 aIndent)
1510 : mOut(aOut),
1511 mIndent(aIndent)
1514 FILE* mOut;
1515 PRInt32 mIndent;
1518 void nsCSSStyleSheet::List(FILE* out, PRInt32 aIndent) const
1521 PRInt32 index;
1523 // Indent
1524 for (index = aIndent; --index >= 0; ) fputs(" ", out);
1526 fputs("CSS Style Sheet: ", out);
1527 nsCAutoString urlSpec;
1528 nsresult rv = mInner->mSheetURI->GetSpec(urlSpec);
1529 if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
1530 fputs(urlSpec.get(), out);
1533 if (mMedia) {
1534 fputs(" media: ", out);
1535 nsAutoString buffer;
1536 mMedia->GetText(buffer);
1537 fputs(NS_ConvertUTF16toUTF8(buffer).get(), out);
1539 fputs("\n", out);
1541 for (const nsCSSStyleSheet* child = mInner->mFirstChild;
1542 child;
1543 child = child->mNext) {
1544 child->List(out, aIndent + 1);
1547 fputs("Rules in source order:\n", out);
1548 ListRules(mInner->mOrderedRules, out, aIndent);
1550 #endif
1552 static PRBool
1553 EnumClearRuleCascades(void* aProcessor, void* aData)
1555 nsCSSRuleProcessor* processor =
1556 static_cast<nsCSSRuleProcessor*>(aProcessor);
1557 processor->ClearRuleCascades();
1558 return PR_TRUE;
1561 void
1562 nsCSSStyleSheet::ClearRuleCascades()
1564 if (mRuleProcessors) {
1565 mRuleProcessors->EnumerateForwards(EnumClearRuleCascades, nsnull);
1567 if (mParent) {
1568 nsCSSStyleSheet* parent = (nsCSSStyleSheet*)mParent;
1569 parent->ClearRuleCascades();
1573 nsresult
1574 nsCSSStyleSheet::WillDirty()
1576 if (!mInner->mComplete) {
1577 // Do nothing
1578 return NS_OK;
1581 return EnsureUniqueInner();
1584 void
1585 nsCSSStyleSheet::DidDirty()
1587 ClearRuleCascades();
1588 mDirty = PR_TRUE;
1591 nsresult
1592 nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() const
1594 // Get the security manager and do the subsumes check
1595 nsIScriptSecurityManager *securityManager =
1596 nsContentUtils::GetSecurityManager();
1598 nsCOMPtr<nsIPrincipal> subjectPrincipal;
1599 securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1601 if (!subjectPrincipal) {
1602 return NS_ERROR_DOM_SECURITY_ERR;
1605 PRBool subsumes;
1606 nsresult rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
1607 NS_ENSURE_SUCCESS(rv, rv);
1609 if (subsumes) {
1610 return NS_OK;
1613 if (!nsContentUtils::IsCallerTrustedForWrite()) {
1614 return NS_ERROR_DOM_SECURITY_ERR;
1617 return NS_OK;
1620 nsresult
1621 nsCSSStyleSheet::RegisterNamespaceRule(nsICSSRule* aRule)
1623 if (!mInner->mNameSpaceMap) {
1624 nsresult rv = mInner->CreateNamespaceMap();
1625 NS_ENSURE_SUCCESS(rv, rv);
1628 AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap);
1629 return NS_OK;
1632 NS_IMETHODIMP
1633 nsCSSStyleSheet::IsModified(PRBool* aSheetModified) const
1635 *aSheetModified = mDirty;
1636 return NS_OK;
1639 NS_IMETHODIMP
1640 nsCSSStyleSheet::SetModified(PRBool aModified)
1642 mDirty = aModified;
1643 return NS_OK;
1646 // nsIDOMStyleSheet interface
1647 NS_IMETHODIMP
1648 nsCSSStyleSheet::GetType(nsAString& aType)
1650 aType.AssignLiteral("text/css");
1651 return NS_OK;
1654 NS_IMETHODIMP
1655 nsCSSStyleSheet::GetDisabled(PRBool* aDisabled)
1657 *aDisabled = mDisabled;
1658 return NS_OK;
1661 NS_IMETHODIMP
1662 nsCSSStyleSheet::SetDisabled(PRBool aDisabled)
1664 // DOM method, so handle BeginUpdate/EndUpdate
1665 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, PR_TRUE);
1666 nsresult rv = nsCSSStyleSheet::SetEnabled(!aDisabled);
1667 return rv;
1670 NS_IMETHODIMP
1671 nsCSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
1673 *aOwnerNode = mOwningNode;
1674 NS_IF_ADDREF(*aOwnerNode);
1675 return NS_OK;
1678 NS_IMETHODIMP
1679 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
1681 NS_ENSURE_ARG_POINTER(aParentStyleSheet);
1683 nsresult rv = NS_OK;
1685 if (mParent) {
1686 rv = mParent->QueryInterface(NS_GET_IID(nsIDOMStyleSheet),
1687 (void **)aParentStyleSheet);
1688 } else {
1689 *aParentStyleSheet = nsnull;
1692 return rv;
1695 NS_IMETHODIMP
1696 nsCSSStyleSheet::GetHref(nsAString& aHref)
1698 if (mInner->mOriginalSheetURI) {
1699 nsCAutoString str;
1700 mInner->mOriginalSheetURI->GetSpec(str);
1701 CopyUTF8toUTF16(str, aHref);
1702 } else {
1703 SetDOMStringToNull(aHref);
1706 return NS_OK;
1709 NS_IMETHODIMP
1710 nsCSSStyleSheet::GetTitle(nsString& aTitle) const
1712 aTitle = mTitle;
1713 return NS_OK;
1716 NS_IMETHODIMP
1717 nsCSSStyleSheet::GetTitle(nsAString& aTitle)
1719 aTitle.Assign(mTitle);
1720 return NS_OK;
1723 NS_IMETHODIMP
1724 nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia)
1726 NS_ENSURE_ARG_POINTER(aMedia);
1727 *aMedia = nsnull;
1729 if (!mMedia) {
1730 mMedia = new nsMediaList();
1731 NS_ENSURE_TRUE(mMedia, NS_ERROR_OUT_OF_MEMORY);
1732 mMedia->SetStyleSheet(this);
1735 *aMedia = mMedia;
1736 NS_ADDREF(*aMedia);
1738 return NS_OK;
1741 NS_IMETHODIMP
1742 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
1744 if (mOwnerRule) {
1745 return mOwnerRule->GetDOMRule(aOwnerRule);
1748 *aOwnerRule = nsnull;
1749 return NS_OK;
1752 NS_IMETHODIMP
1753 nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
1755 // No doing this on incomplete sheets!
1756 PRBool complete;
1757 GetComplete(complete);
1758 if (!complete) {
1759 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1762 //-- Security check: Only scripts whose principal subsumes that of the
1763 // style sheet can access rule collections.
1764 nsresult rv = SubjectSubsumesInnerPrincipal();
1765 NS_ENSURE_SUCCESS(rv, rv);
1767 // OK, security check passed, so get the rule collection
1768 if (nsnull == mRuleCollection) {
1769 mRuleCollection = new CSSRuleListImpl(this);
1770 if (nsnull == mRuleCollection) {
1771 return NS_ERROR_OUT_OF_MEMORY;
1773 NS_ADDREF(mRuleCollection);
1776 *aCssRules = mRuleCollection;
1777 NS_ADDREF(mRuleCollection);
1779 return NS_OK;
1782 NS_IMETHODIMP
1783 nsCSSStyleSheet::InsertRule(const nsAString& aRule,
1784 PRUint32 aIndex,
1785 PRUint32* aReturn)
1787 //-- Security check: Only scripts whose principal subsumes that of the
1788 // style sheet can modify rule collections.
1789 nsresult rv = SubjectSubsumesInnerPrincipal();
1790 NS_ENSURE_SUCCESS(rv, rv);
1792 return InsertRuleInternal(aRule, aIndex, aReturn);
1795 NS_IMETHODIMP
1796 nsCSSStyleSheet::InsertRuleInternal(const nsAString& aRule,
1797 PRUint32 aIndex,
1798 PRUint32* aReturn)
1800 // No doing this if the sheet is not complete!
1801 PRBool complete;
1802 GetComplete(complete);
1803 if (!complete) {
1804 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1807 if (aRule.IsEmpty()) {
1808 // Nothing to do here
1809 return NS_OK;
1812 nsresult result;
1813 result = WillDirty();
1814 if (NS_FAILED(result))
1815 return result;
1817 if (aIndex > PRUint32(mInner->mOrderedRules.Count()))
1818 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1820 NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX,
1821 "Too many style rules!");
1823 // Hold strong ref to the CSSLoader in case the document update
1824 // kills the document
1825 nsCOMPtr<nsICSSLoader> loader;
1826 if (mDocument) {
1827 loader = mDocument->CSSLoader();
1828 NS_ASSERTION(loader, "Document with no CSS loader!");
1831 nsCOMPtr<nsICSSParser> css;
1832 if (loader) {
1833 result = loader->GetParserFor(this, getter_AddRefs(css));
1835 else {
1836 result = NS_NewCSSParser(getter_AddRefs(css));
1837 if (css) {
1838 css->SetStyleSheet(this);
1841 if (NS_FAILED(result))
1842 return result;
1844 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
1846 nsCOMArray<nsICSSRule> rules;
1847 result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
1848 mInner->mPrincipal, rules);
1849 if (NS_FAILED(result))
1850 return result;
1852 PRInt32 rulecount = rules.Count();
1853 if (rulecount == 0) {
1854 // Since we know aRule was not an empty string, just throw
1855 return NS_ERROR_DOM_SYNTAX_ERR;
1858 // Hierarchy checking. Just check the first and last rule in the list.
1860 // check that we're not inserting before a charset rule
1861 PRInt32 nextType = nsICSSRule::UNKNOWN_RULE;
1862 nsICSSRule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
1863 if (nextRule) {
1864 nextRule->GetType(nextType);
1865 if (nextType == nsICSSRule::CHARSET_RULE) {
1866 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1869 // check last rule in list
1870 nsICSSRule* lastRule = rules.ObjectAt(rulecount - 1);
1871 PRInt32 lastType = nsICSSRule::UNKNOWN_RULE;
1872 lastRule->GetType(lastType);
1874 if (nextType == nsICSSRule::IMPORT_RULE &&
1875 lastType != nsICSSRule::CHARSET_RULE &&
1876 lastType != nsICSSRule::IMPORT_RULE) {
1877 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1880 if (nextType == nsICSSRule::NAMESPACE_RULE &&
1881 lastType != nsICSSRule::CHARSET_RULE &&
1882 lastType != nsICSSRule::IMPORT_RULE &&
1883 lastType != nsICSSRule::NAMESPACE_RULE) {
1884 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1888 // check first rule in list
1889 nsICSSRule* firstRule = rules.ObjectAt(0);
1890 PRInt32 firstType = nsICSSRule::UNKNOWN_RULE;
1891 firstRule->GetType(firstType);
1892 if (aIndex != 0) {
1893 if (firstType == nsICSSRule::CHARSET_RULE) { // no inserting charset at nonzero position
1894 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1897 nsICSSRule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1);
1898 PRInt32 prevType = nsICSSRule::UNKNOWN_RULE;
1899 prevRule->GetType(prevType);
1901 if (firstType == nsICSSRule::IMPORT_RULE &&
1902 prevType != nsICSSRule::CHARSET_RULE &&
1903 prevType != nsICSSRule::IMPORT_RULE) {
1904 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1907 if (firstType == nsICSSRule::NAMESPACE_RULE &&
1908 prevType != nsICSSRule::CHARSET_RULE &&
1909 prevType != nsICSSRule::IMPORT_RULE &&
1910 prevType != nsICSSRule::NAMESPACE_RULE) {
1911 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1915 PRBool insertResult = mInner->mOrderedRules.InsertObjectsAt(rules, aIndex);
1916 NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY);
1917 DidDirty();
1919 for (PRInt32 counter = 0; counter < rulecount; counter++) {
1920 nsICSSRule* cssRule = rules.ObjectAt(counter);
1921 cssRule->SetStyleSheet(this);
1923 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
1924 cssRule->GetType(type);
1925 if (type == nsICSSRule::NAMESPACE_RULE) {
1926 // XXXbz does this screw up when inserting a namespace rule before
1927 // another namespace rule that binds the same prefix to a different
1928 // namespace?
1929 result = RegisterNamespaceRule(cssRule);
1930 NS_ENSURE_SUCCESS(result, result);
1933 // We don't notify immediately for @import rules, but rather when
1934 // the sheet the rule is importing is loaded
1935 PRBool notify = PR_TRUE;
1936 if (type == nsICSSRule::IMPORT_RULE) {
1937 nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
1938 NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
1939 nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
1940 importRule->GetStyleSheet(getter_AddRefs(childSheet));
1941 if (!childSheet) {
1942 notify = PR_FALSE;
1945 if (mDocument && notify) {
1946 mDocument->StyleRuleAdded(this, cssRule);
1950 if (loader) {
1951 loader->RecycleParser(css);
1954 *aReturn = aIndex;
1955 return NS_OK;
1958 NS_IMETHODIMP
1959 nsCSSStyleSheet::DeleteRule(PRUint32 aIndex)
1961 nsresult result = NS_ERROR_DOM_INDEX_SIZE_ERR;
1962 // No doing this if the sheet is not complete!
1963 PRBool complete;
1964 GetComplete(complete);
1965 if (!complete) {
1966 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1969 //-- Security check: Only scripts whose principal subsumes that of the
1970 // style sheet can modify rule collections.
1971 nsresult rv = SubjectSubsumesInnerPrincipal();
1972 NS_ENSURE_SUCCESS(rv, rv);
1974 // XXX TBI: handle @rule types
1975 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
1977 result = WillDirty();
1979 if (NS_SUCCEEDED(result)) {
1980 if (aIndex >= PRUint32(mInner->mOrderedRules.Count()))
1981 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1983 NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX,
1984 "Too many style rules!");
1986 // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
1987 nsCOMPtr<nsICSSRule> rule = mInner->mOrderedRules.ObjectAt(aIndex);
1988 if (rule) {
1989 mInner->mOrderedRules.RemoveObjectAt(aIndex);
1990 rule->SetStyleSheet(nsnull);
1991 DidDirty();
1993 if (mDocument) {
1994 mDocument->StyleRuleRemoved(this, rule);
1999 return result;
2002 NS_IMETHODIMP
2003 nsCSSStyleSheet::DeleteRuleFromGroup(nsICSSGroupRule* aGroup, PRUint32 aIndex)
2005 NS_ENSURE_ARG_POINTER(aGroup);
2006 NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!");
2007 nsresult result;
2008 nsCOMPtr<nsICSSRule> rule;
2009 result = aGroup->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
2010 NS_ENSURE_SUCCESS(result, result);
2012 // check that the rule actually belongs to this sheet!
2013 nsCOMPtr<nsIStyleSheet> ruleSheet;
2014 rule->GetStyleSheet(*getter_AddRefs(ruleSheet));
2015 if (this != ruleSheet) {
2016 return NS_ERROR_INVALID_ARG;
2019 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
2021 result = WillDirty();
2022 NS_ENSURE_SUCCESS(result, result);
2024 result = aGroup->DeleteStyleRuleAt(aIndex);
2025 NS_ENSURE_SUCCESS(result, result);
2027 rule->SetStyleSheet(nsnull);
2029 DidDirty();
2031 if (mDocument) {
2032 mDocument->StyleRuleRemoved(this, rule);
2035 return NS_OK;
2038 NS_IMETHODIMP
2039 nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule,
2040 nsICSSGroupRule* aGroup,
2041 PRUint32 aIndex,
2042 PRUint32* _retval)
2044 nsresult result;
2045 NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!");
2046 // check that the group actually belongs to this sheet!
2047 nsCOMPtr<nsIStyleSheet> groupSheet;
2048 aGroup->GetStyleSheet(*getter_AddRefs(groupSheet));
2049 if (this != groupSheet) {
2050 return NS_ERROR_INVALID_ARG;
2053 if (aRule.IsEmpty()) {
2054 // Nothing to do here
2055 return NS_OK;
2058 // Hold strong ref to the CSSLoader in case the document update
2059 // kills the document
2060 nsCOMPtr<nsICSSLoader> loader;
2061 if (mDocument) {
2062 loader = mDocument->CSSLoader();
2063 NS_ASSERTION(loader, "Document with no CSS loader!");
2066 nsCOMPtr<nsICSSParser> css;
2067 if (loader) {
2068 result = loader->GetParserFor(this, getter_AddRefs(css));
2070 else {
2071 result = NS_NewCSSParser(getter_AddRefs(css));
2072 if (css) {
2073 css->SetStyleSheet(this);
2076 NS_ENSURE_SUCCESS(result, result);
2078 // parse and grab the rule
2079 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
2081 result = WillDirty();
2082 NS_ENSURE_SUCCESS(result, result);
2084 nsCOMArray<nsICSSRule> rules;
2085 result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
2086 mInner->mPrincipal, rules);
2087 NS_ENSURE_SUCCESS(result, result);
2089 PRInt32 rulecount = rules.Count();
2090 if (rulecount == 0) {
2091 // Since we know aRule was not an empty string, just throw
2092 return NS_ERROR_DOM_SYNTAX_ERR;
2095 PRInt32 counter;
2096 nsICSSRule* rule;
2097 for (counter = 0; counter < rulecount; counter++) {
2098 // Only rulesets are allowed in a group as of CSS2
2099 PRInt32 type = nsICSSRule::UNKNOWN_RULE;
2100 rule = rules.ObjectAt(counter);
2101 rule->GetType(type);
2102 if (type != nsICSSRule::STYLE_RULE) {
2103 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2107 result = aGroup->InsertStyleRulesAt(aIndex, rules);
2108 NS_ENSURE_SUCCESS(result, result);
2109 DidDirty();
2110 for (counter = 0; counter < rulecount; counter++) {
2111 rule = rules.ObjectAt(counter);
2113 if (mDocument) {
2114 mDocument->StyleRuleAdded(this, rule);
2118 if (loader) {
2119 loader->RecycleParser(css);
2122 *_retval = aIndex;
2123 return NS_OK;
2126 NS_IMETHODIMP
2127 nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule* aGroup,
2128 nsICSSRule* aOld, nsICSSRule* aNew)
2130 nsresult result;
2131 NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
2132 #ifdef DEBUG
2134 nsCOMPtr<nsIStyleSheet> groupSheet;
2135 aGroup->GetStyleSheet(*getter_AddRefs(groupSheet));
2136 NS_ASSERTION(this == groupSheet, "group doesn't belong to this sheet");
2138 #endif
2139 result = WillDirty();
2140 NS_ENSURE_SUCCESS(result, result);
2142 result = aGroup->ReplaceStyleRule(aOld, aNew);
2143 DidDirty();
2144 return result;
2147 // nsICSSLoaderObserver implementation
2148 NS_IMETHODIMP
2149 nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
2150 PRBool aWasAlternate,
2151 nsresult aStatus)
2153 #ifdef DEBUG
2154 nsCOMPtr<nsIStyleSheet> styleSheet(do_QueryInterface(aSheet));
2155 NS_ASSERTION(styleSheet, "Sheet not implementing nsIStyleSheet!\n");
2156 nsCOMPtr<nsIStyleSheet> parentSheet;
2157 aSheet->GetParentSheet(*getter_AddRefs(parentSheet));
2158 nsCOMPtr<nsIStyleSheet> thisSheet;
2159 QueryInterface(NS_GET_IID(nsIStyleSheet), getter_AddRefs(thisSheet));
2160 NS_ASSERTION(thisSheet == parentSheet, "We are being notified of a sheet load for a sheet that is not our child!\n");
2161 #endif
2163 if (mDocument && NS_SUCCEEDED(aStatus)) {
2164 nsCOMPtr<nsICSSImportRule> ownerRule;
2165 aSheet->GetOwnerRule(getter_AddRefs(ownerRule));
2167 mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
2169 // XXXldb @import rules shouldn't even implement nsIStyleRule (but
2170 // they do)!
2171 nsCOMPtr<nsIStyleRule> styleRule(do_QueryInterface(ownerRule));
2173 mDocument->StyleRuleAdded(this, styleRule);
2176 return NS_OK;
2179 nsresult
2180 NS_NewCSSStyleSheet(nsICSSStyleSheet** aInstancePtrResult)
2182 *aInstancePtrResult = nsnull;
2183 nsCSSStyleSheet *it = new nsCSSStyleSheet();
2185 if (!it) {
2186 return NS_ERROR_OUT_OF_MEMORY;
2189 NS_ADDREF(it);
2191 if (!it->mInner || !it->mInner->mPrincipal) {
2192 NS_RELEASE(it);
2193 return NS_ERROR_OUT_OF_MEMORY;
2196 *aInstancePtrResult = it;
2197 return NS_OK;