Bug 1236979 part 3: If there are no listeners for a transition or animation event, check listeners again using a webkit-prefixed event name. r=smaug

This commit is contained in:
Daniel Holbert 2016-02-02 10:03:49 -08:00
Родитель bf2246f94d
Коммит bbdaa0ddbd
2 изменённых файлов: 144 добавлений и 58 удалений

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

@ -19,7 +19,9 @@
#include "mozilla/HalSensor.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/JSEventHandler.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
@ -95,6 +97,21 @@ MutationBitForEventType(EventMessage aEventType)
uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
static bool
IsWebkitPrefixSupportEnabled()
{
static bool sIsWebkitPrefixSupportEnabled;
static bool sIsPrefCached = false;
if (!sIsPrefCached) {
sIsPrefCached = true;
Preferences::AddBoolVarCache(&sIsWebkitPrefixSupportEnabled,
"layout.css.prefixes.webkit");
}
return sIsWebkitPrefixSupportEnabled;
}
EventListenerManagerBase::EventListenerManagerBase()
: mNoListenerForEvent(eVoidEvent)
, mMayHavePaintEventListener(false)
@ -603,9 +620,15 @@ EventListenerManager::RemoveEventListenerInternal(
}
bool
EventListenerManager::ListenerCanHandle(Listener* aListener,
WidgetEvent* aEvent)
EventListenerManager::ListenerCanHandle(const Listener* aListener,
const WidgetEvent* aEvent,
EventMessage aEventMessage) const
{
MOZ_ASSERT(aEventMessage == aEvent->mMessage ||
aEventMessage == GetLegacyEventMessage(aEvent->mMessage),
"aEvent and aEventMessage should agree, modulo legacyness");
// This is slightly different from EVENT_TYPE_EQUALS in that it returns
// true even when aEvent->mMessage == eUnidentifiedEvent and
// aListener=>mEventMessage != eUnidentifiedEvent as long as the atoms are
@ -620,7 +643,7 @@ EventListenerManager::ListenerCanHandle(Listener* aListener,
return aListener->mTypeString.Equals(aEvent->typeString);
}
MOZ_ASSERT(mIsMainThreadELM);
return aListener->mEventMessage == aEvent->mMessage;
return aListener->mEventMessage == aEventMessage;
}
void
@ -1048,6 +1071,30 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
return result;
}
EventMessage
EventListenerManager::GetLegacyEventMessage(EventMessage aEventMessage) const
{
// (If we're off-main-thread, we can't check the pref; so we just behave as
// if it's disabled.)
if (mIsMainThreadELM && IsWebkitPrefixSupportEnabled()) {
// webkit-prefixed legacy events:
if (aEventMessage == eTransitionEnd) {
return eWebkitTransitionEnd;
}
if (aEventMessage == eAnimationStart) {
return eWebkitAnimationStart;
}
if (aEventMessage == eAnimationEnd) {
return eWebkitAnimationEnd;
}
if (aEventMessage == eAnimationIteration) {
return eWebkitAnimationIteration;
}
}
return aEventMessage;
}
nsIDocShell*
EventListenerManager::GetDocShellForTarget()
{
@ -1104,13 +1151,18 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
aEvent->mFlags.mDefaultPrevented = true;
}
nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
Maybe<nsAutoPopupStatePusher> popupStatePusher;
if (mIsMainThreadELM) {
popupStatePusher.emplace(Event::GetEventPopupControlState(aEvent, *aDOMEvent));
}
bool hasListener = false;
bool usingLegacyMessage = false;
EventMessage eventMessage = aEvent->mMessage;
while (true) {
nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners);
Maybe<EventMessageAutoOverride> legacyAutoOverride;
while (iter.HasMore()) {
if (aEvent->mFlags.mImmediatePropagationStopped) {
break;
@ -1118,7 +1170,7 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
Listener* listener = &iter.GetNext();
// Check that the phase is same in event and event listener.
// Handle only trusted events, except when listener permits untrusted events.
if (ListenerCanHandle(listener, aEvent)) {
if (ListenerCanHandle(listener, aEvent, eventMessage)) {
hasListener = true;
if (listener->IsListening(aEvent) &&
(aEvent->mFlags.mIsTrusted ||
@ -1139,6 +1191,11 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
break;
}
}
if (usingLegacyMessage && !legacyAutoOverride) {
// Override the aDOMEvent's event-message (its .type) until we
// finish traversing listeners (when legacyAutoOverride destructs)
legacyAutoOverride.emplace(*aDOMEvent, eventMessage);
}
// Maybe add a marker to the docshell's timeline, but only
// bother with all the logic if some docshell is recording.
@ -1177,6 +1234,26 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
}
}
// If we didn't find any matching listeners, and our event has a legacy
// version, we'll now switch to looking for that legacy version and we'll
// recheck our listeners.
if (hasListener || usingLegacyMessage) {
// (No need to recheck listeners, because we already found a match, or we
// already rechecked them.)
break;
}
EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
if (legacyEventMessage == eventMessage) {
break; // There's no legacy version of our event; no need to recheck.
}
MOZ_ASSERT(GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
"Legacy event messages should not themselves have legacy versions");
// Recheck our listeners, using the legacy event message we just looked up:
eventMessage = legacyEventMessage;
usingLegacyMessage = true;
}
aEvent->currentTarget = nullptr;
if (mIsMainThreadELM && !hasListener) {

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

@ -458,6 +458,13 @@ protected:
nsIDOMEvent* aDOMEvent,
dom::EventTarget* aCurrentTarget);
/**
* If the given EventMessage has a legacy version that we support, then this
* function returns that legacy version. Otherwise, this function simply
* returns the passed-in EventMessage.
*/
EventMessage GetLegacyEventMessage(EventMessage aEventMessage) const;
nsIDocShell* GetDocShellForTarget();
/**
@ -571,7 +578,9 @@ protected:
nsPIDOMWindowInner* GetInnerWindowForTarget();
already_AddRefed<nsPIDOMWindowInner> GetTargetAsInnerWindow() const;
bool ListenerCanHandle(Listener* aListener, WidgetEvent* aEvent);
bool ListenerCanHandle(const Listener* aListener,
const WidgetEvent* aEvent,
EventMessage aEventMessage) const;
// BE AWARE, a lot of instances of EventListenerManager will be created.
// Therefor, we need to keep this class compact. When you add integer