content/base/src/nsDOMMutationObserver.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsDOMMutationObserver.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,606 @@
     1.4 +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
     1.5 +/* vim: set sw=4 ts=8 et tw=80 : */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef nsDOMMutationObserver_h
    1.11 +#define nsDOMMutationObserver_h
    1.12 +
    1.13 +#include "mozilla/Attributes.h"
    1.14 +#include "nsCycleCollectionParticipant.h"
    1.15 +#include "nsPIDOMWindow.h"
    1.16 +#include "nsIScriptContext.h"
    1.17 +#include "nsStubMutationObserver.h"
    1.18 +#include "nsCOMArray.h"
    1.19 +#include "nsTArray.h"
    1.20 +#include "nsAutoPtr.h"
    1.21 +#include "nsIVariant.h"
    1.22 +#include "nsContentList.h"
    1.23 +#include "mozilla/dom/Element.h"
    1.24 +#include "nsClassHashtable.h"
    1.25 +#include "nsNodeUtils.h"
    1.26 +#include "nsIDOMMutationEvent.h"
    1.27 +#include "nsWrapperCache.h"
    1.28 +#include "mozilla/dom/MutationObserverBinding.h"
    1.29 +#include "nsIDocument.h"
    1.30 +
    1.31 +class nsDOMMutationObserver;
    1.32 +using mozilla::dom::MutationObservingInfo;
    1.33 +
    1.34 +class nsDOMMutationRecord : public nsISupports,
    1.35 +                            public nsWrapperCache
    1.36 +{
    1.37 +public:
    1.38 +  nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
    1.39 +  : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
    1.40 +  {
    1.41 +    SetIsDOMBinding();
    1.42 +  }
    1.43 +  virtual ~nsDOMMutationRecord() {}
    1.44 +
    1.45 +  nsISupports* GetParentObject() const
    1.46 +  {
    1.47 +    return mOwner;
    1.48 +  }
    1.49 +
    1.50 +  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
    1.51 +  {
    1.52 +    return mozilla::dom::MutationRecordBinding::Wrap(aCx, this);
    1.53 +  }
    1.54 +
    1.55 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    1.56 +  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
    1.57 +
    1.58 +  void GetType(mozilla::dom::DOMString& aRetVal) const
    1.59 +  {
    1.60 +    aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
    1.61 +  }
    1.62 +
    1.63 +  nsINode* GetTarget() const
    1.64 +  {
    1.65 +    return mTarget;
    1.66 +  }
    1.67 +
    1.68 +  nsINodeList* AddedNodes();
    1.69 +
    1.70 +  nsINodeList* RemovedNodes();
    1.71 +
    1.72 +  nsINode* GetPreviousSibling() const
    1.73 +  {
    1.74 +    return mPreviousSibling;
    1.75 +  }
    1.76 +
    1.77 +  nsINode* GetNextSibling() const
    1.78 +  {
    1.79 +    return mNextSibling;
    1.80 +  }
    1.81 +
    1.82 +  void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
    1.83 +  {
    1.84 +    aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
    1.85 +  }
    1.86 +
    1.87 +  void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
    1.88 +  {
    1.89 +    aRetVal.SetOwnedString(mAttrNamespace);
    1.90 +  }
    1.91 +
    1.92 +  void GetOldValue(mozilla::dom::DOMString& aRetVal) const
    1.93 +  {
    1.94 +    aRetVal.SetOwnedString(mPrevValue);
    1.95 +  }
    1.96 +
    1.97 +  nsCOMPtr<nsINode>             mTarget;
    1.98 +  nsCOMPtr<nsIAtom>             mType;
    1.99 +  nsCOMPtr<nsIAtom>             mAttrName;
   1.100 +  nsString                      mAttrNamespace;
   1.101 +  nsString                      mPrevValue;
   1.102 +  nsRefPtr<nsSimpleContentList> mAddedNodes;
   1.103 +  nsRefPtr<nsSimpleContentList> mRemovedNodes;
   1.104 +  nsCOMPtr<nsINode>             mPreviousSibling;
   1.105 +  nsCOMPtr<nsINode>             mNextSibling;
   1.106 +
   1.107 +  nsRefPtr<nsDOMMutationRecord> mNext;
   1.108 +  nsCOMPtr<nsISupports>         mOwner;
   1.109 +};
   1.110 + 
   1.111 +// Base class just prevents direct access to
   1.112 +// members to make sure we go through getters/setters.
   1.113 +class nsMutationReceiverBase : public nsStubMutationObserver
   1.114 +{
   1.115 +public:
   1.116 +  virtual ~nsMutationReceiverBase() { }
   1.117 +
   1.118 +  nsDOMMutationObserver* Observer();
   1.119 +  nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
   1.120 +  nsINode* RegisterTarget() { return mRegisterTarget; }
   1.121 +
   1.122 +  bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
   1.123 +  void SetSubtree(bool aSubtree)
   1.124 +  {
   1.125 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.126 +    mSubtree = aSubtree;
   1.127 +  }
   1.128 +
   1.129 +  bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
   1.130 +  void SetChildList(bool aChildList)
   1.131 +  {
   1.132 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.133 +    mChildList = aChildList;
   1.134 +  }
   1.135 +
   1.136 +  bool CharacterData()
   1.137 +  {
   1.138 +    return mParent ? mParent->CharacterData() : mCharacterData;
   1.139 +  }
   1.140 +  void SetCharacterData(bool aCharacterData)
   1.141 +  {
   1.142 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.143 +    mCharacterData = aCharacterData;
   1.144 +  }
   1.145 +
   1.146 +  bool CharacterDataOldValue()
   1.147 +  {
   1.148 +    return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
   1.149 +  }
   1.150 +  void SetCharacterDataOldValue(bool aOldValue)
   1.151 +  {
   1.152 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.153 +    mCharacterDataOldValue = aOldValue;
   1.154 +  }
   1.155 +
   1.156 +  bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
   1.157 +  void SetAttributes(bool aAttributes)
   1.158 +  {
   1.159 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.160 +    mAttributes = aAttributes;
   1.161 +  }
   1.162 +
   1.163 +  bool AllAttributes()
   1.164 +  {
   1.165 +    return mParent ? mParent->AllAttributes()
   1.166 +                   : mAllAttributes;
   1.167 +  }
   1.168 +  void SetAllAttributes(bool aAll)
   1.169 +  {
   1.170 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.171 +    mAllAttributes = aAll;
   1.172 +  }
   1.173 +
   1.174 +  bool AttributeOldValue() {
   1.175 +    return mParent ? mParent->AttributeOldValue()
   1.176 +                   : mAttributeOldValue;
   1.177 +  }
   1.178 +  void SetAttributeOldValue(bool aOldValue)
   1.179 +  {
   1.180 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.181 +    mAttributeOldValue = aOldValue;
   1.182 +  }
   1.183 +
   1.184 +  nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
   1.185 +  void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
   1.186 +  {
   1.187 +    NS_ASSERTION(!mParent, "Shouldn't have parent");
   1.188 +    mAttributeFilter.Clear();
   1.189 +    mAttributeFilter.AppendObjects(aFilter);
   1.190 +  }
   1.191 +
   1.192 +  void AddClone(nsMutationReceiverBase* aClone)
   1.193 +  {
   1.194 +    mTransientReceivers.AppendObject(aClone);
   1.195 +  }
   1.196 +
   1.197 +  void RemoveClone(nsMutationReceiverBase* aClone)
   1.198 +  {
   1.199 +    mTransientReceivers.RemoveObject(aClone);
   1.200 +  }
   1.201 +  
   1.202 +protected:
   1.203 +  nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
   1.204 +  : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
   1.205 +  {
   1.206 +    mRegisterTarget->AddMutationObserver(this);
   1.207 +    mRegisterTarget->SetMayHaveDOMMutationObserver();
   1.208 +    mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
   1.209 +  }
   1.210 +
   1.211 +  nsMutationReceiverBase(nsINode* aRegisterTarget,
   1.212 +                         nsMutationReceiverBase* aParent)
   1.213 +  : mTarget(nullptr), mObserver(nullptr), mParent(aParent),
   1.214 +    mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target())
   1.215 +  {
   1.216 +    NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
   1.217 +    mRegisterTarget->AddMutationObserver(this);
   1.218 +    mRegisterTarget->SetMayHaveDOMMutationObserver();
   1.219 +    mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
   1.220 +  }
   1.221 +
   1.222 +  bool ObservesAttr(mozilla::dom::Element* aElement,
   1.223 +                    int32_t aNameSpaceID,
   1.224 +                    nsIAtom* aAttr)
   1.225 +  {
   1.226 +    if (mParent) {
   1.227 +      return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
   1.228 +    }
   1.229 +    if (!Attributes() || (!Subtree() && aElement != Target())) {
   1.230 +      return false;
   1.231 +    }
   1.232 +    if (AllAttributes()) {
   1.233 +      return true;
   1.234 +    }
   1.235 +
   1.236 +    if (aNameSpaceID != kNameSpaceID_None) {
   1.237 +      return false;
   1.238 +    }
   1.239 +
   1.240 +    nsCOMArray<nsIAtom>& filters = AttributeFilter();
   1.241 +    for (int32_t i = 0; i < filters.Count(); ++i) {
   1.242 +      if (filters[i] == aAttr) {
   1.243 +        return true;
   1.244 +      }
   1.245 +    }
   1.246 +    return false;
   1.247 +  }
   1.248 +
   1.249 +  // The target for the MutationObserver.observe() method.
   1.250 +  nsINode*                           mTarget;
   1.251 +  nsDOMMutationObserver*             mObserver;
   1.252 +  nsRefPtr<nsMutationReceiverBase>   mParent; // Cleared after microtask.
   1.253 +  // The node to which Gecko-internal nsIMutationObserver was registered to.
   1.254 +  // This is different than mTarget when dealing with transient observers.
   1.255 +  nsINode*                           mRegisterTarget;
   1.256 +  nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
   1.257 +  // While we have transient receivers, keep the original mutation receiver
   1.258 +  // alive so it doesn't go away and disconnect all its transient receivers.
   1.259 +  nsCOMPtr<nsINode>                  mKungFuDeathGrip;
   1.260 +  
   1.261 +private:
   1.262 +  bool                               mSubtree;
   1.263 +  bool                               mChildList;
   1.264 +  bool                               mCharacterData;
   1.265 +  bool                               mCharacterDataOldValue;
   1.266 +  bool                               mAttributes;
   1.267 +  bool                               mAllAttributes;
   1.268 +  bool                               mAttributeOldValue;
   1.269 +  nsCOMArray<nsIAtom>                mAttributeFilter;
   1.270 +};
   1.271 +
   1.272 +
   1.273 +class nsMutationReceiver : public nsMutationReceiverBase
   1.274 +{
   1.275 +public:
   1.276 +  nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
   1.277 +
   1.278 +  nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
   1.279 +  : nsMutationReceiverBase(aRegisterTarget, aParent)
   1.280 +  {
   1.281 +    NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
   1.282 +                 "Shouldn't create deep observer hierarchies!");
   1.283 +    aParent->AddClone(this);
   1.284 +  }
   1.285 +
   1.286 +  virtual ~nsMutationReceiver() { Disconnect(false); }
   1.287 +
   1.288 +  nsMutationReceiver* GetParent()
   1.289 +  {
   1.290 +    return static_cast<nsMutationReceiver*>(mParent.get());
   1.291 +  }
   1.292 +
   1.293 +  void RemoveClones()
   1.294 +  {
   1.295 +    for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
   1.296 +      nsMutationReceiver* r =
   1.297 +        static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
   1.298 +      r->DisconnectTransientReceiver();
   1.299 +    }
   1.300 +    mTransientReceivers.Clear();
   1.301 +  }
   1.302 +
   1.303 +  void DisconnectTransientReceiver()
   1.304 +  {
   1.305 +    if (mRegisterTarget) {
   1.306 +      mRegisterTarget->RemoveMutationObserver(this);
   1.307 +      mRegisterTarget = nullptr;
   1.308 +    }
   1.309 +
   1.310 +    mParent = nullptr;
   1.311 +    NS_ASSERTION(!mTarget, "Should not have mTarget");
   1.312 +    NS_ASSERTION(!mObserver, "Should not have mObserver");
   1.313 +  }
   1.314 +
   1.315 +  void Disconnect(bool aRemoveFromObserver);
   1.316 +
   1.317 +  NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
   1.318 +  NS_DECL_ISUPPORTS
   1.319 +
   1.320 +  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   1.321 +  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
   1.322 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   1.323 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   1.324 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   1.325 +  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
   1.326 +
   1.327 +  virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
   1.328 +                                          mozilla::dom::Element* aElement,
   1.329 +                                          int32_t aNameSpaceID,
   1.330 +                                          nsIAtom* aAttribute) MOZ_OVERRIDE
   1.331 +  {
   1.332 +    // We can reuse AttributeWillChange implementation.
   1.333 +    AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
   1.334 +                        nsIDOMMutationEvent::MODIFICATION);
   1.335 +  }
   1.336 +};
   1.337 +
   1.338 +#define NS_DOM_MUTATION_OBSERVER_IID \
   1.339 +{ 0x0c3b91f8, 0xcc3b, 0x4b08, \
   1.340 +  { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
   1.341 +
   1.342 +class nsDOMMutationObserver : public nsISupports,
   1.343 +                              public nsWrapperCache
   1.344 +{
   1.345 +public:
   1.346 +  nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
   1.347 +                        mozilla::dom::MutationCallback& aCb)
   1.348 +  : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
   1.349 +    mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
   1.350 +  {
   1.351 +    SetIsDOMBinding();
   1.352 +  }
   1.353 +  virtual ~nsDOMMutationObserver();
   1.354 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   1.355 +  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
   1.356 +  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
   1.357 +
   1.358 +  static already_AddRefed<nsDOMMutationObserver>
   1.359 +  Constructor(const mozilla::dom::GlobalObject& aGlobal,
   1.360 +              mozilla::dom::MutationCallback& aCb,
   1.361 +              mozilla::ErrorResult& aRv);
   1.362 +
   1.363 +  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
   1.364 +  {
   1.365 +    return mozilla::dom::MutationObserverBinding::Wrap(aCx, this);
   1.366 +  }
   1.367 +
   1.368 +  nsISupports* GetParentObject() const
   1.369 +  {
   1.370 +    return mOwner;
   1.371 +  }
   1.372 +
   1.373 +  void Observe(nsINode& aTarget,
   1.374 +               const mozilla::dom::MutationObserverInit& aOptions,
   1.375 +               mozilla::ErrorResult& aRv);
   1.376 +
   1.377 +  void Disconnect();
   1.378 +
   1.379 +  void TakeRecords(nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal);
   1.380 +
   1.381 +  void HandleMutation();
   1.382 +
   1.383 +  void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult);
   1.384 +
   1.385 +  mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
   1.386 +
   1.387 +  void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
   1.388 +  {
   1.389 +    nsRefPtr<nsDOMMutationRecord> record = aRecord;
   1.390 +    MOZ_ASSERT(record);
   1.391 +    if (!mLastPendingMutation) {
   1.392 +      MOZ_ASSERT(!mFirstPendingMutation);
   1.393 +      mFirstPendingMutation = record.forget();
   1.394 +      mLastPendingMutation = mFirstPendingMutation;
   1.395 +    } else {
   1.396 +      MOZ_ASSERT(mFirstPendingMutation);
   1.397 +      mLastPendingMutation->mNext = record.forget();
   1.398 +      mLastPendingMutation = mLastPendingMutation->mNext;
   1.399 +    }
   1.400 +    ++mPendingMutationCount;
   1.401 +  }
   1.402 +
   1.403 +  void ClearPendingRecords()
   1.404 +  {
   1.405 +    mFirstPendingMutation = nullptr;
   1.406 +    mLastPendingMutation = nullptr;
   1.407 +    mPendingMutationCount = 0;
   1.408 +  }
   1.409 +
   1.410 +  // static methods
   1.411 +  static void HandleMutations()
   1.412 +  {
   1.413 +    if (sScheduledMutationObservers) {
   1.414 +      HandleMutationsInternal();
   1.415 +    }
   1.416 +  }
   1.417 +
   1.418 +  static void EnterMutationHandling();
   1.419 +  static void LeaveMutationHandling();
   1.420 +
   1.421 +  static void Shutdown();
   1.422 +protected:
   1.423 +  friend class nsMutationReceiver;
   1.424 +  friend class nsAutoMutationBatch;
   1.425 +  nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate);
   1.426 +  void RemoveReceiver(nsMutationReceiver* aReceiver);
   1.427 +
   1.428 +  already_AddRefed<nsIVariant> TakeRecords();
   1.429 +
   1.430 +  void GetAllSubtreeObserversFor(nsINode* aNode,
   1.431 +                                 nsTArray<nsMutationReceiver*>& aObservers);
   1.432 +  void ScheduleForRun();
   1.433 +  void RescheduleForRun();
   1.434 +
   1.435 +  nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
   1.436 +  bool HasCurrentRecord(const nsAString& aType);
   1.437 +
   1.438 +  bool Suppressed()
   1.439 +  {
   1.440 +    if (mOwner) {
   1.441 +      nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
   1.442 +      return d && d->IsInSyncOperation();
   1.443 +    }
   1.444 +    return false;
   1.445 +  }
   1.446 +
   1.447 +  static void HandleMutationsInternal();
   1.448 +
   1.449 +  static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
   1.450 +
   1.451 +  nsCOMPtr<nsPIDOMWindow>                            mOwner;
   1.452 +
   1.453 +  nsCOMArray<nsMutationReceiver>                     mReceivers;
   1.454 +  nsClassHashtable<nsISupportsHashKey,
   1.455 +                   nsCOMArray<nsMutationReceiver> >  mTransientReceivers;
   1.456 +  // MutationRecords which are being constructed.
   1.457 +  nsAutoTArray<nsDOMMutationRecord*, 4>              mCurrentMutations;
   1.458 +  // MutationRecords which will be handed to the callback at the end of
   1.459 +  // the microtask.
   1.460 +  nsRefPtr<nsDOMMutationRecord>                      mFirstPendingMutation;
   1.461 +  nsDOMMutationRecord*                               mLastPendingMutation;
   1.462 +  uint32_t                                           mPendingMutationCount;
   1.463 +
   1.464 +  nsRefPtr<mozilla::dom::MutationCallback>           mCallback;
   1.465 +
   1.466 +  bool                                               mWaitingForRun;
   1.467 +
   1.468 +  uint64_t                                           mId;
   1.469 +
   1.470 +  static uint64_t                                    sCount;
   1.471 +  static nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
   1.472 +  static nsDOMMutationObserver*                      sCurrentObserver;
   1.473 +
   1.474 +  static uint32_t                                    sMutationLevel;
   1.475 +  static nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
   1.476 +                                                     sCurrentlyHandlingObservers;
   1.477 +};
   1.478 +
   1.479 +NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
   1.480 +
   1.481 +class nsAutoMutationBatch
   1.482 +{
   1.483 +public:
   1.484 +  nsAutoMutationBatch()
   1.485 +  : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
   1.486 +    mFromFirstToLast(false), mAllowNestedBatches(false)    
   1.487 +  {
   1.488 +  }
   1.489 +
   1.490 +  nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
   1.491 +                      bool aAllowNestedBatches)
   1.492 +  : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
   1.493 +    mFromFirstToLast(false), mAllowNestedBatches(false)
   1.494 +  {
   1.495 +    Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
   1.496 +  }
   1.497 +
   1.498 +  void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
   1.499 +  {
   1.500 +    if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
   1.501 +      if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
   1.502 +        return;
   1.503 +      }
   1.504 +      mBatchTarget = aTarget;
   1.505 +      mFromFirstToLast = aFromFirstToLast;
   1.506 +      mAllowNestedBatches = aAllowNestedBatches;
   1.507 +      mPreviousBatch = sCurrentBatch;
   1.508 +      sCurrentBatch = this;
   1.509 +      nsDOMMutationObserver::EnterMutationHandling();
   1.510 +    }
   1.511 +  }
   1.512 +
   1.513 +  void RemovalDone() { mRemovalDone = true; }
   1.514 +  static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
   1.515 +
   1.516 +  void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
   1.517 +  void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
   1.518 +
   1.519 +  void Done();
   1.520 +
   1.521 +  ~nsAutoMutationBatch() { NodesAdded(); }
   1.522 +
   1.523 +  static bool IsBatching()
   1.524 +  {
   1.525 +    return !!sCurrentBatch;
   1.526 +  }
   1.527 +
   1.528 +  static nsAutoMutationBatch* GetCurrentBatch()
   1.529 +  {
   1.530 +    return sCurrentBatch;
   1.531 +  }
   1.532 +
   1.533 +  static void UpdateObserver(nsDOMMutationObserver* aObserver,
   1.534 +                             bool aWantsChildList)
   1.535 +  {
   1.536 +    uint32_t l = sCurrentBatch->mObservers.Length();
   1.537 +    for (uint32_t i = 0; i < l; ++i) {
   1.538 +      if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
   1.539 +        if (aWantsChildList) {
   1.540 +          sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
   1.541 +        } 
   1.542 +        return;
   1.543 +      }
   1.544 +    }
   1.545 +    BatchObserver* bo = sCurrentBatch->mObservers.AppendElement(); 
   1.546 +    bo->mObserver = aObserver;
   1.547 +    bo->mWantsChildList = aWantsChildList;
   1.548 +  }
   1.549 +
   1.550 +
   1.551 +  static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
   1.552 +
   1.553 +  // Mutation receivers notify the batch about removed child nodes.
   1.554 +  static void NodeRemoved(nsIContent* aChild)
   1.555 +  {
   1.556 +    if (IsBatching() && !sCurrentBatch->mRemovalDone) {
   1.557 +      uint32_t len = sCurrentBatch->mRemovedNodes.Length();
   1.558 +      if (!len ||
   1.559 +          sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
   1.560 +        sCurrentBatch->mRemovedNodes.AppendElement(aChild);
   1.561 +      }
   1.562 +    }
   1.563 +  }
   1.564 +
   1.565 +  // Called after new child nodes have been added to the batch target.
   1.566 +  void NodesAdded()
   1.567 +  {
   1.568 +    if (sCurrentBatch != this) {
   1.569 +      return;
   1.570 +    }
   1.571 +
   1.572 +    nsIContent* c =
   1.573 +      mPrevSibling ? mPrevSibling->GetNextSibling() :
   1.574 +                     mBatchTarget->GetFirstChild();
   1.575 +    for (; c != mNextSibling; c = c->GetNextSibling()) {
   1.576 +      mAddedNodes.AppendElement(c);
   1.577 +    }
   1.578 +    Done();
   1.579 +  }
   1.580 +
   1.581 +private:
   1.582 +  struct BatchObserver
   1.583 +  {
   1.584 +    nsDOMMutationObserver* mObserver;
   1.585 +    bool                   mWantsChildList;
   1.586 +  };
   1.587 +  
   1.588 +  static nsAutoMutationBatch* sCurrentBatch;
   1.589 +  nsAutoMutationBatch* mPreviousBatch;
   1.590 +  nsAutoTArray<BatchObserver, 2> mObservers;
   1.591 +  nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
   1.592 +  nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
   1.593 +  nsINode* mBatchTarget;
   1.594 +  bool mRemovalDone;
   1.595 +  bool mFromFirstToLast;
   1.596 +  bool mAllowNestedBatches;
   1.597 +  nsCOMPtr<nsINode> mPrevSibling;
   1.598 +  nsCOMPtr<nsINode> mNextSibling;
   1.599 +};
   1.600 +
   1.601 +inline
   1.602 +nsDOMMutationObserver*
   1.603 +nsMutationReceiverBase::Observer()
   1.604 +{
   1.605 +  return mParent ?
   1.606 +    mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
   1.607 +}
   1.608 +
   1.609 +#endif

mercurial