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