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 "nsCSSRuleProcessor.h"
49 #include "nsICSSNameSpaceRule.h"
50 #include "nsICSSGroupRule.h"
51 #include "nsICSSImportRule.h"
52 #include "nsIMediaList.h"
53 #include "nsIDocument.h"
54 #include "nsPresContext.h"
55 #include "nsGkAtoms.h"
58 #include "nsIDOMCSSStyleSheet.h"
59 #include "nsIDOMCSSImportRule.h"
60 #include "nsICSSRuleList.h"
61 #include "nsIDOMMediaList.h"
62 #include "nsIDOMNode.h"
63 #include "nsDOMError.h"
64 #include "nsCSSParser.h"
65 #include "mozilla/css/Loader.h"
66 #include "nsICSSLoaderObserver.h"
67 #include "nsINameSpaceManager.h"
68 #include "nsXMLNameSpaceMap.h"
70 #include "nsContentUtils.h"
71 #include "nsIScriptSecurityManager.h"
72 #include "mozAutoDocUpdate.h"
73 #include "mozilla/css/Declaration.h"
74 #include "nsRuleNode.h"
75 #include "nsMediaFeatures.h"
77 namespace css
= mozilla::css
;
79 // -------------------------------
80 // Style Rule List for the DOM
82 class CSSRuleListImpl
: public nsICSSRuleList
85 CSSRuleListImpl(nsCSSStyleSheet
*aStyleSheet
);
89 // nsIDOMCSSRuleList interface
90 NS_IMETHOD
GetLength(PRUint32
* aLength
);
91 NS_IMETHOD
Item(PRUint32 aIndex
, nsIDOMCSSRule
** aReturn
);
93 virtual nsIDOMCSSRule
* GetItemAt(PRUint32 aIndex
, nsresult
* aResult
);
95 void DropReference() { mStyleSheet
= nsnull
; }
98 virtual ~CSSRuleListImpl();
100 nsCSSStyleSheet
* mStyleSheet
;
102 PRBool mRulesAccessed
;
105 CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet
*aStyleSheet
)
107 // Not reference counted to avoid circular references.
108 // The style sheet will tell us when its going away.
109 mStyleSheet
= aStyleSheet
;
110 mRulesAccessed
= PR_FALSE
;
113 CSSRuleListImpl::~CSSRuleListImpl()
117 DOMCI_DATA(CSSRuleList
, 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_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSRuleList
)
128 NS_IMPL_ADDREF(CSSRuleListImpl
)
129 NS_IMPL_RELEASE(CSSRuleListImpl
)
133 CSSRuleListImpl::GetLength(PRUint32
* aLength
)
135 if (nsnull
!= mStyleSheet
) {
136 PRInt32 count
= mStyleSheet
->StyleRuleCount();
137 *aLength
= (PRUint32
)count
;
147 CSSRuleListImpl::GetItemAt(PRUint32 aIndex
, nsresult
* aResult
)
149 nsresult result
= NS_OK
;
152 // ensure rules have correct parent
153 if (mStyleSheet
->EnsureUniqueInner() !=
154 nsCSSStyleSheet::eUniqueInner_CloneFailed
) {
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 NS_ADDREF(*aReturn
= rule
);
187 template <class Numeric
>
188 PRInt32
DoCompare(Numeric a
, Numeric b
)
198 nsMediaExpression::Matches(nsPresContext
*aPresContext
,
199 const nsCSSValue
& aActualValue
) const
201 const nsCSSValue
& actual
= aActualValue
;
202 const nsCSSValue
& required
= mValue
;
204 // If we don't have the feature, the match fails.
205 if (actual
.GetUnit() == eCSSUnit_Null
) {
209 // If the expression had no value to match, the match succeeds,
210 // unless the value is an integer 0 or a zero length.
211 if (required
.GetUnit() == eCSSUnit_Null
) {
212 if (actual
.GetUnit() == eCSSUnit_Integer
)
213 return actual
.GetIntValue() != 0;
214 if (actual
.IsLengthUnit())
215 return actual
.GetFloatValue() != 0;
219 NS_ASSERTION(mFeature
->mRangeType
== nsMediaFeature::eMinMaxAllowed
||
220 mRange
== nsMediaExpression::eEqual
, "yikes");
221 PRInt32 cmp
; // -1 (actual < required)
222 // 0 (actual == required)
223 // 1 (actual > required)
224 switch (mFeature
->mValueType
) {
225 case nsMediaFeature::eLength
:
227 NS_ASSERTION(actual
.IsLengthUnit(), "bad actual value");
228 NS_ASSERTION(required
.IsLengthUnit(), "bad required value");
229 nscoord actualCoord
= nsRuleNode::CalcLengthWithInitialFont(
230 aPresContext
, actual
);
231 nscoord requiredCoord
= nsRuleNode::CalcLengthWithInitialFont(
232 aPresContext
, required
);
233 cmp
= DoCompare(actualCoord
, requiredCoord
);
236 case nsMediaFeature::eInteger
:
237 case nsMediaFeature::eBoolInteger
:
239 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Integer
,
241 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Integer
,
242 "bad required value");
243 NS_ASSERTION(mFeature
->mValueType
!= nsMediaFeature::eBoolInteger
||
244 actual
.GetIntValue() == 0 || actual
.GetIntValue() == 1,
245 "bad actual bool integer value");
246 NS_ASSERTION(mFeature
->mValueType
!= nsMediaFeature::eBoolInteger
||
247 required
.GetIntValue() == 0 || required
.GetIntValue() == 1,
248 "bad required bool integer value");
249 cmp
= DoCompare(actual
.GetIntValue(), required
.GetIntValue());
252 case nsMediaFeature::eFloat
:
254 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Number
,
256 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Number
,
257 "bad required value");
258 cmp
= DoCompare(actual
.GetFloatValue(), required
.GetFloatValue());
261 case nsMediaFeature::eIntRatio
:
263 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Array
&&
264 actual
.GetArrayValue()->Count() == 2 &&
265 actual
.GetArrayValue()->Item(0).GetUnit() ==
267 actual
.GetArrayValue()->Item(1).GetUnit() ==
270 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Array
&&
271 required
.GetArrayValue()->Count() == 2 &&
272 required
.GetArrayValue()->Item(0).GetUnit() ==
274 required
.GetArrayValue()->Item(1).GetUnit() ==
276 "bad required value");
277 // Convert to PRInt64 so we can multiply without worry. Note
278 // that while the spec requires that both halves of |required|
279 // be positive, the numerator or denominator of |actual| might
280 // be zero (e.g., when testing 'aspect-ratio' on a 0-width or
282 PRInt64 actualNum
= actual
.GetArrayValue()->Item(0).GetIntValue(),
283 actualDen
= actual
.GetArrayValue()->Item(1).GetIntValue(),
284 requiredNum
= required
.GetArrayValue()->Item(0).GetIntValue(),
285 requiredDen
= required
.GetArrayValue()->Item(1).GetIntValue();
286 cmp
= DoCompare(actualNum
* requiredDen
, requiredNum
* actualDen
);
289 case nsMediaFeature::eResolution
:
291 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Inch
||
292 actual
.GetUnit() == eCSSUnit_Centimeter
,
294 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Inch
||
295 required
.GetUnit() == eCSSUnit_Centimeter
,
296 "bad required value");
297 float actualDPI
= actual
.GetFloatValue();
298 if (actual
.GetUnit() == eCSSUnit_Centimeter
)
299 actualDPI
= actualDPI
* 2.54f
;
300 float requiredDPI
= required
.GetFloatValue();
301 if (required
.GetUnit() == eCSSUnit_Centimeter
)
302 requiredDPI
= requiredDPI
* 2.54f
;
303 cmp
= DoCompare(actualDPI
, requiredDPI
);
306 case nsMediaFeature::eEnumerated
:
308 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Enumerated
,
310 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Enumerated
,
311 "bad required value");
312 NS_ASSERTION(mFeature
->mRangeType
== nsMediaFeature::eMinMaxNotAllowed
,
313 "bad range"); // we asserted above about mRange
314 // We don't really need DoCompare, but it doesn't hurt (and
315 // maybe the compiler will condense this case with eInteger).
316 cmp
= DoCompare(actual
.GetIntValue(), required
.GetIntValue());
319 case nsMediaFeature::eIdent
:
321 NS_ASSERTION(actual
.GetUnit() == eCSSUnit_Ident
,
323 NS_ASSERTION(required
.GetUnit() == eCSSUnit_Ident
,
324 "bad required value");
325 NS_ASSERTION(mFeature
->mRangeType
== nsMediaFeature::eMinMaxNotAllowed
,
327 cmp
= !(actual
== required
); // string comparison
332 case nsMediaExpression::eMin
:
334 case nsMediaExpression::eMax
:
336 case nsMediaExpression::eEqual
:
339 NS_NOTREACHED("unexpected mRange");
344 nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression
* aExpression
,
345 PRBool aExpressionMatches
)
347 const nsMediaFeature
*feature
= aExpression
->mFeature
;
348 FeatureEntry
*entry
= nsnull
;
349 for (PRUint32 i
= 0; i
< mFeatureCache
.Length(); ++i
) {
350 if (mFeatureCache
[i
].mFeature
== feature
) {
351 entry
= &mFeatureCache
[i
];
356 entry
= mFeatureCache
.AppendElement();
358 return; /* out of memory */
360 entry
->mFeature
= feature
;
363 ExpressionEntry eentry
= { *aExpression
, aExpressionMatches
};
364 entry
->mExpressions
.AppendElement(eentry
);
368 nsMediaQueryResultCacheKey::Matches(nsPresContext
* aPresContext
) const
370 if (aPresContext
->Medium() != mMedium
) {
374 for (PRUint32 i
= 0; i
< mFeatureCache
.Length(); ++i
) {
375 const FeatureEntry
*entry
= &mFeatureCache
[i
];
378 (entry
->mFeature
->mGetter
)(aPresContext
, entry
->mFeature
, actual
);
379 NS_ENSURE_SUCCESS(rv
, PR_FALSE
); // any better ideas?
381 for (PRUint32 j
= 0; j
< entry
->mExpressions
.Length(); ++j
) {
382 const ExpressionEntry
&eentry
= entry
->mExpressions
[j
];
383 if (eentry
.mExpression
.Matches(aPresContext
, actual
) !=
384 eentry
.mExpressionMatches
) {
394 nsMediaQuery::AppendToString(nsAString
& aString
) const
396 if (mHadUnknownExpression
) {
397 aString
.AppendLiteral("not all");
401 NS_ASSERTION(!mNegated
|| !mHasOnly
, "can't have not and only");
402 NS_ASSERTION(!mTypeOmitted
|| (!mNegated
&& !mHasOnly
),
403 "can't have not or only when type is omitted");
406 aString
.AppendLiteral("not ");
407 } else if (mHasOnly
) {
408 aString
.AppendLiteral("only ");
410 aString
.Append(nsDependentAtomString(mMediaType
));
413 for (PRUint32 i
= 0, i_end
= mExpressions
.Length(); i
< i_end
; ++i
) {
414 if (i
> 0 || !mTypeOmitted
)
415 aString
.AppendLiteral(" and ");
416 aString
.AppendLiteral("(");
418 const nsMediaExpression
&expr
= mExpressions
[i
];
419 if (expr
.mRange
== nsMediaExpression::eMin
) {
420 aString
.AppendLiteral("min-");
421 } else if (expr
.mRange
== nsMediaExpression::eMax
) {
422 aString
.AppendLiteral("max-");
425 const nsMediaFeature
*feature
= expr
.mFeature
;
426 aString
.Append(nsDependentAtomString(*feature
->mName
));
428 if (expr
.mValue
.GetUnit() != eCSSUnit_Null
) {
429 aString
.AppendLiteral(": ");
430 switch (feature
->mValueType
) {
431 case nsMediaFeature::eLength
:
432 NS_ASSERTION(expr
.mValue
.IsLengthUnit(), "bad unit");
433 // Use 'width' as a property that takes length values
434 // written in the normal way.
435 expr
.mValue
.AppendToString(eCSSProperty_width
, aString
);
437 case nsMediaFeature::eInteger
:
438 case nsMediaFeature::eBoolInteger
:
439 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Integer
,
441 // Use 'z-index' as a property that takes integer values
442 // written without anything extra.
443 expr
.mValue
.AppendToString(eCSSProperty_z_index
, aString
);
445 case nsMediaFeature::eFloat
:
447 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Number
,
449 // Use 'line-height' as a property that takes float values
450 // written in the normal way.
451 expr
.mValue
.AppendToString(eCSSProperty_line_height
, aString
);
454 case nsMediaFeature::eIntRatio
:
456 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Array
,
458 nsCSSValue::Array
*array
= expr
.mValue
.GetArrayValue();
459 NS_ASSERTION(array
->Count() == 2, "unexpected length");
460 NS_ASSERTION(array
->Item(0).GetUnit() == eCSSUnit_Integer
,
462 NS_ASSERTION(array
->Item(1).GetUnit() == eCSSUnit_Integer
,
464 array
->Item(0).AppendToString(eCSSProperty_z_index
, aString
);
465 aString
.AppendLiteral("/");
466 array
->Item(1).AppendToString(eCSSProperty_z_index
, aString
);
469 case nsMediaFeature::eResolution
:
471 aString
.AppendFloat(expr
.mValue
.GetFloatValue());
472 if (expr
.mValue
.GetUnit() == eCSSUnit_Inch
) {
473 aString
.AppendLiteral("dpi");
475 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Centimeter
,
477 aString
.AppendLiteral("dpcm");
481 case nsMediaFeature::eEnumerated
:
482 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Enumerated
,
485 nsCSSProps::ValueToKeyword(expr
.mValue
.GetIntValue(),
486 feature
->mData
.mKeywordTable
),
489 case nsMediaFeature::eIdent
:
490 NS_ASSERTION(expr
.mValue
.GetUnit() == eCSSUnit_Ident
,
492 aString
.Append(expr
.mValue
.GetStringBufferValue());
497 aString
.AppendLiteral(")");
502 nsMediaQuery::Clone() const
504 nsAutoPtr
<nsMediaQuery
> result(new nsMediaQuery(*this));
505 NS_ENSURE_TRUE(result
&&
506 result
->mExpressions
.Length() == mExpressions
.Length(),
508 return result
.forget();
512 nsMediaQuery::Matches(nsPresContext
* aPresContext
,
513 nsMediaQueryResultCacheKey
& aKey
) const
515 if (mHadUnknownExpression
)
519 mMediaType
== aPresContext
->Medium() || mMediaType
== nsGkAtoms::all
;
520 for (PRUint32 i
= 0, i_end
= mExpressions
.Length(); match
&& i
< i_end
; ++i
) {
521 const nsMediaExpression
&expr
= mExpressions
[i
];
524 (expr
.mFeature
->mGetter
)(aPresContext
, expr
.mFeature
, actual
);
525 NS_ENSURE_SUCCESS(rv
, PR_FALSE
); // any better ideas?
527 match
= expr
.Matches(aPresContext
, actual
);
528 aKey
.AddExpression(&expr
, match
);
531 return match
== !mNegated
;
534 DOMCI_DATA(MediaList
, nsMediaList
)
536 NS_INTERFACE_MAP_BEGIN(nsMediaList
)
537 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList
)
538 NS_INTERFACE_MAP_ENTRY(nsISupports
)
539 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MediaList
)
542 NS_IMPL_ADDREF(nsMediaList
)
543 NS_IMPL_RELEASE(nsMediaList
)
546 nsMediaList::nsMediaList()
548 , mStyleSheet(nsnull
)
552 nsMediaList::~nsMediaList()
557 nsMediaList::GetText(nsAString
& aMediaText
)
559 aMediaText
.Truncate();
561 if (mArray
.Length() == 0 && !mIsEmpty
) {
562 aMediaText
.AppendLiteral("not all");
565 for (PRInt32 i
= 0, i_end
= mArray
.Length(); i
< i_end
; ++i
) {
566 nsMediaQuery
* query
= mArray
[i
];
567 NS_ENSURE_TRUE(query
, NS_ERROR_FAILURE
);
569 query
->AppendToString(aMediaText
);
572 aMediaText
.AppendLiteral(", ");
579 // XXXbz this is so ill-defined in the spec, it's not clear quite what
580 // it should be doing....
582 nsMediaList::SetText(const nsAString
& aMediaText
)
585 NS_ENSURE_TRUE(parser
, NS_ERROR_OUT_OF_MEMORY
);
587 PRBool htmlMode
= PR_FALSE
;
589 nsCOMPtr
<nsIDOMNode
> node
;
590 mStyleSheet
->GetOwnerNode(getter_AddRefs(node
));
594 return parser
.ParseMediaList(nsString(aMediaText
), nsnull
, 0,
599 nsMediaList::Matches(nsPresContext
* aPresContext
,
600 nsMediaQueryResultCacheKey
& aKey
)
602 for (PRInt32 i
= 0, i_end
= mArray
.Length(); i
< i_end
; ++i
) {
603 if (mArray
[i
]->Matches(aPresContext
, aKey
)) {
611 nsMediaList::SetStyleSheet(nsCSSStyleSheet
*aSheet
)
613 NS_ASSERTION(aSheet
== mStyleSheet
|| !aSheet
|| !mStyleSheet
,
614 "multiple style sheets competing for one media list");
615 mStyleSheet
= aSheet
;
620 nsMediaList::Clone(nsMediaList
** aResult
)
622 nsRefPtr
<nsMediaList
> result
= new nsMediaList();
623 if (!result
|| !result
->mArray
.AppendElements(mArray
.Length()))
624 return NS_ERROR_OUT_OF_MEMORY
;
625 for (PRInt32 i
= 0, i_end
= mArray
.Length(); i
< i_end
; ++i
) {
626 if (!(result
->mArray
[i
] = mArray
[i
]->Clone())) {
627 return NS_ERROR_OUT_OF_MEMORY
;
630 result
->mIsEmpty
= mIsEmpty
;
631 NS_ADDREF(*aResult
= result
);
636 nsMediaList::GetMediaText(nsAString
& aMediaText
)
638 return GetText(aMediaText
);
641 // "sheet" should be an nsCSSStyleSheet and "doc" should be an
642 // nsCOMPtr<nsIDocument>
643 #define BEGIN_MEDIA_CHANGE(sheet, doc) \
645 doc = sheet->GetOwningDocument(); \
647 mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, PR_TRUE); \
649 rv = sheet->WillDirty(); \
650 NS_ENSURE_SUCCESS(rv, rv); \
653 #define END_MEDIA_CHANGE(sheet, doc) \
657 /* XXXldb Pass something meaningful? */ \
659 doc->StyleRuleChanged(sheet, nsnull, nsnull); \
664 nsMediaList::SetMediaText(const nsAString
& aMediaText
)
667 nsCOMPtr
<nsIDocument
> doc
;
669 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
671 rv
= SetText(aMediaText
);
675 END_MEDIA_CHANGE(mStyleSheet
, doc
)
681 nsMediaList::GetLength(PRUint32
* aLength
)
683 NS_ENSURE_ARG_POINTER(aLength
);
685 *aLength
= mArray
.Length();
690 nsMediaList::Item(PRUint32 aIndex
, nsAString
& aReturn
)
692 PRInt32 index
= aIndex
;
693 if (0 <= index
&& index
< Count()) {
694 nsMediaQuery
* query
= mArray
[index
];
695 NS_ENSURE_TRUE(query
, NS_ERROR_FAILURE
);
698 query
->AppendToString(aReturn
);
700 SetDOMStringToNull(aReturn
);
707 nsMediaList::DeleteMedium(const nsAString
& aOldMedium
)
710 nsCOMPtr
<nsIDocument
> doc
;
712 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
714 rv
= Delete(aOldMedium
);
718 END_MEDIA_CHANGE(mStyleSheet
, doc
)
724 nsMediaList::AppendMedium(const nsAString
& aNewMedium
)
727 nsCOMPtr
<nsIDocument
> doc
;
729 BEGIN_MEDIA_CHANGE(mStyleSheet
, doc
)
731 rv
= Append(aNewMedium
);
735 END_MEDIA_CHANGE(mStyleSheet
, doc
)
741 nsMediaList::Delete(const nsAString
& aOldMedium
)
743 if (aOldMedium
.IsEmpty())
744 return NS_ERROR_DOM_NOT_FOUND_ERR
;
746 for (PRInt32 i
= 0, i_end
= mArray
.Length(); i
< i_end
; ++i
) {
747 nsMediaQuery
* query
= mArray
[i
];
748 NS_ENSURE_TRUE(query
, NS_ERROR_FAILURE
);
751 query
->AppendToString(buf
);
752 if (buf
== aOldMedium
) {
753 mArray
.RemoveElementAt(i
);
758 return NS_ERROR_DOM_NOT_FOUND_ERR
;
762 nsMediaList::Append(const nsAString
& aNewMedium
)
764 if (aNewMedium
.IsEmpty())
765 return NS_ERROR_DOM_NOT_FOUND_ERR
;
770 nsTArray
<nsAutoPtr
<nsMediaQuery
> > buf
;
774 mArray
.SwapElements(buf
);
775 NS_ASSERTION(ok
, "SwapElements should never fail when neither array "
778 if (mArray
.Length() == 1) {
779 nsMediaQuery
*query
= mArray
[0].forget();
780 if (!buf
.AppendElement(query
)) {
782 rv
= NS_ERROR_OUT_OF_MEMORY
;
788 mArray
.SwapElements(buf
);
789 NS_ASSERTION(ok
, "SwapElements should never fail when neither array "
794 // -------------------------------
795 // CSS Style Sheet Inner Data Container
799 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheet
* aPrimarySheet
)
803 , mPrincipalSet(PR_FALSE
)
806 MOZ_COUNT_CTOR(nsCSSStyleSheetInner
);
807 mSheets
.AppendElement(aPrimarySheet
);
809 mPrincipal
= do_CreateInstance("@mozilla.org/nullprincipal;1");
812 static PRBool
SetStyleSheetReference(nsICSSRule
* aRule
, void* aSheet
)
815 aRule
->SetStyleSheet(static_cast<nsCSSStyleSheet
*>(aSheet
));
821 CloneRuleInto(nsICSSRule
* aRule
, void* aArray
)
823 nsCOMPtr
<nsICSSRule
> clone
= aRule
->Clone();
824 static_cast<nsCOMArray
<nsICSSRule
>*>(aArray
)->AppendObject(clone
);
828 struct ChildSheetListBuilder
{
829 nsRefPtr
<nsCSSStyleSheet
>* sheetSlot
;
830 nsCSSStyleSheet
* parent
;
832 void SetParentLinks(nsCSSStyleSheet
* aSheet
) {
833 aSheet
->mParent
= parent
;
834 aSheet
->SetOwningDocument(parent
->mDocument
);
837 static void ReparentChildList(nsCSSStyleSheet
* aPrimarySheet
,
838 nsCSSStyleSheet
* aFirstChild
)
840 for (nsCSSStyleSheet
*child
= aFirstChild
; child
; child
= child
->mNext
) {
841 child
->mParent
= aPrimarySheet
;
842 child
->SetOwningDocument(aPrimarySheet
->mDocument
);
848 nsCSSStyleSheet::RebuildChildList(nsICSSRule
* aRule
, void* aBuilder
)
850 PRInt32 type
= aRule
->GetType();
851 if (type
< nsICSSRule::IMPORT_RULE
) {
852 // Keep going till we get to the import rules.
856 if (type
!= nsICSSRule::IMPORT_RULE
) {
857 // We're past all the import rules; stop the enumeration.
861 ChildSheetListBuilder
* builder
=
862 static_cast<ChildSheetListBuilder
*>(aBuilder
);
864 // XXXbz We really need to decomtaminate all this stuff. Is there a reason
865 // that I can't just QI to nsICSSImportRule and get an nsCSSStyleSheet
867 nsCOMPtr
<nsIDOMCSSImportRule
> importRule(do_QueryInterface(aRule
));
868 NS_ASSERTION(importRule
, "GetType lied");
870 nsCOMPtr
<nsIDOMCSSStyleSheet
> childSheet
;
871 importRule
->GetStyleSheet(getter_AddRefs(childSheet
));
873 // Have to do this QI to be safe, since XPConnect can fake
874 // nsIDOMCSSStyleSheets
875 nsRefPtr
<nsCSSStyleSheet
> cssSheet
= do_QueryObject(childSheet
);
880 (*builder
->sheetSlot
) = cssSheet
;
881 builder
->SetParentLinks(*builder
->sheetSlot
);
882 builder
->sheetSlot
= &(*builder
->sheetSlot
)->mNext
;
886 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner
& aCopy
,
887 nsCSSStyleSheet
* aPrimarySheet
)
889 mSheetURI(aCopy
.mSheetURI
),
890 mOriginalSheetURI(aCopy
.mOriginalSheetURI
),
891 mBaseURI(aCopy
.mBaseURI
),
892 mPrincipal(aCopy
.mPrincipal
),
893 mComplete(aCopy
.mComplete
)
895 , mPrincipalSet(aCopy
.mPrincipalSet
)
898 MOZ_COUNT_CTOR(nsCSSStyleSheetInner
);
899 AddSheet(aPrimarySheet
);
900 aCopy
.mOrderedRules
.EnumerateForwards(CloneRuleInto
, &mOrderedRules
);
901 mOrderedRules
.EnumerateForwards(SetStyleSheetReference
, aPrimarySheet
);
903 ChildSheetListBuilder builder
= { &mFirstChild
, aPrimarySheet
};
904 mOrderedRules
.EnumerateForwards(nsCSSStyleSheet::RebuildChildList
, &builder
);
909 nsCSSStyleSheetInner::~nsCSSStyleSheetInner()
911 MOZ_COUNT_DTOR(nsCSSStyleSheetInner
);
912 mOrderedRules
.EnumerateForwards(SetStyleSheetReference
, nsnull
);
915 nsCSSStyleSheetInner
*
916 nsCSSStyleSheetInner::CloneFor(nsCSSStyleSheet
* aPrimarySheet
)
918 return new nsCSSStyleSheetInner(*this, aPrimarySheet
);
922 nsCSSStyleSheetInner::AddSheet(nsCSSStyleSheet
* aSheet
)
924 mSheets
.AppendElement(aSheet
);
928 nsCSSStyleSheetInner::RemoveSheet(nsCSSStyleSheet
* aSheet
)
930 if (1 == mSheets
.Length()) {
931 NS_ASSERTION(aSheet
== mSheets
.ElementAt(0), "bad parent");
935 if (aSheet
== mSheets
.ElementAt(0)) {
936 mSheets
.RemoveElementAt(0);
937 NS_ASSERTION(mSheets
.Length(), "no parents");
938 mOrderedRules
.EnumerateForwards(SetStyleSheetReference
,
939 mSheets
.ElementAt(0));
941 ChildSheetListBuilder::ReparentChildList(mSheets
[0], mFirstChild
);
944 mSheets
.RemoveElement(aSheet
);
949 AddNamespaceRuleToMap(nsICSSRule
* aRule
, nsXMLNameSpaceMap
* aMap
)
951 NS_ASSERTION(aRule
->GetType() == nsICSSRule::NAMESPACE_RULE
, "Bogus rule type");
953 nsCOMPtr
<nsICSSNameSpaceRule
> nameSpaceRule
= do_QueryInterface(aRule
);
955 nsCOMPtr
<nsIAtom
> prefix
;
956 nsAutoString urlSpec
;
957 nameSpaceRule
->GetPrefix(*getter_AddRefs(prefix
));
958 nameSpaceRule
->GetURLSpec(urlSpec
);
960 aMap
->AddPrefix(prefix
, urlSpec
);
964 CreateNameSpace(nsICSSRule
* aRule
, void* aNameSpacePtr
)
966 PRInt32 type
= aRule
->GetType();
967 if (nsICSSRule::NAMESPACE_RULE
== type
) {
968 AddNamespaceRuleToMap(aRule
,
969 static_cast<nsXMLNameSpaceMap
*>(aNameSpacePtr
));
972 // stop if not namespace, import or charset because namespace can't follow
974 return (nsICSSRule::CHARSET_RULE
== type
|| nsICSSRule::IMPORT_RULE
== type
);
978 nsCSSStyleSheetInner::RebuildNameSpaces()
980 // Just nuke our existing namespace map, if any
981 if (NS_SUCCEEDED(CreateNamespaceMap())) {
982 mOrderedRules
.EnumerateForwards(CreateNameSpace
, mNameSpaceMap
);
987 nsCSSStyleSheetInner::CreateNamespaceMap()
989 mNameSpaceMap
= nsXMLNameSpaceMap::Create(PR_FALSE
);
990 NS_ENSURE_TRUE(mNameSpaceMap
, NS_ERROR_OUT_OF_MEMORY
);
991 // Override the default namespace map behavior for the null prefix to
992 // return the wildcard namespace instead of the null namespace.
993 mNameSpaceMap
->AddPrefix(nsnull
, kNameSpaceID_Unknown
);
997 // -------------------------------
1001 nsCSSStyleSheet::nsCSSStyleSheet()
1005 mRuleCollection(nsnull
),
1007 mOwningNode(nsnull
),
1008 mDisabled(PR_FALSE
),
1010 mRuleProcessors(nsnull
)
1013 mInner
= new nsCSSStyleSheetInner(this);
1016 nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet
& aCopy
,
1017 nsCSSStyleSheet
* aParentToUse
,
1018 nsICSSImportRule
* aOwnerRuleToUse
,
1019 nsIDocument
* aDocumentToUse
,
1020 nsIDOMNode
* aOwningNodeToUse
)
1021 : mTitle(aCopy
.mTitle
),
1022 mParent(aParentToUse
),
1023 mOwnerRule(aOwnerRuleToUse
),
1024 mRuleCollection(nsnull
), // re-created lazily
1025 mDocument(aDocumentToUse
),
1026 mOwningNode(aOwningNodeToUse
),
1027 mDisabled(aCopy
.mDisabled
),
1029 mInner(aCopy
.mInner
),
1030 mRuleProcessors(nsnull
)
1033 mInner
->AddSheet(this);
1035 if (aCopy
.mRuleCollection
&&
1036 aCopy
.mRuleCollection
->mRulesAccessed
) { // CSSOM's been there, force full copy now
1037 NS_ASSERTION(mInner
->mComplete
, "Why have rules been accessed on an incomplete sheet?");
1038 // FIXME: handle failure?
1039 EnsureUniqueInner();
1043 // XXX This is wrong; we should be keeping @import rules and
1045 aCopy
.mMedia
->Clone(getter_AddRefs(mMedia
));
1049 nsCSSStyleSheet::~nsCSSStyleSheet()
1051 for (nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1053 child
= child
->mNext
) {
1054 // XXXbz this is a little bogus; see the XXX comment where we
1055 // declare mFirstChild.
1056 if (child
->mParent
== this) {
1057 child
->mParent
= nsnull
;
1058 child
->mDocument
= nsnull
;
1061 if (nsnull
!= mRuleCollection
) {
1062 mRuleCollection
->DropReference();
1063 NS_RELEASE(mRuleCollection
);
1066 mMedia
->SetStyleSheet(nsnull
);
1069 mInner
->RemoveSheet(this);
1070 // XXX The document reference is not reference counted and should
1071 // not be released. The document will let us know when it is going
1073 if (mRuleProcessors
) {
1074 NS_ASSERTION(mRuleProcessors
->Length() == 0, "destructing sheet with rule processor reference");
1075 delete mRuleProcessors
; // weak refs, should be empty here anyway
1080 DOMCI_DATA(CSSStyleSheet
, nsCSSStyleSheet
)
1082 // QueryInterface implementation for nsCSSStyleSheet
1083 NS_INTERFACE_MAP_BEGIN(nsCSSStyleSheet
)
1084 NS_INTERFACE_MAP_ENTRY(nsIStyleSheet
)
1085 NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet
)
1086 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet
)
1087 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver
)
1088 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIStyleSheet
)
1089 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleSheet
)
1090 if (aIID
.Equals(NS_GET_IID(nsCSSStyleSheet
)))
1091 foundInterface
= reinterpret_cast<nsISupports
*>(this);
1093 NS_INTERFACE_MAP_END
1096 NS_IMPL_ADDREF(nsCSSStyleSheet
)
1097 NS_IMPL_RELEASE(nsCSSStyleSheet
)
1101 nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor
* aProcessor
)
1103 if (! mRuleProcessors
) {
1104 mRuleProcessors
= new nsAutoTArray
<nsCSSRuleProcessor
*, 8>();
1105 if (!mRuleProcessors
)
1106 return NS_ERROR_OUT_OF_MEMORY
;
1108 NS_ASSERTION(mRuleProcessors
->NoIndex
== mRuleProcessors
->IndexOf(aProcessor
),
1109 "processor already registered");
1110 mRuleProcessors
->AppendElement(aProcessor
); // weak ref
1115 nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor
* aProcessor
)
1117 if (!mRuleProcessors
)
1118 return NS_ERROR_FAILURE
;
1119 return mRuleProcessors
->RemoveElement(aProcessor
)
1126 nsCSSStyleSheet::SetURIs(nsIURI
* aSheetURI
, nsIURI
* aOriginalSheetURI
,
1129 NS_PRECONDITION(aSheetURI
&& aBaseURI
, "null ptr");
1131 NS_ASSERTION(mInner
->mOrderedRules
.Count() == 0 && !mInner
->mComplete
,
1132 "Can't call SetURL on sheets that are complete or have rules");
1134 mInner
->mSheetURI
= aSheetURI
;
1135 mInner
->mOriginalSheetURI
= aOriginalSheetURI
;
1136 mInner
->mBaseURI
= aBaseURI
;
1140 nsCSSStyleSheet::SetPrincipal(nsIPrincipal
* aPrincipal
)
1142 NS_PRECONDITION(!mInner
->mPrincipalSet
,
1143 "Should have an inner whose principal has not yet been set");
1145 mInner
->mPrincipal
= aPrincipal
;
1147 mInner
->mPrincipalSet
= PR_TRUE
;
1152 /* virtual */ nsIURI
*
1153 nsCSSStyleSheet::GetSheetURI() const
1155 return mInner
->mSheetURI
;
1158 /* virtual */ nsIURI
*
1159 nsCSSStyleSheet::GetBaseURI() const
1161 return mInner
->mBaseURI
;
1165 nsCSSStyleSheet::GetType(nsString
& aType
) const
1167 aType
.AssignLiteral("text/css");
1171 nsCSSStyleSheet::UseForPresentation(nsPresContext
* aPresContext
,
1172 nsMediaQueryResultCacheKey
& aKey
) const
1175 return mMedia
->Matches(aPresContext
, aKey
);
1182 nsCSSStyleSheet::SetMedia(nsMediaList
* aMedia
)
1187 /* virtual */ PRBool
1188 nsCSSStyleSheet::HasRules() const
1190 return StyleRuleCount() != 0;
1193 /* virtual */ PRBool
1194 nsCSSStyleSheet::IsApplicable() const
1196 return !mDisabled
&& mInner
->mComplete
;
1200 nsCSSStyleSheet::SetEnabled(PRBool aEnabled
)
1202 // Internal method, so callers must handle BeginUpdate/EndUpdate
1203 PRBool oldDisabled
= mDisabled
;
1204 mDisabled
= !aEnabled
;
1206 if (mInner
->mComplete
&& oldDisabled
!= mDisabled
) {
1207 ClearRuleCascades();
1210 mDocument
->SetStyleSheetApplicableState(this, !mDisabled
);
1215 /* virtual */ PRBool
1216 nsCSSStyleSheet::IsComplete() const
1218 return mInner
->mComplete
;
1222 nsCSSStyleSheet::SetComplete()
1224 NS_ASSERTION(!mDirty
, "Can't set a dirty sheet complete!");
1225 mInner
->mComplete
= PR_TRUE
;
1226 if (mDocument
&& !mDisabled
) {
1227 // Let the document know
1228 mDocument
->BeginUpdate(UPDATE_STYLE
);
1229 mDocument
->SetStyleSheetApplicableState(this, PR_TRUE
);
1230 mDocument
->EndUpdate(UPDATE_STYLE
);
1234 /* virtual */ nsIStyleSheet
*
1235 nsCSSStyleSheet::GetParentSheet() const
1240 /* virtual */ nsIDocument
*
1241 nsCSSStyleSheet::GetOwningDocument() const
1247 nsCSSStyleSheet::SetOwningDocument(nsIDocument
* aDocument
)
1248 { // not ref counted
1249 mDocument
= aDocument
;
1250 // Now set the same document on all our child sheets....
1251 // XXXbz this is a little bogus; see the XXX comment where we
1252 // declare mFirstChild.
1253 for (nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1254 child
; child
= child
->mNext
) {
1255 if (child
->mParent
== this) {
1256 child
->SetOwningDocument(aDocument
);
1261 /* virtual */ PRUint64
1262 nsCSSStyleSheet::FindOwningWindowID() const
1264 PRUint64 windowID
= 0;
1266 windowID
= mDocument
->OuterWindowID();
1269 if (windowID
== 0 && mOwningNode
) {
1270 nsCOMPtr
<nsIContent
> node
= do_QueryInterface(mOwningNode
);
1272 nsIDocument
* doc
= node
->GetOwnerDoc();
1274 windowID
= doc
->OuterWindowID();
1279 if (windowID
== 0 && mOwnerRule
) {
1280 nsCOMPtr
<nsIStyleSheet
> sheet
= mOwnerRule
->GetStyleSheet();
1282 nsRefPtr
<nsCSSStyleSheet
> cssSheet
= do_QueryObject(sheet
);
1284 windowID
= cssSheet
->FindOwningWindowID();
1289 if (windowID
== 0 && mParent
) {
1290 windowID
= mParent
->FindOwningWindowID();
1297 nsCSSStyleSheet::AppendStyleSheet(nsCSSStyleSheet
* aSheet
)
1299 NS_PRECONDITION(nsnull
!= aSheet
, "null arg");
1301 if (NS_SUCCEEDED(WillDirty())) {
1302 nsRefPtr
<nsCSSStyleSheet
>* tail
= &mInner
->mFirstChild
;
1304 tail
= &(*tail
)->mNext
;
1308 // This is not reference counted. Our parent tells us when
1310 aSheet
->mParent
= this;
1311 aSheet
->mDocument
= mDocument
;
1317 nsCSSStyleSheet::InsertStyleSheetAt(nsCSSStyleSheet
* aSheet
, PRInt32 aIndex
)
1319 NS_PRECONDITION(nsnull
!= aSheet
, "null arg");
1321 if (NS_SUCCEEDED(WillDirty())) {
1322 nsRefPtr
<nsCSSStyleSheet
>* tail
= &mInner
->mFirstChild
;
1323 while (*tail
&& aIndex
) {
1325 tail
= &(*tail
)->mNext
;
1327 aSheet
->mNext
= *tail
;
1330 // This is not reference counted. Our parent tells us when
1332 aSheet
->mParent
= this;
1333 aSheet
->mDocument
= mDocument
;
1339 nsCSSStyleSheet::PrependStyleRule(nsICSSRule
* aRule
)
1341 NS_PRECONDITION(nsnull
!= aRule
, "null arg");
1343 if (NS_SUCCEEDED(WillDirty())) {
1344 mInner
->mOrderedRules
.InsertObjectAt(aRule
, 0);
1345 aRule
->SetStyleSheet(this);
1348 if (nsICSSRule::NAMESPACE_RULE
== aRule
->GetType()) {
1349 // no api to prepend a namespace (ugh), release old ones and re-create them all
1350 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 if (nsICSSRule::NAMESPACE_RULE
== aRule
->GetType()) {
1366 nsresult rv
= RegisterNamespaceRule(aRule
);
1367 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
1368 "RegisterNamespaceRule returned error");
1374 nsCSSStyleSheet::ReplaceStyleRule(nsICSSRule
* aOld
, nsICSSRule
* aNew
)
1376 NS_PRECONDITION(mInner
->mOrderedRules
.Count() != 0, "can't have old rule");
1377 NS_PRECONDITION(mInner
->mComplete
, "No replacing in an incomplete sheet!");
1379 if (NS_SUCCEEDED(WillDirty())) {
1380 PRInt32 index
= mInner
->mOrderedRules
.IndexOf(aOld
);
1381 if (NS_UNLIKELY(index
== -1)) {
1382 NS_NOTREACHED("Couldn't find old rule");
1385 mInner
->mOrderedRules
.ReplaceObjectAt(aNew
, index
);
1387 aNew
->SetStyleSheet(this);
1388 aOld
->SetStyleSheet(nsnull
);
1390 NS_ASSERTION(nsICSSRule::NAMESPACE_RULE
!= aNew
->GetType(), "not yet implemented");
1391 NS_ASSERTION(nsICSSRule::NAMESPACE_RULE
!= aOld
->GetType(), "not yet implemented");
1396 nsCSSStyleSheet::StyleRuleCount() const
1398 return mInner
->mOrderedRules
.Count();
1402 nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex
, nsICSSRule
*& aRule
) const
1404 // Important: If this function is ever made scriptable, we must add
1405 // a security check here. See GetCssRules below for an example.
1406 aRule
= mInner
->mOrderedRules
.SafeObjectAt(aIndex
);
1412 return NS_ERROR_ILLEGAL_VALUE
;
1416 nsCSSStyleSheet::StyleSheetCount() const
1418 // XXX Far from an ideal way to do this, but the hope is that
1419 // it won't be done too often. If it is, we might want to
1420 // consider storing the children in an array.
1423 const nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1426 child
= child
->mNext
;
1432 nsCSSStyleSheet::EnsureUniqueInnerResult
1433 nsCSSStyleSheet::EnsureUniqueInner()
1435 NS_ABORT_IF_FALSE(mInner
->mSheets
.Length() != 0,
1436 "unexpected number of outers");
1437 if (mInner
->mSheets
.Length() == 1) {
1438 return eUniqueInner_AlreadyUnique
;
1440 nsCSSStyleSheetInner
* clone
= mInner
->CloneFor(this);
1442 return eUniqueInner_CloneFailed
;
1444 mInner
->RemoveSheet(this);
1447 // otherwise the rule processor has pointers to the old rules
1448 ClearRuleCascades();
1450 return eUniqueInner_ClonedInner
;
1454 nsCSSStyleSheet::AppendAllChildSheets(nsTArray
<nsCSSStyleSheet
*>& aArray
)
1456 for (nsCSSStyleSheet
* child
= mInner
->mFirstChild
; child
;
1457 child
= child
->mNext
) {
1458 if (!aArray
.AppendElement(child
)) {
1465 already_AddRefed
<nsCSSStyleSheet
>
1466 nsCSSStyleSheet::Clone(nsCSSStyleSheet
* aCloneParent
,
1467 nsICSSImportRule
* aCloneOwnerRule
,
1468 nsIDocument
* aCloneDocument
,
1469 nsIDOMNode
* aCloneOwningNode
) const
1471 nsCSSStyleSheet
* clone
= new nsCSSStyleSheet(*this,
1476 NS_IF_ADDREF(clone
);
1482 ListRules(const nsCOMArray
<nsICSSRule
>& aRules
, FILE* aOut
, PRInt32 aIndent
)
1484 for (PRInt32 index
= aRules
.Count() - 1; index
>= 0; --index
) {
1485 aRules
.ObjectAt(index
)->List(aOut
, aIndent
);
1489 struct ListEnumData
{
1490 ListEnumData(FILE* aOut
, PRInt32 aIndent
)
1500 nsCSSStyleSheet::List(FILE* out
, PRInt32 aIndent
) const
1506 for (index
= aIndent
; --index
>= 0; ) fputs(" ", out
);
1508 fputs("CSS Style Sheet: ", out
);
1509 nsCAutoString urlSpec
;
1510 nsresult rv
= mInner
->mSheetURI
->GetSpec(urlSpec
);
1511 if (NS_SUCCEEDED(rv
) && !urlSpec
.IsEmpty()) {
1512 fputs(urlSpec
.get(), out
);
1516 fputs(" media: ", out
);
1517 nsAutoString buffer
;
1518 mMedia
->GetText(buffer
);
1519 fputs(NS_ConvertUTF16toUTF8(buffer
).get(), out
);
1523 for (const nsCSSStyleSheet
* child
= mInner
->mFirstChild
;
1525 child
= child
->mNext
) {
1526 child
->List(out
, aIndent
+ 1);
1529 fputs("Rules in source order:\n", out
);
1530 ListRules(mInner
->mOrderedRules
, out
, aIndent
);
1535 nsCSSStyleSheet::ClearRuleCascades()
1537 if (mRuleProcessors
) {
1538 nsCSSRuleProcessor
**iter
= mRuleProcessors
->Elements(),
1539 **end
= iter
+ mRuleProcessors
->Length();
1540 for(; iter
!= end
; ++iter
) {
1541 (*iter
)->ClearRuleCascades();
1545 nsCSSStyleSheet
* parent
= (nsCSSStyleSheet
*)mParent
;
1546 parent
->ClearRuleCascades();
1551 nsCSSStyleSheet::WillDirty()
1553 if (!mInner
->mComplete
) {
1558 if (EnsureUniqueInner() == eUniqueInner_CloneFailed
) {
1559 return NS_ERROR_OUT_OF_MEMORY
;
1565 nsCSSStyleSheet::DidDirty()
1567 ClearRuleCascades();
1572 nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() const
1574 // Get the security manager and do the subsumes check
1575 nsIScriptSecurityManager
*securityManager
=
1576 nsContentUtils::GetSecurityManager();
1578 nsCOMPtr
<nsIPrincipal
> subjectPrincipal
;
1579 securityManager
->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal
));
1581 if (!subjectPrincipal
) {
1582 return NS_ERROR_DOM_SECURITY_ERR
;
1586 nsresult rv
= subjectPrincipal
->Subsumes(mInner
->mPrincipal
, &subsumes
);
1587 NS_ENSURE_SUCCESS(rv
, rv
);
1593 if (!nsContentUtils::IsCallerTrustedForWrite()) {
1594 return NS_ERROR_DOM_SECURITY_ERR
;
1601 nsCSSStyleSheet::RegisterNamespaceRule(nsICSSRule
* aRule
)
1603 if (!mInner
->mNameSpaceMap
) {
1604 nsresult rv
= mInner
->CreateNamespaceMap();
1605 NS_ENSURE_SUCCESS(rv
, rv
);
1608 AddNamespaceRuleToMap(aRule
, mInner
->mNameSpaceMap
);
1612 // nsIDOMStyleSheet interface
1614 nsCSSStyleSheet::GetType(nsAString
& aType
)
1616 aType
.AssignLiteral("text/css");
1621 nsCSSStyleSheet::GetDisabled(PRBool
* aDisabled
)
1623 *aDisabled
= mDisabled
;
1628 nsCSSStyleSheet::SetDisabled(PRBool aDisabled
)
1630 // DOM method, so handle BeginUpdate/EndUpdate
1631 MOZ_AUTO_DOC_UPDATE(mDocument
, UPDATE_STYLE
, PR_TRUE
);
1632 nsCSSStyleSheet::SetEnabled(!aDisabled
);
1637 nsCSSStyleSheet::GetOwnerNode(nsIDOMNode
** aOwnerNode
)
1639 *aOwnerNode
= mOwningNode
;
1640 NS_IF_ADDREF(*aOwnerNode
);
1645 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet
** aParentStyleSheet
)
1647 NS_ENSURE_ARG_POINTER(aParentStyleSheet
);
1649 NS_IF_ADDREF(*aParentStyleSheet
= mParent
);
1655 nsCSSStyleSheet::GetHref(nsAString
& aHref
)
1657 if (mInner
->mOriginalSheetURI
) {
1659 mInner
->mOriginalSheetURI
->GetSpec(str
);
1660 CopyUTF8toUTF16(str
, aHref
);
1662 SetDOMStringToNull(aHref
);
1669 nsCSSStyleSheet::GetTitle(nsString
& aTitle
) const
1675 nsCSSStyleSheet::GetTitle(nsAString
& aTitle
)
1677 aTitle
.Assign(mTitle
);
1682 nsCSSStyleSheet::GetMedia(nsIDOMMediaList
** aMedia
)
1684 NS_ENSURE_ARG_POINTER(aMedia
);
1688 mMedia
= new nsMediaList();
1689 NS_ENSURE_TRUE(mMedia
, NS_ERROR_OUT_OF_MEMORY
);
1690 mMedia
->SetStyleSheet(this);
1700 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule
** aOwnerRule
)
1703 return mOwnerRule
->GetDOMRule(aOwnerRule
);
1706 *aOwnerRule
= nsnull
;
1711 nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList
** aCssRules
)
1713 // No doing this on incomplete sheets!
1714 if (!mInner
->mComplete
) {
1715 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1718 //-- Security check: Only scripts whose principal subsumes that of the
1719 // style sheet can access rule collections.
1720 nsresult rv
= SubjectSubsumesInnerPrincipal();
1721 NS_ENSURE_SUCCESS(rv
, rv
);
1723 // OK, security check passed, so get the rule collection
1724 if (nsnull
== mRuleCollection
) {
1725 mRuleCollection
= new CSSRuleListImpl(this);
1726 if (nsnull
== mRuleCollection
) {
1727 return NS_ERROR_OUT_OF_MEMORY
;
1729 NS_ADDREF(mRuleCollection
);
1732 *aCssRules
= mRuleCollection
;
1733 NS_ADDREF(mRuleCollection
);
1739 nsCSSStyleSheet::InsertRule(const nsAString
& aRule
,
1743 //-- Security check: Only scripts whose principal subsumes that of the
1744 // style sheet can modify rule collections.
1745 nsresult rv
= SubjectSubsumesInnerPrincipal();
1746 NS_ENSURE_SUCCESS(rv
, rv
);
1748 return InsertRuleInternal(aRule
, aIndex
, aReturn
);
1752 nsCSSStyleSheet::InsertRuleInternal(const nsAString
& aRule
,
1756 // No doing this if the sheet is not complete!
1757 if (!mInner
->mComplete
) {
1758 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1761 if (aRule
.IsEmpty()) {
1762 // Nothing to do here
1767 result
= WillDirty();
1768 if (NS_FAILED(result
))
1771 if (aIndex
> PRUint32(mInner
->mOrderedRules
.Count()))
1772 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
1774 NS_ASSERTION(PRUint32(mInner
->mOrderedRules
.Count()) <= PR_INT32_MAX
,
1775 "Too many style rules!");
1777 // Hold strong ref to the CSSLoader in case the document update
1778 // kills the document
1779 nsRefPtr
<css::Loader
> loader
;
1781 loader
= mDocument
->CSSLoader();
1782 NS_ASSERTION(loader
, "Document with no CSS loader!");
1785 nsCSSParser
css(loader
, this);
1787 return NS_ERROR_OUT_OF_MEMORY
;
1789 mozAutoDocUpdate
updateBatch(mDocument
, UPDATE_STYLE
, PR_TRUE
);
1791 nsCOMArray
<nsICSSRule
> rules
;
1792 result
= css
.ParseRule(aRule
, mInner
->mSheetURI
, mInner
->mBaseURI
,
1793 mInner
->mPrincipal
, rules
);
1794 if (NS_FAILED(result
))
1797 PRInt32 rulecount
= rules
.Count();
1798 if (rulecount
== 0) {
1799 // Since we know aRule was not an empty string, just throw
1800 return NS_ERROR_DOM_SYNTAX_ERR
;
1803 // Hierarchy checking. Just check the first and last rule in the list.
1805 // check that we're not inserting before a charset rule
1806 nsICSSRule
* nextRule
= mInner
->mOrderedRules
.SafeObjectAt(aIndex
);
1808 PRInt32 nextType
= nextRule
->GetType();
1809 if (nextType
== nsICSSRule::CHARSET_RULE
) {
1810 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1813 // check last rule in list
1814 nsICSSRule
* lastRule
= rules
.ObjectAt(rulecount
- 1);
1815 PRInt32 lastType
= lastRule
->GetType();
1817 if (nextType
== nsICSSRule::IMPORT_RULE
&&
1818 lastType
!= nsICSSRule::CHARSET_RULE
&&
1819 lastType
!= nsICSSRule::IMPORT_RULE
) {
1820 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1823 if (nextType
== nsICSSRule::NAMESPACE_RULE
&&
1824 lastType
!= nsICSSRule::CHARSET_RULE
&&
1825 lastType
!= nsICSSRule::IMPORT_RULE
&&
1826 lastType
!= nsICSSRule::NAMESPACE_RULE
) {
1827 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1831 // check first rule in list
1832 nsICSSRule
* firstRule
= rules
.ObjectAt(0);
1833 PRInt32 firstType
= firstRule
->GetType();
1835 if (firstType
== nsICSSRule::CHARSET_RULE
) { // no inserting charset at nonzero position
1836 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1839 nsICSSRule
* prevRule
= mInner
->mOrderedRules
.SafeObjectAt(aIndex
- 1);
1840 PRInt32 prevType
= prevRule
->GetType();
1842 if (firstType
== nsICSSRule::IMPORT_RULE
&&
1843 prevType
!= nsICSSRule::CHARSET_RULE
&&
1844 prevType
!= nsICSSRule::IMPORT_RULE
) {
1845 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1848 if (firstType
== nsICSSRule::NAMESPACE_RULE
&&
1849 prevType
!= nsICSSRule::CHARSET_RULE
&&
1850 prevType
!= nsICSSRule::IMPORT_RULE
&&
1851 prevType
!= nsICSSRule::NAMESPACE_RULE
) {
1852 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
1856 PRBool insertResult
= mInner
->mOrderedRules
.InsertObjectsAt(rules
, aIndex
);
1857 NS_ENSURE_TRUE(insertResult
, NS_ERROR_OUT_OF_MEMORY
);
1860 for (PRInt32 counter
= 0; counter
< rulecount
; counter
++) {
1861 nsICSSRule
* cssRule
= rules
.ObjectAt(counter
);
1862 cssRule
->SetStyleSheet(this);
1864 PRInt32 type
= cssRule
->GetType();
1865 if (type
== nsICSSRule::NAMESPACE_RULE
) {
1866 // XXXbz does this screw up when inserting a namespace rule before
1867 // another namespace rule that binds the same prefix to a different
1869 result
= RegisterNamespaceRule(cssRule
);
1870 NS_ENSURE_SUCCESS(result
, result
);
1873 // We don't notify immediately for @import rules, but rather when
1874 // the sheet the rule is importing is loaded
1875 PRBool notify
= PR_TRUE
;
1876 if (type
== nsICSSRule::IMPORT_RULE
) {
1877 nsCOMPtr
<nsIDOMCSSImportRule
> importRule(do_QueryInterface(cssRule
));
1878 NS_ASSERTION(importRule
, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
1879 nsCOMPtr
<nsIDOMCSSStyleSheet
> childSheet
;
1880 importRule
->GetStyleSheet(getter_AddRefs(childSheet
));
1885 if (mDocument
&& notify
) {
1886 mDocument
->StyleRuleAdded(this, cssRule
);
1895 nsCSSStyleSheet::DeleteRule(PRUint32 aIndex
)
1897 nsresult result
= NS_ERROR_DOM_INDEX_SIZE_ERR
;
1898 // No doing this if the sheet is not complete!
1899 if (!mInner
->mComplete
) {
1900 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
1903 //-- Security check: Only scripts whose principal subsumes that of the
1904 // style sheet can modify rule collections.
1905 nsresult rv
= SubjectSubsumesInnerPrincipal();
1906 NS_ENSURE_SUCCESS(rv
, rv
);
1908 // XXX TBI: handle @rule types
1909 mozAutoDocUpdate
updateBatch(mDocument
, UPDATE_STYLE
, PR_TRUE
);
1911 result
= WillDirty();
1913 if (NS_SUCCEEDED(result
)) {
1914 if (aIndex
>= PRUint32(mInner
->mOrderedRules
.Count()))
1915 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
1917 NS_ASSERTION(PRUint32(mInner
->mOrderedRules
.Count()) <= PR_INT32_MAX
,
1918 "Too many style rules!");
1920 // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
1921 nsCOMPtr
<nsICSSRule
> rule
= mInner
->mOrderedRules
.ObjectAt(aIndex
);
1923 mInner
->mOrderedRules
.RemoveObjectAt(aIndex
);
1924 rule
->SetStyleSheet(nsnull
);
1928 mDocument
->StyleRuleRemoved(this, rule
);
1937 nsCSSStyleSheet::DeleteRuleFromGroup(nsICSSGroupRule
* aGroup
, PRUint32 aIndex
)
1939 NS_ENSURE_ARG_POINTER(aGroup
);
1940 NS_ASSERTION(mInner
->mComplete
, "No deleting from an incomplete sheet!");
1942 nsCOMPtr
<nsICSSRule
> rule
;
1943 result
= aGroup
->GetStyleRuleAt(aIndex
, *getter_AddRefs(rule
));
1944 NS_ENSURE_SUCCESS(result
, result
);
1946 // check that the rule actually belongs to this sheet!
1947 nsCOMPtr
<nsIStyleSheet
> ruleSheet
= rule
->GetStyleSheet();
1948 if (this != ruleSheet
) {
1949 return NS_ERROR_INVALID_ARG
;
1952 mozAutoDocUpdate
updateBatch(mDocument
, UPDATE_STYLE
, PR_TRUE
);
1954 result
= WillDirty();
1955 NS_ENSURE_SUCCESS(result
, result
);
1957 result
= aGroup
->DeleteStyleRuleAt(aIndex
);
1958 NS_ENSURE_SUCCESS(result
, result
);
1960 rule
->SetStyleSheet(nsnull
);
1965 mDocument
->StyleRuleRemoved(this, rule
);
1972 nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString
& aRule
,
1973 nsICSSGroupRule
* aGroup
,
1978 NS_ASSERTION(mInner
->mComplete
, "No inserting into an incomplete sheet!");
1979 // check that the group actually belongs to this sheet!
1980 nsCOMPtr
<nsIStyleSheet
> groupSheet
= aGroup
->GetStyleSheet();
1981 if (this != groupSheet
) {
1982 return NS_ERROR_INVALID_ARG
;
1985 if (aRule
.IsEmpty()) {
1986 // Nothing to do here
1990 // Hold strong ref to the CSSLoader in case the document update
1991 // kills the document
1992 nsRefPtr
<css::Loader
> loader
;
1994 loader
= mDocument
->CSSLoader();
1995 NS_ASSERTION(loader
, "Document with no CSS loader!");
1998 nsCSSParser
css(loader
, this);
2000 return NS_ERROR_OUT_OF_MEMORY
;
2003 // parse and grab the rule
2004 mozAutoDocUpdate
updateBatch(mDocument
, UPDATE_STYLE
, PR_TRUE
);
2006 result
= WillDirty();
2007 NS_ENSURE_SUCCESS(result
, result
);
2009 nsCOMArray
<nsICSSRule
> rules
;
2010 result
= css
.ParseRule(aRule
, mInner
->mSheetURI
, mInner
->mBaseURI
,
2011 mInner
->mPrincipal
, rules
);
2012 NS_ENSURE_SUCCESS(result
, result
);
2014 PRInt32 rulecount
= rules
.Count();
2015 if (rulecount
== 0) {
2016 // Since we know aRule was not an empty string, just throw
2017 return NS_ERROR_DOM_SYNTAX_ERR
;
2022 for (counter
= 0; counter
< rulecount
; counter
++) {
2023 // Only rulesets are allowed in a group as of CSS2
2024 rule
= rules
.ObjectAt(counter
);
2025 if (rule
->GetType() != nsICSSRule::STYLE_RULE
) {
2026 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
;
2030 result
= aGroup
->InsertStyleRulesAt(aIndex
, rules
);
2031 NS_ENSURE_SUCCESS(result
, result
);
2033 for (counter
= 0; counter
< rulecount
; counter
++) {
2034 rule
= rules
.ObjectAt(counter
);
2037 mDocument
->StyleRuleAdded(this, rule
);
2046 nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule
* aGroup
,
2047 nsICSSRule
* aOld
, nsICSSRule
* aNew
)
2050 NS_PRECONDITION(mInner
->mComplete
, "No replacing in an incomplete sheet!");
2053 nsCOMPtr
<nsIStyleSheet
> groupSheet
= aGroup
->GetStyleSheet();
2054 NS_ASSERTION(this == groupSheet
, "group doesn't belong to this sheet");
2057 result
= WillDirty();
2058 NS_ENSURE_SUCCESS(result
, result
);
2060 result
= aGroup
->ReplaceStyleRule(aOld
, aNew
);
2065 // nsICSSLoaderObserver implementation
2067 nsCSSStyleSheet::StyleSheetLoaded(nsCSSStyleSheet
* aSheet
,
2068 PRBool aWasAlternate
,
2071 NS_ASSERTION(this == aSheet
->GetParentSheet(),
2072 "We are being notified of a sheet load for a sheet that is not our child!");
2074 if (mDocument
&& NS_SUCCEEDED(aStatus
)) {
2075 nsCOMPtr
<nsICSSImportRule
> ownerRule
= aSheet
->GetOwnerRule();
2077 mozAutoDocUpdate
updateBatch(mDocument
, UPDATE_STYLE
, PR_TRUE
);
2079 // XXXldb @import rules shouldn't even implement nsIStyleRule (but
2081 nsCOMPtr
<nsIStyleRule
> styleRule(do_QueryInterface(ownerRule
));
2083 mDocument
->StyleRuleAdded(this, styleRule
);
2089 /* virtual */ nsIURI
*
2090 nsCSSStyleSheet::GetOriginalURI() const
2092 return mInner
->mOriginalSheetURI
;
2096 NS_NewCSSStyleSheet(nsCSSStyleSheet
** aInstancePtrResult
)
2098 *aInstancePtrResult
= nsnull
;
2099 nsCSSStyleSheet
*it
= new nsCSSStyleSheet();
2102 return NS_ERROR_OUT_OF_MEMORY
;
2107 if (!it
->mInner
|| !it
->mInner
->mPrincipal
) {
2109 return NS_ERROR_OUT_OF_MEMORY
;
2112 *aInstancePtrResult
= it
;