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 "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