diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 60573186f62..e8d9666b360 100755 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -269,7 +269,7 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument, // Try to avoid creating transient observer if the node // already has an observer observing the same set of nodes. nsMutationReceiver* orig = GetParent() ? GetParent() : this; - if (Observer()->GetReceiverFor(aChild, false) != orig) { + if (Observer()->GetReceiverFor(aChild, false, false) != orig) { bool transientExists = false; nsCOMArray* transientReceivers = nullptr; Observer()->mTransientReceivers.Get(aChild, &transientReceivers); @@ -287,7 +287,13 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument, if (!transientExists) { // Make sure the elements which are removed from the // subtree are kept in the same observation set. - transientReceivers->AppendObject(new nsMutationReceiver(aChild, orig)); + nsMutationReceiver* tr; + if (orig->Animations()) { + tr = nsAnimationReceiver::Create(aChild, orig); + } else { + tr = nsMutationReceiver::Create(aChild, orig); + } + transientReceivers->AppendObject(tr); } } } @@ -316,6 +322,87 @@ void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode) Disconnect(true); } +void +nsAnimationReceiver::RecordAnimationMutation(AnimationPlayer* aPlayer, + AnimationMutation aMutationType) +{ + Animation* source = aPlayer->GetSource(); + if (!source) { + return; + } + + Element* animationTarget = source->GetTarget(); + if (!animationTarget) { + return; + } + + if (!Animations() || !(Subtree() || animationTarget == Target()) || + animationTarget->ChromeOnlyAccess()) { + return; + } + + if (nsAutoAnimationMutationBatch::IsBatching()) { + if (nsAutoAnimationMutationBatch::GetBatchTarget() != animationTarget) { + return; + } + + switch (aMutationType) { + case eAnimationMutation_Added: + nsAutoAnimationMutationBatch::AnimationAdded(aPlayer); + break; + case eAnimationMutation_Changed: + nsAutoAnimationMutationBatch::AnimationChanged(aPlayer); + break; + case eAnimationMutation_Removed: + nsAutoAnimationMutationBatch::AnimationRemoved(aPlayer); + break; + } + + nsAutoAnimationMutationBatch::AddObserver(Observer()); + return; + } + + nsDOMMutationRecord* m = + Observer()->CurrentRecord(nsGkAtoms::animations); + + NS_ASSERTION(!m->mTarget, "Wrong target!"); + + m->mTarget = animationTarget; + + switch (aMutationType) { + case eAnimationMutation_Added: + m->mAddedAnimations.AppendElement(aPlayer); + break; + case eAnimationMutation_Changed: + m->mChangedAnimations.AppendElement(aPlayer); + break; + case eAnimationMutation_Removed: + m->mRemovedAnimations.AppendElement(aPlayer); + break; + } +} + +void +nsAnimationReceiver::AnimationAdded(AnimationPlayer* aPlayer) +{ + RecordAnimationMutation(aPlayer, eAnimationMutation_Added); +} + +void +nsAnimationReceiver::AnimationChanged(AnimationPlayer* aPlayer) +{ + RecordAnimationMutation(aPlayer, eAnimationMutation_Changed); +} + +void +nsAnimationReceiver::AnimationRemoved(AnimationPlayer* aPlayer) +{ + RecordAnimationMutation(aPlayer, eAnimationMutation_Removed); +} + +NS_IMPL_ISUPPORTS_INHERITED(nsAnimationReceiver, nsMutationReceiver, + nsIAnimationObserver) + // Observer NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver) @@ -355,8 +442,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END nsMutationReceiver* -nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate) +nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate, + bool aWantsAnimations) { + MOZ_ASSERT(aMayCreate || !aWantsAnimations, + "the value of aWantsAnimations doesn't matter when aMayCreate is " + "false, so just pass in false for it"); + if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) { return nullptr; } @@ -370,7 +462,12 @@ nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate) return nullptr; } - nsMutationReceiver* r = new nsMutationReceiver(aNode, this); + nsMutationReceiver* r; + if (aWantsAnimations) { + r = nsAnimationReceiver::Create(aNode, this); + } else { + r = nsMutationReceiver::Create(aNode, this); + } mReceivers.AppendObject(r); return r; } @@ -389,7 +486,7 @@ nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode, nsINode* n = aNode; while (n) { if (n->MayHaveDOMMutationObserver()) { - nsMutationReceiver* r = GetReceiverFor(n, false); + nsMutationReceiver* r = GetReceiverFor(n, false, false); if (r && r->Subtree() && !aReceivers.Contains(r)) { aReceivers.AppendElement(r); // If we've found all the receivers the observer has, @@ -528,7 +625,7 @@ nsDOMMutationObserver::Observe(nsINode& aTarget, } } - nsMutationReceiver* r = GetReceiverFor(&aTarget, true); + nsMutationReceiver* r = GetReceiverFor(&aTarget, true, animations); r->SetChildList(childList); r->SetAttributes(attributes); r->SetCharacterData(characterData); @@ -860,10 +957,16 @@ nsAutoMutationBatch::Done() for (uint32_t k = 0; k < allObservers.Length(); ++k) { nsMutationReceiver* r = allObservers[k]; nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r; - if (ob->GetReceiverFor(removed, false) != orig) { + if (ob->GetReceiverFor(removed, false, false) != orig) { // Make sure the elements which are removed from the // subtree are kept in the same observation set. - transientReceivers->AppendObject(new nsMutationReceiver(removed, orig)); + nsMutationReceiver* tr; + if (orig->Animations()) { + tr = nsAnimationReceiver::Create(removed, orig); + } else { + tr = nsMutationReceiver::Create(removed, orig); + } + transientReceivers->AppendObject(tr); } } } @@ -890,3 +993,45 @@ nsAutoMutationBatch::Done() } nsDOMMutationObserver::LeaveMutationHandling(); } + +nsAutoAnimationMutationBatch* +nsAutoAnimationMutationBatch::sCurrentBatch = nullptr; + +void +nsAutoAnimationMutationBatch::Done() +{ + if (sCurrentBatch != this) { + return; + } + + sCurrentBatch = mPreviousBatch; + if (mObservers.IsEmpty()) { + nsDOMMutationObserver::LeaveMutationHandling(); + // Nothing to do. + return; + } + + for (nsDOMMutationObserver* ob : mObservers) { + nsRefPtr m = + new nsDOMMutationRecord(nsGkAtoms::animations, ob->GetParentObject()); + m->mTarget = mBatchTarget; + + for (const Entry& e : mEntries) { + if (e.mState == eState_Added) { + m->mAddedAnimations.AppendElement(e.mPlayer); + } else if (e.mState == eState_Removed) { + m->mRemovedAnimations.AppendElement(e.mPlayer); + } else if (e.mState == eState_RemainedPresent && e.mChanged) { + m->mChangedAnimations.AppendElement(e.mPlayer); + } + } + + if (!m->mAddedAnimations.IsEmpty() || + !m->mChangedAnimations.IsEmpty() || + !m->mRemovedAnimations.IsEmpty()) { + ob->AppendMutationRecord(m.forget()); + ob->ScheduleForRun(); + } + } + nsDOMMutationObserver::LeaveMutationHandling(); +} diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index e2a1870bbc7..9a7522dfa32 100755 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -11,7 +11,7 @@ #include "nsCycleCollectionParticipant.h" #include "nsPIDOMWindow.h" #include "nsIScriptContext.h" -#include "nsStubMutationObserver.h" +#include "nsStubAnimationObserver.h" #include "nsCOMArray.h" #include "nsTArray.h" #include "nsAutoPtr.h" @@ -25,6 +25,7 @@ #include "mozilla/dom/MutationObserverBinding.h" #include "nsIDocument.h" #include "mozilla/dom/AnimationPlayer.h" +#include "nsIAnimationObserver.h" class nsDOMMutationObserver; using mozilla::dom::MutationObservingInfo; @@ -128,7 +129,7 @@ class nsDOMMutationRecord final : public nsISupports, // Base class just prevents direct access to // members to make sure we go through getters/setters. -class nsMutationReceiverBase : public nsStubMutationObserver +class nsMutationReceiverBase : public nsStubAnimationObserver { public: virtual ~nsMutationReceiverBase() { } @@ -228,9 +229,6 @@ class nsMutationReceiverBase : public nsStubMutationObserver nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver) : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget) { - mRegisterTarget->AddMutationObserver(this); - mRegisterTarget->SetMayHaveDOMMutationObserver(); - mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers(); } nsMutationReceiverBase(nsINode* aRegisterTarget, @@ -239,7 +237,13 @@ class nsMutationReceiverBase : public nsStubMutationObserver mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target()) { NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!"); - mRegisterTarget->AddMutationObserver(this); + } + + virtual void AddMutationObserver() = 0; + + void AddObserver() + { + AddMutationObserver(); mRegisterTarget->SetMayHaveDOMMutationObserver(); mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers(); } @@ -302,14 +306,20 @@ class nsMutationReceiver : public nsMutationReceiverBase virtual ~nsMutationReceiver() { Disconnect(false); } public: - nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver); + static nsMutationReceiver* Create(nsINode* aTarget, + nsDOMMutationObserver* aObserver) + { + nsMutationReceiver* r = new nsMutationReceiver(aTarget, aObserver); + r->AddObserver(); + return r; + } - nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent) - : nsMutationReceiverBase(aRegisterTarget, aParent) + static nsMutationReceiver* Create(nsINode* aRegisterTarget, + nsMutationReceiverBase* aParent) { - NS_ASSERTION(!static_cast(aParent)->GetParent(), - "Shouldn't create deep observer hierarchies!"); - aParent->AddClone(this); + nsMutationReceiver* r = new nsMutationReceiver(aRegisterTarget, aParent); + r->AddObserver(); + return r; } nsMutationReceiver* GetParent() @@ -360,6 +370,72 @@ class nsMutationReceiver : public nsMutationReceiverBase AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute, nsIDOMMutationEvent::MODIFICATION); } + +protected: + nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver); + + nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent) + : nsMutationReceiverBase(aRegisterTarget, aParent) + { + NS_ASSERTION(!static_cast(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::AnimationPlayer* aPlayer, + AnimationMutation aMutationType); }; #define NS_DOM_MUTATION_OBSERVER_IID \ @@ -448,8 +524,12 @@ class nsDOMMutationObserver final : public nsISupports, virtual ~nsDOMMutationObserver(); friend class nsMutationReceiver; + friend class nsAnimationReceiver; friend class nsAutoMutationBatch; - nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate); + friend class nsAutoAnimationMutationBatch; + nsMutationReceiver* GetReceiverFor(nsINode* aNode, + bool aMayCreate, + bool aWantsAnimations); void RemoveReceiver(nsMutationReceiver* aReceiver); already_AddRefed TakeRecords(); @@ -625,6 +705,156 @@ class nsAutoMutationBatch nsCOMPtr mNextSibling; }; +class nsAutoAnimationMutationBatch +{ + struct Entry; + +public: + nsAutoAnimationMutationBatch(nsINode* aTarget) + : mBatchTarget(nullptr) + { + Init(aTarget); + } + + void Init(nsINode* aTarget) + { + if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) { + mBatchTarget = aTarget; + mPreviousBatch = sCurrentBatch; + 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 nsINode* GetBatchTarget() + { + return sCurrentBatch->mBatchTarget; + } + + static void AnimationAdded(mozilla::dom::AnimationPlayer* aPlayer) + { + if (!IsBatching()) { + return; + } + + Entry* entry = sCurrentBatch->FindEntry(aPlayer); + 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->mEntries.AppendElement(); + entry->mPlayer = aPlayer; + entry->mState = eState_Added; + entry->mChanged = false; + } + } + + static void AnimationChanged(mozilla::dom::AnimationPlayer* aPlayer) + { + Entry* entry = sCurrentBatch->FindEntry(aPlayer); + 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->mEntries.AppendElement(); + entry->mPlayer = aPlayer; + entry->mState = eState_RemainedPresent; + entry->mChanged = true; + } + } + + static void AnimationRemoved(mozilla::dom::AnimationPlayer* aPlayer) + { + Entry* entry = sCurrentBatch->FindEntry(aPlayer); + 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->mEntries.AppendElement(); + entry->mPlayer = aPlayer; + entry->mState = eState_Removed; + entry->mChanged = false; + } + } + +private: + Entry* FindEntry(mozilla::dom::AnimationPlayer* aPlayer) + { + for (Entry& e : mEntries) { + if (e.mPlayer == aPlayer) { + return &e; + } + } + return nullptr; + } + + enum State { + eState_RemainedPresent, + eState_RemainedAbsent, + eState_Added, + eState_Removed + }; + + struct Entry + { + nsRefPtr mPlayer; + State mState; + bool mChanged; + }; + + static nsAutoAnimationMutationBatch* sCurrentBatch; + nsAutoAnimationMutationBatch* mPreviousBatch; + nsAutoTArray mObservers; + nsTArray mEntries; + nsINode* mBatchTarget; +}; + inline nsDOMMutationObserver* nsMutationReceiverBase::Observer() diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 691c719c083..a28d083a0c9 100755 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -88,6 +88,7 @@ GK_ATOM(ancestor, "ancestor") GK_ATOM(ancestorOrSelf, "ancestor-or-self") GK_ATOM(anchor, "anchor") GK_ATOM(_and, "and") +GK_ATOM(animations, "animations") GK_ATOM(anonid, "anonid") GK_ATOM(any, "any") GK_ATOM(mozapp, "mozapp")