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 56 using blink::WebLayer; 57 using blink::WebLayerTreeView; 58 using blink::WebScrollbar; 59 using blink::WebScrollbarLayer; 60 using WebCore::FrameHost; 61 using WebCore::GraphicsLayer; 62 using WebCore::GraphicsLayerFactory; 63 64 namespace WebCore { 65 66 PinchViewport::PinchViewport(FrameHost& owner) 67 : m_frameHost(owner) 68 , m_scale(1) 69 { 70 reset(); 71 } 72 73 PinchViewport::~PinchViewport() { } 74 75 void 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 95 void PinchViewport::reset() 96 { 97 setLocation(FloatPoint()); 98 setScale(1); 99 } 100 101 void 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 113 FloatRect PinchViewport::visibleRect() const 114 { 115 FloatSize scaledSize(m_size); 116 scaledSize.scale(1 / m_scale); 117 return FloatRect(m_offset, scaledSize); 118 } 119 120 FloatRect 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 131 void 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 151 void 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 167 void PinchViewport::move(const FloatPoint& delta) 168 { 169 setLocation(m_offset + delta); 170 } 171 172 void 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 // 212 void 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 279 void 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 311 void 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 333 void PinchViewport::clearLayersForTreeView(WebLayerTreeView* layerTreeView) const 334 { 335 ASSERT(layerTreeView); 336 337 layerTreeView->clearViewportLayers(); 338 } 339 340 int PinchViewport::scrollSize(ScrollbarOrientation orientation) const 341 { 342 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition(); 343 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height(); 344 } 345 346 IntPoint PinchViewport::minimumScrollPosition() const 347 { 348 return IntPoint(); 349 } 350 351 IntPoint PinchViewport::maximumScrollPosition() const 352 { 353 return flooredIntPoint(FloatSize(contentsSize()) - visibleRect().size()); 354 } 355 356 IntRect 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 370 IntSize 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 381 void 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 387 void PinchViewport::setScrollOffset(const IntPoint& offset) 388 { 389 setLocation(offset); 390 } 391 392 GraphicsLayer* PinchViewport::layerForContainer() const 393 { 394 return m_innerViewportContainerLayer.get(); 395 } 396 397 GraphicsLayer* PinchViewport::layerForScrolling() const 398 { 399 return m_innerViewportScrollLayer.get(); 400 } 401 402 GraphicsLayer* PinchViewport::layerForHorizontalScrollbar() const 403 { 404 return m_overlayScrollbarHorizontal.get(); 405 } 406 407 GraphicsLayer* PinchViewport::layerForVerticalScrollbar() const 408 { 409 return m_overlayScrollbarVertical.get(); 410 } 411 412 void PinchViewport::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime) 413 { 414 } 415 416 void PinchViewport::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) 417 { 418 } 419 420 LocalFrame* PinchViewport::mainFrame() const 421 { 422 return m_frameHost.page().mainFrame() && m_frameHost.page().mainFrame()->isLocalFrame() ? m_frameHost.page().deprecatedLocalMainFrame() : 0; 423 } 424 425 FloatPoint 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 433 String 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