Bug 941995 - Disable double-tapping and click delay on pages that are device-width or narrower (B2G). r=mbrubeck,botond

This commit is contained in:
Kartikaya Gupta 2014-02-24 19:22:45 -05:00
parent a7b51a7015
commit 31032942ac
11 changed files with 77 additions and 26 deletions

View File

@ -26,10 +26,11 @@ class MOZ_STACK_CLASS nsViewportInfo
{
public:
nsViewportInfo(const mozilla::ScreenIntSize& aDisplaySize,
bool aAllowZoom = true) :
bool aAllowZoom = true, bool aAllowDoubleTapZoom = true) :
mDefaultZoom(1.0),
mAutoSize(true),
mAllowZoom(aAllowZoom)
mAllowZoom(aAllowZoom),
mAllowDoubleTapZoom(aAllowDoubleTapZoom)
{
mSize = mozilla::gfx::RoundedToInt(mozilla::ScreenSize(aDisplaySize) / mDefaultZoom);
mozilla::CSSToLayoutDeviceScale pixelRatio(1.0f);
@ -43,13 +44,15 @@ class MOZ_STACK_CLASS nsViewportInfo
const mozilla::CSSToScreenScale& aMaxZoom,
const mozilla::CSSIntSize& aSize,
bool aAutoSize,
bool aAllowZoom) :
bool aAllowZoom,
bool aAllowDoubleTapZoom) :
mDefaultZoom(aDefaultZoom),
mMinZoom(aMinZoom),
mMaxZoom(aMaxZoom),
mSize(aSize),
mAutoSize(aAutoSize),
mAllowZoom(aAllowZoom)
mAllowZoom(aAllowZoom),
mAllowDoubleTapZoom(aAllowDoubleTapZoom)
{
ConstrainViewportValues();
}
@ -63,6 +66,9 @@ class MOZ_STACK_CLASS nsViewportInfo
bool IsAutoSizeEnabled() { return mAutoSize; }
bool IsZoomAllowed() { return mAllowZoom; }
bool IsDoubleTapZoomAllowed() { return mAllowDoubleTapZoom; }
void SetAllowDoubleTapZoom(bool aAllowDoubleTapZoom) { mAllowDoubleTapZoom = aAllowDoubleTapZoom; }
private:
@ -94,6 +100,11 @@ class MOZ_STACK_CLASS nsViewportInfo
// Whether or not the user can zoom in and out on the page. Default is true.
bool mAllowZoom;
// Whether or not the user can double-tap to zoom in. When this is disabled
// we can dispatch click events faster on a single tap because we don't have
// to wait to detect the double-tap
bool mAllowDoubleTapZoom;
};
#endif

View File

@ -7453,11 +7453,15 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
nsViewportInfo
nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
{
// In cases where the width of the CSS viewport is less than or equal to the width
// of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
// behaviour. See bug 941995 for details.
switch (mViewportType) {
case DisplayWidthHeight:
return nsViewportInfo(aDisplaySize);
case DisplayWidthHeightNoZoom:
return nsViewportInfo(aDisplaySize, /* allowZoom */ false);
return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false);
case Unknown:
{
nsAutoString viewport;
@ -7477,7 +7481,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
{
// We're making an assumption that the docType can't change here
mViewportType = DisplayWidthHeight;
return nsViewportInfo(aDisplaySize);
return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
}
}
}
@ -7486,7 +7490,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
if (handheldFriendly.EqualsLiteral("true")) {
mViewportType = DisplayWidthHeight;
return nsViewportInfo(aDisplaySize);
return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
}
// Bug 940036. This is bad. When FirefoxOS was built, apps installed
@ -7509,7 +7513,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
"ImplicitMetaViewportTagFallback");
}
mViewportType = DisplayWidthHeightNoZoom;
return nsViewportInfo(aDisplaySize, /* allowZoom */ false);
return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false);
}
}
@ -7583,6 +7587,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
(userScalable.EqualsLiteral("false"))) {
mAllowZoom = false;
}
mAllowDoubleTapZoom = mAllowZoom;
mScaleStrEmpty = scaleStr.IsEmpty();
mWidthStrEmpty = widthStr.IsEmpty();
@ -7649,7 +7654,7 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
}
return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
mAutoSize, mAllowZoom);
mAutoSize, mAllowZoom, mAllowDoubleTapZoom);
}
}

View File

@ -1632,7 +1632,7 @@ private:
mozilla::LayoutDeviceToScreenScale mScaleMaxFloat;
mozilla::LayoutDeviceToScreenScale mScaleFloat;
mozilla::CSSToLayoutDeviceScale mPixelRatio;
bool mAutoSize, mAllowZoom, mValidScaleFloat, mValidMaxScale, mScaleStrEmpty, mWidthStrEmpty;
bool mAutoSize, mAllowZoom, mAllowDoubleTapZoom, mValidScaleFloat, mValidMaxScale, mScaleStrEmpty, mWidthStrEmpty;
mozilla::CSSIntSize mViewportSize;
nsrefcnt mStackRefCnt;

View File

@ -551,10 +551,12 @@ TabChild::HandlePossibleViewportChange()
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
uint32_t presShellId;
ViewID viewId;
if (APZCCallbackHelper::GetScrollIdentifiers(document->GetDocumentElement(),
&presShellId, &viewId)) {
bool scrollIdentifiersValid = APZCCallbackHelper::GetScrollIdentifiers(
document->GetDocumentElement(), &presShellId, &viewId);
if (scrollIdentifiersValid) {
ZoomConstraints constraints(
viewportInfo.IsZoomAllowed(),
viewportInfo.IsDoubleTapZoomAllowed(),
viewportInfo.GetMinZoom(),
viewportInfo.GetMaxZoom());
SendUpdateZoomConstraints(presShellId,
@ -563,7 +565,6 @@ TabChild::HandlePossibleViewportChange()
constraints);
}
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
CSSSize viewport(viewportInfo.GetSize());
@ -666,6 +667,25 @@ TabChild::HandlePossibleViewportChange()
// Force a repaint with these metrics. This, among other things, sets the
// displayport, so we start with async painting.
ProcessUpdateFrame(metrics);
if (viewportInfo.IsZoomAllowed() && scrollIdentifiersValid) {
// If the CSS viewport is narrower than the screen (i.e. width <= device-width)
// then we disable double-tap-to-zoom behaviour.
bool allowDoubleTapZoom = (viewport.width > screenW / metrics.mDevPixelsPerCSSPixel.scale);
if (allowDoubleTapZoom != viewportInfo.IsDoubleTapZoomAllowed()) {
viewportInfo.SetAllowDoubleTapZoom(allowDoubleTapZoom);
ZoomConstraints constraints(
viewportInfo.IsZoomAllowed(),
viewportInfo.IsDoubleTapZoomAllowed(),
viewportInfo.GetMinZoom(),
viewportInfo.GetMaxZoom());
SendUpdateZoomConstraints(presShellId,
viewId,
/* isRoot = */ true,
constraints);
}
}
}
nsresult

View File

@ -733,6 +733,7 @@ struct ParamTraits<mozilla::layers::ZoomConstraints>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mAllowZoom);
WriteParam(aMsg, aParam.mAllowDoubleTapZoom);
WriteParam(aMsg, aParam.mMinZoom);
WriteParam(aMsg, aParam.mMaxZoom);
}
@ -740,6 +741,7 @@ struct ParamTraits<mozilla::layers::ZoomConstraints>
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
return (ReadParam(aMsg, aIter, &aResult->mAllowZoom) &&
ReadParam(aMsg, aIter, &aResult->mAllowDoubleTapZoom) &&
ReadParam(aMsg, aIter, &aResult->mMinZoom) &&
ReadParam(aMsg, aIter, &aResult->mMaxZoom));
}

View File

@ -410,19 +410,23 @@ struct ScrollableLayerGuid {
struct ZoomConstraints {
bool mAllowZoom;
bool mAllowDoubleTapZoom;
CSSToScreenScale mMinZoom;
CSSToScreenScale mMaxZoom;
ZoomConstraints()
: mAllowZoom(true)
, mAllowDoubleTapZoom(true)
{
MOZ_COUNT_CTOR(ZoomConstraints);
}
ZoomConstraints(bool aAllowZoom,
bool aAllowDoubleTapZoom,
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom)
: mAllowZoom(aAllowZoom)
, mAllowDoubleTapZoom(aAllowDoubleTapZoom)
, mMinZoom(aMinZoom)
, mMaxZoom(aMaxZoom)
{
@ -437,6 +441,7 @@ struct ZoomConstraints {
bool operator==(const ZoomConstraints& other) const
{
return mAllowZoom == other.mAllowZoom
&& mAllowDoubleTapZoom == other.mAllowDoubleTapZoom
&& mMinZoom == other.mMinZoom
&& mMaxZoom == other.mMaxZoom;
}

View File

@ -450,7 +450,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
mX(MOZ_THIS_IN_INITIALIZER_LIST()),
mY(MOZ_THIS_IN_INITIALIZER_LIST()),
mPanDirRestricted(false),
mZoomConstraints(false, MIN_ZOOM, MAX_ZOOM),
mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
mLastSampleTime(GetFrameTime()),
mState(NOTHING),
mLastAsyncScrollTime(GetFrameTime()),
@ -957,9 +957,9 @@ nsEventStatus AsyncPanZoomController::OnLongPressUp(const TapGestureInput& aEven
nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
// If mZoomConstraints.mAllowZoom is true we wait for a call to OnSingleTapConfirmed before
// If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
// sending event to content
if (controller && !AllowZoom()) {
if (controller && !AllowDoubleTapZoom()) {
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
CSSIntPoint geckoScreenPoint;
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
@ -1000,7 +1000,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
APZC_LOG("%p got a double-tap in state %d\n", this, mState);
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
if (controller) {
if (AllowZoom()) {
if (AllowDoubleTapZoom()) {
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
CSSIntPoint geckoScreenPoint;
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
@ -1961,6 +1961,11 @@ bool AsyncPanZoomController::AllowZoom() {
&& !(mFrameMetrics.GetDisableScrollingX() || mFrameMetrics.GetDisableScrollingY());
}
bool AsyncPanZoomController::AllowDoubleTapZoom() {
ReentrantMonitorAutoEnter lock(mMonitor);
return mZoomConstraints.mAllowDoubleTapZoom && AllowZoom();
}
void AsyncPanZoomController::SetContentResponseTimer() {
if (!mContentResponseTimeoutTask) {
mContentResponseTimeoutTask =
@ -1976,14 +1981,15 @@ void AsyncPanZoomController::TimeoutContentResponse() {
}
void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConstraints) {
APZC_LOG("%p updating zoom constraints to %d %f %f\n", this, aConstraints.mAllowZoom,
aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
APZC_LOG("%p updating zoom constraints to %d %d %f %f\n", this, aConstraints.mAllowZoom,
aConstraints.mAllowDoubleTapZoom, aConstraints.mMinZoom.scale, aConstraints.mMaxZoom.scale);
if (IsFloatNaN(aConstraints.mMinZoom.scale) || IsFloatNaN(aConstraints.mMaxZoom.scale)) {
NS_WARNING("APZC received zoom constraints with NaN values; dropping...\n");
return;
}
// inf float values and other bad cases should be sanitized by the code below.
mZoomConstraints.mAllowZoom = aConstraints.mAllowZoom;
mZoomConstraints.mAllowDoubleTapZoom = aConstraints.mAllowDoubleTapZoom;
mZoomConstraints.mMinZoom = (MIN_ZOOM > aConstraints.mMinZoom ? MIN_ZOOM : aConstraints.mMinZoom);
mZoomConstraints.mMaxZoom = (MAX_ZOOM > aConstraints.mMaxZoom ? aConstraints.mMaxZoom : MAX_ZOOM);
if (mZoomConstraints.mMaxZoom < mZoomConstraints.mMinZoom) {

View File

@ -600,6 +600,7 @@ private:
bool IsPanningState(PanZoomState mState);
bool AllowZoom();
bool AllowDoubleTapZoom();
enum AxisLockMode {
FREE, /* No locking at all */

View File

@ -331,7 +331,7 @@ TEST(AsyncPanZoomController, Pinch) {
fm.mScrollOffset = CSSPoint(300, 300);
fm.mZoom = CSSToScreenScale(2.0);
apzc->SetFrameMetrics(fm);
apzc->UpdateZoomConstraints(ZoomConstraints(true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@ -408,7 +408,7 @@ TEST(AsyncPanZoomController, Overzoom) {
fm.mScrollOffset = CSSPoint(10, 0);
fm.mZoom = CSSToScreenScale(1.0);
apzc->SetFrameMetrics(fm);
apzc->UpdateZoomConstraints(ZoomConstraints(true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@ -673,7 +673,7 @@ TEST(AsyncPanZoomController, ShortPress) {
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
apzc->UpdateZoomConstraints(ZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
int time = 0;
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 100, mcc.get());
@ -697,7 +697,7 @@ TEST(AsyncPanZoomController, MediumPress) {
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
apzc->UpdateZoomConstraints(ZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
int time = 0;
nsEventStatus status = ApzcTap(apzc, 10, 10, time, 400, mcc.get());
@ -722,7 +722,7 @@ DoLongPressTest(bool aShouldUseTouchAction, uint32_t aBehavior) {
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
apzc->UpdateZoomConstraints(ZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
nsTArray<uint32_t> values;
values.AppendElement(aBehavior);
@ -785,7 +785,7 @@ TEST(AsyncPanZoomController, LongPressPreventDefault) {
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
apzc->UpdateZoomConstraints(ZoomConstraints(false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);

View File

@ -292,6 +292,7 @@ APZController::GetRootZoomConstraints(ZoomConstraints* aOutConstraints)
// Until we support the meta-viewport tag properly allow zooming
// from 1/4 to 4x by default.
aOutConstraints->mAllowZoom = true;
aOutConstraints->mAllowDoubleTapZoom = false;
aOutConstraints->mMinZoom = CSSToScreenScale(0.25f);
aOutConstraints->mMaxZoom = CSSToScreenScale(4.0f);
return true;

View File

@ -1691,7 +1691,7 @@ MetroWidget::Observe(nsISupports *subject, const char *topic, const char16_t *da
ScrollableLayerGuid guid = ScrollableLayerGuid(mRootLayerTreeId, presShellId, viewId);
APZController::sAPZC->UpdateZoomConstraints(guid,
ZoomConstraints(false, CSSToScreenScale(1.0f), CSSToScreenScale(1.0f)));
ZoomConstraints(false, false, CSSToScreenScale(1.0f), CSSToScreenScale(1.0f)));
}
return NS_OK;
}