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.)

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

mercurial