Bug 1298886 - Fire magnify gesture events in the parent process if APZ is enabled but APZ zooming is disabled. r=botond,dvander

MozReview-Commit-ID: 1yhJW7OhI0A
This commit is contained in:
Kartikaya Gupta 2016-09-24 09:46:02 -04:00
parent cb429d93ab
commit 30133fa012
20 changed files with 315 additions and 22 deletions

View File

@ -63,6 +63,12 @@ RemoteCompositorSession::GetContentController()
return mContentController.get();
}
nsIWidget*
RemoteCompositorSession::GetWidget()
{
return mWidget;
}
RefPtr<IAPZCTreeManager>
RemoteCompositorSession::GetAPZCTreeManager() const
{

View File

@ -26,6 +26,7 @@ public:
CompositorBridgeParent* GetInProcessBridge() const override;
void SetContentController(GeckoContentController* aController) override;
GeckoContentController* GetContentController();
nsIWidget* GetWidget();
RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override;
void Shutdown() override;

View File

@ -8,6 +8,7 @@
#define mozilla_layers_GeckoContentController_h
#include "FrameMetrics.h" // for FrameMetrics, etc
#include "InputData.h" // for PinchGestureInput
#include "Units.h" // for CSSPoint, CSSRect, etc
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/EventForwards.h" // for Modifiers
@ -62,6 +63,27 @@ public:
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId) = 0;
/**
* When the apz.allow_zooming pref is set to false, the APZ will not
* translate pinch gestures to actual zooming. Instead, it will call this
* method to notify gecko of the pinch gesture, and allow it to deal with it
* however it wishes. Note that this function is not called if the pinch is
* prevented by content calling preventDefault() on the touch events, or via
* use of the touch-action property.
* @param aType One of PINCHGESTURE_START, PINCHGESTURE_SCALE, or
* PINCHGESTURE_END, indicating the phase of the pinch.
* @param aGuid The guid of the APZ that is detecting the pinch. This is
* generally the root APZC for the layers id.
* @param aSpanChange For the START or END event, this is always 0.
* For a SCALE event, this is the difference in span between the
* previous state and the new state.
* @param aModifiers The keyboard modifiers depressed during the pinch.
*/
virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) = 0;
/**
* Schedules a runnable to run on the controller/UI thread at some time
* in the future.

View File

@ -1307,6 +1307,14 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
return nsEventStatus_eIgnore;
}
// For platforms that don't support APZ zooming, dispatch a message to the
// content controller, it may want to do something else with this gesture.
if (!gfxPrefs::APZAllowZooming()) {
if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
}
}
SetState(PINCHING);
mX.SetVelocity(0);
mY.SetVelocity(0);
@ -1326,6 +1334,15 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
return nsEventStatus_eConsumeNoDefault;
}
if (!gfxPrefs::APZAllowZooming()) {
if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyPinchGesture(aEvent.mType, GetGuid(),
ViewAs<LayoutDevicePixel>(aEvent.mCurrentSpan - aEvent.mPreviousSpan,
PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF),
aEvent.modifiers);
}
}
// Only the root APZC is zoomable, and the root APZC is not allowed to have
// different x and y scales. If it did, the calculations in this function
// would have to be adjusted (as e.g. it would no longer be valid to take
@ -1430,6 +1447,12 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent
return nsEventStatus_eIgnore;
}
if (!gfxPrefs::APZAllowZooming()) {
if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
controller->NotifyPinchGesture(aEvent.mType, GetGuid(), 0, aEvent.modifiers);
}
}
SetState(NOTHING);
{

View File

@ -78,6 +78,7 @@ public:
MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
MOCK_METHOD5(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
MOCK_METHOD4(NotifyPinchGesture, void(PinchGestureInput::PinchGestureType, const ScrollableLayerGuid&, LayoutDeviceCoord, Modifiers));
// Can't use the macros with already_AddRefed :(
void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
RefPtr<Runnable> task = aTask;

View File

@ -102,6 +102,30 @@ public:
: APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
{
}
void DoPinchWithPreventDefaultTest() {
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
MakeApzcWaitForMainThread();
MakeApzcZoomable();
int touchInputId = 0;
uint64_t blockId = 0;
PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
nullptr, nullptr, &blockId);
// Send the prevent-default notification for the touch block
apzc->ContentReceivedInputBlock(blockId, true);
// verify the metrics didn't change (i.e. the pinch was ignored)
FrameMetrics fm = apzc->GetFrameMetrics();
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
apzc->AssertStateIsReset();
}
};
TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) {
@ -137,28 +161,32 @@ TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAl
DoPinchTest(false, &behaviors);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
// Since we are preventing the pinch action via touch-action we should not be
// sending the pinch gesture notifications that would normally be sent when
// APZAllowZooming is false.
EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
mozilla::layers::AllowedTouchBehavior::NONE };
DoPinchTest(false, &behaviors);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
DoPinchWithPreventDefaultTest();
}
MakeApzcWaitForMainThread();
MakeApzcZoomable();
TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) {
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
int touchInputId = 0;
uint64_t blockId = 0;
PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
nullptr, nullptr, &blockId);
// Since we are preventing the pinch action we should not be sending the pinch
// gesture notifications that would normally be sent when APZAllowZooming is
// false.
EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
// Send the prevent-default notification for the touch block
apzc->ContentReceivedInputBlock(blockId, true);
// verify the metrics didn't change (i.e. the pinch was ignored)
FrameMetrics fm = apzc->GetFrameMetrics();
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
apzc->AssertStateIsReset();
DoPinchWithPreventDefaultTest();
}
TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
@ -179,3 +207,33 @@ TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
EXPECT_EQ(325, fm.GetScrollOffset().y);
EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
// When APZAllowZooming is false, the ZoomConstraintsClient produces
// ZoomConstraints with mAllowZoom set to false.
MakeApzcUnzoomable();
// With APZAllowZooming false, we expect the NotifyPinchGesture function to
// get called as the pinch progresses, but the metrics shouldn't change.
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
int touchInputId = 0;
uint64_t blockId = 0;
PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
nullptr, nullptr, &blockId);
// verify the metrics didn't change (i.e. the pinch was ignored inside APZ)
FrameMetrics fm = apzc->GetFrameMetrics();
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
apzc->AssertStateIsReset();
}

View File

@ -903,6 +903,35 @@ APZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame* aFrame)
|| aFrame->LastSmoothScrollOrigin();
}
/* static */ void
APZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers,
nsIWidget* aWidget)
{
EventMessage msg;
switch (aType) {
case PinchGestureInput::PINCHGESTURE_START:
msg = eMagnifyGestureStart;
break;
case PinchGestureInput::PINCHGESTURE_SCALE:
msg = eMagnifyGestureUpdate;
break;
case PinchGestureInput::PINCHGESTURE_END:
msg = eMagnifyGesture;
break;
case PinchGestureInput::PINCHGESTURE_SENTINEL:
default:
MOZ_ASSERT_UNREACHABLE("Invalid gesture type");
return;
}
WidgetSimpleGestureEvent event(true, msg, aWidget);
event.mDelta = aSpanChange;
event.mModifiers = aModifiers;
DispatchWidgetEvent(event);
}
} // namespace layers
} // namespace mozilla

View File

@ -7,6 +7,7 @@
#define mozilla_layers_APZCCallbackHelper_h
#include "FrameMetrics.h"
#include "InputData.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Function.h"
#include "mozilla/layers/APZUtils.h"
@ -187,6 +188,15 @@ public:
*/
static bool
IsScrollInProgress(nsIScrollableFrame* aFrame);
/* Notify content of the progress of a pinch gesture that APZ won't do
* zooming for (because the apz.allow_zooming pref is false). This function
* will dispatch appropriate WidgetSimpleGestureEvent events to gecko.
*/
static void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers,
nsIWidget* aWidget);
private:
static uint64_t sLastTargetAPZCNotificationInputBlock;
};

View File

@ -207,6 +207,28 @@ ChromeProcessController::HandleTap(TapType aType,
}
}
void
ChromeProcessController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers)
{
if (MessageLoop::current() != mUILoop) {
mUILoop->PostTask(NewRunnableMethod
<PinchGestureInput::PinchGestureType,
ScrollableLayerGuid,
LayoutDeviceCoord,
Modifiers>(this,
&ChromeProcessController::NotifyPinchGesture,
aType, aGuid, aSpanChange, aModifiers));
return;
}
if (mWidget) {
APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mWidget.get());
}
}
void
ChromeProcessController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange,

View File

@ -53,6 +53,10 @@ public:
Modifiers aModifiers,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId) override;
virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) override;
virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange,
int aArg) override;

View File

@ -143,6 +143,17 @@ ContentProcessController::HandleTap(
MOZ_ASSERT(false);
}
void
ContentProcessController::NotifyPinchGesture(
PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers)
{
// This should never get called
MOZ_ASSERT_UNREACHABLE("Unexpected message to content process");
}
void
ContentProcessController::NotifyAPZStateChange(
const ScrollableLayerGuid& aGuid,

View File

@ -54,6 +54,11 @@ public:
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId) override;
void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) override;
void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
APZStateChange aChange,
int aArg) override;

View File

@ -8,6 +8,7 @@
#include "InputData.h" // for InputData
#include "mozilla/dom/TabParent.h" // for TabParent
#include "mozilla/layers/APZCCallbackHelper.h" // for APZCCallbackHelper
#include "mozilla/layers/RemoteCompositorSession.h" // for RemoteCompositorSession
namespace mozilla {
@ -241,6 +242,26 @@ APZCTreeManagerChild::RecvHandleTap(const TapType& aType,
return true;
}
bool
APZCTreeManagerChild::RecvNotifyPinchGesture(const PinchGestureType& aType,
const ScrollableLayerGuid& aGuid,
const LayoutDeviceCoord& aSpanChange,
const Modifiers& aModifiers)
{
// This will only get sent from the GPU process to the parent process, so
// this function should never get called in the content process.
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// We want to handle it in this process regardless of what the target guid
// of the pinch is. This may change in the future.
if (mCompositorSession &&
mCompositorSession->GetWidget()) {
APZCCallbackHelper::NotifyPinchGesture(aType, aSpanChange, aModifiers, mCompositorSession->GetWidget());
}
return true;
}
void
APZCTreeManagerChild::OnProcessingError(
Result aCode,

View File

@ -98,6 +98,11 @@ protected:
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId) override;
bool RecvNotifyPinchGesture(const PinchGestureType& aType,
const ScrollableLayerGuid& aGuid,
const LayoutDeviceCoord& aSpanChange,
const Modifiers& aModifiers) override;
virtual
~APZCTreeManagerChild() { }

View File

@ -2454,11 +2454,9 @@ CompositorBridgeParent::GetIndirectShadowTree(uint64_t aId)
return &cit->second;
}
/* static */ APZCTreeManagerParent*
CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId)
static CompositorBridgeParent::LayerTreeState*
GetStateForRoot(uint64_t aContentLayersId, const MonitorAutoLock& aProofOfLock)
{
MonitorAutoLock lock(*sIndirectLayerTreesLock);
CompositorBridgeParent::LayerTreeState* state = nullptr;
LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aContentLayersId);
if (sIndirectLayerTrees.end() != itr) {
@ -2477,9 +2475,27 @@ CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersI
state = (sIndirectLayerTrees.end() != itr) ? &itr->second : nullptr;
}
return state;
}
/* static */ APZCTreeManagerParent*
CompositorBridgeParent::GetApzcTreeManagerParentForRoot(uint64_t aContentLayersId)
{
MonitorAutoLock lock(*sIndirectLayerTreesLock);
CompositorBridgeParent::LayerTreeState* state =
GetStateForRoot(aContentLayersId, lock);
return state ? state->mApzcTreeManagerParent : nullptr;
}
/* static */ GeckoContentController*
CompositorBridgeParent::GetGeckoContentControllerForRoot(uint64_t aContentLayersId)
{
MonitorAutoLock lock(*sIndirectLayerTreesLock);
CompositorBridgeParent::LayerTreeState* state =
GetStateForRoot(aContentLayersId, lock);
return state ? state->mController.get() : nullptr;
}
PTextureParent*
CompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
const LayersBackend& aLayersBackend,

View File

@ -480,6 +480,12 @@ public:
*/
static APZCTreeManagerParent* GetApzcTreeManagerParentForRoot(
uint64_t aContentLayersId);
/**
* Same as the GetApzcTreeManagerParentForRoot function, but returns
* the GeckoContentController for the parent process.
*/
static GeckoContentController* GetGeckoContentControllerForRoot(
uint64_t aContentLayersId);
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
/**

View File

@ -9,6 +9,7 @@ include "ipc/nsGUIEventIPC.h";
include protocol PCompositorBridge;
using CSSRect from "Units.h";
using LayoutDeviceCoord from "Units.h";
using LayoutDeviceIntPoint from "Units.h";
using mozilla::LayoutDevicePoint from "Units.h";
using ScreenPoint from "Units.h";
@ -31,6 +32,7 @@ using class mozilla::MultiTouchInput from "InputData.h";
using class mozilla::MouseInput from "InputData.h";
using class mozilla::PanGestureInput from "InputData.h";
using class mozilla::PinchGestureInput from "InputData.h";
using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
using class mozilla::TapGestureInput from "InputData.h";
using class mozilla::ScrollWheelInput from "InputData.h";
@ -128,6 +130,9 @@ child:
async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
async NotifyPinchGesture(PinchGestureType aType, ScrollableLayerGuid aGuid,
LayoutDeviceCoord aSpanChange, Modifiers aModifiers);
};
} // namespace gfx

View File

@ -85,6 +85,45 @@ RemoteContentController::HandleTap(TapType aTapType,
}
}
void
RemoteContentController::NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers)
{
APZThreadUtils::AssertOnControllerThread();
// For now we only ever want to handle this NotifyPinchGesture message in
// the parent process, even if the APZ is sending it to a content process.
// If we're in the GPU process, try to find a handle to the parent process
// and send it there.
if (XRE_IsGPUProcess()) {
MOZ_ASSERT(MessageLoop::current() == mCompositorThread);
// The raw pointer to APZCTreeManagerParent is ok here because we are on the
// compositor thread.
APZCTreeManagerParent* apzctmp =
CompositorBridgeParent::GetApzcTreeManagerParentForRoot(aGuid.mLayersId);
if (apzctmp) {
Unused << apzctmp->SendNotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
return;
}
}
// If we're in the parent process, handle it directly. We don't have a handle
// to the widget though, so we fish out the ChromeProcessController and
// delegate to that instead.
if (XRE_IsParentProcess()) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<GeckoContentController> rootController =
CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId);
if (rootController) {
rootController->NotifyPinchGesture(aType, aGuid, aSpanChange, aModifiers);
}
}
}
void
RemoteContentController::PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs)
{

View File

@ -48,6 +48,11 @@ public:
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId) override;
virtual void NotifyPinchGesture(PinchGestureInput::PinchGestureType aType,
const ScrollableLayerGuid& aGuid,
LayoutDeviceCoord aSpanChange,
Modifiers aModifiers) override;
virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
virtual bool IsRepaintThread() override;

View File

@ -55,6 +55,10 @@ enum class PixelCastJustification : uint8_t {
MultipleAsyncTransforms
};
template <class TargetUnits, class SourceUnits>
gfx::CoordTyped<TargetUnits> ViewAs(const gfx::CoordTyped<SourceUnits>& aCoord, PixelCastJustification) {
return gfx::CoordTyped<TargetUnits>(aCoord.value);
}
template <class TargetUnits, class SourceUnits>
gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, PixelCastJustification) {
return gfx::SizeTyped<TargetUnits>(aSize.width, aSize.height);