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