1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/frame/PinchViewport.h" 33 34#include "core/frame/FrameHost.h" 35#include "core/frame/FrameView.h" 36#include "core/frame/LocalFrame.h" 37#include "core/frame/Settings.h" 38#include "core/page/Chrome.h" 39#include "core/page/ChromeClient.h" 40#include "core/page/Page.h" 41#include "core/page/scrolling/ScrollingCoordinator.h" 42#include "core/rendering/RenderView.h" 43#include "core/rendering/compositing/RenderLayerCompositor.h" 44#include "platform/TraceEvent.h" 45#include "platform/geometry/FloatSize.h" 46#include "platform/graphics/GraphicsLayer.h" 47#include "platform/graphics/GraphicsLayerFactory.h" 48#include "platform/scroll/Scrollbar.h" 49#include "public/platform/Platform.h" 50#include "public/platform/WebCompositorSupport.h" 51#include "public/platform/WebLayer.h" 52#include "public/platform/WebLayerTreeView.h" 53#include "public/platform/WebScrollbar.h" 54#include "public/platform/WebScrollbarLayer.h" 55 56using blink::WebLayer; 57using blink::WebLayerTreeView; 58using blink::WebScrollbar; 59using blink::WebScrollbarLayer; 60using WebCore::FrameHost; 61using WebCore::GraphicsLayer; 62using WebCore::GraphicsLayerFactory; 63 64namespace WebCore { 65 66PinchViewport::PinchViewport(FrameHost& owner) 67 : m_frameHost(owner) 68 , m_scale(1) 69{ 70 reset(); 71} 72 73PinchViewport::~PinchViewport() { } 74 75void PinchViewport::setSize(const IntSize& size) 76{ 77 if (m_size == size) 78 return; 79 80 TRACE_EVENT2("webkit", "PinchViewport::setSize", "width", size.width(), "height", size.height()); 81 m_size = size; 82 83 // Make sure we clamp the offset to within the new bounds. 84 setLocation(m_offset); 85 86 if (m_innerViewportContainerLayer) { 87 m_innerViewportContainerLayer->setSize(m_size); 88 89 // Need to re-compute sizes for the overlay scrollbars. 90 setupScrollbar(WebScrollbar::Horizontal); 91 setupScrollbar(WebScrollbar::Vertical); 92 } 93} 94 95void PinchViewport::reset() 96{ 97 setLocation(FloatPoint()); 98 setScale(1); 99} 100 101void PinchViewport::mainFrameDidChangeSize() 102{ 103 TRACE_EVENT0("webkit", "PinchViewport::mainFrameDidChangeSize"); 104 105 // In unit tests we may not have initialized the layer tree. 106 if (m_innerViewportScrollLayer) 107 m_innerViewportScrollLayer->setSize(contentsSize()); 108 109 // Make sure the viewport's offset is clamped within the newly sized main frame. 110 setLocation(m_offset); 111} 112 113FloatRect PinchViewport::visibleRect() const 114{ 115 FloatSize scaledSize(m_size); 116 scaledSize.scale(1 / m_scale); 117 return FloatRect(m_offset, scaledSize); 118} 119 120FloatRect PinchViewport::visibleRectInDocument() const 121{ 122 if (!mainFrame() || !mainFrame()->view()) 123 return FloatRect(); 124 125 FloatRect viewRect = mainFrame()->view()->visibleContentRect(); 126 FloatRect pinchRect = visibleRect(); 127 pinchRect.moveBy(viewRect.location()); 128 return pinchRect; 129} 130 131void PinchViewport::scrollIntoView(const FloatRect& rect) 132{ 133 if (!mainFrame() || !mainFrame()->view()) 134 return; 135 136 FrameView* view = mainFrame()->view(); 137 138 float centeringOffsetX = (visibleRect().width() - rect.width()) / 2; 139 float centeringOffsetY = (visibleRect().height() - rect.height()) / 2; 140 141 FloatPoint targetOffset( 142 rect.x() - centeringOffsetX - visibleRect().x(), 143 rect.y() - centeringOffsetY - visibleRect().y()); 144 145 view->setScrollPosition(flooredIntPoint(targetOffset)); 146 147 FloatPoint remainder = FloatPoint(targetOffset - view->scrollPosition()); 148 move(remainder); 149} 150 151void PinchViewport::setLocation(const FloatPoint& newLocation) 152{ 153 FloatPoint clampedOffset(clampOffsetToBoundaries(newLocation)); 154 155 if (clampedOffset == m_offset) 156 return; 157 158 m_offset = clampedOffset; 159 160 ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 161 ASSERT(coordinator); 162 coordinator->scrollableAreaScrollLayerDidChange(this); 163 164 mainFrame()->loader().saveScrollState(); 165} 166 167void PinchViewport::move(const FloatPoint& delta) 168{ 169 setLocation(m_offset + delta); 170} 171 172void PinchViewport::setScale(float scale) 173{ 174 if (scale == m_scale) 175 return; 176 177 m_scale = scale; 178 179 if (mainFrame()) 180 mainFrame()->loader().saveScrollState(); 181 182 // Old-style pinch sets scale here but we shouldn't call into the 183 // clamping code below. 184 if (!m_innerViewportScrollLayer) 185 return; 186 187 // Ensure we clamp so we remain within the bounds. 188 setLocation(visibleRect().location()); 189 190 // TODO: We should probably be calling scaleDidChange type functions here. 191 // see Page::setPageScaleFactor. 192} 193 194// Modifies the top of the graphics layer tree to add layers needed to support 195// the inner/outer viewport fixed-position model for pinch zoom. When finished, 196// the tree will look like this (with * denoting added layers): 197// 198// *rootTransformLayer 199// +- *innerViewportContainerLayer (fixed pos container) 200// +- *pageScaleLayer 201// | +- *innerViewportScrollLayer 202// | +-- overflowControlsHostLayer (root layer) 203// | +-- outerViewportContainerLayer (fixed pos container) [frame container layer in RenderLayerCompositor] 204// | | +-- outerViewportScrollLayer [frame scroll layer in RenderLayerCompositor] 205// | | +-- content layers ... 206// | +-- horizontal ScrollbarLayer (non-overlay) 207// | +-- verticalScrollbarLayer (non-overlay) 208// | +-- scroll corner (non-overlay) 209// +- *horizontalScrollbarLayer (overlay) 210// +- *verticalScrollbarLayer (overlay) 211// 212void PinchViewport::attachToLayerTree(GraphicsLayer* currentLayerTreeRoot, GraphicsLayerFactory* graphicsLayerFactory) 213{ 214 TRACE_EVENT1("webkit", "PinchViewport::attachToLayerTree", "currentLayerTreeRoot", (bool)currentLayerTreeRoot); 215 if (!currentLayerTreeRoot) { 216 m_innerViewportScrollLayer->removeAllChildren(); 217 return; 218 } 219 220 if (currentLayerTreeRoot->parent() && currentLayerTreeRoot->parent() == m_innerViewportScrollLayer) 221 return; 222 223 if (!m_innerViewportScrollLayer) { 224 ASSERT(!m_overlayScrollbarHorizontal 225 && !m_overlayScrollbarVertical 226 && !m_pageScaleLayer 227 && !m_innerViewportContainerLayer); 228 229 // FIXME: The root transform layer should only be created on demand. 230 m_rootTransformLayer = GraphicsLayer::create(graphicsLayerFactory, this); 231 m_innerViewportContainerLayer = GraphicsLayer::create(graphicsLayerFactory, this); 232 m_pageScaleLayer = GraphicsLayer::create(graphicsLayerFactory, this); 233 m_innerViewportScrollLayer = GraphicsLayer::create(graphicsLayerFactory, this); 234 m_overlayScrollbarHorizontal = GraphicsLayer::create(graphicsLayerFactory, this); 235 m_overlayScrollbarVertical = GraphicsLayer::create(graphicsLayerFactory, this); 236 237 WebCore::ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 238 ASSERT(coordinator); 239 coordinator->setLayerIsContainerForFixedPositionLayers(m_innerViewportScrollLayer.get(), true); 240 241 // Set masks to bounds so the compositor doesn't clobber a manually 242 // set inner viewport container layer size. 243 m_innerViewportContainerLayer->setMasksToBounds(m_frameHost.settings().mainFrameClipsContent()); 244 m_innerViewportContainerLayer->setSize(m_size); 245 246 m_innerViewportScrollLayer->platformLayer()->setScrollClipLayer( 247 m_innerViewportContainerLayer->platformLayer()); 248 m_innerViewportScrollLayer->platformLayer()->setUserScrollable(true, true); 249 250 m_rootTransformLayer->addChild(m_innerViewportContainerLayer.get()); 251 m_innerViewportContainerLayer->addChild(m_pageScaleLayer.get()); 252 m_pageScaleLayer->addChild(m_innerViewportScrollLayer.get()); 253 m_innerViewportContainerLayer->addChild(m_overlayScrollbarHorizontal.get()); 254 m_innerViewportContainerLayer->addChild(m_overlayScrollbarVertical.get()); 255 256 // Ensure this class is set as the scroll layer's ScrollableArea. 257 coordinator->scrollableAreaScrollLayerDidChange(this); 258 259 // Setup the inner viewport overlay scrollbars. 260 setupScrollbar(WebScrollbar::Horizontal); 261 setupScrollbar(WebScrollbar::Vertical); 262 } 263 264 m_innerViewportScrollLayer->removeAllChildren(); 265 m_innerViewportScrollLayer->addChild(currentLayerTreeRoot); 266 267 // We only need to disable the existing (outer viewport) scrollbars 268 // if the existing ones are already overlay. 269 // FIXME: If we knew in advance before the overflowControlsHostLayer goes 270 // away, we would re-enable the drawing of these scrollbars. 271 // FIXME: This doesn't seem to work (at least on Android). Commenting out for now until 272 // I figure out how to access RenderLayerCompositor from here. 273 // if (GraphicsLayer* scrollbar = m_frameHost->compositor()->layerForHorizontalScrollbar()) 274 // scrollbar->setDrawsContent(!page->mainFrame()->view()->hasOverlayScrollbars()); 275 // if (GraphicsLayer* scrollbar = m_frameHost->compositor()->layerForVerticalScrollbar()) 276 // scrollbar->setDrawsContent(!page->mainFrame()->view()->hasOverlayScrollbars()); 277} 278 279void PinchViewport::setupScrollbar(WebScrollbar::Orientation orientation) 280{ 281 bool isHorizontal = orientation == WebScrollbar::Horizontal; 282 GraphicsLayer* scrollbarGraphicsLayer = isHorizontal ? 283 m_overlayScrollbarHorizontal.get() : m_overlayScrollbarVertical.get(); 284 OwnPtr<WebScrollbarLayer>& webScrollbarLayer = isHorizontal ? 285 m_webOverlayScrollbarHorizontal : m_webOverlayScrollbarVertical; 286 287 const int overlayScrollbarThickness = m_frameHost.settings().pinchOverlayScrollbarThickness(); 288 289 if (!webScrollbarLayer) { 290 ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 291 ASSERT(coordinator); 292 ScrollbarOrientation webcoreOrientation = isHorizontal ? HorizontalScrollbar : VerticalScrollbar; 293 webScrollbarLayer = coordinator->createSolidColorScrollbarLayer(webcoreOrientation, overlayScrollbarThickness, 0, false); 294 295 webScrollbarLayer->setClipLayer(m_innerViewportContainerLayer->platformLayer()); 296 scrollbarGraphicsLayer->setContentsToPlatformLayer(webScrollbarLayer->layer()); 297 scrollbarGraphicsLayer->setDrawsContent(false); 298 } 299 300 int xPosition = isHorizontal ? 0 : m_innerViewportContainerLayer->size().width() - overlayScrollbarThickness; 301 int yPosition = isHorizontal ? m_innerViewportContainerLayer->size().height() - overlayScrollbarThickness : 0; 302 int width = isHorizontal ? m_innerViewportContainerLayer->size().width() - overlayScrollbarThickness : overlayScrollbarThickness; 303 int height = isHorizontal ? overlayScrollbarThickness : m_innerViewportContainerLayer->size().height() - overlayScrollbarThickness; 304 305 // Use the GraphicsLayer to position the scrollbars. 306 scrollbarGraphicsLayer->setPosition(IntPoint(xPosition, yPosition)); 307 scrollbarGraphicsLayer->setSize(IntSize(width, height)); 308 scrollbarGraphicsLayer->setContentsRect(IntRect(0, 0, width, height)); 309} 310 311void PinchViewport::registerLayersWithTreeView(WebLayerTreeView* layerTreeView) const 312{ 313 TRACE_EVENT0("webkit", "PinchViewport::registerLayersWithTreeView"); 314 ASSERT(layerTreeView); 315 ASSERT(m_frameHost.page().mainFrame()); 316 ASSERT(m_frameHost.page().mainFrame()->isLocalFrame()); 317 ASSERT(m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()); 318 319 RenderLayerCompositor* compositor = m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()->compositor(); 320 // Get the outer viewport scroll layer. 321 WebLayer* scrollLayer = compositor->scrollLayer()->platformLayer(); 322 323 m_webOverlayScrollbarHorizontal->setScrollLayer(scrollLayer); 324 m_webOverlayScrollbarVertical->setScrollLayer(scrollLayer); 325 326 ASSERT(compositor); 327 layerTreeView->registerViewportLayers( 328 m_pageScaleLayer->platformLayer(), 329 m_innerViewportScrollLayer->platformLayer(), 330 scrollLayer); 331} 332 333void PinchViewport::clearLayersForTreeView(WebLayerTreeView* layerTreeView) const 334{ 335 ASSERT(layerTreeView); 336 337 layerTreeView->clearViewportLayers(); 338} 339 340int PinchViewport::scrollSize(ScrollbarOrientation orientation) const 341{ 342 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition(); 343 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height(); 344} 345 346IntPoint PinchViewport::minimumScrollPosition() const 347{ 348 return IntPoint(); 349} 350 351IntPoint PinchViewport::maximumScrollPosition() const 352{ 353 return flooredIntPoint(FloatSize(contentsSize()) - visibleRect().size()); 354} 355 356IntRect PinchViewport::scrollableAreaBoundingBox() const 357{ 358 // This method should return the bounding box in the parent view's coordinate 359 // space; however, PinchViewport technically isn't a child of any Frames. 360 // Nonetheless, the PinchViewport always occupies the entire main frame so just 361 // return that. 362 LocalFrame* frame = mainFrame(); 363 364 if (!frame || !frame->view()) 365 return IntRect(); 366 367 return frame->view()->frameRect(); 368} 369 370IntSize PinchViewport::contentsSize() const 371{ 372 LocalFrame* frame = mainFrame(); 373 374 if (!frame || !frame->view()) 375 return IntSize(); 376 377 ASSERT(frame->view()->visibleContentScaleFactor() == 1); 378 return frame->view()->visibleContentRect(IncludeScrollbars).size(); 379} 380 381void PinchViewport::invalidateScrollbarRect(Scrollbar*, const IntRect&) 382{ 383 // Do nothing. Pinch scrollbars live on the compositor thread and will 384 // be updated when the viewport is synced to the CC. 385} 386 387void PinchViewport::setScrollOffset(const IntPoint& offset) 388{ 389 setLocation(offset); 390} 391 392GraphicsLayer* PinchViewport::layerForContainer() const 393{ 394 return m_innerViewportContainerLayer.get(); 395} 396 397GraphicsLayer* PinchViewport::layerForScrolling() const 398{ 399 return m_innerViewportScrollLayer.get(); 400} 401 402GraphicsLayer* PinchViewport::layerForHorizontalScrollbar() const 403{ 404 return m_overlayScrollbarHorizontal.get(); 405} 406 407GraphicsLayer* PinchViewport::layerForVerticalScrollbar() const 408{ 409 return m_overlayScrollbarVertical.get(); 410} 411 412void PinchViewport::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime) 413{ 414} 415 416void PinchViewport::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) 417{ 418} 419 420LocalFrame* PinchViewport::mainFrame() const 421{ 422 return m_frameHost.page().mainFrame() && m_frameHost.page().mainFrame()->isLocalFrame() ? m_frameHost.page().deprecatedLocalMainFrame() : 0; 423} 424 425FloatPoint PinchViewport::clampOffsetToBoundaries(const FloatPoint& offset) 426{ 427 FloatPoint clampedOffset(offset); 428 clampedOffset = clampedOffset.shrunkTo(FloatPoint(maximumScrollPosition())); 429 clampedOffset = clampedOffset.expandedTo(FloatPoint(minimumScrollPosition())); 430 return clampedOffset; 431} 432 433String PinchViewport::debugName(const GraphicsLayer* graphicsLayer) 434{ 435 String name; 436 if (graphicsLayer == m_innerViewportContainerLayer.get()) { 437 name = "Inner Viewport Container Layer"; 438 } else if (graphicsLayer == m_pageScaleLayer.get()) { 439 name = "Page Scale Layer"; 440 } else if (graphicsLayer == m_innerViewportScrollLayer.get()) { 441 name = "Inner Viewport Scroll Layer"; 442 } else if (graphicsLayer == m_overlayScrollbarHorizontal.get()) { 443 name = "Overlay Scrollbar Horizontal Layer"; 444 } else if (graphicsLayer == m_overlayScrollbarVertical.get()) { 445 name = "Overlay Scrollbar Vertical Layer"; 446 } else { 447 ASSERT_NOT_REACHED(); 448 } 449 450 return name; 451} 452 453} // namespace WebCore 454