Files
palemoon27/dom/base/nsDOMMutationObserver.h
T
roytam1 5e85317f94 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1214048 - Improve callee-not-a-function error for spread calls. (r=jorendorff) (f47489e821)
- Bug 1202561 - Mark no-longer-used constants in jsversion.h. r=jorendorff. (dd9a29e777)
- Bug 995610 - Part 0: Add release property to getBuildConfiguration(). r=evilpie (9aff95b34a)
- Bug 995610 - Part 1: Show deprecated warning for expression closure. r=evilpie (740af95e85)
- Bug 1167029 - Followup: remove now-unused DeprecatedLetBlock telemetry. (rs=efaust) (dc3293d70f)
- Bug 1201740 - Prevent interception if the request is a System XHR. r=bkelly (eea213dcc2)
- Bug 1182571: Make nsXMLHttpRequest use AsyncOpen2. r=ehsan (15089bb64b)
- Back out bug 1149127 because of Thunderbird insanity (2047b66990)
- Bug 1187404 - Allow tab to select an option from a select element in e10s r=Enn (e2c98e7c0f)
- Mac v2 signing - Bug 1047738 - Make distribution code look for the distribution directory under Contents/Resources due to v2 signing requirements. r=bsmedberg # Please enter the commit message for your changes. Lines starting (4d6f19f952)
- Bug 1176703 - load default theme manifest in safe mode, r=bsmedberg (374f8f6ca3)
- misc path and import fixes (4547430310)
- bug 1174704 - Implement GetUserDataDirectoryHome for iOS. r=froydnj (886abeed30)
- Bug 1177819. Replace the warning about anon content being passed into frame construction with an assert plus whitelisting of the known-OK cases. r=dholbert (9d9354f0af)
- Bug 1034110 - Provide a way to observe mutations for ::before/::after pseudo elements;r=smaug (48560d44b6)
- Bug 1205635: Don't define methods of Bluetooth backend interfaces in header, r=btian (a30773e87e)
- Bug 1181482 - Patch1: Refine some data types in gecko backend for GATT server read/write request APIs. r=btian (12921ff365)
- Bug 1181479: Refine StringToUuid; r=jocelyn (68ca54278a)
- Bug 1181479: Refine GenerateUuid(); r=jocelyn (dd91af86c9)
- Bug 1140952 - Implement read/write value of a descriptor for GATT client API (webapi part). f=jocelyn, r=btian, r=mrbkap (276d63e128)
- misspatch (87e07ca21f)
- Bug 1181479: Implement GATT Server service management; r=jocelyn, r=mrbkap (1abb22088d)
- Bug 1161939 - Correct bitwise checking on GATT characteristic write properties. r=jocelyn (fd0c6f1358)
- Bug 1181482 - Patch2: Revise read/write characteristic/descriptor value to cover both GATT client and GATT server role. r=btian, r=bz (658efbd941)
- Bug 1181479: Sync the coding style; r=jocelyn (ab0f1509c1)
- Bug 1181482 - Patch3: Implement |sendResponse| and BluetoothGattAttributeEvent for GATT server read/write requests. r=btian, r=bz (c50c93a6a3)
- Bug 1211140 - Remove telemetry for RegExpSourceProperty. r=till (faa512402e)
- Bug 1208835 - Add telemetry for RegExp static property access after String.prototype.replace with function argument and RegExp static property is changed inside it. r=till,bsmedberg (c412b13e4c)
- Bug 683218 - Remove non-standard __noSuchMethod__ feature. r=efaust (a3a567d797)
- Bug 1201460: Disallow asm.js compilation for class/methods; r=luke (963644c32f)
- Bug 1214013 - Remove drainGlobalOrEvalBindings and use generateBindings for all kinds of scripts. (r=efaust) (338778ce73)
- Bug 1214050 - Don't give overwritten non-deoptimized function bindings slots in global scripts. (r=efaust) (3e95d0ba46)
- Bug 1214013 - Parse global scripts non-incrementally. (r=efaust) (33a378413b)
- Bug 1214013 - Remove funky Maybe<ParseContext> logic in BytecodeCompiler. (r=efaust) (0f02d0e3da)
- Bug 1212719 - Throw SyntaxError immediately for unexpected TOK_TRIPLEDOT. r=Waldo (769ab2403d)
- Bug 1218204 - Remove else after return from Parser<ParseHandler>::maybeParseDirective. r=arai (519b748a63)
- Bug 1216966 - Part 1: Splurge and use separate error messages for generator comprehensions that need more parentheses and yield expressions that need more parentheses. r=efaust. (fcb6b0a5d2)
- Bug 1216966 - Part 2: Tweak a comment about comprehension syntax. r=efaust. (59670db722)
- Bug 1216966 - Part 3: Remove redundant method Parser::parenExprOrGeneratorComprehension. r=efaust. (e2a5d76e35)
- Bug 1216966 - Part 4: Update a big comment about for-loops in Parser.cpp. r=efaust. (ac28e492db)
- Bug 1216966 - Part 5: Code organization and comments around parsing comprehensions. r=efaust. (e5533b379b)
- Bug 1216623 - Part 1: Rename some loop variables to avoid conflicts with ES6 scoping rules. r=fitzgen, r=ttaubert, r=MattN, r=gps. (daf7d973b0)
- Bug 932517 - Treat let as a contextual keyword in sloppy mode and make it versionless. (r=jorendorff) (c95a8c115f)
- align to TFF (e27ad0cc42)
- Bug 1217001 - Refactor BytecodeEmitter::variables. Part 1: preliminaries. r=shu. (dad313be3e)
- Bug 1217001 - Part 2: Rename two local variables and improve some old comments. r=shu. (8f050c8d34)
- Bug 1217001 - Part 3: Remove one goto statement. r=shu. (6ff8f4d4de)
- Bug 1217001 - Part 4: Improve the comments on VarEmitOption. r=shu. (6396585f04)
- Bug 1217001 - Part 5: Further revise control structure in BytecodeEmitter::emitVariables(). r=shu. (e671d81923)
- Bug 1217001 - Part 6: Eliminate some continue statements. r=shu. (80a7767e89)
- Bug 1217001 - Part 7: Change BytecodeEmitter::emitNormalFor() to decouple it from weird expectations about BytecodeEmitter::emitVariables(). r=shu. (557a9249a8)
- Bug 1217001 - Part 8: Eliminate all uses of PNX_POPVAR. r=shu. (c9c27b5072)
- Bug 1217001 - Part 9: Remove PNX_POPVAR. r=shu. (012e716653)
- Bug 1217001 - Part 10: Delete redundant boolean argument. r=shu. (5f528ed198)
- Bug 1217001 - Part 11: Get rid of the last goto in BytecodeEmitter::emitVariables(). r=shu. (0d11883f9e)
- Fix a broken JS test. It landed in rev 093802a6d8ae (bug 1003554) and was apparently fine until it was merged to m-c/m-i, where it probably collided with rev bug 1217099 or bug 1217001. no_r=bustage to a CLOSED TREE. (12470085c8)
- Bug 1217099 - Stop emitting pointless JSOP_GETLOCAL; JSOP_POP bytecode sequence for `var x;`. r=shu. (9c69ca3bd3)
-  Bug 1217110 - Remove unnecessary opcode JSOP_BINDINTRINSIC. r=shu. (58e4f74fd1)
2022-10-27 08:50:13 +08:00

924 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMMutationObserver_h
#define nsDOMMutationObserver_h
#include "mozilla/Attributes.h"
#include "nsCycleCollectionParticipant.h"
#include "nsPIDOMWindow.h"
#include "nsIScriptContext.h"
#include "nsStubAnimationObserver.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsIVariant.h"
#include "nsContentList.h"
#include "mozilla/dom/Element.h"
#include "nsClassHashtable.h"
#include "nsNodeUtils.h"
#include "nsIDOMMutationEvent.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/MutationObserverBinding.h"
#include "nsIDocument.h"
#include "mozilla/dom/Animation.h"
#include "nsIAnimationObserver.h"
class nsDOMMutationObserver;
using mozilla::dom::MutationObservingInfo;
class nsDOMMutationRecord final : public nsISupports,
public nsWrapperCache
{
virtual ~nsDOMMutationRecord() {}
public:
typedef nsTArray<RefPtr<mozilla::dom::Animation>> AnimationArray;
nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
: mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
{
}
nsISupports* GetParentObject() const
{
return mOwner;
}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return mozilla::dom::MutationRecordBinding::Wrap(aCx, this, aGivenProto);
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
void GetType(mozilla::dom::DOMString& aRetVal) const
{
aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
}
nsINode* GetTarget() const
{
return mTarget;
}
nsINodeList* AddedNodes();
nsINodeList* RemovedNodes();
nsINode* GetPreviousSibling() const
{
return mPreviousSibling;
}
nsINode* GetNextSibling() const
{
return mNextSibling;
}
void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
{
aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
}
void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
{
aRetVal.SetOwnedString(mAttrNamespace);
}
void GetOldValue(mozilla::dom::DOMString& aRetVal) const
{
aRetVal.SetOwnedString(mPrevValue);
}
void GetAddedAnimations(AnimationArray& aRetVal) const
{
aRetVal = mAddedAnimations;
}
void GetRemovedAnimations(AnimationArray& aRetVal) const
{
aRetVal = mRemovedAnimations;
}
void GetChangedAnimations(AnimationArray& aRetVal) const
{
aRetVal = mChangedAnimations;
}
nsCOMPtr<nsINode> mTarget;
nsCOMPtr<nsIAtom> mType;
nsCOMPtr<nsIAtom> mAttrName;
nsString mAttrNamespace;
nsString mPrevValue;
RefPtr<nsSimpleContentList> mAddedNodes;
RefPtr<nsSimpleContentList> mRemovedNodes;
nsCOMPtr<nsINode> mPreviousSibling;
nsCOMPtr<nsINode> mNextSibling;
AnimationArray mAddedAnimations;
AnimationArray mRemovedAnimations;
AnimationArray mChangedAnimations;
RefPtr<nsDOMMutationRecord> mNext;
nsCOMPtr<nsISupports> mOwner;
};
// Base class just prevents direct access to
// members to make sure we go through getters/setters.
class nsMutationReceiverBase : public nsStubAnimationObserver
{
public:
virtual ~nsMutationReceiverBase() { }
nsDOMMutationObserver* Observer();
nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
nsINode* RegisterTarget() { return mRegisterTarget; }
bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
void SetSubtree(bool aSubtree)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mSubtree = aSubtree;
}
bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
void SetChildList(bool aChildList)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mChildList = aChildList;
}
bool CharacterData()
{
return mParent ? mParent->CharacterData() : mCharacterData;
}
void SetCharacterData(bool aCharacterData)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mCharacterData = aCharacterData;
}
bool CharacterDataOldValue()
{
return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
}
void SetCharacterDataOldValue(bool aOldValue)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mCharacterDataOldValue = aOldValue;
}
bool NativeAnonymousChildList()
{
return mParent ? mParent->NativeAnonymousChildList() : mNativeAnonymousChildList;
}
void SetNativeAnonymousChildList(bool aOldValue)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mNativeAnonymousChildList = aOldValue;
}
bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
void SetAttributes(bool aAttributes)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mAttributes = aAttributes;
}
bool AllAttributes()
{
return mParent ? mParent->AllAttributes()
: mAllAttributes;
}
void SetAllAttributes(bool aAll)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mAllAttributes = aAll;
}
bool Animations() { return mParent ? mParent->Animations() : mAnimations; }
void SetAnimations(bool aAnimations)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mAnimations = aAnimations;
}
bool AttributeOldValue() {
return mParent ? mParent->AttributeOldValue()
: mAttributeOldValue;
}
void SetAttributeOldValue(bool aOldValue)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mAttributeOldValue = aOldValue;
}
nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
{
NS_ASSERTION(!mParent, "Shouldn't have parent");
mAttributeFilter.Clear();
mAttributeFilter.AppendObjects(aFilter);
}
void AddClone(nsMutationReceiverBase* aClone)
{
mTransientReceivers.AppendObject(aClone);
}
void RemoveClone(nsMutationReceiverBase* aClone)
{
mTransientReceivers.RemoveObject(aClone);
}
protected:
nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
: mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
{
}
nsMutationReceiverBase(nsINode* aRegisterTarget,
nsMutationReceiverBase* aParent)
: mTarget(nullptr), mObserver(nullptr), mParent(aParent),
mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target())
{
NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
}
virtual void AddMutationObserver() = 0;
void AddObserver()
{
AddMutationObserver();
mRegisterTarget->SetMayHaveDOMMutationObserver();
mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
}
bool IsObservable(nsIContent* aContent);
bool ObservesAttr(nsINode* aRegisterTarget,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttr)
{
if (mParent) {
return mParent->ObservesAttr(aRegisterTarget, aElement, aNameSpaceID, aAttr);
}
if (!Attributes() ||
(!Subtree() && aElement != Target()) ||
(Subtree() && aRegisterTarget->SubtreeRoot() != aElement->SubtreeRoot()) ||
!IsObservable(aElement)) {
return false;
}
if (AllAttributes()) {
return true;
}
if (aNameSpaceID != kNameSpaceID_None) {
return false;
}
nsCOMArray<nsIAtom>& filters = AttributeFilter();
for (int32_t i = 0; i < filters.Count(); ++i) {
if (filters[i] == aAttr) {
return true;
}
}
return false;
}
// The target for the MutationObserver.observe() method.
nsINode* mTarget;
nsDOMMutationObserver* mObserver;
RefPtr<nsMutationReceiverBase> mParent; // Cleared after microtask.
// The node to which Gecko-internal nsIMutationObserver was registered to.
// This is different than mTarget when dealing with transient observers.
nsINode* mRegisterTarget;
nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
// While we have transient receivers, keep the original mutation receiver
// alive so it doesn't go away and disconnect all its transient receivers.
nsCOMPtr<nsINode> mKungFuDeathGrip;
private:
bool mSubtree;
bool mChildList;
bool mCharacterData;
bool mCharacterDataOldValue;
bool mNativeAnonymousChildList;
bool mAttributes;
bool mAllAttributes;
bool mAttributeOldValue;
bool mAnimations;
nsCOMArray<nsIAtom> mAttributeFilter;
};
class nsMutationReceiver : public nsMutationReceiverBase
{
protected:
virtual ~nsMutationReceiver() { Disconnect(false); }
public:
static nsMutationReceiver* Create(nsINode* aTarget,
nsDOMMutationObserver* aObserver)
{
nsMutationReceiver* r = new nsMutationReceiver(aTarget, aObserver);
r->AddObserver();
return r;
}
static nsMutationReceiver* Create(nsINode* aRegisterTarget,
nsMutationReceiverBase* aParent)
{
nsMutationReceiver* r = new nsMutationReceiver(aRegisterTarget, aParent);
r->AddObserver();
return r;
}
nsMutationReceiver* GetParent()
{
return static_cast<nsMutationReceiver*>(mParent.get());
}
void RemoveClones()
{
for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
nsMutationReceiver* r =
static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
r->DisconnectTransientReceiver();
}
mTransientReceivers.Clear();
}
void DisconnectTransientReceiver()
{
if (mRegisterTarget) {
mRegisterTarget->RemoveMutationObserver(this);
mRegisterTarget = nullptr;
}
mParent = nullptr;
NS_ASSERTION(!mTarget, "Should not have mTarget");
NS_ASSERTION(!mObserver, "Should not have mObserver");
}
void Disconnect(bool aRemoveFromObserver);
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
NS_DECL_ISUPPORTS
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute) override
{
// We can reuse AttributeWillChange implementation.
AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
nsIDOMMutationEvent::MODIFICATION, nullptr);
}
protected:
nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
: nsMutationReceiverBase(aRegisterTarget, aParent)
{
NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
"Shouldn't create deep observer hierarchies!");
aParent->AddClone(this);
}
virtual void AddMutationObserver() override
{
mRegisterTarget->AddMutationObserver(this);
}
};
class nsAnimationReceiver : public nsMutationReceiver
{
public:
static nsAnimationReceiver* Create(nsINode* aTarget,
nsDOMMutationObserver* aObserver)
{
nsAnimationReceiver* r = new nsAnimationReceiver(aTarget, aObserver);
r->AddObserver();
return r;
}
static nsAnimationReceiver* Create(nsINode* aRegisterTarget,
nsMutationReceiverBase* aParent)
{
nsAnimationReceiver* r = new nsAnimationReceiver(aRegisterTarget, aParent);
r->AddObserver();
return r;
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONADDED
NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONCHANGED
NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONREMOVED
protected:
virtual ~nsAnimationReceiver() {}
nsAnimationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver)
: nsMutationReceiver(aTarget, aObserver) {}
nsAnimationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
: nsMutationReceiver(aRegisterTarget, aParent) {}
virtual void AddMutationObserver() override
{
mRegisterTarget->AddAnimationObserver(this);
}
private:
enum AnimationMutation {
eAnimationMutation_Added,
eAnimationMutation_Changed,
eAnimationMutation_Removed
};
void RecordAnimationMutation(mozilla::dom::Animation* aAnimation,
AnimationMutation aMutationType);
};
#define NS_DOM_MUTATION_OBSERVER_IID \
{ 0x0c3b91f8, 0xcc3b, 0x4b08, \
{ 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
class nsDOMMutationObserver final : public nsISupports,
public nsWrapperCache
{
public:
nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
mozilla::dom::MutationCallback& aCb,
bool aChrome)
: mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome),
mMergeAttributeRecords(false), mId(++sCount)
{
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
static already_AddRefed<nsDOMMutationObserver>
Constructor(const mozilla::dom::GlobalObject& aGlobal,
mozilla::dom::MutationCallback& aCb,
mozilla::ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
{
return mozilla::dom::MutationObserverBinding::Wrap(aCx, this, aGivenProto);
}
nsISupports* GetParentObject() const
{
return mOwner;
}
bool IsChrome()
{
return mIsChrome;
}
void Observe(nsINode& aTarget,
const mozilla::dom::MutationObserverInit& aOptions,
mozilla::ErrorResult& aRv);
void Disconnect();
void TakeRecords(nsTArray<RefPtr<nsDOMMutationRecord> >& aRetVal);
void HandleMutation();
void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo>>& aResult,
mozilla::ErrorResult& aRv);
mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
bool MergeAttributeRecords()
{
return mMergeAttributeRecords;
}
void SetMergeAttributeRecords(bool aVal)
{
mMergeAttributeRecords = aVal;
}
// If both records are for 'attributes' type and for the same target and
// attribute name and namespace are the same, we can skip the newer record.
// aOldRecord->mPrevValue holds the original value, if observed.
bool MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
nsDOMMutationRecord* aRecord);
void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
{
RefPtr<nsDOMMutationRecord> record = aRecord;
MOZ_ASSERT(record);
if (!mLastPendingMutation) {
MOZ_ASSERT(!mFirstPendingMutation);
mFirstPendingMutation = record.forget();
mLastPendingMutation = mFirstPendingMutation;
} else {
MOZ_ASSERT(mFirstPendingMutation);
mLastPendingMutation->mNext = record.forget();
mLastPendingMutation = mLastPendingMutation->mNext;
}
++mPendingMutationCount;
}
void ClearPendingRecords()
{
mFirstPendingMutation = nullptr;
mLastPendingMutation = nullptr;
mPendingMutationCount = 0;
}
// static methods
static void HandleMutations()
{
if (sScheduledMutationObservers) {
HandleMutationsInternal();
}
}
static void EnterMutationHandling();
static void LeaveMutationHandling();
static void Shutdown();
protected:
virtual ~nsDOMMutationObserver();
friend class nsMutationReceiver;
friend class nsAnimationReceiver;
friend class nsAutoMutationBatch;
friend class nsAutoAnimationMutationBatch;
nsMutationReceiver* GetReceiverFor(nsINode* aNode,
bool aMayCreate,
bool aWantsAnimations);
void RemoveReceiver(nsMutationReceiver* aReceiver);
already_AddRefed<nsIVariant> TakeRecords();
void GetAllSubtreeObserversFor(nsINode* aNode,
nsTArray<nsMutationReceiver*>& aObservers);
void ScheduleForRun();
void RescheduleForRun();
nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
bool HasCurrentRecord(const nsAString& aType);
bool Suppressed()
{
if (mOwner) {
nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
return d && d->IsInSyncOperation();
}
return false;
}
static void HandleMutationsInternal();
static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
nsCOMPtr<nsPIDOMWindow> mOwner;
nsCOMArray<nsMutationReceiver> mReceivers;
nsClassHashtable<nsISupportsHashKey,
nsCOMArray<nsMutationReceiver> > mTransientReceivers;
// MutationRecords which are being constructed.
nsAutoTArray<nsDOMMutationRecord*, 4> mCurrentMutations;
// MutationRecords which will be handed to the callback at the end of
// the microtask.
RefPtr<nsDOMMutationRecord> mFirstPendingMutation;
nsDOMMutationRecord* mLastPendingMutation;
uint32_t mPendingMutationCount;
RefPtr<mozilla::dom::MutationCallback> mCallback;
bool mWaitingForRun;
bool mIsChrome;
bool mMergeAttributeRecords;
uint64_t mId;
static uint64_t sCount;
static nsAutoTArray<RefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
static nsDOMMutationObserver* sCurrentObserver;
static uint32_t sMutationLevel;
static nsAutoTArray<nsAutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>*
sCurrentlyHandlingObservers;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
class nsAutoMutationBatch
{
public:
nsAutoMutationBatch()
: mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
mFromFirstToLast(false), mAllowNestedBatches(false)
{
}
nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
bool aAllowNestedBatches)
: mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
mFromFirstToLast(false), mAllowNestedBatches(false)
{
Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
}
void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
{
if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
return;
}
mBatchTarget = aTarget;
mFromFirstToLast = aFromFirstToLast;
mAllowNestedBatches = aAllowNestedBatches;
mPreviousBatch = sCurrentBatch;
sCurrentBatch = this;
nsDOMMutationObserver::EnterMutationHandling();
}
}
void RemovalDone() { mRemovalDone = true; }
static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
void Done();
~nsAutoMutationBatch() { NodesAdded(); }
static bool IsBatching()
{
return !!sCurrentBatch;
}
static nsAutoMutationBatch* GetCurrentBatch()
{
return sCurrentBatch;
}
static void UpdateObserver(nsDOMMutationObserver* aObserver,
bool aWantsChildList)
{
uint32_t l = sCurrentBatch->mObservers.Length();
for (uint32_t i = 0; i < l; ++i) {
if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
if (aWantsChildList) {
sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
}
return;
}
}
BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
bo->mObserver = aObserver;
bo->mWantsChildList = aWantsChildList;
}
static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
// Mutation receivers notify the batch about removed child nodes.
static void NodeRemoved(nsIContent* aChild)
{
if (IsBatching() && !sCurrentBatch->mRemovalDone) {
uint32_t len = sCurrentBatch->mRemovedNodes.Length();
if (!len ||
sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
sCurrentBatch->mRemovedNodes.AppendElement(aChild);
}
}
}
// Called after new child nodes have been added to the batch target.
void NodesAdded()
{
if (sCurrentBatch != this) {
return;
}
nsIContent* c =
mPrevSibling ? mPrevSibling->GetNextSibling() :
mBatchTarget->GetFirstChild();
for (; c != mNextSibling; c = c->GetNextSibling()) {
mAddedNodes.AppendElement(c);
}
Done();
}
private:
struct BatchObserver
{
nsDOMMutationObserver* mObserver;
bool mWantsChildList;
};
static nsAutoMutationBatch* sCurrentBatch;
nsAutoMutationBatch* mPreviousBatch;
nsAutoTArray<BatchObserver, 2> mObservers;
nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
nsINode* mBatchTarget;
bool mRemovalDone;
bool mFromFirstToLast;
bool mAllowNestedBatches;
nsCOMPtr<nsINode> mPrevSibling;
nsCOMPtr<nsINode> mNextSibling;
};
class nsAutoAnimationMutationBatch
{
struct Entry;
public:
explicit nsAutoAnimationMutationBatch(nsIDocument* aDocument)
{
Init(aDocument);
}
void Init(nsIDocument* aDocument)
{
if (!aDocument ||
!aDocument->MayHaveDOMMutationObservers() ||
sCurrentBatch) {
return;
}
sCurrentBatch = this;
nsDOMMutationObserver::EnterMutationHandling();
}
~nsAutoAnimationMutationBatch()
{
Done();
}
void Done();
static bool IsBatching()
{
return !!sCurrentBatch;
}
static nsAutoAnimationMutationBatch* GetCurrentBatch()
{
return sCurrentBatch;
}
static void AddObserver(nsDOMMutationObserver* aObserver)
{
if (sCurrentBatch->mObservers.Contains(aObserver)) {
return;
}
sCurrentBatch->mObservers.AppendElement(aObserver);
}
static void AnimationAdded(mozilla::dom::Animation* aAnimation,
nsINode* aTarget)
{
if (!IsBatching()) {
return;
}
Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
if (entry) {
switch (entry->mState) {
case eState_RemainedAbsent:
entry->mState = eState_Added;
break;
case eState_Removed:
entry->mState = eState_RemainedPresent;
break;
default:
NS_NOTREACHED("shouldn't have observed an animation being added "
"twice");
}
} else {
entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
entry->mState = eState_Added;
entry->mChanged = false;
}
}
static void AnimationChanged(mozilla::dom::Animation* aAnimation,
nsINode* aTarget)
{
Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
if (entry) {
NS_ASSERTION(entry->mState == eState_RemainedPresent ||
entry->mState == eState_Added,
"shouldn't have observed an animation being changed after "
"being removed");
entry->mChanged = true;
} else {
entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
entry->mState = eState_RemainedPresent;
entry->mChanged = true;
}
}
static void AnimationRemoved(mozilla::dom::Animation* aAnimation,
nsINode* aTarget)
{
Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
if (entry) {
switch (entry->mState) {
case eState_RemainedPresent:
entry->mState = eState_Removed;
break;
case eState_Added:
entry->mState = eState_RemainedAbsent;
break;
default:
NS_NOTREACHED("shouldn't have observed an animation being removed "
"twice");
}
} else {
entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
entry->mState = eState_Removed;
entry->mChanged = false;
}
}
private:
Entry* FindEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
{
EntryArray* entries = mEntryTable.Get(aTarget);
if (!entries) {
return nullptr;
}
for (Entry& e : *entries) {
if (e.mAnimation == aAnimation) {
return &e;
}
}
return nullptr;
}
Entry* AddEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
{
EntryArray* entries = sCurrentBatch->mEntryTable.LookupOrAdd(aTarget);
if (entries->IsEmpty()) {
sCurrentBatch->mBatchTargets.AppendElement(aTarget);
}
Entry* entry = entries->AppendElement();
entry->mAnimation = aAnimation;
return entry;
}
enum State {
eState_RemainedPresent,
eState_RemainedAbsent,
eState_Added,
eState_Removed
};
struct Entry
{
RefPtr<mozilla::dom::Animation> mAnimation;
State mState;
bool mChanged;
};
static nsAutoAnimationMutationBatch* sCurrentBatch;
nsAutoTArray<nsDOMMutationObserver*, 2> mObservers;
typedef nsTArray<Entry> EntryArray;
nsClassHashtable<nsPtrHashKey<nsINode>, EntryArray> mEntryTable;
// List of nodes referred to by mEntryTable so we can sort them
nsTArray<nsINode*> mBatchTargets;
};
inline
nsDOMMutationObserver*
nsMutationReceiverBase::Observer()
{
return mParent ?
mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
}
#endif