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 "FrameView.h" 32#include "GraphicsLayer.h" 33#include "IntRect.h" 34#include "MainFrame.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 "ScrollAnimator.h" 42#include "Settings.h" 43#include <wtf/MainThread.h> 44#include <wtf/text/StringBuilder.h> 45 46#if USE(COORDINATED_GRAPHICS) 47#include "ScrollingCoordinatorCoordinatedGraphics.h" 48#endif 49 50#if ENABLE(WEB_REPLAY) 51#include "ReplayController.h" 52#include <replay/InputCursor.h> 53#endif 54 55namespace WebCore { 56 57#if !PLATFORM(COCOA) 58PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) 59{ 60#if USE(COORDINATED_GRAPHICS) 61 return adoptRef(new ScrollingCoordinatorCoordinatedGraphics(page)); 62#endif 63 64 return adoptRef(new ScrollingCoordinator(page)); 65} 66#endif 67 68ScrollingCoordinator::ScrollingCoordinator(Page* page) 69 : m_page(page) 70 , m_forceSynchronousScrollLayerPositionUpdates(false) 71{ 72} 73 74ScrollingCoordinator::~ScrollingCoordinator() 75{ 76 ASSERT(!m_page); 77} 78 79void ScrollingCoordinator::pageDestroyed() 80{ 81 ASSERT(m_page); 82 m_page = 0; 83} 84 85bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const 86{ 87 ASSERT(isMainThread()); 88 ASSERT(m_page); 89 90 if (!frameView->frame().isMainFrame() && !m_page->settings().scrollingTreeIncludesFrames()) 91 return false; 92 93 RenderView* renderView = m_page->mainFrame().contentRenderer(); 94 if (!renderView) 95 return false; 96 return renderView->usesCompositing(); 97} 98 99Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const 100{ 101#if PLATFORM(IOS) 102 // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers. 103 ASSERT(frame->isMainFrame()); 104 UNUSED_PARAM(frameLocation); 105 106 Document* document = frame->document(); 107 if (!document) 108 return Region(); 109 110 Vector<IntRect> touchRects; 111 document->getTouchRects(touchRects); 112 113 Region touchRegion; 114 for (const auto& rect : touchRects) 115 touchRegion.unite(rect); 116 117 return touchRegion; 118#else 119 Region nonFastScrollableRegion; 120 FrameView* frameView = frame->view(); 121 if (!frameView) 122 return nonFastScrollableRegion; 123 124 IntPoint offset = frameLocation; 125 offset.moveBy(frameView->frameRect().location()); 126 offset.move(0, frameView->topContentInset()); 127 128 if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) { 129 for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { 130 ScrollableArea* scrollableArea = *it; 131 // Composited scrollable areas can be scrolled off the main thread. 132 if (scrollableArea->usesCompositedScrolling()) 133 continue; 134 IntRect box = scrollableArea->scrollableAreaBoundingBox(); 135 box.moveBy(offset); 136 nonFastScrollableRegion.unite(box); 137 } 138 } 139 140 for (const auto& child : frameView->children()) { 141 if (!child->isPluginViewBase()) 142 continue; 143 PluginViewBase* pluginViewBase = toPluginViewBase(child.get()); 144 if (pluginViewBase->wantsWheelEvents()) 145 nonFastScrollableRegion.unite(pluginViewBase->frameRect()); 146 } 147 148 for (Frame* subframe = frame->tree().firstChild(); subframe; subframe = subframe->tree().nextSibling()) 149 nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subframe, offset)); 150 151 return nonFastScrollableRegion; 152#endif 153} 154 155unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount() 156{ 157 unsigned wheelEventHandlerCount = 0; 158 159 for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { 160 if (frame->document()) 161 wheelEventHandlerCount += frame->document()->wheelEventHandlerCount(); 162 } 163 164 return wheelEventHandlerCount; 165} 166 167void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView) 168{ 169 ASSERT(isMainThread()); 170 ASSERT(m_page); 171 172 recomputeWheelEventHandlerCountForFrameView(frameView); 173} 174 175void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView) 176{ 177 ASSERT(isMainThread()); 178 ASSERT(m_page); 179 180 if (!coordinatesScrollingForFrameView(frameView)) 181 return; 182 183 updateSynchronousScrollingReasons(frameView); 184} 185 186void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView) 187{ 188 ASSERT(isMainThread()); 189 ASSERT(m_page); 190 191 if (!coordinatesScrollingForFrameView(frameView)) 192 return; 193 194 updateSynchronousScrollingReasons(frameView); 195} 196 197GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea* scrollableArea) 198{ 199 return scrollableArea->layerForScrolling(); 200} 201 202GraphicsLayer* ScrollingCoordinator::horizontalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) 203{ 204 return scrollableArea->layerForHorizontalScrollbar(); 205} 206 207GraphicsLayer* ScrollingCoordinator::verticalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) 208{ 209 return scrollableArea->layerForVerticalScrollbar(); 210} 211 212GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView) 213{ 214 if (RenderView* renderView = frameView->frame().contentRenderer()) 215 return renderView->compositor().scrollLayer(); 216 return nullptr; 217} 218 219GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView* frameView) 220{ 221#if ENABLE(RUBBER_BANDING) 222 if (RenderView* renderView = frameView->frame().contentRenderer()) 223 return renderView->compositor().headerLayer(); 224 return nullptr; 225#else 226 UNUSED_PARAM(frameView); 227 return nullptr; 228#endif 229} 230 231GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView* frameView) 232{ 233#if ENABLE(RUBBER_BANDING) 234 if (RenderView* renderView = frameView->frame().contentRenderer()) 235 return renderView->compositor().footerLayer(); 236 return nullptr; 237#else 238 UNUSED_PARAM(frameView); 239 return nullptr; 240#endif 241} 242 243GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView* frameView) 244{ 245 if (RenderView* renderView = frameView->frame().contentRenderer()) 246 return renderView->compositor().fixedRootBackgroundLayer(); 247 return nullptr; 248} 249 250GraphicsLayer* ScrollingCoordinator::insetClipLayerForFrameView(FrameView* frameView) 251{ 252 if (RenderView* renderView = frameView->frame().contentRenderer()) 253 return renderView->compositor().clipLayer(); 254 return nullptr; 255} 256 257GraphicsLayer* ScrollingCoordinator::contentShadowLayerForFrameView(FrameView* frameView) 258{ 259#if ENABLE(RUBBER_BANDING) 260 if (RenderView* renderView = frameView->frame().contentRenderer()) 261 return renderView->compositor().layerForContentShadow(); 262 263 return nullptr; 264#else 265 UNUSED_PARAM(frameView); 266 return nullptr; 267#endif 268} 269 270GraphicsLayer* ScrollingCoordinator::rootContentLayerForFrameView(FrameView* frameView) 271{ 272 if (RenderView* renderView = frameView->frame().contentRenderer()) 273 return renderView->compositor().rootContentLayer(); 274 return nullptr; 275} 276 277void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) 278{ 279 ASSERT(isMainThread()); 280 ASSERT(m_page); 281 282 if (!coordinatesScrollingForFrameView(frameView)) 283 return; 284 285 frameViewLayoutUpdated(frameView); 286 recomputeWheelEventHandlerCountForFrameView(frameView); 287 updateSynchronousScrollingReasons(frameView); 288} 289 290#if PLATFORM(COCOA) 291void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase) 292{ 293 ASSERT(isMainThread()); 294 295 if (!m_page) 296 return; 297 298 FrameView* frameView = m_page->mainFrame().view(); 299 if (!frameView) 300 return; 301 302 frameView->scrollAnimator()->handleWheelEventPhase(phase); 303} 304#endif 305 306bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const 307{ 308 const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects(); 309 if (!viewportConstrainedObjects) 310 return false; 311 312 for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) { 313 RenderObject* viewportConstrainedObject = *it; 314 if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer()) 315 return true; 316 RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer(); 317 // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling. 318 if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason) 319 return true; 320 } 321 return false; 322} 323 324SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons(FrameView* frameView) const 325{ 326 if (!frameView) 327 return static_cast<SynchronousScrollingReasons>(0); 328 329 SynchronousScrollingReasons synchronousScrollingReasons = (SynchronousScrollingReasons)0; 330 331 if (m_forceSynchronousScrollLayerPositionUpdates) 332 synchronousScrollingReasons |= ForcedOnMainThread; 333#if ENABLE(WEB_REPLAY) 334 InputCursor& cursor = m_page->replayController().activeInputCursor(); 335 if (cursor.isCapturing() || cursor.isReplaying()) 336 synchronousScrollingReasons |= ForcedOnMainThread; 337#endif 338 if (frameView->hasSlowRepaintObjects()) 339 synchronousScrollingReasons |= HasSlowRepaintObjects; 340 if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects()) 341 synchronousScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers; 342 if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) 343 synchronousScrollingReasons |= HasNonLayerViewportConstrainedObjects; 344 if (frameView->frame().mainFrame().document() && frameView->frame().document()->isImageDocument()) 345 synchronousScrollingReasons |= IsImageDocument; 346 347 return synchronousScrollingReasons; 348} 349 350void ScrollingCoordinator::updateSynchronousScrollingReasons(FrameView* frameView) 351{ 352 // FIXME: Once we support async scrolling of iframes, we'll have to track the synchronous scrolling 353 // reasons per frame (maybe on scrolling tree nodes). 354 if (!frameView->frame().isMainFrame()) 355 return; 356 357 setSynchronousScrollingReasons(synchronousScrollingReasons(frameView)); 358} 359 360void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool forceSynchronousScrollLayerPositionUpdates) 361{ 362 if (m_forceSynchronousScrollLayerPositionUpdates == forceSynchronousScrollLayerPositionUpdates) 363 return; 364 365 m_forceSynchronousScrollLayerPositionUpdates = forceSynchronousScrollLayerPositionUpdates; 366 updateSynchronousScrollingReasons(m_page->mainFrame().view()); 367} 368 369bool ScrollingCoordinator::shouldUpdateScrollLayerPositionSynchronously() const 370{ 371 return synchronousScrollingReasons(m_page->mainFrame().view()); 372} 373 374#if ENABLE(WEB_REPLAY) 375void ScrollingCoordinator::replaySessionStateDidChange() 376{ 377 // FIXME: Once we support async scrolling of iframes, this should go through all subframes. 378 updateSynchronousScrollingReasons(m_page->mainFrame().view()); 379} 380#endif 381 382ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID() 383{ 384 static ScrollingNodeID uniqueScrollLayerID = 1; 385 return uniqueScrollLayerID++; 386} 387 388String ScrollingCoordinator::scrollingStateTreeAsText() const 389{ 390 return String(); 391} 392 393String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScrollingReasons reasons) 394{ 395 StringBuilder stringBuilder; 396 397 if (reasons & ScrollingCoordinator::ForcedOnMainThread) 398 stringBuilder.append("Forced on main thread, "); 399 if (reasons & ScrollingCoordinator::HasSlowRepaintObjects) 400 stringBuilder.append("Has slow repaint objects, "); 401 if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers) 402 stringBuilder.append("Has viewport constrained objects without supporting fixed layers, "); 403 if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects) 404 stringBuilder.append("Has non-layer viewport-constrained objects, "); 405 if (reasons & ScrollingCoordinator::IsImageDocument) 406 stringBuilder.append("Is image document, "); 407 408 if (stringBuilder.length()) 409 stringBuilder.resize(stringBuilder.length() - 2); 410 return stringBuilder.toString(); 411} 412 413String ScrollingCoordinator::synchronousScrollingReasonsAsText() const 414{ 415 return synchronousScrollingReasonsAsText(synchronousScrollingReasons(m_page->mainFrame().view())); 416} 417 418} // namespace WebCore 419