Title: [143355] trunk/Source/WebKit/chromium
Revision
143355
Author
ael...@chromium.org
Date
2013-02-19 10:42:27 -0800 (Tue, 19 Feb 2013)

Log Message

[chromium] Fix races in double-tap zoom minimum scale policy
https://bugs.webkit.org/show_bug.cgi?id=110183

Reviewed by Adam Barth.

Double-tap zoom on Android is supposed to return to minimum scale
if no pinch zoom occurred since the last double-tap. Because both
pinch zoom and the result of double-tap zoom gets passed in from CC
via applyScrollAndScale, this logic was brittle and prone to races
depending on when the animation update was received. This patch
keeps track of what the target double-tap scale is to make it more
robust.

I also fixed double-tap zoom test mocking to exercise the entire
page scale animation flow (our previous way of testing was hiding the
raciness), and added a new test case in DivAutoZoomMultipleParamsTest.

* src/WebViewImpl.cpp:
(WebKit::WebViewImpl::WebViewImpl):
(WebKit::WebViewImpl::startPageScaleAnimation):
(WebKit):
(WebKit::WebViewImpl::enableFakeDoubleTapAnimationForTesting):
(WebKit::WebViewImpl::computeScaleAndScrollForHitRect):
(WebKit::WebViewImpl::animateZoomAroundPoint):
(WebKit::WebViewImpl::didCommitLoad):
(WebKit::WebViewImpl::applyScrollAndScale):
* src/WebViewImpl.h:
(WebViewImpl):
(WebKit::WebViewImpl::fakeDoubleTapAnimationPendingForTesting):
(WebKit::WebViewImpl::fakeDoubleTapTargetPositionForTesting):
(WebKit::WebViewImpl::fakeDoubleTapPageScaleFactorForTesting):
(WebKit::WebViewImpl::fakeDoubleTapUseAnchorForTesting):
* tests/WebFrameTest.cpp:

Modified Paths

Diff

Modified: trunk/Source/WebKit/chromium/ChangeLog (143354 => 143355)


--- trunk/Source/WebKit/chromium/ChangeLog	2013-02-19 18:40:03 UTC (rev 143354)
+++ trunk/Source/WebKit/chromium/ChangeLog	2013-02-19 18:42:27 UTC (rev 143355)
@@ -1,3 +1,39 @@
+2013-02-19  Alexandre Elias  <ael...@chromium.org>
+
+        [chromium] Fix races in double-tap zoom minimum scale policy
+        https://bugs.webkit.org/show_bug.cgi?id=110183
+
+        Reviewed by Adam Barth.
+
+        Double-tap zoom on Android is supposed to return to minimum scale
+        if no pinch zoom occurred since the last double-tap. Because both
+        pinch zoom and the result of double-tap zoom gets passed in from CC
+        via applyScrollAndScale, this logic was brittle and prone to races
+        depending on when the animation update was received. This patch
+        keeps track of what the target double-tap scale is to make it more
+        robust.
+
+        I also fixed double-tap zoom test mocking to exercise the entire
+        page scale animation flow (our previous way of testing was hiding the
+        raciness), and added a new test case in DivAutoZoomMultipleParamsTest.
+
+        * src/WebViewImpl.cpp:
+        (WebKit::WebViewImpl::WebViewImpl):
+        (WebKit::WebViewImpl::startPageScaleAnimation):
+        (WebKit):
+        (WebKit::WebViewImpl::enableFakeDoubleTapAnimationForTesting):
+        (WebKit::WebViewImpl::computeScaleAndScrollForHitRect):
+        (WebKit::WebViewImpl::animateZoomAroundPoint):
+        (WebKit::WebViewImpl::didCommitLoad):
+        (WebKit::WebViewImpl::applyScrollAndScale):
+        * src/WebViewImpl.h:
+        (WebViewImpl):
+        (WebKit::WebViewImpl::fakeDoubleTapAnimationPendingForTesting):
+        (WebKit::WebViewImpl::fakeDoubleTapTargetPositionForTesting):
+        (WebKit::WebViewImpl::fakeDoubleTapPageScaleFactorForTesting):
+        (WebKit::WebViewImpl::fakeDoubleTapUseAnchorForTesting):
+        * tests/WebFrameTest.cpp:
+
 2013-02-19  Joshua Bell  <jsb...@chromium.org>
 
         IndexedDB: additional checks on LevelDB decoding

Modified: trunk/Source/WebKit/chromium/src/WebViewImpl.cpp (143354 => 143355)


--- trunk/Source/WebKit/chromium/src/WebViewImpl.cpp	2013-02-19 18:40:03 UTC (rev 143354)
+++ trunk/Source/WebKit/chromium/src/WebViewImpl.cpp	2013-02-19 18:42:27 UTC (rev 143355)
@@ -399,8 +399,11 @@
     , m_ignoreViewportTagMaximumScale(false)
     , m_pageScaleFactorIsSet(false)
     , m_savedPageScaleFactor(0)
-    , m_doubleTapZoomInEffect(false)
-    , m_shouldUseDoubleTapTimeZero(false)
+    , m_doubleTapZoomPageScaleFactor(0)
+    , m_doubleTapZoomPending(false)
+    , m_enableFakeDoubleTapAnimationForTesting(false)
+    , m_fakeDoubleTapPageScaleFactor(0)
+    , m_fakeDoubleTapUseAnchor(false)
     , m_contextMenuAllowed(false)
     , m_doingDragAndDrop(false)
     , m_ignoreInputEvents(false)
@@ -856,20 +859,35 @@
     scheduleAnimation();
 }
 
-void WebViewImpl::startPageScaleAnimation(const IntPoint& targetPosition, bool useAnchor, float newScale, double durationInSeconds)
+bool WebViewImpl::startPageScaleAnimation(const IntPoint& targetPosition, bool useAnchor, float newScale, double durationInSeconds)
 {
     WebPoint clampedPoint = targetPosition;
-    if (!useAnchor)
+    if (!useAnchor) {
         clampedPoint = clampOffsetAtScale(targetPosition, newScale);
-    if ((!durationInSeconds && !useAnchor) || m_shouldUseDoubleTapTimeZero) {
-        setPageScaleFactor(newScale, clampedPoint);
-        return;
+        if (!durationInSeconds) {
+            setPageScaleFactor(newScale, clampedPoint);
+            return false;
+        }
     }
-    if (!m_layerTreeView)
-        return;
+    if (useAnchor && newScale == pageScaleFactor())
+        return false;
 
-    m_layerTreeView->startPageScaleAnimation(targetPosition, useAnchor, newScale, durationInSeconds);
+    if (m_enableFakeDoubleTapAnimationForTesting) {
+        m_fakeDoubleTapTargetPosition = targetPosition;
+        m_fakeDoubleTapUseAnchor = useAnchor;
+        m_fakeDoubleTapPageScaleFactor = newScale;
+    } else {
+        if (!m_layerTreeView)
+            return false;
+        m_layerTreeView->startPageScaleAnimation(targetPosition, useAnchor, newScale, durationInSeconds);
+    }
+    return true;
 }
+
+void WebViewImpl::enableFakeDoubleTapAnimationForTesting(bool enable)
+{
+    m_enableFakeDoubleTapAnimationForTesting = enable;
+}
 #endif
 
 WebViewBenchmarkSupport* WebViewImpl::benchmarkSupport()
@@ -1153,11 +1171,6 @@
     return WebRect(newX, source.y, newWidth, source.height);
 }
 
-void WebViewImpl::shouldUseAnimateDoubleTapTimeZeroForTesting(bool setToZero)
-{
-    m_shouldUseDoubleTapTimeZero = setToZero;
-}
-
 void WebViewImpl::computeScaleAndScrollForHitRect(const WebRect& hitRect, AutoZoomType zoomType, float& scale, WebPoint& scroll, bool& isAnchor)
 {
     scale = pageScaleFactor();
@@ -1215,17 +1228,15 @@
         scaleUnchanged = fabs(pageScaleFactor() - scale) < minScaleDifference;
     }
 
-    if (zoomType == DoubleTap && (rect.isEmpty() || scaleUnchanged || m_doubleTapZoomInEffect)) {
+    bool stillAtPreviousDoubleTapScale = (pageScaleFactor() == m_doubleTapZoomPageScaleFactor
+        && m_doubleTapZoomPageScaleFactor != m_minimumPageScaleFactor)
+        || m_doubleTapZoomPending;
+    if (zoomType == DoubleTap && (rect.isEmpty() || scaleUnchanged || stillAtPreviousDoubleTapScale)) {
         // Zoom out to minimum scale.
         scale = m_minimumPageScaleFactor;
         scroll = WebPoint(hitRect.x, hitRect.y);
         isAnchor = true;
-        m_doubleTapZoomInEffect = false;
     } else {
-        if (zoomType == DoubleTap && scale != m_minimumPageScaleFactor)
-            m_doubleTapZoomInEffect = true;
-        else
-            m_doubleTapZoomInEffect = false;
         // FIXME: If this is being called for auto zoom during find in page,
         // then if the user manually zooms in it'd be nice to preserve the
         // relative increase in zoom they caused (if they zoom out then it's ok
@@ -1343,8 +1354,13 @@
     computeScaleAndScrollForHitRect(WebRect(webPoint.x, webPoint.y, 0, 0), zoomType, scale, scroll, isAnchor);
 
     bool isDoubleTap = (zoomType == DoubleTap);
-    double durationInSeconds = (isDoubleTap && !m_shouldUseDoubleTapTimeZero) ? doubleTapZoomAnimationDurationInSeconds : 0;
-    startPageScaleAnimation(scroll, isAnchor, scale, durationInSeconds);
+    double durationInSeconds = isDoubleTap ? doubleTapZoomAnimationDurationInSeconds : 0;
+    bool isAnimating = startPageScaleAnimation(scroll, isAnchor, scale, durationInSeconds);
+
+    if (isDoubleTap && isAnimating) {
+        m_doubleTapZoomPageScaleFactor = scale;
+        m_doubleTapZoomPending = true;
+    }
 #endif
 }
 
@@ -3767,10 +3783,8 @@
     m_newNavigationLoader = 0;
 #endif
     m_observedNewNavigation = false;
-    if (*isNewNavigation && !isNavigationWithinPage) {
+    if (*isNewNavigation && !isNavigationWithinPage)
         m_pageScaleFactorIsSet = false;
-        m_doubleTapZoomInEffect = false;
-    }
 
     // Make sure link highlight from previous page is cleared.
     m_linkHighlight.clear();
@@ -4217,7 +4231,7 @@
         }
 
         setPageScaleFactor(pageScaleFactor() * pageScaleDelta, scrollPoint);
-        m_doubleTapZoomInEffect = false;
+        m_doubleTapZoomPending = false;
     }
 }
 

Modified: trunk/Source/WebKit/chromium/src/WebViewImpl.h (143354 => 143355)


--- trunk/Source/WebKit/chromium/src/WebViewImpl.h	2013-02-19 18:40:03 UTC (rev 143354)
+++ trunk/Source/WebKit/chromium/src/WebViewImpl.h	2013-02-19 18:42:27 UTC (rev 143355)
@@ -402,7 +402,7 @@
     void mouseDoubleClick(const WebMouseEvent&);
 
     bool detectContentOnTouch(const WebPoint&);
-    void startPageScaleAnimation(const WebCore::IntPoint& targetPosition, bool useAnchor, float newScale, double durationInSeconds);
+    bool startPageScaleAnimation(const WebCore::IntPoint& targetPosition, bool useAnchor, float newScale, double durationInSeconds);
 
     void numberOfWheelEventHandlersChanged(unsigned);
     void hasTouchEventHandlers(bool);
@@ -582,7 +582,11 @@
 #endif
     void animateZoomAroundPoint(const WebCore::IntPoint&, AutoZoomType);
 
-    void shouldUseAnimateDoubleTapTimeZeroForTesting(bool);
+    void enableFakeDoubleTapAnimationForTesting(bool);
+    bool fakeDoubleTapAnimationPendingForTesting() const { return m_doubleTapZoomPending; }
+    WebCore::IntPoint fakeDoubleTapTargetPositionForTesting() const { return m_fakeDoubleTapTargetPosition; }
+    float fakeDoubleTapPageScaleFactorForTesting() const { return m_fakeDoubleTapPageScaleFactor; }
+    bool fakeDoubleTapUseAnchorForTesting() const { return m_fakeDoubleTapUseAnchor; }
 
     void enterFullScreenForElement(WebCore::Element*);
     void exitFullScreenForElement(WebCore::Element*);
@@ -762,11 +766,16 @@
     float m_savedPageScaleFactor; // 0 means that no page scale factor is saved.
     WebCore::IntSize m_savedScrollOffset;
 
-    // Whether the current scale was achieved by zooming in with double tap.
-    bool m_doubleTapZoomInEffect;
+    // The scale moved to by the latest double tap zoom, if any.
+    float m_doubleTapZoomPageScaleFactor;
+    // Have we sent a double-tap zoom and not yet heard back the scale?
+    bool m_doubleTapZoomPending;
 
     // Used for testing purposes.
-    bool m_shouldUseDoubleTapTimeZero;
+    bool m_enableFakeDoubleTapAnimationForTesting;
+    WebCore::IntPoint m_fakeDoubleTapTargetPosition;
+    float m_fakeDoubleTapPageScaleFactor;
+    bool m_fakeDoubleTapUseAnchor;
 
     bool m_contextMenuAllowed;
 

Modified: trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp (143354 => 143355)


--- trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2013-02-19 18:40:03 UTC (rev 143354)
+++ trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2013-02-19 18:42:27 UTC (rev 143355)
@@ -595,7 +595,7 @@
     webView->layout();
 }
 
-TEST_F(WebFrameTest, DivAutoZoomParamsTestCompositorScaling)
+TEST_F(WebFrameTest, DivAutoZoomParamsTest)
 {
     registerMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html");
 
@@ -656,11 +656,14 @@
 void simulateDoubleTap(WebViewImpl* webViewImpl, WebPoint& point, float& scale)
 {
     webViewImpl->animateZoomAroundPoint(point, WebViewImpl::DoubleTap);
-    webViewImpl->mainFrameImpl()->frameView()->layout();
+    EXPECT_TRUE(webViewImpl->fakeDoubleTapAnimationPendingForTesting());
+    WebCore::IntSize scrollDelta = webViewImpl->fakeDoubleTapTargetPositionForTesting() - webViewImpl->mainFrameImpl()->frameView()->scrollPosition();
+    float scaleDelta = webViewImpl->fakeDoubleTapPageScaleFactorForTesting() / webViewImpl->pageScaleFactor();
+    webViewImpl->applyScrollAndScale(scrollDelta, scaleDelta);
     scale = webViewImpl->pageScaleFactor();
 }
 
-TEST_F(WebFrameTest, DivAutoZoomMultipleDivsTestCompositorScaling)
+TEST_F(WebFrameTest, DivAutoZoomMultipleDivsTest)
 {
     registerMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html");
 
@@ -679,7 +682,7 @@
     m_webView->layout();
 
     WebViewImpl* webViewImpl = static_cast<WebViewImpl*>(m_webView);
-    webViewImpl->shouldUseAnimateDoubleTapTimeZeroForTesting(true);
+    webViewImpl->enableFakeDoubleTapAnimationForTesting(true);
 
     WebRect topDiv(200, 100, 200, 150);
     WebRect bottomDiv(200, 300, 200, 150);
@@ -701,9 +704,18 @@
     webViewImpl->applyScrollAndScale(WebSize(), 0.6f);
     simulateDoubleTap(webViewImpl, bottomPoint, scale);
     EXPECT_FLOAT_EQ(1, scale);
+    simulateDoubleTap(webViewImpl, bottomPoint, scale);
+    EXPECT_FLOAT_EQ(webViewImpl->minimumPageScaleFactor(), scale);
+
+    // If we didn't yet get an auto-zoom update and a second double-tap arrives, should go back to minimum scale.
+    webViewImpl->applyScrollAndScale(WebSize(), 1.1f);
+    webViewImpl->animateZoomAroundPoint(topPoint, WebViewImpl::DoubleTap);
+    EXPECT_TRUE(webViewImpl->fakeDoubleTapAnimationPendingForTesting());
+    simulateDoubleTap(webViewImpl, bottomPoint, scale);
+    EXPECT_FLOAT_EQ(webViewImpl->minimumPageScaleFactor(), scale);
 }
 
-TEST_F(WebFrameTest, DivAutoZoomScaleBoundsTestCompositorScaling)
+TEST_F(WebFrameTest, DivAutoZoomScaleBoundsTest)
 {
     registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
 
@@ -719,7 +731,7 @@
     m_webView->layout();
 
     WebViewImpl* webViewImpl = static_cast<WebViewImpl*>(m_webView);
-    webViewImpl->shouldUseAnimateDoubleTapTimeZeroForTesting(true);
+    webViewImpl->enableFakeDoubleTapAnimationForTesting(true);
 
     WebRect div(200, 100, 200, 150);
     WebPoint doubleTapPoint(div.x + 50, div.y + 50);
@@ -768,7 +780,7 @@
 }
 
 #if ENABLE(TEXT_AUTOSIZING)
-TEST_F(WebFrameTest, DivAutoZoomScaleFontScaleFactorTestCompositorScaling)
+TEST_F(WebFrameTest, DivAutoZoomScaleFontScaleFactorTest)
 {
     registerMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
 
@@ -784,7 +796,7 @@
     m_webView->layout();
 
     WebViewImpl* webViewImpl = static_cast<WebViewImpl*>(m_webView);
-    webViewImpl->shouldUseAnimateDoubleTapTimeZeroForTesting(true);
+    webViewImpl->enableFakeDoubleTapAnimationForTesting(true);
     webViewImpl->page()->settings()->setTextAutosizingFontScaleFactor(textAutosizingFontScaleFactor);
 
     WebRect div(200, 100, 200, 150);
@@ -869,7 +881,7 @@
     m_webView->settings()->setAutoZoomFocusedNodeToLegibleScale(true);
 
     WebViewImpl* webViewImpl = static_cast<WebViewImpl*>(m_webView);
-    webViewImpl->shouldUseAnimateDoubleTapTimeZeroForTesting(true);
+    webViewImpl->enableFakeDoubleTapAnimationForTesting(true);
 
     WebRect editBoxWithText(200, 200, 250, 20);
     WebRect editBoxWithNoText(200, 250, 250, 20);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to