layout/style/nsCSSStyleSheet.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial