Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsDOMMutationObserver.h
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
#ifndef nsDOMMutationObserver_h
8
#define nsDOMMutationObserver_h
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/Move.h"
12
#include "nsCycleCollectionParticipant.h"
13
#include "nsPIDOMWindow.h"
14
#include "nsIScriptContext.h"
15
#include "nsStubAnimationObserver.h"
16
#include "nsCOMArray.h"
17
#include "nsTArray.h"
18
#include "nsIVariant.h"
19
#include "nsContentList.h"
20
#include "mozilla/dom/Element.h"
21
#include "nsClassHashtable.h"
22
#include "nsNodeUtils.h"
23
#include "nsWrapperCache.h"
24
#include "mozilla/dom/Nullable.h"
25
#include "mozilla/dom/MutationEventBinding.h"
26
#include "mozilla/dom/MutationObserverBinding.h"
27
#include "nsIDocument.h"
28
#include "mozilla/dom/Animation.h"
29
#include "nsIAnimationObserver.h"
30
#include "nsGlobalWindow.h"
31
32
class nsDOMMutationObserver;
33
using mozilla::dom::MutationObservingInfo;
34
35
class nsDOMMutationRecord final : public nsISupports,
36
                                  public nsWrapperCache
37
{
38
0
  virtual ~nsDOMMutationRecord() {}
39
40
public:
41
  typedef nsTArray<RefPtr<mozilla::dom::Animation>> AnimationArray;
42
43
  nsDOMMutationRecord(nsAtom* aType, nsISupports* aOwner)
44
  : mType(aType), mAttrNamespace(VoidString()), mPrevValue(VoidString()), mOwner(aOwner)
45
0
  {
46
0
  }
47
48
  nsISupports* GetParentObject() const
49
0
  {
50
0
    return mOwner;
51
0
  }
52
53
  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
54
0
  {
55
0
    return mozilla::dom::MutationRecord_Binding::Wrap(aCx, this, aGivenProto);
56
0
  }
57
58
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
59
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
60
61
  void GetType(mozilla::dom::DOMString& aRetVal) const
62
0
  {
63
0
    aRetVal.SetKnownLiveAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
64
0
  }
65
66
  nsINode* GetTarget() const
67
0
  {
68
0
    return mTarget;
69
0
  }
70
71
  nsINodeList* AddedNodes();
72
73
  nsINodeList* RemovedNodes();
74
75
  nsINode* GetPreviousSibling() const
76
0
  {
77
0
    return mPreviousSibling;
78
0
  }
79
80
  nsINode* GetNextSibling() const
81
0
  {
82
0
    return mNextSibling;
83
0
  }
84
85
  void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
86
0
  {
87
0
    aRetVal.SetKnownLiveAtom(mAttrName,
88
0
                             mozilla::dom::DOMString::eTreatNullAsNull);
89
0
  }
90
91
  void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
92
0
  {
93
0
    aRetVal.SetKnownLiveString(mAttrNamespace);
94
0
  }
95
96
  void GetOldValue(mozilla::dom::DOMString& aRetVal) const
97
0
  {
98
0
    aRetVal.SetKnownLiveString(mPrevValue);
99
0
  }
100
101
  void GetAddedAnimations(AnimationArray& aRetVal) const
102
0
  {
103
0
    aRetVal = mAddedAnimations;
104
0
  }
105
106
  void GetRemovedAnimations(AnimationArray& aRetVal) const
107
0
  {
108
0
    aRetVal = mRemovedAnimations;
109
0
  }
110
111
  void GetChangedAnimations(AnimationArray& aRetVal) const
112
0
  {
113
0
    aRetVal = mChangedAnimations;
114
0
  }
115
116
  nsCOMPtr<nsINode>             mTarget;
117
  RefPtr<nsAtom>             mType;
118
  RefPtr<nsAtom>             mAttrName;
119
  nsString                      mAttrNamespace;
120
  nsString                      mPrevValue;
121
  RefPtr<nsSimpleContentList> mAddedNodes;
122
  RefPtr<nsSimpleContentList> mRemovedNodes;
123
  nsCOMPtr<nsINode>             mPreviousSibling;
124
  nsCOMPtr<nsINode>             mNextSibling;
125
  AnimationArray                mAddedAnimations;
126
  AnimationArray                mRemovedAnimations;
127
  AnimationArray                mChangedAnimations;
128
129
  RefPtr<nsDOMMutationRecord> mNext;
130
  nsCOMPtr<nsISupports>         mOwner;
131
};
132
133
// Base class just prevents direct access to
134
// members to make sure we go through getters/setters.
135
class nsMutationReceiverBase : public nsStubAnimationObserver
136
{
137
public:
138
0
  virtual ~nsMutationReceiverBase() { }
139
140
  nsDOMMutationObserver* Observer();
141
0
  nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
142
0
  nsINode* RegisterTarget() { return mRegisterTarget; }
143
144
0
  bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
145
  void SetSubtree(bool aSubtree)
146
0
  {
147
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
148
0
    mSubtree = aSubtree;
149
0
  }
150
151
0
  bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
152
  void SetChildList(bool aChildList)
153
0
  {
154
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
155
0
    mChildList = aChildList;
156
0
  }
157
158
  bool CharacterData()
159
0
  {
160
0
    return mParent ? mParent->CharacterData() : mCharacterData;
161
0
  }
162
  void SetCharacterData(bool aCharacterData)
163
0
  {
164
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
165
0
    mCharacterData = aCharacterData;
166
0
  }
167
168
  bool CharacterDataOldValue()
169
0
  {
170
0
    return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
171
0
  }
172
  void SetCharacterDataOldValue(bool aOldValue)
173
0
  {
174
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
175
0
    mCharacterDataOldValue = aOldValue;
176
0
  }
177
178
  bool NativeAnonymousChildList()
179
0
  {
180
0
    return mParent ? mParent->NativeAnonymousChildList() : mNativeAnonymousChildList;
181
0
  }
182
  void SetNativeAnonymousChildList(bool aOldValue)
183
0
  {
184
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
185
0
    mNativeAnonymousChildList = aOldValue;
186
0
  }
187
188
0
  bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
189
  void SetAttributes(bool aAttributes)
190
0
  {
191
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
192
0
    mAttributes = aAttributes;
193
0
  }
194
195
  bool AllAttributes()
196
0
  {
197
0
    return mParent ? mParent->AllAttributes()
198
0
                   : mAllAttributes;
199
0
  }
200
  void SetAllAttributes(bool aAll)
201
0
  {
202
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
203
0
    mAllAttributes = aAll;
204
0
  }
205
206
0
  bool Animations() { return mParent ? mParent->Animations() : mAnimations; }
207
  void SetAnimations(bool aAnimations)
208
0
  {
209
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
210
0
    mAnimations = aAnimations;
211
0
  }
212
213
0
  bool AttributeOldValue() {
214
0
    return mParent ? mParent->AttributeOldValue()
215
0
                   : mAttributeOldValue;
216
0
  }
217
  void SetAttributeOldValue(bool aOldValue)
218
0
  {
219
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
220
0
    mAttributeOldValue = aOldValue;
221
0
  }
222
223
0
  nsTArray<RefPtr<nsAtom>>& AttributeFilter() { return mAttributeFilter; }
224
  void SetAttributeFilter(nsTArray<RefPtr<nsAtom>>&& aFilter)
225
0
  {
226
0
    NS_ASSERTION(!mParent, "Shouldn't have parent");
227
0
    mAttributeFilter.Clear();
228
0
    mAttributeFilter = std::move(aFilter);
229
0
  }
230
231
  void AddClone(nsMutationReceiverBase* aClone)
232
0
  {
233
0
    mTransientReceivers.AppendObject(aClone);
234
0
  }
235
236
  void RemoveClone(nsMutationReceiverBase* aClone)
237
0
  {
238
0
    mTransientReceivers.RemoveObject(aClone);
239
0
  }
240
241
protected:
242
  nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
243
    : mTarget(aTarget)
244
    , mObserver(aObserver)
245
    , mRegisterTarget(aTarget)
246
    , mSubtree(false)
247
    , mChildList(false)
248
    , mCharacterData(false)
249
    , mCharacterDataOldValue(false)
250
    , mNativeAnonymousChildList(false)
251
    , mAttributes(false)
252
    , mAllAttributes(false)
253
    , mAttributeOldValue(false)
254
    , mAnimations(false)
255
0
  {
256
0
  }
257
258
  nsMutationReceiverBase(nsINode* aRegisterTarget,
259
                         nsMutationReceiverBase* aParent)
260
    : mTarget(nullptr)
261
    , mObserver(nullptr)
262
    , mParent(aParent)
263
    , mRegisterTarget(aRegisterTarget)
264
    , mKungFuDeathGrip(aParent->Target())
265
    , mSubtree(false)
266
    , mChildList(false)
267
    , mCharacterData(false)
268
    , mCharacterDataOldValue(false)
269
    , mNativeAnonymousChildList(false)
270
    , mAttributes(false)
271
    , mAllAttributes(false)
272
    , mAttributeOldValue(false)
273
    , mAnimations(false)
274
0
  {
275
0
    NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
276
0
  }
277
278
  virtual void AddMutationObserver() = 0;
279
280
  void AddObserver()
281
0
  {
282
0
    AddMutationObserver();
283
0
    mRegisterTarget->SetMayHaveDOMMutationObserver();
284
0
    mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
285
0
  }
286
287
  bool IsObservable(nsIContent* aContent);
288
289
  bool ObservesAttr(nsINode* aRegisterTarget,
290
                    mozilla::dom::Element* aElement,
291
                    int32_t aNameSpaceID,
292
                    nsAtom* aAttr)
293
0
  {
294
0
    if (mParent) {
295
0
      return mParent->ObservesAttr(aRegisterTarget, aElement, aNameSpaceID, aAttr);
296
0
    }
297
0
    if (!Attributes() ||
298
0
        (!Subtree() && aElement != Target()) ||
299
0
        (Subtree() && aRegisterTarget->SubtreeRoot() != aElement->SubtreeRoot()) ||
300
0
        !IsObservable(aElement)) {
301
0
      return false;
302
0
    }
303
0
    if (AllAttributes()) {
304
0
      return true;
305
0
    }
306
0
307
0
    if (aNameSpaceID != kNameSpaceID_None) {
308
0
      return false;
309
0
    }
310
0
311
0
    nsTArray<RefPtr<nsAtom>>& filters = AttributeFilter();
312
0
    for (size_t i = 0; i < filters.Length(); ++i) {
313
0
      if (filters[i] == aAttr) {
314
0
        return true;
315
0
      }
316
0
    }
317
0
    return false;
318
0
  }
319
320
  // The target for the MutationObserver.observe() method.
321
  nsINode*                           mTarget;
322
  nsDOMMutationObserver*             mObserver;
323
  RefPtr<nsMutationReceiverBase>   mParent; // Cleared after microtask.
324
  // The node to which Gecko-internal nsIMutationObserver was registered to.
325
  // This is different than mTarget when dealing with transient observers.
326
  nsINode*                           mRegisterTarget;
327
  nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
328
  // While we have transient receivers, keep the original mutation receiver
329
  // alive so it doesn't go away and disconnect all its transient receivers.
330
  nsCOMPtr<nsINode>                  mKungFuDeathGrip;
331
332
private:
333
  bool                               mSubtree;
334
  bool                               mChildList;
335
  bool                               mCharacterData;
336
  bool                               mCharacterDataOldValue;
337
  bool                               mNativeAnonymousChildList;
338
  bool                               mAttributes;
339
  bool                               mAllAttributes;
340
  bool                               mAttributeOldValue;
341
  bool                               mAnimations;
342
  nsTArray<RefPtr<nsAtom>>          mAttributeFilter;
343
};
344
345
346
class nsMutationReceiver : public nsMutationReceiverBase
347
{
348
protected:
349
0
  virtual ~nsMutationReceiver() { Disconnect(false); }
350
351
public:
352
  static nsMutationReceiver* Create(nsINode* aTarget,
353
                                    nsDOMMutationObserver* aObserver)
354
0
  {
355
0
    nsMutationReceiver* r = new nsMutationReceiver(aTarget, aObserver);
356
0
    r->AddObserver();
357
0
    return r;
358
0
  }
359
360
  static nsMutationReceiver* Create(nsINode* aRegisterTarget,
361
                                    nsMutationReceiverBase* aParent)
362
0
  {
363
0
    nsMutationReceiver* r = new nsMutationReceiver(aRegisterTarget, aParent);
364
0
    aParent->AddClone(r);
365
0
    r->AddObserver();
366
0
    return r;
367
0
  }
368
369
  nsMutationReceiver* GetParent()
370
0
  {
371
0
    return static_cast<nsMutationReceiver*>(mParent.get());
372
0
  }
373
374
  void RemoveClones()
375
0
  {
376
0
    for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
377
0
      nsMutationReceiver* r =
378
0
        static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
379
0
      r->DisconnectTransientReceiver();
380
0
    }
381
0
    mTransientReceivers.Clear();
382
0
  }
383
384
  void DisconnectTransientReceiver()
385
0
  {
386
0
    if (mRegisterTarget) {
387
0
      mRegisterTarget->RemoveMutationObserver(this);
388
0
      mRegisterTarget = nullptr;
389
0
    }
390
0
391
0
    mParent = nullptr;
392
0
    NS_ASSERTION(!mTarget, "Should not have mTarget");
393
0
    NS_ASSERTION(!mObserver, "Should not have mObserver");
394
0
  }
395
396
  void Disconnect(bool aRemoveFromObserver);
397
398
  NS_DECL_ISUPPORTS
399
400
  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
401
  NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
402
  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
403
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
404
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
405
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
406
  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
407
408
  virtual void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
409
                                          int32_t aNameSpaceID,
410
                                          nsAtom* aAttribute) override
411
0
  {
412
0
    // We can reuse AttributeWillChange implementation.
413
0
    AttributeWillChange(aElement, aNameSpaceID, aAttribute,
414
0
                        mozilla::dom::MutationEvent_Binding::MODIFICATION, nullptr);
415
0
  }
416
417
protected:
418
  nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
419
420
  nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
421
  : nsMutationReceiverBase(aRegisterTarget, aParent)
422
0
  {
423
0
    NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
424
0
                 "Shouldn't create deep observer hierarchies!");
425
0
  }
426
427
  virtual void AddMutationObserver() override
428
0
  {
429
0
    mRegisterTarget->AddMutationObserver(this);
430
0
  }
431
};
432
433
class nsAnimationReceiver : public nsMutationReceiver
434
{
435
public:
436
  static nsAnimationReceiver* Create(nsINode* aTarget,
437
                                     nsDOMMutationObserver* aObserver)
438
0
  {
439
0
    nsAnimationReceiver* r = new nsAnimationReceiver(aTarget, aObserver);
440
0
    r->AddObserver();
441
0
    return r;
442
0
  }
443
444
  static nsAnimationReceiver* Create(nsINode* aRegisterTarget,
445
                                     nsMutationReceiverBase* aParent)
446
0
  {
447
0
    nsAnimationReceiver* r = new nsAnimationReceiver(aRegisterTarget, aParent);
448
0
    aParent->AddClone(r);
449
0
    r->AddObserver();
450
0
    return r;
451
0
  }
452
453
  NS_DECL_ISUPPORTS_INHERITED
454
455
  NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONADDED
456
  NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONCHANGED
457
  NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONREMOVED
458
459
protected:
460
0
  virtual ~nsAnimationReceiver() {}
461
462
  nsAnimationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver)
463
0
    : nsMutationReceiver(aTarget, aObserver) {}
464
465
  nsAnimationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
466
0
    : nsMutationReceiver(aRegisterTarget, aParent) {}
467
468
  virtual void AddMutationObserver() override
469
0
  {
470
0
    mRegisterTarget->AddAnimationObserver(this);
471
0
  }
472
473
private:
474
  enum AnimationMutation {
475
    eAnimationMutation_Added,
476
    eAnimationMutation_Changed,
477
    eAnimationMutation_Removed
478
  };
479
480
  void RecordAnimationMutation(mozilla::dom::Animation* aAnimation,
481
                               AnimationMutation aMutationType);
482
};
483
484
#define NS_DOM_MUTATION_OBSERVER_IID \
485
{ 0x0c3b91f8, 0xcc3b, 0x4b08, \
486
  { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
487
488
class nsDOMMutationObserver final : public nsISupports,
489
                                    public nsWrapperCache
490
{
491
public:
492
  nsDOMMutationObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
493
                        mozilla::dom::MutationCallback& aCb,
494
                        bool aChrome)
495
  : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
496
    mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome),
497
    mMergeAttributeRecords(false), mId(++sCount)
498
0
  {
499
0
  }
500
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
501
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
502
  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
503
504
  static already_AddRefed<nsDOMMutationObserver>
505
  Constructor(const mozilla::dom::GlobalObject& aGlobal,
506
              mozilla::dom::MutationCallback& aCb,
507
              mozilla::ErrorResult& aRv);
508
509
  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
510
0
  {
511
0
    return mozilla::dom::MutationObserver_Binding::Wrap(aCx, this, aGivenProto);
512
0
  }
513
514
  nsISupports* GetParentObject() const
515
0
  {
516
0
    return mOwner;
517
0
  }
518
519
  bool IsChrome()
520
0
  {
521
0
    return mIsChrome;
522
0
  }
523
524
  void Observe(nsINode& aTarget,
525
               const mozilla::dom::MutationObserverInit& aOptions,
526
               mozilla::ErrorResult& aRv);
527
528
  void Disconnect();
529
530
  void TakeRecords(nsTArray<RefPtr<nsDOMMutationRecord> >& aRetVal);
531
532
  void HandleMutation();
533
534
  void GetObservingInfo(
535
    nsTArray<mozilla::dom::Nullable<MutationObservingInfo>>& aResult,
536
    mozilla::ErrorResult& aRv);
537
538
0
  mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
539
540
  bool MergeAttributeRecords()
541
0
  {
542
0
    return mMergeAttributeRecords;
543
0
  }
544
545
  void SetMergeAttributeRecords(bool aVal)
546
0
  {
547
0
    mMergeAttributeRecords = aVal;
548
0
  }
549
550
  // If both records are for 'attributes' type and for the same target and
551
  // attribute name and namespace are the same, we can skip the newer record.
552
  // aOldRecord->mPrevValue holds the original value, if observed.
553
  bool MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
554
                                nsDOMMutationRecord* aRecord);
555
556
  void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
557
0
  {
558
0
    RefPtr<nsDOMMutationRecord> record = aRecord;
559
0
    MOZ_ASSERT(record);
560
0
    if (!mLastPendingMutation) {
561
0
      MOZ_ASSERT(!mFirstPendingMutation);
562
0
      mFirstPendingMutation = record.forget();
563
0
      mLastPendingMutation = mFirstPendingMutation;
564
0
    } else {
565
0
      MOZ_ASSERT(mFirstPendingMutation);
566
0
      mLastPendingMutation->mNext = record.forget();
567
0
      mLastPendingMutation = mLastPendingMutation->mNext;
568
0
    }
569
0
    ++mPendingMutationCount;
570
0
  }
571
572
  void ClearPendingRecords()
573
0
  {
574
0
    mFirstPendingMutation = nullptr;
575
0
    mLastPendingMutation = nullptr;
576
0
    mPendingMutationCount = 0;
577
0
  }
578
579
  // static methods
580
  static void QueueMutationObserverMicroTask();
581
582
  static void HandleMutations(mozilla::AutoSlowOperation& aAso);
583
584
  static bool AllScheduledMutationObserversAreSuppressed()
585
0
  {
586
0
    if (sScheduledMutationObservers) {
587
0
      uint32_t len = sScheduledMutationObservers->Length();
588
0
      if (len > 0) {
589
0
        for (uint32_t i = 0; i < len; ++i) {
590
0
          if (!(*sScheduledMutationObservers)[i]->Suppressed()) {
591
0
            return false;
592
0
          }
593
0
        }
594
0
        return true;
595
0
      }
596
0
    }
597
0
    return false;
598
0
  }
599
600
  static void EnterMutationHandling();
601
  static void LeaveMutationHandling();
602
603
  static void Shutdown();
604
protected:
605
  virtual ~nsDOMMutationObserver();
606
607
  friend class nsMutationReceiver;
608
  friend class nsAnimationReceiver;
609
  friend class nsAutoMutationBatch;
610
  friend class nsAutoAnimationMutationBatch;
611
  nsMutationReceiver* GetReceiverFor(nsINode* aNode,
612
                                     bool aMayCreate,
613
                                     bool aWantsAnimations);
614
  void RemoveReceiver(nsMutationReceiver* aReceiver);
615
616
  already_AddRefed<nsIVariant> TakeRecords();
617
618
  void GetAllSubtreeObserversFor(nsINode* aNode,
619
                                 nsTArray<nsMutationReceiver*>& aObservers);
620
  void ScheduleForRun();
621
  void RescheduleForRun();
622
623
  nsDOMMutationRecord* CurrentRecord(nsAtom* aType);
624
  bool HasCurrentRecord(const nsAString& aType);
625
626
  bool Suppressed()
627
0
  {
628
0
    return mOwner && nsGlobalWindowInner::Cast(mOwner)->IsInSyncOperation();
629
0
  }
630
631
  static void HandleMutationsInternal(mozilla::AutoSlowOperation& aAso);
632
633
  static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver,
634
                                           uint32_t aMutationLevel);
635
636
  nsCOMPtr<nsPIDOMWindowInner>                       mOwner;
637
638
  nsCOMArray<nsMutationReceiver>                     mReceivers;
639
  nsClassHashtable<nsISupportsHashKey,
640
                   nsCOMArray<nsMutationReceiver> >  mTransientReceivers;
641
  // MutationRecords which are being constructed.
642
  AutoTArray<nsDOMMutationRecord*, 4>              mCurrentMutations;
643
  // MutationRecords which will be handed to the callback at the end of
644
  // the microtask.
645
  RefPtr<nsDOMMutationRecord>                      mFirstPendingMutation;
646
  nsDOMMutationRecord*                               mLastPendingMutation;
647
  uint32_t                                           mPendingMutationCount;
648
649
  RefPtr<mozilla::dom::MutationCallback>           mCallback;
650
651
  bool                                               mWaitingForRun;
652
  bool                                               mIsChrome;
653
  bool                                               mMergeAttributeRecords;
654
655
  uint64_t                                           mId;
656
657
  static uint64_t                                    sCount;
658
  static AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
659
660
  static uint32_t                                    sMutationLevel;
661
  static AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>*
662
                                                     sCurrentlyHandlingObservers;
663
};
664
665
NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
666
667
class nsAutoMutationBatch
668
{
669
public:
670
  nsAutoMutationBatch()
671
  : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
672
    mFromFirstToLast(false), mAllowNestedBatches(false)
673
0
  {
674
0
  }
675
676
  nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
677
                      bool aAllowNestedBatches)
678
  : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
679
    mFromFirstToLast(false), mAllowNestedBatches(false)
680
0
  {
681
0
    Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
682
0
  }
683
684
  void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
685
0
  {
686
0
    if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
687
0
      if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
688
0
        return;
689
0
      }
690
0
      mBatchTarget = aTarget;
691
0
      mFromFirstToLast = aFromFirstToLast;
692
0
      mAllowNestedBatches = aAllowNestedBatches;
693
0
      mPreviousBatch = sCurrentBatch;
694
0
      sCurrentBatch = this;
695
0
      nsDOMMutationObserver::EnterMutationHandling();
696
0
    }
697
0
  }
698
699
0
  void RemovalDone() { mRemovalDone = true; }
700
0
  static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
701
702
0
  void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
703
0
  void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
704
705
  void Done();
706
707
0
  ~nsAutoMutationBatch() { NodesAdded(); }
708
709
  static bool IsBatching()
710
0
  {
711
0
    return !!sCurrentBatch;
712
0
  }
713
714
  static nsAutoMutationBatch* GetCurrentBatch()
715
0
  {
716
0
    return sCurrentBatch;
717
0
  }
718
719
  static void UpdateObserver(nsDOMMutationObserver* aObserver,
720
                             bool aWantsChildList)
721
0
  {
722
0
    uint32_t l = sCurrentBatch->mObservers.Length();
723
0
    for (uint32_t i = 0; i < l; ++i) {
724
0
      if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
725
0
        if (aWantsChildList) {
726
0
          sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
727
0
        }
728
0
        return;
729
0
      }
730
0
    }
731
0
    BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
732
0
    bo->mObserver = aObserver;
733
0
    bo->mWantsChildList = aWantsChildList;
734
0
  }
735
736
737
0
  static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
738
739
  // Mutation receivers notify the batch about removed child nodes.
740
  static void NodeRemoved(nsIContent* aChild)
741
0
  {
742
0
    if (IsBatching() && !sCurrentBatch->mRemovalDone) {
743
0
      uint32_t len = sCurrentBatch->mRemovedNodes.Length();
744
0
      if (!len ||
745
0
          sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
746
0
        sCurrentBatch->mRemovedNodes.AppendElement(aChild);
747
0
      }
748
0
    }
749
0
  }
750
751
  // Called after new child nodes have been added to the batch target.
752
  void NodesAdded()
753
0
  {
754
0
    if (sCurrentBatch != this) {
755
0
      return;
756
0
    }
757
0
758
0
    nsIContent* c =
759
0
      mPrevSibling ? mPrevSibling->GetNextSibling() :
760
0
                     mBatchTarget->GetFirstChild();
761
0
    for (; c != mNextSibling; c = c->GetNextSibling()) {
762
0
      mAddedNodes.AppendElement(c);
763
0
    }
764
0
    Done();
765
0
  }
766
767
private:
768
  struct BatchObserver
769
  {
770
    nsDOMMutationObserver* mObserver;
771
    bool                   mWantsChildList;
772
  };
773
774
  static nsAutoMutationBatch* sCurrentBatch;
775
  nsAutoMutationBatch* mPreviousBatch;
776
  AutoTArray<BatchObserver, 2> mObservers;
777
  nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
778
  nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
779
  nsINode* mBatchTarget;
780
  bool mRemovalDone;
781
  bool mFromFirstToLast;
782
  bool mAllowNestedBatches;
783
  nsCOMPtr<nsINode> mPrevSibling;
784
  nsCOMPtr<nsINode> mNextSibling;
785
};
786
787
class nsAutoAnimationMutationBatch
788
{
789
  struct Entry;
790
791
public:
792
  explicit nsAutoAnimationMutationBatch(nsIDocument* aDocument)
793
0
  {
794
0
    Init(aDocument);
795
0
  }
796
797
  void Init(nsIDocument* aDocument)
798
0
  {
799
0
    if (!aDocument ||
800
0
        !aDocument->MayHaveDOMMutationObservers() ||
801
0
        sCurrentBatch) {
802
0
      return;
803
0
    }
804
0
805
0
    sCurrentBatch = this;
806
0
    nsDOMMutationObserver::EnterMutationHandling();
807
0
  }
808
809
  ~nsAutoAnimationMutationBatch()
810
0
  {
811
0
    Done();
812
0
  }
813
814
  void Done();
815
816
  static bool IsBatching()
817
0
  {
818
0
    return !!sCurrentBatch;
819
0
  }
820
821
  static nsAutoAnimationMutationBatch* GetCurrentBatch()
822
0
  {
823
0
    return sCurrentBatch;
824
0
  }
825
826
  static void AddObserver(nsDOMMutationObserver* aObserver)
827
0
  {
828
0
    if (sCurrentBatch->mObservers.Contains(aObserver)) {
829
0
      return;
830
0
    }
831
0
    sCurrentBatch->mObservers.AppendElement(aObserver);
832
0
  }
833
834
  static void AnimationAdded(mozilla::dom::Animation* aAnimation,
835
                             nsINode* aTarget)
836
0
  {
837
0
    if (!IsBatching()) {
838
0
      return;
839
0
    }
840
0
841
0
    Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
842
0
    if (entry) {
843
0
      switch (entry->mState) {
844
0
        case eState_RemainedAbsent:
845
0
          entry->mState = eState_Added;
846
0
          break;
847
0
        case eState_Removed:
848
0
          entry->mState = eState_RemainedPresent;
849
0
          break;
850
0
        case eState_Added:
851
0
          // FIXME bug 1189015
852
0
          NS_ERROR("shouldn't have observed an animation being added twice");
853
0
          break;
854
0
        case eState_RemainedPresent:
855
0
          MOZ_ASSERT_UNREACHABLE("shouldn't have observed an animation "
856
0
                                 "remaining present");
857
0
          break;
858
0
      }
859
0
    } else {
860
0
      entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
861
0
      entry->mState = eState_Added;
862
0
      entry->mChanged = false;
863
0
    }
864
0
  }
865
866
  static void AnimationChanged(mozilla::dom::Animation* aAnimation,
867
                               nsINode* aTarget)
868
0
  {
869
0
    Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
870
0
    if (entry) {
871
0
      NS_ASSERTION(entry->mState == eState_RemainedPresent ||
872
0
                   entry->mState == eState_Added,
873
0
                   "shouldn't have observed an animation being changed after "
874
0
                   "being removed");
875
0
      entry->mChanged = true;
876
0
    } else {
877
0
      entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
878
0
      entry->mState = eState_RemainedPresent;
879
0
      entry->mChanged = true;
880
0
    }
881
0
  }
882
883
  static void AnimationRemoved(mozilla::dom::Animation* aAnimation,
884
                               nsINode* aTarget)
885
0
  {
886
0
    Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
887
0
    if (entry) {
888
0
      switch (entry->mState) {
889
0
        case eState_RemainedPresent:
890
0
          entry->mState = eState_Removed;
891
0
          break;
892
0
        case eState_Added:
893
0
          entry->mState = eState_RemainedAbsent;
894
0
          break;
895
0
        case eState_RemainedAbsent:
896
0
          MOZ_ASSERT_UNREACHABLE("shouldn't have observed an animation "
897
0
                                 "remaining absent");
898
0
          break;
899
0
        case eState_Removed:
900
0
          // FIXME bug 1189015
901
0
          NS_ERROR("shouldn't have observed an animation being removed twice");
902
0
          break;
903
0
      }
904
0
    } else {
905
0
      entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
906
0
      entry->mState = eState_Removed;
907
0
      entry->mChanged = false;
908
0
    }
909
0
  }
910
911
private:
912
  Entry* FindEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
913
0
  {
914
0
    EntryArray* entries = mEntryTable.Get(aTarget);
915
0
    if (!entries) {
916
0
      return nullptr;
917
0
    }
918
0
919
0
    for (Entry& e : *entries) {
920
0
      if (e.mAnimation == aAnimation) {
921
0
        return &e;
922
0
      }
923
0
    }
924
0
    return nullptr;
925
0
  }
926
927
  Entry* AddEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
928
0
  {
929
0
    EntryArray* entries = sCurrentBatch->mEntryTable.LookupOrAdd(aTarget);
930
0
    if (entries->IsEmpty()) {
931
0
      sCurrentBatch->mBatchTargets.AppendElement(aTarget);
932
0
    }
933
0
    Entry* entry = entries->AppendElement();
934
0
    entry->mAnimation = aAnimation;
935
0
    return entry;
936
0
  }
937
938
  enum State {
939
    eState_RemainedPresent,
940
    eState_RemainedAbsent,
941
    eState_Added,
942
    eState_Removed
943
  };
944
945
  struct Entry
946
  {
947
    RefPtr<mozilla::dom::Animation> mAnimation;
948
    State mState;
949
    bool mChanged;
950
  };
951
952
  static nsAutoAnimationMutationBatch* sCurrentBatch;
953
  AutoTArray<nsDOMMutationObserver*, 2> mObservers;
954
  typedef nsTArray<Entry> EntryArray;
955
  nsClassHashtable<nsPtrHashKey<nsINode>, EntryArray> mEntryTable;
956
  // List of nodes referred to by mEntryTable so we can sort them
957
  // For a specific pseudo element, we use its parent element as the
958
  // batch target, so they will be put in the same EntryArray.
959
  nsTArray<nsINode*> mBatchTargets;
960
};
961
962
inline
963
nsDOMMutationObserver*
964
nsMutationReceiverBase::Observer()
965
0
{
966
0
  return mParent ?
967
0
    mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
968
0
}
969
970
#endif