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 #include "mozilla/ServoKeyframesRule.h"
8
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/ServoBindings.h"
11 #include "mozilla/ServoKeyframeRule.h"
12
13 #include <limits>
14
15 namespace mozilla {
16
17 // -------------------------------------------
18 // ServoKeyframeList
19 //
20
21 class ServoKeyframeList : public dom::CSSRuleList {
22 public:
ServoKeyframeList(already_AddRefed<RawServoKeyframesRule> aRawRule)23 explicit ServoKeyframeList(already_AddRefed<RawServoKeyframesRule> aRawRule)
24 : mRawRule(aRawRule) {
25 mRules.SetCount(Servo_KeyframesRule_GetCount(mRawRule));
26 }
27
28 NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoKeyframeList,dom::CSSRuleList)29 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoKeyframeList, dom::CSSRuleList)
30
31 void SetParentRule(ServoKeyframesRule* aParentRule) {
32 mParentRule = aParentRule;
33 for (css::Rule* rule : mRules) {
34 if (rule) {
35 rule->SetParentRule(aParentRule);
36 }
37 }
38 }
SetStyleSheet(ServoStyleSheet * aSheet)39 void SetStyleSheet(ServoStyleSheet* aSheet) {
40 mStyleSheet = aSheet;
41 for (css::Rule* rule : mRules) {
42 if (rule) {
43 rule->SetStyleSheet(aSheet);
44 }
45 }
46 }
47
GetParentObject()48 ServoStyleSheet* GetParentObject() final { return mStyleSheet; }
49
GetRule(uint32_t aIndex)50 ServoKeyframeRule* GetRule(uint32_t aIndex) {
51 if (!mRules[aIndex]) {
52 uint32_t line = 0, column = 0;
53 RefPtr<RawServoKeyframe> rule =
54 Servo_KeyframesRule_GetKeyframeAt(mRawRule, aIndex, &line, &column)
55 .Consume();
56 ServoKeyframeRule* ruleObj =
57 new ServoKeyframeRule(rule.forget(), line, column);
58 mRules.ReplaceObjectAt(ruleObj, aIndex);
59 ruleObj->SetStyleSheet(mStyleSheet);
60 ruleObj->SetParentRule(mParentRule);
61 }
62 return static_cast<ServoKeyframeRule*>(mRules[aIndex]);
63 }
64
IndexedGetter(uint32_t aIndex,bool & aFound)65 ServoKeyframeRule* IndexedGetter(uint32_t aIndex, bool& aFound) final {
66 if (aIndex >= mRules.Length()) {
67 aFound = false;
68 return nullptr;
69 }
70 aFound = true;
71 return GetRule(aIndex);
72 }
73
AppendRule()74 void AppendRule() { mRules.AppendObject(nullptr); }
75
RemoveRule(uint32_t aIndex)76 void RemoveRule(uint32_t aIndex) {
77 if (aIndex >= mRules.Length()) {
78 return;
79 }
80 if (css::Rule* child = mRules[aIndex]) {
81 child->SetStyleSheet(nullptr);
82 child->SetParentRule(nullptr);
83 }
84 mRules.RemoveObjectAt(aIndex);
85 }
86
Length()87 uint32_t Length() final { return mRules.Length(); }
88
DropReference()89 void DropReference() {
90 mStyleSheet = nullptr;
91 mParentRule = nullptr;
92 for (css::Rule* rule : mRules) {
93 if (rule) {
94 rule->SetStyleSheet(nullptr);
95 rule->SetParentRule(nullptr);
96 }
97 }
98 }
99
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const100 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
101 size_t n = aMallocSizeOf(this);
102 for (const css::Rule* rule : mRules) {
103 n += rule ? rule->SizeOfIncludingThis(aMallocSizeOf) : 0;
104 }
105 return n;
106 }
107
108 private:
~ServoKeyframeList()109 virtual ~ServoKeyframeList() {
110 MOZ_ASSERT(!mParentRule, "Backpointer should have been cleared");
111 MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared");
112 DropAllRules();
113 }
114
DropAllRules()115 void DropAllRules() {
116 if (mParentRule || mStyleSheet) {
117 DropReference();
118 }
119 mRules.Clear();
120 mRawRule = nullptr;
121 }
122
123 // may be nullptr when the style sheet drops the reference to us.
124 ServoStyleSheet* mStyleSheet = nullptr;
125 ServoKeyframesRule* mParentRule = nullptr;
126 RefPtr<RawServoKeyframesRule> mRawRule;
127 nsCOMArray<css::Rule> mRules;
128 };
129
130 // QueryInterface implementation for ServoKeyframeList
131 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoKeyframeList)
132 NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList)
133
134 NS_IMPL_ADDREF_INHERITED(ServoKeyframeList, dom::CSSRuleList)
135 NS_IMPL_RELEASE_INHERITED(ServoKeyframeList, dom::CSSRuleList)
136
137 NS_IMPL_CYCLE_COLLECTION_CLASS(ServoKeyframeList)
138
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ServoKeyframeList)
140 tmp->DropAllRules();
141 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
142 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoKeyframeList,
143 dom::CSSRuleList)
144 for (css::Rule* rule : tmp->mRules) {
145 if (rule) {
146 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
147 cb.NoteXPCOMChild(rule);
148 }
149 }
150 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
151
152 // -------------------------------------------
153 // ServoKeyframesRule
154 //
155
ServoKeyframesRule(RefPtr<RawServoKeyframesRule> aRawRule,uint32_t aLine,uint32_t aColumn)156 ServoKeyframesRule::ServoKeyframesRule(RefPtr<RawServoKeyframesRule> aRawRule,
157 uint32_t aLine, uint32_t aColumn)
158 // Although this class inherits from GroupRule, we don't want to use
159 // it at all, so it is fine to call the constructor for Gecko. We can
160 // make CSSKeyframesRule inherit from Rule directly once we can get
161 // rid of nsCSSKeyframeRule.
162 : dom::CSSKeyframesRule(aLine, aColumn), mRawRule(Move(aRawRule)) {}
163
~ServoKeyframesRule()164 ServoKeyframesRule::~ServoKeyframesRule() {
165 if (mKeyframeList) {
166 mKeyframeList->DropReference();
167 }
168 }
169
170 NS_IMPL_ADDREF_INHERITED(ServoKeyframesRule, dom::CSSKeyframesRule)
171 NS_IMPL_RELEASE_INHERITED(ServoKeyframesRule, dom::CSSKeyframesRule)
172
173 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServoKeyframesRule)
174 NS_INTERFACE_MAP_END_INHERITING(dom::CSSKeyframesRule)
175
176 NS_IMPL_CYCLE_COLLECTION_CLASS(ServoKeyframesRule)
177
178 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServoKeyframesRule,
179 dom::CSSKeyframesRule)
180 if (tmp->mKeyframeList) {
181 tmp->mKeyframeList->DropReference();
182 tmp->mKeyframeList = nullptr;
183 }
184 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
185
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoKeyframesRule,Rule)186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoKeyframesRule, Rule)
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mKeyframeList)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
189
190 /* virtual */ bool ServoKeyframesRule::IsCCLeaf() const {
191 // If we don't have rule list constructed, we are a leaf.
192 return Rule::IsCCLeaf() && !mKeyframeList;
193 }
194
Clone() const195 /* virtual */ already_AddRefed<css::Rule> ServoKeyframesRule::Clone() const {
196 // Rule::Clone is only used when CSSStyleSheetInner is cloned in
197 // preparation of being mutated. However, ServoStyleSheet never clones
198 // anything, so this method should never be called.
199 MOZ_ASSERT_UNREACHABLE("Shouldn't be cloning ServoKeyframesRule");
200 return nullptr;
201 }
202
203 #ifdef DEBUG
List(FILE * out,int32_t aIndent) const204 /* virtual */ void ServoKeyframesRule::List(FILE* out, int32_t aIndent) const {
205 nsAutoCString str;
206 for (int32_t i = 0; i < aIndent; i++) {
207 str.AppendLiteral(" ");
208 }
209 Servo_KeyframesRule_Debug(mRawRule, &str);
210 fprintf_stderr(out, "%s\n", str.get());
211 }
212 #endif
213
SetStyleSheet(StyleSheet * aSheet)214 /* virtual */ void ServoKeyframesRule::SetStyleSheet(StyleSheet* aSheet) {
215 if (mKeyframeList) {
216 mKeyframeList->SetStyleSheet(aSheet ? aSheet->AsServo() : nullptr);
217 }
218 dom::CSSKeyframesRule::SetStyleSheet(aSheet);
219 }
220
221 static const uint32_t kRuleNotFound = std::numeric_limits<uint32_t>::max();
222
FindRuleIndexForKey(const nsAString & aKey)223 uint32_t ServoKeyframesRule::FindRuleIndexForKey(const nsAString& aKey) {
224 NS_ConvertUTF16toUTF8 key(aKey);
225 return Servo_KeyframesRule_FindRule(mRawRule, &key);
226 }
227
228 template <typename Func>
UpdateRule(Func aCallback)229 void ServoKeyframesRule::UpdateRule(Func aCallback) {
230 nsIDocument* doc = GetDocument();
231 MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
232
233 aCallback();
234
235 if (StyleSheet* sheet = GetStyleSheet()) {
236 sheet->RuleChanged(this);
237 }
238 }
239
GetName(nsAString & aName) const240 void ServoKeyframesRule::GetName(nsAString& aName) const {
241 nsAtom* name = Servo_KeyframesRule_GetName(mRawRule);
242 aName = nsDependentAtomString(name);
243 }
244
SetName(const nsAString & aName)245 void ServoKeyframesRule::SetName(const nsAString& aName) {
246 RefPtr<nsAtom> name = NS_Atomize(aName);
247 nsAtom* oldName = Servo_KeyframesRule_GetName(mRawRule);
248 if (name == oldName) {
249 return;
250 }
251
252 UpdateRule([this, &name]() {
253 Servo_KeyframesRule_SetName(mRawRule, name.forget().take());
254 });
255 }
256
AppendRule(const nsAString & aRule)257 void ServoKeyframesRule::AppendRule(const nsAString& aRule) {
258 StyleSheet* sheet = GetStyleSheet();
259 if (!sheet) {
260 // We cannot parse the rule if we don't have a stylesheet.
261 return;
262 }
263
264 NS_ConvertUTF16toUTF8 rule(aRule);
265 UpdateRule([this, sheet, &rule]() {
266 bool parsedOk = Servo_KeyframesRule_AppendRule(
267 mRawRule, sheet->AsServo()->RawContents(), &rule);
268 if (parsedOk && mKeyframeList) {
269 mKeyframeList->AppendRule();
270 }
271 });
272 }
273
DeleteRule(const nsAString & aKey)274 void ServoKeyframesRule::DeleteRule(const nsAString& aKey) {
275 auto index = FindRuleIndexForKey(aKey);
276 if (index == kRuleNotFound) {
277 return;
278 }
279
280 UpdateRule([this, index]() {
281 Servo_KeyframesRule_DeleteRule(mRawRule, index);
282 if (mKeyframeList) {
283 mKeyframeList->RemoveRule(index);
284 }
285 });
286 }
287
GetCssText(nsAString & aCssText) const288 /* virtual */ void ServoKeyframesRule::GetCssText(nsAString& aCssText) const {
289 Servo_KeyframesRule_GetCssText(mRawRule, &aCssText);
290 }
291
CssRules()292 /* virtual */ dom::CSSRuleList* ServoKeyframesRule::CssRules() {
293 if (!mKeyframeList) {
294 mKeyframeList = new ServoKeyframeList(do_AddRef(mRawRule));
295 mKeyframeList->SetParentRule(this);
296 if (StyleSheet* sheet = GetStyleSheet()) {
297 mKeyframeList->SetStyleSheet(sheet->AsServo());
298 }
299 }
300 return mKeyframeList;
301 }
302
FindRule(const nsAString & aKey)303 /* virtual */ dom::CSSKeyframeRule* ServoKeyframesRule::FindRule(
304 const nsAString& aKey) {
305 auto index = FindRuleIndexForKey(aKey);
306 if (index != kRuleNotFound) {
307 // Construct mKeyframeList but ignore the result.
308 CssRules();
309 return mKeyframeList->GetRule(index);
310 }
311 return nullptr;
312 }
313
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const314 /* virtual */ size_t ServoKeyframesRule::SizeOfIncludingThis(
315 MallocSizeOf aMallocSizeOf) const {
316 size_t n = aMallocSizeOf(this);
317 n += GroupRule::SizeOfExcludingThis(aMallocSizeOf);
318 if (mKeyframeList) {
319 n += mKeyframeList->SizeOfIncludingThis(aMallocSizeOf);
320 }
321 return n;
322 }
323
324 } // namespace mozilla
325