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