1/*
2 * Copyright (C) 2011 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "ScrollingCoordinator.h"
29
30#include "Document.h"
31#include "EventNames.h"
32#include "Frame.h"
33#include "FrameView.h"
34#include "GraphicsLayer.h"
35#include "Page.h"
36#include "PlatformWheelEvent.h"
37#include "PluginViewBase.h"
38#include "Region.h"
39#include "RenderLayerCompositor.h"
40#include "RenderView.h"
41#include "RuntimeEnabledFeatures.h"
42#include "ScrollAnimator.h"
43#include "Settings.h"
44#include <wtf/MainThread.h>
45#include <wtf/text/StringBuilder.h>
46#include <wtf/text/TextStream.h>
47
48namespace WebCore {
49
50#if PLATFORM(IOS_FAMILY) || !ENABLE(ASYNC_SCROLLING)
51Ref<ScrollingCoordinator> ScrollingCoordinator::create(Page* page)
52{
53 return adoptRef(*new ScrollingCoordinator(page));
54}
55#endif
56
57ScrollingCoordinator::ScrollingCoordinator(Page* page)
58 : m_page(page)
59{
60}
61
62ScrollingCoordinator::~ScrollingCoordinator()
63{
64 ASSERT(!m_page);
65}
66
67void ScrollingCoordinator::pageDestroyed()
68{
69 ASSERT(m_page);
70 m_page = nullptr;
71}
72
73bool ScrollingCoordinator::coordinatesScrollingForFrameView(const FrameView& frameView) const
74{
75 ASSERT(isMainThread());
76 ASSERT(m_page);
77
78 if (!frameView.frame().isMainFrame() && !m_page->settings().scrollingTreeIncludesFrames()
79#if PLATFORM(MAC)
80 && !m_page->settings().asyncFrameScrollingEnabled()
81#endif
82 )
83 return false;
84
85 auto* renderView = frameView.frame().contentRenderer();
86 if (!renderView)
87 return false;
88 return renderView->usesCompositing();
89}
90
91bool ScrollingCoordinator::coordinatesScrollingForOverflowLayer(const RenderLayer& layer) const
92{
93 ASSERT(isMainThread());
94 ASSERT(m_page);
95
96 return layer.hasCompositedScrollableOverflow();
97}
98
99EventTrackingRegions ScrollingCoordinator::absoluteEventTrackingRegionsForFrame(const Frame& frame) const
100{
101 auto* renderView = frame.contentRenderer();
102 if (!renderView || renderView->renderTreeBeingDestroyed())
103 return EventTrackingRegions();
104
105#if ENABLE(IOS_TOUCH_EVENTS)
106 // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers.
107 ASSERT(frame.isMainFrame());
108 auto* document = frame.document();
109 if (!document)
110 return EventTrackingRegions();
111 auto eventTrackingRegions = document->eventTrackingRegions();
112
113#if ENABLE(POINTER_EVENTS)
114 if (!document->quirks().shouldDisablePointerEventsQuirk() && RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) {
115 if (auto* touchActionElements = frame.document()->touchActionElements()) {
116 auto& touchActionData = eventTrackingRegions.touchActionData;
117 for (const auto& element : *touchActionElements) {
118 ASSERT(element);
119 touchActionData.append({
120 element->computedTouchActions(),
121 element->nearestScrollingNodeIDUsingTouchOverflowScrolling(),
122 element->document().absoluteEventRegionForNode(*element).first
123 });
124 }
125 }
126 }
127#endif
128
129 return eventTrackingRegions;
130#else
131 auto* frameView = frame.view();
132 if (!frameView)
133 return EventTrackingRegions();
134
135 Region nonFastScrollableRegion;
136
137 // FIXME: should ASSERT(!frameView->needsLayout()) here, but need to fix DebugPageOverlays
138 // to not ask for regions at bad times.
139
140 if (auto* scrollableAreas = frameView->scrollableAreas()) {
141 for (auto& scrollableArea : *scrollableAreas) {
142 // Composited scrollable areas can be scrolled off the main thread.
143 if (scrollableArea->usesAsyncScrolling())
144 continue;
145
146 bool isInsideFixed;
147 IntRect box = scrollableArea->scrollableAreaBoundingBox(&isInsideFixed);
148 if (isInsideFixed)
149 box = IntRect(frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(box)));
150
151 nonFastScrollableRegion.unite(box);
152 }
153 }
154
155 for (auto& widget : frameView->widgetsInRenderTree()) {
156 if (!is<PluginViewBase>(*widget))
157 continue;
158 if (!downcast<PluginViewBase>(*widget).wantsWheelEvents())
159 continue;
160 auto* renderWidget = RenderWidget::find(*widget);
161 if (!renderWidget)
162 continue;
163 nonFastScrollableRegion.unite(renderWidget->absoluteBoundingBoxRect());
164 }
165
166 EventTrackingRegions eventTrackingRegions;
167
168 // FIXME: if we've already accounted for this subframe as a scrollable area, we can avoid recursing into it here.
169 for (auto* subframe = frame.tree().firstChild(); subframe; subframe = subframe->tree().nextSibling()) {
170 auto* subframeView = subframe->view();
171 if (!subframeView)
172 continue;
173
174 EventTrackingRegions subframeRegion = absoluteEventTrackingRegionsForFrame(*subframe);
175 // Map from the frame document to our document.
176 IntPoint offset = subframeView->contentsToContainingViewContents(IntPoint());
177
178 // FIXME: this translation ignores non-trival transforms on the frame.
179 subframeRegion.translate(toIntSize(offset));
180 eventTrackingRegions.unite(subframeRegion);
181 }
182
183 auto wheelHandlerRegion = frame.document()->absoluteRegionForEventTargets(frame.document()->wheelEventTargets());
184 bool wheelHandlerInFixedContent = wheelHandlerRegion.second;
185 if (wheelHandlerInFixedContent) {
186 // FIXME: need to handle position:sticky here too.
187 LayoutRect inflatedWheelHandlerBounds = frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(wheelHandlerRegion.first.bounds()));
188 wheelHandlerRegion.first.unite(enclosingIntRect(inflatedWheelHandlerBounds));
189 }
190
191 nonFastScrollableRegion.unite(wheelHandlerRegion.first);
192
193 // FIXME: If this is not the main frame, we could clip the region to the frame's bounds.
194 eventTrackingRegions.uniteSynchronousRegion(eventNames().wheelEvent, nonFastScrollableRegion);
195
196 return eventTrackingRegions;
197#endif
198}
199
200EventTrackingRegions ScrollingCoordinator::absoluteEventTrackingRegions() const
201{
202 return absoluteEventTrackingRegionsForFrame(m_page->mainFrame());
203}
204
205void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView& frameView)
206{
207 ASSERT(isMainThread());
208 ASSERT(m_page);
209
210 if (!coordinatesScrollingForFrameView(frameView))
211 return;
212
213 updateSynchronousScrollingReasons(frameView);
214}
215
216void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView& frameView)
217{
218 ASSERT(isMainThread());
219 ASSERT(m_page);
220
221 if (!coordinatesScrollingForFrameView(frameView))
222 return;
223
224 updateSynchronousScrollingReasons(frameView);
225}
226
227GraphicsLayer* ScrollingCoordinator::scrollContainerLayerForFrameView(FrameView& frameView)
228{
229 if (auto* renderView = frameView.frame().contentRenderer())
230 return renderView->compositor().scrollContainerLayer();
231 return nullptr;
232}
233
234GraphicsLayer* ScrollingCoordinator::scrolledContentsLayerForFrameView(FrameView& frameView)
235{
236 if (auto* renderView = frameView.frame().contentRenderer())
237 return renderView->compositor().scrolledContentsLayer();
238 return nullptr;
239}
240
241GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView& frameView)
242{
243#if ENABLE(RUBBER_BANDING)
244 if (auto* renderView = frameView.frame().contentRenderer())
245 return renderView->compositor().headerLayer();
246 return nullptr;
247#else
248 UNUSED_PARAM(frameView);
249 return nullptr;
250#endif
251}
252
253GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView& frameView)
254{
255#if ENABLE(RUBBER_BANDING)
256 if (auto* renderView = frameView.frame().contentRenderer())
257 return renderView->compositor().footerLayer();
258 return nullptr;
259#else
260 UNUSED_PARAM(frameView);
261 return nullptr;
262#endif
263}
264
265GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView& frameView)
266{
267 if (auto* renderView = frameView.frame().contentRenderer())
268 return renderView->compositor().fixedRootBackgroundLayer();
269 return nullptr;
270}
271
272GraphicsLayer* ScrollingCoordinator::insetClipLayerForFrameView(FrameView& frameView)
273{
274 if (auto* renderView = frameView.frame().contentRenderer())
275 return renderView->compositor().clipLayer();
276 return nullptr;
277}
278
279GraphicsLayer* ScrollingCoordinator::contentShadowLayerForFrameView(FrameView& frameView)
280{
281#if ENABLE(RUBBER_BANDING)
282 if (auto* renderView = frameView.frame().contentRenderer())
283 return renderView->compositor().layerForContentShadow();
284
285 return nullptr;
286#else
287 UNUSED_PARAM(frameView);
288 return nullptr;
289#endif
290}
291
292GraphicsLayer* ScrollingCoordinator::rootContentsLayerForFrameView(FrameView& frameView)
293{
294 if (auto* renderView = frameView.frame().contentRenderer())
295 return renderView->compositor().rootContentsLayer();
296 return nullptr;
297}
298
299void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView)
300{
301 ASSERT(isMainThread());
302 ASSERT(m_page);
303
304 if (!coordinatesScrollingForFrameView(frameView))
305 return;
306
307 frameViewLayoutUpdated(frameView);
308 updateSynchronousScrollingReasons(frameView);
309}
310
311#if PLATFORM(COCOA)
312void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase)
313{
314 ASSERT(isMainThread());
315
316 if (!m_page)
317 return;
318
319 auto* frameView = m_page->mainFrame().view();
320 if (!frameView)
321 return;
322
323 frameView->scrollAnimator().handleWheelEventPhase(phase);
324}
325#endif
326
327bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView& frameView) const
328{
329 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView.viewportConstrainedObjects();
330 if (!viewportConstrainedObjects)
331 return false;
332
333 for (auto& viewportConstrainedObject : *viewportConstrainedObjects) {
334 if (!is<RenderBoxModelObject>(*viewportConstrainedObject) || !viewportConstrainedObject->hasLayer())
335 return true;
336 auto& layer = *downcast<RenderBoxModelObject>(*viewportConstrainedObject).layer();
337 // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling.
338 if (!layer.isComposited() && layer.viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason)
339 return true;
340 }
341 return false;
342}
343
344SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons(const FrameView& frameView) const
345{
346 SynchronousScrollingReasons synchronousScrollingReasons = (SynchronousScrollingReasons)0;
347
348 if (m_forceSynchronousScrollLayerPositionUpdates)
349 synchronousScrollingReasons |= ForcedOnMainThread;
350 if (frameView.hasSlowRepaintObjects())
351 synchronousScrollingReasons |= HasSlowRepaintObjects;
352 if (hasVisibleSlowRepaintViewportConstrainedObjects(frameView))
353 synchronousScrollingReasons |= HasNonLayerViewportConstrainedObjects;
354 if (frameView.frame().mainFrame().document() && frameView.frame().document()->isImageDocument())
355 synchronousScrollingReasons |= IsImageDocument;
356
357 return synchronousScrollingReasons;
358}
359
360void ScrollingCoordinator::updateSynchronousScrollingReasons(FrameView& frameView)
361{
362 ASSERT(coordinatesScrollingForFrameView(frameView));
363 setSynchronousScrollingReasons(frameView, synchronousScrollingReasons(frameView));
364}
365
366void ScrollingCoordinator::updateSynchronousScrollingReasonsForAllFrames()
367{
368 for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
369 if (auto* frameView = frame->view()) {
370 if (coordinatesScrollingForFrameView(*frameView))
371 updateSynchronousScrollingReasons(*frameView);
372 }
373 }
374}
375
376void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool forceSynchronousScrollLayerPositionUpdates)
377{
378 if (m_forceSynchronousScrollLayerPositionUpdates == forceSynchronousScrollLayerPositionUpdates)
379 return;
380
381 m_forceSynchronousScrollLayerPositionUpdates = forceSynchronousScrollLayerPositionUpdates;
382 updateSynchronousScrollingReasonsForAllFrames();
383}
384
385bool ScrollingCoordinator::shouldUpdateScrollLayerPositionSynchronously(const FrameView& frameView) const
386{
387 if (&frameView == m_page->mainFrame().view())
388 return synchronousScrollingReasons(frameView);
389
390 return true;
391}
392
393ScrollingNodeID ScrollingCoordinator::uniqueScrollingNodeID()
394{
395 static ScrollingNodeID uniqueScrollingNodeID = 1;
396 return uniqueScrollingNodeID++;
397}
398
399String ScrollingCoordinator::scrollingStateTreeAsText(ScrollingStateTreeAsTextBehavior) const
400{
401 return String();
402}
403
404String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScrollingReasons reasons)
405{
406 StringBuilder stringBuilder;
407
408 if (reasons & ScrollingCoordinator::ForcedOnMainThread)
409 stringBuilder.appendLiteral("Forced on main thread, ");
410 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects)
411 stringBuilder.appendLiteral("Has slow repaint objects, ");
412 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers)
413 stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, ");
414 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects)
415 stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, ");
416 if (reasons & ScrollingCoordinator::IsImageDocument)
417 stringBuilder.appendLiteral("Is image document, ");
418
419 if (stringBuilder.length())
420 stringBuilder.resize(stringBuilder.length() - 2);
421 return stringBuilder.toString();
422}
423
424String ScrollingCoordinator::synchronousScrollingReasonsAsText() const
425{
426 if (auto* frameView = m_page->mainFrame().view())
427 return synchronousScrollingReasonsAsText(synchronousScrollingReasons(*frameView));
428
429 return String();
430}
431
432TextStream& operator<<(TextStream& ts, ScrollableAreaParameters scrollableAreaParameters)
433{
434 ts.dumpProperty("horizontal scroll elasticity", scrollableAreaParameters.horizontalScrollElasticity);
435 ts.dumpProperty("vertical scroll elasticity", scrollableAreaParameters.verticalScrollElasticity);
436 ts.dumpProperty("horizontal scrollbar mode", scrollableAreaParameters.horizontalScrollbarMode);
437 ts.dumpProperty("vertical scrollbar mode", scrollableAreaParameters.verticalScrollbarMode);
438
439 if (scrollableAreaParameters.hasEnabledHorizontalScrollbar)
440 ts.dumpProperty("has enabled horizontal scrollbar", scrollableAreaParameters.hasEnabledHorizontalScrollbar);
441 if (scrollableAreaParameters.hasEnabledVerticalScrollbar)
442 ts.dumpProperty("has enabled vertical scrollbar", scrollableAreaParameters.hasEnabledVerticalScrollbar);
443
444 if (scrollableAreaParameters.horizontalScrollbarHiddenByStyle)
445 ts.dumpProperty("horizontal scrollbar hidden by style", scrollableAreaParameters.horizontalScrollbarHiddenByStyle);
446 if (scrollableAreaParameters.verticalScrollbarHiddenByStyle)
447 ts.dumpProperty("vertical scrollbar hidden by style", scrollableAreaParameters.verticalScrollbarHiddenByStyle);
448
449 return ts;
450}
451
452TextStream& operator<<(TextStream& ts, ScrollingNodeType nodeType)
453{
454 switch (nodeType) {
455 case ScrollingNodeType::MainFrame:
456 ts << "main-frame-scrolling";
457 break;
458 case ScrollingNodeType::Subframe:
459 ts << "subframe-scrolling";
460 break;
461 case ScrollingNodeType::FrameHosting:
462 ts << "frame-hosting";
463 break;
464 case ScrollingNodeType::Overflow:
465 ts << "overflow-scrolling";
466 break;
467 case ScrollingNodeType::Fixed:
468 ts << "fixed";
469 break;
470 case ScrollingNodeType::Sticky:
471 ts << "sticky";
472 break;
473 case ScrollingNodeType::Positioned:
474 ts << "positioned";
475 break;
476 }
477 return ts;
478}
479
480TextStream& operator<<(TextStream& ts, ScrollingLayerPositionAction action)
481{
482 switch (action) {
483 case ScrollingLayerPositionAction::Set:
484 ts << "set";
485 break;
486 case ScrollingLayerPositionAction::SetApproximate:
487 ts << "set approximate";
488 break;
489 case ScrollingLayerPositionAction::Sync:
490 ts << "sync";
491 break;
492 }
493 return ts;
494}
495
496TextStream& operator<<(TextStream& ts, ViewportRectStability stability)
497{
498 switch (stability) {
499 case ViewportRectStability::Stable:
500 ts << "stable";
501 break;
502 case ViewportRectStability::Unstable:
503 ts << "unstable";
504 break;
505 case ViewportRectStability::ChangingObscuredInsetsInteractively:
506 ts << "changing obscured insets interactively";
507 break;
508 }
509 return ts;
510}
511
512TextStream& operator<<(TextStream& ts, ScrollType scrollType)
513{
514 switch (scrollType) {
515 case ScrollType::User: ts << "user"; break;
516 case ScrollType::Programmatic: ts << "programmatic"; break;
517 }
518 return ts;
519}
520
521} // namespace WebCore
522