Line data Source code
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/. */
6 :
7 : #include "nsDOMMutationObserver.h"
8 :
9 : #include "mozilla/AnimationTarget.h"
10 : #include "mozilla/Maybe.h"
11 : #include "mozilla/OwningNonNull.h"
12 :
13 : #include "mozilla/dom/Animation.h"
14 : #include "mozilla/dom/KeyframeEffectReadOnly.h"
15 :
16 : #include "nsContentUtils.h"
17 : #include "nsCSSPseudoElements.h"
18 : #include "nsError.h"
19 : #include "nsIDOMMutationEvent.h"
20 : #include "nsIScriptGlobalObject.h"
21 : #include "nsServiceManagerUtils.h"
22 : #include "nsTextFragment.h"
23 : #include "nsThreadUtils.h"
24 :
25 : using mozilla::Maybe;
26 : using mozilla::Move;
27 : using mozilla::NonOwningAnimationTarget;
28 : using mozilla::dom::TreeOrderComparator;
29 : using mozilla::dom::Animation;
30 : using mozilla::dom::Element;
31 :
32 : AutoTArray<RefPtr<nsDOMMutationObserver>, 4>*
33 : nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
34 :
35 : nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
36 :
37 : uint32_t nsDOMMutationObserver::sMutationLevel = 0;
38 : uint64_t nsDOMMutationObserver::sCount = 0;
39 :
40 : AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>*
41 : nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr;
42 :
43 : nsINodeList*
44 0 : nsDOMMutationRecord::AddedNodes()
45 : {
46 0 : if (!mAddedNodes) {
47 0 : mAddedNodes = new nsSimpleContentList(mTarget);
48 : }
49 0 : return mAddedNodes;
50 : }
51 :
52 : nsINodeList*
53 0 : nsDOMMutationRecord::RemovedNodes()
54 : {
55 0 : if (!mRemovedNodes) {
56 0 : mRemovedNodes = new nsSimpleContentList(mTarget);
57 : }
58 0 : return mRemovedNodes;
59 : }
60 :
61 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord)
62 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
63 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
64 0 : NS_INTERFACE_MAP_END
65 :
66 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
67 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
68 :
69 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord,
70 : mTarget,
71 : mPreviousSibling, mNextSibling,
72 : mAddedNodes, mRemovedNodes,
73 : mAddedAnimations, mRemovedAnimations,
74 : mChangedAnimations,
75 : mNext, mOwner)
76 :
77 : // Observer
78 :
79 : bool
80 0 : nsMutationReceiverBase::IsObservable(nsIContent* aContent)
81 : {
82 0 : return !aContent->ChromeOnlyAccess() &&
83 0 : (Observer()->IsChrome() || !aContent->IsInAnonymousSubtree());
84 : }
85 :
86 1 : NS_IMPL_ADDREF(nsMutationReceiver)
87 0 : NS_IMPL_RELEASE(nsMutationReceiver)
88 :
89 0 : NS_INTERFACE_MAP_BEGIN(nsMutationReceiver)
90 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
91 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
92 0 : NS_INTERFACE_MAP_END
93 :
94 1 : nsMutationReceiver::nsMutationReceiver(nsINode* aTarget,
95 1 : nsDOMMutationObserver* aObserver)
96 1 : : nsMutationReceiverBase(aTarget, aObserver)
97 : {
98 1 : mTarget->BindObject(aObserver);
99 1 : }
100 :
101 : void
102 0 : nsMutationReceiver::Disconnect(bool aRemoveFromObserver)
103 : {
104 0 : if (mRegisterTarget) {
105 0 : mRegisterTarget->RemoveMutationObserver(this);
106 0 : mRegisterTarget = nullptr;
107 : }
108 :
109 0 : mParent = nullptr;
110 0 : nsINode* target = mTarget;
111 0 : mTarget = nullptr;
112 0 : nsDOMMutationObserver* observer = mObserver;
113 0 : mObserver = nullptr;
114 0 : RemoveClones();
115 :
116 0 : if (target && observer) {
117 0 : if (aRemoveFromObserver) {
118 0 : static_cast<nsDOMMutationObserver*>(observer)->RemoveReceiver(this);
119 : }
120 : // UnbindObject may delete 'this'!
121 0 : target->UnbindObject(observer);
122 : }
123 0 : }
124 :
125 : void
126 0 : nsMutationReceiver::NativeAnonymousChildListChange(nsIDocument* aDocument,
127 : nsIContent* aContent,
128 : bool aIsRemove) {
129 0 : if (!NativeAnonymousChildList()) {
130 0 : return;
131 : }
132 :
133 0 : nsINode* parent = aContent->GetParentNode();
134 0 : if (!parent ||
135 0 : (!Subtree() && Target() != parent) ||
136 0 : (Subtree() && RegisterTarget()->SubtreeRoot() != parent->SubtreeRoot())) {
137 0 : return;
138 : }
139 :
140 : nsDOMMutationRecord* m =
141 0 : Observer()->CurrentRecord(nsGkAtoms::nativeAnonymousChildList);
142 :
143 0 : if (m->mTarget) {
144 0 : return;
145 : }
146 0 : m->mTarget = parent;
147 :
148 0 : if (aIsRemove) {
149 0 : m->mRemovedNodes = new nsSimpleContentList(parent);
150 0 : m->mRemovedNodes->AppendElement(aContent);
151 : } else {
152 0 : m->mAddedNodes = new nsSimpleContentList(parent);
153 0 : m->mAddedNodes->AppendElement(aContent);
154 : }
155 : }
156 :
157 : void
158 0 : nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
159 : mozilla::dom::Element* aElement,
160 : int32_t aNameSpaceID,
161 : nsIAtom* aAttribute,
162 : int32_t aModType,
163 : const nsAttrValue* aNewValue)
164 : {
165 0 : if (nsAutoMutationBatch::IsBatching() ||
166 0 : !ObservesAttr(RegisterTarget(), aElement, aNameSpaceID, aAttribute)) {
167 0 : return;
168 : }
169 :
170 : nsDOMMutationRecord* m =
171 0 : Observer()->CurrentRecord(nsGkAtoms::attributes);
172 :
173 0 : NS_ASSERTION(!m->mTarget || m->mTarget == aElement,
174 : "Wrong target!");
175 0 : NS_ASSERTION(!m->mAttrName || m->mAttrName == aAttribute,
176 : "Wrong attribute!");
177 0 : if (!m->mTarget) {
178 0 : m->mTarget = aElement;
179 0 : m->mAttrName = aAttribute;
180 0 : if (aNameSpaceID == kNameSpaceID_None) {
181 0 : m->mAttrNamespace.SetIsVoid(true);
182 : } else {
183 0 : nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID,
184 0 : m->mAttrNamespace);
185 : }
186 : }
187 :
188 0 : if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
189 0 : if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
190 0 : m->mPrevValue.SetIsVoid(true);
191 : }
192 : }
193 : }
194 :
195 : void
196 0 : nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
197 : nsIContent* aContent,
198 : CharacterDataChangeInfo* aInfo)
199 : {
200 0 : if (nsAutoMutationBatch::IsBatching() ||
201 0 : !CharacterData() ||
202 0 : (!Subtree() && aContent != Target()) ||
203 0 : (Subtree() && RegisterTarget()->SubtreeRoot() != aContent->SubtreeRoot()) ||
204 0 : !IsObservable(aContent)) {
205 0 : return;
206 : }
207 :
208 : nsDOMMutationRecord* m =
209 0 : Observer()->CurrentRecord(nsGkAtoms::characterData);
210 :
211 0 : NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
212 : "Wrong target!");
213 :
214 0 : if (!m->mTarget) {
215 0 : m->mTarget = aContent;
216 : }
217 0 : if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) {
218 0 : aContent->GetText()->AppendTo(m->mPrevValue);
219 : }
220 : }
221 :
222 : void
223 0 : nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
224 : nsIContent* aContainer,
225 : nsIContent* aFirstNewContent,
226 : int32_t aNewIndexInContainer)
227 : {
228 0 : nsINode* parent = NODE_FROM(aContainer, aDocument);
229 : bool wantsChildList =
230 0 : ChildList() &&
231 0 : ((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
232 0 : parent == Target());
233 0 : if (!wantsChildList || !IsObservable(aFirstNewContent)) {
234 0 : return;
235 : }
236 :
237 0 : if (nsAutoMutationBatch::IsBatching()) {
238 0 : if (parent == nsAutoMutationBatch::GetBatchTarget()) {
239 0 : nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
240 : }
241 0 : return;
242 : }
243 :
244 : nsDOMMutationRecord* m =
245 0 : Observer()->CurrentRecord(nsGkAtoms::childList);
246 0 : NS_ASSERTION(!m->mTarget || m->mTarget == parent,
247 : "Wrong target!");
248 0 : if (m->mTarget) {
249 : // Already handled case.
250 0 : return;
251 : }
252 0 : m->mTarget = parent;
253 0 : m->mAddedNodes = new nsSimpleContentList(parent);
254 :
255 0 : nsINode* n = aFirstNewContent;
256 0 : while (n) {
257 0 : m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n));
258 0 : n = n->GetNextSibling();
259 : }
260 0 : m->mPreviousSibling = aFirstNewContent->GetPreviousSibling();
261 : }
262 :
263 : void
264 0 : nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
265 : nsIContent* aContainer,
266 : nsIContent* aChild,
267 : int32_t aIndexInContainer)
268 : {
269 0 : nsINode* parent = NODE_FROM(aContainer, aDocument);
270 : bool wantsChildList =
271 0 : ChildList() &&
272 0 : ((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
273 0 : parent == Target());
274 0 : if (!wantsChildList || !IsObservable(aChild)) {
275 0 : return;
276 : }
277 :
278 0 : if (nsAutoMutationBatch::IsBatching()) {
279 0 : if (parent == nsAutoMutationBatch::GetBatchTarget()) {
280 0 : nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
281 : }
282 0 : return;
283 : }
284 :
285 : nsDOMMutationRecord* m =
286 0 : Observer()->CurrentRecord(nsGkAtoms::childList);
287 0 : if (m->mTarget) {
288 : // Already handled case.
289 0 : return;
290 : }
291 0 : m->mTarget = parent;
292 0 : m->mAddedNodes = new nsSimpleContentList(parent);
293 0 : m->mAddedNodes->AppendElement(aChild);
294 0 : m->mPreviousSibling = aChild->GetPreviousSibling();
295 0 : m->mNextSibling = aChild->GetNextSibling();
296 : }
297 :
298 : void
299 0 : nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
300 : nsIContent* aContainer,
301 : nsIContent* aChild,
302 : int32_t aIndexInContainer,
303 : nsIContent* aPreviousSibling)
304 : {
305 0 : if (!IsObservable(aChild)) {
306 0 : return;
307 : }
308 :
309 0 : nsINode* parent = NODE_FROM(aContainer, aDocument);
310 0 : if (Subtree() && parent->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
311 0 : return;
312 : }
313 0 : if (nsAutoMutationBatch::IsBatching()) {
314 0 : if (nsAutoMutationBatch::IsRemovalDone()) {
315 : // This can happen for example if HTML parser parses to
316 : // context node, but needs to move elements around.
317 0 : return;
318 : }
319 0 : if (nsAutoMutationBatch::GetBatchTarget() != parent) {
320 0 : return;
321 : }
322 :
323 0 : bool wantsChildList = ChildList() && (Subtree() || parent == Target());
324 0 : if (wantsChildList || Subtree()) {
325 0 : nsAutoMutationBatch::NodeRemoved(aChild);
326 0 : nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
327 : }
328 :
329 0 : return;
330 : }
331 :
332 0 : if (Subtree()) {
333 : // Try to avoid creating transient observer if the node
334 : // already has an observer observing the same set of nodes.
335 0 : nsMutationReceiver* orig = GetParent() ? GetParent() : this;
336 0 : if (Observer()->GetReceiverFor(aChild, false, false) != orig) {
337 0 : bool transientExists = false;
338 0 : bool isNewEntry = false;
339 : nsCOMArray<nsMutationReceiver>* transientReceivers =
340 0 : Observer()->mTransientReceivers.LookupForAdd(aChild).OrInsert(
341 0 : [&isNewEntry] () {
342 0 : isNewEntry = true;
343 0 : return new nsCOMArray<nsMutationReceiver>();
344 0 : });
345 0 : if (!isNewEntry) {
346 0 : for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
347 0 : nsMutationReceiver* r = transientReceivers->ObjectAt(i);
348 0 : if (r->GetParent() == orig) {
349 0 : transientExists = true;
350 : }
351 : }
352 : }
353 0 : if (!transientExists) {
354 : // Make sure the elements which are removed from the
355 : // subtree are kept in the same observation set.
356 : nsMutationReceiver* tr;
357 0 : if (orig->Animations()) {
358 0 : tr = nsAnimationReceiver::Create(aChild, orig);
359 : } else {
360 0 : tr = nsMutationReceiver::Create(aChild, orig);
361 : }
362 0 : transientReceivers->AppendObject(tr);
363 : }
364 : }
365 : }
366 :
367 0 : if (ChildList() && (Subtree() || parent == Target())) {
368 : nsDOMMutationRecord* m =
369 0 : Observer()->CurrentRecord(nsGkAtoms::childList);
370 0 : if (m->mTarget) {
371 : // Already handled case.
372 0 : return;
373 : }
374 0 : m->mTarget = parent;
375 0 : m->mRemovedNodes = new nsSimpleContentList(parent);
376 0 : m->mRemovedNodes->AppendElement(aChild);
377 0 : m->mPreviousSibling = aPreviousSibling;
378 0 : m->mNextSibling = parent->GetChildAt(aIndexInContainer);
379 : }
380 : // We need to schedule always, so that after microtask mTransientReceivers
381 : // can be cleared correctly.
382 0 : Observer()->ScheduleForRun();
383 : }
384 :
385 0 : void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode)
386 : {
387 0 : NS_ASSERTION(!mParent, "Shouldn't have mParent here!");
388 0 : Disconnect(true);
389 0 : }
390 :
391 : void
392 0 : nsAnimationReceiver::RecordAnimationMutation(Animation* aAnimation,
393 : AnimationMutation aMutationType)
394 : {
395 0 : mozilla::dom::AnimationEffectReadOnly* effect = aAnimation->GetEffect();
396 0 : if (!effect) {
397 0 : return;
398 : }
399 :
400 : mozilla::dom::KeyframeEffectReadOnly* keyframeEffect =
401 0 : effect->AsKeyframeEffect();
402 0 : if (!keyframeEffect) {
403 0 : return;
404 : }
405 :
406 0 : Maybe<NonOwningAnimationTarget> animationTarget = keyframeEffect->GetTarget();
407 0 : if (!animationTarget) {
408 0 : return;
409 : }
410 :
411 0 : Element* elem = animationTarget->mElement;
412 0 : if (!Animations() || !(Subtree() || elem == Target()) ||
413 0 : elem->ChromeOnlyAccess()) {
414 0 : return;
415 : }
416 :
417 : // Record animations targeting to a pseudo element only when subtree is true.
418 0 : if (animationTarget->mPseudoType != mozilla::CSSPseudoElementType::NotPseudo &&
419 0 : !Subtree()) {
420 0 : return;
421 : }
422 :
423 0 : if (nsAutoAnimationMutationBatch::IsBatching()) {
424 0 : switch (aMutationType) {
425 : case eAnimationMutation_Added:
426 0 : nsAutoAnimationMutationBatch::AnimationAdded(aAnimation, elem);
427 0 : break;
428 : case eAnimationMutation_Changed:
429 0 : nsAutoAnimationMutationBatch::AnimationChanged(aAnimation, elem);
430 0 : break;
431 : case eAnimationMutation_Removed:
432 0 : nsAutoAnimationMutationBatch::AnimationRemoved(aAnimation, elem);
433 0 : break;
434 : }
435 :
436 0 : nsAutoAnimationMutationBatch::AddObserver(Observer());
437 0 : return;
438 : }
439 :
440 : nsDOMMutationRecord* m =
441 0 : Observer()->CurrentRecord(nsGkAtoms::animations);
442 :
443 0 : NS_ASSERTION(!m->mTarget, "Wrong target!");
444 :
445 0 : m->mTarget = elem;
446 :
447 0 : switch (aMutationType) {
448 : case eAnimationMutation_Added:
449 0 : m->mAddedAnimations.AppendElement(aAnimation);
450 0 : break;
451 : case eAnimationMutation_Changed:
452 0 : m->mChangedAnimations.AppendElement(aAnimation);
453 0 : break;
454 : case eAnimationMutation_Removed:
455 0 : m->mRemovedAnimations.AppendElement(aAnimation);
456 0 : break;
457 : }
458 : }
459 :
460 : void
461 0 : nsAnimationReceiver::AnimationAdded(Animation* aAnimation)
462 : {
463 0 : RecordAnimationMutation(aAnimation, eAnimationMutation_Added);
464 0 : }
465 :
466 : void
467 0 : nsAnimationReceiver::AnimationChanged(Animation* aAnimation)
468 : {
469 0 : RecordAnimationMutation(aAnimation, eAnimationMutation_Changed);
470 0 : }
471 :
472 : void
473 0 : nsAnimationReceiver::AnimationRemoved(Animation* aAnimation)
474 : {
475 0 : RecordAnimationMutation(aAnimation, eAnimationMutation_Removed);
476 0 : }
477 :
478 0 : NS_IMPL_ISUPPORTS_INHERITED(nsAnimationReceiver, nsMutationReceiver,
479 : nsIAnimationObserver)
480 :
481 : // Observer
482 :
483 10 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver)
484 1 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
485 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
486 0 : NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver)
487 0 : NS_INTERFACE_MAP_END
488 :
489 3 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver)
490 1 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
491 :
492 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver)
493 :
494 2 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver)
495 2 : NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
496 2 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
497 :
498 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver)
499 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
500 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
501 0 : for (int32_t i = 0; i < tmp->mReceivers.Count(); ++i) {
502 0 : tmp->mReceivers[i]->Disconnect(false);
503 : }
504 0 : tmp->mReceivers.Clear();
505 0 : tmp->ClearPendingRecords();
506 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
507 : // No need to handle mTransientReceivers
508 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
509 :
510 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver)
511 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
512 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers)
513 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation)
514 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
515 : // No need to handle mTransientReceivers
516 1 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
517 :
518 : nsMutationReceiver*
519 1 : nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate,
520 : bool aWantsAnimations)
521 : {
522 1 : MOZ_ASSERT(aMayCreate || !aWantsAnimations,
523 : "the value of aWantsAnimations doesn't matter when aMayCreate is "
524 : "false, so just pass in false for it");
525 :
526 1 : if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
527 0 : return nullptr;
528 : }
529 :
530 1 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
531 0 : if (mReceivers[i]->Target() == aNode) {
532 0 : return mReceivers[i];
533 : }
534 : }
535 1 : if (!aMayCreate) {
536 0 : return nullptr;
537 : }
538 :
539 : nsMutationReceiver* r;
540 1 : if (aWantsAnimations) {
541 0 : r = nsAnimationReceiver::Create(aNode, this);
542 : } else {
543 1 : r = nsMutationReceiver::Create(aNode, this);
544 : }
545 1 : mReceivers.AppendObject(r);
546 1 : return r;
547 : }
548 :
549 : void
550 0 : nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver)
551 : {
552 0 : mReceivers.RemoveObject(aReceiver);
553 0 : }
554 :
555 : void
556 0 : nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode,
557 : nsTArray<nsMutationReceiver*>&
558 : aReceivers)
559 : {
560 0 : nsINode* n = aNode;
561 0 : while (n) {
562 0 : if (n->MayHaveDOMMutationObserver()) {
563 0 : nsMutationReceiver* r = GetReceiverFor(n, false, false);
564 0 : if (r && r->Subtree() && !aReceivers.Contains(r)) {
565 0 : aReceivers.AppendElement(r);
566 : // If we've found all the receivers the observer has,
567 : // no need to search for more.
568 0 : if (mReceivers.Count() == int32_t(aReceivers.Length())) {
569 0 : return;
570 : }
571 : }
572 0 : nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
573 0 : if (mTransientReceivers.Get(n, &transientReceivers) && transientReceivers) {
574 0 : for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
575 0 : nsMutationReceiver* r = transientReceivers->ObjectAt(i);
576 0 : nsMutationReceiver* parent = r->GetParent();
577 0 : if (r->Subtree() && parent && !aReceivers.Contains(parent)) {
578 0 : aReceivers.AppendElement(parent);
579 : }
580 : }
581 0 : if (mReceivers.Count() == int32_t(aReceivers.Length())) {
582 0 : return;
583 : }
584 : }
585 : }
586 0 : n = n->GetParentNode();
587 : }
588 : }
589 :
590 : void
591 0 : nsDOMMutationObserver::ScheduleForRun()
592 : {
593 0 : nsDOMMutationObserver::AddCurrentlyHandlingObserver(this, sMutationLevel);
594 :
595 0 : if (mWaitingForRun) {
596 0 : return;
597 : }
598 0 : mWaitingForRun = true;
599 0 : RescheduleForRun();
600 : }
601 :
602 : void
603 0 : nsDOMMutationObserver::RescheduleForRun()
604 : {
605 0 : if (!sScheduledMutationObservers) {
606 0 : sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
607 : }
608 :
609 0 : bool didInsert = false;
610 0 : for (uint32_t i = 0; i < sScheduledMutationObservers->Length(); ++i) {
611 0 : if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i])
612 0 : ->mId > mId) {
613 0 : sScheduledMutationObservers->InsertElementAt(i, this);
614 0 : didInsert = true;
615 0 : break;
616 : }
617 : }
618 0 : if (!didInsert) {
619 0 : sScheduledMutationObservers->AppendElement(this);
620 : }
621 0 : }
622 :
623 : void
624 1 : nsDOMMutationObserver::Observe(nsINode& aTarget,
625 : const mozilla::dom::MutationObserverInit& aOptions,
626 : mozilla::ErrorResult& aRv)
627 : {
628 :
629 1 : bool childList = aOptions.mChildList;
630 : bool attributes =
631 1 : aOptions.mAttributes.WasPassed() &&
632 1 : aOptions.mAttributes.Value();
633 : bool characterData =
634 1 : aOptions.mCharacterData.WasPassed() &&
635 1 : aOptions.mCharacterData.Value();
636 1 : bool subtree = aOptions.mSubtree;
637 : bool attributeOldValue =
638 1 : aOptions.mAttributeOldValue.WasPassed() &&
639 1 : aOptions.mAttributeOldValue.Value();
640 1 : bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList;
641 : bool characterDataOldValue =
642 1 : aOptions.mCharacterDataOldValue.WasPassed() &&
643 1 : aOptions.mCharacterDataOldValue.Value();
644 1 : bool animations = aOptions.mAnimations;
645 :
646 2 : if (!aOptions.mAttributes.WasPassed() &&
647 2 : (aOptions.mAttributeOldValue.WasPassed() ||
648 1 : aOptions.mAttributeFilter.WasPassed())) {
649 0 : attributes = true;
650 : }
651 :
652 2 : if (!aOptions.mCharacterData.WasPassed() &&
653 1 : aOptions.mCharacterDataOldValue.WasPassed()) {
654 0 : characterData = true;
655 : }
656 :
657 1 : if (!(childList || attributes || characterData || animations ||
658 0 : nativeAnonymousChildList)) {
659 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
660 0 : return;
661 : }
662 :
663 2 : if (aOptions.mAttributeOldValue.WasPassed() &&
664 0 : aOptions.mAttributeOldValue.Value() &&
665 1 : aOptions.mAttributes.WasPassed() &&
666 0 : !aOptions.mAttributes.Value()) {
667 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
668 0 : return;
669 : }
670 :
671 2 : if (aOptions.mAttributeFilter.WasPassed() &&
672 1 : aOptions.mAttributes.WasPassed() &&
673 0 : !aOptions.mAttributes.Value()) {
674 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
675 0 : return;
676 : }
677 :
678 2 : if (aOptions.mCharacterDataOldValue.WasPassed() &&
679 0 : aOptions.mCharacterDataOldValue.Value() &&
680 1 : aOptions.mCharacterData.WasPassed() &&
681 0 : !aOptions.mCharacterData.Value()) {
682 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
683 0 : return;
684 : }
685 :
686 2 : nsCOMArray<nsIAtom> filters;
687 1 : bool allAttrs = true;
688 1 : if (aOptions.mAttributeFilter.WasPassed()) {
689 0 : allAttrs = false;
690 : const mozilla::dom::Sequence<nsString>& filtersAsString =
691 0 : aOptions.mAttributeFilter.Value();
692 0 : uint32_t len = filtersAsString.Length();
693 0 : filters.SetCapacity(len);
694 :
695 0 : for (uint32_t i = 0; i < len; ++i) {
696 0 : filters.AppendElement(NS_Atomize(filtersAsString[i]));
697 : }
698 : }
699 :
700 1 : nsMutationReceiver* r = GetReceiverFor(&aTarget, true, animations);
701 1 : r->SetChildList(childList);
702 1 : r->SetAttributes(attributes);
703 1 : r->SetCharacterData(characterData);
704 1 : r->SetSubtree(subtree);
705 1 : r->SetAttributeOldValue(attributeOldValue);
706 1 : r->SetCharacterDataOldValue(characterDataOldValue);
707 1 : r->SetNativeAnonymousChildList(nativeAnonymousChildList);
708 1 : r->SetAttributeFilter(Move(filters));
709 1 : r->SetAllAttributes(allAttrs);
710 1 : r->SetAnimations(animations);
711 1 : r->RemoveClones();
712 :
713 : #ifdef DEBUG
714 2 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
715 1 : NS_WARNING_ASSERTION(mReceivers[i]->Target(),
716 : "All the receivers should have a target!");
717 : }
718 : #endif
719 : }
720 :
721 : void
722 0 : nsDOMMutationObserver::Disconnect()
723 : {
724 0 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
725 0 : mReceivers[i]->Disconnect(false);
726 : }
727 0 : mReceivers.Clear();
728 0 : mCurrentMutations.Clear();
729 0 : ClearPendingRecords();
730 0 : }
731 :
732 : void
733 0 : nsDOMMutationObserver::TakeRecords(
734 : nsTArray<RefPtr<nsDOMMutationRecord> >& aRetVal)
735 : {
736 0 : aRetVal.Clear();
737 0 : aRetVal.SetCapacity(mPendingMutationCount);
738 0 : RefPtr<nsDOMMutationRecord> current;
739 0 : current.swap(mFirstPendingMutation);
740 0 : for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
741 0 : RefPtr<nsDOMMutationRecord> next;
742 0 : current->mNext.swap(next);
743 0 : if (!mMergeAttributeRecords ||
744 0 : !MergeableAttributeRecord(aRetVal.SafeLastElement(nullptr),
745 : current)) {
746 0 : *aRetVal.AppendElement() = current.forget();
747 : }
748 0 : current.swap(next);
749 : }
750 0 : ClearPendingRecords();
751 0 : }
752 :
753 : void
754 0 : nsDOMMutationObserver::GetObservingInfo(
755 : nsTArray<Nullable<MutationObservingInfo>>& aResult,
756 : mozilla::ErrorResult& aRv)
757 : {
758 0 : aResult.SetCapacity(mReceivers.Count());
759 0 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
760 0 : MutationObservingInfo& info = aResult.AppendElement()->SetValue();
761 0 : nsMutationReceiver* mr = mReceivers[i];
762 0 : info.mChildList = mr->ChildList();
763 0 : info.mAttributes.Construct(mr->Attributes());
764 0 : info.mCharacterData.Construct(mr->CharacterData());
765 0 : info.mSubtree = mr->Subtree();
766 0 : info.mAttributeOldValue.Construct(mr->AttributeOldValue());
767 0 : info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
768 0 : info.mNativeAnonymousChildList = mr->NativeAnonymousChildList();
769 0 : info.mAnimations = mr->Animations();
770 0 : nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
771 0 : if (filters.Count()) {
772 0 : info.mAttributeFilter.Construct();
773 : mozilla::dom::Sequence<nsString>& filtersAsStrings =
774 0 : info.mAttributeFilter.Value();
775 0 : nsString* strings = filtersAsStrings.AppendElements(filters.Count(),
776 0 : mozilla::fallible);
777 0 : if (!strings) {
778 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
779 0 : return;
780 : }
781 0 : for (int32_t j = 0; j < filters.Count(); ++j) {
782 0 : filters[j]->ToString(strings[j]);
783 : }
784 : }
785 0 : info.mObservedNode = mr->Target();
786 : }
787 : }
788 :
789 : // static
790 : already_AddRefed<nsDOMMutationObserver>
791 1 : nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
792 : mozilla::dom::MutationCallback& aCb,
793 : mozilla::ErrorResult& aRv)
794 : {
795 2 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
796 1 : if (!window) {
797 0 : aRv.Throw(NS_ERROR_FAILURE);
798 0 : return nullptr;
799 : }
800 1 : MOZ_ASSERT(window->IsInnerWindow());
801 1 : bool isChrome = nsContentUtils::IsChromeDoc(window->GetExtantDoc());
802 : RefPtr<nsDOMMutationObserver> observer =
803 3 : new nsDOMMutationObserver(window.forget(), aCb, isChrome);
804 1 : return observer.forget();
805 : }
806 :
807 :
808 : bool
809 0 : nsDOMMutationObserver::MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
810 : nsDOMMutationRecord* aRecord)
811 : {
812 0 : MOZ_ASSERT(mMergeAttributeRecords);
813 : return
814 0 : aOldRecord &&
815 0 : aOldRecord->mType == nsGkAtoms::attributes &&
816 0 : aOldRecord->mType == aRecord->mType &&
817 0 : aOldRecord->mTarget == aRecord->mTarget &&
818 0 : aOldRecord->mAttrName == aRecord->mAttrName &&
819 0 : aOldRecord->mAttrNamespace.Equals(aRecord->mAttrNamespace);
820 : }
821 :
822 : void
823 0 : nsDOMMutationObserver::HandleMutation()
824 : {
825 0 : NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
826 0 : NS_ASSERTION(mCurrentMutations.IsEmpty(),
827 : "Still generating MutationRecords?");
828 :
829 0 : mWaitingForRun = false;
830 :
831 0 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
832 0 : mReceivers[i]->RemoveClones();
833 : }
834 0 : mTransientReceivers.Clear();
835 :
836 0 : nsPIDOMWindowOuter* outer = mOwner->GetOuterWindow();
837 0 : if (!mPendingMutationCount || !outer ||
838 0 : outer->GetCurrentInnerWindow() != mOwner) {
839 0 : ClearPendingRecords();
840 0 : return;
841 : }
842 :
843 : mozilla::dom::Sequence<mozilla::OwningNonNull<nsDOMMutationRecord> >
844 0 : mutations;
845 0 : if (mutations.SetCapacity(mPendingMutationCount, mozilla::fallible)) {
846 : // We can't use TakeRecords easily here, because it deals with a
847 : // different type of array, and we want to optimize out any extra copying.
848 0 : RefPtr<nsDOMMutationRecord> current;
849 0 : current.swap(mFirstPendingMutation);
850 0 : for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
851 0 : RefPtr<nsDOMMutationRecord> next;
852 0 : current->mNext.swap(next);
853 0 : if (!mMergeAttributeRecords ||
854 0 : !MergeableAttributeRecord(mutations.Length() ?
855 0 : mutations.LastElement().get() : nullptr,
856 : current)) {
857 0 : *mutations.AppendElement(mozilla::fallible) = current;
858 : }
859 0 : current.swap(next);
860 : }
861 : }
862 0 : ClearPendingRecords();
863 :
864 0 : mCallback->Call(this, mutations, *this);
865 : }
866 :
867 0 : class AsyncMutationHandler : public mozilla::Runnable
868 : {
869 : public:
870 0 : AsyncMutationHandler() : mozilla::Runnable("AsyncMutationHandler") {}
871 0 : NS_IMETHOD Run() override
872 : {
873 0 : nsDOMMutationObserver::HandleMutations();
874 0 : return NS_OK;
875 : }
876 : };
877 :
878 : void
879 0 : nsDOMMutationObserver::HandleMutationsInternal()
880 : {
881 0 : if (!nsContentUtils::IsSafeToRunScript()) {
882 0 : nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
883 0 : return;
884 : }
885 0 : static RefPtr<nsDOMMutationObserver> sCurrentObserver;
886 0 : if (sCurrentObserver && !sCurrentObserver->Suppressed()) {
887 : // In normal cases sScheduledMutationObservers will be handled
888 : // after previous mutations are handled. But in case some
889 : // callback calls a sync API, which spins the eventloop, we need to still
890 : // process other mutations happening during that sync call.
891 : // This does *not* catch all cases, but should work for stuff running
892 : // in separate tabs.
893 0 : return;
894 : }
895 :
896 0 : mozilla::AutoSlowOperation aso;
897 :
898 0 : nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
899 :
900 0 : while (sScheduledMutationObservers) {
901 : AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* observers =
902 0 : sScheduledMutationObservers;
903 0 : sScheduledMutationObservers = nullptr;
904 0 : for (uint32_t i = 0; i < observers->Length(); ++i) {
905 0 : sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]);
906 0 : if (!sCurrentObserver->Suppressed()) {
907 0 : sCurrentObserver->HandleMutation();
908 : } else {
909 0 : if (!suppressedObservers) {
910 0 : suppressedObservers = new nsTArray<RefPtr<nsDOMMutationObserver> >;
911 : }
912 0 : if (!suppressedObservers->Contains(sCurrentObserver)) {
913 0 : suppressedObservers->AppendElement(sCurrentObserver);
914 : }
915 : }
916 : }
917 0 : delete observers;
918 0 : aso.CheckForInterrupt();
919 : }
920 :
921 0 : if (suppressedObservers) {
922 0 : for (uint32_t i = 0; i < suppressedObservers->Length(); ++i) {
923 0 : static_cast<nsDOMMutationObserver*>(suppressedObservers->ElementAt(i))->
924 0 : RescheduleForRun();
925 : }
926 0 : delete suppressedObservers;
927 0 : suppressedObservers = nullptr;
928 : }
929 0 : sCurrentObserver = nullptr;
930 : }
931 :
932 : nsDOMMutationRecord*
933 0 : nsDOMMutationObserver::CurrentRecord(nsIAtom* aType)
934 : {
935 0 : NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
936 :
937 0 : while (mCurrentMutations.Length() < sMutationLevel) {
938 0 : mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr));
939 : }
940 :
941 0 : uint32_t last = sMutationLevel - 1;
942 0 : if (!mCurrentMutations[last]) {
943 0 : RefPtr<nsDOMMutationRecord> r = new nsDOMMutationRecord(aType, GetParentObject());
944 0 : mCurrentMutations[last] = r;
945 0 : AppendMutationRecord(r.forget());
946 0 : ScheduleForRun();
947 : }
948 :
949 : #ifdef DEBUG
950 0 : MOZ_ASSERT(sCurrentlyHandlingObservers->Length() == sMutationLevel);
951 0 : for (size_t i = 0; i < sCurrentlyHandlingObservers->Length(); ++i) {
952 0 : MOZ_ASSERT(sCurrentlyHandlingObservers->ElementAt(i).Contains(this),
953 : "MutationObserver should be added as an observer of all the "
954 : "nested mutations!");
955 : }
956 : #endif
957 :
958 0 : NS_ASSERTION(mCurrentMutations[last]->mType == aType,
959 : "Unexpected MutationRecord type!");
960 :
961 0 : return mCurrentMutations[last];
962 : }
963 :
964 0 : nsDOMMutationObserver::~nsDOMMutationObserver()
965 : {
966 0 : for (int32_t i = 0; i < mReceivers.Count(); ++i) {
967 0 : mReceivers[i]->RemoveClones();
968 : }
969 0 : }
970 :
971 : void
972 1043 : nsDOMMutationObserver::EnterMutationHandling()
973 : {
974 1043 : ++sMutationLevel;
975 1043 : }
976 :
977 : // Leave the current mutation level (there can be several levels if in case
978 : // of nested calls to the nsIMutationObserver methods).
979 : // The most recent mutation record is removed from mCurrentMutations, so
980 : // that is doesn't get modified anymore by receivers.
981 : void
982 1043 : nsDOMMutationObserver::LeaveMutationHandling()
983 : {
984 1043 : if (sCurrentlyHandlingObservers &&
985 1043 : sCurrentlyHandlingObservers->Length() == sMutationLevel) {
986 : nsTArray<RefPtr<nsDOMMutationObserver> >& obs =
987 0 : sCurrentlyHandlingObservers->ElementAt(sMutationLevel - 1);
988 0 : for (uint32_t i = 0; i < obs.Length(); ++i) {
989 : nsDOMMutationObserver* o =
990 0 : static_cast<nsDOMMutationObserver*>(obs[i]);
991 0 : if (o->mCurrentMutations.Length() == sMutationLevel) {
992 : // It is already in pending mutations.
993 0 : o->mCurrentMutations.RemoveElementAt(sMutationLevel - 1);
994 : }
995 : }
996 0 : sCurrentlyHandlingObservers->RemoveElementAt(sMutationLevel - 1);
997 : }
998 1043 : --sMutationLevel;
999 1043 : }
1000 :
1001 : void
1002 0 : nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver,
1003 : uint32_t aMutationLevel)
1004 : {
1005 0 : NS_ASSERTION(aMutationLevel > 0, "Unexpected mutation level!");
1006 :
1007 0 : if (aMutationLevel > 1) {
1008 : // MutationObserver must be in the currently handling observer list
1009 : // in all the nested levels.
1010 0 : AddCurrentlyHandlingObserver(aObserver, aMutationLevel - 1);
1011 : }
1012 :
1013 0 : if (!sCurrentlyHandlingObservers) {
1014 0 : sCurrentlyHandlingObservers =
1015 0 : new AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>;
1016 : }
1017 :
1018 0 : while (sCurrentlyHandlingObservers->Length() < aMutationLevel) {
1019 0 : sCurrentlyHandlingObservers->InsertElementAt(
1020 0 : sCurrentlyHandlingObservers->Length());
1021 : }
1022 :
1023 0 : uint32_t index = aMutationLevel - 1;
1024 0 : if (!sCurrentlyHandlingObservers->ElementAt(index).Contains(aObserver)) {
1025 0 : sCurrentlyHandlingObservers->ElementAt(index).AppendElement(aObserver);
1026 : }
1027 0 : }
1028 :
1029 : void
1030 0 : nsDOMMutationObserver::Shutdown()
1031 : {
1032 0 : delete sCurrentlyHandlingObservers;
1033 0 : sCurrentlyHandlingObservers = nullptr;
1034 0 : delete sScheduledMutationObservers;
1035 0 : sScheduledMutationObservers = nullptr;
1036 0 : }
1037 :
1038 : nsAutoMutationBatch*
1039 : nsAutoMutationBatch::sCurrentBatch = nullptr;
1040 :
1041 : void
1042 10 : nsAutoMutationBatch::Done()
1043 : {
1044 10 : if (sCurrentBatch != this) {
1045 0 : return;
1046 : }
1047 :
1048 10 : sCurrentBatch = mPreviousBatch;
1049 10 : if (mObservers.IsEmpty()) {
1050 10 : nsDOMMutationObserver::LeaveMutationHandling();
1051 : // Nothing to do.
1052 10 : return;
1053 : }
1054 :
1055 0 : uint32_t len = mObservers.Length();
1056 0 : for (uint32_t i = 0; i < len; ++i) {
1057 0 : nsDOMMutationObserver* ob = mObservers[i].mObserver;
1058 0 : bool wantsChildList = mObservers[i].mWantsChildList;
1059 :
1060 0 : RefPtr<nsSimpleContentList> removedList;
1061 0 : if (wantsChildList) {
1062 0 : removedList = new nsSimpleContentList(mBatchTarget);
1063 : }
1064 :
1065 0 : nsTArray<nsMutationReceiver*> allObservers;
1066 0 : ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers);
1067 :
1068 0 : int32_t j = mFromFirstToLast ? 0 : mRemovedNodes.Length() - 1;
1069 0 : int32_t end = mFromFirstToLast ? mRemovedNodes.Length() : -1;
1070 0 : for (; j != end; mFromFirstToLast ? ++j : --j) {
1071 0 : nsCOMPtr<nsIContent> removed = mRemovedNodes[j];
1072 0 : if (removedList) {
1073 0 : removedList->AppendElement(removed);
1074 : }
1075 :
1076 0 : if (allObservers.Length()) {
1077 : nsCOMArray<nsMutationReceiver>* transientReceivers =
1078 0 : ob->mTransientReceivers.LookupForAdd(removed).OrInsert(
1079 0 : [] () { return new nsCOMArray<nsMutationReceiver>(); });
1080 0 : for (uint32_t k = 0; k < allObservers.Length(); ++k) {
1081 0 : nsMutationReceiver* r = allObservers[k];
1082 0 : nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r;
1083 0 : if (ob->GetReceiverFor(removed, false, false) != orig) {
1084 : // Make sure the elements which are removed from the
1085 : // subtree are kept in the same observation set.
1086 : nsMutationReceiver* tr;
1087 0 : if (orig->Animations()) {
1088 0 : tr = nsAnimationReceiver::Create(removed, orig);
1089 : } else {
1090 0 : tr = nsMutationReceiver::Create(removed, orig);
1091 : }
1092 0 : transientReceivers->AppendObject(tr);
1093 : }
1094 : }
1095 : }
1096 : }
1097 0 : if (wantsChildList && (mRemovedNodes.Length() || mAddedNodes.Length())) {
1098 : RefPtr<nsSimpleContentList> addedList =
1099 0 : new nsSimpleContentList(mBatchTarget);
1100 0 : for (uint32_t i = 0; i < mAddedNodes.Length(); ++i) {
1101 0 : addedList->AppendElement(mAddedNodes[i]);
1102 : }
1103 : RefPtr<nsDOMMutationRecord> m =
1104 : new nsDOMMutationRecord(nsGkAtoms::childList,
1105 0 : ob->GetParentObject());
1106 0 : m->mTarget = mBatchTarget;
1107 0 : m->mRemovedNodes = removedList;
1108 0 : m->mAddedNodes = addedList;
1109 0 : m->mPreviousSibling = mPrevSibling;
1110 0 : m->mNextSibling = mNextSibling;
1111 0 : ob->AppendMutationRecord(m.forget());
1112 : }
1113 : // Always schedule the observer so that transient receivers are
1114 : // removed correctly.
1115 0 : ob->ScheduleForRun();
1116 : }
1117 0 : nsDOMMutationObserver::LeaveMutationHandling();
1118 : }
1119 :
1120 : nsAutoAnimationMutationBatch*
1121 : nsAutoAnimationMutationBatch::sCurrentBatch = nullptr;
1122 :
1123 : void
1124 86 : nsAutoAnimationMutationBatch::Done()
1125 : {
1126 86 : if (sCurrentBatch != this) {
1127 13 : return;
1128 : }
1129 :
1130 73 : sCurrentBatch = nullptr;
1131 73 : if (mObservers.IsEmpty()) {
1132 73 : nsDOMMutationObserver::LeaveMutationHandling();
1133 : // Nothing to do.
1134 73 : return;
1135 : }
1136 :
1137 0 : mBatchTargets.Sort(TreeOrderComparator());
1138 :
1139 0 : for (nsDOMMutationObserver* ob : mObservers) {
1140 0 : bool didAddRecords = false;
1141 :
1142 0 : for (nsINode* target : mBatchTargets) {
1143 0 : EntryArray* entries = mEntryTable.Get(target);
1144 0 : MOZ_ASSERT(entries,
1145 : "Targets in entry table and targets list should match");
1146 :
1147 : RefPtr<nsDOMMutationRecord> m =
1148 0 : new nsDOMMutationRecord(nsGkAtoms::animations, ob->GetParentObject());
1149 0 : m->mTarget = target;
1150 :
1151 0 : for (const Entry& e : *entries) {
1152 0 : if (e.mState == eState_Added) {
1153 0 : m->mAddedAnimations.AppendElement(e.mAnimation);
1154 0 : } else if (e.mState == eState_Removed) {
1155 0 : m->mRemovedAnimations.AppendElement(e.mAnimation);
1156 0 : } else if (e.mState == eState_RemainedPresent && e.mChanged) {
1157 0 : m->mChangedAnimations.AppendElement(e.mAnimation);
1158 : }
1159 : }
1160 :
1161 0 : if (!m->mAddedAnimations.IsEmpty() ||
1162 0 : !m->mChangedAnimations.IsEmpty() ||
1163 0 : !m->mRemovedAnimations.IsEmpty()) {
1164 0 : ob->AppendMutationRecord(m.forget());
1165 0 : didAddRecords = true;
1166 : }
1167 : }
1168 :
1169 0 : if (didAddRecords) {
1170 0 : ob->ScheduleForRun();
1171 : }
1172 : }
1173 0 : nsDOMMutationObserver::LeaveMutationHandling();
1174 : }
|