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