• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PinchViewport(FrameHost & owner)67 PinchViewport::PinchViewport(FrameHost& owner)
68     : m_frameHost(owner)
69     , m_scale(1)
70 {
71     reset();
72 }
73 
~PinchViewport()74 PinchViewport::~PinchViewport() { }
75 
setSize(const IntSize & size)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 
reset()96 void PinchViewport::reset()
97 {
98     setLocation(FloatPoint());
99     setScale(1);
100 }
101 
mainFrameDidChangeSize()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 
visibleRect() const114 FloatRect PinchViewport::visibleRect() const
115 {
116     FloatSize scaledSize(m_size);
117     scaledSize.scale(1 / m_scale);
118     return FloatRect(m_offset, scaledSize);
119 }
120 
visibleRectInDocument() const121 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 
scrollIntoView(const FloatRect & rect)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 
setLocation(const FloatPoint & newLocation)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 
move(const FloatPoint & delta)168 void PinchViewport::move(const FloatPoint& delta)
169 {
170     setLocation(m_offset + delta);
171 }
172 
setScale(float scale)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 //
attachToLayerTree(GraphicsLayer * currentLayerTreeRoot,GraphicsLayerFactory * graphicsLayerFactory)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 
setupScrollbar(WebScrollbar::Orientation orientation)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 
registerLayersWithTreeView(WebLayerTreeView * layerTreeView) const309 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 
clearLayersForTreeView(WebLayerTreeView * layerTreeView) const331 void PinchViewport::clearLayersForTreeView(WebLayerTreeView* layerTreeView) const
332 {
333     ASSERT(layerTreeView);
334 
335     layerTreeView->clearViewportLayers();
336 }
337 
scrollSize(ScrollbarOrientation orientation) const338 int PinchViewport::scrollSize(ScrollbarOrientation orientation) const
339 {
340     IntSize scrollDimensions = maximumScrollPosition() - minimumScrollPosition();
341     return (orientation == HorizontalScrollbar) ? scrollDimensions.width() : scrollDimensions.height();
342 }
343 
minimumScrollPosition() const344 IntPoint PinchViewport::minimumScrollPosition() const
345 {
346     return IntPoint();
347 }
348 
maximumScrollPosition() const349 IntPoint PinchViewport::maximumScrollPosition() const
350 {
351     return flooredIntPoint(FloatSize(contentsSize()) - visibleRect().size());
352 }
353 
scrollableAreaBoundingBox() const354 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 
contentsSize() const368 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 
invalidateScrollbarRect(Scrollbar *,const IntRect &)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 
setScrollOffset(const IntPoint & offset)385 void PinchViewport::setScrollOffset(const IntPoint& offset)
386 {
387     setLocation(offset);
388 }
389 
layerForContainer() const390 GraphicsLayer* PinchViewport::layerForContainer() const
391 {
392     return m_innerViewportContainerLayer.get();
393 }
394 
layerForScrolling() const395 GraphicsLayer* PinchViewport::layerForScrolling() const
396 {
397     return m_innerViewportScrollLayer.get();
398 }
399 
layerForHorizontalScrollbar() const400 GraphicsLayer* PinchViewport::layerForHorizontalScrollbar() const
401 {
402     return m_overlayScrollbarHorizontal.get();
403 }
404 
layerForVerticalScrollbar() const405 GraphicsLayer* PinchViewport::layerForVerticalScrollbar() const
406 {
407     return m_overlayScrollbarVertical.get();
408 }
409 
notifyAnimationStarted(const GraphicsLayer *,double monotonicTime)410 void PinchViewport::notifyAnimationStarted(const GraphicsLayer*, double monotonicTime)
411 {
412 }
413 
paintContents(const GraphicsLayer *,GraphicsContext &,GraphicsLayerPaintingPhase,const IntRect & inClip)414 void PinchViewport::paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip)
415 {
416 }
417 
mainFrame() const418 LocalFrame* PinchViewport::mainFrame() const
419 {
420     return m_frameHost.page().mainFrame() && m_frameHost.page().mainFrame()->isLocalFrame() ? m_frameHost.page().deprecatedLocalMainFrame() : 0;
421 }
422 
clampOffsetToBoundaries(const FloatPoint & offset)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 
debugName(const GraphicsLayer * graphicsLayer)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