Home | History | Annotate | Download | only in frame
      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