Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsDOMMutationObserver.h
blobc49b0afca6a7df174d6198f78906ed3735dd696f
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 MOZ_FINAL : public nsISupports,
32 public nsWrapperCache
34 virtual ~nsDOMMutationRecord() {}
36 public:
37 nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
38 : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
42 nsISupports* GetParentObject() const
44 return mOwner;
47 virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
49 return mozilla::dom::MutationRecordBinding::Wrap(aCx, this);
52 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
53 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
55 void GetType(mozilla::dom::DOMString& aRetVal) const
57 aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
60 nsINode* GetTarget() const
62 return mTarget;
65 nsINodeList* AddedNodes();
67 nsINodeList* RemovedNodes();
69 nsINode* GetPreviousSibling() const
71 return mPreviousSibling;
74 nsINode* GetNextSibling() const
76 return mNextSibling;
79 void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
81 aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
84 void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
86 aRetVal.SetOwnedString(mAttrNamespace);
89 void GetOldValue(mozilla::dom::DOMString& aRetVal) const
91 aRetVal.SetOwnedString(mPrevValue);
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;
108 // Base class just prevents direct access to
109 // members to make sure we go through getters/setters.
110 class nsMutationReceiverBase : public nsStubMutationObserver
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)
122 NS_ASSERTION(!mParent, "Shouldn't have parent");
123 mSubtree = aSubtree;
126 bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
127 void SetChildList(bool aChildList)
129 NS_ASSERTION(!mParent, "Shouldn't have parent");
130 mChildList = aChildList;
133 bool CharacterData()
135 return mParent ? mParent->CharacterData() : mCharacterData;
137 void SetCharacterData(bool aCharacterData)
139 NS_ASSERTION(!mParent, "Shouldn't have parent");
140 mCharacterData = aCharacterData;
143 bool CharacterDataOldValue()
145 return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
147 void SetCharacterDataOldValue(bool aOldValue)
149 NS_ASSERTION(!mParent, "Shouldn't have parent");
150 mCharacterDataOldValue = aOldValue;
153 bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
154 void SetAttributes(bool aAttributes)
156 NS_ASSERTION(!mParent, "Shouldn't have parent");
157 mAttributes = aAttributes;
160 bool AllAttributes()
162 return mParent ? mParent->AllAttributes()
163 : mAllAttributes;
165 void SetAllAttributes(bool aAll)
167 NS_ASSERTION(!mParent, "Shouldn't have parent");
168 mAllAttributes = aAll;
171 bool AttributeOldValue() {
172 return mParent ? mParent->AttributeOldValue()
173 : mAttributeOldValue;
175 void SetAttributeOldValue(bool aOldValue)
177 NS_ASSERTION(!mParent, "Shouldn't have parent");
178 mAttributeOldValue = aOldValue;
181 nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
182 void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
184 NS_ASSERTION(!mParent, "Shouldn't have parent");
185 mAttributeFilter.Clear();
186 mAttributeFilter.AppendObjects(aFilter);
189 void AddClone(nsMutationReceiverBase* aClone)
191 mTransientReceivers.AppendObject(aClone);
194 void RemoveClone(nsMutationReceiverBase* aClone)
196 mTransientReceivers.RemoveObject(aClone);
199 protected:
200 nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
201 : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
203 mRegisterTarget->AddMutationObserver(this);
204 mRegisterTarget->SetMayHaveDOMMutationObserver();
205 mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
208 nsMutationReceiverBase(nsINode* aRegisterTarget,
209 nsMutationReceiverBase* aParent)
210 : mTarget(nullptr), mObserver(nullptr), mParent(aParent),
211 mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target())
213 NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
214 mRegisterTarget->AddMutationObserver(this);
215 mRegisterTarget->SetMayHaveDOMMutationObserver();
216 mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
219 bool ObservesAttr(mozilla::dom::Element* aElement,
220 int32_t aNameSpaceID,
221 nsIAtom* aAttr)
223 if (mParent) {
224 return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
226 if (!Attributes() || (!Subtree() && aElement != Target())) {
227 return false;
229 if (AllAttributes()) {
230 return true;
233 if (aNameSpaceID != kNameSpaceID_None) {
234 return false;
237 nsCOMArray<nsIAtom>& filters = AttributeFilter();
238 for (int32_t i = 0; i < filters.Count(); ++i) {
239 if (filters[i] == aAttr) {
240 return true;
243 return false;
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;
270 class nsMutationReceiver : public nsMutationReceiverBase
272 protected:
273 virtual ~nsMutationReceiver() { Disconnect(false); }
275 public:
276 nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
278 nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
279 : nsMutationReceiverBase(aRegisterTarget, aParent)
281 NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
282 "Shouldn't create deep observer hierarchies!");
283 aParent->AddClone(this);
286 nsMutationReceiver* GetParent()
288 return static_cast<nsMutationReceiver*>(mParent.get());
291 void RemoveClones()
293 for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
294 nsMutationReceiver* r =
295 static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
296 r->DisconnectTransientReceiver();
298 mTransientReceivers.Clear();
301 void DisconnectTransientReceiver()
303 if (mRegisterTarget) {
304 mRegisterTarget->RemoveMutationObserver(this);
305 mRegisterTarget = nullptr;
308 mParent = nullptr;
309 NS_ASSERTION(!mTarget, "Should not have mTarget");
310 NS_ASSERTION(!mObserver, "Should not have mObserver");
313 void Disconnect(bool aRemoveFromObserver);
315 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
316 NS_DECL_ISUPPORTS
318 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
319 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
320 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
321 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
322 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
323 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
325 virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
326 mozilla::dom::Element* aElement,
327 int32_t aNameSpaceID,
328 nsIAtom* aAttribute) MOZ_OVERRIDE
330 // We can reuse AttributeWillChange implementation.
331 AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
332 nsIDOMMutationEvent::MODIFICATION);
336 #define NS_DOM_MUTATION_OBSERVER_IID \
337 { 0x0c3b91f8, 0xcc3b, 0x4b08, \
338 { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
340 class nsDOMMutationObserver MOZ_FINAL : public nsISupports,
341 public nsWrapperCache
343 public:
344 nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
345 mozilla::dom::MutationCallback& aCb)
346 : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
347 mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
350 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
351 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
352 NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
354 static already_AddRefed<nsDOMMutationObserver>
355 Constructor(const mozilla::dom::GlobalObject& aGlobal,
356 mozilla::dom::MutationCallback& aCb,
357 mozilla::ErrorResult& aRv);
359 virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
361 return mozilla::dom::MutationObserverBinding::Wrap(aCx, this);
364 nsISupports* GetParentObject() const
366 return mOwner;
369 void Observe(nsINode& aTarget,
370 const mozilla::dom::MutationObserverInit& aOptions,
371 mozilla::ErrorResult& aRv);
373 void Disconnect();
375 void TakeRecords(nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal);
377 void HandleMutation();
379 void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult);
381 mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
383 void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
385 nsRefPtr<nsDOMMutationRecord> record = aRecord;
386 MOZ_ASSERT(record);
387 if (!mLastPendingMutation) {
388 MOZ_ASSERT(!mFirstPendingMutation);
389 mFirstPendingMutation = record.forget();
390 mLastPendingMutation = mFirstPendingMutation;
391 } else {
392 MOZ_ASSERT(mFirstPendingMutation);
393 mLastPendingMutation->mNext = record.forget();
394 mLastPendingMutation = mLastPendingMutation->mNext;
396 ++mPendingMutationCount;
399 void ClearPendingRecords()
401 mFirstPendingMutation = nullptr;
402 mLastPendingMutation = nullptr;
403 mPendingMutationCount = 0;
406 // static methods
407 static void HandleMutations()
409 if (sScheduledMutationObservers) {
410 HandleMutationsInternal();
414 static void EnterMutationHandling();
415 static void LeaveMutationHandling();
417 static void Shutdown();
418 protected:
419 virtual ~nsDOMMutationObserver();
421 friend class nsMutationReceiver;
422 friend class nsAutoMutationBatch;
423 nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate);
424 void RemoveReceiver(nsMutationReceiver* aReceiver);
426 already_AddRefed<nsIVariant> TakeRecords();
428 void GetAllSubtreeObserversFor(nsINode* aNode,
429 nsTArray<nsMutationReceiver*>& aObservers);
430 void ScheduleForRun();
431 void RescheduleForRun();
433 nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
434 bool HasCurrentRecord(const nsAString& aType);
436 bool Suppressed()
438 if (mOwner) {
439 nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
440 return d && d->IsInSyncOperation();
442 return false;
445 static void HandleMutationsInternal();
447 static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
449 nsCOMPtr<nsPIDOMWindow> mOwner;
451 nsCOMArray<nsMutationReceiver> mReceivers;
452 nsClassHashtable<nsISupportsHashKey,
453 nsCOMArray<nsMutationReceiver> > mTransientReceivers;
454 // MutationRecords which are being constructed.
455 nsAutoTArray<nsDOMMutationRecord*, 4> mCurrentMutations;
456 // MutationRecords which will be handed to the callback at the end of
457 // the microtask.
458 nsRefPtr<nsDOMMutationRecord> mFirstPendingMutation;
459 nsDOMMutationRecord* mLastPendingMutation;
460 uint32_t mPendingMutationCount;
462 nsRefPtr<mozilla::dom::MutationCallback> mCallback;
464 bool mWaitingForRun;
466 uint64_t mId;
468 static uint64_t sCount;
469 static nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
470 static nsDOMMutationObserver* sCurrentObserver;
472 static uint32_t sMutationLevel;
473 static nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
474 sCurrentlyHandlingObservers;
477 NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
479 class nsAutoMutationBatch
481 public:
482 nsAutoMutationBatch()
483 : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
484 mFromFirstToLast(false), mAllowNestedBatches(false)
488 nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
489 bool aAllowNestedBatches)
490 : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
491 mFromFirstToLast(false), mAllowNestedBatches(false)
493 Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
496 void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
498 if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
499 if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
500 return;
502 mBatchTarget = aTarget;
503 mFromFirstToLast = aFromFirstToLast;
504 mAllowNestedBatches = aAllowNestedBatches;
505 mPreviousBatch = sCurrentBatch;
506 sCurrentBatch = this;
507 nsDOMMutationObserver::EnterMutationHandling();
511 void RemovalDone() { mRemovalDone = true; }
512 static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
514 void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
515 void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
517 void Done();
519 ~nsAutoMutationBatch() { NodesAdded(); }
521 static bool IsBatching()
523 return !!sCurrentBatch;
526 static nsAutoMutationBatch* GetCurrentBatch()
528 return sCurrentBatch;
531 static void UpdateObserver(nsDOMMutationObserver* aObserver,
532 bool aWantsChildList)
534 uint32_t l = sCurrentBatch->mObservers.Length();
535 for (uint32_t i = 0; i < l; ++i) {
536 if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
537 if (aWantsChildList) {
538 sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
540 return;
543 BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
544 bo->mObserver = aObserver;
545 bo->mWantsChildList = aWantsChildList;
549 static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
551 // Mutation receivers notify the batch about removed child nodes.
552 static void NodeRemoved(nsIContent* aChild)
554 if (IsBatching() && !sCurrentBatch->mRemovalDone) {
555 uint32_t len = sCurrentBatch->mRemovedNodes.Length();
556 if (!len ||
557 sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
558 sCurrentBatch->mRemovedNodes.AppendElement(aChild);
563 // Called after new child nodes have been added to the batch target.
564 void NodesAdded()
566 if (sCurrentBatch != this) {
567 return;
570 nsIContent* c =
571 mPrevSibling ? mPrevSibling->GetNextSibling() :
572 mBatchTarget->GetFirstChild();
573 for (; c != mNextSibling; c = c->GetNextSibling()) {
574 mAddedNodes.AppendElement(c);
576 Done();
579 private:
580 struct BatchObserver
582 nsDOMMutationObserver* mObserver;
583 bool mWantsChildList;
586 static nsAutoMutationBatch* sCurrentBatch;
587 nsAutoMutationBatch* mPreviousBatch;
588 nsAutoTArray<BatchObserver, 2> mObservers;
589 nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
590 nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
591 nsINode* mBatchTarget;
592 bool mRemovalDone;
593 bool mFromFirstToLast;
594 bool mAllowNestedBatches;
595 nsCOMPtr<nsINode> mPrevSibling;
596 nsCOMPtr<nsINode> mNextSibling;
599 inline
600 nsDOMMutationObserver*
601 nsMutationReceiverBase::Observer()
603 return mParent ?
604 mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
607 #endif