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
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.
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"
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"
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"
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
87 CSSRuleListImpl(nsCSSStyleSheet
*aStyleSheet
);
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
; }
100 virtual ~CSSRuleListImpl();
102 nsCSSStyleSheet
* mStyleSheet
;
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
)
128 NS_IMPL_ADDREF(CSSRuleListImpl
)
129 NS_IMPL_RELEASE(CSSRuleListImpl
)
133 CSSRuleListImpl::GetLength(PRUint32
* aLength
)
135 if (nsnull
!= mStyleSheet
) {
137 mStyleSheet
->StyleRuleCount(count
);
138 *aLength
= (PRUint32
)count
;
148 CSSRuleListImpl::GetItemAt(PRUint32 aIndex
, nsresult
* aResult
)
150 nsresult result
= NS_OK
;
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
));
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."
173 CSSRuleListImpl::Item(PRUint32 aIndex
, nsIDOMCSSRule
** aReturn
)
176 nsIDOMCSSRule
* rule
= GetItemAt(aIndex
, &rv
);
183 return CallQueryInterface(rule
, aReturn
);
186 template <class Numeric
>
187 PRInt32
DoCompare(Numeric a
, Numeric b
)
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
) {
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;
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
);
235 case nsMediaFeature::eInteger
:
236 case nsMediaFeature::eBoolInteger
:
238 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Integer
,
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());
251 case nsMediaFeature::eIntRatio
:
253 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Array
&&
254 actual
.GetArrayValue()->Count() == 2 &&
255 actual
.GetArrayValue()->Item(0).GetUnit() ==
257 actual
.GetArrayValue()->Item(1).GetUnit() ==
260 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Array
&&
261 required
.GetArrayValue()->Count() == 2 &&
262 required
.GetArrayValue()->Item(0).GetUnit() ==
264 required
.GetArrayValue()->Item(1).GetUnit() ==
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
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
);
279 case nsMediaFeature::eResolution
:
281 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Inch
||
282 actual
.GetUnit() == eCSSUnit_Centimeter
,
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
);
296 case nsMediaFeature::eEnumerated
:
298 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Enumerated
,
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());
311 case nsMediaExpression::eMin
:
313 case nsMediaExpression::eMax
:
315 case nsMediaExpression::eEqual
:
318 NS_NOTREACHED("unexpected mRange");
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
];
335 entry
= mFeatureCache
.AppendElement();
337 return; /* out of memory */
339 entry
->mFeature
= feature
;
342 ExpressionEntry eentry
= { *aExpression
, aExpressionMatches
};
343 entry
->mExpressions
.AppendElement(eentry
);
347 nsMediaQueryResultCacheKey::Matches(nsPresContext
* aPresContext
) const
349 if (aPresContext
->Medium() != mMedium
) {
353 for (PRUint32 i
= 0; i
< mFeatureCache
.Length(); ++i
) {
354 const FeatureEntry
*entry
= &mFeatureCache
[i
];
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
) {
372 nsMediaQuery::AppendToString(nsAString
& aString
) const
376 if (mHadUnknownExpression
) {
377 aString
.AppendLiteral("not all");
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");
386 aString
.AppendLiteral("not ");
387 } else if (mHasOnly
) {
388 aString
.AppendLiteral("only ");
390 mMediaType
->ToString(buffer
);
391 aString
.Append(buffer
);
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
);
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
);
422 case nsMediaFeature::eInteger
:
423 case nsMediaFeature::eBoolInteger
:
424 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Integer
,
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
);
431 case nsMediaFeature::eIntRatio
:
433 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Array
,
435 nsCSSValue::Array
*array
= expr
.mValue
.GetArrayValue();
436 NS_ASSERTION(array
->Count() == 2, "unexpected length");
437 NS_ASSERTION(array
->Item(0).GetUnit() == eCSSUnit_Integer
,
439 NS_ASSERTION(array
->Item(1).GetUnit() == eCSSUnit_Integer
,
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
);
448 case nsMediaFeature::eResolution
:
449 buffer
.AppendFloat(expr
.mValue
.GetFloatValue());
450 aString
.Append(buffer
);
452 if (expr
.mValue
.GetUnit() == eCSSUnit_Inch
) {
453 aString
.AppendLiteral("dpi");
455 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Centimeter
,
457 aString
.AppendLiteral("dpcm");
460 case nsMediaFeature::eEnumerated
:
461 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Enumerated
,
464 nsCSSProps::ValueToKeyword(expr
.mValue
.GetIntValue(),
465 feature
->mKeywordTable
),
471 aString
.AppendLiteral(")");
476 nsMediaQuery::Clone() const
478 nsAutoPtr
<nsMediaQuery
> result(new nsMediaQuery(*this));
479 NS_ENSURE_TRUE(result
&&
480 result
->mExpressions
.Length() == mExpressions
.Length(),
482 return result
.forget();
486 nsMediaQuery::Matches(nsPresContext
* aPresContext
,
487 nsMediaQueryResultCacheKey
& aKey
) const
489 if (mHadUnknownExpression
)
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
];
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
)
513 NS_IMPL_ADDREF(nsMediaList
)
514 NS_IMPL_RELEASE(nsMediaList
)
517 nsMediaList::nsMediaList()
519 , mStyleSheet(nsnull
)
523 nsMediaList::~nsMediaList()
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
);
543 aMediaText
.AppendLiteral(", ");
550 // XXXbz this is so ill-defined in the spec, it's not clear quite what
551 // it should be doing....
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
));
563 nsCOMPtr
<nsIDOMNode
> node
;
564 domSheet
->GetOwnerNode(getter_AddRefs(node
));
568 return parser
->ParseMediaList(nsString(aMediaText
), nsnull
, 0,
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
)) {
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
);
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
);
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) \
618 rv = sheet->GetOwningDocument(*getter_AddRefs(doc)); \
619 NS_ENSURE_SUCCESS(rv, rv); \
621 mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, PR_TRUE); \
623 rv = sheet->WillDirty(); \
624 NS_ENSURE_SUCCESS(rv, rv); \
627 #define END_MEDIA_CHANGE(sheet, doc) \
631 /* XXXldb Pass something meaningful? */ \
633 doc->StyleRuleChanged(sheet, nsnull, nsnull); \
638 nsMediaList::SetMediaText(const nsAString
& aMediaText
)
641 nsCOMPtr
<nsIDocument
> doc
;
643 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
645 rv
= SetText(aMediaText
);
649 END_MEDIA_CHANGE(mStyleSheet
, doc
)
655 nsMediaList::GetLength(PRUint32
* aLength
)
657 NS_ENSURE_ARG_POINTER(aLength
);
659 *aLength
= mArray
.Length();
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
);
672 query
->AppendToString(aReturn
);
674 SetDOMStringToNull(aReturn
);
681 nsMediaList::DeleteMedium(const nsAString
& aOldMedium
)
684 nsCOMPtr
<nsIDocument
> doc
;
686 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
688 rv
= Delete(aOldMedium
);
692 END_MEDIA_CHANGE(mStyleSheet
, doc
)
698 nsMediaList::AppendMedium(const nsAString
& aNewMedium
)
701 nsCOMPtr
<nsIDocument
> doc
;
703 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
705 rv
= Append(aNewMedium
);
709 END_MEDIA_CHANGE(mStyleSheet
, doc
)
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
);
725 query
->AppendToString(buf
);
726 if (buf
== aOldMedium
) {
727 mArray
.RemoveElementAt(i
);
732 return NS_ERROR_DOM_NOT_FOUND_ERR
;
736 nsMediaList::Append(const nsAString
& aNewMedium
)
738 if (aNewMedium
.IsEmpty())
739 return NS_ERROR_DOM_NOT_FOUND_ERR
;
744 nsTArray
<nsAutoPtr
<nsMediaQuery
> > buf
;
748 mArray
.SwapElements(buf
);
749 NS_ASSERTION(ok
, "SwapElements should never fail when neither array "
752 if (mArray
.Length() == 1) {
753 nsMediaQuery
*query
= mArray
[0].forget();
754 if (!buf
.AppendElement(query
)) {
756 rv
= NS_ERROR_OUT_OF_MEMORY
;
762 mArray
.SwapElements(buf
);
763 NS_ASSERTION(ok
, "SwapElements should never fail when neither array "
768 // -------------------------------
769 // CSS Style Sheet Inner Data Container
773 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsICSSStyleSheet
* aPrimarySheet
)
777 , mPrincipalSet(PR_FALSE
)
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
)
789 aRule
->SetStyleSheet((nsICSSStyleSheet
*)aSheet
);
795 CloneRuleInto(nsICSSRule
* aRule
, void* aArray
)
797 nsICSSRule
* clone
= nsnull
;
800 static_cast<nsCOMArray
<nsICSSRule
>*>(aArray
)->AppendObject(clone
);
806 struct ChildSheetListBuilder
{
807 nsRefPtr
<nsCSSStyleSheet
>* sheetSlot
;
808 nsCSSStyleSheet
* parent
;
810 void SetParentLinks(nsCSSStyleSheet
* aSheet
) {
811 aSheet
->mParent
= parent
;
812 aSheet
->SetOwningDocument(parent
->mDocument
);
817 RebuildChildList(nsICSSRule
* aRule
, void* aBuilder
)
820 aRule
->GetType(type
);
821 if (type
== nsICSSRule::CHARSET_RULE
) {
825 if (type
== nsICSSRule::NAMESPACE_RULE
|| type
== nsICSSRule::MEDIA_RULE
||
826 type
== nsICSSRule::STYLE_RULE
) {
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
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
);
849 (*builder
->sheetSlot
) = static_cast<nsCSSStyleSheet
*>(cssSheet
.get());
850 builder
->SetParentLinks(*builder
->sheetSlot
);
854 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner
& aCopy
,
855 nsCSSStyleSheet
* aPrimarySheet
)
857 mSheetURI(aCopy
.mSheetURI
),
858 mOriginalSheetURI(aCopy
.mOriginalSheetURI
),
859 mBaseURI(aCopy
.mBaseURI
),
860 mPrincipal(aCopy
.mPrincipal
),
861 mComplete(aCopy
.mComplete
)
863 , mPrincipalSet(aCopy
.mPrincipalSet
)
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
);
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
);
890 nsCSSStyleSheetInner::AddSheet(nsICSSStyleSheet
* aSheet
)
892 mSheets
.AppendElement(aSheet
);
896 nsCSSStyleSheetInner::RemoveSheet(nsICSSStyleSheet
* aSheet
)
898 if (1 == mSheets
.Count()) {
899 NS_ASSERTION(aSheet
== (nsICSSStyleSheet
*)mSheets
.ElementAt(0), "bad parent");
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));
910 mSheets
.RemoveElement(aSheet
);
915 AddNamespaceRuleToMap(nsICSSRule
* aRule
, nsXMLNameSpaceMap
* aMap
)
919 aRule
->GetType(type
);
920 NS_ASSERTION(type
== nsICSSRule::NAMESPACE_RULE
, "Bogus rule type");
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
);
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
));
943 // stop if not namespace, import or charset because namespace can't follow
945 return (nsICSSRule::CHARSET_RULE
== type
|| nsICSSRule::IMPORT_RULE
== type
);
949 nsCSSStyleSheetInner::RebuildNameSpaces()
951 // Just nuke our existing namespace map, if any
952 if (NS_SUCCEEDED(CreateNamespaceMap())) {
953 mOrderedRules
.EnumerateForwards(CreateNameSpace
, mNameSpaceMap
);
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
);
968 // -------------------------------
972 nsCSSStyleSheet::nsCSSStyleSheet()
973 : nsICSSStyleSheet(),
979 mRuleCollection(nsnull
),
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(),
997 mTitle(aCopy
.mTitle
),
999 mParent(aParentToUse
),
1000 mOwnerRule(aOwnerRuleToUse
),
1001 mRuleCollection(nsnull
), // re-created lazily
1002 mDocument(aDocumentToUse
),
1003 mOwningNode(aOwningNodeToUse
),
1004 mDisabled(aCopy
.mDisabled
),
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();
1019 // XXX This is wrong; we should be keeping @import rules and
1021 aCopy
.mMedia
->Clone(getter_AddRefs(mMedia
));
1025 nsCSSStyleSheet::~nsCSSStyleSheet()
1027 for (nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
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
);
1042 mMedia
->SetStyleSheet(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
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
)
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
1087 nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor
* aProcessor
)
1089 if (!mRuleProcessors
)
1090 return NS_ERROR_FAILURE
;
1091 return mRuleProcessors
->RemoveElement(aProcessor
)
1098 nsCSSStyleSheet::SetURIs(nsIURI
* aSheetURI
, nsIURI
* aOriginalSheetURI
,
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
;
1113 nsCSSStyleSheet::SetPrincipal(nsIPrincipal
* aPrincipal
)
1115 NS_PRECONDITION(!mInner
->mPrincipalSet
,
1116 "Should have an inner whose principal has not yet been set");
1118 mInner
->mPrincipal
= aPrincipal
;
1120 mInner
->mPrincipalSet
= PR_TRUE
;
1126 nsCSSStyleSheet::Principal() const
1128 return mInner
->mPrincipal
;
1132 nsCSSStyleSheet::GetSheetURI(nsIURI
** aSheetURI
) const
1134 NS_IF_ADDREF(*aSheetURI
= mInner
->mSheetURI
.get());
1139 nsCSSStyleSheet::GetBaseURI(nsIURI
** aBaseURI
) const
1141 NS_IF_ADDREF(*aBaseURI
= mInner
->mBaseURI
.get());
1146 nsCSSStyleSheet::SetTitle(const nsAString
& aTitle
)
1153 nsCSSStyleSheet::GetType(nsString
& aType
) const
1155 aType
.AssignLiteral("text/css");
1160 nsCSSStyleSheet::UseForPresentation(nsPresContext
* aPresContext
,
1161 nsMediaQueryResultCacheKey
& aKey
) const
1164 return mMedia
->Matches(aPresContext
, aKey
);
1171 nsCSSStyleSheet::SetMedia(nsMediaList
* aMedia
)
1177 NS_IMETHODIMP_(PRBool
)
1178 nsCSSStyleSheet::HasRules() const
1181 StyleRuleCount(count
);
1186 nsCSSStyleSheet::GetApplicable(PRBool
& aApplicable
) const
1188 aApplicable
= !mDisabled
&& mInner
->mComplete
;
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();
1203 mDocument
->SetStyleSheetApplicableState(this, !mDisabled
);
1211 nsCSSStyleSheet::GetComplete(PRBool
& aComplete
) const
1213 aComplete
= mInner
->mComplete
;
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
);
1232 nsCSSStyleSheet::GetParentSheet(nsIStyleSheet
*& aParent
) const
1235 NS_IF_ADDREF(aParent
);
1240 nsCSSStyleSheet::GetOwningDocument(nsIDocument
*& aDocument
) const
1242 aDocument
= mDocument
;
1243 NS_IF_ADDREF(aDocument
);
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
);
1264 nsCSSStyleSheet::SetOwningNode(nsIDOMNode
* aOwningNode
)
1265 { // not ref counted
1266 mOwningNode
= aOwningNode
;
1271 nsCSSStyleSheet::SetOwnerRule(nsICSSImportRule
* aOwnerRule
)
1272 { // not ref counted
1273 mOwnerRule
= aOwnerRule
;
1278 nsCSSStyleSheet::GetOwnerRule(nsICSSImportRule
** aOwnerRule
)
1280 *aOwnerRule
= mOwnerRule
;
1281 NS_IF_ADDREF(*aOwnerRule
);
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
;
1295 tail
= &(*tail
)->mNext
;
1299 // This is not reference counted. Our parent tells us when
1301 sheet
->mParent
= this;
1302 sheet
->mDocument
= mDocument
;
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
) {
1321 tail
= &(*tail
)->mNext
;
1323 sheet
->mNext
= *tail
;
1326 // This is not reference counted. Our parent tells us when
1328 sheet
->mParent
= this;
1329 sheet
->mDocument
= mDocument
;
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);
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();
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);
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
);
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
);
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");
1401 nsCSSStyleSheet::StyleRuleCount(PRInt32
& aCount
) const
1403 aCount
= mInner
->mOrderedRules
.Count();
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
);
1418 return NS_ERROR_ILLEGAL_VALUE
;
1422 nsCSSStyleSheet::GetNameSpaceMap() const
1424 return mInner
->mNameSpaceMap
;
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.
1435 const nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1438 child
= child
->mNext
;
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
1452 nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1453 while (child
&& (0 != aIndex
)) {
1455 child
= child
->mNext
;
1458 NS_IF_ADDREF(aSheet
= child
);
1464 nsCSSStyleSheet::EnsureUniqueInner()
1466 if (1 < mInner
->mSheets
.Count()) {
1467 nsCSSStyleSheetInner
* clone
= mInner
->CloneFor(this);
1469 mInner
->RemoveSheet(this);
1473 return NS_ERROR_OUT_OF_MEMORY
;
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,
1493 *aClone
= static_cast<nsICSSStyleSheet
*>(clone
);
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
)
1518 void nsCSSStyleSheet::List(FILE* out
, PRInt32 aIndent
) const
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
);
1534 fputs(" media: ", out
);
1535 nsAutoString buffer
;
1536 mMedia
->GetText(buffer
);
1537 fputs(NS_ConvertUTF16toUTF8(buffer
).get(), out
);
1541 for (const nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1543 child
= child
->mNext
) {
1544 child
->List(out
, aIndent
+ 1);
1547 fputs("Rules in source order:\n", out
);
1548 ListRules(mInner
->mOrderedRules
, out
, aIndent
);
1553 EnumClearRuleCascades(void* aProcessor
, void* aData
)
1555 nsCSSRuleProcessor
* processor
=
1556 static_cast<nsCSSRuleProcessor
*>(aProcessor
);
1557 processor
->ClearRuleCascades();
1562 nsCSSStyleSheet::ClearRuleCascades()
1564 if (mRuleProcessors
) {
1565 mRuleProcessors
->EnumerateForwards(EnumClearRuleCascades
, nsnull
);
1568 nsCSSStyleSheet
* parent
= (nsCSSStyleSheet
*)mParent
;
1569 parent
->ClearRuleCascades();
1574 nsCSSStyleSheet::WillDirty()
1576 if (!mInner
->mComplete
) {
1581 return EnsureUniqueInner();
1585 nsCSSStyleSheet::DidDirty()
1587 ClearRuleCascades();
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
;
1606 nsresult rv
= subjectPrincipal
->Subsumes(mInner
->mPrincipal
, &subsumes
);
1607 NS_ENSURE_SUCCESS(rv
, rv
);
1613 if (!nsContentUtils::IsCallerTrustedForWrite()) {
1614 return NS_ERROR_DOM_SECURITY_ERR
;
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
);
1633 nsCSSStyleSheet::IsModified(PRBool
* aSheetModified
) const
1635 *aSheetModified
= mDirty
;
1640 nsCSSStyleSheet::SetModified(PRBool aModified
)
1646 // nsIDOMStyleSheet interface
1648 nsCSSStyleSheet::GetType(nsAString
& aType
)
1650 aType
.AssignLiteral("text/css");
1655 nsCSSStyleSheet::GetDisabled(PRBool
* aDisabled
)
1657 *aDisabled
= mDisabled
;
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
);
1671 nsCSSStyleSheet::GetOwnerNode(nsIDOMNode
** aOwnerNode
)
1673 *aOwnerNode
= mOwningNode
;
1674 NS_IF_ADDREF(*aOwnerNode
);
1679 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet
** aParentStyleSheet
)
1681 NS_ENSURE_ARG_POINTER(aParentStyleSheet
);
1683 nsresult rv
= NS_OK
;
1686 rv
= mParent
->QueryInterface(NS_GET_IID(nsIDOMStyleSheet
),
1687 (void **)aParentStyleSheet
);
1689 *aParentStyleSheet
= nsnull
;
1696 nsCSSStyleSheet::GetHref(nsAString
& aHref
)
1698 if (mInner
->mOriginalSheetURI
) {
1700 mInner
->mOriginalSheetURI
->GetSpec(str
);
1701 CopyUTF8toUTF16(str
, aHref
);
1703 SetDOMStringToNull(aHref
);
1710 nsCSSStyleSheet::GetTitle(nsString
& aTitle
) const
1717 nsCSSStyleSheet::GetTitle(nsAString
& aTitle
)
1719 aTitle
.Assign(mTitle
);
1724 nsCSSStyleSheet::GetMedia(nsIDOMMediaList
** aMedia
)
1726 NS_ENSURE_ARG_POINTER(aMedia
);
1730 mMedia
= new nsMediaList();
1731 NS_ENSURE_TRUE(mMedia
, NS_ERROR_OUT_OF_MEMORY
);
1732 mMedia
->SetStyleSheet(this);
1742 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule
** aOwnerRule
)
1745 return mOwnerRule
->GetDOMRule(aOwnerRule
);
1748 *aOwnerRule
= nsnull
;
1753 nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList
** aCssRules
)
1755 // No doing this on incomplete sheets!
1757 GetComplete(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
);
1783 nsCSSStyleSheet::InsertRule(const nsAString
& aRule
,
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
);
1796 nsCSSStyleSheet::InsertRuleInternal(const nsAString
& aRule
,
1800 // No doing this if the sheet is not complete!
1802 GetComplete(complete
);
1804 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1807 if (aRule
.IsEmpty()) {
1808 // Nothing to do here
1813 result
= WillDirty();
1814 if (NS_FAILED(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
;
1827 loader
= mDocument
->CSSLoader();
1828 NS_ASSERTION(loader
, "Document with no CSS loader!");
1831 nsCOMPtr
<nsICSSParser
> css
;
1833 result
= loader
->GetParserFor(this, getter_AddRefs(css
));
1836 result
= NS_NewCSSParser(getter_AddRefs(css
));
1838 css
->SetStyleSheet(this);
1841 if (NS_FAILED(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
))
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
);
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
);
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
);
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
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
));
1945 if (mDocument
&& notify
) {
1946 mDocument
->StyleRuleAdded(this, cssRule
);
1951 loader
->RecycleParser(css
);
1959 nsCSSStyleSheet::DeleteRule(PRUint32 aIndex
)
1961 nsresult result
= NS_ERROR_DOM_INDEX_SIZE_ERR
;
1962 // No doing this if the sheet is not complete!
1964 GetComplete(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
);
1989 mInner
->mOrderedRules
.RemoveObjectAt(aIndex
);
1990 rule
->SetStyleSheet(nsnull
);
1994 mDocument
->StyleRuleRemoved(this, rule
);
2003 nsCSSStyleSheet::DeleteRuleFromGroup(nsICSSGroupRule
* aGroup
, PRUint32 aIndex
)
2005 NS_ENSURE_ARG_POINTER(aGroup
);
2006 NS_ASSERTION(mInner
->mComplete
, "No deleting from an incomplete sheet!");
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
);
2032 mDocument
->StyleRuleRemoved(this, rule
);
2039 nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString
& aRule
,
2040 nsICSSGroupRule
* aGroup
,
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
2058 // Hold strong ref to the CSSLoader in case the document update
2059 // kills the document
2060 nsCOMPtr
<nsICSSLoader
> loader
;
2062 loader
= mDocument
->CSSLoader();
2063 NS_ASSERTION(loader
, "Document with no CSS loader!");
2066 nsCOMPtr
<nsICSSParser
> css
;
2068 result
= loader
->GetParserFor(this, getter_AddRefs(css
));
2071 result
= NS_NewCSSParser(getter_AddRefs(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
;
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
);
2110 for (counter
= 0; counter
< rulecount
; counter
++) {
2111 rule
= rules
.ObjectAt(counter
);
2114 mDocument
->StyleRuleAdded(this, rule
);
2119 loader
->RecycleParser(css
);
2127 nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule
* aGroup
,
2128 nsICSSRule
* aOld
, nsICSSRule
* aNew
)
2131 NS_PRECONDITION(mInner
->mComplete
, "No replacing in an incomplete sheet!");
2134 nsCOMPtr
<nsIStyleSheet
> groupSheet
;
2135 aGroup
->GetStyleSheet(*getter_AddRefs(groupSheet
));
2136 NS_ASSERTION(this == groupSheet
, "group doesn't belong to this sheet");
2139 result
= WillDirty();
2140 NS_ENSURE_SUCCESS(result
, result
);
2142 result
= aGroup
->ReplaceStyleRule(aOld
, aNew
);
2147 // nsICSSLoaderObserver implementation
2149 nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet
* aSheet
,
2150 PRBool aWasAlternate
,
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");
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
2171 nsCOMPtr
<nsIStyleRule
> styleRule(do_QueryInterface(ownerRule
));
2173 mDocument
->StyleRuleAdded(this, styleRule
);
2180 NS_NewCSSStyleSheet(nsICSSStyleSheet
** aInstancePtrResult
)
2182 *aInstancePtrResult
= nsnull
;
2183 nsCSSStyleSheet
*it
= new nsCSSStyleSheet();
2186 return NS_ERROR_OUT_OF_MEMORY
;
2191 if (!it
->mInner
|| !it
->mInner
->mPrincipal
) {
2193 return NS_ERROR_OUT_OF_MEMORY
;
2196 *aInstancePtrResult
= it
;