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 #include "mozilla/dom/CSSKeyframesRule.h"
9 #include "mozilla/dom/CSSKeyframesRuleBinding.h"
10 #include "mozilla/dom/CSSRuleList.h"
11 #include "mozilla/ServoBindings.h"
12 #include "nsCOMArray.h"
19 // -------------------------------------------
23 class CSSKeyframeList
: public dom::CSSRuleList
{
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
));
31 NS_DECL_ISUPPORTS_INHERITED
32 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSKeyframeList
, dom::CSSRuleList
)
34 void DropSheetReference() {
38 mStyleSheet
= nullptr;
39 for (css::Rule
* rule
: mRules
) {
41 rule
->DropSheetReference();
46 StyleSheet
* GetParentObject() final
{ return mStyleSheet
; }
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
)
54 CSSKeyframeRule
* ruleObj
= new CSSKeyframeRule(rule
.forget(), mStyleSheet
,
55 mParentRule
, line
, column
);
56 mRules
.ReplaceObjectAt(ruleObj
, aIndex
);
58 return static_cast<CSSKeyframeRule
*>(mRules
[aIndex
]);
61 CSSKeyframeRule
* IndexedGetter(uint32_t aIndex
, bool& aFound
) final
{
62 if (aIndex
>= mRules
.Length()) {
67 return GetRule(aIndex
);
71 MOZ_ASSERT(!mParentRule
->IsReadOnly());
72 mRules
.AppendObject(nullptr);
75 void RemoveRule(uint32_t aIndex
) {
76 MOZ_ASSERT(!mParentRule
->IsReadOnly());
78 if (aIndex
>= mRules
.Length()) {
81 if (css::Rule
* child
= mRules
[aIndex
]) {
82 child
->DropReferences();
84 mRules
.RemoveObjectAt(aIndex
);
87 uint32_t Length() final
{ return mRules
.Length(); }
89 void DropReferences() {
90 if (!mStyleSheet
&& !mParentRule
) {
93 mStyleSheet
= nullptr;
94 mParentRule
= nullptr;
95 for (css::Rule
* rule
: mRules
) {
97 rule
->DropParentRuleReference();
98 rule
->DropSheetReference();
103 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;
112 virtual ~CSSKeyframeList() {
113 MOZ_ASSERT(!mParentRule
, "Backpointer should have been cleared");
114 MOZ_ASSERT(!mStyleSheet
, "Backpointer should have been cleared");
118 void DropAllRules() {
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
;
131 // QueryInterface implementation for CSSKeyframeList
132 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframeList
)
133 NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList
)
135 NS_IMPL_ADDREF_INHERITED(CSSKeyframeList
, dom::CSSRuleList
)
136 NS_IMPL_RELEASE_INHERITED(CSSKeyframeList
, dom::CSSRuleList
)
138 NS_IMPL_CYCLE_COLLECTION_CLASS(CSSKeyframeList
)
140 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSKeyframeList
)
142 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList
)
143 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSKeyframeList
,
145 for (css::Rule
* rule
: tmp
->mRules
) {
147 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mRules[i]");
148 cb
.NoteXPCOMChild(rule
);
151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
153 // -------------------------------------------
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
)) {}
163 CSSKeyframesRule::~CSSKeyframesRule() {
165 mKeyframeList
->DropReferences();
169 NS_IMPL_ADDREF_INHERITED(CSSKeyframesRule
, css::Rule
)
170 NS_IMPL_RELEASE_INHERITED(CSSKeyframesRule
, css::Rule
)
172 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframesRule
)
173 NS_INTERFACE_MAP_END_INHERITING(css::Rule
)
175 NS_IMPL_CYCLE_COLLECTION_CLASS(CSSKeyframesRule
)
177 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CSSKeyframesRule
, css::Rule
)
178 if (tmp
->mKeyframeList
) {
179 tmp
->mKeyframeList
->DropReferences();
180 tmp
->mKeyframeList
= nullptr;
182 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSKeyframesRule
, Rule
)
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mKeyframeList
)
186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
189 bool CSSKeyframesRule::IsCCLeaf() const {
190 // If we don't have rule list constructed, we are a leaf.
191 return Rule::IsCCLeaf() && !mKeyframeList
;
196 void CSSKeyframesRule::List(FILE* out
, int32_t aIndent
) const {
198 for (int32_t i
= 0; i
< aIndent
; i
++) {
199 str
.AppendLiteral(" ");
201 Servo_KeyframesRule_Debug(mRawRule
, &str
);
202 fprintf_stderr(out
, "%s\n", str
.get());
207 void CSSKeyframesRule::DropSheetReference() {
209 mKeyframeList
->DropSheetReference();
211 css::Rule::DropSheetReference();
214 static const uint32_t kRuleNotFound
= std::numeric_limits
<uint32_t>::max();
216 uint32_t CSSKeyframesRule::FindRuleIndexForKey(const nsAString
& aKey
) {
217 NS_ConvertUTF16toUTF8
key(aKey
);
218 return Servo_KeyframesRule_FindRule(mRawRule
, &key
);
221 template <typename Func
>
222 nsresult
CSSKeyframesRule::UpdateRule(Func aCallback
) {
228 if (StyleSheet
* sheet
= GetStyleSheet()) {
229 sheet
->RuleChanged(this, StyleRuleChangeKind::Generic
);
235 void CSSKeyframesRule::GetName(nsAString
& aName
) const {
236 nsAtom
* name
= Servo_KeyframesRule_GetName(mRawRule
);
237 aName
= nsDependentAtomString(name
);
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
) {
247 UpdateRule([this, &name
]() {
248 Servo_KeyframesRule_SetName(mRawRule
, name
.forget().take());
252 void CSSKeyframesRule::AppendRule(const nsAString
& aRule
) {
253 StyleSheet
* sheet
= GetStyleSheet();
255 // We cannot parse the rule if we don't have a stylesheet.
259 NS_ConvertUTF16toUTF8
rule(aRule
);
260 UpdateRule([this, sheet
, &rule
]() {
262 Servo_KeyframesRule_AppendRule(mRawRule
, sheet
->RawContents(), &rule
);
263 if (parsedOk
&& mKeyframeList
) {
264 mKeyframeList
->AppendRule();
269 void CSSKeyframesRule::DeleteRule(const nsAString
& aKey
) {
270 auto index
= FindRuleIndexForKey(aKey
);
271 if (index
== kRuleNotFound
) {
275 UpdateRule([this, index
]() {
276 Servo_KeyframesRule_DeleteRule(mRawRule
, index
);
278 mKeyframeList
->RemoveRule(index
);
284 void CSSKeyframesRule::GetCssText(nsACString
& aCssText
) const {
285 Servo_KeyframesRule_GetCssText(mRawRule
, &aCssText
);
288 /* virtual */ dom::CSSRuleList
* CSSKeyframesRule::CssRules() {
289 if (!mKeyframeList
) {
290 mKeyframeList
= new CSSKeyframeList(do_AddRef(mRawRule
), mSheet
, this);
292 return mKeyframeList
;
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.
301 return mKeyframeList
->GetRule(index
);
307 size_t CSSKeyframesRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
308 size_t n
= aMallocSizeOf(this);
310 n
+= mKeyframeList
->SizeOfIncludingThis(aMallocSizeOf
);
316 JSObject
* CSSKeyframesRule::WrapObject(JSContext
* aCx
,
317 JS::Handle
<JSObject
*> aGivenProto
) {
318 return CSSKeyframesRule_Binding::Wrap(aCx
, this, aGivenProto
);
322 } // namespace mozilla