content/base/src/nsDOMMutationObserver.h

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
     2 /* vim: set sw=4 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #ifndef nsDOMMutationObserver_h
     8 #define nsDOMMutationObserver_h
    10 #include "mozilla/Attributes.h"
    11 #include "nsCycleCollectionParticipant.h"
    12 #include "nsPIDOMWindow.h"
    13 #include "nsIScriptContext.h"
    14 #include "nsStubMutationObserver.h"
    15 #include "nsCOMArray.h"
    16 #include "nsTArray.h"
    17 #include "nsAutoPtr.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 "nsIDOMMutationEvent.h"
    24 #include "nsWrapperCache.h"
    25 #include "mozilla/dom/MutationObserverBinding.h"
    26 #include "nsIDocument.h"
    28 class nsDOMMutationObserver;
    29 using mozilla::dom::MutationObservingInfo;
    31 class nsDOMMutationRecord : public nsISupports,
    32                             public nsWrapperCache
    33 {
    34 public:
    35   nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
    36   : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
    37   {
    38     SetIsDOMBinding();
    39   }
    40   virtual ~nsDOMMutationRecord() {}
    42   nsISupports* GetParentObject() const
    43   {
    44     return mOwner;
    45   }
    47   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
    48   {
    49     return mozilla::dom::MutationRecordBinding::Wrap(aCx, this);
    50   }
    52   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    53   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
    55   void GetType(mozilla::dom::DOMString& aRetVal) const
    56   {
    57     aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
    58   }
    60   nsINode* GetTarget() const
    61   {
    62     return mTarget;
    63   }
    65   nsINodeList* AddedNodes();
    67   nsINodeList* RemovedNodes();
    69   nsINode* GetPreviousSibling() const
    70   {
    71     return mPreviousSibling;
    72   }
    74   nsINode* GetNextSibling() const
    75   {
    76     return mNextSibling;
    77   }
    79   void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
    80   {
    81     aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
    82   }
    84   void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
    85   {
    86     aRetVal.SetOwnedString(mAttrNamespace);
    87   }
    89   void GetOldValue(mozilla::dom::DOMString& aRetVal) const
    90   {
    91     aRetVal.SetOwnedString(mPrevValue);
    92   }
    94   nsCOMPtr<nsINode>             mTarget;
    95   nsCOMPtr<nsIAtom>             mType;
    96   nsCOMPtr<nsIAtom>             mAttrName;
    97   nsString                      mAttrNamespace;
    98   nsString                      mPrevValue;
    99   nsRefPtr<nsSimpleContentList> mAddedNodes;
   100   nsRefPtr<nsSimpleContentList> mRemovedNodes;
   101   nsCOMPtr<nsINode>             mPreviousSibling;
   102   nsCOMPtr<nsINode>             mNextSibling;
   104   nsRefPtr<nsDOMMutationRecord> mNext;
   105   nsCOMPtr<nsISupports>         mOwner;
   106 };
   108 // Base class just prevents direct access to
   109 // members to make sure we go through getters/setters.
   110 class nsMutationReceiverBase : public nsStubMutationObserver
   111 {
   112 public:
   113   virtual ~nsMutationReceiverBase() { }
   115   nsDOMMutationObserver* Observer();
   116   nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
   117   nsINode* RegisterTarget() { return mRegisterTarget; }
   119   bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
   120   void SetSubtree(bool aSubtree)
   121   {
   122     NS_ASSERTION(!mParent, "Shouldn't have parent");
   123     mSubtree = aSubtree;
   124   }
   126   bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
   127   void SetChildList(bool aChildList)
   128   {
   129     NS_ASSERTION(!mParent, "Shouldn't have parent");
   130     mChildList = aChildList;
   131   }
   133   bool CharacterData()
   134   {
   135     return mParent ? mParent->CharacterData() : mCharacterData;
   136   }
   137   void SetCharacterData(bool aCharacterData)
   138   {
   139     NS_ASSERTION(!mParent, "Shouldn't have parent");
   140     mCharacterData = aCharacterData;
   141   }
   143   bool CharacterDataOldValue()
   144   {
   145     return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
   146   }
   147   void SetCharacterDataOldValue(bool aOldValue)
   148   {
   149     NS_ASSERTION(!mParent, "Shouldn't have parent");
   150     mCharacterDataOldValue = aOldValue;
   151   }
   153   bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
   154   void SetAttributes(bool aAttributes)
   155   {
   156     NS_ASSERTION(!mParent, "Shouldn't have parent");
   157     mAttributes = aAttributes;
   158   }
   160   bool AllAttributes()
   161   {
   162     return mParent ? mParent->AllAttributes()
   163                    : mAllAttributes;
   164   }
   165   void SetAllAttributes(bool aAll)
   166   {
   167     NS_ASSERTION(!mParent, "Shouldn't have parent");
   168     mAllAttributes = aAll;
   169   }
   171   bool AttributeOldValue() {
   172     return mParent ? mParent->AttributeOldValue()
   173                    : mAttributeOldValue;
   174   }
   175   void SetAttributeOldValue(bool aOldValue)
   176   {
   177     NS_ASSERTION(!mParent, "Shouldn't have parent");
   178     mAttributeOldValue = aOldValue;
   179   }
   181   nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
   182   void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
   183   {
   184     NS_ASSERTION(!mParent, "Shouldn't have parent");
   185     mAttributeFilter.Clear();
   186     mAttributeFilter.AppendObjects(aFilter);
   187   }
   189   void AddClone(nsMutationReceiverBase* aClone)
   190   {
   191     mTransientReceivers.AppendObject(aClone);
   192   }
   194   void RemoveClone(nsMutationReceiverBase* aClone)
   195   {
   196     mTransientReceivers.RemoveObject(aClone);
   197   }
   199 protected:
   200   nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
   201   : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
   202   {
   203     mRegisterTarget->AddMutationObserver(this);
   204     mRegisterTarget->SetMayHaveDOMMutationObserver();
   205     mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
   206   }
   208   nsMutationReceiverBase(nsINode* aRegisterTarget,
   209                          nsMutationReceiverBase* aParent)
   210   : mTarget(nullptr), mObserver(nullptr), mParent(aParent),
   211     mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target())
   212   {
   213     NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
   214     mRegisterTarget->AddMutationObserver(this);
   215     mRegisterTarget->SetMayHaveDOMMutationObserver();
   216     mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
   217   }
   219   bool ObservesAttr(mozilla::dom::Element* aElement,
   220                     int32_t aNameSpaceID,
   221                     nsIAtom* aAttr)
   222   {
   223     if (mParent) {
   224       return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
   225     }
   226     if (!Attributes() || (!Subtree() && aElement != Target())) {
   227       return false;
   228     }
   229     if (AllAttributes()) {
   230       return true;
   231     }
   233     if (aNameSpaceID != kNameSpaceID_None) {
   234       return false;
   235     }
   237     nsCOMArray<nsIAtom>& filters = AttributeFilter();
   238     for (int32_t i = 0; i < filters.Count(); ++i) {
   239       if (filters[i] == aAttr) {
   240         return true;
   241       }
   242     }
   243     return false;
   244   }
   246   // The target for the MutationObserver.observe() method.
   247   nsINode*                           mTarget;
   248   nsDOMMutationObserver*             mObserver;
   249   nsRefPtr<nsMutationReceiverBase>   mParent; // Cleared after microtask.
   250   // The node to which Gecko-internal nsIMutationObserver was registered to.
   251   // This is different than mTarget when dealing with transient observers.
   252   nsINode*                           mRegisterTarget;
   253   nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
   254   // While we have transient receivers, keep the original mutation receiver
   255   // alive so it doesn't go away and disconnect all its transient receivers.
   256   nsCOMPtr<nsINode>                  mKungFuDeathGrip;
   258 private:
   259   bool                               mSubtree;
   260   bool                               mChildList;
   261   bool                               mCharacterData;
   262   bool                               mCharacterDataOldValue;
   263   bool                               mAttributes;
   264   bool                               mAllAttributes;
   265   bool                               mAttributeOldValue;
   266   nsCOMArray<nsIAtom>                mAttributeFilter;
   267 };
   270 class nsMutationReceiver : public nsMutationReceiverBase
   271 {
   272 public:
   273   nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
   275   nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
   276   : nsMutationReceiverBase(aRegisterTarget, aParent)
   277   {
   278     NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
   279                  "Shouldn't create deep observer hierarchies!");
   280     aParent->AddClone(this);
   281   }
   283   virtual ~nsMutationReceiver() { Disconnect(false); }
   285   nsMutationReceiver* GetParent()
   286   {
   287     return static_cast<nsMutationReceiver*>(mParent.get());
   288   }
   290   void RemoveClones()
   291   {
   292     for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
   293       nsMutationReceiver* r =
   294         static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
   295       r->DisconnectTransientReceiver();
   296     }
   297     mTransientReceivers.Clear();
   298   }
   300   void DisconnectTransientReceiver()
   301   {
   302     if (mRegisterTarget) {
   303       mRegisterTarget->RemoveMutationObserver(this);
   304       mRegisterTarget = nullptr;
   305     }
   307     mParent = nullptr;
   308     NS_ASSERTION(!mTarget, "Should not have mTarget");
   309     NS_ASSERTION(!mObserver, "Should not have mObserver");
   310   }
   312   void Disconnect(bool aRemoveFromObserver);
   314   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
   315   NS_DECL_ISUPPORTS
   317   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   318   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
   319   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   320   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   321   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   322   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
   324   virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
   325                                           mozilla::dom::Element* aElement,
   326                                           int32_t aNameSpaceID,
   327                                           nsIAtom* aAttribute) MOZ_OVERRIDE
   328   {
   329     // We can reuse AttributeWillChange implementation.
   330     AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
   331                         nsIDOMMutationEvent::MODIFICATION);
   332   }
   333 };
   335 #define NS_DOM_MUTATION_OBSERVER_IID \
   336 { 0x0c3b91f8, 0xcc3b, 0x4b08, \
   337   { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
   339 class nsDOMMutationObserver : public nsISupports,
   340                               public nsWrapperCache
   341 {
   342 public:
   343   nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
   344                         mozilla::dom::MutationCallback& aCb)
   345   : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
   346     mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
   347   {
   348     SetIsDOMBinding();
   349   }
   350   virtual ~nsDOMMutationObserver();
   351   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   352   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
   353   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
   355   static already_AddRefed<nsDOMMutationObserver>
   356   Constructor(const mozilla::dom::GlobalObject& aGlobal,
   357               mozilla::dom::MutationCallback& aCb,
   358               mozilla::ErrorResult& aRv);
   360   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
   361   {
   362     return mozilla::dom::MutationObserverBinding::Wrap(aCx, this);
   363   }
   365   nsISupports* GetParentObject() const
   366   {
   367     return mOwner;
   368   }
   370   void Observe(nsINode& aTarget,
   371                const mozilla::dom::MutationObserverInit& aOptions,
   372                mozilla::ErrorResult& aRv);
   374   void Disconnect();
   376   void TakeRecords(nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal);
   378   void HandleMutation();
   380   void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult);
   382   mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
   384   void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
   385   {
   386     nsRefPtr<nsDOMMutationRecord> record = aRecord;
   387     MOZ_ASSERT(record);
   388     if (!mLastPendingMutation) {
   389       MOZ_ASSERT(!mFirstPendingMutation);
   390       mFirstPendingMutation = record.forget();
   391       mLastPendingMutation = mFirstPendingMutation;
   392     } else {
   393       MOZ_ASSERT(mFirstPendingMutation);
   394       mLastPendingMutation->mNext = record.forget();
   395       mLastPendingMutation = mLastPendingMutation->mNext;
   396     }
   397     ++mPendingMutationCount;
   398   }
   400   void ClearPendingRecords()
   401   {
   402     mFirstPendingMutation = nullptr;
   403     mLastPendingMutation = nullptr;
   404     mPendingMutationCount = 0;
   405   }
   407   // static methods
   408   static void HandleMutations()
   409   {
   410     if (sScheduledMutationObservers) {
   411       HandleMutationsInternal();
   412     }
   413   }
   415   static void EnterMutationHandling();
   416   static void LeaveMutationHandling();
   418   static void Shutdown();
   419 protected:
   420   friend class nsMutationReceiver;
   421   friend class nsAutoMutationBatch;
   422   nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate);
   423   void RemoveReceiver(nsMutationReceiver* aReceiver);
   425   already_AddRefed<nsIVariant> TakeRecords();
   427   void GetAllSubtreeObserversFor(nsINode* aNode,
   428                                  nsTArray<nsMutationReceiver*>& aObservers);
   429   void ScheduleForRun();
   430   void RescheduleForRun();
   432   nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
   433   bool HasCurrentRecord(const nsAString& aType);
   435   bool Suppressed()
   436   {
   437     if (mOwner) {
   438       nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
   439       return d && d->IsInSyncOperation();
   440     }
   441     return false;
   442   }
   444   static void HandleMutationsInternal();
   446   static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
   448   nsCOMPtr<nsPIDOMWindow>                            mOwner;
   450   nsCOMArray<nsMutationReceiver>                     mReceivers;
   451   nsClassHashtable<nsISupportsHashKey,
   452                    nsCOMArray<nsMutationReceiver> >  mTransientReceivers;
   453   // MutationRecords which are being constructed.
   454   nsAutoTArray<nsDOMMutationRecord*, 4>              mCurrentMutations;
   455   // MutationRecords which will be handed to the callback at the end of
   456   // the microtask.
   457   nsRefPtr<nsDOMMutationRecord>                      mFirstPendingMutation;
   458   nsDOMMutationRecord*                               mLastPendingMutation;
   459   uint32_t                                           mPendingMutationCount;
   461   nsRefPtr<mozilla::dom::MutationCallback>           mCallback;
   463   bool                                               mWaitingForRun;
   465   uint64_t                                           mId;
   467   static uint64_t                                    sCount;
   468   static nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
   469   static nsDOMMutationObserver*                      sCurrentObserver;
   471   static uint32_t                                    sMutationLevel;
   472   static nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
   473                                                      sCurrentlyHandlingObservers;
   474 };
   476 NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
   478 class nsAutoMutationBatch
   479 {
   480 public:
   481   nsAutoMutationBatch()
   482   : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
   483     mFromFirstToLast(false), mAllowNestedBatches(false)    
   484   {
   485   }
   487   nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
   488                       bool aAllowNestedBatches)
   489   : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
   490     mFromFirstToLast(false), mAllowNestedBatches(false)
   491   {
   492     Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
   493   }
   495   void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
   496   {
   497     if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
   498       if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
   499         return;
   500       }
   501       mBatchTarget = aTarget;
   502       mFromFirstToLast = aFromFirstToLast;
   503       mAllowNestedBatches = aAllowNestedBatches;
   504       mPreviousBatch = sCurrentBatch;
   505       sCurrentBatch = this;
   506       nsDOMMutationObserver::EnterMutationHandling();
   507     }
   508   }
   510   void RemovalDone() { mRemovalDone = true; }
   511   static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
   513   void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
   514   void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
   516   void Done();
   518   ~nsAutoMutationBatch() { NodesAdded(); }
   520   static bool IsBatching()
   521   {
   522     return !!sCurrentBatch;
   523   }
   525   static nsAutoMutationBatch* GetCurrentBatch()
   526   {
   527     return sCurrentBatch;
   528   }
   530   static void UpdateObserver(nsDOMMutationObserver* aObserver,
   531                              bool aWantsChildList)
   532   {
   533     uint32_t l = sCurrentBatch->mObservers.Length();
   534     for (uint32_t i = 0; i < l; ++i) {
   535       if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
   536         if (aWantsChildList) {
   537           sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
   538         } 
   539         return;
   540       }
   541     }
   542     BatchObserver* bo = sCurrentBatch->mObservers.AppendElement(); 
   543     bo->mObserver = aObserver;
   544     bo->mWantsChildList = aWantsChildList;
   545   }
   548   static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
   550   // Mutation receivers notify the batch about removed child nodes.
   551   static void NodeRemoved(nsIContent* aChild)
   552   {
   553     if (IsBatching() && !sCurrentBatch->mRemovalDone) {
   554       uint32_t len = sCurrentBatch->mRemovedNodes.Length();
   555       if (!len ||
   556           sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
   557         sCurrentBatch->mRemovedNodes.AppendElement(aChild);
   558       }
   559     }
   560   }
   562   // Called after new child nodes have been added to the batch target.
   563   void NodesAdded()
   564   {
   565     if (sCurrentBatch != this) {
   566       return;
   567     }
   569     nsIContent* c =
   570       mPrevSibling ? mPrevSibling->GetNextSibling() :
   571                      mBatchTarget->GetFirstChild();
   572     for (; c != mNextSibling; c = c->GetNextSibling()) {
   573       mAddedNodes.AppendElement(c);
   574     }
   575     Done();
   576   }
   578 private:
   579   struct BatchObserver
   580   {
   581     nsDOMMutationObserver* mObserver;
   582     bool                   mWantsChildList;
   583   };
   585   static nsAutoMutationBatch* sCurrentBatch;
   586   nsAutoMutationBatch* mPreviousBatch;
   587   nsAutoTArray<BatchObserver, 2> mObservers;
   588   nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
   589   nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
   590   nsINode* mBatchTarget;
   591   bool mRemovalDone;
   592   bool mFromFirstToLast;
   593   bool mAllowNestedBatches;
   594   nsCOMPtr<nsINode> mPrevSibling;
   595   nsCOMPtr<nsINode> mNextSibling;
   596 };
   598 inline
   599 nsDOMMutationObserver*
   600 nsMutationReceiverBase::Observer()
   601 {
   602   return mParent ?
   603     mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
   604 }
   606 #endif

mercurial