mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-06 14:44:26 +00:00
Bug 1266066 - Implement DOM support for the 'passive' event listener flag. r=smaug
MozReview-Commit-ID: EvSCDxYC7g6
This commit is contained in:
parent
6c277c9ba0
commit
a412581b20
@ -520,6 +520,9 @@ Event::PreventDefaultInternal(bool aCalledByDefaultHandler)
|
||||
if (!mEvent->mFlags.mCancelable) {
|
||||
return;
|
||||
}
|
||||
if (mEvent->mFlags.mInPassiveListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEvent->PreventDefault(aCalledByDefaultHandler);
|
||||
|
||||
|
@ -264,7 +264,7 @@ EventListenerManager::AddEventListenerInternal(
|
||||
listener = &mListeners.ElementAt(i);
|
||||
// mListener == aListenerHolder is the last one, since it can be a bit slow.
|
||||
if (listener->mListenerIsHandler == aHandler &&
|
||||
listener->mFlags == aFlags &&
|
||||
listener->mFlags.EqualsForAddition(aFlags) &&
|
||||
EVENT_TYPE_EQUALS(listener, aEventMessage, aTypeAtom, aTypeString,
|
||||
aAllEvents) &&
|
||||
listener->mListener == aListenerHolder) {
|
||||
@ -617,7 +617,7 @@ EventListenerManager::RemoveEventListenerInternal(
|
||||
aAllEvents)) {
|
||||
++typeCount;
|
||||
if (listener->mListener == aListenerHolder &&
|
||||
listener->mFlags.EqualsIgnoringTrustness(aFlags)) {
|
||||
listener->mFlags.EqualsForRemoval(aFlags)) {
|
||||
RefPtr<EventListenerManager> kungFuDeathGrip(this);
|
||||
mListeners.RemoveElementAt(i);
|
||||
--count;
|
||||
@ -1279,9 +1279,11 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
|
||||
aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
|
||||
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
|
||||
aEvent->mFlags.mExceptionWasRaised = true;
|
||||
}
|
||||
aEvent->mFlags.mInPassiveListener = false;
|
||||
|
||||
if (needsEndEventMarker) {
|
||||
timelines->AddMarkerForDocShell(
|
||||
@ -1352,9 +1354,12 @@ EventListenerManager::AddEventListener(
|
||||
bool aWantsUntrusted)
|
||||
{
|
||||
EventListenerFlags flags;
|
||||
flags.mCapture =
|
||||
aOptions.IsBoolean() ? aOptions.GetAsBoolean()
|
||||
: aOptions.GetAsAddEventListenerOptions().mCapture;
|
||||
if (aOptions.IsBoolean()) {
|
||||
flags.mCapture = aOptions.GetAsBoolean();
|
||||
} else {
|
||||
flags.mCapture = aOptions.GetAsAddEventListenerOptions().mCapture;
|
||||
flags.mPassive = aOptions.GetAsAddEventListenerOptions().mPassive;
|
||||
}
|
||||
flags.mAllowUntrustedEvents = aWantsUntrusted;
|
||||
return AddEventListenerByType(aListenerHolder, aType, flags);
|
||||
}
|
||||
|
@ -58,31 +58,32 @@ public:
|
||||
// If mAllowUntrustedEvents is true, the listener is listening to the
|
||||
// untrusted events too.
|
||||
bool mAllowUntrustedEvents : 1;
|
||||
// If mPassive is true, the listener will not be calling preventDefault on the
|
||||
// event. (If it does call preventDefault, we should ignore it).
|
||||
bool mPassive : 1;
|
||||
|
||||
EventListenerFlags() :
|
||||
mListenerIsJSListener(false),
|
||||
mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false)
|
||||
mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false),
|
||||
mPassive(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool Equals(const EventListenerFlags& aOther) const
|
||||
bool EqualsForAddition(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return (mCapture == aOther.mCapture &&
|
||||
mInSystemGroup == aOther.mInSystemGroup &&
|
||||
mListenerIsJSListener == aOther.mListenerIsJSListener &&
|
||||
mAllowUntrustedEvents == aOther.mAllowUntrustedEvents);
|
||||
// Don't compare mPassive
|
||||
}
|
||||
|
||||
bool EqualsIgnoringTrustness(const EventListenerFlags& aOther) const
|
||||
bool EqualsForRemoval(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return (mCapture == aOther.mCapture &&
|
||||
mInSystemGroup == aOther.mInSystemGroup &&
|
||||
mListenerIsJSListener == aOther.mListenerIsJSListener);
|
||||
}
|
||||
|
||||
bool operator==(const EventListenerFlags& aOther) const
|
||||
{
|
||||
return Equals(aOther);
|
||||
// Don't compare mAllowUntrustedEvents or mPassive
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -199,3 +199,4 @@ skip-if = buildapp == 'b2g' # no wheel events on b2g
|
||||
[test_dom_activate_event.html]
|
||||
[test_bug1264380.html]
|
||||
run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode.
|
||||
[test_passive_listeners.html]
|
||||
|
118
dom/events/test/test_passive_listeners.html
Normal file
118
dom/events/test/test_passive_listeners.html
Normal file
@ -0,0 +1,118 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests for passive event listeners</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="dummy">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var listenerHitCount;
|
||||
var doPreventDefault;
|
||||
|
||||
function listener(e)
|
||||
{
|
||||
listenerHitCount++;
|
||||
if (doPreventDefault) {
|
||||
// When this function is registered as a passive listener, this
|
||||
// call should be a no-op and might report a console warning.
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function listener2(e)
|
||||
{
|
||||
if (doPreventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
var elem = document.getElementById('dummy');
|
||||
|
||||
function doTest(description, passiveArg)
|
||||
{
|
||||
listenerHitCount = 0;
|
||||
|
||||
elem.addEventListener('test', listener, { passive: passiveArg });
|
||||
|
||||
// Test with a cancelable event
|
||||
var e1 = new Event('test', { cancelable: true });
|
||||
elem.dispatchEvent(e1);
|
||||
is(listenerHitCount, 1, description + ' | hit count');
|
||||
var expectedDefaultPrevented = (doPreventDefault && !passiveArg);
|
||||
is(e1.defaultPrevented, expectedDefaultPrevented, description + ' | default prevented');
|
||||
|
||||
// Test with a non-cancelable event
|
||||
var e2 = new Event('test', { cancelable: false });
|
||||
elem.dispatchEvent(e2);
|
||||
is(listenerHitCount, 2, description + ' | hit count after non-cancelable event');
|
||||
is(e2.defaultPrevented, false, description + ' | default prevented on non-cancelable event');
|
||||
|
||||
// Test combining passive-enabled and "traditional" listeners
|
||||
elem.addEventListener('test', listener2, false);
|
||||
var e3 = new Event('test', { cancelable: true });
|
||||
elem.dispatchEvent(e3);
|
||||
is(listenerHitCount, 3, description + ' | hit count with second listener');
|
||||
is(e3.defaultPrevented, doPreventDefault, description + ' | default prevented with second listener');
|
||||
elem.removeEventListener('test', listener2, false);
|
||||
|
||||
elem.removeEventListener('test', listener, false);
|
||||
}
|
||||
|
||||
function testAddListenerKey(passiveListenerFirst)
|
||||
{
|
||||
listenerHitCount = 0;
|
||||
doPreventDefault = true;
|
||||
|
||||
elem.addEventListener('test', listener, { capture: false, passive: passiveListenerFirst });
|
||||
// This second listener should not be registered, because the "key" of
|
||||
// { type, callback, capture } is the same, even though the 'passive' flag
|
||||
// is different.
|
||||
elem.addEventListener('test', listener, { capture: false, passive: !passiveListenerFirst });
|
||||
|
||||
var e1 = new Event('test', { cancelable: true });
|
||||
elem.dispatchEvent(e1);
|
||||
|
||||
is(listenerHitCount, 1, 'Duplicate addEventListener was correctly ignored');
|
||||
is(e1.defaultPrevented, !passiveListenerFirst, 'Prevent-default result based on first registered listener');
|
||||
|
||||
// Even though passive is the opposite of the first addEventListener call, it
|
||||
// should remove the listener registered above.
|
||||
elem.removeEventListener('test', listener, { capture: false, passive: !passiveListenerFirst });
|
||||
|
||||
var e2 = new Event('test', { cancelable: true });
|
||||
elem.dispatchEvent(e2);
|
||||
|
||||
is(listenerHitCount, 1, 'non-passive listener was correctly unregistered');
|
||||
is(e2.defaultPrevented, false, 'no listener was registered to preventDefault this event');
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
doPreventDefault = false;
|
||||
|
||||
doTest('base case', undefined);
|
||||
doTest('non-passive listener', false);
|
||||
doTest('passive listener', true);
|
||||
|
||||
doPreventDefault = true;
|
||||
|
||||
doTest('base case', undefined);
|
||||
doTest('non-passive listener', false);
|
||||
doTest('passive listener', true);
|
||||
|
||||
testAddListenerKey(false);
|
||||
testAddListenerKey(true);
|
||||
}
|
||||
|
||||
test();
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -123,6 +123,9 @@ public:
|
||||
// perform its associated action. This is currently only relevant for
|
||||
// wheel and touch events.
|
||||
bool mHandledByAPZ : 1;
|
||||
// True if the event is currently being handled by an event listener that
|
||||
// was registered as a passive listener.
|
||||
bool mInPassiveListener: 1;
|
||||
|
||||
// If the event is being handled in target phase, returns true.
|
||||
inline bool InTargetPhase() const
|
||||
|
Loading…
x
Reference in New Issue
Block a user