michael@0: /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */ michael@0: /* vim: set sw=4 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsDOMMutationObserver_h michael@0: #define nsDOMMutationObserver_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsStubMutationObserver.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsTArray.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsContentList.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsIDOMMutationEvent.h" michael@0: #include "nsWrapperCache.h" michael@0: #include "mozilla/dom/MutationObserverBinding.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: class nsDOMMutationObserver; michael@0: using mozilla::dom::MutationObservingInfo; michael@0: michael@0: class nsDOMMutationRecord : public nsISupports, michael@0: public nsWrapperCache michael@0: { michael@0: public: michael@0: nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner) michael@0: : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: virtual ~nsDOMMutationRecord() {} michael@0: michael@0: nsISupports* GetParentObject() const michael@0: { michael@0: return mOwner; michael@0: } michael@0: michael@0: virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE michael@0: { michael@0: return mozilla::dom::MutationRecordBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord) michael@0: michael@0: void GetType(mozilla::dom::DOMString& aRetVal) const michael@0: { michael@0: aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected); michael@0: } michael@0: michael@0: nsINode* GetTarget() const michael@0: { michael@0: return mTarget; michael@0: } michael@0: michael@0: nsINodeList* AddedNodes(); michael@0: michael@0: nsINodeList* RemovedNodes(); michael@0: michael@0: nsINode* GetPreviousSibling() const michael@0: { michael@0: return mPreviousSibling; michael@0: } michael@0: michael@0: nsINode* GetNextSibling() const michael@0: { michael@0: return mNextSibling; michael@0: } michael@0: michael@0: void GetAttributeName(mozilla::dom::DOMString& aRetVal) const michael@0: { michael@0: aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull); michael@0: } michael@0: michael@0: void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const michael@0: { michael@0: aRetVal.SetOwnedString(mAttrNamespace); michael@0: } michael@0: michael@0: void GetOldValue(mozilla::dom::DOMString& aRetVal) const michael@0: { michael@0: aRetVal.SetOwnedString(mPrevValue); michael@0: } michael@0: michael@0: nsCOMPtr mTarget; michael@0: nsCOMPtr mType; michael@0: nsCOMPtr mAttrName; michael@0: nsString mAttrNamespace; michael@0: nsString mPrevValue; michael@0: nsRefPtr mAddedNodes; michael@0: nsRefPtr mRemovedNodes; michael@0: nsCOMPtr mPreviousSibling; michael@0: nsCOMPtr mNextSibling; michael@0: michael@0: nsRefPtr mNext; michael@0: nsCOMPtr mOwner; michael@0: }; michael@0: michael@0: // Base class just prevents direct access to michael@0: // members to make sure we go through getters/setters. michael@0: class nsMutationReceiverBase : public nsStubMutationObserver michael@0: { michael@0: public: michael@0: virtual ~nsMutationReceiverBase() { } michael@0: michael@0: nsDOMMutationObserver* Observer(); michael@0: nsINode* Target() { return mParent ? mParent->Target() : mTarget; } michael@0: nsINode* RegisterTarget() { return mRegisterTarget; } michael@0: michael@0: bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; } michael@0: void SetSubtree(bool aSubtree) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mSubtree = aSubtree; michael@0: } michael@0: michael@0: bool ChildList() { return mParent ? mParent->ChildList() : mChildList; } michael@0: void SetChildList(bool aChildList) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mChildList = aChildList; michael@0: } michael@0: michael@0: bool CharacterData() michael@0: { michael@0: return mParent ? mParent->CharacterData() : mCharacterData; michael@0: } michael@0: void SetCharacterData(bool aCharacterData) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mCharacterData = aCharacterData; michael@0: } michael@0: michael@0: bool CharacterDataOldValue() michael@0: { michael@0: return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue; michael@0: } michael@0: void SetCharacterDataOldValue(bool aOldValue) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mCharacterDataOldValue = aOldValue; michael@0: } michael@0: michael@0: bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; } michael@0: void SetAttributes(bool aAttributes) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mAttributes = aAttributes; michael@0: } michael@0: michael@0: bool AllAttributes() michael@0: { michael@0: return mParent ? mParent->AllAttributes() michael@0: : mAllAttributes; michael@0: } michael@0: void SetAllAttributes(bool aAll) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mAllAttributes = aAll; michael@0: } michael@0: michael@0: bool AttributeOldValue() { michael@0: return mParent ? mParent->AttributeOldValue() michael@0: : mAttributeOldValue; michael@0: } michael@0: void SetAttributeOldValue(bool aOldValue) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mAttributeOldValue = aOldValue; michael@0: } michael@0: michael@0: nsCOMArray& AttributeFilter() { return mAttributeFilter; } michael@0: void SetAttributeFilter(nsCOMArray& aFilter) michael@0: { michael@0: NS_ASSERTION(!mParent, "Shouldn't have parent"); michael@0: mAttributeFilter.Clear(); michael@0: mAttributeFilter.AppendObjects(aFilter); michael@0: } michael@0: michael@0: void AddClone(nsMutationReceiverBase* aClone) michael@0: { michael@0: mTransientReceivers.AppendObject(aClone); michael@0: } michael@0: michael@0: void RemoveClone(nsMutationReceiverBase* aClone) michael@0: { michael@0: mTransientReceivers.RemoveObject(aClone); michael@0: } michael@0: michael@0: protected: michael@0: nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver) michael@0: : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget) michael@0: { michael@0: mRegisterTarget->AddMutationObserver(this); michael@0: mRegisterTarget->SetMayHaveDOMMutationObserver(); michael@0: mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers(); michael@0: } michael@0: michael@0: nsMutationReceiverBase(nsINode* aRegisterTarget, michael@0: nsMutationReceiverBase* aParent) michael@0: : mTarget(nullptr), mObserver(nullptr), mParent(aParent), michael@0: mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target()) michael@0: { michael@0: NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!"); michael@0: mRegisterTarget->AddMutationObserver(this); michael@0: mRegisterTarget->SetMayHaveDOMMutationObserver(); michael@0: mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers(); michael@0: } michael@0: michael@0: bool ObservesAttr(mozilla::dom::Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttr) michael@0: { michael@0: if (mParent) { michael@0: return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr); michael@0: } michael@0: if (!Attributes() || (!Subtree() && aElement != Target())) { michael@0: return false; michael@0: } michael@0: if (AllAttributes()) { michael@0: return true; michael@0: } michael@0: michael@0: if (aNameSpaceID != kNameSpaceID_None) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMArray& filters = AttributeFilter(); michael@0: for (int32_t i = 0; i < filters.Count(); ++i) { michael@0: if (filters[i] == aAttr) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // The target for the MutationObserver.observe() method. michael@0: nsINode* mTarget; michael@0: nsDOMMutationObserver* mObserver; michael@0: nsRefPtr mParent; // Cleared after microtask. michael@0: // The node to which Gecko-internal nsIMutationObserver was registered to. michael@0: // This is different than mTarget when dealing with transient observers. michael@0: nsINode* mRegisterTarget; michael@0: nsCOMArray mTransientReceivers; michael@0: // While we have transient receivers, keep the original mutation receiver michael@0: // alive so it doesn't go away and disconnect all its transient receivers. michael@0: nsCOMPtr mKungFuDeathGrip; michael@0: michael@0: private: michael@0: bool mSubtree; michael@0: bool mChildList; michael@0: bool mCharacterData; michael@0: bool mCharacterDataOldValue; michael@0: bool mAttributes; michael@0: bool mAllAttributes; michael@0: bool mAttributeOldValue; michael@0: nsCOMArray mAttributeFilter; michael@0: }; michael@0: michael@0: michael@0: class nsMutationReceiver : public nsMutationReceiverBase michael@0: { michael@0: public: michael@0: nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver); michael@0: michael@0: nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent) michael@0: : nsMutationReceiverBase(aRegisterTarget, aParent) michael@0: { michael@0: NS_ASSERTION(!static_cast(aParent)->GetParent(), michael@0: "Shouldn't create deep observer hierarchies!"); michael@0: aParent->AddClone(this); michael@0: } michael@0: michael@0: virtual ~nsMutationReceiver() { Disconnect(false); } michael@0: michael@0: nsMutationReceiver* GetParent() michael@0: { michael@0: return static_cast(mParent.get()); michael@0: } michael@0: michael@0: void RemoveClones() michael@0: { michael@0: for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) { michael@0: nsMutationReceiver* r = michael@0: static_cast(mTransientReceivers[i]); michael@0: r->DisconnectTransientReceiver(); michael@0: } michael@0: mTransientReceivers.Clear(); michael@0: } michael@0: michael@0: void DisconnectTransientReceiver() michael@0: { michael@0: if (mRegisterTarget) { michael@0: mRegisterTarget->RemoveMutationObserver(this); michael@0: mRegisterTarget = nullptr; michael@0: } michael@0: michael@0: mParent = nullptr; michael@0: NS_ASSERTION(!mTarget, "Should not have mTarget"); michael@0: NS_ASSERTION(!mObserver, "Should not have mObserver"); michael@0: } michael@0: michael@0: void Disconnect(bool aRemoveFromObserver); michael@0: michael@0: NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE michael@0: NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE michael@0: NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED michael@0: NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED michael@0: NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED michael@0: NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED michael@0: michael@0: virtual void AttributeSetToCurrentValue(nsIDocument* aDocument, michael@0: mozilla::dom::Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute) MOZ_OVERRIDE michael@0: { michael@0: // We can reuse AttributeWillChange implementation. michael@0: AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute, michael@0: nsIDOMMutationEvent::MODIFICATION); michael@0: } michael@0: }; michael@0: michael@0: #define NS_DOM_MUTATION_OBSERVER_IID \ michael@0: { 0x0c3b91f8, 0xcc3b, 0x4b08, \ michael@0: { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } } michael@0: michael@0: class nsDOMMutationObserver : public nsISupports, michael@0: public nsWrapperCache michael@0: { michael@0: public: michael@0: nsDOMMutationObserver(already_AddRefed&& aOwner, michael@0: mozilla::dom::MutationCallback& aCb) michael@0: : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0), michael@0: mCallback(&aCb), mWaitingForRun(false), mId(++sCount) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: virtual ~nsDOMMutationObserver(); michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver) michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID) michael@0: michael@0: static already_AddRefed michael@0: Constructor(const mozilla::dom::GlobalObject& aGlobal, michael@0: mozilla::dom::MutationCallback& aCb, michael@0: mozilla::ErrorResult& aRv); michael@0: michael@0: virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE michael@0: { michael@0: return mozilla::dom::MutationObserverBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: nsISupports* GetParentObject() const michael@0: { michael@0: return mOwner; michael@0: } michael@0: michael@0: void Observe(nsINode& aTarget, michael@0: const mozilla::dom::MutationObserverInit& aOptions, michael@0: mozilla::ErrorResult& aRv); michael@0: michael@0: void Disconnect(); michael@0: michael@0: void TakeRecords(nsTArray >& aRetVal); michael@0: michael@0: void HandleMutation(); michael@0: michael@0: void GetObservingInfo(nsTArray >& aResult); michael@0: michael@0: mozilla::dom::MutationCallback* MutationCallback() { return mCallback; } michael@0: michael@0: void AppendMutationRecord(already_AddRefed aRecord) michael@0: { michael@0: nsRefPtr record = aRecord; michael@0: MOZ_ASSERT(record); michael@0: if (!mLastPendingMutation) { michael@0: MOZ_ASSERT(!mFirstPendingMutation); michael@0: mFirstPendingMutation = record.forget(); michael@0: mLastPendingMutation = mFirstPendingMutation; michael@0: } else { michael@0: MOZ_ASSERT(mFirstPendingMutation); michael@0: mLastPendingMutation->mNext = record.forget(); michael@0: mLastPendingMutation = mLastPendingMutation->mNext; michael@0: } michael@0: ++mPendingMutationCount; michael@0: } michael@0: michael@0: void ClearPendingRecords() michael@0: { michael@0: mFirstPendingMutation = nullptr; michael@0: mLastPendingMutation = nullptr; michael@0: mPendingMutationCount = 0; michael@0: } michael@0: michael@0: // static methods michael@0: static void HandleMutations() michael@0: { michael@0: if (sScheduledMutationObservers) { michael@0: HandleMutationsInternal(); michael@0: } michael@0: } michael@0: michael@0: static void EnterMutationHandling(); michael@0: static void LeaveMutationHandling(); michael@0: michael@0: static void Shutdown(); michael@0: protected: michael@0: friend class nsMutationReceiver; michael@0: friend class nsAutoMutationBatch; michael@0: nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate); michael@0: void RemoveReceiver(nsMutationReceiver* aReceiver); michael@0: michael@0: already_AddRefed TakeRecords(); michael@0: michael@0: void GetAllSubtreeObserversFor(nsINode* aNode, michael@0: nsTArray& aObservers); michael@0: void ScheduleForRun(); michael@0: void RescheduleForRun(); michael@0: michael@0: nsDOMMutationRecord* CurrentRecord(nsIAtom* aType); michael@0: bool HasCurrentRecord(const nsAString& aType); michael@0: michael@0: bool Suppressed() michael@0: { michael@0: if (mOwner) { michael@0: nsCOMPtr d = mOwner->GetExtantDoc(); michael@0: return d && d->IsInSyncOperation(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static void HandleMutationsInternal(); michael@0: michael@0: static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver); michael@0: michael@0: nsCOMPtr mOwner; michael@0: michael@0: nsCOMArray mReceivers; michael@0: nsClassHashtable > mTransientReceivers; michael@0: // MutationRecords which are being constructed. michael@0: nsAutoTArray mCurrentMutations; michael@0: // MutationRecords which will be handed to the callback at the end of michael@0: // the microtask. michael@0: nsRefPtr mFirstPendingMutation; michael@0: nsDOMMutationRecord* mLastPendingMutation; michael@0: uint32_t mPendingMutationCount; michael@0: michael@0: nsRefPtr mCallback; michael@0: michael@0: bool mWaitingForRun; michael@0: michael@0: uint64_t mId; michael@0: michael@0: static uint64_t sCount; michael@0: static nsAutoTArray, 4>* sScheduledMutationObservers; michael@0: static nsDOMMutationObserver* sCurrentObserver; michael@0: michael@0: static uint32_t sMutationLevel; michael@0: static nsAutoTArray, 4>, 4>* michael@0: sCurrentlyHandlingObservers; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID) michael@0: michael@0: class nsAutoMutationBatch michael@0: { michael@0: public: michael@0: nsAutoMutationBatch() michael@0: : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false), michael@0: mFromFirstToLast(false), mAllowNestedBatches(false) michael@0: { michael@0: } michael@0: michael@0: nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast, michael@0: bool aAllowNestedBatches) michael@0: : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false), michael@0: mFromFirstToLast(false), mAllowNestedBatches(false) michael@0: { michael@0: Init(aTarget, aFromFirstToLast, aAllowNestedBatches); michael@0: } michael@0: michael@0: void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches) michael@0: { michael@0: if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) { michael@0: if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) { michael@0: return; michael@0: } michael@0: mBatchTarget = aTarget; michael@0: mFromFirstToLast = aFromFirstToLast; michael@0: mAllowNestedBatches = aAllowNestedBatches; michael@0: mPreviousBatch = sCurrentBatch; michael@0: sCurrentBatch = this; michael@0: nsDOMMutationObserver::EnterMutationHandling(); michael@0: } michael@0: } michael@0: michael@0: void RemovalDone() { mRemovalDone = true; } michael@0: static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; } michael@0: michael@0: void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; } michael@0: void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; } michael@0: michael@0: void Done(); michael@0: michael@0: ~nsAutoMutationBatch() { NodesAdded(); } michael@0: michael@0: static bool IsBatching() michael@0: { michael@0: return !!sCurrentBatch; michael@0: } michael@0: michael@0: static nsAutoMutationBatch* GetCurrentBatch() michael@0: { michael@0: return sCurrentBatch; michael@0: } michael@0: michael@0: static void UpdateObserver(nsDOMMutationObserver* aObserver, michael@0: bool aWantsChildList) michael@0: { michael@0: uint32_t l = sCurrentBatch->mObservers.Length(); michael@0: for (uint32_t i = 0; i < l; ++i) { michael@0: if (sCurrentBatch->mObservers[i].mObserver == aObserver) { michael@0: if (aWantsChildList) { michael@0: sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList; michael@0: } michael@0: return; michael@0: } michael@0: } michael@0: BatchObserver* bo = sCurrentBatch->mObservers.AppendElement(); michael@0: bo->mObserver = aObserver; michael@0: bo->mWantsChildList = aWantsChildList; michael@0: } michael@0: michael@0: michael@0: static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; } michael@0: michael@0: // Mutation receivers notify the batch about removed child nodes. michael@0: static void NodeRemoved(nsIContent* aChild) michael@0: { michael@0: if (IsBatching() && !sCurrentBatch->mRemovalDone) { michael@0: uint32_t len = sCurrentBatch->mRemovedNodes.Length(); michael@0: if (!len || michael@0: sCurrentBatch->mRemovedNodes[len - 1] != aChild) { michael@0: sCurrentBatch->mRemovedNodes.AppendElement(aChild); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Called after new child nodes have been added to the batch target. michael@0: void NodesAdded() michael@0: { michael@0: if (sCurrentBatch != this) { michael@0: return; michael@0: } michael@0: michael@0: nsIContent* c = michael@0: mPrevSibling ? mPrevSibling->GetNextSibling() : michael@0: mBatchTarget->GetFirstChild(); michael@0: for (; c != mNextSibling; c = c->GetNextSibling()) { michael@0: mAddedNodes.AppendElement(c); michael@0: } michael@0: Done(); michael@0: } michael@0: michael@0: private: michael@0: struct BatchObserver michael@0: { michael@0: nsDOMMutationObserver* mObserver; michael@0: bool mWantsChildList; michael@0: }; michael@0: michael@0: static nsAutoMutationBatch* sCurrentBatch; michael@0: nsAutoMutationBatch* mPreviousBatch; michael@0: nsAutoTArray mObservers; michael@0: nsTArray > mRemovedNodes; michael@0: nsTArray > mAddedNodes; michael@0: nsINode* mBatchTarget; michael@0: bool mRemovalDone; michael@0: bool mFromFirstToLast; michael@0: bool mAllowNestedBatches; michael@0: nsCOMPtr mPrevSibling; michael@0: nsCOMPtr mNextSibling; michael@0: }; michael@0: michael@0: inline michael@0: nsDOMMutationObserver* michael@0: nsMutationReceiverBase::Observer() michael@0: { michael@0: return mParent ? michael@0: mParent->Observer() : static_cast(mObserver); michael@0: } michael@0: michael@0: #endif