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>