Chromium Code Reviews| Index: Source/WebKit/chromium/src/WebViewImpl.cpp |
| diff --git a/Source/WebKit/chromium/src/WebViewImpl.cpp b/Source/WebKit/chromium/src/WebViewImpl.cpp |
| index 0a7b1f89494d0fbbcfd72c97ca7d33e6ddc9ec00..369b199873e47b2265ef21c3e99d44236b509e77 100644 |
| --- a/Source/WebKit/chromium/src/WebViewImpl.cpp |
| +++ b/Source/WebKit/chromium/src/WebViewImpl.cpp |
| @@ -197,6 +197,10 @@ static const float doubleTapZoomContentMinimumMargin = 2; |
| static const double doubleTapZoomAnimationDurationInSeconds = 0.25; |
| static const float doubleTapZoomAlreadyLegibleRatio = 1.2f; |
| +static const float viewportAnchorRelativeEpsilon = 0.1f; |
| +static const float viewportAnchorXCoord = 0.5f; |
| +static const float viewportAnchorYCoord = 0; |
| + |
| // Constants for zooming in on a focused text field. |
| static const double scrollAndScaleAnimationDurationInSeconds = 0.2; |
| static const int minReadableCaretHeight = 18; |
| @@ -206,6 +210,105 @@ static const int caretPadding = 10; |
| namespace WebKit { |
| +#if ENABLE(VIEWPORT) |
| +namespace { |
| +// ViewportAnchor provides a way to anchor a viewport origin to a DOM node. |
| +// In particular, the user supplies the current viewport (in CSS coordinates) |
| +// and an anchor point (in view coordinates, e.g., (0, 0) == viewport origin, |
| +// (0.5, 0) == viewport top center). The anchor point tracks the underlying DOM |
| +// node; as the node moves or the view is resized, the viewport anchor maintains |
| +// its orientation relative to the node, and the viewport origin maintains its |
| +// orientation relative to the anchor. |
| +class ViewportAnchor { |
|
abarth-chromium
2013/04/12 17:33:30
This class should go in its own file. WebViewImpl
jdduke (slow)
2013/04/12 19:20:16
Done.
|
| +public: |
| + ViewportAnchor(EventHandler* eventHandler) |
|
abarth-chromium
2013/04/12 17:33:30
one-argument constructors should be marked explici
jdduke (slow)
2013/04/12 19:20:16
Done.
|
| + : m_eventHandler(eventHandler) { } |
| + |
| + void setAnchor(const IntRect& viewRect, const FloatSize& anchorInViewCoords) |
| + { |
| + m_viewRect = viewRect; |
| + m_anchorNode.clear(); |
| + m_anchorNodeBounds = LayoutRect(); |
| + m_anchorInNodeCoords = FloatSize(); |
| + m_anchorInViewCoords = anchorInViewCoords; |
| + |
| + if (viewRect.isEmpty()) |
| + return; |
| + |
| + // Preserve origins at the absolute screen origin |
| + if (viewRect.location() == IntPoint::zero()) |
| + return; |
| + |
| + FloatSize anchorOffset = viewRect.size(); |
| + anchorOffset.scale(anchorInViewCoords.width(), anchorInViewCoords.height()); |
| + const FloatPoint anchorPoint = FloatPoint(viewRect.location()) + anchorOffset; |
| + |
| + Node* node = findNonEmptyAnchorNode(flooredIntPoint(anchorPoint), viewRect, m_eventHandler); |
| + if (!node) |
| + return; |
| + |
| + m_anchorNode = node; |
| + m_anchorNodeBounds = node->boundingBox(); |
| + m_anchorInNodeCoords = anchorPoint - m_anchorNodeBounds.location(); |
| + m_anchorInNodeCoords.scale(1.f / m_anchorNodeBounds.width(), 1.f / m_anchorNodeBounds.height()); |
| + } |
| + |
| + // Note: The returned point may not lie within the document bounds. |
| + IntPoint computeOrigin(const IntSize& currentViewSize) const |
| + { |
| + if (!m_anchorNode || !m_anchorNode->inDocument()) |
| + return m_viewRect.location(); |
| + |
| + const LayoutRect currentNodeBounds = m_anchorNode->boundingBox(); |
| + if (m_anchorNodeBounds == currentNodeBounds) |
| + return m_viewRect.location(); |
| + |
| + // Compute the new anchor point relative to the node position |
| + FloatSize anchorOffsetFromNode = currentNodeBounds.size(); |
| + anchorOffsetFromNode.scale(m_anchorInNodeCoords.width(), m_anchorInNodeCoords.height()); |
| + FloatPoint anchorPoint = currentNodeBounds.location() + anchorOffsetFromNode; |
| + |
| + // Compute the new origin point relative to the new anchor point |
| + FloatSize anchorOffsetFromOrigin = currentViewSize; |
| + anchorOffsetFromOrigin.scale(m_anchorInViewCoords.width(), m_anchorInViewCoords.height()); |
| + return flooredIntPoint(anchorPoint - anchorOffsetFromOrigin); |
| + } |
| + |
| +private: |
| + ViewportAnchor(); |
| + |
| + static Node* findNonEmptyAnchorNode(const IntPoint& point, const IntRect& viewRect, EventHandler* eventHandler) |
| + { |
| + Node* node = eventHandler->hitTestResultAtPoint(point).innerNode(); |
| + |
| + // If the node bounding box contains the view, make a single attempt to |
| + // find a smaller node; the larger the node bounds, the greater the |
| + // variability under resize. |
| + if (node && node->boundingBox().contains(viewRect)) { |
| + IntSize pointOffset = viewRect.size(); |
| + pointOffset.scale(viewportAnchorRelativeEpsilon); |
| + node = eventHandler->hitTestResultAtPoint(point + pointOffset).innerNode(); |
| + } |
| + |
| + while (node && node->boundingBox().isEmpty()) |
| + node = node->parentNode(); |
| + |
| + return node; |
| + } |
| + |
| + EventHandler* m_eventHandler; |
| + |
| + IntRect m_viewRect; |
| + |
| + RefPtr<Node> m_anchorNode; |
| + LayoutRect m_anchorNodeBounds; |
| + |
| + FloatSize m_anchorInViewCoords; |
| + FloatSize m_anchorInNodeCoords; |
| +}; |
| +} |
| +#endif // ENABLE(VIEWPORT) |
| + |
| // Change the text zoom level by kTextSizeMultiplierRatio each time the user |
| // zooms text in or out (ie., change by 20%). The min and max values limit |
| // text zoom to half and 3x the original text size. These three values match |
| @@ -1654,12 +1757,18 @@ void WebViewImpl::resize(const WebSize& newSize) |
| WebSize oldSize = m_size; |
| float oldPageScaleFactor = pageScaleFactor(); |
| - IntSize oldScrollOffset = view->scrollOffset(); |
| + float oldMinimumPageScaleFactor = m_minimumPageScaleFactor; |
| int oldFixedLayoutWidth = fixedLayoutSize().width; |
| + int oldContentsWidth = contentsSize().width(); |
| m_size = newSize; |
| #if ENABLE(VIEWPORT) |
| + ViewportAnchor viewportAnchor(mainFrameImpl()->frame()->eventHandler()); |
| + if (settings()->viewportEnabled()) |
| + viewportAnchor.setAnchor(view->visibleContentRect(), |
| + FloatSize(viewportAnchorXCoord, viewportAnchorYCoord)); |
| + |
| ViewportArguments viewportArguments = mainFrameImpl()->frame()->document()->viewportArguments(); |
| m_page->chrome()->client()->dispatchViewportPropertiesDidChange(viewportArguments); |
| #endif |
| @@ -1679,21 +1788,29 @@ void WebViewImpl::resize(const WebSize& newSize) |
| if (view->needsLayout()) |
| view->layout(); |
| - // When the device rotates: |
| - // - If the page width is unchanged, then zoom by new width/old width |
| - // such as to keep the same content horizontally onscreen. |
| - // - If the page width stretches proportionally to the change in |
| - // screen width, then don't zoom at all (assuming the content has |
| - // scaled uniformly, then the same content will be horizontally |
| - // onscreen). |
| - // - If the page width partially stretches, then zoom partially to |
| - // make up the difference. |
| - // In all cases try to keep the same content at the top of the screen. |
| + // The minimum scale limit may change as a result of layout content |
| + // changes; in this case, scale relative to the change in content width. |
| + // Otherwise scale relative to the change in layout width. |
| + // Don't do any scaling if the old width was zero (i.e., first resize). |
| + float scalingWidthRatio = 1; |
| + if (oldSize.width && oldContentsWidth && m_minimumPageScaleFactor != oldMinimumPageScaleFactor) |
| + scalingWidthRatio = contentsSize().width() / (float) oldContentsWidth; |
| + else if (oldFixedLayoutWidth) |
| + scalingWidthRatio = fixedLayoutSize().width / (float) oldFixedLayoutWidth; |
| + |
| float viewportWidthRatio = !oldSize.width ? 1 : newSize.width / (float) oldSize.width; |
| - float fixedLayoutWidthRatio = !oldFixedLayoutWidth ? 1 : fixedLayoutSize().width / (float) oldFixedLayoutWidth; |
| - float scaleMultiplier = viewportWidthRatio / fixedLayoutWidthRatio; |
| - if (scaleMultiplier != 1) |
| - setPageScaleFactor(oldPageScaleFactor * scaleMultiplier, WebPoint(oldScrollOffset.width(), oldScrollOffset.height())); |
| + float scaleMultiplier = viewportWidthRatio / scalingWidthRatio; |
| + |
| + IntSize viewportSize = view->visibleContentRect().size(); |
| + if (scaleMultiplier != 1) { |
| + float newPageScaleFactor = oldPageScaleFactor * scaleMultiplier; |
| + viewportSize.scale(pageScaleFactor() / newPageScaleFactor); |
| + IntPoint scrollOffsetAtNewScale = viewportAnchor.computeOrigin(viewportSize); |
| + setPageScaleFactor(newPageScaleFactor, scrollOffsetAtNewScale); |
| + } else { |
| + IntPoint scrollOffsetAtNewScale = clampOffsetAtScale(viewportAnchor.computeOrigin(viewportSize), pageScaleFactor()); |
| + updateMainFrameScrollPosition(scrollOffsetAtNewScale, false); |
| + } |
| } |
| #endif |