Bug 1447993, when handling pointerup while there is pointercapture, do a hit test in order to find the click target, r=masayuki

--HG--
extra : rebase_source : 160ef0aae3922cb32b11476650c15a3f55334691
This commit is contained in:
Olli Pettay 2018-03-29 19:22:59 +03:00
Родитель fc6227cd10
Коммит 57fd877895
5 изменённых файлов: 152 добавлений и 39 удалений

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

@ -506,7 +506,8 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIFrame* aTargetFrame,
nsIContent* aTargetContent,
nsEventStatus* aStatus)
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget)
{
NS_ENSURE_ARG_POINTER(aStatus);
NS_ENSURE_ARG(aPresContext);
@ -655,7 +656,8 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
MOZ_FALLTHROUGH;
case WidgetMouseEvent::eRightButton:
case WidgetMouseEvent::eMiddleButton:
SetClickCount(mouseEvent, aStatus);
RefPtr<EventStateManager> esm = ESMFromContentOrThis(aOverrideClickTarget);
esm->SetClickCount(mouseEvent, aStatus, aOverrideClickTarget);
NotifyTargetUserActivation(aEvent, aTargetContent);
break;
}
@ -922,6 +924,26 @@ EventStateManager::NotifyTargetUserActivation(WidgetEvent* aEvent,
doc->NotifyUserActivation();
}
already_AddRefed<EventStateManager>
EventStateManager::ESMFromContentOrThis(nsIContent* aContent)
{
if (aContent) {
nsIPresShell* shell = aContent->OwnerDoc()->GetShell();
if (shell) {
nsPresContext* prescontext = shell->GetPresContext();
if (prescontext) {
RefPtr<EventStateManager> esm = prescontext->EventStateManager();
if (esm) {
return esm.forget();
}
}
}
}
RefPtr<EventStateManager> esm = this;
return esm.forget();
}
void
EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
{
@ -3055,7 +3077,8 @@ nsresult
EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIFrame* aTargetFrame,
nsEventStatus* aStatus)
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget)
{
NS_ENSURE_ARG(aPresContext);
NS_ENSURE_ARG_POINTER(aStatus);
@ -3317,7 +3340,10 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
}
// Make sure to dispatch the click even if there is no frame for
// the current target element. This is required for Web compatibility.
ret = CheckForAndDispatchClick(mouseEvent, aStatus);
RefPtr<EventStateManager> esm =
ESMFromContentOrThis(aOverrideClickTarget);
ret = esm->CheckForAndDispatchClick(mouseEvent, aStatus,
aOverrideClickTarget);
}
nsIPresShell *shell = presContext->GetPresShell();
@ -4786,11 +4812,12 @@ EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent)
nsresult
EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget)
{
nsCOMPtr<nsIContent> mouseContent;
nsCOMPtr<nsIContent> mouseContent = aOverrideClickTarget;
nsIContent* mouseContentParent = nullptr;
if (mCurrentTarget) {
if (!mouseContent && mCurrentTarget) {
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
}
if (mouseContent) {
@ -4868,7 +4895,8 @@ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
nsIPresShell* aPresShell,
nsIContent* aMouseTarget,
AutoWeakFrame aCurrentTarget,
bool aNoContentDispatch)
bool aNoContentDispatch,
nsIContent* aOverrideClickTarget)
{
WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
aEvent->mWidget, WidgetMouseEvent::eReal);
@ -4883,14 +4911,21 @@ EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
event.button = aEvent->button;
event.pointerId = aEvent->pointerId;
event.inputSource = aEvent->inputSource;
nsIContent* target = aMouseTarget;
nsIFrame* targetFrame = aCurrentTarget;
if (aOverrideClickTarget) {
target = aOverrideClickTarget;
targetFrame = aOverrideClickTarget->GetPrimaryFrame();
}
return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
aMouseTarget, aStatus);
return aPresShell->HandleEventWithTarget(&event, targetFrame,
target, aStatus);
}
nsresult
EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget)
{
nsresult ret = NS_OK;
@ -4920,7 +4955,7 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
mouseContent = mouseContent->GetParent();
}
if (!mouseContent && !mCurrentTarget) {
if (!mouseContent && !mCurrentTarget && !aOverrideClickTarget) {
return NS_OK;
}
@ -4928,20 +4963,22 @@ EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
AutoWeakFrame currentTarget = mCurrentTarget;
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
presShell, mouseContent, currentTarget,
notDispatchToContents);
notDispatchToContents,
aOverrideClickTarget);
if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
mouseContent && mouseContent->IsInComposedDoc()) {
//fire double click
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
presShell, mouseContent, currentTarget,
notDispatchToContents);
notDispatchToContents,
aOverrideClickTarget);
}
if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
mouseContent->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
presShell, mouseContent, currentTarget,
false);
false, aOverrideClickTarget);
}
}
}

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

@ -92,12 +92,17 @@ public:
* be conditional based on either DOM or frame processing should occur in
* PostHandleEvent. Any centralized event processing which must occur before
* DOM or frame event handling should occur here as well.
*
* aOverrideClickTarget can be used to indicate which element should be
* used as the *up target when deciding whether to send click event.
* This is used when releasing pointer capture. Otherwise null.
*/
nsresult PreHandleEvent(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIFrame* aTargetFrame,
nsIContent* aTargetContent,
nsEventStatus* aStatus);
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget);
/* The PostHandleEvent method should contain all system processing which
* should occur conditionally based on DOM or frame processing. It should
@ -107,7 +112,8 @@ public:
nsresult PostHandleEvent(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIFrame* aTargetFrame,
nsEventStatus* aStatus);
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget);
void PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
nsIFrame* aTargetFrame, nsEventStatus& aStatus);
@ -442,10 +448,13 @@ protected:
nsIPresShell* aPresShell,
nsIContent* aMouseTarget,
AutoWeakFrame aCurrentTarget,
bool aNoContentDispatch);
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
bool aNoContentDispatch,
nsIContent* aOverrideClickTarget);
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget = nullptr);
nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus);
nsEventStatus* aStatus,
nsIContent* aOverrideClickTarget);
void EnsureDocument(nsPresContext* aPresContext);
void FlushPendingEvents(nsPresContext* aPresContext);
@ -1023,6 +1032,8 @@ private:
void NotifyTargetUserActivation(WidgetEvent* aEvent,
nsIContent* aTargetContent);
already_AddRefed<EventStateManager> ESMFromContentOrThis(nsIContent* aContent);
int32_t mLockCursor;
bool mLastFrameConsumedSetCursor;

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

@ -7077,23 +7077,29 @@ PresShell::HandleEvent(nsIFrame* aFrame,
frame = pointerCapturingContent->GetPrimaryFrame();
if (!frame) {
RefPtr<PresShell> shell =
GetShellForEventTarget(nullptr, pointerCapturingContent);
if (!shell) {
// If we can't process event for the capturing content, release
// the capture.
PointerEventHandler::ReleaseIfCaptureByDescendant(
pointerCapturingContent);
return NS_OK;
}
nsCOMPtr<nsIContent> overrideClickTarget =
GetOverrideClickTarget(aEvent, aFrame);
// Dispatch events to the capturing content even it's frame is
// destroyed.
PointerEventHandler::DispatchPointerFromMouseOrTouch(
this, nullptr, pointerCapturingContent, aEvent, false,
shell, nullptr, pointerCapturingContent, aEvent, false,
aEventStatus, nullptr);
RefPtr<PresShell> shell =
GetShellForEventTarget(nullptr, pointerCapturingContent);
if (!shell) {
// The capturing element could be changed when dispatch pointer
// events.
return NS_OK;
}
return shell->HandleEventWithTarget(aEvent, nullptr,
pointerCapturingContent,
aEventStatus, true);
aEventStatus, true, nullptr,
overrideClickTarget);
}
}
@ -7224,6 +7230,7 @@ PresShell::HandleEvent(nsIFrame* aFrame,
}
}
nsCOMPtr<nsIContent> overrideClickTarget;
if (PointerEventHandler::IsPointerEventEnabled()) {
// Dispatch pointer events from the mouse or touch events. Regarding
// pointer events from mouse, we should dispatch those pointer events to
@ -7239,6 +7246,21 @@ PresShell::HandleEvent(nsIFrame* aFrame,
nsIFrame* targetFrame =
aEvent->mClass == eTouchEventClass ? aFrame : frame;
if (pointerCapturingContent) {
overrideClickTarget = GetOverrideClickTarget(aEvent, aFrame);
shell = GetShellForEventTarget(nullptr, pointerCapturingContent);
if (!shell) {
// If we can't process event for the capturing content, release
// the capture.
PointerEventHandler::ReleaseIfCaptureByDescendant(
pointerCapturingContent);
return NS_OK;
}
targetFrame = pointerCapturingContent->GetPrimaryFrame();
frame = targetFrame;
}
AutoWeakFrame weakFrame(targetFrame);
nsCOMPtr<nsIContent> targetContent;
PointerEventHandler::DispatchPointerFromMouseOrTouch(
@ -7288,7 +7310,8 @@ PresShell::HandleEvent(nsIFrame* aFrame,
// must have been captured by us or some ancestor shell and we
// now ask the subshell to dispatch it normally.
shell->PushCurrentEventInfo(frame, targetElement);
rv = shell->HandleEventInternal(aEvent, aEventStatus, true);
rv = shell->HandleEventInternal(aEvent, aEventStatus, true,
overrideClickTarget);
#ifdef DEBUG
shell->ShowEventTargetDebug();
#endif
@ -7445,7 +7468,8 @@ nsresult
PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
nsIContent* aContent, nsEventStatus* aStatus,
bool aIsHandlingNativeEvent,
nsIContent** aTargetContent)
nsIContent** aTargetContent,
nsIContent* aOverrideClickTarget)
{
#if DEBUG
MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
@ -7460,7 +7484,8 @@ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
NS_ENSURE_STATE(!aContent || aContent->GetComposedDoc() == mDocument);
AutoPointerEventTargetUpdater updater(this, aEvent, aFrame, aTargetContent);
PushCurrentEventInfo(aFrame, aContent);
nsresult rv = HandleEventInternal(aEvent, aStatus, false);
nsresult rv =
HandleEventInternal(aEvent, aStatus, false, aOverrideClickTarget);
PopCurrentEventInfo();
return rv;
}
@ -7468,7 +7493,8 @@ PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
nsresult
PresShell::HandleEventInternal(WidgetEvent* aEvent,
nsEventStatus* aStatus,
bool aIsHandlingNativeEvent)
bool aIsHandlingNativeEvent,
nsIContent* aOverrideClickTarget)
{
RefPtr<EventStateManager> manager = mPresContext->EventStateManager();
nsresult rv = NS_OK;
@ -7640,7 +7666,8 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent,
// 1. Give event to event manager for pre event state changes and
// generation of synthetic events.
rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
mCurrentEventContent, aStatus);
mCurrentEventContent, aStatus,
aOverrideClickTarget);
// 2. Give event to the DOM for third party and JS use.
if (NS_SUCCEEDED(rv)) {
@ -7694,7 +7721,8 @@ PresShell::HandleEventInternal(WidgetEvent* aEvent,
// generation of synthetic events.
if (!mIsDestroying && NS_SUCCEEDED(rv)) {
rv = manager->PostHandleEvent(mPresContext, aEvent,
GetCurrentEventFrame(), aStatus);
GetCurrentEventFrame(), aStatus,
aOverrideClickTarget);
}
}
@ -10641,3 +10669,34 @@ PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
RemoveSheet(ToSheetType(aSheetType), aSheet);
}
nsIContent*
PresShell::GetOverrideClickTarget(WidgetGUIEvent* aEvent,
nsIFrame* aFrame)
{
if (aEvent->mMessage != eMouseUp) {
return nullptr;
}
MOZ_ASSERT(aEvent->mClass == eMouseEventClass);
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
uint32_t flags = 0;
nsPoint eventPoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, aFrame);
if (mouseEvent->mIgnoreRootScrollFrame) {
flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
}
nsIFrame* target =
FindFrameTargetedByInputEvent(aEvent, aFrame, eventPoint, flags);
if (!target) {
return nullptr;
}
nsIContent* overrideClickTarget = target->GetContent();
while (overrideClickTarget && !overrideClickTarget->IsElement()) {
overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
}
return overrideClickTarget;
}

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

@ -169,7 +169,9 @@ public:
nsIContent* aContent,
nsEventStatus* aStatus,
bool aIsHandlingNativeEvent = false,
nsIContent** aTargetContent = nullptr) override;
nsIContent** aTargetContent = nullptr,
nsIContent* aOverrideClickTarget = nullptr)
override;
nsIFrame* GetEventTargetFrame() override;
already_AddRefed<nsIContent>
GetEventTargetContent(WidgetEvent* aEvent) override;
@ -673,7 +675,8 @@ private:
*/
nsresult HandleEventInternal(WidgetEvent* aEvent,
nsEventStatus* aStatus,
bool aIsHandlingNativeEvent);
bool aIsHandlingNativeEvent,
nsIContent* aOverrideClickTarget = nullptr);
/*
* This and the next two helper methods are used to target and position the
@ -754,6 +757,8 @@ private:
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
nsIContent* GetOverrideClickTarget(WidgetGUIEvent* aEvent,
nsIFrame* aFrame);
#ifdef DEBUG
// The reflow root under which we're currently reflowing. Null when
// not in reflow.

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

@ -927,7 +927,8 @@ public:
nsIContent* aContent,
nsEventStatus* aStatus,
bool aIsHandlingNativeEvent = false,
nsIContent** aTargetContent = nullptr) = 0;
nsIContent** aTargetContent = nullptr,
nsIContent* aOverrideClickTarget = nullptr) = 0;
/**
* Dispatch event to content only (NOT full processing)