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::ContentRemoved(nsIContent
* aChild
,
258 nsIContent
* aPreviousSibling
) {
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
= aPreviousSibling
;
337 m
->mNextSibling
= aPreviousSibling
? aPreviousSibling
->GetNextSibling()
338 : parent
->GetFirstChild();
340 // We need to schedule always, so that after microtask mTransientReceivers
341 // can be cleared correctly.
342 Observer()->ScheduleForRun();
345 void nsMutationReceiver::NodeWillBeDestroyed(nsINode
* aNode
) {
346 NS_ASSERTION(!mParent
, "Shouldn't have mParent here!");
350 void nsAnimationReceiver::RecordAnimationMutation(
351 Animation
* aAnimation
, AnimationMutation aMutationType
) {
352 AnimationEffect
* effect
= aAnimation
->GetEffect();
357 KeyframeEffect
* keyframeEffect
= effect
->AsKeyframeEffect();
358 if (!keyframeEffect
) {
362 NonOwningAnimationTarget animationTarget
=
363 keyframeEffect
->GetAnimationTarget();
364 if (!animationTarget
) {
368 Element
* elem
= animationTarget
.mElement
;
369 if (!Animations() || !(Subtree() || elem
== Target()) ||
370 elem
->ChromeOnlyAccess()) {
374 // Record animations targeting to a pseudo element only when subtree is true.
375 if (animationTarget
.mPseudoType
!= PseudoStyleType::NotPseudo
&& !Subtree()) {
379 if (nsAutoAnimationMutationBatch::IsBatching()) {
380 switch (aMutationType
) {
381 case eAnimationMutation_Added
:
382 nsAutoAnimationMutationBatch::AnimationAdded(aAnimation
, elem
);
384 case eAnimationMutation_Changed
:
385 nsAutoAnimationMutationBatch::AnimationChanged(aAnimation
, elem
);
387 case eAnimationMutation_Removed
:
388 nsAutoAnimationMutationBatch::AnimationRemoved(aAnimation
, elem
);
392 nsAutoAnimationMutationBatch::AddObserver(Observer());
396 nsDOMMutationRecord
* m
= Observer()->CurrentRecord(nsGkAtoms::animations
);
398 NS_ASSERTION(!m
->mTarget
, "Wrong target!");
402 switch (aMutationType
) {
403 case eAnimationMutation_Added
:
404 m
->mAddedAnimations
.AppendElement(aAnimation
);
406 case eAnimationMutation_Changed
:
407 m
->mChangedAnimations
.AppendElement(aAnimation
);
409 case eAnimationMutation_Removed
:
410 m
->mRemovedAnimations
.AppendElement(aAnimation
);
415 void nsAnimationReceiver::AnimationAdded(Animation
* aAnimation
) {
416 RecordAnimationMutation(aAnimation
, eAnimationMutation_Added
);
419 void nsAnimationReceiver::AnimationChanged(Animation
* aAnimation
) {
420 RecordAnimationMutation(aAnimation
, eAnimationMutation_Changed
);
423 void nsAnimationReceiver::AnimationRemoved(Animation
* aAnimation
) {
424 RecordAnimationMutation(aAnimation
, eAnimationMutation_Removed
);
427 NS_IMPL_ISUPPORTS_INHERITED(nsAnimationReceiver
, nsMutationReceiver
,
428 nsIAnimationObserver
)
432 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver
)
433 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
434 NS_INTERFACE_MAP_ENTRY(nsISupports
)
435 NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver
)
438 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver
)
439 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver
)
441 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver
)
443 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver
)
444 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
445 NS_IMPL_CYCLE_COLLECTION_TRACE_END
447 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver
)
448 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
449 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner
)
450 for (int32_t i
= 0; i
< tmp
->mReceivers
.Count(); ++i
) {
451 tmp
->mReceivers
[i
]->Disconnect(false);
453 tmp
->mReceivers
.Clear();
454 tmp
->ClearPendingRecords();
455 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback
)
456 // No need to handle mTransientReceivers
457 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
459 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver
)
460 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner
)
461 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers
)
462 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation
)
463 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback
)
464 // No need to handle mTransientReceivers
465 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
467 nsMutationReceiver
* nsDOMMutationObserver::GetReceiverFor(
468 nsINode
* aNode
, bool aMayCreate
, bool aWantsAnimations
) {
469 MOZ_ASSERT(aMayCreate
|| !aWantsAnimations
,
470 "the value of aWantsAnimations doesn't matter when aMayCreate is "
471 "false, so just pass in false for it");
473 if (!aMayCreate
&& !aNode
->MayHaveDOMMutationObserver()) {
477 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
478 if (mReceivers
[i
]->Target() == aNode
) {
479 return mReceivers
[i
];
486 nsMutationReceiver
* r
;
487 if (aWantsAnimations
) {
488 r
= nsAnimationReceiver::Create(aNode
, this);
490 r
= nsMutationReceiver::Create(aNode
, this);
492 mReceivers
.AppendObject(r
);
496 void nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver
* aReceiver
) {
497 mReceivers
.RemoveObject(aReceiver
);
500 void nsDOMMutationObserver::GetAllSubtreeObserversFor(
501 nsINode
* aNode
, nsTArray
<nsMutationReceiver
*>& aReceivers
) {
504 if (n
->MayHaveDOMMutationObserver()) {
505 nsMutationReceiver
* r
= GetReceiverFor(n
, false, false);
506 if (r
&& r
->Subtree() && !aReceivers
.Contains(r
)) {
507 aReceivers
.AppendElement(r
);
508 // If we've found all the receivers the observer has,
509 // no need to search for more.
510 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
514 nsCOMArray
<nsMutationReceiver
>* transientReceivers
= nullptr;
515 if (mTransientReceivers
.Get(n
, &transientReceivers
) &&
516 transientReceivers
) {
517 for (int32_t i
= 0; i
< transientReceivers
->Count(); ++i
) {
518 nsMutationReceiver
* r
= transientReceivers
->ObjectAt(i
);
519 nsMutationReceiver
* parent
= r
->GetParent();
520 if (r
->Subtree() && parent
&& !aReceivers
.Contains(parent
)) {
521 aReceivers
.AppendElement(parent
);
524 if (mReceivers
.Count() == int32_t(aReceivers
.Length())) {
529 n
= n
->GetParentNode();
533 void nsDOMMutationObserver::ScheduleForRun() {
534 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this, sMutationLevel
);
536 if (mWaitingForRun
) {
539 mWaitingForRun
= true;
543 class MutationObserverMicroTask final
: public MicroTaskRunnable
{
546 virtual void Run(AutoSlowOperation
& aAso
) override
{
547 nsDOMMutationObserver::HandleMutations(aAso
);
550 virtual bool Suppressed() override
{
551 return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed();
556 void nsDOMMutationObserver::QueueMutationObserverMicroTask() {
557 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
562 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
563 ccjs
->DispatchToMicroTask(momt
.forget());
566 void nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation
& aAso
) {
567 if (sScheduledMutationObservers
|| DocGroup::sPendingDocGroups
) {
568 HandleMutationsInternal(aAso
);
572 void nsDOMMutationObserver::RescheduleForRun() {
573 if (!sScheduledMutationObservers
) {
574 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
579 RefPtr
<MutationObserverMicroTask
> momt
= new MutationObserverMicroTask();
580 ccjs
->DispatchToMicroTask(momt
.forget());
581 sScheduledMutationObservers
=
582 new AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>;
585 bool didInsert
= false;
586 for (uint32_t i
= 0; i
< sScheduledMutationObservers
->Length(); ++i
) {
587 if (static_cast<nsDOMMutationObserver
*>((*sScheduledMutationObservers
)[i
])
589 sScheduledMutationObservers
->InsertElementAt(i
, this);
595 sScheduledMutationObservers
->AppendElement(this);
599 void nsDOMMutationObserver::Observe(nsINode
& aTarget
,
600 const MutationObserverInit
& aOptions
,
601 nsIPrincipal
& aSubjectPrincipal
,
603 bool childList
= aOptions
.mChildList
;
605 aOptions
.mAttributes
.WasPassed() && aOptions
.mAttributes
.Value();
607 aOptions
.mCharacterData
.WasPassed() && aOptions
.mCharacterData
.Value();
608 bool subtree
= aOptions
.mSubtree
;
609 bool attributeOldValue
= aOptions
.mAttributeOldValue
.WasPassed() &&
610 aOptions
.mAttributeOldValue
.Value();
611 bool characterDataOldValue
= aOptions
.mCharacterDataOldValue
.WasPassed() &&
612 aOptions
.mCharacterDataOldValue
.Value();
613 bool animations
= aOptions
.mAnimations
;
614 bool chromeOnlyNodes
= aOptions
.mChromeOnlyNodes
;
616 if (!aOptions
.mAttributes
.WasPassed() &&
617 (aOptions
.mAttributeOldValue
.WasPassed() ||
618 aOptions
.mAttributeFilter
.WasPassed())) {
622 if (!aOptions
.mCharacterData
.WasPassed() &&
623 aOptions
.mCharacterDataOldValue
.WasPassed()) {
624 characterData
= true;
627 if (!(childList
|| attributes
|| characterData
|| animations
)) {
629 "One of 'childList', 'attributes', 'characterData' must not be false.");
633 if (aOptions
.mAttributeOldValue
.WasPassed() &&
634 aOptions
.mAttributeOldValue
.Value() && !attributes
) {
636 "If 'attributeOldValue' is true, 'attributes' must not be false.");
640 if (aOptions
.mAttributeFilter
.WasPassed() && !attributes
) {
642 "If 'attributesFilter' is present, 'attributes' must not be false.");
646 if (aOptions
.mCharacterDataOldValue
.WasPassed() &&
647 aOptions
.mCharacterDataOldValue
.Value() && !characterData
) {
649 "If 'characterDataOldValue' is true, 'characterData' must not be "
654 nsTArray
<RefPtr
<nsAtom
>> filters
;
655 bool allAttrs
= true;
656 if (aOptions
.mAttributeFilter
.WasPassed()) {
658 const Sequence
<nsString
>& filtersAsString
=
659 aOptions
.mAttributeFilter
.Value();
660 uint32_t len
= filtersAsString
.Length();
661 filters
.SetCapacity(len
);
663 for (uint32_t i
= 0; i
< len
; ++i
) {
664 filters
.AppendElement(NS_Atomize(filtersAsString
[i
]));
668 nsMutationReceiver
* r
= GetReceiverFor(&aTarget
, true, animations
);
669 r
->SetChildList(childList
);
670 r
->SetAttributes(attributes
);
671 r
->SetCharacterData(characterData
);
672 r
->SetSubtree(subtree
);
673 r
->SetAttributeOldValue(attributeOldValue
);
674 r
->SetCharacterDataOldValue(characterDataOldValue
);
675 r
->SetAttributeFilter(std::move(filters
));
676 r
->SetAllAttributes(allAttrs
);
677 r
->SetAnimations(animations
);
678 r
->SetChromeOnlyNodes(chromeOnlyNodes
);
681 if (!aSubjectPrincipal
.IsSystemPrincipal() &&
682 !aSubjectPrincipal
.GetIsAddonOrExpandedAddonPrincipal()) {
683 if (nsPIDOMWindowInner
* window
= aTarget
.OwnerDoc()->GetInnerWindow()) {
684 window
->SetMutationObserverHasObservedNodeForTelemetry();
689 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
690 NS_WARNING_ASSERTION(mReceivers
[i
]->Target(),
691 "All the receivers should have a target!");
696 void nsDOMMutationObserver::Disconnect() {
697 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
698 mReceivers
[i
]->Disconnect(false);
701 mCurrentMutations
.Clear();
702 ClearPendingRecords();
705 void nsDOMMutationObserver::TakeRecords(
706 nsTArray
<RefPtr
<nsDOMMutationRecord
>>& aRetVal
) {
708 aRetVal
.SetCapacity(mPendingMutationCount
);
709 RefPtr
<nsDOMMutationRecord
> current
;
710 current
.swap(mFirstPendingMutation
);
711 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
712 RefPtr
<nsDOMMutationRecord
> next
;
713 current
->mNext
.swap(next
);
714 if (!mMergeAttributeRecords
||
715 !MergeableAttributeRecord(aRetVal
.SafeLastElement(nullptr), current
)) {
716 *aRetVal
.AppendElement() = std::move(current
);
720 ClearPendingRecords();
723 void nsDOMMutationObserver::GetObservingInfo(
724 nsTArray
<Nullable
<MutationObservingInfo
>>& aResult
,
725 mozilla::ErrorResult
& aRv
) {
726 aResult
.SetCapacity(mReceivers
.Count());
727 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
728 MutationObservingInfo
& info
= aResult
.AppendElement()->SetValue();
729 nsMutationReceiver
* mr
= mReceivers
[i
];
730 info
.mChildList
= mr
->ChildList();
731 info
.mAttributes
.Construct(mr
->Attributes());
732 info
.mCharacterData
.Construct(mr
->CharacterData());
733 info
.mSubtree
= mr
->Subtree();
734 info
.mAttributeOldValue
.Construct(mr
->AttributeOldValue());
735 info
.mCharacterDataOldValue
.Construct(mr
->CharacterDataOldValue());
736 info
.mAnimations
= mr
->Animations();
737 nsTArray
<RefPtr
<nsAtom
>>& filters
= mr
->AttributeFilter();
738 if (filters
.Length()) {
739 info
.mAttributeFilter
.Construct();
740 Sequence
<nsString
>& filtersAsStrings
= info
.mAttributeFilter
.Value();
742 filtersAsStrings
.AppendElements(filters
.Length(), mozilla::fallible
);
744 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
747 for (size_t j
= 0; j
< filters
.Length(); ++j
) {
748 filters
[j
]->ToString(strings
[j
]);
751 info
.mObservedNode
= mr
->Target();
756 already_AddRefed
<nsDOMMutationObserver
> nsDOMMutationObserver::Constructor(
757 const GlobalObject
& aGlobal
, dom::MutationCallback
& aCb
, ErrorResult
& aRv
) {
758 nsCOMPtr
<nsPIDOMWindowInner
> window
=
759 do_QueryInterface(aGlobal
.GetAsSupports());
761 aRv
.Throw(NS_ERROR_FAILURE
);
764 return MakeAndAddRef
<nsDOMMutationObserver
>(std::move(window
), aCb
);
767 bool nsDOMMutationObserver::MergeableAttributeRecord(
768 nsDOMMutationRecord
* aOldRecord
, nsDOMMutationRecord
* aRecord
) {
769 MOZ_ASSERT(mMergeAttributeRecords
);
770 return aOldRecord
&& aOldRecord
->mType
== nsGkAtoms::attributes
&&
771 aOldRecord
->mType
== aRecord
->mType
&&
772 aOldRecord
->mTarget
== aRecord
->mTarget
&&
773 aOldRecord
->mAttrName
== aRecord
->mAttrName
&&
774 aOldRecord
->mAttrNamespace
.Equals(aRecord
->mAttrNamespace
);
777 void nsDOMMutationObserver::HandleMutation() {
778 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
779 NS_ASSERTION(mCurrentMutations
.IsEmpty(),
780 "Still generating MutationRecords?");
782 mWaitingForRun
= false;
784 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
785 mReceivers
[i
]->RemoveClones();
787 mTransientReceivers
.Clear();
789 nsPIDOMWindowOuter
* outer
= mOwner
->GetOuterWindow();
790 if (!mPendingMutationCount
|| !outer
||
791 outer
->GetCurrentInnerWindow() != mOwner
) {
792 ClearPendingRecords();
796 mozilla::dom::Sequence
<mozilla::OwningNonNull
<nsDOMMutationRecord
>> mutations
;
797 if (mutations
.SetCapacity(mPendingMutationCount
, mozilla::fallible
)) {
798 // We can't use TakeRecords easily here, because it deals with a
799 // different type of array, and we want to optimize out any extra copying.
800 RefPtr
<nsDOMMutationRecord
> current
;
801 current
.swap(mFirstPendingMutation
);
802 for (uint32_t i
= 0; i
< mPendingMutationCount
; ++i
) {
803 RefPtr
<nsDOMMutationRecord
> next
;
804 current
->mNext
.swap(next
);
805 if (!mMergeAttributeRecords
||
806 !MergeableAttributeRecord(
807 mutations
.Length() ? mutations
.LastElement().get() : nullptr,
809 *mutations
.AppendElement(mozilla::fallible
) = current
;
814 ClearPendingRecords();
816 RefPtr
<dom::MutationCallback
> callback(mCallback
);
817 callback
->Call(this, mutations
, *this);
820 void nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation
& aAso
) {
821 nsTArray
<RefPtr
<nsDOMMutationObserver
>>* suppressedObservers
= nullptr;
823 // This loop implements:
824 // * Let signalList be a copy of unit of related similar-origin browsing
825 // contexts' signal slot list.
826 // * Empty unit of related similar-origin browsing contexts' signal slot
828 nsTArray
<nsTArray
<RefPtr
<HTMLSlotElement
>>> signalLists
;
829 if (DocGroup::sPendingDocGroups
) {
830 signalLists
.SetCapacity(DocGroup::sPendingDocGroups
->Length());
831 for (DocGroup
* docGroup
: *DocGroup::sPendingDocGroups
) {
832 signalLists
.AppendElement(docGroup
->MoveSignalSlotList());
834 delete DocGroup::sPendingDocGroups
;
835 DocGroup::sPendingDocGroups
= nullptr;
838 if (sScheduledMutationObservers
) {
839 AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>* observers
=
840 sScheduledMutationObservers
;
841 sScheduledMutationObservers
= nullptr;
842 for (uint32_t i
= 0; i
< observers
->Length(); ++i
) {
843 RefPtr
<nsDOMMutationObserver
> currentObserver
=
844 static_cast<nsDOMMutationObserver
*>((*observers
)[i
]);
845 if (!currentObserver
->Suppressed()) {
846 currentObserver
->HandleMutation();
848 if (!suppressedObservers
) {
849 suppressedObservers
= new nsTArray
<RefPtr
<nsDOMMutationObserver
>>;
851 if (!suppressedObservers
->Contains(currentObserver
)) {
852 suppressedObservers
->AppendElement(currentObserver
);
857 aAso
.CheckForInterrupt();
860 if (suppressedObservers
) {
861 for (uint32_t i
= 0; i
< suppressedObservers
->Length(); ++i
) {
862 static_cast<nsDOMMutationObserver
*>(suppressedObservers
->ElementAt(i
))
863 ->RescheduleForRun();
865 delete suppressedObservers
;
866 suppressedObservers
= nullptr;
869 // Fire slotchange event for each slot in signalLists.
870 for (const nsTArray
<RefPtr
<HTMLSlotElement
>>& signalList
: signalLists
) {
871 for (const RefPtr
<HTMLSlotElement
>& signal
: signalList
) {
872 signal
->FireSlotChangeEvent();
877 nsDOMMutationRecord
* nsDOMMutationObserver::CurrentRecord(nsAtom
* aType
) {
878 NS_ASSERTION(sMutationLevel
> 0, "Unexpected mutation level!");
880 while (mCurrentMutations
.Length() < sMutationLevel
) {
881 mCurrentMutations
.AppendElement(static_cast<nsDOMMutationRecord
*>(nullptr));
884 uint32_t last
= sMutationLevel
- 1;
885 if (!mCurrentMutations
[last
]) {
886 RefPtr
<nsDOMMutationRecord
> r
=
887 new nsDOMMutationRecord(aType
, GetParentObject());
888 mCurrentMutations
[last
] = r
;
889 AppendMutationRecord(r
.forget());
894 MOZ_ASSERT(sCurrentlyHandlingObservers
->Length() == sMutationLevel
);
895 for (size_t i
= 0; i
< sCurrentlyHandlingObservers
->Length(); ++i
) {
896 MOZ_ASSERT(sCurrentlyHandlingObservers
->ElementAt(i
).Contains(this),
897 "MutationObserver should be added as an observer of all the "
898 "nested mutations!");
902 NS_ASSERTION(mCurrentMutations
[last
]->mType
== aType
,
903 "Unexpected MutationRecord type!");
905 return mCurrentMutations
[last
];
908 nsDOMMutationObserver::~nsDOMMutationObserver() {
909 for (int32_t i
= 0; i
< mReceivers
.Count(); ++i
) {
910 mReceivers
[i
]->RemoveClones();
914 void nsDOMMutationObserver::EnterMutationHandling() { ++sMutationLevel
; }
916 // Leave the current mutation level (there can be several levels if in case
917 // of nested calls to the nsIMutationObserver methods).
918 // The most recent mutation record is removed from mCurrentMutations, so
919 // that is doesn't get modified anymore by receivers.
920 void nsDOMMutationObserver::LeaveMutationHandling() {
921 if (sCurrentlyHandlingObservers
&&
922 sCurrentlyHandlingObservers
->Length() == sMutationLevel
) {
923 nsTArray
<RefPtr
<nsDOMMutationObserver
>> obs
=
924 sCurrentlyHandlingObservers
->PopLastElement();
925 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
926 nsDOMMutationObserver
* o
= static_cast<nsDOMMutationObserver
*>(obs
[i
]);
927 if (o
->mCurrentMutations
.Length() == sMutationLevel
) {
928 // It is already in pending mutations.
929 o
->mCurrentMutations
.RemoveLastElement();
936 void nsDOMMutationObserver::AddCurrentlyHandlingObserver(
937 nsDOMMutationObserver
* aObserver
, uint32_t aMutationLevel
) {
938 NS_ASSERTION(aMutationLevel
> 0, "Unexpected mutation level!");
940 if (aMutationLevel
> 1) {
941 // MutationObserver must be in the currently handling observer list
942 // in all the nested levels.
943 AddCurrentlyHandlingObserver(aObserver
, aMutationLevel
- 1);
946 if (!sCurrentlyHandlingObservers
) {
947 sCurrentlyHandlingObservers
=
948 new AutoTArray
<AutoTArray
<RefPtr
<nsDOMMutationObserver
>, 4>, 4>;
951 while (sCurrentlyHandlingObservers
->Length() < aMutationLevel
) {
952 sCurrentlyHandlingObservers
->InsertElementAt(
953 sCurrentlyHandlingObservers
->Length());
956 uint32_t index
= aMutationLevel
- 1;
957 if (!sCurrentlyHandlingObservers
->ElementAt(index
).Contains(aObserver
)) {
958 sCurrentlyHandlingObservers
->ElementAt(index
).AppendElement(aObserver
);
962 void nsDOMMutationObserver::Shutdown() {
963 delete sCurrentlyHandlingObservers
;
964 sCurrentlyHandlingObservers
= nullptr;
965 delete sScheduledMutationObservers
;
966 sScheduledMutationObservers
= nullptr;
969 nsAutoMutationBatch
* nsAutoMutationBatch::sCurrentBatch
= nullptr;
971 void nsAutoMutationBatch::Done() {
972 if (sCurrentBatch
!= this) {
976 sCurrentBatch
= mPreviousBatch
;
977 if (mObservers
.IsEmpty()) {
978 nsDOMMutationObserver::LeaveMutationHandling();
983 uint32_t len
= mObservers
.Length();
984 for (uint32_t i
= 0; i
< len
; ++i
) {
985 nsDOMMutationObserver
* ob
= mObservers
[i
].mObserver
;
986 bool wantsChildList
= mObservers
[i
].mWantsChildList
;
988 RefPtr
<nsSimpleContentList
> removedList
;
989 if (wantsChildList
) {
990 removedList
= new nsSimpleContentList(mBatchTarget
);
993 nsTArray
<nsMutationReceiver
*> allObservers
;
994 ob
->GetAllSubtreeObserversFor(mBatchTarget
, allObservers
);
996 int32_t j
= mFromFirstToLast
? 0 : mRemovedNodes
.Length() - 1;
997 int32_t end
= mFromFirstToLast
? mRemovedNodes
.Length() : -1;
998 for (; j
!= end
; mFromFirstToLast
? ++j
: --j
) {
999 nsCOMPtr
<nsIContent
> removed
= mRemovedNodes
[j
];
1001 removedList
->AppendElement(removed
);
1004 if (allObservers
.Length()) {
1005 auto* const transientReceivers
=
1006 ob
->mTransientReceivers
.GetOrInsertNew(removed
);
1007 for (uint32_t k
= 0; k
< allObservers
.Length(); ++k
) {
1008 nsMutationReceiver
* r
= allObservers
[k
];
1009 nsMutationReceiver
* orig
= r
->GetParent() ? r
->GetParent() : r
;
1010 if (ob
->GetReceiverFor(removed
, false, false) != orig
) {
1011 // Make sure the elements which are removed from the
1012 // subtree are kept in the same observation set.
1013 nsMutationReceiver
* tr
;
1014 if (orig
->Animations()) {
1015 tr
= nsAnimationReceiver::Create(removed
, orig
);
1017 tr
= nsMutationReceiver::Create(removed
, orig
);
1019 transientReceivers
->AppendObject(tr
);
1024 if (wantsChildList
&& (mRemovedNodes
.Length() || mAddedNodes
.Length())) {
1025 RefPtr
<nsSimpleContentList
> addedList
=
1026 new nsSimpleContentList(mBatchTarget
);
1027 for (uint32_t i
= 0; i
< mAddedNodes
.Length(); ++i
) {
1028 addedList
->AppendElement(mAddedNodes
[i
]);
1030 RefPtr
<nsDOMMutationRecord
> m
=
1031 new nsDOMMutationRecord(nsGkAtoms::childList
, ob
->GetParentObject());
1032 m
->mTarget
= mBatchTarget
;
1033 m
->mRemovedNodes
= removedList
;
1034 m
->mAddedNodes
= addedList
;
1035 m
->mPreviousSibling
= mPrevSibling
;
1036 m
->mNextSibling
= mNextSibling
;
1037 ob
->AppendMutationRecord(m
.forget());
1039 // Always schedule the observer so that transient receivers are
1040 // removed correctly.
1041 ob
->ScheduleForRun();
1043 nsDOMMutationObserver::LeaveMutationHandling();
1046 nsAutoAnimationMutationBatch
* nsAutoAnimationMutationBatch::sCurrentBatch
=
1049 void nsAutoAnimationMutationBatch::Done() {
1050 if (sCurrentBatch
!= this) {
1054 sCurrentBatch
= nullptr;
1055 if (mObservers
.IsEmpty()) {
1056 nsDOMMutationObserver::LeaveMutationHandling();
1061 mBatchTargets
.Sort(TreeOrderComparator());
1063 for (nsDOMMutationObserver
* ob
: mObservers
) {
1064 bool didAddRecords
= false;
1066 for (nsINode
* target
: mBatchTargets
) {
1067 EntryArray
* entries
= mEntryTable
.Get(target
);
1069 "Targets in entry table and targets list should match");
1071 RefPtr
<nsDOMMutationRecord
> m
=
1072 new nsDOMMutationRecord(nsGkAtoms::animations
, ob
->GetParentObject());
1073 m
->mTarget
= target
;
1075 for (const Entry
& e
: *entries
) {
1076 if (e
.mState
== eState_Added
) {
1077 m
->mAddedAnimations
.AppendElement(e
.mAnimation
);
1078 } else if (e
.mState
== eState_Removed
) {
1079 m
->mRemovedAnimations
.AppendElement(e
.mAnimation
);
1080 } else if (e
.mState
== eState_RemainedPresent
&& e
.mChanged
) {
1081 m
->mChangedAnimations
.AppendElement(e
.mAnimation
);
1085 if (!m
->mAddedAnimations
.IsEmpty() || !m
->mChangedAnimations
.IsEmpty() ||
1086 !m
->mRemovedAnimations
.IsEmpty()) {
1087 ob
->AppendMutationRecord(m
.forget());
1088 didAddRecords
= true;
1092 if (didAddRecords
) {
1093 ob
->ScheduleForRun();
1096 nsDOMMutationObserver::LeaveMutationHandling();