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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsDOMMutationObserver_h
8 #define nsDOMMutationObserver_h
12 #include "mozilla/Attributes.h"
13 #include "mozilla/dom/Animation.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/MutationEventBinding.h"
16 #include "mozilla/dom/MutationObserverBinding.h"
17 #include "mozilla/dom/Nullable.h"
18 #include "nsCOMArray.h"
19 #include "nsClassHashtable.h"
20 #include "nsContentList.h"
21 #include "nsCycleCollectionParticipant.h"
22 #include "nsGlobalWindowInner.h"
23 #include "nsIAnimationObserver.h"
24 #include "nsPIDOMWindow.h"
25 #include "nsStubAnimationObserver.h"
27 #include "nsWrapperCache.h"
31 class nsDOMMutationObserver
;
32 using mozilla::dom::MutationObservingInfo
;
34 namespace mozilla::dom
{
38 class nsDOMMutationRecord final
: public nsISupports
, public nsWrapperCache
{
39 virtual ~nsDOMMutationRecord() = default;
42 using AnimationArray
= nsTArray
<RefPtr
<mozilla::dom::Animation
>>;
44 nsDOMMutationRecord(nsAtom
* aType
, nsISupports
* aOwner
)
46 mAttrNamespace(VoidString()),
47 mPrevValue(VoidString()),
50 nsISupports
* GetParentObject() const { return mOwner
; }
52 virtual JSObject
* WrapObject(JSContext
* aCx
,
53 JS::Handle
<JSObject
*> aGivenProto
) override
{
54 return mozilla::dom::MutationRecord_Binding::Wrap(aCx
, this, aGivenProto
);
57 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
58 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsDOMMutationRecord
)
60 void GetType(mozilla::dom::DOMString
& aRetVal
) const {
61 aRetVal
.SetKnownLiveAtom(mType
, mozilla::dom::DOMString::eNullNotExpected
);
64 nsINode
* GetTarget() const { return mTarget
; }
66 nsINodeList
* AddedNodes();
68 nsINodeList
* RemovedNodes();
70 nsINode
* GetPreviousSibling() const { return mPreviousSibling
; }
72 nsINode
* GetNextSibling() const { return mNextSibling
; }
74 void GetAttributeName(mozilla::dom::DOMString
& aRetVal
) const {
75 aRetVal
.SetKnownLiveAtom(mAttrName
,
76 mozilla::dom::DOMString::eTreatNullAsNull
);
79 void GetAttributeNamespace(mozilla::dom::DOMString
& aRetVal
) const {
80 aRetVal
.SetKnownLiveString(mAttrNamespace
);
83 void GetOldValue(mozilla::dom::DOMString
& aRetVal
) const {
84 aRetVal
.SetKnownLiveString(mPrevValue
);
87 void GetAddedAnimations(AnimationArray
& aRetVal
) const {
88 aRetVal
= mAddedAnimations
.Clone();
91 void GetRemovedAnimations(AnimationArray
& aRetVal
) const {
92 aRetVal
= mRemovedAnimations
.Clone();
95 void GetChangedAnimations(AnimationArray
& aRetVal
) const {
96 aRetVal
= mChangedAnimations
.Clone();
99 nsCOMPtr
<nsINode
> mTarget
;
100 RefPtr
<nsAtom
> mType
;
101 RefPtr
<nsAtom
> mAttrName
;
102 nsString mAttrNamespace
;
104 RefPtr
<nsSimpleContentList
> mAddedNodes
;
105 RefPtr
<nsSimpleContentList
> mRemovedNodes
;
106 nsCOMPtr
<nsINode
> mPreviousSibling
;
107 nsCOMPtr
<nsINode
> mNextSibling
;
108 AnimationArray mAddedAnimations
;
109 AnimationArray mRemovedAnimations
;
110 AnimationArray mChangedAnimations
;
112 RefPtr
<nsDOMMutationRecord
> mNext
;
113 nsCOMPtr
<nsISupports
> mOwner
;
116 // Base class just prevents direct access to
117 // members to make sure we go through getters/setters.
118 class nsMutationReceiverBase
: public nsStubAnimationObserver
{
120 virtual ~nsMutationReceiverBase() = default;
122 nsDOMMutationObserver
* Observer();
123 nsINode
* Target() { return mParent
? mParent
->Target() : mTarget
; }
124 nsINode
* RegisterTarget() { return mRegisterTarget
; }
126 bool Subtree() { return mParent
? mParent
->Subtree() : mSubtree
; }
127 void SetSubtree(bool aSubtree
) {
128 NS_ASSERTION(!mParent
, "Shouldn't have parent");
132 bool ChildList() { return mParent
? mParent
->ChildList() : mChildList
; }
133 void SetChildList(bool aChildList
) {
134 NS_ASSERTION(!mParent
, "Shouldn't have parent");
135 mChildList
= aChildList
;
138 bool CharacterData() {
139 return mParent
? mParent
->CharacterData() : mCharacterData
;
141 void SetCharacterData(bool aCharacterData
) {
142 NS_ASSERTION(!mParent
, "Shouldn't have parent");
143 mCharacterData
= aCharacterData
;
146 bool CharacterDataOldValue() const {
147 return mParent
? mParent
->CharacterDataOldValue() : mCharacterDataOldValue
;
149 void SetCharacterDataOldValue(bool aOldValue
) {
150 NS_ASSERTION(!mParent
, "Shouldn't have parent");
151 mCharacterDataOldValue
= aOldValue
;
154 bool Attributes() const {
155 return mParent
? mParent
->Attributes() : mAttributes
;
157 void SetAttributes(bool aAttributes
) {
158 NS_ASSERTION(!mParent
, "Shouldn't have parent");
159 mAttributes
= aAttributes
;
162 bool AllAttributes() const {
163 return mParent
? mParent
->AllAttributes() : mAllAttributes
;
165 void SetAllAttributes(bool aAll
) {
166 NS_ASSERTION(!mParent
, "Shouldn't have parent");
167 mAllAttributes
= aAll
;
170 bool Animations() const {
171 return mParent
? mParent
->Animations() : mAnimations
;
173 void SetAnimations(bool aAnimations
) {
174 NS_ASSERTION(!mParent
, "Shouldn't have parent");
175 mAnimations
= aAnimations
;
178 bool AttributeOldValue() const {
179 return mParent
? mParent
->AttributeOldValue() : mAttributeOldValue
;
181 void SetAttributeOldValue(bool aOldValue
) {
182 NS_ASSERTION(!mParent
, "Shouldn't have parent");
183 mAttributeOldValue
= aOldValue
;
186 bool ChromeOnlyNodes() const {
187 return mParent
? mParent
->ChromeOnlyNodes() : mChromeOnlyNodes
;
190 void SetChromeOnlyNodes(bool aChromeOnlyNodes
) {
191 NS_ASSERTION(!mParent
, "Shouldn't have parent");
192 mChromeOnlyNodes
= aChromeOnlyNodes
;
195 nsTArray
<RefPtr
<nsAtom
>>& AttributeFilter() { return mAttributeFilter
; }
196 void SetAttributeFilter(nsTArray
<RefPtr
<nsAtom
>>&& aFilter
) {
197 NS_ASSERTION(!mParent
, "Shouldn't have parent");
198 mAttributeFilter
.Clear();
199 mAttributeFilter
= std::move(aFilter
);
202 void AddClone(nsMutationReceiverBase
* aClone
) {
203 mTransientReceivers
.AppendObject(aClone
);
206 void RemoveClone(nsMutationReceiverBase
* aClone
) {
207 mTransientReceivers
.RemoveObject(aClone
);
211 nsMutationReceiverBase(nsINode
* aTarget
, nsDOMMutationObserver
* aObserver
)
213 mObserver(aObserver
),
214 mRegisterTarget(aTarget
),
217 mCharacterData(false),
218 mCharacterDataOldValue(false),
220 mAllAttributes(false),
221 mAttributeOldValue(false),
222 mAnimations(false) {}
224 nsMutationReceiverBase(nsINode
* aRegisterTarget
,
225 nsMutationReceiverBase
* aParent
)
229 mRegisterTarget(aRegisterTarget
),
230 mKungFuDeathGrip(aParent
->Target()),
233 mCharacterData(false),
234 mCharacterDataOldValue(false),
236 mAllAttributes(false),
237 mAttributeOldValue(false),
239 mChromeOnlyNodes(false) {
240 NS_ASSERTION(mParent
->Subtree(), "Should clone a non-subtree observer!");
243 virtual void AddMutationObserver() = 0;
246 AddMutationObserver();
247 mRegisterTarget
->SetMayHaveDOMMutationObserver();
248 mRegisterTarget
->OwnerDoc()->SetMayHaveDOMMutationObservers();
251 bool IsObservable(nsIContent
* aContent
);
253 bool ObservesAttr(nsINode
* aRegisterTarget
, mozilla::dom::Element
* aElement
,
254 int32_t aNameSpaceID
, nsAtom
* aAttr
);
256 // The target for the MutationObserver.observe() method.
258 nsDOMMutationObserver
* mObserver
;
259 RefPtr
<nsMutationReceiverBase
> mParent
; // Cleared after microtask.
260 // The node to which Gecko-internal nsIMutationObserver was registered to.
261 // This is different than mTarget when dealing with transient observers.
262 nsINode
* mRegisterTarget
;
263 nsCOMArray
<nsMutationReceiverBase
> mTransientReceivers
;
264 // While we have transient receivers, keep the original mutation receiver
265 // alive so it doesn't go away and disconnect all its transient receivers.
266 nsCOMPtr
<nsINode
> mKungFuDeathGrip
;
269 nsTArray
<RefPtr
<nsAtom
>> mAttributeFilter
;
272 bool mCharacterData
: 1;
273 bool mCharacterDataOldValue
: 1;
274 bool mAttributes
: 1;
275 bool mAllAttributes
: 1;
276 bool mAttributeOldValue
: 1;
277 bool mAnimations
: 1;
278 bool mChromeOnlyNodes
: 1;
281 class nsMutationReceiver
: public nsMutationReceiverBase
{
283 virtual ~nsMutationReceiver() { Disconnect(false); }
286 static nsMutationReceiver
* Create(nsINode
* aTarget
,
287 nsDOMMutationObserver
* aObserver
) {
288 nsMutationReceiver
* r
= new nsMutationReceiver(aTarget
, aObserver
);
293 static nsMutationReceiver
* Create(nsINode
* aRegisterTarget
,
294 nsMutationReceiverBase
* aParent
) {
295 nsMutationReceiver
* r
= new nsMutationReceiver(aRegisterTarget
, aParent
);
296 aParent
->AddClone(r
);
301 nsMutationReceiver
* GetParent() {
302 return static_cast<nsMutationReceiver
*>(mParent
.get());
305 void RemoveClones() {
306 for (int32_t i
= 0; i
< mTransientReceivers
.Count(); ++i
) {
307 nsMutationReceiver
* r
=
308 static_cast<nsMutationReceiver
*>(mTransientReceivers
[i
]);
309 r
->DisconnectTransientReceiver();
311 mTransientReceivers
.Clear();
314 void DisconnectTransientReceiver() {
315 if (mRegisterTarget
) {
316 mRegisterTarget
->RemoveMutationObserver(this);
317 mRegisterTarget
= nullptr;
321 NS_ASSERTION(!mTarget
, "Should not have mTarget");
322 NS_ASSERTION(!mObserver
, "Should not have mObserver");
325 void Disconnect(bool aRemoveFromObserver
);
329 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
330 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
331 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
332 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
333 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
334 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
336 virtual void AttributeSetToCurrentValue(mozilla::dom::Element
* aElement
,
337 int32_t aNameSpaceID
,
338 nsAtom
* aAttribute
) override
{
339 // We can reuse AttributeWillChange implementation.
340 AttributeWillChange(aElement
, aNameSpaceID
, aAttribute
,
341 mozilla::dom::MutationEvent_Binding::MODIFICATION
);
345 nsMutationReceiver(nsINode
* aTarget
, nsDOMMutationObserver
* aObserver
);
347 nsMutationReceiver(nsINode
* aRegisterTarget
, nsMutationReceiverBase
* aParent
)
348 : nsMutationReceiverBase(aRegisterTarget
, aParent
) {
349 NS_ASSERTION(!static_cast<nsMutationReceiver
*>(aParent
)->GetParent(),
350 "Shouldn't create deep observer hierarchies!");
353 virtual void AddMutationObserver() override
{
354 mRegisterTarget
->AddMutationObserver(this);
358 class nsAnimationReceiver
: public nsMutationReceiver
{
360 static nsAnimationReceiver
* Create(nsINode
* aTarget
,
361 nsDOMMutationObserver
* aObserver
) {
362 nsAnimationReceiver
* r
= new nsAnimationReceiver(aTarget
, aObserver
);
367 static nsAnimationReceiver
* Create(nsINode
* aRegisterTarget
,
368 nsMutationReceiverBase
* aParent
) {
369 nsAnimationReceiver
* r
= new nsAnimationReceiver(aRegisterTarget
, aParent
);
370 aParent
->AddClone(r
);
375 NS_DECL_ISUPPORTS_INHERITED
377 NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONADDED
378 NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONCHANGED
379 NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONREMOVED
382 virtual ~nsAnimationReceiver() = default;
384 nsAnimationReceiver(nsINode
* aTarget
, nsDOMMutationObserver
* aObserver
)
385 : nsMutationReceiver(aTarget
, aObserver
) {}
387 nsAnimationReceiver(nsINode
* aRegisterTarget
, nsMutationReceiverBase
* aParent
)
388 : nsMutationReceiver(aRegisterTarget
, aParent
) {}
390 virtual void AddMutationObserver() override
{
391 mRegisterTarget
->AddAnimationObserver(this);
395 enum AnimationMutation
{
396 eAnimationMutation_Added
,
397 eAnimationMutation_Changed
,
398 eAnimationMutation_Removed
401 void RecordAnimationMutation(mozilla::dom::Animation
* aAnimation
,
402 AnimationMutation aMutationType
);
405 #define NS_DOM_MUTATION_OBSERVER_IID \
407 0x0c3b91f8, 0xcc3b, 0x4b08, { \
408 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 \
412 class nsDOMMutationObserver final
: public nsISupports
, public nsWrapperCache
{
414 nsDOMMutationObserver(nsCOMPtr
<nsPIDOMWindowInner
>&& aOwner
,
415 mozilla::dom::MutationCallback
& aCb
)
416 : mOwner(std::move(aOwner
)),
417 mLastPendingMutation(nullptr),
418 mPendingMutationCount(0),
420 mWaitingForRun(false),
421 mMergeAttributeRecords(false),
423 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
424 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver
)
425 NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID
)
427 static already_AddRefed
<nsDOMMutationObserver
> Constructor(
428 const mozilla::dom::GlobalObject
&, mozilla::dom::MutationCallback
&,
429 mozilla::ErrorResult
&);
431 JSObject
* WrapObject(JSContext
* aCx
,
432 JS::Handle
<JSObject
*> aGivenProto
) override
{
433 return mozilla::dom::MutationObserver_Binding::Wrap(aCx
, this, aGivenProto
);
436 nsISupports
* GetParentObject() const { return mOwner
; }
438 void Observe(nsINode
& aTarget
,
439 const mozilla::dom::MutationObserverInit
& aOptions
,
440 nsIPrincipal
& aSubjectPrincipal
, mozilla::ErrorResult
& aRv
);
444 void TakeRecords(nsTArray
<RefPtr
<nsDOMMutationRecord
>>& aRetVal
);
446 MOZ_CAN_RUN_SCRIPT
void HandleMutation();
448 void GetObservingInfo(
449 nsTArray
<mozilla::dom::Nullable
<MutationObservingInfo
>>& aResult
,
450 mozilla::ErrorResult
& aRv
);
452 mozilla::dom::MutationCallback
* MutationCallback() { return mCallback
; }
454 bool MergeAttributeRecords() { return mMergeAttributeRecords
; }
456 void SetMergeAttributeRecords(bool aVal
) { mMergeAttributeRecords
= aVal
; }
458 // If both records are for 'attributes' type and for the same target and
459 // attribute name and namespace are the same, we can skip the newer record.
460 // aOldRecord->mPrevValue holds the original value, if observed.
461 bool MergeableAttributeRecord(nsDOMMutationRecord
* aOldRecord
,
462 nsDOMMutationRecord
* aRecord
);
464 void AppendMutationRecord(already_AddRefed
<nsDOMMutationRecord
> aRecord
) {
465 RefPtr
<nsDOMMutationRecord
> record
= aRecord
;
467 if (!mLastPendingMutation
) {
468 MOZ_ASSERT(!mFirstPendingMutation
);
469 mFirstPendingMutation
= std::move(record
);
470 mLastPendingMutation
= mFirstPendingMutation
;
472 MOZ_ASSERT(mFirstPendingMutation
);
473 mLastPendingMutation
->mNext
= std::move(record
);
474 mLastPendingMutation
= mLastPendingMutation
->mNext
;
476 ++mPendingMutationCount
;
479 void ClearPendingRecords() {
480 // Break down the pending mutation record list so that cycle collector
481 // can delete the objects sooner.
482 RefPtr
<nsDOMMutationRecord
> current
= std::move(mFirstPendingMutation
);
483 mLastPendingMutation
= nullptr;
484 mPendingMutationCount
= 0;
486 current
= std::move(current
->mNext
);
491 static void QueueMutationObserverMicroTask();
494 static void HandleMutations(mozilla::AutoSlowOperation
& aAso
);
496 static bool AllScheduledMutationObserversAreSuppressed() {
497 if (sScheduledMutationObservers
) {
498 uint32_t len
= sScheduledMutationObservers
->Length();
500 for (uint32_t i
= 0; i
< len
; ++i
) {
501 if (!(*sScheduledMutationObservers
)[i
]->Suppressed()) {
511 static void EnterMutationHandling();
512 static void LeaveMutationHandling();
514 static void Shutdown();
517 virtual ~nsDOMMutationObserver();
519 friend class nsMutationReceiver
;
520 friend class nsAnimationReceiver
;
521 friend class nsAutoMutationBatch
;
522 friend class nsAutoAnimationMutationBatch
;
523 nsMutationReceiver
* GetReceiverFor(nsINode
* aNode
, bool aMayCreate
,
524 bool aWantsAnimations
);
525 void RemoveReceiver(nsMutationReceiver
* aReceiver
);
527 void GetAllSubtreeObserversFor(nsINode
* aNode
,
528 nsTArray
<nsMutationReceiver
*>& aObservers
);
529 void ScheduleForRun();
530 void RescheduleForRun();
532 nsDOMMutationRecord
* CurrentRecord(nsAtom
* aType
);
533 bool HasCurrentRecord(const nsAString
& aType
);
536 return mOwner
&& nsGlobalWindowInner::Cast(mOwner
)->IsInSyncOperation();
540 static void HandleMutationsInternal(mozilla::AutoSlowOperation
& aAso
);
542 static void AddCurrentlyHandlingObserver(nsDOMMutationObserver
* aObserver
,
543 uint32_t aMutationLevel
);
545 nsCOMPtr
<nsPIDOMWindowInner
> mOwner
;
547 nsCOMArray
<nsMutationReceiver
> mReceivers
;
548 nsClassHashtable
<nsISupportsHashKey
, nsCOMArray
<nsMutationReceiver
>>
550 // MutationRecords which are being constructed.
551 AutoTArray
<nsDOMMutationRecord
*, 4> mCurrentMutations
;
552 // MutationRecords which will be handed to the callback at the end of
554 RefPtr
<nsDOMMutationRecord
> mFirstPendingMutation
;
555 nsDOMMutationRecord
* mLastPendingMutation
;
556 uint32_t mPendingMutationCount
;
558 RefPtr
<mozilla::dom::MutationCallback
> mCallback
;
561 bool mMergeAttributeRecords
;
565 static uint64_t sCount
;
566 static AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>*
567 sScheduledMutationObservers
;
569 static uint32_t sMutationLevel
;
570 static AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>*
571 sCurrentlyHandlingObservers
;
574 NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver
,
575 NS_DOM_MUTATION_OBSERVER_IID
)
577 class nsAutoMutationBatch
{
579 nsAutoMutationBatch()
580 : mPreviousBatch(nullptr),
581 mBatchTarget(nullptr),
583 mFromFirstToLast(false),
584 mAllowNestedBatches(false) {}
586 nsAutoMutationBatch(nsINode
* aTarget
, bool aFromFirstToLast
,
587 bool aAllowNestedBatches
)
588 : mPreviousBatch(nullptr),
589 mBatchTarget(nullptr),
591 mFromFirstToLast(false),
592 mAllowNestedBatches(false) {
593 Init(aTarget
, aFromFirstToLast
, aAllowNestedBatches
);
596 void Init(nsINode
* aTarget
, bool aFromFirstToLast
, bool aAllowNestedBatches
) {
597 if (aTarget
&& aTarget
->OwnerDoc()->MayHaveDOMMutationObservers()) {
598 if (sCurrentBatch
&& !sCurrentBatch
->mAllowNestedBatches
) {
601 mBatchTarget
= aTarget
;
602 mFromFirstToLast
= aFromFirstToLast
;
603 mAllowNestedBatches
= aAllowNestedBatches
;
604 mPreviousBatch
= sCurrentBatch
;
605 sCurrentBatch
= this;
606 nsDOMMutationObserver::EnterMutationHandling();
610 void RemovalDone() { mRemovalDone
= true; }
611 static bool IsRemovalDone() { return sCurrentBatch
->mRemovalDone
; }
613 void SetPrevSibling(nsINode
* aNode
) { mPrevSibling
= aNode
; }
614 void SetNextSibling(nsINode
* aNode
) { mNextSibling
= aNode
; }
618 ~nsAutoMutationBatch() { NodesAdded(); }
620 static bool IsBatching() { return !!sCurrentBatch
; }
622 static nsAutoMutationBatch
* GetCurrentBatch() { return sCurrentBatch
; }
624 static void UpdateObserver(nsDOMMutationObserver
* aObserver
,
625 bool aWantsChildList
) {
626 uint32_t l
= sCurrentBatch
->mObservers
.Length();
627 for (uint32_t i
= 0; i
< l
; ++i
) {
628 if (sCurrentBatch
->mObservers
[i
].mObserver
== aObserver
) {
629 if (aWantsChildList
) {
630 sCurrentBatch
->mObservers
[i
].mWantsChildList
= aWantsChildList
;
635 BatchObserver
* bo
= sCurrentBatch
->mObservers
.AppendElement();
636 bo
->mObserver
= aObserver
;
637 bo
->mWantsChildList
= aWantsChildList
;
640 static nsINode
* GetBatchTarget() { return sCurrentBatch
->mBatchTarget
; }
642 // Mutation receivers notify the batch about removed child nodes.
643 static void NodeRemoved(nsIContent
* aChild
) {
644 if (IsBatching() && !sCurrentBatch
->mRemovalDone
) {
645 uint32_t len
= sCurrentBatch
->mRemovedNodes
.Length();
646 if (!len
|| sCurrentBatch
->mRemovedNodes
[len
- 1] != aChild
) {
647 sCurrentBatch
->mRemovedNodes
.AppendElement(aChild
);
652 // Called after new child nodes have been added to the batch target.
654 if (sCurrentBatch
!= this) {
658 nsIContent
* c
= mPrevSibling
? mPrevSibling
->GetNextSibling()
659 : mBatchTarget
->GetFirstChild();
660 for (; c
!= mNextSibling
; c
= c
->GetNextSibling()) {
661 mAddedNodes
.AppendElement(c
);
667 struct BatchObserver
{
668 nsDOMMutationObserver
* mObserver
;
669 bool mWantsChildList
;
672 static nsAutoMutationBatch
* sCurrentBatch
;
673 nsAutoMutationBatch
* mPreviousBatch
;
674 AutoTArray
<BatchObserver
, 2> mObservers
;
675 nsTArray
<nsCOMPtr
<nsIContent
>> mRemovedNodes
;
676 nsTArray
<nsCOMPtr
<nsIContent
>> mAddedNodes
;
677 nsINode
* mBatchTarget
;
679 bool mFromFirstToLast
;
680 bool mAllowNestedBatches
;
681 nsCOMPtr
<nsINode
> mPrevSibling
;
682 nsCOMPtr
<nsINode
> mNextSibling
;
685 class nsAutoAnimationMutationBatch
{
689 explicit nsAutoAnimationMutationBatch(mozilla::dom::Document
* aDocument
) {
693 void Init(mozilla::dom::Document
* aDocument
) {
694 if (!aDocument
|| !aDocument
->MayHaveDOMMutationObservers() ||
699 sCurrentBatch
= this;
700 nsDOMMutationObserver::EnterMutationHandling();
703 ~nsAutoAnimationMutationBatch() { Done(); }
707 static bool IsBatching() { return !!sCurrentBatch
; }
709 static nsAutoAnimationMutationBatch
* GetCurrentBatch() {
710 return sCurrentBatch
;
713 static void AddObserver(nsDOMMutationObserver
* aObserver
) {
714 if (sCurrentBatch
->mObservers
.Contains(aObserver
)) {
717 sCurrentBatch
->mObservers
.AppendElement(aObserver
);
720 static void AnimationAdded(mozilla::dom::Animation
* aAnimation
,
726 Entry
* entry
= sCurrentBatch
->FindEntry(aAnimation
, aTarget
);
728 switch (entry
->mState
) {
729 case eState_RemainedAbsent
:
730 entry
->mState
= eState_Added
;
733 entry
->mState
= eState_RemainedPresent
;
737 NS_ERROR("shouldn't have observed an animation being added twice");
739 case eState_RemainedPresent
:
740 MOZ_ASSERT_UNREACHABLE(
741 "shouldn't have observed an animation "
742 "remaining present");
746 entry
= sCurrentBatch
->AddEntry(aAnimation
, aTarget
);
747 entry
->mState
= eState_Added
;
748 entry
->mChanged
= false;
752 static void AnimationChanged(mozilla::dom::Animation
* aAnimation
,
754 Entry
* entry
= sCurrentBatch
->FindEntry(aAnimation
, aTarget
);
756 NS_ASSERTION(entry
->mState
== eState_RemainedPresent
||
757 entry
->mState
== eState_Added
,
758 "shouldn't have observed an animation being changed after "
760 entry
->mChanged
= true;
762 entry
= sCurrentBatch
->AddEntry(aAnimation
, aTarget
);
763 entry
->mState
= eState_RemainedPresent
;
764 entry
->mChanged
= true;
768 static void AnimationRemoved(mozilla::dom::Animation
* aAnimation
,
770 Entry
* entry
= sCurrentBatch
->FindEntry(aAnimation
, aTarget
);
772 switch (entry
->mState
) {
773 case eState_RemainedPresent
:
774 entry
->mState
= eState_Removed
;
777 entry
->mState
= eState_RemainedAbsent
;
779 case eState_RemainedAbsent
:
780 MOZ_ASSERT_UNREACHABLE(
781 "shouldn't have observed an animation "
786 NS_ERROR("shouldn't have observed an animation being removed twice");
790 entry
= sCurrentBatch
->AddEntry(aAnimation
, aTarget
);
791 entry
->mState
= eState_Removed
;
792 entry
->mChanged
= false;
797 Entry
* FindEntry(mozilla::dom::Animation
* aAnimation
, nsINode
* aTarget
) {
798 EntryArray
* entries
= mEntryTable
.Get(aTarget
);
803 for (Entry
& e
: *entries
) {
804 if (e
.mAnimation
== aAnimation
) {
811 Entry
* AddEntry(mozilla::dom::Animation
* aAnimation
, nsINode
* aTarget
) {
812 EntryArray
* entries
= sCurrentBatch
->mEntryTable
.GetOrInsertNew(aTarget
);
813 if (entries
->IsEmpty()) {
814 sCurrentBatch
->mBatchTargets
.AppendElement(aTarget
);
816 Entry
* entry
= entries
->AppendElement();
817 entry
->mAnimation
= aAnimation
;
822 eState_RemainedPresent
,
823 eState_RemainedAbsent
,
829 RefPtr
<mozilla::dom::Animation
> mAnimation
;
834 static nsAutoAnimationMutationBatch
* sCurrentBatch
;
835 AutoTArray
<nsDOMMutationObserver
*, 2> mObservers
;
836 using EntryArray
= nsTArray
<Entry
>;
837 nsClassHashtable
<nsPtrHashKey
<nsINode
>, EntryArray
> mEntryTable
;
838 // List of nodes referred to by mEntryTable so we can sort them
839 // For a specific pseudo element, we use its parent element as the
840 // batch target, so they will be put in the same EntryArray.
841 nsTArray
<nsINode
*> mBatchTargets
;
844 inline nsDOMMutationObserver
* nsMutationReceiverBase::Observer() {
845 return mParent
? mParent
->Observer()
846 : static_cast<nsDOMMutationObserver
*>(mObserver
);
849 class MOZ_RAII nsDOMMutationEnterLeave
{
851 explicit nsDOMMutationEnterLeave(mozilla::dom::Document
* aDoc
)
852 : mNeeded(aDoc
->MayHaveDOMMutationObservers()) {
854 nsDOMMutationObserver::EnterMutationHandling();
857 ~nsDOMMutationEnterLeave() {
859 nsDOMMutationObserver::LeaveMutationHandling();