diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp b/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp new file mode 100644 index 000000000..9ed78b3f0 --- /dev/null +++ b/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2011, 2012 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2011 Benjamin Poulain <benjamin@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "PageViewportController.h" + +#include "AcceleratedDrawingAreaProxy.h" +#include "PageViewportControllerClient.h" +#include "WebPageProxy.h" +#include <WebCore/FloatRect.h> +#include <WebCore/FloatSize.h> +#include <wtf/MathExtras.h> + +using namespace WebCore; + +namespace WebKit { + +PageViewportController::PageViewportController(WebKit::WebPageProxy* proxy, PageViewportControllerClient& client) + : m_webPageProxy(proxy) + , m_client(client) + , m_allowsUserScaling(false) + , m_minimumScaleToFit(1) + , m_initiallyFitToViewport(true) + , m_hadUserInteraction(false) + , m_pageScaleFactor(1) + , m_pendingPositionChange(false) + , m_pendingScaleChange(false) + , m_layerTreeStateIsFrozen(false) +{ + // Initializing Viewport Raw Attributes to avoid random negative or infinity scale factors + // if there is a race condition between the first layout and setting the viewport attributes for the first time. + m_rawAttributes.minimumScale = 1; + m_rawAttributes.maximumScale = 1; + m_rawAttributes.userScalable = m_allowsUserScaling; + + // The initial scale might be implicit and set to -1, in this case we have to infer it + // using the viewport size and the final layout size. + // To be able to assert for valid scale we initialize it to -1. + m_rawAttributes.initialScale = -1; +} + +float PageViewportController::innerBoundedViewportScale(float viewportScale) const +{ + return clampTo(viewportScale, m_minimumScaleToFit, m_rawAttributes.maximumScale); +} + +float PageViewportController::outerBoundedViewportScale(float viewportScale) const +{ + if (m_allowsUserScaling) { + // Bounded by [0.1, 10.0] like the viewport meta code in WebCore. + float hardMin = std::max<float>(0.1, 0.5 * m_minimumScaleToFit); + float hardMax = std::min<float>(10, 2 * m_rawAttributes.maximumScale); + return clampTo(viewportScale, hardMin, hardMax); + } + return innerBoundedViewportScale(viewportScale); +} + +float PageViewportController::deviceScaleFactor() const +{ + return m_webPageProxy->deviceScaleFactor(); +} + +static inline bool isIntegral(float value) +{ + return static_cast<int>(value) == value; +} + +FloatPoint PageViewportController::pixelAlignedFloatPoint(const FloatPoint& framePosition) +{ + return framePosition; +} + +FloatPoint PageViewportController::boundContentsPositionAtScale(const WebCore::FloatPoint& framePosition, float scale) +{ + // We need to floor the viewport here as to allow aligning the content in device units. If not, + // it might not be possible to scroll the last pixel and that affects fixed position elements. + FloatRect bounds; + bounds.setWidth(std::max(0.f, m_contentsSize.width() - floorf(m_viewportSize.width() / scale))); + bounds.setHeight(std::max(0.f, m_contentsSize.height() - floorf(m_viewportSize.height() / scale))); + + FloatPoint position; + position.setX(clampTo(framePosition.x(), bounds.x(), bounds.width())); + position.setY(clampTo(framePosition.y(), bounds.y(), bounds.height())); + + return position; +} + +FloatPoint PageViewportController::boundContentsPosition(const WebCore::FloatPoint& framePosition) +{ + return boundContentsPositionAtScale(framePosition, m_pageScaleFactor); +} + +void PageViewportController::didCommitLoad() +{ + // Do not count the previous committed page contents as covered. + m_lastFrameCoveredRect = FloatRect(); + + // Do not continue to use the content size of the previous page. + m_contentsSize = IntSize(); + + m_contentsPosition = FloatPoint(); + + m_layerTreeStateIsFrozen = true; + + m_initiallyFitToViewport = true; + + // Reset the position to the top, page/history scroll requests may override this before we re-enable rendering. + applyPositionAfterRenderingContents(FloatPoint()); +} + +void PageViewportController::didChangeContentsSize(const IntSize& newSize) +{ + m_contentsSize = newSize; + + bool minimumScaleUpdated = updateMinimumScaleToFit(false); + + if (m_initiallyFitToViewport) { + // Restrict scale factors to m_minimumScaleToFit. + ASSERT(m_minimumScaleToFit > 0); + m_rawAttributes.initialScale = m_minimumScaleToFit; + WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes); + } + + if (minimumScaleUpdated) + m_client.didChangeViewportAttributes(); + + // We might have pending position change which is now possible. + syncVisibleContents(); +} + +void PageViewportController::didRenderFrame(const IntSize& contentsSize, const IntRect& coveredRect) +{ + if (m_clientContentsSize != contentsSize) { + m_clientContentsSize = contentsSize; + // Only update the viewport's contents dimensions along with its render if the + // size actually changed since animations on the page trigger DidRenderFrame + // messages without causing dimension changes. + m_client.didChangeContentsSize(contentsSize); + } + + m_lastFrameCoveredRect = coveredRect; + + // Apply any scale or scroll position we locked to be set on the viewport + // only when there is something to display there. The scale goes first to + // avoid offsetting our deferred position by scaling at the viewport center. + // All position and scale changes resulting from a web process event should + // go through here to be applied on the viewport to avoid showing incomplete + // tiles to the user during a few milliseconds. + + if (m_pendingScaleChange) { + m_pendingScaleChange = false; + m_client.setPageScaleFactor(m_pageScaleFactor); + + // The scale changed, we have to re-pixel align. + m_pendingPositionChange = true; + FloatPoint currentDiscretePos = roundedIntPoint(m_contentsPosition); + FloatPoint pixelAlignedPos = pixelAlignedFloatPoint(currentDiscretePos); + m_contentsPosition = boundContentsPosition(pixelAlignedPos); + + m_webPageProxy->scalePage(m_pageScaleFactor, roundedIntPoint(m_contentsPosition)); + } + + // There might be rendered frames not covering our requested position yet, wait for it. + FloatRect endVisibleContentRect(m_contentsPosition, visibleContentsSize()); + if (m_pendingPositionChange && endVisibleContentRect.intersects(coveredRect)) { + m_client.setViewportPosition(m_contentsPosition); + m_pendingPositionChange = false; + } + + m_layerTreeStateIsFrozen = false; +} + +void PageViewportController::pageTransitionViewportReady() +{ + if (!m_rawAttributes.layoutSize.isEmpty() && m_initiallyFitToViewport) { + m_hadUserInteraction = false; + float initialScale = m_initiallyFitToViewport ? m_minimumScaleToFit : m_rawAttributes.initialScale; + applyScaleAfterRenderingContents(innerBoundedViewportScale(initialScale)); + } +} + +void PageViewportController::pageDidRequestScroll(const IntPoint& cssPosition) +{ + // Ignore the request if suspended. Can only happen due to delay in event delivery. + if (m_webPageProxy->areActiveDOMObjectsAndAnimationsSuspended()) + return; + + FloatPoint boundPosition = boundContentsPosition(FloatPoint(cssPosition)); + FloatPoint alignedPosition = pixelAlignedFloatPoint(boundPosition); + FloatRect endVisibleContentRect(alignedPosition, visibleContentsSize()); + + if (m_lastFrameCoveredRect.intersects(endVisibleContentRect)) + m_client.setViewportPosition(alignedPosition); + else { + // Keep the unbound position in case the contents size is changed later on. + FloatPoint position = pixelAlignedFloatPoint(FloatPoint(cssPosition)); + applyPositionAfterRenderingContents(position); + } +} + +void PageViewportController::didChangeViewportSize(const FloatSize& newSize) +{ + if (newSize.isEmpty()) + return; + + m_viewportSize = newSize; +} + +void PageViewportController::didChangeContentsVisibility(const FloatPoint& position, float scale, const FloatPoint& trajectoryVector) +{ + if (!m_pendingPositionChange) + m_contentsPosition = position; + if (!m_pendingScaleChange) + applyScaleAfterRenderingContents(scale); + + syncVisibleContents(trajectoryVector); +} + +bool PageViewportController::syncVisibleContents(const FloatPoint& trajectoryVector) +{ + auto* drawingArea = static_cast<AcceleratedDrawingAreaProxy*>(m_webPageProxy->drawingArea()); + if (!drawingArea || m_viewportSize.isEmpty() || m_contentsSize.isEmpty()) + return false; + + FloatRect visibleContentsRect(boundContentsPosition(m_contentsPosition), visibleContentsSize()); + visibleContentsRect.intersect(FloatRect(FloatPoint::zero(), m_contentsSize)); + drawingArea->coordinatedLayerTreeHostProxy().setVisibleContentsRect(visibleContentsRect, trajectoryVector); + + if (!m_layerTreeStateIsFrozen) + m_client.didChangeVisibleContents(); + + return true; +} + +void PageViewportController::didChangeViewportAttributes(const WebCore::ViewportAttributes& newAttributes) +{ + if (!m_initiallyFitToViewport) + return; + + if (newAttributes.layoutSize.isEmpty()) + return; + + m_rawAttributes = newAttributes; + m_allowsUserScaling = !!m_rawAttributes.userScalable; + m_initiallyFitToViewport = (m_rawAttributes.initialScale < 0); + + if (!m_initiallyFitToViewport) + WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes); + + updateMinimumScaleToFit(true); + + // As the viewport attributes are calculated when loading pages, after load, or after + // viewport resize, it is important that we inform the client of the new scale and + // position, so that the content can be positioned correctly and pixel aligned. + m_pendingPositionChange = true; + m_pendingScaleChange = true; + + m_client.didChangeViewportAttributes(); +} + +FloatSize PageViewportController::visibleContentsSize() const +{ + return FloatSize(m_viewportSize.width() / m_pageScaleFactor, m_viewportSize.height() / m_pageScaleFactor); +} + +void PageViewportController::applyScaleAfterRenderingContents(float scale) +{ + if (m_pageScaleFactor == scale) + return; + + float oldPageScaleFactor = m_pageScaleFactor; + m_pageScaleFactor = scale; + m_pendingScaleChange = true; + if (!syncVisibleContents()) { + m_pageScaleFactor = oldPageScaleFactor; + m_webPageProxy->scalePage(m_pageScaleFactor, roundedIntPoint(m_contentsPosition)); + } +} + +void PageViewportController::applyPositionAfterRenderingContents(const FloatPoint& pos) +{ + if (m_contentsPosition == pos) + return; + + m_contentsPosition = pos; + m_pendingPositionChange = true; + syncVisibleContents(); +} + +bool PageViewportController::updateMinimumScaleToFit(bool userInitiatedUpdate) +{ + if (m_viewportSize.isEmpty() || m_contentsSize.isEmpty() || !m_initiallyFitToViewport || m_hadUserInteraction) + return false; + + // FIXME: Why this arbitrary precision? We likely want to omit the third argument so that + // std::numeric_limits<float>::epsilon() is used instead, similarly to Mac / iOS. + bool currentlyScaledToFit = WTF::areEssentiallyEqual(m_pageScaleFactor, m_minimumScaleToFit, 0.0001f); + float minimumScale = WebCore::computeMinimumScaleFactorForContentContained(m_rawAttributes, WebCore::roundedIntSize(m_viewportSize), WebCore::roundedIntSize(m_contentsSize)); + + if (minimumScale <= 0) + return false; + + if (!WTF::areEssentiallyEqual(minimumScale, m_minimumScaleToFit, 0.0001f)) { + m_minimumScaleToFit = minimumScale; + + if (!m_webPageProxy->areActiveDOMObjectsAndAnimationsSuspended()) { + if (!m_hadUserInteraction || (userInitiatedUpdate && currentlyScaledToFit)) + applyScaleAfterRenderingContents(m_minimumScaleToFit); + else { + // Ensure the effective scale stays within bounds. + float boundedScale = innerBoundedViewportScale(m_pageScaleFactor); + if (!WTF::areEssentiallyEqual(boundedScale, m_pageScaleFactor, 0.0001f)) + applyScaleAfterRenderingContents(boundedScale); + } + } + + return true; + } + return false; +} + +} // namespace WebKit |