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 "platform/scroll/ScrollbarTheme.h" 50 #include "public/platform/Platform.h" 51 #include "public/platform/WebCompositorSupport.h" 52 #include "public/platform/WebLayer.h" 53 #include "public/platform/WebLayerTreeView.h" 54 #include "public/platform/WebScrollbar.h" 55 #include "public/platform/WebScrollbarLayer.h" 56 57 using blink::WebLayer; 58 using blink::WebLayerTreeView; 59 using blink::WebScrollbar; 60 using blink::WebScrollbarLayer; 61 using blink::FrameHost; 62 using blink::GraphicsLayer; 63 using blink::GraphicsLayerFactory; 64 65 namespace blink { 66 67 PinchViewport::PinchViewport(FrameHost& owner) 68 : m_frameHost(owner) 69 , m_scale(1) 70 { 71 reset(); 72 } 73 74 PinchViewport::~PinchViewport() { } 75 76 void PinchViewport::setSize(const IntSize& size) 77 { 78 if (m_size == size) 79 return; 80 81 TRACE_EVENT2("blink", "PinchViewport::setSize", "width", size.width(), "height", size.height()); 82 m_size = size; 83 84 // Make sure we clamp the offset to within the new bounds. 85 setLocation(m_offset); 86 87 if (m_innerViewportContainerLayer) { 88 m_innerViewportContainerLayer->setSize(m_size); 89 90 // Need to re-compute sizes for the overlay scrollbars. 91 setupScrollbar(WebScrollbar::Horizontal); 92 setupScrollbar(WebScrollbar::Vertical); 93 } 94 } 95 96 void PinchViewport::reset() 97 { 98 setLocation(FloatPoint()); 99 setScale(1); 100 } 101 102 void PinchViewport::mainFrameDidChangeSize() 103 { 104 TRACE_EVENT0("blink", "PinchViewport::mainFrameDidChangeSize"); 105 106 // In unit tests we may not have initialized the layer tree. 107 if (m_innerViewportScrollLayer) 108 m_innerViewportScrollLayer->setSize(contentsSize()); 109 110 // Make sure the viewport's offset is clamped within the newly sized main frame. 111 setLocation(m_offset); 112 } 113 114 FloatRect PinchViewport::visibleRect() const 115 { 116 FloatSize scaledSize(m_size); 117 scaledSize.scale(1 / m_scale); 118 return FloatRect(m_offset, scaledSize); 119 } 120 121 FloatRect PinchViewport::visibleRectInDocument() const 122 { 123 if (!mainFrame() || !mainFrame()->view()) 124 return FloatRect(); 125 126 FloatRect viewRect = mainFrame()->view()->visibleContentRect(); 127 FloatRect pinchRect = visibleRect(); 128 pinchRect.moveBy(viewRect.location()); 129 return pinchRect; 130 } 131 132 void PinchViewport::scrollIntoView(const FloatRect& rect) 133 { 134 if (!mainFrame() || !mainFrame()->view()) 135 return; 136 137 FrameView* view = mainFrame()->view(); 138 139 float centeringOffsetX = (visibleRect().width() - rect.width()) / 2; 140 float centeringOffsetY = (visibleRect().height() - rect.height()) / 2; 141 142 FloatPoint targetOffset( 143 rect.x() - centeringOffsetX - visibleRect().x(), 144 rect.y() - centeringOffsetY - visibleRect().y()); 145 146 view->setScrollPosition(flooredIntPoint(targetOffset)); 147 148 FloatPoint remainder = FloatPoint(targetOffset - view->scrollPosition()); 149 move(remainder); 150 } 151 152 void PinchViewport::setLocation(const FloatPoint& newLocation) 153 { 154 FloatPoint clampedOffset(clampOffsetToBoundaries(newLocation)); 155 156 if (clampedOffset == m_offset) 157 return; 158 159 m_offset = clampedOffset; 160 161 ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 162 ASSERT(coordinator); 163 coordinator->scrollableAreaScrollLayerDidChange(this); 164 165 mainFrame()->loader().saveScrollState(); 166 } 167 168 void PinchViewport::move(const FloatPoint& delta) 169 { 170 setLocation(m_offset + delta); 171 } 172 173 void PinchViewport::setScale(float scale) 174 { 175 if (scale == m_scale) 176 return; 177 178 m_scale = scale; 179 180 if (mainFrame()) 181 mainFrame()->loader().saveScrollState(); 182 183 // Old-style pinch sets scale here but we shouldn't call into the 184 // clamping code below. 185 if (!m_innerViewportScrollLayer) 186 return; 187 188 // Ensure we clamp so we remain within the bounds. 189 setLocation(visibleRect().location()); 190 191 // TODO: We should probably be calling scaleDidChange type functions here. 192 // see Page::setPageScaleFactor. 193 } 194 195 // Modifies the top of the graphics layer tree to add layers needed to support 196 // the inner/outer viewport fixed-position model for pinch zoom. When finished, 197 // the tree will look like this (with * denoting added layers): 198 // 199 // *rootTransformLayer 200 // +- *innerViewportContainerLayer (fixed pos container) 201 // +- *pageScaleLayer 202 // | +- *innerViewportScrollLayer 203 // | +-- overflowControlsHostLayer (root layer) 204 // | +-- outerViewportContainerLayer (fixed pos container) [frame container layer in RenderLayerCompositor] 205 // | | +-- outerViewportScrollLayer [frame scroll layer in RenderLayerCompositor] 206 // | | +-- content layers ... 207 // | +-- horizontal ScrollbarLayer (non-overlay) 208 // | +-- verticalScrollbarLayer (non-overlay) 209 // | +-- scroll corner (non-overlay) 210 // +- *horizontalScrollbarLayer (overlay) 211 // +- *verticalScrollbarLayer (overlay) 212 // 213 void PinchViewport::attachToLayerTree(GraphicsLayer* currentLayerTreeRoot, GraphicsLayerFactory* graphicsLayerFactory) 214 { 215 TRACE_EVENT1("blink", "PinchViewport::attachToLayerTree", "currentLayerTreeRoot", (bool)currentLayerTreeRoot); 216 if (!currentLayerTreeRoot) { 217 m_innerViewportScrollLayer->removeAllChildren(); 218 return; 219 } 220 221 if (currentLayerTreeRoot->parent() && currentLayerTreeRoot->parent() == m_innerViewportScrollLayer) 222 return; 223 224 if (!m_innerViewportScrollLayer) { 225 ASSERT(!m_overlayScrollbarHorizontal 226 && !m_overlayScrollbarVertical 227 && !m_pageScaleLayer 228 && !m_innerViewportContainerLayer); 229 230 // FIXME: The root transform layer should only be created on demand. 231 m_rootTransformLayer = GraphicsLayer::create(graphicsLayerFactory, this); 232 m_innerViewportContainerLayer = GraphicsLayer::create(graphicsLayerFactory, this); 233 m_pageScaleLayer = GraphicsLayer::create(graphicsLayerFactory, this); 234 m_innerViewportScrollLayer = GraphicsLayer::create(graphicsLayerFactory, this); 235 m_overlayScrollbarHorizontal = GraphicsLayer::create(graphicsLayerFactory, this); 236 m_overlayScrollbarVertical = GraphicsLayer::create(graphicsLayerFactory, this); 237 238 blink::ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 239 ASSERT(coordinator); 240 coordinator->setLayerIsContainerForFixedPositionLayers(m_innerViewportScrollLayer.get(), true); 241 242 // Set masks to bounds so the compositor doesn't clobber a manually 243 // set inner viewport container layer size. 244 m_innerViewportContainerLayer->setMasksToBounds(m_frameHost.settings().mainFrameClipsContent()); 245 m_innerViewportContainerLayer->setSize(m_size); 246 247 m_innerViewportScrollLayer->platformLayer()->setScrollClipLayer( 248 m_innerViewportContainerLayer->platformLayer()); 249 m_innerViewportScrollLayer->platformLayer()->setUserScrollable(true, true); 250 251 m_rootTransformLayer->addChild(m_innerViewportContainerLayer.get()); 252 m_innerViewportContainerLayer->addChild(m_pageScaleLayer.get()); 253 m_pageScaleLayer->addChild(m_innerViewportScrollLayer.get()); 254 m_innerViewportContainerLayer->addChild(m_overlayScrollbarHorizontal.get()); 255 m_innerViewportContainerLayer->addChild(m_overlayScrollbarVertical.get()); 256 257 // Ensure this class is set as the scroll layer's ScrollableArea. 258 coordinator->scrollableAreaScrollLayerDidChange(this); 259 260 // Setup the inner viewport overlay scrollbars. 261 setupScrollbar(WebScrollbar::Horizontal); 262 setupScrollbar(WebScrollbar::Vertical); 263 } 264 265 m_innerViewportScrollLayer->removeAllChildren(); 266 m_innerViewportScrollLayer->addChild(currentLayerTreeRoot); 267 } 268 269 void PinchViewport::setupScrollbar(WebScrollbar::Orientation orientation) 270 { 271 bool isHorizontal = orientation == WebScrollbar::Horizontal; 272 GraphicsLayer* scrollbarGraphicsLayer = isHorizontal ? 273 m_overlayScrollbarHorizontal.get() : m_overlayScrollbarVertical.get(); 274 OwnPtr<WebScrollbarLayer>& webScrollbarLayer = isHorizontal ? 275 m_webOverlayScrollbarHorizontal : m_webOverlayScrollbarVertical; 276 277 int thumbThickness = m_frameHost.settings().pinchOverlayScrollbarThickness(); 278 int scrollbarThickness = thumbThickness; 279 280 // FIXME: Rather than manually creating scrollbar layers, we should create 281 // real scrollbars so we can reuse all the machinery from ScrollbarTheme. 282 #if OS(ANDROID) 283 thumbThickness = ScrollbarTheme::theme()->thumbThickness(0); 284 scrollbarThickness = ScrollbarTheme::theme()->scrollbarThickness(RegularScrollbar); 285 #endif 286 287 if (!webScrollbarLayer) { 288 ScrollingCoordinator* coordinator = m_frameHost.page().scrollingCoordinator(); 289 ASSERT(coordinator); 290 ScrollbarOrientation webcoreOrientation = isHorizontal ? HorizontalScrollbar : VerticalScrollbar; 291 webScrollbarLayer = coordinator->createSolidColorScrollbarLayer(webcoreOrientation, thumbThickness, 0, false); 292 293 webScrollbarLayer->setClipLayer(m_innerViewportContainerLayer->platformLayer()); 294 scrollbarGraphicsLayer->setContentsToPlatformLayer(webScrollbarLayer->layer()); 295 scrollbarGraphicsLayer->setDrawsContent(false); 296 } 297 298 int xPosition = isHorizontal ? 0 : m_innerViewportContainerLayer->size().width() - scrollbarThickness; 299 int yPosition = isHorizontal ? m_innerViewportContainerLayer->size().height() - scrollbarThickness : 0; 300 int width = isHorizontal ? m_innerViewportContainerLayer->size().width() - scrollbarThickness : scrollbarThickness; 301 int height = isHorizontal ? scrollbarThickness : m_innerViewportContainerLayer->size().height() - scrollbarThickness; 302 303 // Use the GraphicsLayer to position the scrollbars. 304 scrollbarGraphicsLayer->setPosition(IntPoint(xPosition, yPosition)); 305 scrollbarGraphicsLayer->setSize(IntSize(width, height)); 306 scrollbarGraphicsLayer->setContentsRect(IntRect(0, 0, width, height)); 307 } 308 309 void PinchViewport::registerLayersWithTreeView(WebLayerTreeView* layerTreeView) const 310 { 311 TRACE_EVENT0("blink", "PinchViewport::registerLayersWithTreeView"); 312 ASSERT(layerTreeView); 313 ASSERT(m_frameHost.page().mainFrame()); 314 ASSERT(m_frameHost.page().mainFrame()->isLocalFrame()); 315 ASSERT(m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()); 316 317 RenderLayerCompositor* compositor = m_frameHost.page().deprecatedLocalMainFrame()->contentRenderer()->compositor(); 318 // Get the outer viewport scroll layer. 319 WebLayer* scrollLayer = compositor->scrollLayer()->platformLayer(); 320 321 m_webOverlayScrollbarHorizontal->setScrollLayer(scrollLayer); 322 m_webOverlayScrollbarVertical->setScrollLayer(scrollLayer); 323 324 ASSERT(compositor); 325 layerTreeView->registerViewportLayers( 326 m_pageScaleLayer->platformLayer(), 327 m_innerViewportScrollLayer->platformLayer(), 328 scrollLayer); 329 } 330 331 void PinchViewport::clearLayersForTreeView(WebLayerTreeView* layerTreeView) const 332 { 333 ASSERT(layerTreeView); 334 335 layerTreeView->clearViewportLayers(); 336 } 337 338 int PinchViewport::scrollSize(ScrollbarOrientation orientation) const 339 { 340 IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition(); 341 return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height(); 342 } 343 344 IntPoint PinchViewport::minimumScrollPosition() const 345 { 346 return IntPoint(); 347 } 348 349 IntPoint PinchViewport::maximumScrollPosition() const 350 { 351 return flooredIntPoint(FloatSize(contentsSize()) - visibleRect().size()); 352 } 353 354 IntRect PinchViewport::scrollableAreaBoundingBox() const 355 { 356 // This method should return the bounding box in the parent view's coordinate 357 // space; however, PinchViewport technically isn't a child of any Frames. 358 // Nonetheless, the PinchViewport always occupies the entire main frame so just 359 // return that. 360 LocalFrame* frame = mainFrame(); 361 362 if (!frame || !frame->view()) 363 return IntRect(); 364 365 return frame->view()->frameRect(); 366 } 367 368 IntSize PinchViewport::contentsSize() const 369 { 370 LocalFrame* frame = mainFrame(); 371 372 if (!frame || !frame->view()) 373 return IntSize(); 374 375 ASSERT(frame->view()->visibleContentScaleFactor() == 1); 376 return frame->view()->visibleContentRect(IncludeScrollbars).size(); 377 } 378 379 void PinchViewport::invalidateScrollbarRect(Scrollbar*, const IntRect&) 380 { 381 // Do nothing. Pinch scrollbars live on the compositor thread and will 382 // be updated when the viewport is synced to the CC. 383 } 384 385 void PinchViewport::setScrollOffset(const IntPoint& offset) 386 { 387 setLocation(offset); 388 } 389 390 GraphicsLayer* PinchViewport::layerForContainer() const 391 { 392 return m_innerViewportContainerLayer.get(); 393 } 394 395 GraphicsLayer* PinchViewport::layerForScrolling() const 396 { 397 return m_innerViewportScrollLayer.get(); 398 } 399 400 GraphicsLayer* PinchViewport::layerForHorizontalScrollbar() const 401 { 402 return m_overlayScrollbarHorizontal.get(); 403 } 404 405 GraphicsLayer* PinchViewport::layerForVerticalScrollbar() const 406 { 407 return m_overlayScrollbarVertical.get(); 408 } 409 410 void PinchViewport::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime) 411 { 412 } 413 414 void PinchViewport::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) 415 { 416 } 417 418 LocalFrame* PinchViewport::mainFrame() const 419 { 420 return m_frameHost.page().mainFrame() && m_frameHost.page().mainFrame()->isLocalFrame() ? m_frameHost.page().deprecatedLocalMainFrame() : 0; 421 } 422 423 FloatPoint PinchViewport::clampOffsetToBoundaries(const FloatPoint& offset) 424 { 425 FloatPoint clampedOffset(offset); 426 clampedOffset = clampedOffset.shrunkTo(FloatPoint(maximumScrollPosition())); 427 clampedOffset = clampedOffset.expandedTo(FloatPoint(minimumScrollPosition())); 428 return clampedOffset; 429 } 430 431 String PinchViewport::debugName(const GraphicsLayer* graphicsLayer) 432 { 433 String name; 434 if (graphicsLayer == m_innerViewportContainerLayer.get()) { 435 name = "Inner Viewport Container Layer"; 436 } else if (graphicsLayer == m_pageScaleLayer.get()) { 437 name = "Page Scale Layer"; 438 } else if (graphicsLayer == m_innerViewportScrollLayer.get()) { 439 name = "Inner Viewport Scroll Layer"; 440 } else if (graphicsLayer == m_overlayScrollbarHorizontal.get()) { 441 name = "Overlay Scrollbar Horizontal Layer"; 442 } else if (graphicsLayer == m_overlayScrollbarVertical.get()) { 443 name = "Overlay Scrollbar Vertical Layer"; 444 } else { 445 ASSERT_NOT_REACHED(); 446 } 447 448 return name; 449 } 450 451 } // namespace blink 452