diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/frame/PinchViewport.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/frame/PinchViewport.cpp | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/core/frame/PinchViewport.cpp b/chromium/third_party/WebKit/Source/core/frame/PinchViewport.cpp new file mode 100644 index 00000000000..09947a9b41d --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/frame/PinchViewport.cpp @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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. + */ + +#include "config.h" +#include "core/frame/PinchViewport.h" + +#include "core/frame/FrameHost.h" +#include "core/frame/FrameView.h" +#include "core/frame/LocalFrame.h" +#include "core/frame/Settings.h" +#include "core/page/Chrome.h" +#include "core/page/ChromeClient.h" +#include "core/page/Page.h" +#include "core/page/scrolling/ScrollingCoordinator.h" +#include "core/rendering/RenderView.h" +#include "core/rendering/compositing/RenderLayerCompositor.h" +#include "platform/TraceEvent.h" +#include "platform/geometry/FloatSize.h" +#include "platform/graphics/GraphicsLayer.h" +#include "platform/graphics/GraphicsLayerFactory.h" +#include "platform/scroll/Scrollbar.h" +#include "public/platform/Platform.h" +#include "public/platform/WebCompositorSupport.h" +#include "public/platform/WebLayer.h" +#include "public/platform/WebLayerTreeView.h" +#include "public/platform/WebScrollbar.h" +#include "public/platform/WebScrollbarLayer.h" + +using blink::WebLayer; +using blink::WebLayerTreeView; +using blink::WebScrollbar; +using blink::WebScrollbarLayer; +using WebCore::FrameHost; +using WebCore::GraphicsLayer; +using WebCore::GraphicsLayerFactory; + +namespace WebCore { + +PinchViewport::PinchViewport(FrameHost& owner) + : m_frameHost(owner) + , m_scale(1) +{ + reset(); +} + +PinchViewport::~PinchViewport() { } + +void PinchViewport::setSize(const IntSize& size) +{ + if (m_size == size) + return; + + TRACE_EVENT2("webkit", "PinchViewport::setSize", "width", size.width(), "height", size.height()); + m_size = size; + + // Make sure we clamp the offset to within the new bounds. + setLocation(m_offset); + + if (m_innerViewportContainerLayer) { + m_innerViewportContainerLayer->setSize(m_size); + + // Need to re-compute sizes for the overlay scrollbars. + setupScrollbar(WebScrollbar::Horizontal); + setupScrollbar(WebScrollbar::Vertical); + } +} + +void PinchViewport::reset() +{ + setLocation(FloatPoint()); + setScale(1); +} + +void PinchViewport::mainFrameDidChangeSize() +{ + TRACE_EVENT0("webkit", "PinchViewport::mainFrameDidChangeSize"); + + // In unit tests we may not have initialized the layer tree. + if (m_innerViewportScrollLayer) + m_innerViewportScrollLayer->setSize(contentsSize()); + + // Make sure the viewport's offset is clamped within the newly sized main frame. + setLocation(m_offset); +} + +FloatRect PinchViewport::visibleRect() const +{ + FloatSize scaledSize(m_size); + scaledSize.scale(1 / m_scale); + return FloatRect(m_offset, scaledSize); +} + +FloatRect PinchViewport::visibleRectInDocument() const +{ + if (!mainFrame() || !mainFrame()->view()) + return FloatRect(); + + FloatRect viewRect = mainFrame()->view()->visibleContentRect(); + FloatRect pinchRect = visibleRect(); + pinchRect.moveBy(viewRect.location()); + return pinchRect; +} + +void PinchViewport::scrollIntoView(const FloatRect& rect) +{ + if (!mainFrame() || !mainFrame()->view()) + return; + + FrameView* view = mainFrame()->view(); + + float centeringOffsetX = (visibleRect().width() - rect.width()) / 2; + float centeringOffsetY = (visibleRect().height() - rect.height()) / 2; + + FloatPoint targetOffset( + rect.x() - centeringOffsetX - visibleRect().x(), + rect.y() - centeringOffsetY - visibleRect().y()); + + view->setScrollPosition(flooredIntPoint(targetOffset)); + + FloatPoint remainder = FloatPoint(targetOffset - view->scrollPosition()); + move(remainder); +} + +void PinchViewport::setLocation(const FloatPoint& newLocation) +{ + FloatPoint clampedOffset(clampOffsetToBoundaries(newLocation)); + + if (clampedOffset == m_offset) + return; + + m_offset = clampedOffset; + + ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); + ASSERT(coordinator); + coordinator->scrollableAreaScrollLayerDidChange(this); + + mainFrame()->loader().saveScrollState(); +} + +void PinchViewport::move(const FloatPoint& delta) +{ + setLocation(m_offset + delta); +} + +void PinchViewport::setScale(float scale) +{ + if (scale == m_scale) + return; + + m_scale = scale; + + if (mainFrame()) + mainFrame()->loader().saveScrollState(); + + // Old-style pinch sets scale here but we shouldn't call into the + // clamping code below. + if (!m_innerViewportScrollLayer) + return; + + // Ensure we clamp so we remain within the bounds. + setLocation(visibleRect().location()); + + // TODO: We should probably be calling scaleDidChange type functions here. + // see Page::setPageScaleFactor. +} + +// Modifies the top of the graphics layer tree to add layers needed to support +// the inner/outer viewport fixed-position model for pinch zoom. When finished, +// the tree will look like this (with * denoting added layers): +// +// *rootTransformLayer +// +- *innerViewportContainerLayer (fixed pos container) +// +- *pageScaleLayer +// | +- *innerViewportScrollLayer +// | +-- overflowControlsHostLayer (root layer) +// | +-- outerViewportContainerLayer (fixed pos container) [frame container layer in RenderLayerCompositor] +// | | +-- outerViewportScrollLayer [frame scroll layer in RenderLayerCompositor] +// | | +-- content layers ... +// | +-- horizontal ScrollbarLayer (non-overlay) +// | +-- verticalScrollbarLayer (non-overlay) +// | +-- scroll corner (non-overlay) +// +- *horizontalScrollbarLayer (overlay) +// +- *verticalScrollbarLayer (overlay) +// +void PinchViewport::attachToLayerTree(GraphicsLayer* currentLayerTreeRoot, GraphicsLayerFactory* graphicsLayerFactory) +{ + TRACE_EVENT1("webkit", "PinchViewport::attachToLayerTree", "currentLayerTreeRoot", (bool)currentLayerTreeRoot); + if (!currentLayerTreeRoot) { + m_innerViewportScrollLayer->removeAllChildren(); + return; + } + + if (currentLayerTreeRoot->parent() && currentLayerTreeRoot->parent() == m_innerViewportScrollLayer) + return; + + if (!m_innerViewportScrollLayer) { + ASSERT(!m_overlayScrollbarHorizontal + && !m_overlayScrollbarVertical + && !m_pageScaleLayer + && !m_innerViewportContainerLayer); + + // FIXME: The root transform layer should only be created on demand. + m_rootTransformLayer = GraphicsLayer::create(graphicsLayerFactory, this); + m_innerViewportContainerLayer = GraphicsLayer::create(graphicsLayerFactory, this); + m_pageScaleLayer = GraphicsLayer::create(graphicsLayerFactory, this); + m_innerViewportScrollLayer = GraphicsLayer::create(graphicsLayerFactory, this); + m_overlayScrollbarHorizontal = GraphicsLayer::create(graphicsLayerFactory, this); + m_overlayScrollbarVertical = GraphicsLayer::create(graphicsLayerFactory, this); + + WebCore::ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); + ASSERT(coordinator); + coordinator->setLayerIsContainerForFixedPositionLayers(m_innerViewportScrollLayer.get(), true); + + // Set masks to bounds so the compositor doesn't clobber a manually + // set inner viewport container layer size. + m_innerViewportContainerLayer->setMasksToBounds(m_frameHost.settings().mainFrameClipsContent()); + m_innerViewportContainerLayer->setSize(m_size); + + m_innerViewportScrollLayer->platformLayer()->setScrollClipLayer( + m_innerViewportContainerLayer->platformLayer()); + m_innerViewportScrollLayer->platformLayer()->setUserScrollable(true, true); + + m_rootTransformLayer->addChild(m_innerViewportContainerLayer.get()); + m_innerViewportContainerLayer->addChild(m_pageScaleLayer.get()); + m_pageScaleLayer->addChild(m_innerViewportScrollLayer.get()); + m_innerViewportContainerLayer->addChild(m_overlayScrollbarHorizontal.get()); + m_innerViewportContainerLayer->addChild(m_overlayScrollbarVertical.get()); + + // Ensure this class is set as the scroll layer's ScrollableArea. + coordinator->scrollableAreaScrollLayerDidChange(this); + + // Setup the inner viewport overlay scrollbars. + setupScrollbar(WebScrollbar::Horizontal); + setupScrollbar(WebScrollbar::Vertical); + } + + m_innerViewportScrollLayer->removeAllChildren(); + m_innerViewportScrollLayer->addChild(currentLayerTreeRoot); + + // We only need to disable the existing (outer viewport) scrollbars + // if the existing ones are already overlay. + // FIXME: If we knew in advance before the overflowControlsHostLayer goes + // away, we would re-enable the drawing of these scrollbars. + // FIXME: This doesn't seem to work (at least on Android). Commenting out for now until + // I figure out how to access RenderLayerCompositor from here. + // if (GraphicsLayer* scrollbar = m_frameHost->compositor()->layerForHorizontalScrollbar()) + // scrollbar->setDrawsContent(!page->mainFrame()->view()->hasOverlayScrollbars()); + // if (GraphicsLayer* scrollbar = m_frameHost->compositor()->layerForVerticalScrollbar()) + // scrollbar->setDrawsContent(!page->mainFrame()->view()->hasOverlayScrollbars()); +} + +void PinchViewport::setupScrollbar(WebScrollbar::Orientation orientation) +{ + bool isHorizontal = orientation == WebScrollbar::Horizontal; + GraphicsLayer* scrollbarGraphicsLayer = isHorizontal ? + m_overlayScrollbarHorizontal.get() : m_overlayScrollbarVertical.get(); + OwnPtr<WebScrollbarLayer>& webScrollbarLayer = isHorizontal ? + m_webOverlayScrollbarHorizontal : m_webOverlayScrollbarVertical; + + const int overlayScrollbarThickness = m_frameHost.settings().pinchOverlayScrollbarThickness(); + + if (!webScrollbarLayer) { + ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); + ASSERT(coordinator); + ScrollbarOrientation webcoreOrientation = isHorizontal ? HorizontalScrollbar : VerticalScrollbar; + webScrollbarLayer = coordinator->createSolidColorScrollbarLayer(webcoreOrientation, overlayScrollbarThickness, 0, false); + + webScrollbarLayer->setClipLayer(m_innerViewportContainerLayer->platformLayer()); + scrollbarGraphicsLayer->setContentsToPlatformLayer(webScrollbarLayer->layer()); + scrollbarGraphicsLayer->setDrawsContent(false); + } + + int xPosition = isHorizontal ? 0 : m_innerViewportContainerLayer->size().width() - overlayScrollbarThickness; + int yPosition = isHorizontal ? m_innerViewportContainerLayer->size().height() - overlayScrollbarThickness : 0; + int width = isHorizontal ? m_innerViewportContainerLayer->size().width() - overlayScrollbarThickness : overlayScrollbarThickness; + int height = isHorizontal ? overlayScrollbarThickness : m_innerViewportContainerLayer->size().height() - overlayScrollbarThickness; + + // Use the GraphicsLayer to position the scrollbars. + scrollbarGraphicsLayer->setPosition(IntPoint(xPosition, yPosition)); + scrollbarGraphicsLayer->setSize(IntSize(width, height)); + scrollbarGraphicsLayer->setContentsRect(IntRect(0, 0, width, height)); +} + +void PinchViewport::registerLayersWithTreeView(WebLayerTreeView* layerTreeView) const +{ + TRACE_EVENT0("webkit", "PinchViewport::registerLayersWithTreeView"); + ASSERT(layerTreeView); + ASSERT(m_frameHost.page().mainFrame()); + ASSERT(m_frameHost.page().mainFrame()->isLocalFrame()); + ASSERT(m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()); + + RenderLayerCompositor* compositor = m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()->compositor(); + // Get the outer viewport scroll layer. + WebLayer* scrollLayer = compositor->scrollLayer()->platformLayer(); + + m_webOverlayScrollbarHorizontal->setScrollLayer(scrollLayer); + m_webOverlayScrollbarVertical->setScrollLayer(scrollLayer); + + ASSERT(compositor); + layerTreeView->registerViewportLayers( + m_pageScaleLayer->platformLayer(), + m_innerViewportScrollLayer->platformLayer(), + scrollLayer); +} + +void PinchViewport::clearLayersForTreeView(WebLayerTreeView* layerTreeView) const +{ + ASSERT(layerTreeView); + + layerTreeView->clearViewportLayers(); +} + +int PinchViewport::scrollSize(ScrollbarOrientation orientation) const +{ + IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition(); + return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height(); +} + +IntPoint PinchViewport::minimumScrollPosition() const +{ + return IntPoint(); +} + +IntPoint PinchViewport::maximumScrollPosition() const +{ + return flooredIntPoint(FloatSize(contentsSize()) - visibleRect().size()); +} + +IntRect PinchViewport::scrollableAreaBoundingBox() const +{ + // This method should return the bounding box in the parent view's coordinate + // space; however, PinchViewport technically isn't a child of any Frames. + // Nonetheless, the PinchViewport always occupies the entire main frame so just + // return that. + LocalFrame* frame = mainFrame(); + + if (!frame || !frame->view()) + return IntRect(); + + return frame->view()->frameRect(); +} + +IntSize PinchViewport::contentsSize() const +{ + LocalFrame* frame = mainFrame(); + + if (!frame || !frame->view()) + return IntSize(); + + ASSERT(frame->view()->visibleContentScaleFactor() == 1); + return frame->view()->visibleContentRect(IncludeScrollbars).size(); +} + +void PinchViewport::invalidateScrollbarRect(Scrollbar*, const IntRect&) +{ + // Do nothing. Pinch scrollbars live on the compositor thread and will + // be updated when the viewport is synced to the CC. +} + +void PinchViewport::setScrollOffset(const IntPoint& offset) +{ + setLocation(offset); +} + +GraphicsLayer* PinchViewport::layerForContainer() const +{ + return m_innerViewportContainerLayer.get(); +} + +GraphicsLayer* PinchViewport::layerForScrolling() const +{ + return m_innerViewportScrollLayer.get(); +} + +GraphicsLayer* PinchViewport::layerForHorizontalScrollbar() const +{ + return m_overlayScrollbarHorizontal.get(); +} + +GraphicsLayer* PinchViewport::layerForVerticalScrollbar() const +{ + return m_overlayScrollbarVertical.get(); +} + +void PinchViewport::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime) +{ +} + +void PinchViewport::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) +{ +} + +LocalFrame* PinchViewport::mainFrame() const +{ + return m_frameHost.page().mainFrame() && m_frameHost.page().mainFrame()->isLocalFrame() ? m_frameHost.page().deprecatedLocalMainFrame() : 0; +} + +FloatPoint PinchViewport::clampOffsetToBoundaries(const FloatPoint& offset) +{ + FloatPoint clampedOffset(offset); + clampedOffset = clampedOffset.shrunkTo(FloatPoint(maximumScrollPosition())); + clampedOffset = clampedOffset.expandedTo(FloatPoint(minimumScrollPosition())); + return clampedOffset; +} + +String PinchViewport::debugName(const GraphicsLayer* graphicsLayer) +{ + String name; + if (graphicsLayer == m_innerViewportContainerLayer.get()) { + name = "Inner Viewport Container Layer"; + } else if (graphicsLayer == m_pageScaleLayer.get()) { + name = "Page Scale Layer"; + } else if (graphicsLayer == m_innerViewportScrollLayer.get()) { + name = "Inner Viewport Scroll Layer"; + } else if (graphicsLayer == m_overlayScrollbarHorizontal.get()) { + name = "Overlay Scrollbar Horizontal Layer"; + } else if (graphicsLayer == m_overlayScrollbarVertical.get()) { + name = "Overlay Scrollbar Vertical Layer"; + } else { + ASSERT_NOT_REACHED(); + } + + return name; +} + +} // namespace WebCore |