Bug 1123523 - Part 6: Listen for nsIAnimationObserver notifications and translate them to MutationObserver notifications. r=smaug

This commit is contained in:
Cameron McCormack 2015-03-14 16:34:40 +11:00
Родитель 3496ca27f3
Коммит d068522170
3 изменённых файлов: 398 добавлений и 22 удалений

Просмотреть файл

@ -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<nsMutationReceiver>* 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<nsDOMMutationRecord> 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();
}

Просмотреть файл

@ -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 @@ public:
// 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 @@ protected:
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 @@ protected:
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 @@ protected:
virtual ~nsMutationReceiver() { Disconnect(false); }
public:
nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
: nsMutationReceiverBase(aRegisterTarget, aParent)
static nsMutationReceiver* Create(nsINode* aTarget,
nsDOMMutationObserver* aObserver)
{
NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
"Shouldn't create deep observer hierarchies!");
aParent->AddClone(this);
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()
@ -360,6 +370,72 @@ public:
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<nsMutationReceiver*>(aParent)->GetParent(),
"Shouldn't create deep observer hierarchies!");
aParent->AddClone(this);
}
virtual void AddMutationObserver() MOZ_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() MOZ_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 @@ protected:
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<nsIVariant> TakeRecords();
@ -625,6 +705,156 @@ private:
nsCOMPtr<nsINode> 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<mozilla::dom::AnimationPlayer> mPlayer;
State mState;
bool mChanged;
};
static nsAutoAnimationMutationBatch* sCurrentBatch;
nsAutoAnimationMutationBatch* mPreviousBatch;
nsAutoTArray<nsDOMMutationObserver*, 2> mObservers;
nsTArray<Entry> mEntries;
nsINode* mBatchTarget;
};
inline
nsDOMMutationObserver*
nsMutationReceiverBase::Observer()

Просмотреть файл

@ -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")