Bug 915213 - Transform all gecko input through the apzc via widget to compensate for zoom. r=bbondy

This commit is contained in:
Jim Mathies 2013-09-24 16:17:26 -05:00
parent 17fa48d08b
commit a774def610
4 changed files with 110 additions and 24 deletions

View File

@ -203,6 +203,7 @@ namespace winrt {
MetroInput::MetroInput(MetroWidget* aWidget, MetroInput::MetroInput(MetroWidget* aWidget,
UI::Core::ICoreWindow* aWindow) UI::Core::ICoreWindow* aWindow)
: mWidget(aWidget), : mWidget(aWidget),
mChromeHitTestCacheForTouch(false),
mWindow(aWindow) mWindow(aWindow)
{ {
LogFunction(); LogFunction();
@ -644,6 +645,43 @@ MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
return S_OK; return S_OK;
} }
// Tests for chrome vs. content target so we know whether input coordinates need
// to be transformed through the apz. Eventually this hit testing should move
// into the apz (bug 918288).
bool
MetroInput::HitTestChrome(const LayoutDeviceIntPoint& pt)
{
// Confirm this event targets content. We pick this up in browser's input.js.
nsMouseEvent hittest(true, NS_MOUSE_MOZHITTEST, mWidget.Get(), nsMouseEvent::eReal, nsMouseEvent::eNormal);
hittest.refPoint = pt;
nsEventStatus status;
mWidget->DispatchEvent(&hittest, status);
return (status == nsEventStatus_eConsumeNoDefault);
}
void
MetroInput::TransformRefPoint(const Foundation::Point& aPosition, LayoutDeviceIntPoint& aRefPointOut)
{
// If this event is destined for content we need to transform our ref point through
// the apz so that zoom can be accounted for.
LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(aPosition));
aRefPointOut = pt;
// This is currently a general contained rect hit test, it may produce a false positive for
// overlay chrome elements.
bool apzIntersect = mWidget->HitTestAPZC(mozilla::ScreenPoint(pt.x, pt.y));
if (apzIntersect && HitTestChrome(pt)) {
return;
}
nsMouseEvent event(true,
NS_MOUSE_MOVE,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
event.refPoint = aRefPointOut;
mWidget->ApzReceiveInputEvent(&event);
aRefPointOut = event.refPoint;
}
void void
MetroInput::InitGeckoMouseEventFromPointerPoint( MetroInput::InitGeckoMouseEventFromPointerPoint(
nsMouseEvent* aEvent, nsMouseEvent* aEvent,
@ -667,7 +705,7 @@ MetroInput::InitGeckoMouseEventFromPointerPoint(
props->get_Pressure(&pressure); props->get_Pressure(&pressure);
mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap); mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap);
aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)); TransformRefPoint(position, aEvent->refPoint);
if (!canBeDoubleTap) { if (!canBeDoubleTap) {
aEvent->clickCount = 1; aEvent->clickCount = 1;
@ -965,8 +1003,7 @@ MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
Foundation::Point position; Foundation::Point position;
aArgs->get_Position(&position); aArgs->get_Position(&position);
HandleSingleTap( HandleSingleTap(position);
LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)));
return S_OK; return S_OK;
} }
@ -987,18 +1024,20 @@ MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
Foundation::Point position; Foundation::Point position;
aArgs->get_Position(&position); aArgs->get_Position(&position);
HandleLongTap( HandleLongTap(position);
LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)));
return S_OK; return S_OK;
} }
void void
MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint) MetroInput::HandleSingleTap(const Foundation::Point& aPoint)
{ {
#ifdef DEBUG_INPUT #ifdef DEBUG_INPUT
LogFunction(); LogFunction();
#endif #endif
LayoutDeviceIntPoint refPoint;
TransformRefPoint(aPoint, refPoint);
// send mousemove // send mousemove
nsMouseEvent* mouseEvent = new nsMouseEvent(true, nsMouseEvent* mouseEvent = new nsMouseEvent(true,
@ -1006,7 +1045,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(), mWidget.Get(),
nsMouseEvent::eReal, nsMouseEvent::eReal,
nsMouseEvent::eNormal); nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint; mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1; mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
DispatchAsyncEventIgnoreStatus(mouseEvent); DispatchAsyncEventIgnoreStatus(mouseEvent);
@ -1017,7 +1056,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(), mWidget.Get(),
nsMouseEvent::eReal, nsMouseEvent::eReal,
nsMouseEvent::eNormal); nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint; mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1; mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
mouseEvent->button = nsMouseEvent::buttonType::eLeftButton; mouseEvent->button = nsMouseEvent::buttonType::eLeftButton;
@ -1028,7 +1067,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(), mWidget.Get(),
nsMouseEvent::eReal, nsMouseEvent::eReal,
nsMouseEvent::eNormal); nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint; mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1; mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
mouseEvent->button = nsMouseEvent::buttonType::eLeftButton; mouseEvent->button = nsMouseEvent::buttonType::eLeftButton;
@ -1055,18 +1094,20 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
} }
void void
MetroInput::HandleLongTap(const LayoutDeviceIntPoint& aPoint) MetroInput::HandleLongTap(const Foundation::Point& aPoint)
{ {
#ifdef DEBUG_INPUT #ifdef DEBUG_INPUT
LogFunction(); LogFunction();
#endif #endif
LayoutDeviceIntPoint refPoint;
TransformRefPoint(aPoint, refPoint);
nsMouseEvent* contextEvent = new nsMouseEvent(true, nsMouseEvent* contextEvent = new nsMouseEvent(true,
NS_CONTEXTMENU, NS_CONTEXTMENU,
mWidget.Get(), mWidget.Get(),
nsMouseEvent::eReal, nsMouseEvent::eReal,
nsMouseEvent::eNormal); nsMouseEvent::eNormal);
contextEvent->refPoint = aPoint; contextEvent->refPoint = refPoint;
contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
DispatchAsyncEventIgnoreStatus(contextEvent); DispatchAsyncEventIgnoreStatus(contextEvent);
} }
@ -1128,7 +1169,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
* (ignoring return result) and to content and return the content event * (ignoring return result) and to content and return the content event
* status result to our caller. * status result to our caller.
* 2) mTouchStartDefaultPrevented or mTouchMoveDefaultPrevented are true * 2) mTouchStartDefaultPrevented or mTouchMoveDefaultPrevented are true
* Deliver touch directly to content and bypass the apz. Our callers * Deliver touch to content after transforming through the apz. Our callers
* handle calling cancel for the touch sequence on the apz. * handle calling cancel for the touch sequence on the apz.
* 3) mTouchStartDefaultPrevented and mTouchMoveDefaultPrevented are false * 3) mTouchStartDefaultPrevented and mTouchMoveDefaultPrevented are false
* Deliver events to the apz. If the apz returns eConsumeNoDefault dispatch * Deliver events to the apz. If the apz returns eConsumeNoDefault dispatch
@ -1137,17 +1178,33 @@ MetroInput::DeliverNextQueuedTouchEvent()
* the overhead of delivering dom events.) * the overhead of delivering dom events.)
*/ */
// Test for chrome vs. content target. To do this we only use the first touch
// point since that will be the input batch target. Cache this for touch events
// since HitTestChrome has to send a dom event.
if (event->message == NS_TOUCH_START) {
nsRefPtr<Touch> touch = event->touches[0];
LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
bool apzIntersect = mWidget->HitTestAPZC(mozilla::ScreenPoint(pt.x, pt.y));
mChromeHitTestCacheForTouch = (apzIntersect && HitTestChrome(pt));
}
// Check if content called preventDefault on touchstart or first touchmove. If so // Check if content called preventDefault on touchstart or first touchmove. If so
// send directly to content, do not forward to the apz. // and the event is destined for chrome, send the event. If destined for content,
// translate coordinates through the apz then send.
if (mTouchStartDefaultPrevented || mTouchMoveDefaultPrevented) { if (mTouchStartDefaultPrevented || mTouchMoveDefaultPrevented) {
// continue delivering events to content if (!mChromeHitTestCacheForTouch) {
// ContentReceivedTouch has already been called so this shouldn't cause
// the apz to react. We still need to transform our coordinates though.
mWidget->ApzReceiveInputEvent(event);
}
mWidget->DispatchEvent(event, status); mWidget->DispatchEvent(event, status);
return status; return status;
} }
// Forward event data to apz. If the apz consumes the event, don't forward to // Forward event data to apz. If the apz consumes the event, don't forward to
// content if this is not a cancelable event. // content if this is not a cancelable event.
status = mWidget->ApzReceiveInputEvent(event); nsTouchEvent transformedEvent(*event);
status = mWidget->ApzReceiveInputEvent(event, &transformedEvent);
if (!mCancelable && status == nsEventStatus_eConsumeNoDefault) { if (!mCancelable && status == nsEventStatus_eConsumeNoDefault) {
if (!mTouchCancelSent) { if (!mTouchCancelSent) {
mTouchCancelSent = true; mTouchCancelSent = true;
@ -1156,8 +1213,9 @@ MetroInput::DeliverNextQueuedTouchEvent()
return status; return status;
} }
// Deliver event to content // Deliver event. If this is destined for chrome, use the untransformed event
mWidget->DispatchEvent(event, status); // data, if it's destined for content, use the transformed event.
mWidget->DispatchEvent(!mChromeHitTestCacheForTouch ? &transformedEvent : event, status);
return status; return status;
} }

View File

@ -145,8 +145,8 @@ public:
HRESULT OnRightTapped(IGestureRecognizer* aSender, HRESULT OnRightTapped(IGestureRecognizer* aSender,
IRightTappedEventArgs* aArgs); IRightTappedEventArgs* aArgs);
void HandleSingleTap(const mozilla::LayoutDeviceIntPoint& aPoint); void HandleSingleTap(const Point& aPoint);
void HandleLongTap(const mozilla::LayoutDeviceIntPoint& aPoint); void HandleLongTap(const Point& aPoint);
private: private:
Microsoft::WRL::ComPtr<ICoreWindow> mWindow; Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
@ -159,7 +159,13 @@ private:
void RegisterInputEvents(); void RegisterInputEvents();
void UnregisterInputEvents(); void UnregisterInputEvents();
// Hit testing for chrome content
bool mChromeHitTestCacheForTouch;
bool HitTestChrome(const LayoutDeviceIntPoint& pt);
// Event processing helpers. See function definitions for more info. // Event processing helpers. See function definitions for more info.
void TransformRefPoint(const Point& aPosition,
LayoutDeviceIntPoint& aRefPointOut);
void OnPointerNonTouch(IPointerPoint* aPoint); void OnPointerNonTouch(IPointerPoint* aPoint);
void InitGeckoMouseEventFromPointerPoint(nsMouseEvent* aEvent, void InitGeckoMouseEventFromPointerPoint(nsMouseEvent* aEvent,
IPointerPoint* aPoint); IPointerPoint* aPoint);

View File

@ -999,17 +999,38 @@ MetroWidget::ApzContentIgnoringTouch()
MetroWidget::sAPZC->ContentReceivedTouch(mRootLayerTreeId, false); MetroWidget::sAPZC->ContentReceivedTouch(mRootLayerTreeId, false);
} }
bool
MetroWidget::HitTestAPZC(ScreenPoint& pt)
{
if (!MetroWidget::sAPZC) {
return false;
}
return MetroWidget::sAPZC->HitTestAPZC(pt);
}
nsEventStatus nsEventStatus
MetroWidget::ApzReceiveInputEvent(nsTouchEvent* aEvent) MetroWidget::ApzReceiveInputEvent(nsInputEvent* aEvent)
{ {
MOZ_ASSERT(aEvent); MOZ_ASSERT(aEvent);
if (!MetroWidget::sAPZC) { if (!MetroWidget::sAPZC) {
return nsEventStatus_eIgnore; return nsEventStatus_eIgnore;
} }
nsInputEvent& event = static_cast<nsInputEvent&>(*aEvent);
return MetroWidget::sAPZC->ReceiveInputEvent(event);
}
MultiTouchInput inputData(*aEvent); nsEventStatus
return MetroWidget::sAPZC->ReceiveInputEvent(inputData); MetroWidget::ApzReceiveInputEvent(nsInputEvent* aInEvent, nsInputEvent* aOutEvent)
{
MOZ_ASSERT(aInEvent);
MOZ_ASSERT(aOutEvent);
if (!MetroWidget::sAPZC) {
return nsEventStatus_eIgnore;
}
nsInputEvent& event = static_cast<nsInputEvent&>(*aInEvent);
return MetroWidget::sAPZC->ReceiveInputEvent(event, aOutEvent);
} }
LayerManager* LayerManager*

View File

@ -24,7 +24,6 @@
#include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/LayerManagerComposite.h"
#include "Units.h"
#include "nsDeque.h" #include "nsDeque.h"
#include "APZController.h" #include "APZController.h"
@ -199,7 +198,9 @@ public:
// APZ related apis // APZ related apis
void ApzContentConsumingTouch(); void ApzContentConsumingTouch();
void ApzContentIgnoringTouch(); void ApzContentIgnoringTouch();
nsEventStatus ApzReceiveInputEvent(nsTouchEvent* aEvent); nsEventStatus ApzReceiveInputEvent(nsInputEvent* aEvent);
nsEventStatus ApzReceiveInputEvent(nsInputEvent* aInEvent, nsInputEvent* aOutEvent);
bool HitTestAPZC(mozilla::ScreenPoint& pt);
nsresult RequestContentScroll(); nsresult RequestContentScroll();
void RequestContentRepaintImplMainThread(); void RequestContentRepaintImplMainThread();