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
5 : *
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/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org>
25 : * Pierre Phaneuf <pp@ludusdesign.com>
26 : * Daniel Glazman <glazman@netscape.com>
27 : *
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.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /* representation of a CSS style sheet */
43 :
44 : #include "nsCSSStyleSheet.h"
45 :
46 : #include "nsCRT.h"
47 : #include "nsIAtom.h"
48 : #include "nsCSSRuleProcessor.h"
49 : #include "mozilla/css/NameSpaceRule.h"
50 : #include "mozilla/css/GroupRule.h"
51 : #include "mozilla/css/ImportRule.h"
52 : #include "nsIMediaList.h"
53 : #include "nsIDocument.h"
54 : #include "nsPresContext.h"
55 : #include "nsGkAtoms.h"
56 : #include "nsString.h"
57 : #include "nsTArray.h"
58 : #include "nsIDOMCSSStyleSheet.h"
59 : #include "nsICSSRuleList.h"
60 : #include "nsIDOMMediaList.h"
61 : #include "nsIDOMNode.h"
62 : #include "nsDOMError.h"
63 : #include "nsCSSParser.h"
64 : #include "mozilla/css/Loader.h"
65 : #include "nsICSSLoaderObserver.h"
66 : #include "nsINameSpaceManager.h"
67 : #include "nsXMLNameSpaceMap.h"
68 : #include "nsCOMPtr.h"
69 : #include "nsContentUtils.h"
70 : #include "nsIScriptSecurityManager.h"
71 : #include "mozAutoDocUpdate.h"
72 : #include "mozilla/css/Declaration.h"
73 : #include "nsRuleNode.h"
74 : #include "nsMediaFeatures.h"
75 :
76 : namespace css = mozilla::css;
77 :
78 : // -------------------------------
79 : // Style Rule List for the DOM
80 : //
81 : class CSSRuleListImpl : public nsICSSRuleList
82 : {
83 : public:
84 : CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet);
85 :
86 : NS_DECL_ISUPPORTS
87 :
88 : // nsIDOMCSSRuleList interface
89 : NS_IMETHOD GetLength(PRUint32* aLength);
90 : NS_IMETHOD Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn);
91 :
92 : virtual nsIDOMCSSRule* GetItemAt(PRUint32 aIndex, nsresult* aResult);
93 :
94 0 : void DropReference() { mStyleSheet = nsnull; }
95 :
96 : protected:
97 : virtual ~CSSRuleListImpl();
98 :
99 : nsCSSStyleSheet* mStyleSheet;
100 : };
101 :
102 0 : CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet)
103 : {
104 : // Not reference counted to avoid circular references.
105 : // The style sheet will tell us when its going away.
106 0 : mStyleSheet = aStyleSheet;
107 0 : }
108 :
109 0 : CSSRuleListImpl::~CSSRuleListImpl()
110 : {
111 0 : }
112 :
113 : DOMCI_DATA(CSSRuleList, CSSRuleListImpl)
114 :
115 : // QueryInterface implementation for CSSRuleList
116 0 : NS_INTERFACE_MAP_BEGIN(CSSRuleListImpl)
117 0 : NS_INTERFACE_MAP_ENTRY(nsICSSRuleList)
118 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList)
119 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
120 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSRuleList)
121 0 : NS_INTERFACE_MAP_END
122 :
123 :
124 0 : NS_IMPL_ADDREF(CSSRuleListImpl)
125 0 : NS_IMPL_RELEASE(CSSRuleListImpl)
126 :
127 :
128 : NS_IMETHODIMP
129 0 : CSSRuleListImpl::GetLength(PRUint32* aLength)
130 : {
131 0 : if (nsnull != mStyleSheet) {
132 0 : PRInt32 count = mStyleSheet->StyleRuleCount();
133 0 : *aLength = (PRUint32)count;
134 : }
135 : else {
136 0 : *aLength = 0;
137 : }
138 :
139 0 : return NS_OK;
140 : }
141 :
142 : nsIDOMCSSRule*
143 0 : CSSRuleListImpl::GetItemAt(PRUint32 aIndex, nsresult* aResult)
144 : {
145 0 : nsresult result = NS_OK;
146 :
147 0 : if (mStyleSheet) {
148 : // ensure rules have correct parent
149 0 : if (mStyleSheet->EnsureUniqueInner() !=
150 : nsCSSStyleSheet::eUniqueInner_CloneFailed) {
151 0 : nsRefPtr<css::Rule> rule;
152 :
153 0 : result = mStyleSheet->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
154 0 : if (rule) {
155 0 : *aResult = NS_OK;
156 0 : return rule->GetDOMRule();
157 : }
158 0 : if (result == NS_ERROR_ILLEGAL_VALUE) {
159 0 : result = NS_OK; // per spec: "Return Value ... null if ... not a valid index."
160 : }
161 : }
162 : }
163 :
164 0 : *aResult = result;
165 0 : return nsnull;
166 : }
167 :
168 : NS_IMETHODIMP
169 0 : CSSRuleListImpl::Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn)
170 : {
171 : nsresult rv;
172 0 : nsIDOMCSSRule* rule = GetItemAt(aIndex, &rv);
173 0 : if (!rule) {
174 0 : *aReturn = nsnull;
175 :
176 0 : return rv;
177 : }
178 :
179 0 : NS_ADDREF(*aReturn = rule);
180 0 : return NS_OK;
181 : }
182 :
183 : template <class Numeric>
184 0 : PRInt32 DoCompare(Numeric a, Numeric b)
185 : {
186 0 : if (a == b)
187 0 : return 0;
188 0 : if (a < b)
189 0 : return -1;
190 0 : return 1;
191 : }
192 :
193 : bool
194 0 : nsMediaExpression::Matches(nsPresContext *aPresContext,
195 : const nsCSSValue& aActualValue) const
196 : {
197 0 : const nsCSSValue& actual = aActualValue;
198 0 : const nsCSSValue& required = mValue;
199 :
200 : // If we don't have the feature, the match fails.
201 0 : if (actual.GetUnit() == eCSSUnit_Null) {
202 0 : return false;
203 : }
204 :
205 : // If the expression had no value to match, the match succeeds,
206 : // unless the value is an integer 0 or a zero length.
207 0 : if (required.GetUnit() == eCSSUnit_Null) {
208 0 : if (actual.GetUnit() == eCSSUnit_Integer)
209 0 : return actual.GetIntValue() != 0;
210 0 : if (actual.IsLengthUnit())
211 0 : return actual.GetFloatValue() != 0;
212 0 : return true;
213 : }
214 :
215 0 : NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxAllowed ||
216 : mRange == nsMediaExpression::eEqual, "yikes");
217 : PRInt32 cmp; // -1 (actual < required)
218 : // 0 (actual == required)
219 : // 1 (actual > required)
220 0 : switch (mFeature->mValueType) {
221 : case nsMediaFeature::eLength:
222 : {
223 0 : NS_ASSERTION(actual.IsLengthUnit(), "bad actual value");
224 0 : NS_ASSERTION(required.IsLengthUnit(), "bad required value");
225 : nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont(
226 0 : aPresContext, actual);
227 : nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont(
228 0 : aPresContext, required);
229 0 : cmp = DoCompare(actualCoord, requiredCoord);
230 : }
231 0 : break;
232 : case nsMediaFeature::eInteger:
233 : case nsMediaFeature::eBoolInteger:
234 : {
235 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Integer,
236 : "bad actual value");
237 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Integer,
238 : "bad required value");
239 0 : NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
240 : actual.GetIntValue() == 0 || actual.GetIntValue() == 1,
241 : "bad actual bool integer value");
242 0 : NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger ||
243 : required.GetIntValue() == 0 || required.GetIntValue() == 1,
244 : "bad required bool integer value");
245 0 : cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
246 : }
247 0 : break;
248 : case nsMediaFeature::eFloat:
249 : {
250 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Number,
251 : "bad actual value");
252 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Number,
253 : "bad required value");
254 0 : cmp = DoCompare(actual.GetFloatValue(), required.GetFloatValue());
255 : }
256 0 : break;
257 : case nsMediaFeature::eIntRatio:
258 : {
259 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array &&
260 : actual.GetArrayValue()->Count() == 2 &&
261 : actual.GetArrayValue()->Item(0).GetUnit() ==
262 : eCSSUnit_Integer &&
263 : actual.GetArrayValue()->Item(1).GetUnit() ==
264 : eCSSUnit_Integer,
265 : "bad actual value");
266 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Array &&
267 : required.GetArrayValue()->Count() == 2 &&
268 : required.GetArrayValue()->Item(0).GetUnit() ==
269 : eCSSUnit_Integer &&
270 : required.GetArrayValue()->Item(1).GetUnit() ==
271 : eCSSUnit_Integer,
272 : "bad required value");
273 : // Convert to PRInt64 so we can multiply without worry. Note
274 : // that while the spec requires that both halves of |required|
275 : // be positive, the numerator or denominator of |actual| might
276 : // be zero (e.g., when testing 'aspect-ratio' on a 0-width or
277 : // 0-height iframe).
278 0 : PRInt64 actualNum = actual.GetArrayValue()->Item(0).GetIntValue(),
279 0 : actualDen = actual.GetArrayValue()->Item(1).GetIntValue(),
280 0 : requiredNum = required.GetArrayValue()->Item(0).GetIntValue(),
281 0 : requiredDen = required.GetArrayValue()->Item(1).GetIntValue();
282 0 : cmp = DoCompare(actualNum * requiredDen, requiredNum * actualDen);
283 : }
284 0 : break;
285 : case nsMediaFeature::eResolution:
286 : {
287 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch ||
288 : actual.GetUnit() == eCSSUnit_Centimeter,
289 : "bad actual value");
290 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch ||
291 : required.GetUnit() == eCSSUnit_Centimeter,
292 : "bad required value");
293 0 : float actualDPI = actual.GetFloatValue();
294 0 : if (actual.GetUnit() == eCSSUnit_Centimeter)
295 0 : actualDPI = actualDPI * 2.54f;
296 0 : float requiredDPI = required.GetFloatValue();
297 0 : if (required.GetUnit() == eCSSUnit_Centimeter)
298 0 : requiredDPI = requiredDPI * 2.54f;
299 0 : cmp = DoCompare(actualDPI, requiredDPI);
300 : }
301 0 : break;
302 : case nsMediaFeature::eEnumerated:
303 : {
304 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated,
305 : "bad actual value");
306 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated,
307 : "bad required value");
308 0 : NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed,
309 : "bad range"); // we asserted above about mRange
310 : // We don't really need DoCompare, but it doesn't hurt (and
311 : // maybe the compiler will condense this case with eInteger).
312 0 : cmp = DoCompare(actual.GetIntValue(), required.GetIntValue());
313 : }
314 0 : break;
315 : case nsMediaFeature::eIdent:
316 : {
317 0 : NS_ASSERTION(actual.GetUnit() == eCSSUnit_Ident,
318 : "bad actual value");
319 0 : NS_ASSERTION(required.GetUnit() == eCSSUnit_Ident,
320 : "bad required value");
321 0 : NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed,
322 : "bad range");
323 0 : cmp = !(actual == required); // string comparison
324 : }
325 0 : break;
326 : }
327 0 : switch (mRange) {
328 : case nsMediaExpression::eMin:
329 0 : return cmp != -1;
330 : case nsMediaExpression::eMax:
331 0 : return cmp != 1;
332 : case nsMediaExpression::eEqual:
333 0 : return cmp == 0;
334 : }
335 0 : NS_NOTREACHED("unexpected mRange");
336 0 : return false;
337 : }
338 :
339 : void
340 0 : nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression* aExpression,
341 : bool aExpressionMatches)
342 : {
343 0 : const nsMediaFeature *feature = aExpression->mFeature;
344 0 : FeatureEntry *entry = nsnull;
345 0 : for (PRUint32 i = 0; i < mFeatureCache.Length(); ++i) {
346 0 : if (mFeatureCache[i].mFeature == feature) {
347 0 : entry = &mFeatureCache[i];
348 0 : break;
349 : }
350 : }
351 0 : if (!entry) {
352 0 : entry = mFeatureCache.AppendElement();
353 0 : if (!entry) {
354 0 : return; /* out of memory */
355 : }
356 0 : entry->mFeature = feature;
357 : }
358 :
359 0 : ExpressionEntry eentry = { *aExpression, aExpressionMatches };
360 0 : entry->mExpressions.AppendElement(eentry);
361 : }
362 :
363 : bool
364 0 : nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const
365 : {
366 0 : if (aPresContext->Medium() != mMedium) {
367 0 : return false;
368 : }
369 :
370 0 : for (PRUint32 i = 0; i < mFeatureCache.Length(); ++i) {
371 0 : const FeatureEntry *entry = &mFeatureCache[i];
372 0 : nsCSSValue actual;
373 : nsresult rv =
374 0 : (entry->mFeature->mGetter)(aPresContext, entry->mFeature, actual);
375 0 : NS_ENSURE_SUCCESS(rv, false); // any better ideas?
376 :
377 0 : for (PRUint32 j = 0; j < entry->mExpressions.Length(); ++j) {
378 0 : const ExpressionEntry &eentry = entry->mExpressions[j];
379 0 : if (eentry.mExpression.Matches(aPresContext, actual) !=
380 : eentry.mExpressionMatches) {
381 0 : return false;
382 : }
383 : }
384 : }
385 :
386 0 : return true;
387 : }
388 :
389 : void
390 0 : nsMediaQuery::AppendToString(nsAString& aString) const
391 : {
392 0 : if (mHadUnknownExpression) {
393 0 : aString.AppendLiteral("not all");
394 0 : return;
395 : }
396 :
397 0 : NS_ASSERTION(!mNegated || !mHasOnly, "can't have not and only");
398 0 : NS_ASSERTION(!mTypeOmitted || (!mNegated && !mHasOnly),
399 : "can't have not or only when type is omitted");
400 0 : if (!mTypeOmitted) {
401 0 : if (mNegated) {
402 0 : aString.AppendLiteral("not ");
403 0 : } else if (mHasOnly) {
404 0 : aString.AppendLiteral("only ");
405 : }
406 0 : aString.Append(nsDependentAtomString(mMediaType));
407 : }
408 :
409 0 : for (PRUint32 i = 0, i_end = mExpressions.Length(); i < i_end; ++i) {
410 0 : if (i > 0 || !mTypeOmitted)
411 0 : aString.AppendLiteral(" and ");
412 0 : aString.AppendLiteral("(");
413 :
414 0 : const nsMediaExpression &expr = mExpressions[i];
415 0 : if (expr.mRange == nsMediaExpression::eMin) {
416 0 : aString.AppendLiteral("min-");
417 0 : } else if (expr.mRange == nsMediaExpression::eMax) {
418 0 : aString.AppendLiteral("max-");
419 : }
420 :
421 0 : const nsMediaFeature *feature = expr.mFeature;
422 0 : aString.Append(nsDependentAtomString(*feature->mName));
423 :
424 0 : if (expr.mValue.GetUnit() != eCSSUnit_Null) {
425 0 : aString.AppendLiteral(": ");
426 0 : switch (feature->mValueType) {
427 : case nsMediaFeature::eLength:
428 0 : NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit");
429 : // Use 'width' as a property that takes length values
430 : // written in the normal way.
431 0 : expr.mValue.AppendToString(eCSSProperty_width, aString);
432 0 : break;
433 : case nsMediaFeature::eInteger:
434 : case nsMediaFeature::eBoolInteger:
435 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer,
436 : "bad unit");
437 : // Use 'z-index' as a property that takes integer values
438 : // written without anything extra.
439 0 : expr.mValue.AppendToString(eCSSProperty_z_index, aString);
440 0 : break;
441 : case nsMediaFeature::eFloat:
442 : {
443 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Number,
444 : "bad unit");
445 : // Use 'line-height' as a property that takes float values
446 : // written in the normal way.
447 0 : expr.mValue.AppendToString(eCSSProperty_line_height, aString);
448 : }
449 0 : break;
450 : case nsMediaFeature::eIntRatio:
451 : {
452 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array,
453 : "bad unit");
454 0 : nsCSSValue::Array *array = expr.mValue.GetArrayValue();
455 0 : NS_ASSERTION(array->Count() == 2, "unexpected length");
456 0 : NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer,
457 : "bad unit");
458 0 : NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer,
459 : "bad unit");
460 0 : array->Item(0).AppendToString(eCSSProperty_z_index, aString);
461 0 : aString.AppendLiteral("/");
462 0 : array->Item(1).AppendToString(eCSSProperty_z_index, aString);
463 : }
464 0 : break;
465 : case nsMediaFeature::eResolution:
466 : {
467 0 : aString.AppendFloat(expr.mValue.GetFloatValue());
468 0 : if (expr.mValue.GetUnit() == eCSSUnit_Inch) {
469 0 : aString.AppendLiteral("dpi");
470 : } else {
471 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter,
472 : "bad unit");
473 0 : aString.AppendLiteral("dpcm");
474 : }
475 : }
476 0 : break;
477 : case nsMediaFeature::eEnumerated:
478 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Enumerated,
479 : "bad unit");
480 : AppendASCIItoUTF16(
481 : nsCSSProps::ValueToKeyword(expr.mValue.GetIntValue(),
482 0 : feature->mData.mKeywordTable),
483 0 : aString);
484 0 : break;
485 : case nsMediaFeature::eIdent:
486 0 : NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Ident,
487 : "bad unit");
488 0 : aString.Append(expr.mValue.GetStringBufferValue());
489 0 : break;
490 : }
491 : }
492 :
493 0 : aString.AppendLiteral(")");
494 : }
495 : }
496 :
497 : nsMediaQuery*
498 0 : nsMediaQuery::Clone() const
499 : {
500 0 : nsAutoPtr<nsMediaQuery> result(new nsMediaQuery(*this));
501 0 : NS_ENSURE_TRUE(result &&
502 : result->mExpressions.Length() == mExpressions.Length(),
503 : nsnull);
504 0 : return result.forget();
505 : }
506 :
507 : bool
508 0 : nsMediaQuery::Matches(nsPresContext* aPresContext,
509 : nsMediaQueryResultCacheKey* aKey) const
510 : {
511 0 : if (mHadUnknownExpression)
512 0 : return false;
513 :
514 : bool match =
515 0 : mMediaType == aPresContext->Medium() || mMediaType == nsGkAtoms::all;
516 0 : for (PRUint32 i = 0, i_end = mExpressions.Length(); match && i < i_end; ++i) {
517 0 : const nsMediaExpression &expr = mExpressions[i];
518 0 : nsCSSValue actual;
519 : nsresult rv =
520 0 : (expr.mFeature->mGetter)(aPresContext, expr.mFeature, actual);
521 0 : NS_ENSURE_SUCCESS(rv, false); // any better ideas?
522 :
523 0 : match = expr.Matches(aPresContext, actual);
524 0 : if (aKey) {
525 0 : aKey->AddExpression(&expr, match);
526 : }
527 : }
528 :
529 0 : return match == !mNegated;
530 : }
531 :
532 : DOMCI_DATA(MediaList, nsMediaList)
533 :
534 0 : NS_INTERFACE_MAP_BEGIN(nsMediaList)
535 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList)
536 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
537 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MediaList)
538 0 : NS_INTERFACE_MAP_END
539 :
540 0 : NS_IMPL_ADDREF(nsMediaList)
541 0 : NS_IMPL_RELEASE(nsMediaList)
542 :
543 :
544 0 : nsMediaList::nsMediaList()
545 0 : : mStyleSheet(nsnull)
546 : {
547 0 : }
548 :
549 0 : nsMediaList::~nsMediaList()
550 : {
551 0 : }
552 :
553 : nsresult
554 0 : nsMediaList::GetText(nsAString& aMediaText)
555 : {
556 0 : aMediaText.Truncate();
557 :
558 0 : for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
559 0 : nsMediaQuery* query = mArray[i];
560 0 : NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
561 :
562 0 : query->AppendToString(aMediaText);
563 :
564 0 : if (i + 1 < i_end) {
565 0 : aMediaText.AppendLiteral(", ");
566 : }
567 : }
568 :
569 0 : return NS_OK;
570 : }
571 :
572 : // XXXbz this is so ill-defined in the spec, it's not clear quite what
573 : // it should be doing....
574 : nsresult
575 0 : nsMediaList::SetText(const nsAString& aMediaText)
576 : {
577 0 : nsCSSParser parser;
578 :
579 0 : bool htmlMode = false;
580 0 : if (mStyleSheet) {
581 0 : nsCOMPtr<nsIDOMNode> node;
582 0 : mStyleSheet->GetOwnerNode(getter_AddRefs(node));
583 0 : htmlMode = !!node;
584 : }
585 :
586 : return parser.ParseMediaList(aMediaText, nsnull, 0,
587 0 : this, htmlMode);
588 : }
589 :
590 : bool
591 0 : nsMediaList::Matches(nsPresContext* aPresContext,
592 : nsMediaQueryResultCacheKey* aKey)
593 : {
594 0 : for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
595 0 : if (mArray[i]->Matches(aPresContext, aKey)) {
596 0 : return true;
597 : }
598 : }
599 0 : return mArray.IsEmpty();
600 : }
601 :
602 : nsresult
603 0 : nsMediaList::SetStyleSheet(nsCSSStyleSheet *aSheet)
604 : {
605 0 : NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
606 : "multiple style sheets competing for one media list");
607 0 : mStyleSheet = aSheet;
608 0 : return NS_OK;
609 : }
610 :
611 : nsresult
612 0 : nsMediaList::Clone(nsMediaList** aResult)
613 : {
614 0 : nsRefPtr<nsMediaList> result = new nsMediaList();
615 0 : if (!result || !result->mArray.AppendElements(mArray.Length()))
616 0 : return NS_ERROR_OUT_OF_MEMORY;
617 0 : for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
618 0 : if (!(result->mArray[i] = mArray[i]->Clone())) {
619 0 : return NS_ERROR_OUT_OF_MEMORY;
620 : }
621 : }
622 0 : NS_ADDREF(*aResult = result);
623 0 : return NS_OK;
624 : }
625 :
626 : NS_IMETHODIMP
627 0 : nsMediaList::GetMediaText(nsAString& aMediaText)
628 : {
629 0 : return GetText(aMediaText);
630 : }
631 :
632 : // "sheet" should be an nsCSSStyleSheet and "doc" should be an
633 : // nsCOMPtr<nsIDocument>
634 : #define BEGIN_MEDIA_CHANGE(sheet, doc) \
635 : if (sheet) { \
636 : doc = sheet->GetOwningDocument(); \
637 : } \
638 : mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); \
639 : if (sheet) { \
640 : rv = sheet->WillDirty(); \
641 : NS_ENSURE_SUCCESS(rv, rv); \
642 : }
643 :
644 : #define END_MEDIA_CHANGE(sheet, doc) \
645 : if (sheet) { \
646 : sheet->DidDirty(); \
647 : } \
648 : /* XXXldb Pass something meaningful? */ \
649 : if (doc) { \
650 : doc->StyleRuleChanged(sheet, nsnull, nsnull); \
651 : }
652 :
653 :
654 : NS_IMETHODIMP
655 0 : nsMediaList::SetMediaText(const nsAString& aMediaText)
656 : {
657 0 : nsresult rv = NS_OK;
658 0 : nsCOMPtr<nsIDocument> doc;
659 :
660 0 : BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
661 :
662 0 : rv = SetText(aMediaText);
663 0 : if (NS_FAILED(rv))
664 0 : return rv;
665 :
666 0 : END_MEDIA_CHANGE(mStyleSheet, doc)
667 :
668 0 : return rv;
669 : }
670 :
671 : NS_IMETHODIMP
672 0 : nsMediaList::GetLength(PRUint32* aLength)
673 : {
674 0 : NS_ENSURE_ARG_POINTER(aLength);
675 :
676 0 : *aLength = mArray.Length();
677 0 : return NS_OK;
678 : }
679 :
680 : NS_IMETHODIMP
681 0 : nsMediaList::Item(PRUint32 aIndex, nsAString& aReturn)
682 : {
683 0 : PRInt32 index = aIndex;
684 0 : if (0 <= index && index < Count()) {
685 0 : nsMediaQuery* query = mArray[index];
686 0 : NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
687 :
688 0 : aReturn.Truncate();
689 0 : query->AppendToString(aReturn);
690 : } else {
691 0 : SetDOMStringToNull(aReturn);
692 : }
693 :
694 0 : return NS_OK;
695 : }
696 :
697 : NS_IMETHODIMP
698 0 : nsMediaList::DeleteMedium(const nsAString& aOldMedium)
699 : {
700 0 : nsresult rv = NS_OK;
701 0 : nsCOMPtr<nsIDocument> doc;
702 :
703 0 : BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
704 :
705 0 : rv = Delete(aOldMedium);
706 0 : if (NS_FAILED(rv))
707 0 : return rv;
708 :
709 0 : END_MEDIA_CHANGE(mStyleSheet, doc)
710 :
711 0 : return rv;
712 : }
713 :
714 : NS_IMETHODIMP
715 0 : nsMediaList::AppendMedium(const nsAString& aNewMedium)
716 : {
717 0 : nsresult rv = NS_OK;
718 0 : nsCOMPtr<nsIDocument> doc;
719 :
720 0 : BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
721 :
722 0 : rv = Append(aNewMedium);
723 0 : if (NS_FAILED(rv))
724 0 : return rv;
725 :
726 0 : END_MEDIA_CHANGE(mStyleSheet, doc)
727 :
728 0 : return rv;
729 : }
730 :
731 : nsresult
732 0 : nsMediaList::Delete(const nsAString& aOldMedium)
733 : {
734 0 : if (aOldMedium.IsEmpty())
735 0 : return NS_ERROR_DOM_NOT_FOUND_ERR;
736 :
737 0 : for (PRInt32 i = 0, i_end = mArray.Length(); i < i_end; ++i) {
738 0 : nsMediaQuery* query = mArray[i];
739 0 : NS_ENSURE_TRUE(query, NS_ERROR_FAILURE);
740 :
741 0 : nsAutoString buf;
742 0 : query->AppendToString(buf);
743 0 : if (buf == aOldMedium) {
744 0 : mArray.RemoveElementAt(i);
745 0 : return NS_OK;
746 : }
747 : }
748 :
749 0 : return NS_ERROR_DOM_NOT_FOUND_ERR;
750 : }
751 :
752 : nsresult
753 0 : nsMediaList::Append(const nsAString& aNewMedium)
754 : {
755 0 : if (aNewMedium.IsEmpty())
756 0 : return NS_ERROR_DOM_NOT_FOUND_ERR;
757 :
758 0 : Delete(aNewMedium);
759 :
760 0 : nsresult rv = NS_OK;
761 0 : nsTArray<nsAutoPtr<nsMediaQuery> > buf;
762 : #ifdef DEBUG
763 : bool ok =
764 : #endif
765 0 : mArray.SwapElements(buf);
766 0 : NS_ASSERTION(ok, "SwapElements should never fail when neither array "
767 : "is an auto array");
768 0 : SetText(aNewMedium);
769 0 : if (mArray.Length() == 1) {
770 0 : nsMediaQuery *query = mArray[0].forget();
771 0 : if (!buf.AppendElement(query)) {
772 0 : delete query;
773 0 : rv = NS_ERROR_OUT_OF_MEMORY;
774 : }
775 : }
776 : #ifdef DEBUG
777 : ok =
778 : #endif
779 0 : mArray.SwapElements(buf);
780 0 : NS_ASSERTION(ok, "SwapElements should never fail when neither array "
781 : "is an auto array");
782 0 : return rv;
783 : }
784 :
785 : // -------------------------------
786 : // CSS Style Sheet Inner Data Container
787 : //
788 :
789 :
790 0 : nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheet* aPrimarySheet)
791 : : mSheets(),
792 : mComplete(false)
793 : #ifdef DEBUG
794 0 : , mPrincipalSet(false)
795 : #endif
796 : {
797 0 : MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
798 0 : mSheets.AppendElement(aPrimarySheet);
799 :
800 0 : mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
801 0 : }
802 :
803 0 : static bool SetStyleSheetReference(css::Rule* aRule, void* aSheet)
804 : {
805 0 : if (aRule) {
806 0 : aRule->SetStyleSheet(static_cast<nsCSSStyleSheet*>(aSheet));
807 : }
808 0 : return true;
809 : }
810 :
811 : static bool
812 0 : CloneRuleInto(css::Rule* aRule, void* aArray)
813 : {
814 0 : nsRefPtr<css::Rule> clone = aRule->Clone();
815 0 : static_cast<nsCOMArray<css::Rule>*>(aArray)->AppendObject(clone);
816 0 : return true;
817 : }
818 :
819 : struct ChildSheetListBuilder {
820 : nsRefPtr<nsCSSStyleSheet>* sheetSlot;
821 : nsCSSStyleSheet* parent;
822 :
823 0 : void SetParentLinks(nsCSSStyleSheet* aSheet) {
824 0 : aSheet->mParent = parent;
825 0 : aSheet->SetOwningDocument(parent->mDocument);
826 0 : }
827 :
828 0 : static void ReparentChildList(nsCSSStyleSheet* aPrimarySheet,
829 : nsCSSStyleSheet* aFirstChild)
830 : {
831 0 : for (nsCSSStyleSheet *child = aFirstChild; child; child = child->mNext) {
832 0 : child->mParent = aPrimarySheet;
833 0 : child->SetOwningDocument(aPrimarySheet->mDocument);
834 : }
835 0 : }
836 : };
837 :
838 : bool
839 0 : nsCSSStyleSheet::RebuildChildList(css::Rule* aRule, void* aBuilder)
840 : {
841 0 : PRInt32 type = aRule->GetType();
842 0 : if (type < css::Rule::IMPORT_RULE) {
843 : // Keep going till we get to the import rules.
844 0 : return true;
845 : }
846 :
847 0 : if (type != css::Rule::IMPORT_RULE) {
848 : // We're past all the import rules; stop the enumeration.
849 0 : return false;
850 : }
851 :
852 : ChildSheetListBuilder* builder =
853 0 : static_cast<ChildSheetListBuilder*>(aBuilder);
854 :
855 : // XXXbz We really need to decomtaminate all this stuff. Is there a reason
856 : // that I can't just QI to ImportRule and get an nsCSSStyleSheet
857 : // directly from it?
858 0 : nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(aRule));
859 0 : NS_ASSERTION(importRule, "GetType lied");
860 :
861 0 : nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
862 0 : importRule->GetStyleSheet(getter_AddRefs(childSheet));
863 :
864 : // Have to do this QI to be safe, since XPConnect can fake
865 : // nsIDOMCSSStyleSheets
866 0 : nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(childSheet);
867 0 : if (!cssSheet) {
868 0 : return true;
869 : }
870 :
871 0 : (*builder->sheetSlot) = cssSheet;
872 0 : builder->SetParentLinks(*builder->sheetSlot);
873 0 : builder->sheetSlot = &(*builder->sheetSlot)->mNext;
874 0 : return true;
875 : }
876 :
877 : size_t
878 0 : nsCSSStyleSheet::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
879 : {
880 0 : size_t n = 0;
881 0 : const nsCSSStyleSheet* s = this;
882 0 : while (s) {
883 0 : n += aMallocSizeOf(s);
884 0 : n += s->mInner->SizeOfIncludingThis(aMallocSizeOf);
885 :
886 : // Measurement of the following members may be added later if DMD finds it is
887 : // worthwhile:
888 : // - s->mTitle
889 : // - s->mMedia
890 : // - s->mRuleCollection
891 : // - s->mRuleProcessors
892 : //
893 : // The following members are not measured:
894 : // - s->mOwnerRule, because it's non-owning
895 :
896 0 : s = s->mNext;
897 : }
898 0 : return n;
899 : }
900 :
901 0 : nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy,
902 : nsCSSStyleSheet* aPrimarySheet)
903 : : mSheets(),
904 : mSheetURI(aCopy.mSheetURI),
905 : mOriginalSheetURI(aCopy.mOriginalSheetURI),
906 : mBaseURI(aCopy.mBaseURI),
907 : mPrincipal(aCopy.mPrincipal),
908 : mComplete(aCopy.mComplete)
909 : #ifdef DEBUG
910 0 : , mPrincipalSet(aCopy.mPrincipalSet)
911 : #endif
912 : {
913 0 : MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
914 0 : AddSheet(aPrimarySheet);
915 0 : aCopy.mOrderedRules.EnumerateForwards(CloneRuleInto, &mOrderedRules);
916 0 : mOrderedRules.EnumerateForwards(SetStyleSheetReference, aPrimarySheet);
917 :
918 0 : ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet };
919 0 : mOrderedRules.EnumerateForwards(nsCSSStyleSheet::RebuildChildList, &builder);
920 :
921 0 : RebuildNameSpaces();
922 0 : }
923 :
924 0 : nsCSSStyleSheetInner::~nsCSSStyleSheetInner()
925 : {
926 0 : MOZ_COUNT_DTOR(nsCSSStyleSheetInner);
927 0 : mOrderedRules.EnumerateForwards(SetStyleSheetReference, nsnull);
928 0 : }
929 :
930 : nsCSSStyleSheetInner*
931 0 : nsCSSStyleSheetInner::CloneFor(nsCSSStyleSheet* aPrimarySheet)
932 : {
933 0 : return new nsCSSStyleSheetInner(*this, aPrimarySheet);
934 : }
935 :
936 : void
937 0 : nsCSSStyleSheetInner::AddSheet(nsCSSStyleSheet* aSheet)
938 : {
939 0 : mSheets.AppendElement(aSheet);
940 0 : }
941 :
942 : void
943 0 : nsCSSStyleSheetInner::RemoveSheet(nsCSSStyleSheet* aSheet)
944 : {
945 0 : if (1 == mSheets.Length()) {
946 0 : NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
947 0 : delete this;
948 0 : return;
949 : }
950 0 : if (aSheet == mSheets.ElementAt(0)) {
951 0 : mSheets.RemoveElementAt(0);
952 0 : NS_ASSERTION(mSheets.Length(), "no parents");
953 : mOrderedRules.EnumerateForwards(SetStyleSheetReference,
954 0 : mSheets.ElementAt(0));
955 :
956 0 : ChildSheetListBuilder::ReparentChildList(mSheets[0], mFirstChild);
957 : }
958 : else {
959 0 : mSheets.RemoveElement(aSheet);
960 : }
961 : }
962 :
963 : static void
964 0 : AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap)
965 : {
966 0 : NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type");
967 :
968 0 : nsRefPtr<css::NameSpaceRule> nameSpaceRule = do_QueryObject(aRule);
969 :
970 0 : nsAutoString urlSpec;
971 0 : nameSpaceRule->GetURLSpec(urlSpec);
972 :
973 0 : aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec);
974 0 : }
975 :
976 : static bool
977 0 : CreateNameSpace(css::Rule* aRule, void* aNameSpacePtr)
978 : {
979 0 : PRInt32 type = aRule->GetType();
980 0 : if (css::Rule::NAMESPACE_RULE == type) {
981 : AddNamespaceRuleToMap(aRule,
982 0 : static_cast<nsXMLNameSpaceMap*>(aNameSpacePtr));
983 0 : return true;
984 : }
985 : // stop if not namespace, import or charset because namespace can't follow
986 : // anything else
987 0 : return (css::Rule::CHARSET_RULE == type || css::Rule::IMPORT_RULE == type);
988 : }
989 :
990 : void
991 0 : nsCSSStyleSheetInner::RebuildNameSpaces()
992 : {
993 : // Just nuke our existing namespace map, if any
994 0 : if (NS_SUCCEEDED(CreateNamespaceMap())) {
995 0 : mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap);
996 : }
997 0 : }
998 :
999 : nsresult
1000 0 : nsCSSStyleSheetInner::CreateNamespaceMap()
1001 : {
1002 0 : mNameSpaceMap = nsXMLNameSpaceMap::Create(false);
1003 0 : NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
1004 : // Override the default namespace map behavior for the null prefix to
1005 : // return the wildcard namespace instead of the null namespace.
1006 0 : mNameSpaceMap->AddPrefix(nsnull, kNameSpaceID_Unknown);
1007 0 : return NS_OK;
1008 : }
1009 :
1010 : size_t
1011 0 : nsCSSStyleSheetInner::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
1012 : {
1013 0 : size_t n = aMallocSizeOf(this);
1014 : n += mOrderedRules.SizeOfExcludingThis(css::Rule::SizeOfCOMArrayElementIncludingThis,
1015 0 : aMallocSizeOf);
1016 0 : n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0;
1017 :
1018 : // Measurement of the following members may be added later if DMD finds it is
1019 : // worthwhile:
1020 : // - mSheetURI
1021 : // - mOriginalSheetURI
1022 : // - mBaseURI
1023 : // - mPrincipal
1024 : // - mNameSpaceMap
1025 : //
1026 : // The following members are not measured:
1027 : // - mSheets, because it's non-owning
1028 :
1029 0 : return n;
1030 : }
1031 :
1032 : // -------------------------------
1033 : // CSS Style Sheet
1034 : //
1035 :
1036 0 : nsCSSStyleSheet::nsCSSStyleSheet()
1037 : : mTitle(),
1038 : mParent(nsnull),
1039 : mOwnerRule(nsnull),
1040 : mRuleCollection(nsnull),
1041 : mDocument(nsnull),
1042 : mOwningNode(nsnull),
1043 : mDisabled(false),
1044 : mDirty(false),
1045 0 : mRuleProcessors(nsnull)
1046 : {
1047 :
1048 0 : mInner = new nsCSSStyleSheetInner(this);
1049 0 : }
1050 :
1051 0 : nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet& aCopy,
1052 : nsCSSStyleSheet* aParentToUse,
1053 : css::ImportRule* aOwnerRuleToUse,
1054 : nsIDocument* aDocumentToUse,
1055 : nsIDOMNode* aOwningNodeToUse)
1056 : : mTitle(aCopy.mTitle),
1057 : mParent(aParentToUse),
1058 : mOwnerRule(aOwnerRuleToUse),
1059 : mRuleCollection(nsnull), // re-created lazily
1060 : mDocument(aDocumentToUse),
1061 : mOwningNode(aOwningNodeToUse),
1062 : mDisabled(aCopy.mDisabled),
1063 : mDirty(aCopy.mDirty),
1064 : mInner(aCopy.mInner),
1065 0 : mRuleProcessors(nsnull)
1066 : {
1067 :
1068 0 : mInner->AddSheet(this);
1069 :
1070 0 : if (mDirty) { // CSSOM's been there, force full copy now
1071 0 : NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
1072 : // FIXME: handle failure?
1073 0 : EnsureUniqueInner();
1074 : }
1075 :
1076 0 : if (aCopy.mMedia) {
1077 : // XXX This is wrong; we should be keeping @import rules and
1078 : // sheets in sync!
1079 0 : aCopy.mMedia->Clone(getter_AddRefs(mMedia));
1080 : }
1081 0 : }
1082 :
1083 0 : nsCSSStyleSheet::~nsCSSStyleSheet()
1084 : {
1085 0 : for (nsCSSStyleSheet* child = mInner->mFirstChild;
1086 : child;
1087 0 : child = child->mNext) {
1088 : // XXXbz this is a little bogus; see the XXX comment where we
1089 : // declare mFirstChild.
1090 0 : if (child->mParent == this) {
1091 0 : child->mParent = nsnull;
1092 0 : child->mDocument = nsnull;
1093 : }
1094 : }
1095 0 : if (nsnull != mRuleCollection) {
1096 0 : mRuleCollection->DropReference();
1097 0 : NS_RELEASE(mRuleCollection);
1098 : }
1099 0 : if (mMedia) {
1100 0 : mMedia->SetStyleSheet(nsnull);
1101 0 : mMedia = nsnull;
1102 : }
1103 0 : mInner->RemoveSheet(this);
1104 : // XXX The document reference is not reference counted and should
1105 : // not be released. The document will let us know when it is going
1106 : // away.
1107 0 : if (mRuleProcessors) {
1108 0 : NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference");
1109 0 : delete mRuleProcessors; // weak refs, should be empty here anyway
1110 : }
1111 0 : }
1112 :
1113 :
1114 : DOMCI_DATA(CSSStyleSheet, nsCSSStyleSheet)
1115 :
1116 : // QueryInterface implementation for nsCSSStyleSheet
1117 0 : NS_INTERFACE_MAP_BEGIN(nsCSSStyleSheet)
1118 0 : NS_INTERFACE_MAP_ENTRY(nsIStyleSheet)
1119 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
1120 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
1121 0 : NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
1122 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleSheet)
1123 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleSheet)
1124 0 : if (aIID.Equals(NS_GET_IID(nsCSSStyleSheet)))
1125 0 : foundInterface = reinterpret_cast<nsISupports*>(this);
1126 : else
1127 0 : NS_INTERFACE_MAP_END
1128 :
1129 :
1130 0 : NS_IMPL_ADDREF(nsCSSStyleSheet)
1131 0 : NS_IMPL_RELEASE(nsCSSStyleSheet)
1132 :
1133 :
1134 : nsresult
1135 0 : nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor)
1136 : {
1137 0 : if (! mRuleProcessors) {
1138 0 : mRuleProcessors = new nsAutoTArray<nsCSSRuleProcessor*, 8>();
1139 0 : if (!mRuleProcessors)
1140 0 : return NS_ERROR_OUT_OF_MEMORY;
1141 : }
1142 0 : NS_ASSERTION(mRuleProcessors->NoIndex == mRuleProcessors->IndexOf(aProcessor),
1143 : "processor already registered");
1144 0 : mRuleProcessors->AppendElement(aProcessor); // weak ref
1145 0 : return NS_OK;
1146 : }
1147 :
1148 : nsresult
1149 0 : nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor)
1150 : {
1151 0 : if (!mRuleProcessors)
1152 0 : return NS_ERROR_FAILURE;
1153 0 : return mRuleProcessors->RemoveElement(aProcessor)
1154 : ? NS_OK
1155 0 : : NS_ERROR_FAILURE;
1156 : }
1157 :
1158 :
1159 : void
1160 0 : nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
1161 : nsIURI* aBaseURI)
1162 : {
1163 0 : NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
1164 :
1165 0 : NS_ASSERTION(mInner->mOrderedRules.Count() == 0 && !mInner->mComplete,
1166 : "Can't call SetURL on sheets that are complete or have rules");
1167 :
1168 0 : mInner->mSheetURI = aSheetURI;
1169 0 : mInner->mOriginalSheetURI = aOriginalSheetURI;
1170 0 : mInner->mBaseURI = aBaseURI;
1171 0 : }
1172 :
1173 : void
1174 0 : nsCSSStyleSheet::SetPrincipal(nsIPrincipal* aPrincipal)
1175 : {
1176 0 : NS_PRECONDITION(!mInner->mPrincipalSet,
1177 : "Should have an inner whose principal has not yet been set");
1178 0 : if (aPrincipal) {
1179 0 : mInner->mPrincipal = aPrincipal;
1180 : #ifdef DEBUG
1181 0 : mInner->mPrincipalSet = true;
1182 : #endif
1183 : }
1184 0 : }
1185 :
1186 : /* virtual */ nsIURI*
1187 0 : nsCSSStyleSheet::GetSheetURI() const
1188 : {
1189 0 : return mInner->mSheetURI;
1190 : }
1191 :
1192 : /* virtual */ nsIURI*
1193 0 : nsCSSStyleSheet::GetBaseURI() const
1194 : {
1195 0 : return mInner->mBaseURI;
1196 : }
1197 :
1198 : /* virtual */ void
1199 0 : nsCSSStyleSheet::GetType(nsString& aType) const
1200 : {
1201 0 : aType.AssignLiteral("text/css");
1202 0 : }
1203 :
1204 : bool
1205 0 : nsCSSStyleSheet::UseForPresentation(nsPresContext* aPresContext,
1206 : nsMediaQueryResultCacheKey& aKey) const
1207 : {
1208 0 : if (mMedia) {
1209 0 : return mMedia->Matches(aPresContext, &aKey);
1210 : }
1211 0 : return true;
1212 : }
1213 :
1214 :
1215 : void
1216 0 : nsCSSStyleSheet::SetMedia(nsMediaList* aMedia)
1217 : {
1218 0 : mMedia = aMedia;
1219 0 : }
1220 :
1221 : /* virtual */ bool
1222 0 : nsCSSStyleSheet::HasRules() const
1223 : {
1224 0 : return StyleRuleCount() != 0;
1225 : }
1226 :
1227 : /* virtual */ bool
1228 0 : nsCSSStyleSheet::IsApplicable() const
1229 : {
1230 0 : return !mDisabled && mInner->mComplete;
1231 : }
1232 :
1233 : /* virtual */ void
1234 0 : nsCSSStyleSheet::SetEnabled(bool aEnabled)
1235 : {
1236 : // Internal method, so callers must handle BeginUpdate/EndUpdate
1237 0 : bool oldDisabled = mDisabled;
1238 0 : mDisabled = !aEnabled;
1239 :
1240 0 : if (mInner->mComplete && oldDisabled != mDisabled) {
1241 0 : ClearRuleCascades();
1242 :
1243 0 : if (mDocument) {
1244 0 : mDocument->SetStyleSheetApplicableState(this, !mDisabled);
1245 : }
1246 : }
1247 0 : }
1248 :
1249 : /* virtual */ bool
1250 0 : nsCSSStyleSheet::IsComplete() const
1251 : {
1252 0 : return mInner->mComplete;
1253 : }
1254 :
1255 : /* virtual */ void
1256 0 : nsCSSStyleSheet::SetComplete()
1257 : {
1258 0 : NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
1259 0 : mInner->mComplete = true;
1260 0 : if (mDocument && !mDisabled) {
1261 : // Let the document know
1262 0 : mDocument->BeginUpdate(UPDATE_STYLE);
1263 0 : mDocument->SetStyleSheetApplicableState(this, true);
1264 0 : mDocument->EndUpdate(UPDATE_STYLE);
1265 : }
1266 0 : }
1267 :
1268 : /* virtual */ nsIStyleSheet*
1269 0 : nsCSSStyleSheet::GetParentSheet() const
1270 : {
1271 0 : return mParent;
1272 : }
1273 :
1274 : /* virtual */ nsIDocument*
1275 0 : nsCSSStyleSheet::GetOwningDocument() const
1276 : {
1277 0 : return mDocument;
1278 : }
1279 :
1280 : /* virtual */ void
1281 0 : nsCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument)
1282 : { // not ref counted
1283 0 : mDocument = aDocument;
1284 : // Now set the same document on all our child sheets....
1285 : // XXXbz this is a little bogus; see the XXX comment where we
1286 : // declare mFirstChild.
1287 0 : for (nsCSSStyleSheet* child = mInner->mFirstChild;
1288 0 : child; child = child->mNext) {
1289 0 : if (child->mParent == this) {
1290 0 : child->SetOwningDocument(aDocument);
1291 : }
1292 : }
1293 0 : }
1294 :
1295 : PRUint64
1296 0 : nsCSSStyleSheet::FindOwningWindowInnerID() const
1297 : {
1298 0 : PRUint64 windowID = 0;
1299 0 : if (mDocument) {
1300 0 : windowID = mDocument->InnerWindowID();
1301 : }
1302 :
1303 0 : if (windowID == 0 && mOwningNode) {
1304 0 : nsCOMPtr<nsIContent> node = do_QueryInterface(mOwningNode);
1305 0 : if (node) {
1306 0 : windowID = node->OwnerDoc()->InnerWindowID();
1307 : }
1308 : }
1309 :
1310 0 : if (windowID == 0 && mOwnerRule) {
1311 0 : nsCOMPtr<nsIStyleSheet> sheet = static_cast<css::Rule*>(mOwnerRule)->GetStyleSheet();
1312 0 : if (sheet) {
1313 0 : nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheet);
1314 0 : if (cssSheet) {
1315 0 : windowID = cssSheet->FindOwningWindowInnerID();
1316 : }
1317 : }
1318 : }
1319 :
1320 0 : if (windowID == 0 && mParent) {
1321 0 : windowID = mParent->FindOwningWindowInnerID();
1322 : }
1323 :
1324 0 : return windowID;
1325 : }
1326 :
1327 : void
1328 0 : nsCSSStyleSheet::AppendStyleSheet(nsCSSStyleSheet* aSheet)
1329 : {
1330 0 : NS_PRECONDITION(nsnull != aSheet, "null arg");
1331 :
1332 0 : if (NS_SUCCEEDED(WillDirty())) {
1333 0 : nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild;
1334 0 : while (*tail) {
1335 0 : tail = &(*tail)->mNext;
1336 : }
1337 0 : *tail = aSheet;
1338 :
1339 : // This is not reference counted. Our parent tells us when
1340 : // it's going away.
1341 0 : aSheet->mParent = this;
1342 0 : aSheet->mDocument = mDocument;
1343 0 : DidDirty();
1344 : }
1345 0 : }
1346 :
1347 : void
1348 0 : nsCSSStyleSheet::InsertStyleSheetAt(nsCSSStyleSheet* aSheet, PRInt32 aIndex)
1349 : {
1350 0 : NS_PRECONDITION(nsnull != aSheet, "null arg");
1351 :
1352 0 : if (NS_SUCCEEDED(WillDirty())) {
1353 0 : nsRefPtr<nsCSSStyleSheet>* tail = &mInner->mFirstChild;
1354 0 : while (*tail && aIndex) {
1355 0 : --aIndex;
1356 0 : tail = &(*tail)->mNext;
1357 : }
1358 0 : aSheet->mNext = *tail;
1359 0 : *tail = aSheet;
1360 :
1361 : // This is not reference counted. Our parent tells us when
1362 : // it's going away.
1363 0 : aSheet->mParent = this;
1364 0 : aSheet->mDocument = mDocument;
1365 0 : DidDirty();
1366 : }
1367 0 : }
1368 :
1369 : void
1370 0 : nsCSSStyleSheet::PrependStyleRule(css::Rule* aRule)
1371 : {
1372 0 : NS_PRECONDITION(nsnull != aRule, "null arg");
1373 :
1374 0 : if (NS_SUCCEEDED(WillDirty())) {
1375 0 : mInner->mOrderedRules.InsertObjectAt(aRule, 0);
1376 0 : aRule->SetStyleSheet(this);
1377 0 : DidDirty();
1378 :
1379 0 : if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
1380 : // no api to prepend a namespace (ugh), release old ones and re-create them all
1381 0 : mInner->RebuildNameSpaces();
1382 : }
1383 : }
1384 0 : }
1385 :
1386 : void
1387 0 : nsCSSStyleSheet::AppendStyleRule(css::Rule* aRule)
1388 : {
1389 0 : NS_PRECONDITION(nsnull != aRule, "null arg");
1390 :
1391 0 : if (NS_SUCCEEDED(WillDirty())) {
1392 0 : mInner->mOrderedRules.AppendObject(aRule);
1393 0 : aRule->SetStyleSheet(this);
1394 0 : DidDirty();
1395 :
1396 0 : if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
1397 : #ifdef DEBUG
1398 : nsresult rv =
1399 : #endif
1400 0 : RegisterNamespaceRule(aRule);
1401 0 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
1402 : "RegisterNamespaceRule returned error");
1403 : }
1404 : }
1405 0 : }
1406 :
1407 : void
1408 0 : nsCSSStyleSheet::ReplaceStyleRule(css::Rule* aOld, css::Rule* aNew)
1409 : {
1410 0 : NS_PRECONDITION(mInner->mOrderedRules.Count() != 0, "can't have old rule");
1411 0 : NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
1412 :
1413 0 : if (NS_SUCCEEDED(WillDirty())) {
1414 0 : PRInt32 index = mInner->mOrderedRules.IndexOf(aOld);
1415 0 : if (NS_UNLIKELY(index == -1)) {
1416 0 : NS_NOTREACHED("Couldn't find old rule");
1417 0 : return;
1418 : }
1419 0 : mInner->mOrderedRules.ReplaceObjectAt(aNew, index);
1420 :
1421 0 : aNew->SetStyleSheet(this);
1422 0 : aOld->SetStyleSheet(nsnull);
1423 0 : DidDirty();
1424 0 : NS_ASSERTION(css::Rule::NAMESPACE_RULE != aNew->GetType(), "not yet implemented");
1425 0 : NS_ASSERTION(css::Rule::NAMESPACE_RULE != aOld->GetType(), "not yet implemented");
1426 : }
1427 : }
1428 :
1429 : PRInt32
1430 0 : nsCSSStyleSheet::StyleRuleCount() const
1431 : {
1432 0 : return mInner->mOrderedRules.Count();
1433 : }
1434 :
1435 : nsresult
1436 0 : nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex, css::Rule*& aRule) const
1437 : {
1438 : // Important: If this function is ever made scriptable, we must add
1439 : // a security check here. See GetCssRules below for an example.
1440 0 : aRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
1441 0 : if (aRule) {
1442 0 : NS_ADDREF(aRule);
1443 0 : return NS_OK;
1444 : }
1445 :
1446 0 : return NS_ERROR_ILLEGAL_VALUE;
1447 : }
1448 :
1449 : PRInt32
1450 0 : nsCSSStyleSheet::StyleSheetCount() const
1451 : {
1452 : // XXX Far from an ideal way to do this, but the hope is that
1453 : // it won't be done too often. If it is, we might want to
1454 : // consider storing the children in an array.
1455 0 : PRInt32 count = 0;
1456 :
1457 0 : const nsCSSStyleSheet* child = mInner->mFirstChild;
1458 0 : while (child) {
1459 0 : count++;
1460 0 : child = child->mNext;
1461 : }
1462 :
1463 0 : return count;
1464 : }
1465 :
1466 : nsCSSStyleSheet::EnsureUniqueInnerResult
1467 0 : nsCSSStyleSheet::EnsureUniqueInner()
1468 : {
1469 0 : mDirty = true;
1470 :
1471 0 : NS_ABORT_IF_FALSE(mInner->mSheets.Length() != 0,
1472 : "unexpected number of outers");
1473 0 : if (mInner->mSheets.Length() == 1) {
1474 0 : return eUniqueInner_AlreadyUnique;
1475 : }
1476 0 : nsCSSStyleSheetInner* clone = mInner->CloneFor(this);
1477 0 : if (!clone) {
1478 0 : return eUniqueInner_CloneFailed;
1479 : }
1480 0 : mInner->RemoveSheet(this);
1481 0 : mInner = clone;
1482 :
1483 : // otherwise the rule processor has pointers to the old rules
1484 0 : ClearRuleCascades();
1485 :
1486 0 : return eUniqueInner_ClonedInner;
1487 : }
1488 :
1489 : bool
1490 0 : nsCSSStyleSheet::AppendAllChildSheets(nsTArray<nsCSSStyleSheet*>& aArray)
1491 : {
1492 0 : for (nsCSSStyleSheet* child = mInner->mFirstChild; child;
1493 0 : child = child->mNext) {
1494 0 : if (!aArray.AppendElement(child)) {
1495 0 : return false;
1496 : }
1497 : }
1498 0 : return true;
1499 : }
1500 :
1501 : already_AddRefed<nsCSSStyleSheet>
1502 0 : nsCSSStyleSheet::Clone(nsCSSStyleSheet* aCloneParent,
1503 : css::ImportRule* aCloneOwnerRule,
1504 : nsIDocument* aCloneDocument,
1505 : nsIDOMNode* aCloneOwningNode) const
1506 : {
1507 : nsCSSStyleSheet* clone = new nsCSSStyleSheet(*this,
1508 : aCloneParent,
1509 : aCloneOwnerRule,
1510 : aCloneDocument,
1511 0 : aCloneOwningNode);
1512 0 : NS_IF_ADDREF(clone);
1513 0 : return clone;
1514 : }
1515 :
1516 : #ifdef DEBUG
1517 : static void
1518 0 : ListRules(const nsCOMArray<css::Rule>& aRules, FILE* aOut, PRInt32 aIndent)
1519 : {
1520 0 : for (PRInt32 index = aRules.Count() - 1; index >= 0; --index) {
1521 0 : aRules.ObjectAt(index)->List(aOut, aIndent);
1522 : }
1523 0 : }
1524 :
1525 : struct ListEnumData {
1526 : ListEnumData(FILE* aOut, PRInt32 aIndent)
1527 : : mOut(aOut),
1528 : mIndent(aIndent)
1529 : {
1530 : }
1531 : FILE* mOut;
1532 : PRInt32 mIndent;
1533 : };
1534 :
1535 : /* virtual */ void
1536 0 : nsCSSStyleSheet::List(FILE* out, PRInt32 aIndent) const
1537 : {
1538 :
1539 : PRInt32 index;
1540 :
1541 : // Indent
1542 0 : for (index = aIndent; --index >= 0; ) fputs(" ", out);
1543 :
1544 0 : fputs("CSS Style Sheet: ", out);
1545 0 : nsCAutoString urlSpec;
1546 0 : nsresult rv = mInner->mSheetURI->GetSpec(urlSpec);
1547 0 : if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
1548 0 : fputs(urlSpec.get(), out);
1549 : }
1550 :
1551 0 : if (mMedia) {
1552 0 : fputs(" media: ", out);
1553 0 : nsAutoString buffer;
1554 0 : mMedia->GetText(buffer);
1555 0 : fputs(NS_ConvertUTF16toUTF8(buffer).get(), out);
1556 : }
1557 0 : fputs("\n", out);
1558 :
1559 0 : for (const nsCSSStyleSheet* child = mInner->mFirstChild;
1560 : child;
1561 0 : child = child->mNext) {
1562 0 : child->List(out, aIndent + 1);
1563 : }
1564 :
1565 0 : fputs("Rules in source order:\n", out);
1566 0 : ListRules(mInner->mOrderedRules, out, aIndent);
1567 0 : }
1568 : #endif
1569 :
1570 : void
1571 0 : nsCSSStyleSheet::ClearRuleCascades()
1572 : {
1573 0 : if (mRuleProcessors) {
1574 0 : nsCSSRuleProcessor **iter = mRuleProcessors->Elements(),
1575 0 : **end = iter + mRuleProcessors->Length();
1576 0 : for(; iter != end; ++iter) {
1577 0 : (*iter)->ClearRuleCascades();
1578 : }
1579 : }
1580 0 : if (mParent) {
1581 0 : nsCSSStyleSheet* parent = (nsCSSStyleSheet*)mParent;
1582 0 : parent->ClearRuleCascades();
1583 : }
1584 0 : }
1585 :
1586 : nsresult
1587 0 : nsCSSStyleSheet::WillDirty()
1588 : {
1589 0 : if (!mInner->mComplete) {
1590 : // Do nothing
1591 0 : return NS_OK;
1592 : }
1593 :
1594 0 : if (EnsureUniqueInner() == eUniqueInner_CloneFailed) {
1595 0 : return NS_ERROR_OUT_OF_MEMORY;
1596 : }
1597 0 : return NS_OK;
1598 : }
1599 :
1600 : void
1601 0 : nsCSSStyleSheet::DidDirty()
1602 : {
1603 0 : NS_ABORT_IF_FALSE(!mInner->mComplete || mDirty,
1604 : "caller must have called WillDirty()");
1605 0 : ClearRuleCascades();
1606 0 : }
1607 :
1608 : nsresult
1609 0 : nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() const
1610 : {
1611 : // Get the security manager and do the subsumes check
1612 : nsIScriptSecurityManager *securityManager =
1613 0 : nsContentUtils::GetSecurityManager();
1614 :
1615 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
1616 0 : nsresult rv = securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
1617 0 : NS_ENSURE_SUCCESS(rv, rv);
1618 :
1619 0 : if (!subjectPrincipal) {
1620 0 : return NS_ERROR_DOM_SECURITY_ERR;
1621 : }
1622 :
1623 : bool subsumes;
1624 0 : rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
1625 0 : NS_ENSURE_SUCCESS(rv, rv);
1626 :
1627 0 : if (subsumes) {
1628 0 : return NS_OK;
1629 : }
1630 :
1631 0 : if (!nsContentUtils::IsCallerTrustedForWrite()) {
1632 0 : return NS_ERROR_DOM_SECURITY_ERR;
1633 : }
1634 :
1635 0 : return NS_OK;
1636 : }
1637 :
1638 : nsresult
1639 0 : nsCSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule)
1640 : {
1641 0 : if (!mInner->mNameSpaceMap) {
1642 0 : nsresult rv = mInner->CreateNamespaceMap();
1643 0 : NS_ENSURE_SUCCESS(rv, rv);
1644 : }
1645 :
1646 0 : AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap);
1647 0 : return NS_OK;
1648 : }
1649 :
1650 : // nsIDOMStyleSheet interface
1651 : NS_IMETHODIMP
1652 0 : nsCSSStyleSheet::GetType(nsAString& aType)
1653 : {
1654 0 : aType.AssignLiteral("text/css");
1655 0 : return NS_OK;
1656 : }
1657 :
1658 : NS_IMETHODIMP
1659 0 : nsCSSStyleSheet::GetDisabled(bool* aDisabled)
1660 : {
1661 0 : *aDisabled = mDisabled;
1662 0 : return NS_OK;
1663 : }
1664 :
1665 : NS_IMETHODIMP
1666 0 : nsCSSStyleSheet::SetDisabled(bool aDisabled)
1667 : {
1668 : // DOM method, so handle BeginUpdate/EndUpdate
1669 0 : MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
1670 0 : nsCSSStyleSheet::SetEnabled(!aDisabled);
1671 0 : return NS_OK;
1672 : }
1673 :
1674 : NS_IMETHODIMP
1675 0 : nsCSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
1676 : {
1677 0 : *aOwnerNode = mOwningNode;
1678 0 : NS_IF_ADDREF(*aOwnerNode);
1679 0 : return NS_OK;
1680 : }
1681 :
1682 : NS_IMETHODIMP
1683 0 : nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
1684 : {
1685 0 : NS_ENSURE_ARG_POINTER(aParentStyleSheet);
1686 :
1687 0 : NS_IF_ADDREF(*aParentStyleSheet = mParent);
1688 :
1689 0 : return NS_OK;
1690 : }
1691 :
1692 : NS_IMETHODIMP
1693 0 : nsCSSStyleSheet::GetHref(nsAString& aHref)
1694 : {
1695 0 : if (mInner->mOriginalSheetURI) {
1696 0 : nsCAutoString str;
1697 0 : mInner->mOriginalSheetURI->GetSpec(str);
1698 0 : CopyUTF8toUTF16(str, aHref);
1699 : } else {
1700 0 : SetDOMStringToNull(aHref);
1701 : }
1702 :
1703 0 : return NS_OK;
1704 : }
1705 :
1706 : /* virtual */ void
1707 0 : nsCSSStyleSheet::GetTitle(nsString& aTitle) const
1708 : {
1709 0 : aTitle = mTitle;
1710 0 : }
1711 :
1712 : NS_IMETHODIMP
1713 0 : nsCSSStyleSheet::GetTitle(nsAString& aTitle)
1714 : {
1715 0 : aTitle.Assign(mTitle);
1716 0 : return NS_OK;
1717 : }
1718 :
1719 : NS_IMETHODIMP
1720 0 : nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia)
1721 : {
1722 0 : NS_ENSURE_ARG_POINTER(aMedia);
1723 0 : *aMedia = nsnull;
1724 :
1725 0 : if (!mMedia) {
1726 0 : mMedia = new nsMediaList();
1727 0 : NS_ENSURE_TRUE(mMedia, NS_ERROR_OUT_OF_MEMORY);
1728 0 : mMedia->SetStyleSheet(this);
1729 : }
1730 :
1731 0 : *aMedia = mMedia;
1732 0 : NS_ADDREF(*aMedia);
1733 :
1734 0 : return NS_OK;
1735 : }
1736 :
1737 : NS_IMETHODIMP
1738 0 : nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
1739 : {
1740 0 : if (mOwnerRule) {
1741 0 : NS_IF_ADDREF(*aOwnerRule = mOwnerRule->GetDOMRule());
1742 : } else {
1743 0 : *aOwnerRule = nsnull;
1744 : }
1745 0 : return NS_OK;
1746 : }
1747 :
1748 : NS_IMETHODIMP
1749 0 : nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
1750 : {
1751 : // No doing this on incomplete sheets!
1752 0 : if (!mInner->mComplete) {
1753 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1754 : }
1755 :
1756 : //-- Security check: Only scripts whose principal subsumes that of the
1757 : // style sheet can access rule collections.
1758 0 : nsresult rv = SubjectSubsumesInnerPrincipal();
1759 0 : NS_ENSURE_SUCCESS(rv, rv);
1760 :
1761 : // OK, security check passed, so get the rule collection
1762 0 : if (nsnull == mRuleCollection) {
1763 0 : mRuleCollection = new CSSRuleListImpl(this);
1764 0 : if (nsnull == mRuleCollection) {
1765 0 : return NS_ERROR_OUT_OF_MEMORY;
1766 : }
1767 0 : NS_ADDREF(mRuleCollection);
1768 : }
1769 :
1770 0 : *aCssRules = mRuleCollection;
1771 0 : NS_ADDREF(mRuleCollection);
1772 :
1773 0 : return NS_OK;
1774 : }
1775 :
1776 : NS_IMETHODIMP
1777 0 : nsCSSStyleSheet::InsertRule(const nsAString& aRule,
1778 : PRUint32 aIndex,
1779 : PRUint32* aReturn)
1780 : {
1781 : //-- Security check: Only scripts whose principal subsumes that of the
1782 : // style sheet can modify rule collections.
1783 0 : nsresult rv = SubjectSubsumesInnerPrincipal();
1784 0 : NS_ENSURE_SUCCESS(rv, rv);
1785 :
1786 0 : return InsertRuleInternal(aRule, aIndex, aReturn);
1787 : }
1788 :
1789 : nsresult
1790 0 : nsCSSStyleSheet::InsertRuleInternal(const nsAString& aRule,
1791 : PRUint32 aIndex,
1792 : PRUint32* aReturn)
1793 : {
1794 : // No doing this if the sheet is not complete!
1795 0 : if (!mInner->mComplete) {
1796 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1797 : }
1798 :
1799 0 : if (aRule.IsEmpty()) {
1800 : // Nothing to do here
1801 0 : return NS_OK;
1802 : }
1803 :
1804 : nsresult result;
1805 0 : result = WillDirty();
1806 0 : if (NS_FAILED(result))
1807 0 : return result;
1808 :
1809 0 : if (aIndex > PRUint32(mInner->mOrderedRules.Count()))
1810 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1811 :
1812 0 : NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX,
1813 : "Too many style rules!");
1814 :
1815 : // Hold strong ref to the CSSLoader in case the document update
1816 : // kills the document
1817 0 : nsRefPtr<css::Loader> loader;
1818 0 : if (mDocument) {
1819 0 : loader = mDocument->CSSLoader();
1820 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
1821 : }
1822 :
1823 0 : nsCSSParser css(loader, this);
1824 :
1825 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1826 :
1827 0 : nsCOMArray<css::Rule> rules;
1828 : result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
1829 0 : mInner->mPrincipal, rules);
1830 0 : if (NS_FAILED(result))
1831 0 : return result;
1832 :
1833 0 : PRInt32 rulecount = rules.Count();
1834 0 : if (rulecount == 0) {
1835 : // Since we know aRule was not an empty string, just throw
1836 0 : return NS_ERROR_DOM_SYNTAX_ERR;
1837 : }
1838 :
1839 : // Hierarchy checking. Just check the first and last rule in the list.
1840 :
1841 : // check that we're not inserting before a charset rule
1842 0 : css::Rule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
1843 0 : if (nextRule) {
1844 0 : PRInt32 nextType = nextRule->GetType();
1845 0 : if (nextType == css::Rule::CHARSET_RULE) {
1846 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1847 : }
1848 :
1849 : // check last rule in list
1850 0 : css::Rule* lastRule = rules.ObjectAt(rulecount - 1);
1851 0 : PRInt32 lastType = lastRule->GetType();
1852 :
1853 0 : if (nextType == css::Rule::IMPORT_RULE &&
1854 : lastType != css::Rule::CHARSET_RULE &&
1855 : lastType != css::Rule::IMPORT_RULE) {
1856 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1857 : }
1858 :
1859 0 : if (nextType == css::Rule::NAMESPACE_RULE &&
1860 : lastType != css::Rule::CHARSET_RULE &&
1861 : lastType != css::Rule::IMPORT_RULE &&
1862 : lastType != css::Rule::NAMESPACE_RULE) {
1863 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1864 : }
1865 : }
1866 :
1867 : // check first rule in list
1868 0 : css::Rule* firstRule = rules.ObjectAt(0);
1869 0 : PRInt32 firstType = firstRule->GetType();
1870 0 : if (aIndex != 0) {
1871 0 : if (firstType == css::Rule::CHARSET_RULE) { // no inserting charset at nonzero position
1872 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1873 : }
1874 :
1875 0 : css::Rule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1);
1876 0 : PRInt32 prevType = prevRule->GetType();
1877 :
1878 0 : if (firstType == css::Rule::IMPORT_RULE &&
1879 : prevType != css::Rule::CHARSET_RULE &&
1880 : prevType != css::Rule::IMPORT_RULE) {
1881 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1882 : }
1883 :
1884 0 : if (firstType == css::Rule::NAMESPACE_RULE &&
1885 : prevType != css::Rule::CHARSET_RULE &&
1886 : prevType != css::Rule::IMPORT_RULE &&
1887 : prevType != css::Rule::NAMESPACE_RULE) {
1888 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
1889 : }
1890 : }
1891 :
1892 0 : bool insertResult = mInner->mOrderedRules.InsertObjectsAt(rules, aIndex);
1893 0 : NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY);
1894 0 : DidDirty();
1895 :
1896 0 : for (PRInt32 counter = 0; counter < rulecount; counter++) {
1897 0 : css::Rule* cssRule = rules.ObjectAt(counter);
1898 0 : cssRule->SetStyleSheet(this);
1899 :
1900 0 : PRInt32 type = cssRule->GetType();
1901 0 : if (type == css::Rule::NAMESPACE_RULE) {
1902 : // XXXbz does this screw up when inserting a namespace rule before
1903 : // another namespace rule that binds the same prefix to a different
1904 : // namespace?
1905 0 : result = RegisterNamespaceRule(cssRule);
1906 0 : NS_ENSURE_SUCCESS(result, result);
1907 : }
1908 :
1909 : // We don't notify immediately for @import rules, but rather when
1910 : // the sheet the rule is importing is loaded
1911 0 : bool notify = true;
1912 0 : if (type == css::Rule::IMPORT_RULE) {
1913 0 : nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
1914 0 : NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
1915 0 : nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
1916 0 : importRule->GetStyleSheet(getter_AddRefs(childSheet));
1917 0 : if (!childSheet) {
1918 0 : notify = false;
1919 : }
1920 : }
1921 0 : if (mDocument && notify) {
1922 0 : mDocument->StyleRuleAdded(this, cssRule);
1923 : }
1924 : }
1925 :
1926 0 : *aReturn = aIndex;
1927 0 : return NS_OK;
1928 : }
1929 :
1930 : NS_IMETHODIMP
1931 0 : nsCSSStyleSheet::DeleteRule(PRUint32 aIndex)
1932 : {
1933 0 : nsresult result = NS_ERROR_DOM_INDEX_SIZE_ERR;
1934 : // No doing this if the sheet is not complete!
1935 0 : if (!mInner->mComplete) {
1936 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1937 : }
1938 :
1939 : //-- Security check: Only scripts whose principal subsumes that of the
1940 : // style sheet can modify rule collections.
1941 0 : nsresult rv = SubjectSubsumesInnerPrincipal();
1942 0 : NS_ENSURE_SUCCESS(rv, rv);
1943 :
1944 : // XXX TBI: handle @rule types
1945 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1946 :
1947 0 : result = WillDirty();
1948 :
1949 0 : if (NS_SUCCEEDED(result)) {
1950 0 : if (aIndex >= PRUint32(mInner->mOrderedRules.Count()))
1951 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1952 :
1953 0 : NS_ASSERTION(PRUint32(mInner->mOrderedRules.Count()) <= PR_INT32_MAX,
1954 : "Too many style rules!");
1955 :
1956 : // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
1957 0 : nsRefPtr<css::Rule> rule = mInner->mOrderedRules.ObjectAt(aIndex);
1958 0 : if (rule) {
1959 0 : mInner->mOrderedRules.RemoveObjectAt(aIndex);
1960 0 : rule->SetStyleSheet(nsnull);
1961 0 : DidDirty();
1962 :
1963 0 : if (mDocument) {
1964 0 : mDocument->StyleRuleRemoved(this, rule);
1965 : }
1966 : }
1967 : }
1968 :
1969 0 : return result;
1970 : }
1971 :
1972 : nsresult
1973 0 : nsCSSStyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, PRUint32 aIndex)
1974 : {
1975 0 : NS_ENSURE_ARG_POINTER(aGroup);
1976 0 : NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!");
1977 : nsresult result;
1978 0 : nsRefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
1979 0 : NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
1980 :
1981 : // check that the rule actually belongs to this sheet!
1982 0 : if (this != rule->GetStyleSheet()) {
1983 0 : return NS_ERROR_INVALID_ARG;
1984 : }
1985 :
1986 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
1987 :
1988 0 : result = WillDirty();
1989 0 : NS_ENSURE_SUCCESS(result, result);
1990 :
1991 0 : result = aGroup->DeleteStyleRuleAt(aIndex);
1992 0 : NS_ENSURE_SUCCESS(result, result);
1993 :
1994 0 : rule->SetStyleSheet(nsnull);
1995 :
1996 0 : DidDirty();
1997 :
1998 0 : if (mDocument) {
1999 0 : mDocument->StyleRuleRemoved(this, rule);
2000 : }
2001 :
2002 0 : return NS_OK;
2003 : }
2004 :
2005 : nsresult
2006 0 : nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule,
2007 : css::GroupRule* aGroup,
2008 : PRUint32 aIndex,
2009 : PRUint32* _retval)
2010 : {
2011 : nsresult result;
2012 0 : NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!");
2013 : // check that the group actually belongs to this sheet!
2014 0 : if (this != aGroup->GetStyleSheet()) {
2015 0 : return NS_ERROR_INVALID_ARG;
2016 : }
2017 :
2018 0 : if (aRule.IsEmpty()) {
2019 : // Nothing to do here
2020 0 : return NS_OK;
2021 : }
2022 :
2023 : // Hold strong ref to the CSSLoader in case the document update
2024 : // kills the document
2025 0 : nsRefPtr<css::Loader> loader;
2026 0 : if (mDocument) {
2027 0 : loader = mDocument->CSSLoader();
2028 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
2029 : }
2030 :
2031 0 : nsCSSParser css(loader, this);
2032 :
2033 : // parse and grab the rule
2034 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2035 :
2036 0 : result = WillDirty();
2037 0 : NS_ENSURE_SUCCESS(result, result);
2038 :
2039 0 : nsCOMArray<css::Rule> rules;
2040 : result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
2041 0 : mInner->mPrincipal, rules);
2042 0 : NS_ENSURE_SUCCESS(result, result);
2043 :
2044 0 : PRInt32 rulecount = rules.Count();
2045 0 : if (rulecount == 0) {
2046 : // Since we know aRule was not an empty string, just throw
2047 0 : return NS_ERROR_DOM_SYNTAX_ERR;
2048 : }
2049 :
2050 : PRInt32 counter;
2051 : css::Rule* rule;
2052 0 : for (counter = 0; counter < rulecount; counter++) {
2053 : // Only rulesets are allowed in a group as of CSS2
2054 0 : rule = rules.ObjectAt(counter);
2055 0 : if (rule->GetType() != css::Rule::STYLE_RULE) {
2056 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2057 : }
2058 : }
2059 :
2060 0 : result = aGroup->InsertStyleRulesAt(aIndex, rules);
2061 0 : NS_ENSURE_SUCCESS(result, result);
2062 0 : DidDirty();
2063 0 : for (counter = 0; counter < rulecount; counter++) {
2064 0 : rule = rules.ObjectAt(counter);
2065 :
2066 0 : if (mDocument) {
2067 0 : mDocument->StyleRuleAdded(this, rule);
2068 : }
2069 : }
2070 :
2071 0 : *_retval = aIndex;
2072 0 : return NS_OK;
2073 : }
2074 :
2075 : nsresult
2076 0 : nsCSSStyleSheet::ReplaceRuleInGroup(css::GroupRule* aGroup,
2077 : css::Rule* aOld, css::Rule* aNew)
2078 : {
2079 : nsresult result;
2080 0 : NS_PRECONDITION(mInner->mComplete, "No replacing in an incomplete sheet!");
2081 0 : NS_ASSERTION(this == aGroup->GetStyleSheet(), "group doesn't belong to this sheet");
2082 0 : result = WillDirty();
2083 0 : NS_ENSURE_SUCCESS(result, result);
2084 :
2085 0 : result = aGroup->ReplaceStyleRule(aOld, aNew);
2086 0 : DidDirty();
2087 0 : return result;
2088 : }
2089 :
2090 : // nsICSSLoaderObserver implementation
2091 : NS_IMETHODIMP
2092 0 : nsCSSStyleSheet::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
2093 : bool aWasAlternate,
2094 : nsresult aStatus)
2095 : {
2096 0 : NS_ASSERTION(this == aSheet->GetParentSheet(),
2097 : "We are being notified of a sheet load for a sheet that is not our child!");
2098 :
2099 0 : if (mDocument && NS_SUCCEEDED(aStatus)) {
2100 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
2101 :
2102 : // XXXldb @import rules shouldn't even implement nsIStyleRule (but
2103 : // they do)!
2104 0 : mDocument->StyleRuleAdded(this, aSheet->GetOwnerRule());
2105 : }
2106 :
2107 0 : return NS_OK;
2108 : }
2109 :
2110 : /* virtual */ nsIURI*
2111 0 : nsCSSStyleSheet::GetOriginalURI() const
2112 : {
2113 0 : return mInner->mOriginalSheetURI;
2114 : }
2115 :
2116 : nsresult
2117 0 : NS_NewCSSStyleSheet(nsCSSStyleSheet** aInstancePtrResult)
2118 : {
2119 0 : *aInstancePtrResult = nsnull;
2120 0 : nsCSSStyleSheet *it = new nsCSSStyleSheet();
2121 :
2122 0 : if (!it) {
2123 0 : return NS_ERROR_OUT_OF_MEMORY;
2124 : }
2125 :
2126 0 : NS_ADDREF(it);
2127 :
2128 0 : if (!it->mInner || !it->mInner->mPrincipal) {
2129 0 : NS_RELEASE(it);
2130 0 : return NS_ERROR_OUT_OF_MEMORY;
2131 : }
2132 :
2133 0 : *aInstancePtrResult = it;
2134 0 : return NS_OK;
2135 : }
|