ServoCSSRuleList.cpp (11887B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* representation of CSSRuleList for stylo */ 8 9 #include "mozilla/ServoCSSRuleList.h" 10 11 #include "mozilla/ServoBindings.h" 12 #include "mozilla/StyleSheet.h" 13 #include "mozilla/dom/CSSContainerRule.h" 14 #include "mozilla/dom/CSSCounterStyleRule.h" 15 #include "mozilla/dom/CSSCustomMediaRule.h" 16 #include "mozilla/dom/CSSFontFaceRule.h" 17 #include "mozilla/dom/CSSFontFeatureValuesRule.h" 18 #include "mozilla/dom/CSSFontPaletteValuesRule.h" 19 #include "mozilla/dom/CSSImportRule.h" 20 #include "mozilla/dom/CSSKeyframesRule.h" 21 #include "mozilla/dom/CSSLayerBlockRule.h" 22 #include "mozilla/dom/CSSLayerStatementRule.h" 23 #include "mozilla/dom/CSSMarginRule.h" 24 #include "mozilla/dom/CSSMediaRule.h" 25 #include "mozilla/dom/CSSMozDocumentRule.h" 26 #include "mozilla/dom/CSSNamespaceRule.h" 27 #include "mozilla/dom/CSSNestedDeclarations.h" 28 #include "mozilla/dom/CSSPageRule.h" 29 #include "mozilla/dom/CSSPositionTryRule.h" 30 #include "mozilla/dom/CSSPropertyRule.h" 31 #include "mozilla/dom/CSSScopeRule.h" 32 #include "mozilla/dom/CSSStartingStyleRule.h" 33 #include "mozilla/dom/CSSStyleRule.h" 34 #include "mozilla/dom/CSSSupportsRule.h" 35 #include "mozilla/dom/Document.h" 36 37 using namespace mozilla::dom; 38 39 namespace mozilla { 40 41 ServoCSSRuleList::ServoCSSRuleList( 42 already_AddRefed<StyleLockedCssRules> aRawRules, StyleSheet* aSheet, 43 css::GroupRule* aParentRule) 44 : mStyleSheet(aSheet), mParentRule(aParentRule), mRawRules(aRawRules) { 45 ResetRules(); 46 } 47 48 // QueryInterface implementation for ServoCSSRuleList 49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoCSSRuleList) 50 NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList) 51 52 NS_IMPL_ADDREF_INHERITED(ServoCSSRuleList, dom::CSSRuleList) 53 NS_IMPL_RELEASE_INHERITED(ServoCSSRuleList, dom::CSSRuleList) 54 55 NS_IMPL_CYCLE_COLLECTION_CLASS(ServoCSSRuleList) 56 57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ServoCSSRuleList) 58 tmp->DropAllRules(); 59 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList) 60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList, 61 dom::CSSRuleList) 62 tmp->EnumerateInstantiatedRules([&](css::Rule* aRule, uint32_t) { 63 if (!aRule->IsCCLeaf()) { 64 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]"); 65 cb.NoteXPCOMChild(aRule); 66 } 67 }); 68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 69 70 css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) { 71 uintptr_t rule = mRules[aIndex]; 72 if (rule <= kMaxRuleType) { 73 RefPtr<css::Rule> ruleObj = nullptr; 74 switch (StyleCssRuleType(rule)) { 75 #define CASE_RULE_WITH_PREFIX(const_, prefix_, name_) \ 76 case StyleCssRuleType::const_: { \ 77 uint32_t line = 0, column = 0; \ 78 RefPtr<Style##prefix_##const_##Rule> raw = \ 79 Servo_CssRules_Get##const_##RuleAt(mRawRules, aIndex, &line, &column) \ 80 .Consume(); \ 81 MOZ_ASSERT(raw); \ 82 ruleObj = new CSS##name_##Rule(raw.forget(), mStyleSheet, mParentRule, \ 83 line, column); \ 84 MOZ_ASSERT(ruleObj->Type() == StyleCssRuleType(rule)); \ 85 break; \ 86 } 87 #define CASE_RULE_LOCKED(const_, name_) \ 88 CASE_RULE_WITH_PREFIX(const_, Locked, name_) 89 #define CASE_RULE_UNLOCKED(const_, name_) CASE_RULE_WITH_PREFIX(const_, , name_) 90 CASE_RULE_LOCKED(Style, Style) 91 CASE_RULE_LOCKED(Keyframes, Keyframes) 92 CASE_RULE_UNLOCKED(Media, Media) 93 CASE_RULE_UNLOCKED(Namespace, Namespace) 94 CASE_RULE_UNLOCKED(Margin, Margin) 95 CASE_RULE_LOCKED(Page, Page) 96 CASE_RULE_UNLOCKED(Property, Property) 97 CASE_RULE_UNLOCKED(Supports, Supports) 98 CASE_RULE_UNLOCKED(Document, MozDocument) 99 CASE_RULE_LOCKED(Import, Import) 100 CASE_RULE_UNLOCKED(FontFeatureValues, FontFeatureValues) 101 CASE_RULE_UNLOCKED(FontPaletteValues, FontPaletteValues) 102 CASE_RULE_LOCKED(FontFace, FontFace) 103 CASE_RULE_LOCKED(CounterStyle, CounterStyle) 104 CASE_RULE_UNLOCKED(LayerBlock, LayerBlock) 105 CASE_RULE_UNLOCKED(LayerStatement, LayerStatement) 106 CASE_RULE_UNLOCKED(Container, Container) 107 CASE_RULE_UNLOCKED(Scope, Scope) 108 CASE_RULE_UNLOCKED(StartingStyle, StartingStyle) 109 CASE_RULE_LOCKED(PositionTry, PositionTry) 110 CASE_RULE_LOCKED(NestedDeclarations, NestedDeclarations) 111 CASE_RULE_UNLOCKED(CustomMedia, CustomMedia) 112 #undef CASE_RULE_LOCKED 113 #undef CASE_RULE_UNLOCKED 114 #undef CASE_RULE_WITH_PREFIX 115 case StyleCssRuleType::Keyframe: 116 MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here"); 117 return nullptr; 118 } 119 rule = CastToUint(ruleObj.forget().take()); 120 mRules[aIndex] = rule; 121 } 122 return CastToPtr(rule); 123 } 124 125 css::Rule* ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound) { 126 if (aIndex >= mRules.Length()) { 127 aFound = false; 128 return nullptr; 129 } 130 aFound = true; 131 return GetRule(aIndex); 132 } 133 134 template <typename Func> 135 void ServoCSSRuleList::EnumerateInstantiatedRules(Func aCallback) { 136 uint32_t index = 0; 137 for (uintptr_t rule : mRules) { 138 if (rule > kMaxRuleType) { 139 aCallback(CastToPtr(rule), index); 140 } 141 index++; 142 } 143 } 144 145 static void DropRule(already_AddRefed<css::Rule> aRule) { 146 RefPtr<css::Rule> rule = aRule; 147 rule->DropReferences(); 148 } 149 150 void ServoCSSRuleList::ResetRules() { 151 // DropRule could reenter here via the cycle collector. 152 auto rules = std::move(mRules); 153 for (uintptr_t rule : rules) { 154 if (rule > kMaxRuleType) { 155 DropRule(already_AddRefed<css::Rule>(CastToPtr(rule))); 156 } 157 } 158 MOZ_ASSERT(mRules.IsEmpty()); 159 if (mRawRules) { 160 Servo_CssRules_ListTypes(mRawRules, &mRules); 161 } 162 } 163 164 void ServoCSSRuleList::DropAllRules() { 165 mStyleSheet = nullptr; 166 mParentRule = nullptr; 167 mRawRules = nullptr; 168 169 ResetRules(); 170 } 171 172 void ServoCSSRuleList::DropSheetReference() { 173 // If mStyleSheet is not set on this rule list, then it is not set on any of 174 // its instantiated rules either. To avoid O(N^2) beavhior in the depth of 175 // group rule nesting, which can happen if we are unlinked starting from the 176 // deepest nested group rule, skip recursing into the rule list if we know we 177 // don't need to. 178 if (!mStyleSheet) { 179 return; 180 } 181 mStyleSheet = nullptr; 182 EnumerateInstantiatedRules( 183 [](css::Rule* rule, uint32_t) { rule->DropSheetReference(); }); 184 } 185 186 void ServoCSSRuleList::DropParentRuleReference() { 187 mParentRule = nullptr; 188 EnumerateInstantiatedRules( 189 [](css::Rule* rule, uint32_t) { rule->DropParentRuleReference(); }); 190 } 191 192 nsresult ServoCSSRuleList::InsertRule(const nsACString& aRule, 193 uint32_t aIndex) { 194 MOZ_ASSERT(mStyleSheet, 195 "Caller must ensure that " 196 "the list is not unlinked from stylesheet"); 197 198 if (!mRawRules || IsReadOnly()) { 199 return NS_OK; 200 } 201 202 mStyleSheet->WillDirty(); 203 204 css::Loader* loader = nullptr; 205 auto allowImportRules = mStyleSheet->SelfOrAncestorIsConstructed() 206 ? StyleAllowImportRules::No 207 : StyleAllowImportRules::Yes; 208 209 // TODO(emilio, bug 1535456): Should probably always be able to get a handle 210 // to some loader if we're parsing an @import rule, but which one? 211 // 212 // StyleSheet::ReparseSheet just mints a new loader, but that'd be wrong in 213 // this case I think, since such a load will bypass CSP checks. 214 if (Document* doc = mStyleSheet->GetAssociatedDocument()) { 215 loader = &doc->EnsureCSSLoader(); 216 } 217 auto containingState = css::Rule::ContainingRuleState::From(mParentRule); 218 StyleCssRuleType type; 219 nsresult rv = Servo_CssRules_InsertRule( 220 mRawRules, mStyleSheet->RawContents(), &aRule, aIndex, 221 containingState.mContainingTypes, 222 containingState.mParseRelativeType.ptrOr(nullptr), loader, 223 allowImportRules, mStyleSheet, &type); 224 NS_ENSURE_SUCCESS(rv, rv); 225 mRules.InsertElementAt(aIndex, uintptr_t(type)); 226 return rv; 227 } 228 229 nsresult ServoCSSRuleList::DeleteRule(uint32_t aIndex) { 230 if (!mRawRules || IsReadOnly()) { 231 return NS_OK; 232 } 233 234 nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex); 235 if (!NS_FAILED(rv)) { 236 uintptr_t rule = mRules[aIndex]; 237 mRules.RemoveElementAt(aIndex); 238 if (rule > kMaxRuleType) { 239 DropRule(already_AddRefed<css::Rule>(CastToPtr(rule))); 240 } 241 } 242 return rv; 243 } 244 245 void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules, 246 bool aFromClone) { 247 mRawRules = std::move(aNewRules); 248 if (!aFromClone) { 249 ResetRules(); 250 return; 251 } 252 253 EnumerateInstantiatedRules([&](css::Rule* aRule, uint32_t aIndex) { 254 #define RULE_CASE_WITH_PREFIX(constant_, prefix_, type_) \ 255 case StyleCssRuleType::constant_: { \ 256 uint32_t line = 0, column = 0; \ 257 RefPtr<Style##prefix_##constant_##Rule> raw = \ 258 Servo_CssRules_Get##constant_##RuleAt(mRawRules, aIndex, &line, \ 259 &column) \ 260 .Consume(); \ 261 static_cast<dom::CSS##type_##Rule*>(aRule)->SetRawAfterClone( \ 262 std::move(raw)); \ 263 break; \ 264 } 265 #define RULE_CASE_LOCKED(constant_, type_) \ 266 RULE_CASE_WITH_PREFIX(constant_, Locked, type_) 267 #define RULE_CASE_UNLOCKED(constant_, type_) \ 268 RULE_CASE_WITH_PREFIX(constant_, , type_) 269 switch (aRule->Type()) { 270 RULE_CASE_LOCKED(Style, Style) 271 RULE_CASE_LOCKED(Keyframes, Keyframes) 272 RULE_CASE_UNLOCKED(Media, Media) 273 RULE_CASE_UNLOCKED(Namespace, Namespace) 274 RULE_CASE_UNLOCKED(Margin, Margin) 275 RULE_CASE_LOCKED(Page, Page) 276 RULE_CASE_UNLOCKED(Property, Property) 277 RULE_CASE_UNLOCKED(Supports, Supports) 278 RULE_CASE_UNLOCKED(Document, MozDocument) 279 RULE_CASE_LOCKED(Import, Import) 280 RULE_CASE_UNLOCKED(FontFeatureValues, FontFeatureValues) 281 RULE_CASE_UNLOCKED(FontPaletteValues, FontPaletteValues) 282 RULE_CASE_LOCKED(FontFace, FontFace) 283 RULE_CASE_LOCKED(CounterStyle, CounterStyle) 284 RULE_CASE_UNLOCKED(LayerBlock, LayerBlock) 285 RULE_CASE_UNLOCKED(LayerStatement, LayerStatement) 286 RULE_CASE_UNLOCKED(Container, Container) 287 RULE_CASE_UNLOCKED(Scope, Scope) 288 RULE_CASE_UNLOCKED(StartingStyle, StartingStyle) 289 RULE_CASE_LOCKED(PositionTry, PositionTry) 290 RULE_CASE_LOCKED(NestedDeclarations, NestedDeclarations) 291 RULE_CASE_UNLOCKED(CustomMedia, CustomMedia) 292 case StyleCssRuleType::Keyframe: 293 MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here"); 294 break; 295 } 296 #undef RULE_CASE_WITH_PREFIX 297 #undef RULE_CASE_LOCKED 298 #undef RULE_CASE_UNLOCKED 299 }); 300 } 301 302 ServoCSSRuleList::~ServoCSSRuleList() { 303 MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared"); 304 MOZ_ASSERT(!mParentRule, "Backpointer should have been cleared"); 305 DropAllRules(); 306 } 307 308 bool ServoCSSRuleList::IsReadOnly() const { 309 MOZ_ASSERT(!mStyleSheet || !mParentRule || 310 mStyleSheet->IsReadOnly() == mParentRule->IsReadOnly(), 311 "a parent rule should be read only iff the owning sheet is " 312 "read only"); 313 return mStyleSheet && mStyleSheet->IsReadOnly(); 314 } 315 316 } // namespace mozilla