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