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 #include "nsDOMMutationObserver.h"
9 #include "mozilla/AnimationTarget.h"
10 #include "mozilla/CycleCollectedJSContext.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/OwningNonNull.h"
14 #include "mozilla/dom/Animation.h"
15 #include "mozilla/dom/KeyframeEffect.h"
16 #include "mozilla/dom/DocGroup.h"
18 #include "mozilla/BasePrincipal.h"
20 #include "nsContentUtils.h"
21 #include "nsCSSPseudoElements.h"
23 #include "nsIScriptGlobalObject.h"
24 #include "nsNameSpaceManager.h"
25 #include "nsServiceManagerUtils.h"
26 #include "nsTextFragment.h"
27 #include "nsThreadUtils.h"
29 using namespace mozilla
;
30 using namespace mozilla::dom
;
31 using mozilla::dom::DocGroup
;
32 using mozilla::dom::HTMLSlotElement
;
34 AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>*
35 nsDOMMutationObserver::sScheduledMutationObservers
= nullptr;
37 uint32_t nsDOMMutationObserver::sMutationLevel
= 0;
38 uint64_t nsDOMMutationObserver::sCount
= 0;
40 AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>*
41 nsDOMMutationObserver::sCurrentlyHandlingObservers
= nullptr;
43 nsINodeList
* nsDOMMutationRecord::AddedNodes() {
45 mAddedNodes
= new nsSimpleContentList(mTarget
);
50 nsINodeList
* nsDOMMutationRecord::RemovedNodes() {
52 mRemovedNodes
= new nsSimpleContentList(mTarget
);
57 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord
)
58 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
59 NS_INTERFACE_MAP_ENTRY(nsISupports
)
62 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord
)
63 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord
)
65 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord
, mTarget
,
66 mPreviousSibling
, mNextSibling
,
67 mAddedNodes
, mRemovedNodes
,
68 mAddedAnimations
, mRemovedAnimations
,
69 mChangedAnimations
, mNext
, mOwner
)
73 bool nsMutationReceiverBase::IsObservable(nsIContent
* aContent
) {
74 return !aContent
->ChromeOnlyAccess() &&
75 (Observer()->IsChrome() || !aContent
->IsInNativeAnonymousSubtree());
78 bool nsMutationReceiverBase::ObservesAttr(nsINode
* aRegisterTarget
,
79 mozilla::dom::Element
* aElement
,
80 int32_t aNameSpaceID
, nsAtom
* aAttr
) {
82 return mParent
->ObservesAttr(aRegisterTarget
, aElement
, aNameSpaceID
,
85 if (!Attributes() || (!Subtree() && aElement
!= Target()) ||
87 aRegisterTarget
->SubtreeRoot() != aElement
->SubtreeRoot()) ||
88 !IsObservable(aElement
)) {
91 if (AllAttributes()) {
95 if (aNameSpaceID
!= kNameSpaceID_None
) {
99 nsTArray
<RefPtr
<nsAtom
>>& filters
= AttributeFilter();
100 for (size_t i
= 0; i
< filters
.Length(); ++i
) {
101 if (filters
[i
] == aAttr
) {
108 NS_IMPL_ADDREF(nsMutationReceiver
)
109 NS_IMPL_RELEASE(nsMutationReceiver
)
111 NS_INTERFACE_MAP_BEGIN(nsMutationReceiver
)
112 NS_INTERFACE_MAP_ENTRY(nsISupports
)
113 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
116 nsMutationReceiver::nsMutationReceiver(nsINode
* aTarget
,
117 nsDOMMutationObserver
* aObserver
)
118 : nsMutationReceiverBase(aTarget
, aObserver
) {
119 mTarget
->BindObject(aObserver
);
122 void nsMutationReceiver::Disconnect(bool aRemoveFromObserver
) {
123 if (mRegisterTarget
) {
124 mRegisterTarget
->RemoveMutationObserver(this);
125 mRegisterTarget
= nullptr;
129 nsINode
* target
= mTarget
;
131 nsDOMMutationObserver
* observer
= mObserver
;
135 if (target
&& observer
) {
136 if (aRemoveFromObserver
) {
137 static_cast<nsDOMMutationObserver
*>(observer
)->RemoveReceiver(this);
139 // UnbindObject may delete 'this'!
140 target
->UnbindObject(observer
);
144 void nsMutationReceiver::NativeAnonymousChildListChange(nsIContent
* aContent
,
146 if (!NativeAnonymousChildList()) {
150 nsINode
* parent
= aContent
->GetParentNode();
151 if (!parent
|| (!Subtree() && Target() != parent
) ||
152 (Subtree() && RegisterTarget()->SubtreeRoot() != parent
->SubtreeRoot())) {
156 nsDOMMutationRecord
* m
=
157 Observer()->CurrentRecord(nsGkAtoms::nativeAnonymousChildList
);
165 m
->mRemovedNodes
= new nsSimpleContentList(parent
);
166 m
->mRemovedNodes
->AppendElement(aContent
);
168 m
->mAddedNodes
= new nsSimpleContentList(parent
);
169 m
->mAddedNodes
->AppendElement(aContent
);
173 void nsMutationReceiver::AttributeWillChange(mozilla::dom::Element
* aElement
,
174 int32_t aNameSpaceID
,
177 if (nsAutoMutationBatch::IsBatching() ||
178 !ObservesAttr(RegisterTarget(), aElement
, aNameSpaceID
, aAttribute
)) {
182 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::attributes
);
184 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== aElement
, "Wrong target!");
185 NS_ASSERTION(!m
->mAttrName
|| m
->mAttrName
== aAttribute
, "Wrong attribute!");
187 m
->mTarget
= aElement
;
188 m
->mAttrName
= aAttribute
;
189 if (aNameSpaceID
== kNameSpaceID_None
) {
190 m
->mAttrNamespace
.SetIsVoid(true);
192 nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNameSpaceID
,
197 if (AttributeOldValue() && m
->mPrevValue
.IsVoid()) {
198 if (!aElement
->GetAttr(aNameSpaceID
, aAttribute
, m
->mPrevValue
)) {
199 m
->mPrevValue
.SetIsVoid(true);
204 void nsMutationReceiver::CharacterDataWillChange(
205 nsIContent
* aContent
, const CharacterDataChangeInfo
&) {
206 if (nsAutoMutationBatch::IsBatching() || !CharacterData() ||
207 (!Subtree() && aContent
!= Target()) ||
209 RegisterTarget()->SubtreeRoot() != aContent
->SubtreeRoot()) ||
210 !IsObservable(aContent
)) {
214 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::characterData
);
216 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== aContent
, "Wrong target!");
219 m
->mTarget
= aContent
;
221 if (CharacterDataOldValue() && m
->mPrevValue
.IsVoid()) {
222 aContent
->GetText()->AppendTo(m
->mPrevValue
);
226 void nsMutationReceiver::ContentAppended(nsIContent
* aFirstNewContent
) {
227 nsINode
* parent
= aFirstNewContent
->GetParentNode();
228 bool wantsChildList
=
229 ChildList() && ((Subtree() && RegisterTarget()->SubtreeRoot() ==
230 parent
->SubtreeRoot()) ||
232 if (!wantsChildList
|| !IsObservable(aFirstNewContent
)) {
236 if (nsAutoMutationBatch::IsBatching()) {
237 if (parent
== nsAutoMutationBatch::GetBatchTarget()) {
238 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
243 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
244 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== parent
, "Wrong target!");
246 // Already handled case.
250 m
->mAddedNodes
= new nsSimpleContentList(parent
);
252 nsINode
* n
= aFirstNewContent
;
254 m
->mAddedNodes
->AppendElement(static_cast<nsIContent
*>(n
));
255 n
= n
->GetNextSibling();
257 m
->mPreviousSibling
= aFirstNewContent
->GetPreviousSibling();
260 void nsMutationReceiver::ContentInserted(nsIContent
* aChild
) {
261 nsINode
* parent
= aChild
->GetParentNode();
262 bool wantsChildList
=
263 ChildList() && ((Subtree() && RegisterTarget()->SubtreeRoot() ==
264 parent
->SubtreeRoot()) ||
266 if (!wantsChildList
|| !IsObservable(aChild
)) {
270 if (nsAutoMutationBatch::IsBatching()) {
271 if (parent
== nsAutoMutationBatch::GetBatchTarget()) {
272 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
277 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
279 // Already handled case.
283 m
->mAddedNodes
= new nsSimpleContentList(parent
);
284 m
->mAddedNodes
->AppendElement(aChild
);
285 m
->mPreviousSibling
= aChild
->GetPreviousSibling();
286 m
->mNextSibling
= aChild
->GetNextSibling();
289 void nsMutationReceiver::ContentRemoved(nsIContent
* aChild
,
290 nsIContent
* aPreviousSibling
) {
291 if (!IsObservable(aChild
)) {
295 nsINode
* parent
= aChild
->GetParentNode();
296 if (Subtree() && parent
->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
299 if (nsAutoMutationBatch::IsBatching()) {
300 if (nsAutoMutationBatch::IsRemovalDone()) {
301 // This can happen for example if HTML parser parses to
302 // context node, but needs to move elements around.
305 if (nsAutoMutationBatch::GetBatchTarget() != parent
) {
309 bool wantsChildList
= ChildList() && (Subtree() || parent
== Target());
310 if (wantsChildList
|| Subtree()) {
311 nsAutoMutationBatch::NodeRemoved(aChild
);
312 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
319 // Try to avoid creating transient observer if the node
320 // already has an observer observing the same set of nodes.
321 nsMutationReceiver
* orig
= GetParent() ? GetParent() : this;
322 if (Observer()->GetReceiverFor(aChild
, false, false) != orig
) {
323 bool transientExists
= false;
324 bool isNewEntry
= false;
325 auto* const transientReceivers
=
327 ->mTransientReceivers
332 return MakeUnique
<nsCOMArray
<nsMutationReceiver
>>();
336 for (int32_t i
= 0; i
< transientReceivers
->Count(); ++i
) {
337 nsMutationReceiver
* r
= transientReceivers
->ObjectAt(i
);
338 if (r
->GetParent() == orig
) {
339 transientExists
= true;
343 if (!transientExists
) {
344 // Make sure the elements which are removed from the
345 // subtree are kept in the same observation set.
346 nsMutationReceiver
* tr
;
347 if (orig
->Animations()) {
348 tr
= nsAnimationReceiver::Create(aChild
, orig
);
350 tr
= nsMutationReceiver::Create(aChild
, orig
);
352 transientReceivers
->AppendObject(tr
);
357 if (ChildList() && (Subtree() || parent
== Target())) {
358 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
360 // Already handled case.
366 m
->mRemovedNodes
= new nsSimpleContentList(parent
);
367 m
->mRemovedNodes
->AppendElement(aChild
);
368 m
->mPreviousSibling
= aPreviousSibling
;
369 m
->mNextSibling
= aPreviousSibling
? aPreviousSibling
->GetNextSibling()
370 : parent
->GetFirstChild();
372 // We need to schedule always, so that after microtask mTransientReceivers
373 // can be cleared correctly.
374 Observer()->ScheduleForRun();
377 void nsMutationReceiver::NodeWillBeDestroyed(const nsINode
* aNode
) {
378 NS_ASSERTION(!mParent
, "Shouldn't have mParent here!");
382 void nsAnimationReceiver::RecordAnimationMutation(
383 Animation
* aAnimation
, AnimationMutation aMutationType
) {
384 mozilla::dom::AnimationEffect
* effect
= aAnimation
->GetEffect();
389 mozilla::dom::KeyframeEffect
* keyframeEffect
= effect
->AsKeyframeEffect();
390 if (!keyframeEffect
) {
394 NonOwningAnimationTarget animationTarget
=
395 keyframeEffect
->GetAnimationTarget();
396 if (!animationTarget
) {
400 Element
* elem
= animationTarget
.mElement
;
401 if (!Animations() || !(Subtree() || elem
== Target()) ||
402 elem
->ChromeOnlyAccess()) {
406 // Record animations targeting to a pseudo element only when subtree is true.
407 if (animationTarget
.mPseudoType
!= PseudoStyleType::NotPseudo
&& !Subtree()) {
411 if (nsAutoAnimationMutationBatch::IsBatching()) {
412 switch (aMutationType
) {
413 case eAnimationMutation_Added
:
414 nsAutoAnimationMutationBatch::AnimationAdded(aAnimation
, elem
);
416 case eAnimationMutation_Changed
:
417 nsAutoAnimationMutationBatch::AnimationChanged(aAnimation
, elem
);
419 case eAnimationMutation_Removed
:
420 nsAutoAnimationMutationBatch::AnimationRemoved(aAnimation
, elem
);
424 nsAutoAnimationMutationBatch::AddObserver(Observer());
428 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::animations
);
430 NS_ASSERTION(!m
->mTarget
, "Wrong target!");
434 switch (aMutationType
) {
435 case eAnimationMutation_Added
:
436 m
->mAddedAnimations
.AppendElement(aAnimation
);
438 case eAnimationMutation_Changed
:
439 m
->mChangedAnimations
.AppendElement(aAnimation
);
441 case eAnimationMutation_Removed
:
442 m
->mRemovedAnimations
.AppendElement(aAnimation
);
447 void nsAnimationReceiver::AnimationAdded(Animation
* aAnimation
) {
448 RecordAnimationMutation(aAnimation
, eAnimationMutation_Added
);
451 void nsAnimationReceiver::AnimationChanged(Animation
* aAnimation
) {
452 RecordAnimationMutation(aAnimation
, eAnimationMutation_Changed
);
455 void nsAnimationReceiver::AnimationRemoved(Animation
* aAnimation
) {
456 RecordAnimationMutation(aAnimation
, eAnimationMutation_Removed
);
459 NS_IMPL_ISUPPORTS_INHERITED(nsAnimationReceiver
, nsMutationReceiver
,
460 nsIAnimationObserver
)
464 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver
)
465 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
466 NS_INTERFACE_MAP_ENTRY(nsISupports
)
467 NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver
)
470 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver
)
471 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver
)
473 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver
)
475 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver
)
476 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
477 NS_IMPL_CYCLE_COLLECTION_TRACE_END
479 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver
)
480 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
481 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
482 for (int32_t i
= 0; i
< tmp
->mReceivers
.Count(); ++i
) {
483 tmp
->mReceivers
[i
]->Disconnect(false);
485 tmp
->mReceivers
.Clear();
486 tmp
->ClearPendingRecords();
487 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback
)
488 // No need to handle mTransientReceivers
489 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
491 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver
)
492 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
493 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers
)
494 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation
)
495 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback
)
496 // No need to handle mTransientReceivers
497 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
499 nsMutationReceiver
* nsDOMMutationObserver::GetReceiverFor(
500 nsINode
* aNode
, bool aMayCreate
, bool aWantsAnimations
) {
501 MOZ_ASSERT(aMayCreate
|| !aWantsAnimations
,
502 "the value of aWantsAnimations doesn't matter when aMayCreate is "
503 "false, so just pass in false for it");
505 if (!aMayCreate
&& !aNode
->MayHaveDOMMutationObserver()) {
509 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
510 if (mReceivers
[i
]->Target() == aNode
) {
511 return mReceivers
[i
];
518 nsMutationReceiver
* r
;
519 if (aWantsAnimations
) {
520 r
= nsAnimationReceiver::Create(aNode
, this);
522 r
= nsMutationReceiver::Create(aNode
, this);
524 mReceivers
.AppendObject(r
);
528 void nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver
* aReceiver
) {
529 mReceivers
.RemoveObject(aReceiver
);
532 void nsDOMMutationObserver::GetAllSubtreeObserversFor(
533 nsINode
* aNode
, nsTArray
<nsMutationReceiver
*>& aReceivers
) {
536 if (n
->MayHaveDOMMutationObserver()) {
537 nsMutationReceiver
* r
= GetReceiverFor(n
, false, false);
538 if (r
&& r
->Subtree() && !aReceivers
.Contains(r
)) {
539 aReceivers
.AppendElement(r
);
540 // If we've found all the receivers the observer has,
541 // no need to search for more.
542 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
546 nsCOMArray
<nsMutationReceiver
>* transientReceivers
= nullptr;
547 if (mTransientReceivers
.Get(n
, &transientReceivers
) &&
548 transientReceivers
) {
549 for (int32_t i
= 0; i
< transientReceivers
->Count(); ++i
) {
550 nsMutationReceiver
* r
= transientReceivers
->ObjectAt(i
);
551 nsMutationReceiver
* parent
= r
->GetParent();
552 if (r
->Subtree() && parent
&& !aReceivers
.Contains(parent
)) {
553 aReceivers
.AppendElement(parent
);
556 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
561 n
= n
->GetParentNode();
565 void nsDOMMutationObserver::ScheduleForRun() {
566 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this, sMutationLevel
);
568 if (mWaitingForRun
) {
571 mWaitingForRun
= true;
575 class MutationObserverMicroTask final
: public MicroTaskRunnable
{
578 virtual void Run(AutoSlowOperation
& aAso
) override
{
579 nsDOMMutationObserver::HandleMutations(aAso
);
582 virtual bool Suppressed() override
{
583 return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed();
588 void nsDOMMutationObserver::QueueMutationObserverMicroTask() {
589 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
594 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
595 ccjs
->DispatchToMicroTask(momt
.forget());
598 void nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation
& aAso
) {
599 if (sScheduledMutationObservers
||
600 mozilla::dom::DocGroup::sPendingDocGroups
) {
601 HandleMutationsInternal(aAso
);
605 void nsDOMMutationObserver::RescheduleForRun() {
606 if (!sScheduledMutationObservers
) {
607 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
612 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
613 ccjs
->DispatchToMicroTask(momt
.forget());
614 sScheduledMutationObservers
=
615 new AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>;
618 bool didInsert
= false;
619 for (uint32_t i
= 0; i
< sScheduledMutationObservers
->Length(); ++i
) {
620 if (static_cast<nsDOMMutationObserver
*>((*sScheduledMutationObservers
)[i
])
622 sScheduledMutationObservers
->InsertElementAt(i
, this);
628 sScheduledMutationObservers
->AppendElement(this);
632 void nsDOMMutationObserver::Observe(
633 nsINode
& aTarget
, const mozilla::dom::MutationObserverInit
& aOptions
,
634 nsIPrincipal
& aSubjectPrincipal
, mozilla::ErrorResult
& aRv
) {
635 bool childList
= aOptions
.mChildList
;
637 aOptions
.mAttributes
.WasPassed() && aOptions
.mAttributes
.Value();
639 aOptions
.mCharacterData
.WasPassed() && aOptions
.mCharacterData
.Value();
640 bool subtree
= aOptions
.mSubtree
;
641 bool attributeOldValue
= aOptions
.mAttributeOldValue
.WasPassed() &&
642 aOptions
.mAttributeOldValue
.Value();
643 bool nativeAnonymousChildList
= aOptions
.mNativeAnonymousChildList
;
644 bool characterDataOldValue
= aOptions
.mCharacterDataOldValue
.WasPassed() &&
645 aOptions
.mCharacterDataOldValue
.Value();
646 bool animations
= aOptions
.mAnimations
;
648 if (!aOptions
.mAttributes
.WasPassed() &&
649 (aOptions
.mAttributeOldValue
.WasPassed() ||
650 aOptions
.mAttributeFilter
.WasPassed())) {
654 if (!aOptions
.mCharacterData
.WasPassed() &&
655 aOptions
.mCharacterDataOldValue
.WasPassed()) {
656 characterData
= true;
659 if (!(childList
|| attributes
|| characterData
|| animations
||
660 nativeAnonymousChildList
)) {
662 "One of 'childList', 'attributes', 'characterData' must not be false.");
666 if (aOptions
.mAttributeOldValue
.WasPassed() &&
667 aOptions
.mAttributeOldValue
.Value() && !attributes
) {
669 "If 'attributeOldValue' is true, 'attributes' must not be false.");
673 if (aOptions
.mAttributeFilter
.WasPassed() && !attributes
) {
675 "If 'attributesFilter' is present, 'attributes' must not be false.");
679 if (aOptions
.mCharacterDataOldValue
.WasPassed() &&
680 aOptions
.mCharacterDataOldValue
.Value() && !characterData
) {
682 "If 'characterDataOldValue' is true, 'characterData' must not be "
687 nsTArray
<RefPtr
<nsAtom
>> filters
;
688 bool allAttrs
= true;
689 if (aOptions
.mAttributeFilter
.WasPassed()) {
691 const mozilla::dom::Sequence
<nsString
>& filtersAsString
=
692 aOptions
.mAttributeFilter
.Value();
693 uint32_t len
= filtersAsString
.Length();
694 filters
.SetCapacity(len
);
696 for (uint32_t i
= 0; i
< len
; ++i
) {
697 filters
.AppendElement(NS_Atomize(filtersAsString
[i
]));
701 nsMutationReceiver
* r
= GetReceiverFor(&aTarget
, true, animations
);
702 r
->SetChildList(childList
);
703 r
->SetAttributes(attributes
);
704 r
->SetCharacterData(characterData
);
705 r
->SetSubtree(subtree
);
706 r
->SetAttributeOldValue(attributeOldValue
);
707 r
->SetCharacterDataOldValue(characterDataOldValue
);
708 r
->SetNativeAnonymousChildList(nativeAnonymousChildList
);
709 r
->SetAttributeFilter(std::move(filters
));
710 r
->SetAllAttributes(allAttrs
);
711 r
->SetAnimations(animations
);
714 if (!aSubjectPrincipal
.IsSystemPrincipal() &&
715 !aSubjectPrincipal
.GetIsAddonOrExpandedAddonPrincipal()) {
716 if (nsPIDOMWindowInner
* window
= aTarget
.OwnerDoc()->GetInnerWindow()) {
717 window
->SetMutationObserverHasObservedNodeForTelemetry();
722 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
723 NS_WARNING_ASSERTION(mReceivers
[i
]->Target(),
724 "All the receivers should have a target!");
729 void nsDOMMutationObserver::Disconnect() {
730 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
731 mReceivers
[i
]->Disconnect(false);
734 mCurrentMutations
.Clear();
735 ClearPendingRecords();
738 void nsDOMMutationObserver::TakeRecords(
739 nsTArray
<RefPtr
<nsDOMMutationRecord
>>& aRetVal
) {
741 aRetVal
.SetCapacity(mPendingMutationCount
);
742 RefPtr
<nsDOMMutationRecord
> current
;
743 current
.swap(mFirstPendingMutation
);
744 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
745 RefPtr
<nsDOMMutationRecord
> next
;
746 current
->mNext
.swap(next
);
747 if (!mMergeAttributeRecords
||
748 !MergeableAttributeRecord(aRetVal
.SafeLastElement(nullptr), current
)) {
749 *aRetVal
.AppendElement() = std::move(current
);
753 ClearPendingRecords();
756 void nsDOMMutationObserver::GetObservingInfo(
757 nsTArray
<Nullable
<MutationObservingInfo
>>& aResult
,
758 mozilla::ErrorResult
& aRv
) {
759 aResult
.SetCapacity(mReceivers
.Count());
760 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
761 MutationObservingInfo
& info
= aResult
.AppendElement()->SetValue();
762 nsMutationReceiver
* mr
= mReceivers
[i
];
763 info
.mChildList
= mr
->ChildList();
764 info
.mAttributes
.Construct(mr
->Attributes());
765 info
.mCharacterData
.Construct(mr
->CharacterData());
766 info
.mSubtree
= mr
->Subtree();
767 info
.mAttributeOldValue
.Construct(mr
->AttributeOldValue());
768 info
.mCharacterDataOldValue
.Construct(mr
->CharacterDataOldValue());
769 info
.mNativeAnonymousChildList
= mr
->NativeAnonymousChildList();
770 info
.mAnimations
= mr
->Animations();
771 nsTArray
<RefPtr
<nsAtom
>>& filters
= mr
->AttributeFilter();
772 if (filters
.Length()) {
773 info
.mAttributeFilter
.Construct();
774 mozilla::dom::Sequence
<nsString
>& filtersAsStrings
=
775 info
.mAttributeFilter
.Value();
777 filtersAsStrings
.AppendElements(filters
.Length(), mozilla::fallible
);
779 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
782 for (size_t j
= 0; j
< filters
.Length(); ++j
) {
783 filters
[j
]->ToString(strings
[j
]);
786 info
.mObservedNode
= mr
->Target();
791 already_AddRefed
<nsDOMMutationObserver
> nsDOMMutationObserver::Constructor(
792 const mozilla::dom::GlobalObject
& aGlobal
,
793 mozilla::dom::MutationCallback
& aCb
, mozilla::ErrorResult
& aRv
) {
794 nsCOMPtr
<nsPIDOMWindowInner
> window
=
795 do_QueryInterface(aGlobal
.GetAsSupports());
797 aRv
.Throw(NS_ERROR_FAILURE
);
800 bool isChrome
= nsContentUtils::IsChromeDoc(window
->GetExtantDoc());
801 RefPtr
<nsDOMMutationObserver
> observer
=
802 new nsDOMMutationObserver(std::move(window
), aCb
, isChrome
);
803 return observer
.forget();
806 bool nsDOMMutationObserver::MergeableAttributeRecord(
807 nsDOMMutationRecord
* aOldRecord
, nsDOMMutationRecord
* aRecord
) {
808 MOZ_ASSERT(mMergeAttributeRecords
);
809 return aOldRecord
&& aOldRecord
->mType
== nsGkAtoms::attributes
&&
810 aOldRecord
->mType
== aRecord
->mType
&&
811 aOldRecord
->mTarget
== aRecord
->mTarget
&&
812 aOldRecord
->mAttrName
== aRecord
->mAttrName
&&
813 aOldRecord
->mAttrNamespace
.Equals(aRecord
->mAttrNamespace
);
816 void nsDOMMutationObserver::HandleMutation() {
817 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
818 NS_ASSERTION(mCurrentMutations
.IsEmpty(),
819 "Still generating MutationRecords?");
821 mWaitingForRun
= false;
823 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
824 mReceivers
[i
]->RemoveClones();
826 mTransientReceivers
.Clear();
828 nsPIDOMWindowOuter
* outer
= mOwner
->GetOuterWindow();
829 if (!mPendingMutationCount
|| !outer
||
830 outer
->GetCurrentInnerWindow() != mOwner
) {
831 ClearPendingRecords();
835 mozilla::dom::Sequence
<mozilla::OwningNonNull
<nsDOMMutationRecord
>> mutations
;
836 if (mutations
.SetCapacity(mPendingMutationCount
, mozilla::fallible
)) {
837 // We can't use TakeRecords easily here, because it deals with a
838 // different type of array, and we want to optimize out any extra copying.
839 RefPtr
<nsDOMMutationRecord
> current
;
840 current
.swap(mFirstPendingMutation
);
841 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
842 RefPtr
<nsDOMMutationRecord
> next
;
843 current
->mNext
.swap(next
);
844 if (!mMergeAttributeRecords
||
845 !MergeableAttributeRecord(
846 mutations
.Length() ? mutations
.LastElement().get() : nullptr,
848 *mutations
.AppendElement(mozilla::fallible
) = current
;
853 ClearPendingRecords();
855 RefPtr
<dom::MutationCallback
> callback(mCallback
);
856 callback
->Call(this, mutations
, *this);
859 void nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation
& aAso
) {
860 nsTArray
<RefPtr
<nsDOMMutationObserver
>>* suppressedObservers
= nullptr;
862 // This loop implements:
863 // * Let signalList be a copy of unit of related similar-origin browsing
864 // contexts' signal slot list.
865 // * Empty unit of related similar-origin browsing contexts' signal slot
867 nsTArray
<nsTArray
<RefPtr
<HTMLSlotElement
>>> signalLists
;
868 if (DocGroup::sPendingDocGroups
) {
869 signalLists
.SetCapacity(DocGroup::sPendingDocGroups
->Length());
870 for (DocGroup
* docGroup
: *DocGroup::sPendingDocGroups
) {
871 signalLists
.AppendElement(docGroup
->MoveSignalSlotList());
873 delete DocGroup::sPendingDocGroups
;
874 DocGroup::sPendingDocGroups
= nullptr;
877 if (sScheduledMutationObservers
) {
878 AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>* observers
=
879 sScheduledMutationObservers
;
880 sScheduledMutationObservers
= nullptr;
881 for (uint32_t i
= 0; i
< observers
->Length(); ++i
) {
882 RefPtr
<nsDOMMutationObserver
> currentObserver
=
883 static_cast<nsDOMMutationObserver
*>((*observers
)[i
]);
884 if (!currentObserver
->Suppressed()) {
885 currentObserver
->HandleMutation();
887 if (!suppressedObservers
) {
888 suppressedObservers
= new nsTArray
<RefPtr
<nsDOMMutationObserver
>>;
890 if (!suppressedObservers
->Contains(currentObserver
)) {
891 suppressedObservers
->AppendElement(currentObserver
);
896 aAso
.CheckForInterrupt();
899 if (suppressedObservers
) {
900 for (uint32_t i
= 0; i
< suppressedObservers
->Length(); ++i
) {
901 static_cast<nsDOMMutationObserver
*>(suppressedObservers
->ElementAt(i
))
902 ->RescheduleForRun();
904 delete suppressedObservers
;
905 suppressedObservers
= nullptr;
908 // Fire slotchange event for each slot in signalLists.
909 for (const nsTArray
<RefPtr
<HTMLSlotElement
>>& signalList
: signalLists
) {
910 for (const RefPtr
<HTMLSlotElement
>& signal
: signalList
) {
911 signal
->FireSlotChangeEvent();
916 nsDOMMutationRecord
* nsDOMMutationObserver::CurrentRecord(nsAtom
* aType
) {
917 NS_ASSERTION(sMutationLevel
> 0, "Unexpected mutation level!");
919 while (mCurrentMutations
.Length() < sMutationLevel
) {
920 mCurrentMutations
.AppendElement(static_cast<nsDOMMutationRecord
*>(nullptr));
923 uint32_t last
= sMutationLevel
- 1;
924 if (!mCurrentMutations
[last
]) {
925 RefPtr
<nsDOMMutationRecord
> r
=
926 new nsDOMMutationRecord(aType
, GetParentObject());
927 mCurrentMutations
[last
] = r
;
928 AppendMutationRecord(r
.forget());
933 MOZ_ASSERT(sCurrentlyHandlingObservers
->Length() == sMutationLevel
);
934 for (size_t i
= 0; i
< sCurrentlyHandlingObservers
->Length(); ++i
) {
935 MOZ_ASSERT(sCurrentlyHandlingObservers
->ElementAt(i
).Contains(this),
936 "MutationObserver should be added as an observer of all the "
937 "nested mutations!");
941 NS_ASSERTION(mCurrentMutations
[last
]->mType
== aType
,
942 "Unexpected MutationRecord type!");
944 return mCurrentMutations
[last
];
947 nsDOMMutationObserver::~nsDOMMutationObserver() {
948 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
949 mReceivers
[i
]->RemoveClones();
953 void nsDOMMutationObserver::EnterMutationHandling() { ++sMutationLevel
; }
955 // Leave the current mutation level (there can be several levels if in case
956 // of nested calls to the nsIMutationObserver methods).
957 // The most recent mutation record is removed from mCurrentMutations, so
958 // that is doesn't get modified anymore by receivers.
959 void nsDOMMutationObserver::LeaveMutationHandling() {
960 if (sCurrentlyHandlingObservers
&&
961 sCurrentlyHandlingObservers
->Length() == sMutationLevel
) {
962 nsTArray
<RefPtr
<nsDOMMutationObserver
>> obs
=
963 sCurrentlyHandlingObservers
->PopLastElement();
964 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
965 nsDOMMutationObserver
* o
= static_cast<nsDOMMutationObserver
*>(obs
[i
]);
966 if (o
->mCurrentMutations
.Length() == sMutationLevel
) {
967 // It is already in pending mutations.
968 o
->mCurrentMutations
.RemoveLastElement();
975 void nsDOMMutationObserver::AddCurrentlyHandlingObserver(
976 nsDOMMutationObserver
* aObserver
, uint32_t aMutationLevel
) {
977 NS_ASSERTION(aMutationLevel
> 0, "Unexpected mutation level!");
979 if (aMutationLevel
> 1) {
980 // MutationObserver must be in the currently handling observer list
981 // in all the nested levels.
982 AddCurrentlyHandlingObserver(aObserver
, aMutationLevel
- 1);
985 if (!sCurrentlyHandlingObservers
) {
986 sCurrentlyHandlingObservers
=
987 new AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>;
990 while (sCurrentlyHandlingObservers
->Length() < aMutationLevel
) {
991 sCurrentlyHandlingObservers
->InsertElementAt(
992 sCurrentlyHandlingObservers
->Length());
995 uint32_t index
= aMutationLevel
- 1;
996 if (!sCurrentlyHandlingObservers
->ElementAt(index
).Contains(aObserver
)) {
997 sCurrentlyHandlingObservers
->ElementAt(index
).AppendElement(aObserver
);
1001 void nsDOMMutationObserver::Shutdown() {
1002 delete sCurrentlyHandlingObservers
;
1003 sCurrentlyHandlingObservers
= nullptr;
1004 delete sScheduledMutationObservers
;
1005 sScheduledMutationObservers
= nullptr;
1008 nsAutoMutationBatch
* nsAutoMutationBatch::sCurrentBatch
= nullptr;
1010 void nsAutoMutationBatch::Done() {
1011 if (sCurrentBatch
!= this) {
1015 sCurrentBatch
= mPreviousBatch
;
1016 if (mObservers
.IsEmpty()) {
1017 nsDOMMutationObserver::LeaveMutationHandling();
1022 uint32_t len
= mObservers
.Length();
1023 for (uint32_t i
= 0; i
< len
; ++i
) {
1024 nsDOMMutationObserver
* ob
= mObservers
[i
].mObserver
;
1025 bool wantsChildList
= mObservers
[i
].mWantsChildList
;
1027 RefPtr
<nsSimpleContentList
> removedList
;
1028 if (wantsChildList
) {
1029 removedList
= new nsSimpleContentList(mBatchTarget
);
1032 nsTArray
<nsMutationReceiver
*> allObservers
;
1033 ob
->GetAllSubtreeObserversFor(mBatchTarget
, allObservers
);
1035 int32_t j
= mFromFirstToLast
? 0 : mRemovedNodes
.Length() - 1;
1036 int32_t end
= mFromFirstToLast
? mRemovedNodes
.Length() : -1;
1037 for (; j
!= end
; mFromFirstToLast
? ++j
: --j
) {
1038 nsCOMPtr
<nsIContent
> removed
= mRemovedNodes
[j
];
1040 removedList
->AppendElement(removed
);
1043 if (allObservers
.Length()) {
1044 auto* const transientReceivers
=
1045 ob
->mTransientReceivers
.GetOrInsertNew(removed
);
1046 for (uint32_t k
= 0; k
< allObservers
.Length(); ++k
) {
1047 nsMutationReceiver
* r
= allObservers
[k
];
1048 nsMutationReceiver
* orig
= r
->GetParent() ? r
->GetParent() : r
;
1049 if (ob
->GetReceiverFor(removed
, false, false) != orig
) {
1050 // Make sure the elements which are removed from the
1051 // subtree are kept in the same observation set.
1052 nsMutationReceiver
* tr
;
1053 if (orig
->Animations()) {
1054 tr
= nsAnimationReceiver::Create(removed
, orig
);
1056 tr
= nsMutationReceiver::Create(removed
, orig
);
1058 transientReceivers
->AppendObject(tr
);
1063 if (wantsChildList
&& (mRemovedNodes
.Length() || mAddedNodes
.Length())) {
1064 RefPtr
<nsSimpleContentList
> addedList
=
1065 new nsSimpleContentList(mBatchTarget
);
1066 for (uint32_t i
= 0; i
< mAddedNodes
.Length(); ++i
) {
1067 addedList
->AppendElement(mAddedNodes
[i
]);
1069 RefPtr
<nsDOMMutationRecord
> m
=
1070 new nsDOMMutationRecord(nsGkAtoms::childList
, ob
->GetParentObject());
1071 m
->mTarget
= mBatchTarget
;
1072 m
->mRemovedNodes
= removedList
;
1073 m
->mAddedNodes
= addedList
;
1074 m
->mPreviousSibling
= mPrevSibling
;
1075 m
->mNextSibling
= mNextSibling
;
1076 ob
->AppendMutationRecord(m
.forget());
1078 // Always schedule the observer so that transient receivers are
1079 // removed correctly.
1080 ob
->ScheduleForRun();
1082 nsDOMMutationObserver::LeaveMutationHandling();
1085 nsAutoAnimationMutationBatch
* nsAutoAnimationMutationBatch::sCurrentBatch
=
1088 void nsAutoAnimationMutationBatch::Done() {
1089 if (sCurrentBatch
!= this) {
1093 sCurrentBatch
= nullptr;
1094 if (mObservers
.IsEmpty()) {
1095 nsDOMMutationObserver::LeaveMutationHandling();
1100 mBatchTargets
.Sort(TreeOrderComparator());
1102 for (nsDOMMutationObserver
* ob
: mObservers
) {
1103 bool didAddRecords
= false;
1105 for (nsINode
* target
: mBatchTargets
) {
1106 EntryArray
* entries
= mEntryTable
.Get(target
);
1108 "Targets in entry table and targets list should match");
1110 RefPtr
<nsDOMMutationRecord
> m
=
1111 new nsDOMMutationRecord(nsGkAtoms::animations
, ob
->GetParentObject());
1112 m
->mTarget
= target
;
1114 for (const Entry
& e
: *entries
) {
1115 if (e
.mState
== eState_Added
) {
1116 m
->mAddedAnimations
.AppendElement(e
.mAnimation
);
1117 } else if (e
.mState
== eState_Removed
) {
1118 m
->mRemovedAnimations
.AppendElement(e
.mAnimation
);
1119 } else if (e
.mState
== eState_RemainedPresent
&& e
.mChanged
) {
1120 m
->mChangedAnimations
.AppendElement(e
.mAnimation
);
1124 if (!m
->mAddedAnimations
.IsEmpty() || !m
->mChangedAnimations
.IsEmpty() ||
1125 !m
->mRemovedAnimations
.IsEmpty()) {
1126 ob
->AppendMutationRecord(m
.forget());
1127 didAddRecords
= true;
1131 if (didAddRecords
) {
1132 ob
->ScheduleForRun();
1135 nsDOMMutationObserver::LeaveMutationHandling();