summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/CoordinatedGraphics
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/CoordinatedGraphics')
-rw-r--r--Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.cpp340
-rw-r--r--Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.h114
-rw-r--r--Source/WebKit2/UIProcess/CoordinatedGraphics/WKCoordinatedSceneAPICast.h63
3 files changed, 517 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
diff --git a/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.h b/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.h
new file mode 100644
index 000000000..e02d7671d
--- /dev/null
+++ b/Source/WebKit2/UIProcess/CoordinatedGraphics/PageViewportController.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef PageViewportController_h
+#define PageViewportController_h
+
+#include <WebCore/FloatPoint.h>
+#include <WebCore/FloatRect.h>
+#include <WebCore/FloatSize.h>
+#include <WebCore/ViewportArguments.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+class IntPoint;
+class IntSize;
+}
+
+namespace WebKit {
+
+class WebPageProxy;
+class PageViewportController;
+class PageViewportControllerClient;
+
+class PageViewportController {
+ WTF_MAKE_NONCOPYABLE(PageViewportController);
+
+public:
+ PageViewportController(WebKit::WebPageProxy*, PageViewportControllerClient&);
+ virtual ~PageViewportController() { }
+
+ float innerBoundedViewportScale(float) const;
+ float outerBoundedViewportScale(float) const;
+
+ WebCore::FloatPoint pixelAlignedFloatPoint(const WebCore::FloatPoint&);
+
+ WebCore::FloatPoint boundContentsPosition(const WebCore::FloatPoint&);
+ WebCore::FloatPoint boundContentsPositionAtScale(const WebCore::FloatPoint&, float scale);
+
+ WebCore::FloatSize visibleContentsSize() const;
+
+ bool hadUserInteraction() const { return m_hadUserInteraction; }
+ bool allowsUserScaling() const { return m_allowsUserScaling; }
+
+ WebCore::FloatSize contentsLayoutSize() const { return m_rawAttributes.layoutSize; }
+ float deviceScaleFactor() const;
+ float minimumScale() const { return m_minimumScaleToFit; }
+ float maximumScale() const { return m_rawAttributes.maximumScale; }
+ float currentScale() const { return m_pageScaleFactor; }
+
+ void setHadUserInteraction(bool didUserInteract) { m_hadUserInteraction = didUserInteract; }
+ void setInitiallyFitToViewport(bool initiallyFit) { m_initiallyFitToViewport = initiallyFit; }
+
+ // Notifications from the viewport.
+ void didChangeViewportSize(const WebCore::FloatSize& newSize);
+ void didChangeContentsVisibility(const WebCore::FloatPoint&, float scale, const WebCore::FloatPoint& trajectoryVector = WebCore::FloatPoint::zero());
+
+ // Notifications from the WebProcess.
+ void didCommitLoad();
+ void didChangeContentsSize(const WebCore::IntSize& newSize);
+ void didChangeViewportAttributes(const WebCore::ViewportAttributes&);
+ void didRenderFrame(const WebCore::IntSize& contentsSize, const WebCore::IntRect& coveredRect);
+ void pageTransitionViewportReady();
+ void pageDidRequestScroll(const WebCore::IntPoint& cssPosition);
+
+private:
+ bool syncVisibleContents(const WebCore::FloatPoint &trajectoryVector = WebCore::FloatPoint::zero());
+ void applyScaleAfterRenderingContents(float scale);
+ void applyPositionAfterRenderingContents(const WebCore::FloatPoint& pos);
+ bool updateMinimumScaleToFit(bool userInitiatedUpdate);
+
+ WebPageProxy* const m_webPageProxy;
+ PageViewportControllerClient& m_client;
+
+ WebCore::ViewportAttributes m_rawAttributes;
+
+ bool m_allowsUserScaling;
+ float m_minimumScaleToFit;
+ bool m_initiallyFitToViewport;
+
+ bool m_hadUserInteraction;
+
+ WebCore::FloatPoint m_contentsPosition;
+ WebCore::FloatSize m_contentsSize;
+ WebCore::FloatSize m_viewportSize;
+ WebCore::IntSize m_clientContentsSize;
+ float m_pageScaleFactor;
+
+ bool m_pendingPositionChange;
+ bool m_pendingScaleChange;
+ bool m_layerTreeStateIsFrozen;
+ WebCore::FloatRect m_lastFrameCoveredRect;
+};
+
+} // namespace WebKit
+
+#endif // PageViewportController_h
diff --git a/Source/WebKit2/UIProcess/CoordinatedGraphics/WKCoordinatedSceneAPICast.h b/Source/WebKit2/UIProcess/CoordinatedGraphics/WKCoordinatedSceneAPICast.h
new file mode 100644
index 000000000..d33dcdaea
--- /dev/null
+++ b/Source/WebKit2/UIProcess/CoordinatedGraphics/WKCoordinatedSceneAPICast.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WKCoordinatedSceneAPICast_h
+#define WKCoordinatedSceneAPICast_h
+
+#include <WebKit/WKCoordinatedScene.h>
+
+namespace WebCore {
+class TextureMapperLayer;
+}
+
+namespace WebKit {
+
+class CoordinatedGraphicsScene;
+
+inline WebKit::CoordinatedGraphicsScene* toImpl(WKCoordinatedScene scene)
+{
+ return reinterpret_cast<WebKit::CoordinatedGraphicsScene*>(scene);
+}
+
+inline WKCoordinatedScene toAPI(WebKit::CoordinatedGraphicsScene* scene)
+{
+ return reinterpret_cast<WKCoordinatedScene>(scene);
+}
+
+inline WebCore::TextureMapperLayer* toImpl(WKCoordinatedSceneLayer layer)
+{
+ return reinterpret_cast<WebCore::TextureMapperLayer*>(layer);
+}
+
+inline WKCoordinatedSceneLayer toAPI(WebCore::TextureMapperLayer* layer)
+{
+ return reinterpret_cast<WKCoordinatedSceneLayer>(layer);
+}
+
+} // namespace WebKit
+
+#endif // WKCoordinatedSceneAPICast_h