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,
UI::Core::ICoreWindow* aWindow)
: mWidget(aWidget),
mChromeHitTestCacheForTouch(false),
mWindow(aWindow)
{
LogFunction();
@ -644,6 +645,43 @@ MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
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
MetroInput::InitGeckoMouseEventFromPointerPoint(
nsMouseEvent* aEvent,
@ -667,7 +705,7 @@ MetroInput::InitGeckoMouseEventFromPointerPoint(
props->get_Pressure(&pressure);
mGestureRecognizer->CanBeDoubleTap(aPointerPoint, &canBeDoubleTap);
aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position));
TransformRefPoint(position, aEvent->refPoint);
if (!canBeDoubleTap) {
aEvent->clickCount = 1;
@ -965,8 +1003,7 @@ MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
Foundation::Point position;
aArgs->get_Position(&position);
HandleSingleTap(
LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)));
HandleSingleTap(position);
return S_OK;
}
@ -987,18 +1024,20 @@ MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
Foundation::Point position;
aArgs->get_Position(&position);
HandleLongTap(
LayoutDeviceIntPoint::FromUntyped(MetroUtils::LogToPhys(position)));
HandleLongTap(position);
return S_OK;
}
void
MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
MetroInput::HandleSingleTap(const Foundation::Point& aPoint)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
LayoutDeviceIntPoint refPoint;
TransformRefPoint(aPoint, refPoint);
// send mousemove
nsMouseEvent* mouseEvent = new nsMouseEvent(true,
@ -1006,7 +1045,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint;
mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
DispatchAsyncEventIgnoreStatus(mouseEvent);
@ -1017,7 +1056,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint;
mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
mouseEvent->button = nsMouseEvent::buttonType::eLeftButton;
@ -1028,7 +1067,7 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
mouseEvent->refPoint = aPoint;
mouseEvent->refPoint = refPoint;
mouseEvent->clickCount = 1;
mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
mouseEvent->button = nsMouseEvent::buttonType::eLeftButton;
@ -1055,18 +1094,20 @@ MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint)
}
void
MetroInput::HandleLongTap(const LayoutDeviceIntPoint& aPoint)
MetroInput::HandleLongTap(const Foundation::Point& aPoint)
{
#ifdef DEBUG_INPUT
LogFunction();
#endif
LayoutDeviceIntPoint refPoint;
TransformRefPoint(aPoint, refPoint);
nsMouseEvent* contextEvent = new nsMouseEvent(true,
NS_CONTEXTMENU,
mWidget.Get(),
nsMouseEvent::eReal,
nsMouseEvent::eNormal);
contextEvent->refPoint = aPoint;
contextEvent->refPoint = refPoint;
contextEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
DispatchAsyncEventIgnoreStatus(contextEvent);
}
@ -1128,7 +1169,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
* (ignoring return result) and to content and return the content event
* status result to our caller.
* 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.
* 3) mTouchStartDefaultPrevented and mTouchMoveDefaultPrevented are false
* Deliver events to the apz. If the apz returns eConsumeNoDefault dispatch
@ -1137,17 +1178,33 @@ MetroInput::DeliverNextQueuedTouchEvent()
* 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
// 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) {
// 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);
return status;
}
// Forward event data to apz. If the apz consumes the event, don't forward to
// 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 (!mTouchCancelSent) {
mTouchCancelSent = true;
@ -1156,8 +1213,9 @@ MetroInput::DeliverNextQueuedTouchEvent()
return status;
}
// Deliver event to content
mWidget->DispatchEvent(event, status);
// Deliver event. If this is destined for chrome, use the untransformed event
// data, if it's destined for content, use the transformed event.
mWidget->DispatchEvent(!mChromeHitTestCacheForTouch ? &transformedEvent : event, status);
return status;
}

View File

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

View File

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

View File

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