Bug 967796 - Implement Pointer Enter/Leave events support. implementation for pointer's. r=smaug

This commit is contained in:
Oleg Romashin 2014-02-10 22:34:51 -08:00
parent f627a1c40d
commit f82bb788b2
4 changed files with 127 additions and 51 deletions

View File

@ -701,7 +701,9 @@ nsIContent::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// inside chrome access only content.
bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
if ((aVisitor.mEvent->message == NS_MOUSE_ENTER_SYNTH ||
aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH) &&
aVisitor.mEvent->message == NS_MOUSE_EXIT_SYNTH ||
aVisitor.mEvent->message == NS_POINTER_OVER ||
aVisitor.mEvent->message == NS_POINTER_OUT) &&
// Check if we should stop event propagation when event has just been
// dispatched or when we're about to propagate from
// chrome access only subtree.

View File

@ -1111,7 +1111,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
aEvent->message = 0;
break;
}
case NS_MOUSE_MOVE: {
case NS_MOUSE_MOVE:
case NS_POINTER_MOVE: {
// on the Mac, GenerateDragGesture() may not return until the drag
// has completed and so |aTargetFrame| may have been deleted (moving
// a bookmark, for example). If this is the case, however, we know
@ -3378,6 +3379,16 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
SetActiveManager(this, activeContent);
}
break;
case NS_POINTER_CANCEL:
case NS_POINTER_UP: {
WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
// After UP/Cancel Touch pointers become invalid so we can remove relevant helper from Table
// Mouse/Pen pointers are valid all the time (not only between down/up)
if (pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
}
break;
}
case NS_MOUSE_BUTTON_UP:
{
ClearGlobalActiveContent(this);
@ -3753,6 +3764,7 @@ nsEventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext)
// as if the new presentation is resized, a new element may be hovered.
SetContentState(nullptr, NS_EVENT_STATE_HOVER);
}
mPointersEnterLeaveHelper.Clear();
}
void
@ -4042,10 +4054,10 @@ nsEventStateManager::IsHandlingUserInput()
}
nsIFrame*
nsEventStateManager::DispatchMouseEvent(WidgetMouseEvent* aMouseEvent,
uint32_t aMessage,
nsIContent* aTargetContent,
nsIContent* aRelatedContent)
nsEventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
uint32_t aMessage,
nsIContent* aTargetContent,
nsIContent* aRelatedContent)
{
// http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#methods
// "[When the mouse is locked on an element...e]vents that require the concept
@ -4066,16 +4078,35 @@ nsEventStateManager::DispatchMouseEvent(WidgetMouseEvent* aMouseEvent,
return mPresContext->GetPrimaryFrameFor(content);
}
PROFILER_LABEL("Input", "DispatchMouseEvent");
nsEventStatus status = nsEventStatus_eIgnore;
WidgetMouseEvent event(aMouseEvent->mFlags.mIsTrusted, aMessage,
aMouseEvent->widget, WidgetMouseEvent::eReal);
event.refPoint = aMouseEvent->refPoint;
event.modifiers = aMouseEvent->modifiers;
event.buttons = aMouseEvent->buttons;
event.pluginEvent = aMouseEvent->pluginEvent;
event.relatedTarget = aRelatedContent;
event.inputSource = aMouseEvent->inputSource;
nsAutoPtr<WidgetPointerEvent> newPointerEvent;
nsAutoPtr<WidgetMouseEvent> newMouseEvent;
WidgetMouseEvent* event = nullptr;
WidgetPointerEvent* sourcePointer = aMouseEvent->AsPointerEvent();
if (sourcePointer) {
PROFILER_LABEL("Input", "DispatchPointerEvent");
newPointerEvent =
new WidgetPointerEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
aMouseEvent->widget);
newPointerEvent->isPrimary = sourcePointer->isPrimary;
newPointerEvent->pointerId = sourcePointer->pointerId;
newPointerEvent->width = sourcePointer->width;
newPointerEvent->height = sourcePointer->height;
newPointerEvent->inputSource = sourcePointer->inputSource;
event = newPointerEvent.get();
} else {
PROFILER_LABEL("Input", "DispatchMouseEvent");
newMouseEvent =
new WidgetMouseEvent(aMouseEvent->mFlags.mIsTrusted, aMessage,
aMouseEvent->widget, WidgetMouseEvent::eReal);
event = newMouseEvent.get();
}
event->refPoint = aMouseEvent->refPoint;
event->modifiers = aMouseEvent->modifiers;
event->buttons = aMouseEvent->buttons;
event->pluginEvent = aMouseEvent->pluginEvent;
event->relatedTarget = aRelatedContent;
event->inputSource = aMouseEvent->inputSource;
nsWeakFrame previousTarget = mCurrentTarget;
@ -4084,7 +4115,7 @@ nsEventStateManager::DispatchMouseEvent(WidgetMouseEvent* aMouseEvent,
nsIFrame* targetFrame = nullptr;
if (aTargetContent) {
nsESMEventCB callback(aTargetContent);
nsEventDispatcher::Dispatch(aTargetContent, mPresContext, &event, nullptr,
nsEventDispatcher::Dispatch(aTargetContent, mPresContext, event, nullptr,
&status, &callback);
// Although the primary frame was checked in event callback,
@ -4101,17 +4132,18 @@ nsEventStateManager::DispatchMouseEvent(WidgetMouseEvent* aMouseEvent,
return targetFrame;
}
class MouseEnterLeaveDispatcher
class EnterLeaveDispatcher
{
public:
MouseEnterLeaveDispatcher(nsEventStateManager* aESM,
nsIContent* aTarget, nsIContent* aRelatedTarget,
WidgetMouseEvent* aMouseEvent, uint32_t aType)
EnterLeaveDispatcher(nsEventStateManager* aESM,
nsIContent* aTarget, nsIContent* aRelatedTarget,
WidgetMouseEvent* aMouseEvent, uint32_t aType)
: mESM(aESM), mMouseEvent(aMouseEvent), mType(aType)
{
nsPIDOMWindow* win =
aTarget ? aTarget->OwnerDoc()->GetInnerWindow() : nullptr;
if (win && win->HasMouseEnterLeaveEventListeners()) {
if (aMouseEvent->AsPointerEvent() ? win && win->HasPointerEnterLeaveEventListeners() :
win && win->HasMouseEnterLeaveEventListeners()) {
mRelatedTarget = aRelatedTarget ?
aRelatedTarget->FindFirstNonChromeOnlyAccessContent() : nullptr;
nsINode* commonParent = nullptr;
@ -4131,17 +4163,18 @@ public:
}
}
~MouseEnterLeaveDispatcher()
~EnterLeaveDispatcher()
{
if (mType == NS_MOUSEENTER) {
if (mType == NS_MOUSEENTER ||
mType == NS_POINTER_ENTER) {
for (int32_t i = mTargets.Count() - 1; i >= 0; --i) {
mESM->DispatchMouseEvent(mMouseEvent, mType, mTargets[i],
mRelatedTarget);
mESM->DispatchMouseOrPointerEvent(mMouseEvent, mType, mTargets[i],
mRelatedTarget);
}
} else {
for (int32_t i = 0; i < mTargets.Count(); ++i) {
mESM->DispatchMouseEvent(mMouseEvent, mType, mTargets[i],
mRelatedTarget);
mESM->DispatchMouseOrPointerEvent(mMouseEvent, mType, mTargets[i],
mRelatedTarget);
}
}
}
@ -4197,17 +4230,20 @@ nsEventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent,
// hover state itself, and we have optimizations for hover switching between
// two nearby elements both deep in the DOM tree that would be defeated by
// switching the hover state to null here.
if (!aMovingInto) {
bool isPointer = aMouseEvent->eventStructType == NS_POINTER_EVENT;
if (!aMovingInto && !isPointer) {
// Unset :hover
SetContentState(nullptr, NS_EVENT_STATE_HOVER);
}
MouseEnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement, aMovingInto,
aMouseEvent, NS_MOUSELEAVE);
EnterLeaveDispatcher leaveDispatcher(this, wrapper->mLastOverElement,
aMovingInto, aMouseEvent,
isPointer ? NS_POINTER_LEAVE :
NS_MOUSELEAVE);
// Fire mouseout
DispatchMouseEvent(aMouseEvent, NS_MOUSE_EXIT_SYNTH,
wrapper->mLastOverElement, aMovingInto);
DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? NS_POINTER_OUT : NS_MOUSE_EXIT_SYNTH,
wrapper->mLastOverElement, aMovingInto);
wrapper->mLastOverFrame = nullptr;
wrapper->mLastOverElement = nullptr;
@ -4252,11 +4288,14 @@ nsEventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
return;
// Remember mLastOverElement as the related content for the
// DispatchMouseEvent() call below, since NotifyMouseOut() resets it, bug 298477.
// DispatchMouseOrPointerEvent() call below, since NotifyMouseOut() resets it, bug 298477.
nsCOMPtr<nsIContent> lastOverElement = wrapper->mLastOverElement;
MouseEnterLeaveDispatcher enterDispatcher(this, aContent, lastOverElement,
aMouseEvent, NS_MOUSEENTER);
bool isPointer = aMouseEvent->eventStructType == NS_POINTER_EVENT;
EnterLeaveDispatcher enterDispatcher(this, aContent, lastOverElement,
aMouseEvent,
isPointer ? NS_POINTER_ENTER :
NS_MOUSEENTER);
NotifyMouseOut(aMouseEvent, aContent);
@ -4264,11 +4303,16 @@ nsEventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
// to that element while the first mouseOver is still ongoing.
wrapper->mFirstOverEventElement = aContent;
SetContentState(aContent, NS_EVENT_STATE_HOVER);
if (!isPointer) {
SetContentState(aContent, NS_EVENT_STATE_HOVER);
}
// Fire mouseover
wrapper->mLastOverFrame = DispatchMouseEvent(aMouseEvent, NS_MOUSE_ENTER_SYNTH,
aContent, lastOverElement);
wrapper->mLastOverFrame =
DispatchMouseOrPointerEvent(aMouseEvent,
isPointer ? NS_POINTER_OVER :
NS_MOUSE_ENTER_SYNTH,
aContent, lastOverElement);
wrapper->mLastOverElement = aContent;
// Turn recursion protection back off
@ -4376,6 +4420,9 @@ nsEventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
// Update the last known refPoint with the current refPoint.
sLastRefPoint = aMouseEvent->refPoint;
}
case NS_POINTER_MOVE:
{
// Get the target content target (mousemove target == mouseover target)
nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
if (!targetElement) {
@ -4389,17 +4436,18 @@ nsEventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
}
}
break;
case NS_POINTER_LEAVE:
case NS_MOUSE_EXIT:
{
// This is actually the window mouse exit event. We're not moving
// This is actually the window mouse exit or pointer leave event. We're not moving
// into any new element.
OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
if (helper->mLastOverFrame &&
nsContentUtils::GetTopLevelWidget(aMouseEvent->widget) !=
nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) {
// the MouseOut event widget doesn't have same top widget with
// mLastMouseOverFrame, it's a spurious event for mLastMouseOverFrame
// the Mouse/PointerOut event widget doesn't have same top widget with
// mLastOverFrame, it's a spurious event for mLastOverFrame
break;
}
@ -4419,10 +4467,21 @@ nsEventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent)
OverOutElementsWrapper*
nsEventStateManager::GetWrapperByEventID(WidgetMouseEvent* aEvent)
{
if (!mMouseEnterLeaveHelper) {
mMouseEnterLeaveHelper = new OverOutElementsWrapper();
WidgetPointerEvent* pointer = aEvent->AsPointerEvent();
if (!pointer) {
MOZ_ASSERT(aEvent->AsMouseEvent() != nullptr);
if (!mMouseEnterLeaveHelper) {
mMouseEnterLeaveHelper = new OverOutElementsWrapper();
}
return mMouseEnterLeaveHelper;
}
return mMouseEnterLeaveHelper.get();
nsRefPtr<OverOutElementsWrapper> helper;
if (!mPointersEnterLeaveHelper.Get(pointer->pointerId, getter_AddRefs(helper))) {
helper = new OverOutElementsWrapper();
mPointersEnterLeaveHelper.Put(pointer->pointerId, helper);
}
return helper;
}
void
@ -5135,6 +5194,7 @@ nsEventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent
// See bug 292146 for why we want to null this out
ResetLastOverForContent(0, mMouseEnterLeaveHelper, aContent);
mPointersEnterLeaveHelper.Enumerate(&nsEventStateManager::ResetLastOverForContent, aContent);
}
bool

View File

@ -25,7 +25,7 @@ class nsIDocShell;
class nsIDocShellTreeItem;
class imgIContainer;
class nsDOMDataTransfer;
class MouseEnterLeaveDispatcher;
class EnterLeaveDispatcher;
class nsIMarkupDocumentViewer;
class nsIScrollableFrame;
class nsITimer;
@ -234,7 +234,7 @@ public:
static nsWeakPtr sPointerLockedDoc;
protected:
friend class MouseEnterLeaveDispatcher;
friend class EnterLeaveDispatcher;
/**
* Prefs class capsules preference management.
@ -272,14 +272,14 @@ protected:
nsIFrame* aTargetFrame,
nsEventStatus* aStatus);
/**
* Turn a GUI mouse event into a mouse event targeted at the specified
* Turn a GUI mouse/pointer event into a mouse/pointer event targeted at the specified
* content. This returns the primary frame for the content (or null
* if it goes away during the event).
*/
nsIFrame* DispatchMouseEvent(mozilla::WidgetMouseEvent* aMouseEvent,
uint32_t aMessage,
nsIContent* aTargetContent,
nsIContent* aRelatedContent);
nsIFrame* DispatchMouseOrPointerEvent(mozilla::WidgetMouseEvent* aMouseEvent,
uint32_t aMessage,
nsIContent* aTargetContent,
nsIContent* aRelatedContent);
/**
* Synthesize DOM and frame mouseover and mouseout events from this
* MOUSE_MOVE or MOUSE_EXIT event.

View File

@ -536,6 +536,7 @@ public:
, tiltY(0)
, isPrimary(true)
{
UpdateFlags();
}
WidgetPointerEvent(const WidgetMouseEvent& aEvent)
@ -548,6 +549,19 @@ public:
, isPrimary(true)
{
eventStructType = NS_POINTER_EVENT;
UpdateFlags();
}
void UpdateFlags()
{
switch (message) {
case NS_POINTER_ENTER:
case NS_POINTER_LEAVE:
mFlags.mBubbles = false;
break;
default:
break;
}
}
virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE