Title: [108027] trunk/Source/WebKit/chromium
Revision
108027
Author
[email protected]
Date
2012-02-16 21:33:57 -0800 (Thu, 16 Feb 2012)

Log Message

[Chromium] Add method to WebViewImpl to extract zoom/scroll params for gesture events on touch devices
https://bugs.webkit.org/show_bug.cgi?id=72909

Patch by Varun Jain <[email protected]> on 2012-02-16
Reviewed by James Robinson.

* src/WebViewImpl.cpp:
(std):
(WebKit):
(WebKit::WebViewImpl::computeBlockBounds):
(WebKit::WebViewImpl::widenRectWithinPageBounds):
(WebKit::WebViewImpl::computeScaleAndScrollForHitRect):
* src/WebViewImpl.h:
(WebViewImpl):
* tests/WebFrameTest.cpp:
(WebKit):
(WebKit::TEST_F):
* tests/data/get_scale_for_auto_zoom_into_div_test.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit/chromium/ChangeLog (108026 => 108027)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-02-17 05:26:47 UTC (rev 108026)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-02-17 05:33:57 UTC (rev 108027)
@@ -1,3 +1,23 @@
+2012-02-16  Varun Jain  <[email protected]>
+
+        [Chromium] Add method to WebViewImpl to extract zoom/scroll params for gesture events on touch devices
+        https://bugs.webkit.org/show_bug.cgi?id=72909
+
+        Reviewed by James Robinson.
+
+        * src/WebViewImpl.cpp:
+        (std):
+        (WebKit):
+        (WebKit::WebViewImpl::computeBlockBounds):
+        (WebKit::WebViewImpl::widenRectWithinPageBounds):
+        (WebKit::WebViewImpl::computeScaleAndScrollForHitRect):
+        * src/WebViewImpl.h:
+        (WebViewImpl):
+        * tests/WebFrameTest.cpp:
+        (WebKit):
+        (WebKit::TEST_F):
+        * tests/data/get_scale_for_auto_zoom_into_div_test.html: Added.
+
 2012-02-16  Sami Kyostila  <[email protected]>
 
         [chromium] LayerChromium::setNeedsDisplay does not apply contents scale correctly

Modified: trunk/Source/WebKit/chromium/src/WebViewImpl.cpp (108026 => 108027)


--- trunk/Source/WebKit/chromium/src/WebViewImpl.cpp	2012-02-17 05:26:47 UTC (rev 108026)
+++ trunk/Source/WebKit/chromium/src/WebViewImpl.cpp	2012-02-17 05:33:57 UTC (rev 108027)
@@ -188,6 +188,14 @@
     return attributes;
 }
 
+// The following constants control parameters for automated scaling of webpages
+// (such as due to a double tap gesture or find in page etc.). These are
+// experimentally determined.
+static const int touchPointPadding = 32;
+static const float minScaleDifference = 0.01;
+static const float doubleTapZoomContentDefaultMargin = 5;
+static const float doubleTapZoomContentMinimumMargin = 2;
+
 } // anonymous namespace
 
 namespace WebKit {
@@ -794,6 +802,149 @@
 }
 #endif
 
+#if ENABLE(GESTURE_EVENTS)
+WebRect WebViewImpl::computeBlockBounds(const WebRect& rect, AutoZoomType zoomType)
+{
+    if (!mainFrameImpl())
+        return WebRect();
+
+    // Use the rect-based hit test to find the node.
+    IntPoint point = mainFrameImpl()->frameView()->windowToContents(IntPoint(rect.x, rect.y));
+    HitTestResult result = mainFrameImpl()->frame()->eventHandler()->hitTestResultAtPoint(point,
+            false, zoomType == FindInPage, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
+            IntSize(rect.width, rect.height));
+
+    Node* node = result.innerNonSharedNode();
+    if (!node)
+        return WebRect();
+
+    // Find the block type node based on the hit node.
+    while (node && (!node->renderer() || node->renderer()->isInline()))
+        node = node->parentNode();
+
+    // Return the bounding box in the window coordinate system.
+    if (node) {
+        IntRect rect = node->Node::getRect();
+        Frame* frame = node->document()->frame();
+        return frame->view()->contentsToWindow(rect);
+    }
+    return WebRect();
+}
+
+WebRect WebViewImpl::widenRectWithinPageBounds(const WebRect& source, int targetMargin, int minimumMargin)
+{
+    WebSize maxSize;
+    if (mainFrame())
+        maxSize = mainFrame()->contentsSize();
+    IntSize scrollOffset;
+    if (mainFrame())
+        scrollOffset = mainFrame()->scrollOffset();
+    int leftMargin = targetMargin;
+    int rightMargin = targetMargin;
+
+    const int absoluteSourceX = source.x + scrollOffset.width();
+    if (leftMargin > absoluteSourceX) {
+        leftMargin = absoluteSourceX;
+        rightMargin = max(leftMargin, minimumMargin);
+    }
+
+    const int maximumRightMargin = maxSize.width - (source.width + absoluteSourceX);
+    if (rightMargin > maximumRightMargin) {
+        rightMargin = maximumRightMargin;
+        leftMargin = min(leftMargin, max(rightMargin, minimumMargin));
+    }
+
+    const int newWidth = source.width + leftMargin + rightMargin;
+    const int newX = source.x - leftMargin;
+
+    ASSERT(newWidth >= 0);
+    ASSERT(scrollOffset.width() + newX + newWidth <= maxSize.width);
+
+    return WebRect(newX, source.y, newWidth, source.height);
+}
+
+void WebViewImpl::computeScaleAndScrollForHitRect(const WebRect& hitRect, AutoZoomType zoomType, float& scale, WebPoint& scroll)
+{
+    scale = pageScaleFactor();
+    scroll.x = scroll.y = 0;
+    WebRect targetRect = hitRect;
+    if (targetRect.isEmpty())
+        targetRect.width = targetRect.height = touchPointPadding;
+
+    WebRect rect = computeBlockBounds(targetRect, zoomType);
+
+    const float overviewScale = m_minimumPageScaleFactor;
+    bool scaleUnchanged = true;
+    if (!rect.isEmpty()) {
+        // Pages should be as legible as on desktop when at dpi scale, so no
+        // need to zoom in further when automatically determining zoom level
+        // (after double tap, find in page, etc), though the user should still
+        // be allowed to manually pinch zoom in further if they desire.
+        const float maxScale = deviceScaleFactor();
+
+        const float defaultMargin = doubleTapZoomContentDefaultMargin * deviceScaleFactor();
+        const float minimumMargin = doubleTapZoomContentMinimumMargin * deviceScaleFactor();
+        // We want the margins to have the same physical size, which means we
+        // need to express them in post-scale size. To do that we'd need to know
+        // the scale we're scaling to, but that depends on the margins. Instead
+        // we express them as a fraction of the target rectangle: this will be
+        // correct if we end up fully zooming to it, and won't matter if we
+        // don't.
+        rect = widenRectWithinPageBounds(rect,
+                static_cast<int>(defaultMargin * rect.width / m_size.width),
+                static_cast<int>(minimumMargin * rect.width / m_size.width));
+
+        // Fit block to screen, respecting limits.
+        scale *= static_cast<float>(m_size.width) / rect.width;
+        scale = min(scale, maxScale);
+        scale = clampPageScaleFactorToLimits(scale);
+
+        scaleUnchanged = fabs(pageScaleFactor() - scale) < minScaleDifference;
+    }
+
+    if (zoomType == DoubleTap) {
+        if (rect.isEmpty() || scaleUnchanged) {
+            // Zoom out to overview mode.
+            if (overviewScale)
+                scale = overviewScale;
+            return;
+        }
+    } else if (rect.isEmpty()) {
+        // Keep current scale (no need to scroll as x,y will normally already
+        // be visible). FIXME: Revisit this if it isn't always true.
+        return;
+    }
+
+    // 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 to zoom
+    // them back in again). This isn't compatible with our current double-tap
+    // zoom strategy (fitting the containing block to the screen) though.
+
+    float screenHeight = m_size.height / scale * pageScaleFactor();
+    float screenWidth = m_size.width / scale * pageScaleFactor();
+
+    // Scroll to vertically align the block.
+    if (rect.height < screenHeight) {
+        // Vertically center short blocks.
+        rect.y -= 0.5 * (screenHeight - rect.height);
+    } else {
+        // Ensure position we're zooming to (+ padding) isn't off the bottom of
+        // the screen.
+        rect.y = max<float>(rect.y, hitRect.y + touchPointPadding - screenHeight);
+    } // Otherwise top align the block.
+
+    // Do the same thing for horizontal alignment.
+    if (rect.width < screenWidth)
+        rect.x -= 0.5 * (screenWidth - rect.width);
+    else
+        rect.x = max<float>(rect.x, hitRect.x + touchPointPadding - screenWidth);
+
+    scroll.x = rect.x;
+    scroll.y = rect.y;
+}
+#endif
+
 void WebViewImpl::numberOfWheelEventHandlersChanged(unsigned numberOfWheelHandlers)
 {
     m_haveWheelEventHandlers = numberOfWheelHandlers > 0;

Modified: trunk/Source/WebKit/chromium/src/WebViewImpl.h (108026 => 108027)


--- trunk/Source/WebKit/chromium/src/WebViewImpl.h	2012-02-17 05:26:47 UTC (rev 108026)
+++ trunk/Source/WebKit/chromium/src/WebViewImpl.h	2012-02-17 05:33:57 UTC (rev 108027)
@@ -96,6 +96,11 @@
 
 class WebViewImpl : public WebView, public WebCore::CCLayerTreeHostClient, public RefCounted<WebViewImpl> {
 public:
+    enum AutoZoomType {
+        DoubleTap,
+        FindInPage,
+    };
+
     // WebWidget methods:
     virtual void close();
     virtual WebSize size() { return m_size; }
@@ -459,6 +464,10 @@
     // a plugin can update its own zoom, say because of its own UI.
     void fullFramePluginZoomLevelChanged(double zoomLevel);
 
+#if ENABLE(GESTURE_EVENTS)
+    void computeScaleAndScrollForHitRect(const WebRect& hitRect, AutoZoomType, float& scale, WebPoint& scroll);
+#endif
+
     void loseCompositorContext(int numTimes);
 
     void enterFullScreenForElement(WebCore::Element*);
@@ -529,6 +538,15 @@
     void updateLayerTreeViewport();
 #endif
 
+#if ENABLE(GESTURE_EVENTS)
+    // Returns the bounding box of the block type node touched by the WebRect.
+    WebRect computeBlockBounds(const WebRect&, AutoZoomType);
+
+    // Helper function: Widens the width of |source| by the specified margins
+    // while keeping it smaller than page width.
+    WebRect widenRectWithinPageBounds(const WebRect& source, int targetMargin, int minimumMargin);
+#endif
+
 #if ENABLE(POINTER_LOCK)
     void pointerLockMouseEvent(const WebInputEvent&);
 #endif

Modified: trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp (108026 => 108027)


--- trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2012-02-17 05:26:47 UTC (rev 108026)
+++ trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2012-02-17 05:33:57 UTC (rev 108027)
@@ -151,6 +151,78 @@
     EXPECT_EQ(std::string::npos, content.find("Clobbered"));
 }
 
+#if ENABLE(GESTURE_EVENTS)
+TEST_F(WebFrameTest, DivAutoZoomParamsTest)
+{
+    registerMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html");
+
+    WebViewImpl* webViewImpl = static_cast<WebViewImpl*>(FrameTestHelpers::createWebViewAndLoad(m_baseURL + "get_scale_for_auto_zoom_into_div_test.html", true));
+    int pageWidth = 640;
+    int pageHeight = 480;
+    int divPosX = 200;
+    int divPosY = 200;
+    int divWidth = 200;
+    int divHeight = 150;
+    WebRect doubleTapPoint(250, 250, 0, 0);
+    webViewImpl->resize(WebSize(pageWidth, pageHeight));
+    float scale;
+    WebPoint scroll;
+
+    // Test for Doubletap scaling
+
+    // Tests for zooming in and out without clamping.
+    // Set device scale and scale limits so we dont get clamped.
+    webViewImpl->setDeviceScaleFactor(4);
+    webViewImpl->setPageScaleFactorLimits(0, 4 / webViewImpl->deviceScaleFactor());
+
+    // Test zooming into div.
+    webViewImpl->computeScaleAndScrollForHitRect(doubleTapPoint, WebViewImpl::DoubleTap, scale, scroll);
+    float scaledDivWidth = divWidth * scale;
+    float scaledDivHeight = divHeight * scale;
+    int hScroll = ((divPosX * scale) - ((pageWidth - scaledDivWidth) / 2)) / scale;
+    int vScroll = ((divPosY * scale) - ((pageHeight - scaledDivHeight) / 2)) / scale;
+    EXPECT_NEAR(pageWidth / divWidth, scale, 0.1);
+    EXPECT_EQ(hScroll, scroll.x);
+    EXPECT_EQ(vScroll, scroll.y);
+
+    // Test zoom out to overview scale.
+    webViewImpl->applyScrollAndScale(WebCore::IntSize(scroll.x, scroll.y), scale / webViewImpl->pageScaleFactor());
+    webViewImpl->computeScaleAndScrollForHitRect(doubleTapPoint, WebViewImpl::DoubleTap, scale, scroll);
+    EXPECT_FLOAT_EQ(1, scale);
+    EXPECT_EQ(WebPoint(0, 0), scroll);
+
+    // Tests for clamped scaling.
+    // Test clamp to device scale:
+    webViewImpl->applyScrollAndScale(WebCore::IntSize(scroll.x, scroll.y), scale / webViewImpl->pageScaleFactor());
+    webViewImpl->setDeviceScaleFactor(2.5);
+    webViewImpl->computeScaleAndScrollForHitRect(doubleTapPoint, WebViewImpl::DoubleTap, scale, scroll);
+    EXPECT_FLOAT_EQ(2.5, scale);
+
+    // Test clamp to minimum scale:
+    webViewImpl->applyScrollAndScale(WebCore::IntSize(scroll.x, scroll.y), scale / webViewImpl->pageScaleFactor());
+    webViewImpl->setPageScaleFactorLimits(1.5 / webViewImpl->deviceScaleFactor(), 4 / webViewImpl->deviceScaleFactor());
+    webViewImpl->computeScaleAndScrollForHitRect(doubleTapPoint, WebViewImpl::DoubleTap, scale, scroll);
+    EXPECT_FLOAT_EQ(1.5, scale);
+    EXPECT_EQ(WebPoint(0, 0), scroll);
+
+    // Test clamp to maximum scale:
+    webViewImpl->applyScrollAndScale(WebCore::IntSize(scroll.x, scroll.y), scale / webViewImpl->pageScaleFactor());
+    webViewImpl->setDeviceScaleFactor(4);
+    webViewImpl->setPageScaleFactorLimits(0, 3 / webViewImpl->deviceScaleFactor());
+    webViewImpl->computeScaleAndScrollForHitRect(doubleTapPoint, WebViewImpl::DoubleTap, scale, scroll);
+    EXPECT_FLOAT_EQ(3, scale);
+
+
+    // Test for Non-doubletap scaling
+    webViewImpl->setPageScaleFactor(1, WebPoint(0, 0));
+    webViewImpl->setDeviceScaleFactor(4);
+    webViewImpl->setPageScaleFactorLimits(0, 4 / webViewImpl->deviceScaleFactor());
+    // Test zooming into div.
+    webViewImpl->computeScaleAndScrollForHitRect(WebRect(250, 250, 10, 10), WebViewImpl::FindInPage, scale, scroll);
+    EXPECT_NEAR(pageWidth / divWidth, scale, 0.1);
+}
+#endif
+
 class TestReloadDoesntRedirectWebFrameClient : public WebFrameClient {
 public:
     virtual WebNavigationPolicy decidePolicyForNavigation(

Added: trunk/Source/WebKit/chromium/tests/data/get_scale_for_auto_zoom_into_div_test.html (0 => 108027)


--- trunk/Source/WebKit/chromium/tests/data/get_scale_for_auto_zoom_into_div_test.html	                        (rev 0)
+++ trunk/Source/WebKit/chromium/tests/data/get_scale_for_auto_zoom_into_div_test.html	2012-02-17 05:33:57 UTC (rev 108027)
@@ -0,0 +1,13 @@
+<html>
+  <body>
+    <div style="background-color: green; position: absolute; left: 0px; top: 0px; width: 640px; height: 480px">
+      <p>Top Div</p>
+      <div style="background-color: white; position: absolute; left: 200px; top: 200px; width: 200px; height: 150px">
+        <p id="innerDiv">Div to zoom to</p>
+        <div style="background-color: red; position: fixed; left: 220px; top: 350px; width: 160px; height: 40px">
+          <p id="innerInnerDiv">Div NOT to zoom to</p>
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to