no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / layout / style / ServoCSSRuleList.cpp
blobe26305c9932aa908ef1c8faaeffb69d3dfba759e
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/. */
7 /* representation of CSSRuleList for stylo */
9 #include "mozilla/ServoCSSRuleList.h"
11 #include "mozilla/dom/CSSCounterStyleRule.h"
12 #include "mozilla/dom/CSSFontFaceRule.h"
13 #include "mozilla/dom/CSSFontFeatureValuesRule.h"
14 #include "mozilla/dom/CSSFontPaletteValuesRule.h"
15 #include "mozilla/dom/CSSImportRule.h"
16 #include "mozilla/dom/CSSLayerBlockRule.h"
17 #include "mozilla/dom/CSSLayerStatementRule.h"
18 #include "mozilla/dom/CSSKeyframesRule.h"
19 #include "mozilla/dom/CSSContainerRule.h"
20 #include "mozilla/dom/CSSMediaRule.h"
21 #include "mozilla/dom/CSSMozDocumentRule.h"
22 #include "mozilla/dom/CSSNamespaceRule.h"
23 #include "mozilla/dom/CSSPageRule.h"
24 #include "mozilla/dom/CSSPropertyRule.h"
25 #include "mozilla/dom/CSSStyleRule.h"
26 #include "mozilla/dom/CSSSupportsRule.h"
27 #include "mozilla/dom/CSSScopeRule.h"
28 #include "mozilla/IntegerRange.h"
29 #include "mozilla/ServoBindings.h"
30 #include "mozilla/StyleSheet.h"
31 #include "mozilla/dom/Document.h"
33 using namespace mozilla::dom;
35 namespace mozilla {
37 ServoCSSRuleList::ServoCSSRuleList(
38 already_AddRefed<StyleLockedCssRules> aRawRules, StyleSheet* aSheet,
39 css::GroupRule* aParentRule)
40 : mStyleSheet(aSheet), mParentRule(aParentRule), mRawRules(aRawRules) {
41 ResetRules();
44 // QueryInterface implementation for ServoCSSRuleList
45 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoCSSRuleList)
46 NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList)
48 NS_IMPL_ADDREF_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
49 NS_IMPL_RELEASE_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
51 NS_IMPL_CYCLE_COLLECTION_CLASS(ServoCSSRuleList)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ServoCSSRuleList)
54 tmp->DropAllRules();
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList,
57 dom::CSSRuleList)
58 tmp->EnumerateInstantiatedRules([&](css::Rule* aRule, uint32_t) {
59 if (!aRule->IsCCLeaf()) {
60 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
61 cb.NoteXPCOMChild(aRule);
63 });
64 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
66 css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) {
67 uintptr_t rule = mRules[aIndex];
68 if (rule <= kMaxRuleType) {
69 RefPtr<css::Rule> ruleObj = nullptr;
70 switch (StyleCssRuleType(rule)) {
71 #define CASE_RULE_WITH_PREFIX(const_, prefix_, name_) \
72 case StyleCssRuleType::const_: { \
73 uint32_t line = 0, column = 0; \
74 RefPtr<Style##prefix_##const_##Rule> raw = \
75 Servo_CssRules_Get##const_##RuleAt(mRawRules, aIndex, &line, &column) \
76 .Consume(); \
77 MOZ_ASSERT(raw); \
78 ruleObj = new CSS##name_##Rule(raw.forget(), mStyleSheet, mParentRule, \
79 line, column); \
80 MOZ_ASSERT(ruleObj->Type() == StyleCssRuleType(rule)); \
81 break; \
83 #define CASE_RULE_LOCKED(const_, name_) \
84 CASE_RULE_WITH_PREFIX(const_, Locked, name_)
85 #define CASE_RULE_UNLOCKED(const_, name_) CASE_RULE_WITH_PREFIX(const_, , name_)
86 CASE_RULE_LOCKED(Style, Style)
87 CASE_RULE_LOCKED(Keyframes, Keyframes)
88 CASE_RULE_UNLOCKED(Media, Media)
89 CASE_RULE_UNLOCKED(Namespace, Namespace)
90 CASE_RULE_LOCKED(Page, Page)
91 CASE_RULE_UNLOCKED(Property, Property)
92 CASE_RULE_UNLOCKED(Supports, Supports)
93 CASE_RULE_UNLOCKED(Document, MozDocument)
94 CASE_RULE_LOCKED(Import, Import)
95 CASE_RULE_UNLOCKED(FontFeatureValues, FontFeatureValues)
96 CASE_RULE_UNLOCKED(FontPaletteValues, FontPaletteValues)
97 CASE_RULE_LOCKED(FontFace, FontFace)
98 CASE_RULE_LOCKED(CounterStyle, CounterStyle)
99 CASE_RULE_UNLOCKED(LayerBlock, LayerBlock)
100 CASE_RULE_UNLOCKED(LayerStatement, LayerStatement)
101 CASE_RULE_UNLOCKED(Container, Container)
102 CASE_RULE_UNLOCKED(Scope, Scope)
103 #undef CASE_RULE_LOCKED
104 #undef CASE_RULE_UNLOCKED
105 #undef CASE_RULE_WITH_PREFIX
106 case StyleCssRuleType::Keyframe:
107 MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
108 return nullptr;
109 case StyleCssRuleType::Margin:
110 // Margin rules not implemented yet, see bug 1864737
111 return nullptr;
113 rule = CastToUint(ruleObj.forget().take());
114 mRules[aIndex] = rule;
116 return CastToPtr(rule);
119 css::Rule* ServoCSSRuleList::IndexedGetter(uint32_t aIndex, bool& aFound) {
120 if (aIndex >= mRules.Length()) {
121 aFound = false;
122 return nullptr;
124 aFound = true;
125 return GetRule(aIndex);
128 template <typename Func>
129 void ServoCSSRuleList::EnumerateInstantiatedRules(Func aCallback) {
130 uint32_t index = 0;
131 for (uintptr_t rule : mRules) {
132 if (rule > kMaxRuleType) {
133 aCallback(CastToPtr(rule), index);
135 index++;
139 static void DropRule(already_AddRefed<css::Rule> aRule) {
140 RefPtr<css::Rule> rule = aRule;
141 rule->DropReferences();
144 void ServoCSSRuleList::ResetRules() {
145 // DropRule could reenter here via the cycle collector.
146 auto rules = std::move(mRules);
147 for (uintptr_t rule : rules) {
148 if (rule > kMaxRuleType) {
149 DropRule(already_AddRefed<css::Rule>(CastToPtr(rule)));
152 MOZ_ASSERT(mRules.IsEmpty());
153 if (mRawRules) {
154 Servo_CssRules_ListTypes(mRawRules, &mRules);
158 void ServoCSSRuleList::DropAllRules() {
159 mStyleSheet = nullptr;
160 mParentRule = nullptr;
161 mRawRules = nullptr;
163 ResetRules();
166 void ServoCSSRuleList::DropSheetReference() {
167 // If mStyleSheet is not set on this rule list, then it is not set on any of
168 // its instantiated rules either. To avoid O(N^2) beavhior in the depth of
169 // group rule nesting, which can happen if we are unlinked starting from the
170 // deepest nested group rule, skip recursing into the rule list if we know we
171 // don't need to.
172 if (!mStyleSheet) {
173 return;
175 mStyleSheet = nullptr;
176 EnumerateInstantiatedRules(
177 [](css::Rule* rule, uint32_t) { rule->DropSheetReference(); });
180 void ServoCSSRuleList::DropParentRuleReference() {
181 mParentRule = nullptr;
182 EnumerateInstantiatedRules(
183 [](css::Rule* rule, uint32_t) { rule->DropParentRuleReference(); });
186 nsresult ServoCSSRuleList::InsertRule(const nsACString& aRule,
187 uint32_t aIndex) {
188 MOZ_ASSERT(mStyleSheet,
189 "Caller must ensure that "
190 "the list is not unlinked from stylesheet");
192 if (!mRawRules || IsReadOnly()) {
193 return NS_OK;
196 mStyleSheet->WillDirty();
198 css::Loader* loader = nullptr;
199 auto allowImportRules = mStyleSheet->SelfOrAncestorIsConstructed()
200 ? StyleAllowImportRules::No
201 : StyleAllowImportRules::Yes;
203 // TODO(emilio, bug 1535456): Should probably always be able to get a handle
204 // to some loader if we're parsing an @import rule, but which one?
206 // StyleSheet::ReparseSheet just mints a new loader, but that'd be wrong in
207 // this case I think, since such a load will bypass CSP checks.
208 if (Document* doc = mStyleSheet->GetAssociatedDocument()) {
209 loader = doc->CSSLoader();
211 StyleCssRuleType type;
212 uint32_t containingTypes = 0;
213 for (css::Rule* rule = mParentRule; rule; rule = rule->GetParentRule()) {
214 containingTypes |= (1 << uint32_t(rule->Type()));
216 nsresult rv = Servo_CssRules_InsertRule(
217 mRawRules, mStyleSheet->RawContents(), &aRule, aIndex, containingTypes,
218 loader, allowImportRules, mStyleSheet, &type);
219 NS_ENSURE_SUCCESS(rv, rv);
220 mRules.InsertElementAt(aIndex, uintptr_t(type));
221 return rv;
224 nsresult ServoCSSRuleList::DeleteRule(uint32_t aIndex) {
225 if (!mRawRules || IsReadOnly()) {
226 return NS_OK;
229 nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex);
230 if (!NS_FAILED(rv)) {
231 uintptr_t rule = mRules[aIndex];
232 mRules.RemoveElementAt(aIndex);
233 if (rule > kMaxRuleType) {
234 DropRule(already_AddRefed<css::Rule>(CastToPtr(rule)));
237 return rv;
240 void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules,
241 bool aFromClone) {
242 mRawRules = std::move(aNewRules);
243 if (!aFromClone) {
244 ResetRules();
245 return;
248 EnumerateInstantiatedRules([&](css::Rule* aRule, uint32_t aIndex) {
249 #define RULE_CASE_WITH_PREFIX(constant_, prefix_, type_) \
250 case StyleCssRuleType::constant_: { \
251 uint32_t line = 0, column = 0; \
252 RefPtr<Style##prefix_##constant_##Rule> raw = \
253 Servo_CssRules_Get##constant_##RuleAt(mRawRules, aIndex, &line, \
254 &column) \
255 .Consume(); \
256 static_cast<dom::CSS##type_##Rule*>(aRule)->SetRawAfterClone( \
257 std::move(raw)); \
258 break; \
260 #define RULE_CASE_LOCKED(constant_, type_) \
261 RULE_CASE_WITH_PREFIX(constant_, Locked, type_)
262 #define RULE_CASE_UNLOCKED(constant_, type_) \
263 RULE_CASE_WITH_PREFIX(constant_, , type_)
264 switch (aRule->Type()) {
265 RULE_CASE_LOCKED(Style, Style)
266 RULE_CASE_LOCKED(Keyframes, Keyframes)
267 RULE_CASE_UNLOCKED(Media, Media)
268 RULE_CASE_UNLOCKED(Namespace, Namespace)
269 RULE_CASE_LOCKED(Page, Page)
270 RULE_CASE_UNLOCKED(Property, Property)
271 RULE_CASE_UNLOCKED(Supports, Supports)
272 RULE_CASE_UNLOCKED(Document, MozDocument)
273 RULE_CASE_LOCKED(Import, Import)
274 RULE_CASE_UNLOCKED(FontFeatureValues, FontFeatureValues)
275 RULE_CASE_UNLOCKED(FontPaletteValues, FontPaletteValues)
276 RULE_CASE_LOCKED(FontFace, FontFace)
277 RULE_CASE_LOCKED(CounterStyle, CounterStyle)
278 RULE_CASE_UNLOCKED(LayerBlock, LayerBlock)
279 RULE_CASE_UNLOCKED(LayerStatement, LayerStatement)
280 RULE_CASE_UNLOCKED(Container, Container)
281 RULE_CASE_UNLOCKED(Scope, Scope)
282 case StyleCssRuleType::Keyframe:
283 MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
284 break;
285 case StyleCssRuleType::Margin:
286 // Margin rules not implemented yet, see bug 1864737
287 break;
289 #undef RULE_CASE_WITH_PREFIX
290 #undef RULE_CASE_LOCKED
291 #undef RULE_CASE_UNLOCKED
295 ServoCSSRuleList::~ServoCSSRuleList() {
296 MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared");
297 MOZ_ASSERT(!mParentRule, "Backpointer should have been cleared");
298 DropAllRules();
301 bool ServoCSSRuleList::IsReadOnly() const {
302 MOZ_ASSERT(!mStyleSheet || !mParentRule ||
303 mStyleSheet->IsReadOnly() == mParentRule->IsReadOnly(),
304 "a parent rule should be read only iff the owning sheet is "
305 "read only");
306 return mStyleSheet && mStyleSheet->IsReadOnly();
309 } // namespace mozilla