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