Coverage Report

Created: 2018-09-25 14:53

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