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
;
32 AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>*
33 nsDOMMutationObserver::sScheduledMutationObservers
= nullptr;
35 uint32_t nsDOMMutationObserver::sMutationLevel
= 0;
36 uint64_t nsDOMMutationObserver::sCount
= 0;
38 AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>*
39 nsDOMMutationObserver::sCurrentlyHandlingObservers
= nullptr;
41 nsINodeList
* nsDOMMutationRecord::AddedNodes() {
43 mAddedNodes
= new nsSimpleContentList(mTarget
);
48 nsINodeList
* nsDOMMutationRecord::RemovedNodes() {
50 mRemovedNodes
= new nsSimpleContentList(mTarget
);
55 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord
)
56 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
57 NS_INTERFACE_MAP_ENTRY(nsISupports
)
60 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord
)
61 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord
)
63 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord
, mTarget
,
64 mPreviousSibling
, mNextSibling
,
65 mAddedNodes
, mRemovedNodes
,
66 mAddedAnimations
, mRemovedAnimations
,
67 mChangedAnimations
, mNext
, mOwner
)
71 bool nsMutationReceiverBase::IsObservable(nsIContent
* aContent
) {
72 return !aContent
->ChromeOnlyAccess() || ChromeOnlyNodes();
75 bool nsMutationReceiverBase::ObservesAttr(nsINode
* aRegisterTarget
,
77 int32_t aNameSpaceID
, nsAtom
* aAttr
) {
79 return mParent
->ObservesAttr(aRegisterTarget
, aElement
, aNameSpaceID
,
82 if (!Attributes() || (!Subtree() && aElement
!= Target()) ||
84 aRegisterTarget
->SubtreeRoot() != aElement
->SubtreeRoot()) ||
85 !IsObservable(aElement
)) {
88 if (AllAttributes()) {
92 if (aNameSpaceID
!= kNameSpaceID_None
) {
96 nsTArray
<RefPtr
<nsAtom
>>& filters
= AttributeFilter();
97 for (size_t i
= 0; i
< filters
.Length(); ++i
) {
98 if (filters
[i
] == aAttr
) {
105 NS_IMPL_ADDREF(nsMutationReceiver
)
106 NS_IMPL_RELEASE(nsMutationReceiver
)
108 NS_INTERFACE_MAP_BEGIN(nsMutationReceiver
)
109 NS_INTERFACE_MAP_ENTRY(nsISupports
)
110 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
113 nsMutationReceiver::nsMutationReceiver(nsINode
* aTarget
,
114 nsDOMMutationObserver
* aObserver
)
115 : nsMutationReceiverBase(aTarget
, aObserver
) {
116 mTarget
->BindObject(aObserver
);
119 void nsMutationReceiver::Disconnect(bool aRemoveFromObserver
) {
120 if (mRegisterTarget
) {
121 mRegisterTarget
->RemoveMutationObserver(this);
122 mRegisterTarget
= nullptr;
126 nsINode
* target
= mTarget
;
128 nsDOMMutationObserver
* observer
= mObserver
;
132 if (target
&& observer
) {
133 if (aRemoveFromObserver
) {
134 static_cast<nsDOMMutationObserver
*>(observer
)->RemoveReceiver(this);
136 // UnbindObject may delete 'this'!
137 target
->UnbindObject(observer
);
141 void nsMutationReceiver::AttributeWillChange(Element
* aElement
,
142 int32_t aNameSpaceID
,
145 if (nsAutoMutationBatch::IsBatching() ||
146 !ObservesAttr(RegisterTarget(), aElement
, aNameSpaceID
, aAttribute
)) {
150 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::attributes
);
152 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== aElement
, "Wrong target!");
153 NS_ASSERTION(!m
->mAttrName
|| m
->mAttrName
== aAttribute
, "Wrong attribute!");
155 m
->mTarget
= aElement
;
156 m
->mAttrName
= aAttribute
;
157 if (aNameSpaceID
== kNameSpaceID_None
) {
158 m
->mAttrNamespace
.SetIsVoid(true);
160 nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNameSpaceID
,
165 if (AttributeOldValue() && m
->mPrevValue
.IsVoid()) {
166 if (!aElement
->GetAttr(aNameSpaceID
, aAttribute
, m
->mPrevValue
)) {
167 m
->mPrevValue
.SetIsVoid(true);
172 void nsMutationReceiver::CharacterDataWillChange(
173 nsIContent
* aContent
, const CharacterDataChangeInfo
&) {
174 if (nsAutoMutationBatch::IsBatching() || !CharacterData() ||
175 (!Subtree() && aContent
!= Target()) ||
177 RegisterTarget()->SubtreeRoot() != aContent
->SubtreeRoot()) ||
178 !IsObservable(aContent
)) {
182 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::characterData
);
184 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== aContent
, "Wrong target!");
187 m
->mTarget
= aContent
;
189 if (CharacterDataOldValue() && m
->mPrevValue
.IsVoid()) {
190 aContent
->GetText()->AppendTo(m
->mPrevValue
);
194 void nsMutationReceiver::ContentAppended(nsIContent
* aFirstNewContent
) {
195 nsINode
* parent
= aFirstNewContent
->GetParentNode();
196 bool wantsChildList
=
197 ChildList() && ((Subtree() && RegisterTarget()->SubtreeRoot() ==
198 parent
->SubtreeRoot()) ||
200 if (!wantsChildList
|| !IsObservable(aFirstNewContent
)) {
204 if (nsAutoMutationBatch::IsBatching()) {
205 if (parent
== nsAutoMutationBatch::GetBatchTarget()) {
206 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
211 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
212 NS_ASSERTION(!m
->mTarget
|| m
->mTarget
== parent
, "Wrong target!");
214 // Already handled case.
218 m
->mAddedNodes
= new nsSimpleContentList(parent
);
220 nsINode
* n
= aFirstNewContent
;
222 m
->mAddedNodes
->AppendElement(static_cast<nsIContent
*>(n
));
223 n
= n
->GetNextSibling();
225 m
->mPreviousSibling
= aFirstNewContent
->GetPreviousSibling();
228 void nsMutationReceiver::ContentInserted(nsIContent
* aChild
) {
229 nsINode
* parent
= aChild
->GetParentNode();
230 bool wantsChildList
=
231 ChildList() && ((Subtree() && RegisterTarget()->SubtreeRoot() ==
232 parent
->SubtreeRoot()) ||
234 if (!wantsChildList
|| !IsObservable(aChild
)) {
238 if (nsAutoMutationBatch::IsBatching()) {
239 if (parent
== nsAutoMutationBatch::GetBatchTarget()) {
240 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
245 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
247 // Already handled case.
251 m
->mAddedNodes
= new nsSimpleContentList(parent
);
252 m
->mAddedNodes
->AppendElement(aChild
);
253 m
->mPreviousSibling
= aChild
->GetPreviousSibling();
254 m
->mNextSibling
= aChild
->GetNextSibling();
257 void nsMutationReceiver::ContentWillBeRemoved(nsIContent
* aChild
,
258 const BatchRemovalState
*) {
259 if (!IsObservable(aChild
)) {
263 nsINode
* parent
= aChild
->GetParentNode();
264 if (Subtree() && parent
->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
267 if (nsAutoMutationBatch::IsBatching()) {
268 if (nsAutoMutationBatch::IsRemovalDone()) {
269 // This can happen for example if HTML parser parses to
270 // context node, but needs to move elements around.
273 if (nsAutoMutationBatch::GetBatchTarget() != parent
) {
277 bool wantsChildList
= ChildList() && (Subtree() || parent
== Target());
278 if (wantsChildList
|| Subtree()) {
279 nsAutoMutationBatch::NodeRemoved(aChild
);
280 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList
);
287 // Try to avoid creating transient observer if the node
288 // already has an observer observing the same set of nodes.
289 nsMutationReceiver
* orig
= GetParent() ? GetParent() : this;
290 if (Observer()->GetReceiverFor(aChild
, false, false) != orig
) {
291 bool transientExists
= false;
292 bool isNewEntry
= false;
293 auto* const transientReceivers
=
295 ->mTransientReceivers
300 return MakeUnique
<nsCOMArray
<nsMutationReceiver
>>();
304 for (int32_t i
= 0; i
< transientReceivers
->Count(); ++i
) {
305 nsMutationReceiver
* r
= transientReceivers
->ObjectAt(i
);
306 if (r
->GetParent() == orig
) {
307 transientExists
= true;
311 if (!transientExists
) {
312 // Make sure the elements which are removed from the
313 // subtree are kept in the same observation set.
314 nsMutationReceiver
* tr
;
315 if (orig
->Animations()) {
316 tr
= nsAnimationReceiver::Create(aChild
, orig
);
318 tr
= nsMutationReceiver::Create(aChild
, orig
);
320 transientReceivers
->AppendObject(tr
);
325 if (ChildList() && (Subtree() || parent
== Target())) {
326 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::childList
);
328 // Already handled case.
334 m
->mRemovedNodes
= new nsSimpleContentList(parent
);
335 m
->mRemovedNodes
->AppendElement(aChild
);
336 m
->mPreviousSibling
= aChild
->GetPreviousSibling();
337 m
->mNextSibling
= aChild
->GetNextSibling();
339 // We need to schedule always, so that after microtask mTransientReceivers
340 // can be cleared correctly.
341 Observer()->ScheduleForRun();
344 void nsMutationReceiver::NodeWillBeDestroyed(nsINode
* aNode
) {
345 NS_ASSERTION(!mParent
, "Shouldn't have mParent here!");
349 void nsAnimationReceiver::RecordAnimationMutation(
350 Animation
* aAnimation
, AnimationMutation aMutationType
) {
351 AnimationEffect
* effect
= aAnimation
->GetEffect();
356 KeyframeEffect
* keyframeEffect
= effect
->AsKeyframeEffect();
357 if (!keyframeEffect
) {
361 NonOwningAnimationTarget animationTarget
=
362 keyframeEffect
->GetAnimationTarget();
363 if (!animationTarget
) {
367 Element
* elem
= animationTarget
.mElement
;
368 if (!Animations() || !(Subtree() || elem
== Target()) ||
369 elem
->ChromeOnlyAccess()) {
373 // Record animations targeting to a pseudo element only when subtree is true.
374 if (!animationTarget
.mPseudoRequest
.IsNotPseudo() && !Subtree()) {
378 if (nsAutoAnimationMutationBatch::IsBatching()) {
379 switch (aMutationType
) {
380 case eAnimationMutation_Added
:
381 nsAutoAnimationMutationBatch::AnimationAdded(aAnimation
, elem
);
383 case eAnimationMutation_Changed
:
384 nsAutoAnimationMutationBatch::AnimationChanged(aAnimation
, elem
);
386 case eAnimationMutation_Removed
:
387 nsAutoAnimationMutationBatch::AnimationRemoved(aAnimation
, elem
);
391 nsAutoAnimationMutationBatch::AddObserver(Observer());
395 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::animations
);
397 NS_ASSERTION(!m
->mTarget
, "Wrong target!");
401 switch (aMutationType
) {
402 case eAnimationMutation_Added
:
403 m
->mAddedAnimations
.AppendElement(aAnimation
);
405 case eAnimationMutation_Changed
:
406 m
->mChangedAnimations
.AppendElement(aAnimation
);
408 case eAnimationMutation_Removed
:
409 m
->mRemovedAnimations
.AppendElement(aAnimation
);
414 void nsAnimationReceiver::AnimationAdded(Animation
* aAnimation
) {
415 RecordAnimationMutation(aAnimation
, eAnimationMutation_Added
);
418 void nsAnimationReceiver::AnimationChanged(Animation
* aAnimation
) {
419 RecordAnimationMutation(aAnimation
, eAnimationMutation_Changed
);
422 void nsAnimationReceiver::AnimationRemoved(Animation
* aAnimation
) {
423 RecordAnimationMutation(aAnimation
, eAnimationMutation_Removed
);
426 NS_IMPL_ISUPPORTS_INHERITED(nsAnimationReceiver
, nsMutationReceiver
,
427 nsIAnimationObserver
)
431 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver
)
432 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
433 NS_INTERFACE_MAP_ENTRY(nsISupports
)
434 NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver
)
437 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver
)
438 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver
)
440 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver
)
442 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver
)
443 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
444 NS_IMPL_CYCLE_COLLECTION_TRACE_END
446 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver
)
447 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
448 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
449 for (int32_t i
= 0; i
< tmp
->mReceivers
.Count(); ++i
) {
450 tmp
->mReceivers
[i
]->Disconnect(false);
452 tmp
->mReceivers
.Clear();
453 tmp
->ClearPendingRecords();
454 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback
)
455 // No need to handle mTransientReceivers
456 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
458 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver
)
459 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers
)
461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation
)
462 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback
)
463 // No need to handle mTransientReceivers
464 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
466 nsMutationReceiver
* nsDOMMutationObserver::GetReceiverFor(
467 nsINode
* aNode
, bool aMayCreate
, bool aWantsAnimations
) {
468 MOZ_ASSERT(aMayCreate
|| !aWantsAnimations
,
469 "the value of aWantsAnimations doesn't matter when aMayCreate is "
470 "false, so just pass in false for it");
472 if (!aMayCreate
&& !aNode
->MayHaveDOMMutationObserver()) {
476 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
477 if (mReceivers
[i
]->Target() == aNode
) {
478 return mReceivers
[i
];
485 nsMutationReceiver
* r
;
486 if (aWantsAnimations
) {
487 r
= nsAnimationReceiver::Create(aNode
, this);
489 r
= nsMutationReceiver::Create(aNode
, this);
491 mReceivers
.AppendObject(r
);
495 void nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver
* aReceiver
) {
496 mReceivers
.RemoveObject(aReceiver
);
499 void nsDOMMutationObserver::GetAllSubtreeObserversFor(
500 nsINode
* aNode
, nsTArray
<nsMutationReceiver
*>& aReceivers
) {
503 if (n
->MayHaveDOMMutationObserver()) {
504 nsMutationReceiver
* r
= GetReceiverFor(n
, false, false);
505 if (r
&& r
->Subtree() && !aReceivers
.Contains(r
)) {
506 aReceivers
.AppendElement(r
);
507 // If we've found all the receivers the observer has,
508 // no need to search for more.
509 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
513 nsCOMArray
<nsMutationReceiver
>* transientReceivers
= nullptr;
514 if (mTransientReceivers
.Get(n
, &transientReceivers
) &&
515 transientReceivers
) {
516 for (int32_t i
= 0; i
< transientReceivers
->Count(); ++i
) {
517 nsMutationReceiver
* r
= transientReceivers
->ObjectAt(i
);
518 nsMutationReceiver
* parent
= r
->GetParent();
519 if (r
->Subtree() && parent
&& !aReceivers
.Contains(parent
)) {
520 aReceivers
.AppendElement(parent
);
523 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
528 n
= n
->GetParentNode();
532 void nsDOMMutationObserver::ScheduleForRun() {
533 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this, sMutationLevel
);
535 if (mWaitingForRun
) {
538 mWaitingForRun
= true;
542 class MutationObserverMicroTask final
: public MicroTaskRunnable
{
545 virtual void Run(AutoSlowOperation
& aAso
) override
{
546 nsDOMMutationObserver::HandleMutations(aAso
);
549 virtual bool Suppressed() override
{
550 return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed();
555 void nsDOMMutationObserver::QueueMutationObserverMicroTask() {
556 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
561 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
562 ccjs
->DispatchToMicroTask(momt
.forget());
565 void nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation
& aAso
) {
566 if (sScheduledMutationObservers
|| DocGroup::sPendingDocGroups
) {
567 HandleMutationsInternal(aAso
);
571 void nsDOMMutationObserver::RescheduleForRun() {
572 if (!sScheduledMutationObservers
) {
573 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
578 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
579 ccjs
->DispatchToMicroTask(momt
.forget());
580 sScheduledMutationObservers
=
581 new AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>;
584 bool didInsert
= false;
585 for (uint32_t i
= 0; i
< sScheduledMutationObservers
->Length(); ++i
) {
586 if (static_cast<nsDOMMutationObserver
*>((*sScheduledMutationObservers
)[i
])
588 sScheduledMutationObservers
->InsertElementAt(i
, this);
594 sScheduledMutationObservers
->AppendElement(this);
598 void nsDOMMutationObserver::Observe(nsINode
& aTarget
,
599 const MutationObserverInit
& aOptions
,
600 nsIPrincipal
& aSubjectPrincipal
,
602 bool childList
= aOptions
.mChildList
;
604 aOptions
.mAttributes
.WasPassed() && aOptions
.mAttributes
.Value();
606 aOptions
.mCharacterData
.WasPassed() && aOptions
.mCharacterData
.Value();
607 bool subtree
= aOptions
.mSubtree
;
608 bool attributeOldValue
= aOptions
.mAttributeOldValue
.WasPassed() &&
609 aOptions
.mAttributeOldValue
.Value();
610 bool characterDataOldValue
= aOptions
.mCharacterDataOldValue
.WasPassed() &&
611 aOptions
.mCharacterDataOldValue
.Value();
612 bool animations
= aOptions
.mAnimations
;
613 bool chromeOnlyNodes
= aOptions
.mChromeOnlyNodes
;
615 if (!aOptions
.mAttributes
.WasPassed() &&
616 (aOptions
.mAttributeOldValue
.WasPassed() ||
617 aOptions
.mAttributeFilter
.WasPassed())) {
621 if (!aOptions
.mCharacterData
.WasPassed() &&
622 aOptions
.mCharacterDataOldValue
.WasPassed()) {
623 characterData
= true;
626 if (!(childList
|| attributes
|| characterData
|| animations
)) {
628 "One of 'childList', 'attributes', 'characterData' must not be false.");
632 if (aOptions
.mAttributeOldValue
.WasPassed() &&
633 aOptions
.mAttributeOldValue
.Value() && !attributes
) {
635 "If 'attributeOldValue' is true, 'attributes' must not be false.");
639 if (aOptions
.mAttributeFilter
.WasPassed() && !attributes
) {
641 "If 'attributesFilter' is present, 'attributes' must not be false.");
645 if (aOptions
.mCharacterDataOldValue
.WasPassed() &&
646 aOptions
.mCharacterDataOldValue
.Value() && !characterData
) {
648 "If 'characterDataOldValue' is true, 'characterData' must not be "
653 nsTArray
<RefPtr
<nsAtom
>> filters
;
654 bool allAttrs
= true;
655 if (aOptions
.mAttributeFilter
.WasPassed()) {
657 const Sequence
<nsString
>& filtersAsString
=
658 aOptions
.mAttributeFilter
.Value();
659 uint32_t len
= filtersAsString
.Length();
660 filters
.SetCapacity(len
);
662 for (uint32_t i
= 0; i
< len
; ++i
) {
663 filters
.AppendElement(NS_Atomize(filtersAsString
[i
]));
667 nsMutationReceiver
* r
= GetReceiverFor(&aTarget
, true, animations
);
668 r
->SetChildList(childList
);
669 r
->SetAttributes(attributes
);
670 r
->SetCharacterData(characterData
);
671 r
->SetSubtree(subtree
);
672 r
->SetAttributeOldValue(attributeOldValue
);
673 r
->SetCharacterDataOldValue(characterDataOldValue
);
674 r
->SetAttributeFilter(std::move(filters
));
675 r
->SetAllAttributes(allAttrs
);
676 r
->SetAnimations(animations
);
677 r
->SetChromeOnlyNodes(chromeOnlyNodes
);
680 if (!aSubjectPrincipal
.IsSystemPrincipal() &&
681 !aSubjectPrincipal
.GetIsAddonOrExpandedAddonPrincipal()) {
682 if (nsPIDOMWindowInner
* window
= aTarget
.OwnerDoc()->GetInnerWindow()) {
683 window
->SetMutationObserverHasObservedNodeForTelemetry();
688 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
689 NS_WARNING_ASSERTION(mReceivers
[i
]->Target(),
690 "All the receivers should have a target!");
695 void nsDOMMutationObserver::Disconnect() {
696 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
697 mReceivers
[i
]->Disconnect(false);
700 mCurrentMutations
.Clear();
701 ClearPendingRecords();
704 void nsDOMMutationObserver::TakeRecords(
705 nsTArray
<RefPtr
<nsDOMMutationRecord
>>& aRetVal
) {
707 aRetVal
.SetCapacity(mPendingMutationCount
);
708 RefPtr
<nsDOMMutationRecord
> current
;
709 current
.swap(mFirstPendingMutation
);
710 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
711 RefPtr
<nsDOMMutationRecord
> next
;
712 current
->mNext
.swap(next
);
713 if (!mMergeAttributeRecords
||
714 !MergeableAttributeRecord(aRetVal
.SafeLastElement(nullptr), current
)) {
715 *aRetVal
.AppendElement() = std::move(current
);
719 ClearPendingRecords();
722 void nsDOMMutationObserver::GetObservingInfo(
723 nsTArray
<Nullable
<MutationObservingInfo
>>& aResult
,
724 mozilla::ErrorResult
& aRv
) {
725 aResult
.SetCapacity(mReceivers
.Count());
726 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
727 MutationObservingInfo
& info
= aResult
.AppendElement()->SetValue();
728 nsMutationReceiver
* mr
= mReceivers
[i
];
729 info
.mChildList
= mr
->ChildList();
730 info
.mAttributes
.Construct(mr
->Attributes());
731 info
.mCharacterData
.Construct(mr
->CharacterData());
732 info
.mSubtree
= mr
->Subtree();
733 info
.mAttributeOldValue
.Construct(mr
->AttributeOldValue());
734 info
.mCharacterDataOldValue
.Construct(mr
->CharacterDataOldValue());
735 info
.mAnimations
= mr
->Animations();
736 nsTArray
<RefPtr
<nsAtom
>>& filters
= mr
->AttributeFilter();
737 if (filters
.Length()) {
738 info
.mAttributeFilter
.Construct();
739 Sequence
<nsString
>& filtersAsStrings
= info
.mAttributeFilter
.Value();
741 filtersAsStrings
.AppendElements(filters
.Length(), mozilla::fallible
);
743 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
746 for (size_t j
= 0; j
< filters
.Length(); ++j
) {
747 filters
[j
]->ToString(strings
[j
]);
750 info
.mObservedNode
= mr
->Target();
755 already_AddRefed
<nsDOMMutationObserver
> nsDOMMutationObserver::Constructor(
756 const GlobalObject
& aGlobal
, dom::MutationCallback
& aCb
, ErrorResult
& aRv
) {
757 nsCOMPtr
<nsPIDOMWindowInner
> window
=
758 do_QueryInterface(aGlobal
.GetAsSupports());
760 aRv
.Throw(NS_ERROR_FAILURE
);
763 return MakeAndAddRef
<nsDOMMutationObserver
>(std::move(window
), aCb
);
766 bool nsDOMMutationObserver::MergeableAttributeRecord(
767 nsDOMMutationRecord
* aOldRecord
, nsDOMMutationRecord
* aRecord
) {
768 MOZ_ASSERT(mMergeAttributeRecords
);
769 return aOldRecord
&& aOldRecord
->mType
== nsGkAtoms::attributes
&&
770 aOldRecord
->mType
== aRecord
->mType
&&
771 aOldRecord
->mTarget
== aRecord
->mTarget
&&
772 aOldRecord
->mAttrName
== aRecord
->mAttrName
&&
773 aOldRecord
->mAttrNamespace
.Equals(aRecord
->mAttrNamespace
);
776 void nsDOMMutationObserver::HandleMutation() {
777 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
778 NS_ASSERTION(mCurrentMutations
.IsEmpty(),
779 "Still generating MutationRecords?");
781 mWaitingForRun
= false;
783 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
784 mReceivers
[i
]->RemoveClones();
786 mTransientReceivers
.Clear();
788 nsPIDOMWindowOuter
* outer
= mOwner
->GetOuterWindow();
789 if (!mPendingMutationCount
|| !outer
||
790 outer
->GetCurrentInnerWindow() != mOwner
) {
791 ClearPendingRecords();
795 mozilla::dom::Sequence
<mozilla::OwningNonNull
<nsDOMMutationRecord
>> mutations
;
796 if (mutations
.SetCapacity(mPendingMutationCount
, mozilla::fallible
)) {
797 // We can't use TakeRecords easily here, because it deals with a
798 // different type of array, and we want to optimize out any extra copying.
799 RefPtr
<nsDOMMutationRecord
> current
;
800 current
.swap(mFirstPendingMutation
);
801 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
802 RefPtr
<nsDOMMutationRecord
> next
;
803 current
->mNext
.swap(next
);
804 if (!mMergeAttributeRecords
||
805 !MergeableAttributeRecord(
806 mutations
.Length() ? mutations
.LastElement().get() : nullptr,
808 *mutations
.AppendElement(mozilla::fallible
) = current
;
813 ClearPendingRecords();
815 RefPtr
<dom::MutationCallback
> callback(mCallback
);
816 callback
->Call(this, mutations
, *this);
819 void nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation
& aAso
) {
820 nsTArray
<RefPtr
<nsDOMMutationObserver
>>* suppressedObservers
= nullptr;
822 // This loop implements:
823 // * Let signalList be a copy of unit of related similar-origin browsing
824 // contexts' signal slot list.
825 // * Empty unit of related similar-origin browsing contexts' signal slot
827 nsTArray
<nsTArray
<RefPtr
<HTMLSlotElement
>>> signalLists
;
828 if (DocGroup::sPendingDocGroups
) {
829 signalLists
.SetCapacity(DocGroup::sPendingDocGroups
->Length());
830 for (DocGroup
* docGroup
: *DocGroup::sPendingDocGroups
) {
831 signalLists
.AppendElement(docGroup
->MoveSignalSlotList());
833 delete DocGroup::sPendingDocGroups
;
834 DocGroup::sPendingDocGroups
= nullptr;
837 if (sScheduledMutationObservers
) {
838 AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>* observers
=
839 sScheduledMutationObservers
;
840 sScheduledMutationObservers
= nullptr;
841 for (uint32_t i
= 0; i
< observers
->Length(); ++i
) {
842 RefPtr
<nsDOMMutationObserver
> currentObserver
=
843 static_cast<nsDOMMutationObserver
*>((*observers
)[i
]);
844 if (!currentObserver
->Suppressed()) {
845 currentObserver
->HandleMutation();
847 if (!suppressedObservers
) {
848 suppressedObservers
= new nsTArray
<RefPtr
<nsDOMMutationObserver
>>;
850 if (!suppressedObservers
->Contains(currentObserver
)) {
851 suppressedObservers
->AppendElement(currentObserver
);
856 aAso
.CheckForInterrupt();
859 if (suppressedObservers
) {
860 for (uint32_t i
= 0; i
< suppressedObservers
->Length(); ++i
) {
861 static_cast<nsDOMMutationObserver
*>(suppressedObservers
->ElementAt(i
))
862 ->RescheduleForRun();
864 delete suppressedObservers
;
865 suppressedObservers
= nullptr;
868 // Fire slotchange event for each slot in signalLists.
869 for (const nsTArray
<RefPtr
<HTMLSlotElement
>>& signalList
: signalLists
) {
870 for (const RefPtr
<HTMLSlotElement
>& signal
: signalList
) {
871 signal
->FireSlotChangeEvent();
876 nsDOMMutationRecord
* nsDOMMutationObserver::CurrentRecord(nsAtom
* aType
) {
877 NS_ASSERTION(sMutationLevel
> 0, "Unexpected mutation level!");
879 while (mCurrentMutations
.Length() < sMutationLevel
) {
880 mCurrentMutations
.AppendElement(static_cast<nsDOMMutationRecord
*>(nullptr));
883 uint32_t last
= sMutationLevel
- 1;
884 if (!mCurrentMutations
[last
]) {
885 RefPtr
<nsDOMMutationRecord
> r
=
886 new nsDOMMutationRecord(aType
, GetParentObject());
887 mCurrentMutations
[last
] = r
;
888 AppendMutationRecord(r
.forget());
893 MOZ_ASSERT(sCurrentlyHandlingObservers
->Length() == sMutationLevel
);
894 for (size_t i
= 0; i
< sCurrentlyHandlingObservers
->Length(); ++i
) {
895 MOZ_ASSERT(sCurrentlyHandlingObservers
->ElementAt(i
).Contains(this),
896 "MutationObserver should be added as an observer of all the "
897 "nested mutations!");
901 NS_ASSERTION(mCurrentMutations
[last
]->mType
== aType
,
902 "Unexpected MutationRecord type!");
904 return mCurrentMutations
[last
];
907 nsDOMMutationObserver::~nsDOMMutationObserver() {
908 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
909 mReceivers
[i
]->RemoveClones();
913 void nsDOMMutationObserver::EnterMutationHandling() { ++sMutationLevel
; }
915 // Leave the current mutation level (there can be several levels if in case
916 // of nested calls to the nsIMutationObserver methods).
917 // The most recent mutation record is removed from mCurrentMutations, so
918 // that is doesn't get modified anymore by receivers.
919 void nsDOMMutationObserver::LeaveMutationHandling() {
920 if (sCurrentlyHandlingObservers
&&
921 sCurrentlyHandlingObservers
->Length() == sMutationLevel
) {
922 nsTArray
<RefPtr
<nsDOMMutationObserver
>> obs
=
923 sCurrentlyHandlingObservers
->PopLastElement();
924 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
925 nsDOMMutationObserver
* o
= static_cast<nsDOMMutationObserver
*>(obs
[i
]);
926 if (o
->mCurrentMutations
.Length() == sMutationLevel
) {
927 // It is already in pending mutations.
928 o
->mCurrentMutations
.RemoveLastElement();
935 void nsDOMMutationObserver::AddCurrentlyHandlingObserver(
936 nsDOMMutationObserver
* aObserver
, uint32_t aMutationLevel
) {
937 NS_ASSERTION(aMutationLevel
> 0, "Unexpected mutation level!");
939 if (aMutationLevel
> 1) {
940 // MutationObserver must be in the currently handling observer list
941 // in all the nested levels.
942 AddCurrentlyHandlingObserver(aObserver
, aMutationLevel
- 1);
945 if (!sCurrentlyHandlingObservers
) {
946 sCurrentlyHandlingObservers
=
947 new AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>;
950 while (sCurrentlyHandlingObservers
->Length() < aMutationLevel
) {
951 sCurrentlyHandlingObservers
->InsertElementAt(
952 sCurrentlyHandlingObservers
->Length());
955 uint32_t index
= aMutationLevel
- 1;
956 if (!sCurrentlyHandlingObservers
->ElementAt(index
).Contains(aObserver
)) {
957 sCurrentlyHandlingObservers
->ElementAt(index
).AppendElement(aObserver
);
961 void nsDOMMutationObserver::Shutdown() {
962 delete sCurrentlyHandlingObservers
;
963 sCurrentlyHandlingObservers
= nullptr;
964 delete sScheduledMutationObservers
;
965 sScheduledMutationObservers
= nullptr;
968 nsAutoMutationBatch
* nsAutoMutationBatch::sCurrentBatch
= nullptr;
970 void nsAutoMutationBatch::Done() {
971 if (sCurrentBatch
!= this) {
975 sCurrentBatch
= mPreviousBatch
;
976 if (mObservers
.IsEmpty()) {
977 nsDOMMutationObserver::LeaveMutationHandling();
982 uint32_t len
= mObservers
.Length();
983 for (uint32_t i
= 0; i
< len
; ++i
) {
984 nsDOMMutationObserver
* ob
= mObservers
[i
].mObserver
;
985 bool wantsChildList
= mObservers
[i
].mWantsChildList
;
987 RefPtr
<nsSimpleContentList
> removedList
;
988 if (wantsChildList
) {
989 removedList
= new nsSimpleContentList(mBatchTarget
);
992 nsTArray
<nsMutationReceiver
*> allObservers
;
993 ob
->GetAllSubtreeObserversFor(mBatchTarget
, allObservers
);
995 int32_t j
= mFromFirstToLast
? 0 : mRemovedNodes
.Length() - 1;
996 int32_t end
= mFromFirstToLast
? mRemovedNodes
.Length() : -1;
997 for (; j
!= end
; mFromFirstToLast
? ++j
: --j
) {
998 nsCOMPtr
<nsIContent
> removed
= mRemovedNodes
[j
];
1000 removedList
->AppendElement(removed
);
1003 if (allObservers
.Length()) {
1004 auto* const transientReceivers
=
1005 ob
->mTransientReceivers
.GetOrInsertNew(removed
);
1006 for (uint32_t k
= 0; k
< allObservers
.Length(); ++k
) {
1007 nsMutationReceiver
* r
= allObservers
[k
];
1008 nsMutationReceiver
* orig
= r
->GetParent() ? r
->GetParent() : r
;
1009 if (ob
->GetReceiverFor(removed
, false, false) != orig
) {
1010 // Make sure the elements which are removed from the
1011 // subtree are kept in the same observation set.
1012 nsMutationReceiver
* tr
;
1013 if (orig
->Animations()) {
1014 tr
= nsAnimationReceiver::Create(removed
, orig
);
1016 tr
= nsMutationReceiver::Create(removed
, orig
);
1018 transientReceivers
->AppendObject(tr
);
1023 if (wantsChildList
&& (mRemovedNodes
.Length() || mAddedNodes
.Length())) {
1024 RefPtr
<nsSimpleContentList
> addedList
=
1025 new nsSimpleContentList(mBatchTarget
);
1026 for (uint32_t i
= 0; i
< mAddedNodes
.Length(); ++i
) {
1027 addedList
->AppendElement(mAddedNodes
[i
]);
1029 RefPtr
<nsDOMMutationRecord
> m
=
1030 new nsDOMMutationRecord(nsGkAtoms::childList
, ob
->GetParentObject());
1031 m
->mTarget
= mBatchTarget
;
1032 m
->mRemovedNodes
= removedList
;
1033 m
->mAddedNodes
= addedList
;
1034 m
->mPreviousSibling
= mPrevSibling
;
1035 m
->mNextSibling
= mNextSibling
;
1036 ob
->AppendMutationRecord(m
.forget());
1038 // Always schedule the observer so that transient receivers are
1039 // removed correctly.
1040 ob
->ScheduleForRun();
1042 nsDOMMutationObserver::LeaveMutationHandling();
1045 nsAutoAnimationMutationBatch
* nsAutoAnimationMutationBatch::sCurrentBatch
=
1048 void nsAutoAnimationMutationBatch::Done() {
1049 if (sCurrentBatch
!= this) {
1053 sCurrentBatch
= nullptr;
1054 if (mObservers
.IsEmpty()) {
1055 nsDOMMutationObserver::LeaveMutationHandling();
1060 mBatchTargets
.Sort(TreeOrderComparator());
1062 for (nsDOMMutationObserver
* ob
: mObservers
) {
1063 bool didAddRecords
= false;
1065 for (nsINode
* target
: mBatchTargets
) {
1066 EntryArray
* entries
= mEntryTable
.Get(target
);
1068 "Targets in entry table and targets list should match");
1070 RefPtr
<nsDOMMutationRecord
> m
=
1071 new nsDOMMutationRecord(nsGkAtoms::animations
, ob
->GetParentObject());
1072 m
->mTarget
= target
;
1074 for (const Entry
& e
: *entries
) {
1075 if (e
.mState
== eState_Added
) {
1076 m
->mAddedAnimations
.AppendElement(e
.mAnimation
);
1077 } else if (e
.mState
== eState_Removed
) {
1078 m
->mRemovedAnimations
.AppendElement(e
.mAnimation
);
1079 } else if (e
.mState
== eState_RemainedPresent
&& e
.mChanged
) {
1080 m
->mChangedAnimations
.AppendElement(e
.mAnimation
);
1084 if (!m
->mAddedAnimations
.IsEmpty() || !m
->mChangedAnimations
.IsEmpty() ||
1085 !m
->mRemovedAnimations
.IsEmpty()) {
1086 ob
->AppendMutationRecord(m
.forget());
1087 didAddRecords
= true;
1091 if (didAddRecords
) {
1092 ob
->ScheduleForRun();
1095 nsDOMMutationObserver::LeaveMutationHandling();