Bug 1298908 - On Windows, fire the contextmenu and long-tap events on the long-tap-up user action. r=botond,jimm

This patch prevents the Windows widget code from dispatching the contextmenu
event if APZ is handling touch input. Instead, the APZ code processes the
raw touch input, and will fire a contextmenu event when the user lifts their
finger after a long-press action, in keeping with the Windows platform
convention. Doing it this way also allows us to respect web conventions where
the web content can prevent the contextmenu event from firing by calling
preventDefault on the touchstart event; this was not possible when dispatching
the contextmenu event directly from the widget code.

This also makes long-pressing on browser chrome components work properly, as
it just shifts the point in time that the contextmenu event is fired without
changing any of the code that triggers the XUL popup. However, some changes
were needed to have the widget code ignore the synthetic mouse events that
the Windows platform sends us, because those would otherwise immediately
dismiss the contextmenu popup after it appeared.

MozReview-Commit-ID: 9HFZLC6xUAi

--HG--
extra : rebase_source : aea932d9f95454c585bcdf962d151c946b5c6ec2
This commit is contained in:
Kartikaya Gupta 2016-08-30 17:32:08 -04:00
Родитель 107c16e2ee
Коммит 48a5a4c393
6 изменённых файлов: 60 добавлений и 6 удалений

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

@ -1749,7 +1749,7 @@ TabChild::HandleTap(GeckoContentController::TapType aType,
break; break;
case GeckoContentController::TapType::eLongTapUp: case GeckoContentController::TapType::eLongTapUp:
if (mGlobal && mTabChildGlobal) { if (mGlobal && mTabChildGlobal) {
mAPZEventState->ProcessLongTapUp(); mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
} }
break; break;
case GeckoContentController::TapType::eSentinel: case GeckoContentController::TapType::eSentinel:

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

@ -258,8 +258,15 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
SendPendingTouchPreventedResponse(false); SendPendingTouchPreventedResponse(false);
#ifdef XP_WIN
// On Windows, we fire the contextmenu events when the user lifts their
// finger, in keeping with the platform convention. This happens in the
// ProcessLongTapUp function.
bool eventHandled = false;
#else
bool eventHandled = FireContextmenuEvents(aPresShell, aPoint, aScale, bool eventHandled = FireContextmenuEvents(aPresShell, aPoint, aScale,
aModifiers, widget); aModifiers, widget);
#endif
mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled); mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
if (eventHandled) { if (eventHandled) {
@ -275,10 +282,17 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
} }
void void
APZEventState::ProcessLongTapUp() APZEventState::ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers)
{ {
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); #ifdef XP_WIN
observerService->NotifyObservers(nullptr, "APZ:LongTapUp", nullptr); nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget) {
FireContextmenuEvents(aPresShell, aPoint, aScale, aModifiers, widget);
}
#endif
} }
void void

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

@ -56,7 +56,10 @@ public:
Modifiers aModifiers, Modifiers aModifiers,
const ScrollableLayerGuid& aGuid, const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId); uint64_t aInputBlockId);
void ProcessLongTapUp(); void ProcessLongTapUp(const nsCOMPtr<nsIPresShell>& aPresShell,
const CSSPoint& aPoint,
const CSSToLayoutDeviceScale& aScale,
Modifiers aModifiers);
void ProcessTouchEvent(const WidgetTouchEvent& aEvent, void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid, const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId, uint64_t aInputBlockId,

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

@ -197,7 +197,7 @@ ChromeProcessController::HandleTap(TapType aType,
aInputBlockId); aInputBlockId);
break; break;
case TapType::eLongTapUp: case TapType::eLongTapUp:
mAPZEventState->ProcessLongTapUp(); mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
break; break;
case TapType::eSentinel: case TapType::eSentinel:
// Should never happen, but we need to handle this case branch for the // Should never happen, but we need to handle this case branch for the

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

@ -5205,6 +5205,15 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
case WM_CONTEXTMENU: case WM_CONTEXTMENU:
{ {
// If the context menu is brought up by a touch long-press, then
// the APZ code is responsible for dealing with this, so we don't
// need to do anything.
if (mTouchWindow && MOUSE_INPUT_SOURCE() == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
MOZ_ASSERT(mAPZC); // since mTouchWindow is true, APZ must be enabled
result = true;
break;
}
// if the context menu is brought up from the keyboard, |lParam| // if the context menu is brought up from the keyboard, |lParam|
// will be -1. // will be -1.
LPARAM pos; LPARAM pos;
@ -7410,6 +7419,13 @@ nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd)
return window && !window->IsPopup(); return window && !window->IsPopup();
} }
static bool
IsTouchSupportEnabled(HWND aWnd)
{
nsWindow* topWindow = WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd, true));
return topWindow ? topWindow->IsTouchWindow() : false;
}
// static // static
bool bool
nsWindow::DealWithPopups(HWND aWnd, UINT aMessage, nsWindow::DealWithPopups(HWND aWnd, UINT aMessage,
@ -7441,12 +7457,32 @@ nsWindow::DealWithPopups(HWND aWnd, UINT aMessage,
nsWindow* popupWindow = static_cast<nsWindow*>(popup.get()); nsWindow* popupWindow = static_cast<nsWindow*>(popup.get());
UINT nativeMessage = WinUtils::GetNativeMessage(aMessage); UINT nativeMessage = WinUtils::GetNativeMessage(aMessage);
switch (nativeMessage) { switch (nativeMessage) {
case WM_TOUCH:
if (!IsTouchSupportEnabled(aWnd)) {
// If APZ is disabled, don't allow touch inputs to dismiss popups. The
// compatibility mouse events will do it instead.
return false;
}
MOZ_FALLTHROUGH;
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN: case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN: case WM_MBUTTONDOWN:
case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDOWN:
case WM_NCRBUTTONDOWN: case WM_NCRBUTTONDOWN:
case WM_NCMBUTTONDOWN: case WM_NCMBUTTONDOWN:
if (nativeMessage != WM_TOUCH &&
IsTouchSupportEnabled(aWnd) &&
MOUSE_INPUT_SOURCE() == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
// If any of these mouse events are really compatibility events that
// Windows is sending for touch inputs, then don't allow them to dismiss
// popups when APZ is enabled (instead we do the dismissing as part of
// WM_TOUCH handling which is more correct).
// If we don't do this, then when the user lifts their finger after a
// long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
// us will dismiss the contextmenu popup that we displayed as part of
// handling the long-tap-up.
return false;
}
if (!EventIsInsideWindow(popupWindow) && if (!EventIsInsideWindow(popupWindow) &&
GetPopupsToRollup(rollupListener, &popupsToRollup)) { GetPopupsToRollup(rollupListener, &popupsToRollup)) {
break; break;

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

@ -314,6 +314,7 @@ public:
nsIKeyEventInPluginCallback* aCallback) override; nsIKeyEventInPluginCallback* aCallback) override;
void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override; void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override;
bool IsTouchWindow() const { return mTouchWindow; }
protected: protected:
virtual ~nsWindow(); virtual ~nsWindow();