1/* 2 * Copyright (C) 2012, 2014 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#import "config.h" 27#import "ScrollingTreeFrameScrollingNodeMac.h" 28 29#if ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC) 30 31#import "FrameView.h" 32#import "NSScrollerImpDetails.h" 33#import "PlatformWheelEvent.h" 34#import "ScrollingCoordinator.h" 35#import "ScrollingTree.h" 36#import "ScrollingStateTree.h" 37#import "Settings.h" 38#import "TileController.h" 39#import "WebLayer.h" 40 41#import <QuartzCore/QuartzCore.h> 42#import <wtf/CurrentTime.h> 43#import <wtf/Deque.h> 44#import <wtf/text/StringBuilder.h> 45#import <wtf/text/CString.h> 46 47namespace WebCore { 48 49static void logThreadedScrollingMode(unsigned synchronousScrollingReasons); 50static void logWheelEventHandlerCountChanged(unsigned); 51 52 53PassRefPtr<ScrollingTreeFrameScrollingNode> ScrollingTreeFrameScrollingNodeMac::create(ScrollingTree& scrollingTree, ScrollingNodeID nodeID) 54{ 55 return adoptRef(new ScrollingTreeFrameScrollingNodeMac(scrollingTree, nodeID)); 56} 57 58ScrollingTreeFrameScrollingNodeMac::ScrollingTreeFrameScrollingNodeMac(ScrollingTree& scrollingTree, ScrollingNodeID nodeID) 59 : ScrollingTreeFrameScrollingNode(scrollingTree, nodeID) 60 , m_scrollElasticityController(this) 61 , m_verticalScrollbarPainter(0) 62 , m_horizontalScrollbarPainter(0) 63 , m_lastScrollHadUnfilledPixels(false) 64{ 65} 66 67ScrollingTreeFrameScrollingNodeMac::~ScrollingTreeFrameScrollingNodeMac() 68{ 69 if (m_snapRubberbandTimer) 70 CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get()); 71} 72 73void ScrollingTreeFrameScrollingNodeMac::updateBeforeChildren(const ScrollingStateNode& stateNode) 74{ 75 ScrollingTreeFrameScrollingNode::updateBeforeChildren(stateNode); 76 const auto& scrollingStateNode = toScrollingStateFrameScrollingNode(stateNode); 77 78 if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer)) 79 m_scrollLayer = scrollingStateNode.layer(); 80 81 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ScrolledContentsLayer)) 82 m_scrolledContentsLayer = scrollingStateNode.scrolledContentsLayer(); 83 84 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::CounterScrollingLayer)) 85 m_counterScrollingLayer = scrollingStateNode.counterScrollingLayer(); 86 87 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::InsetClipLayer)) 88 m_insetClipLayer = scrollingStateNode.insetClipLayer(); 89 90 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ContentShadowLayer)) 91 m_contentShadowLayer = scrollingStateNode.contentShadowLayer(); 92 93 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::HeaderLayer)) 94 m_headerLayer = scrollingStateNode.headerLayer(); 95 96 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::FooterLayer)) 97 m_footerLayer = scrollingStateNode.footerLayer(); 98 99 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::PainterForScrollbar)) { 100 m_verticalScrollbarPainter = scrollingStateNode.verticalScrollbarPainter(); 101 m_horizontalScrollbarPainter = scrollingStateNode.horizontalScrollbarPainter(); 102 } 103 104 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::ReasonsForSynchronousScrolling)) { 105 if (shouldUpdateScrollLayerPositionSynchronously()) { 106 // We're transitioning to the slow "update scroll layer position on the main thread" mode. 107 // Initialize the probable main thread scroll position with the current scroll layer position. 108 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition)) 109 m_probableMainThreadScrollPosition = scrollingStateNode.requestedScrollPosition(); 110 else { 111 CGPoint scrollLayerPosition = m_scrollLayer.get().position; 112 m_probableMainThreadScrollPosition = FloatPoint(-scrollLayerPosition.x, -scrollLayerPosition.y); 113 } 114 } 115 116 if (scrollingTree().scrollingPerformanceLoggingEnabled()) 117 logThreadedScrollingMode(synchronousScrollingReasons()); 118 } 119 120 if (scrollingStateNode.hasChangedProperty(ScrollingStateFrameScrollingNode::WheelEventHandlerCount)) { 121 if (scrollingTree().scrollingPerformanceLoggingEnabled()) 122 logWheelEventHandlerCountChanged(scrollingStateNode.wheelEventHandlerCount()); 123 } 124} 125 126void ScrollingTreeFrameScrollingNodeMac::updateAfterChildren(const ScrollingStateNode& stateNode) 127{ 128 ScrollingTreeFrameScrollingNode::updateAfterChildren(stateNode); 129 130 const auto& scrollingStateNode = toScrollingStateScrollingNode(stateNode); 131 132 // Update the scroll position after child nodes have been updated, because they need to have updated their constraints before any scrolling happens. 133 if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition)) 134 setScrollPosition(scrollingStateNode.requestedScrollPosition()); 135 136 if (scrollingStateNode.hasChangedProperty(ScrollingStateNode::ScrollLayer) 137 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize) 138 || scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize)) 139 updateMainFramePinState(scrollPosition()); 140} 141 142void ScrollingTreeFrameScrollingNodeMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent) 143{ 144 if (!canHaveScrollbars()) 145 return; 146 147 if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseBegan) { 148 [m_verticalScrollbarPainter setUsePresentationValue:YES]; 149 [m_horizontalScrollbarPainter setUsePresentationValue:YES]; 150 } 151 if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded || wheelEvent.momentumPhase() == PlatformWheelEventPhaseCancelled) { 152 [m_verticalScrollbarPainter setUsePresentationValue:NO]; 153 [m_horizontalScrollbarPainter setUsePresentationValue:NO]; 154 } 155 156 m_scrollElasticityController.handleWheelEvent(wheelEvent); 157 scrollingTree().setOrClearLatchedNode(wheelEvent, scrollingNodeID()); 158 scrollingTree().handleWheelEventPhase(wheelEvent.phase()); 159} 160 161bool ScrollingTreeFrameScrollingNodeMac::allowsHorizontalStretching() 162{ 163 switch (horizontalScrollElasticity()) { 164 case ScrollElasticityAutomatic: 165 return hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar(); 166 case ScrollElasticityNone: 167 return false; 168 case ScrollElasticityAllowed: 169 return true; 170 } 171 172 ASSERT_NOT_REACHED(); 173 return false; 174} 175 176bool ScrollingTreeFrameScrollingNodeMac::allowsVerticalStretching() 177{ 178 switch (verticalScrollElasticity()) { 179 case ScrollElasticityAutomatic: 180 return hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar(); 181 case ScrollElasticityNone: 182 return false; 183 case ScrollElasticityAllowed: 184 return true; 185 } 186 187 ASSERT_NOT_REACHED(); 188 return false; 189} 190 191IntSize ScrollingTreeFrameScrollingNodeMac::stretchAmount() 192{ 193 IntSize stretch; 194 195 if (scrollPosition().y() < minimumScrollPosition().y()) 196 stretch.setHeight(scrollPosition().y() - minimumScrollPosition().y()); 197 else if (scrollPosition().y() > maximumScrollPosition().y()) 198 stretch.setHeight(scrollPosition().y() - maximumScrollPosition().y()); 199 200 if (scrollPosition().x() < minimumScrollPosition().x()) 201 stretch.setWidth(scrollPosition().x() - minimumScrollPosition().x()); 202 else if (scrollPosition().x() > maximumScrollPosition().x()) 203 stretch.setWidth(scrollPosition().x() - maximumScrollPosition().x()); 204 205 if (scrollingTree().rootNode() == this) { 206 if (stretch.isZero()) 207 scrollingTree().setMainFrameIsRubberBanding(false); 208 else 209 scrollingTree().setMainFrameIsRubberBanding(true); 210 } 211 212 return stretch; 213} 214 215bool ScrollingTreeFrameScrollingNodeMac::pinnedInDirection(const FloatSize& delta) 216{ 217 FloatSize limitDelta; 218 219 if (fabsf(delta.height()) >= fabsf(delta.width())) { 220 if (delta.height() < 0) { 221 // We are trying to scroll up. Make sure we are not pinned to the top. 222 limitDelta.setHeight(scrollPosition().y() - minimumScrollPosition().y()); 223 } else { 224 // We are trying to scroll down. Make sure we are not pinned to the bottom. 225 limitDelta.setHeight(maximumScrollPosition().y() - scrollPosition().y()); 226 } 227 } else if (delta.width()) { 228 if (delta.width() < 0) { 229 // We are trying to scroll left. Make sure we are not pinned to the left. 230 limitDelta.setWidth(scrollPosition().x() - minimumScrollPosition().x()); 231 } else { 232 // We are trying to scroll right. Make sure we are not pinned to the right. 233 limitDelta.setWidth(maximumScrollPosition().x() - scrollPosition().x()); 234 } 235 } 236 237 if ((delta.width() || delta.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1)) 238 return true; 239 240 return false; 241} 242 243bool ScrollingTreeFrameScrollingNodeMac::canScrollHorizontally() 244{ 245 return hasEnabledHorizontalScrollbar(); 246} 247 248bool ScrollingTreeFrameScrollingNodeMac::canScrollVertically() 249{ 250 return hasEnabledVerticalScrollbar(); 251} 252 253bool ScrollingTreeFrameScrollingNodeMac::shouldRubberBandInDirection(ScrollDirection) 254{ 255 return true; 256} 257 258IntPoint ScrollingTreeFrameScrollingNodeMac::absoluteScrollPosition() 259{ 260 return roundedIntPoint(scrollPosition()); 261} 262 263void ScrollingTreeFrameScrollingNodeMac::immediateScrollBy(const FloatSize& offset) 264{ 265 scrollBy(offset); 266} 267 268void ScrollingTreeFrameScrollingNodeMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& offset) 269{ 270 scrollByWithoutContentEdgeConstraints(offset); 271} 272 273void ScrollingTreeFrameScrollingNodeMac::startSnapRubberbandTimer() 274{ 275 ASSERT(!m_snapRubberbandTimer); 276 277 CFTimeInterval timerInterval = 1.0 / 60.0; 278 279 m_snapRubberbandTimer = adoptCF(CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + timerInterval, timerInterval, 0, 0, ^(CFRunLoopTimerRef) { 280 m_scrollElasticityController.snapRubberBandTimerFired(); 281 })); 282 CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_snapRubberbandTimer.get(), kCFRunLoopDefaultMode); 283} 284 285void ScrollingTreeFrameScrollingNodeMac::stopSnapRubberbandTimer() 286{ 287 if (!m_snapRubberbandTimer) 288 return; 289 290 scrollingTree().setMainFrameIsRubberBanding(false); 291 292 // Since the rubberband timer has stopped, totalContentsSizeForRubberBand can be synchronized with totalContentsSize. 293 setTotalContentsSizeForRubberBand(totalContentsSize()); 294 295 CFRunLoopTimerInvalidate(m_snapRubberbandTimer.get()); 296 m_snapRubberbandTimer = nullptr; 297} 298 299void ScrollingTreeFrameScrollingNodeMac::adjustScrollPositionToBoundsIfNecessary() 300{ 301 FloatPoint currentScrollPosition = absoluteScrollPosition(); 302 FloatPoint minPosition = minimumScrollPosition(); 303 FloatPoint maxPosition = maximumScrollPosition(); 304 305 float nearestXWithinBounds = std::max(std::min(currentScrollPosition.x(), maxPosition.x()), minPosition.x()); 306 float nearestYWithinBounds = std::max(std::min(currentScrollPosition.y(), maxPosition.y()), minPosition.y()); 307 308 FloatPoint nearestPointWithinBounds(nearestXWithinBounds, nearestYWithinBounds); 309 immediateScrollBy(nearestPointWithinBounds - currentScrollPosition); 310} 311 312FloatPoint ScrollingTreeFrameScrollingNodeMac::scrollPosition() const 313{ 314 if (shouldUpdateScrollLayerPositionSynchronously()) 315 return m_probableMainThreadScrollPosition; 316 317 CGPoint scrollLayerPosition = m_scrollLayer.get().position; 318 return FloatPoint(-scrollLayerPosition.x + scrollOrigin().x(), -scrollLayerPosition.y + scrollOrigin().y()); 319} 320 321void ScrollingTreeFrameScrollingNodeMac::setScrollPosition(const FloatPoint& scrollPosition) 322{ 323 // Scroll deltas can be non-integral with some input devices, so scrollPosition may not be integral. 324 // FIXME: when we support half-pixel scroll positions on Retina displays, this will need to round to half pixels. 325 FloatPoint roundedPosition(roundf(scrollPosition.x()), roundf(scrollPosition.y())); 326 327 ScrollingTreeFrameScrollingNode::setScrollPosition(roundedPosition); 328 329 if (scrollingTree().scrollingPerformanceLoggingEnabled()) 330 logExposedUnfilledArea(); 331} 332 333void ScrollingTreeFrameScrollingNodeMac::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition) 334{ 335 updateMainFramePinState(scrollPosition); 336 337 if (shouldUpdateScrollLayerPositionSynchronously()) { 338 m_probableMainThreadScrollPosition = scrollPosition; 339 scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, SetScrollingLayerPosition); 340 return; 341 } 342 343 setScrollLayerPosition(scrollPosition); 344 scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition); 345} 346 347void ScrollingTreeFrameScrollingNodeMac::setScrollLayerPosition(const FloatPoint& position) 348{ 349 ASSERT(!shouldUpdateScrollLayerPositionSynchronously()); 350 m_scrollLayer.get().position = CGPointMake(-position.x() + scrollOrigin().x(), -position.y() + scrollOrigin().y()); 351 352 ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements(); 353 FloatPoint scrollOffset = position - toFloatSize(scrollOrigin()); 354 FloatRect viewportRect(FloatPoint(), scrollableAreaSize()); 355 356 FloatSize scrollOffsetForFixedChildren = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), 357 roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), frameScaleFactor(), false, behaviorForFixed, headerHeight(), footerHeight()); 358 359 if (m_counterScrollingLayer) 360 m_counterScrollingLayer.get().position = FloatPoint(scrollOffsetForFixedChildren); 361 362 float topContentInset = this->topContentInset(); 363 if (m_insetClipLayer && m_scrolledContentsLayer && topContentInset) { 364 m_insetClipLayer.get().position = FloatPoint(0, FrameView::yPositionForInsetClipLayer(position, topContentInset)); 365 m_scrolledContentsLayer.get().position = FloatPoint(m_scrolledContentsLayer.get().position.x, 366 FrameView::yPositionForRootContentLayer(position, topContentInset, headerHeight())); 367 if (m_contentShadowLayer) 368 m_contentShadowLayer.get().position = m_scrolledContentsLayer.get().position; 369 } 370 371 if (m_headerLayer || m_footerLayer) { 372 // Generally the banners should have the same horizontal-position computation as a fixed element. However, 373 // the banners are not affected by the frameScaleFactor(), so if there is currently a non-1 frameScaleFactor() 374 // then we should recompute scrollOffsetForFixedChildren for the banner with a scale factor of 1. 375 float horizontalScrollOffsetForBanner = scrollOffsetForFixedChildren.width(); 376 if (frameScaleFactor() != 1) 377 horizontalScrollOffsetForBanner = FrameView::scrollOffsetForFixedPosition(enclosingLayoutRect(viewportRect), roundedLayoutSize(totalContentsSize()), roundedLayoutPoint(scrollOffset), scrollOrigin(), 1, false, behaviorForFixed, headerHeight(), footerHeight()).width(); 378 379 if (m_headerLayer) 380 m_headerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, FrameView::yPositionForHeaderLayer(position, topContentInset)); 381 382 if (m_footerLayer) { 383 m_footerLayer.get().position = FloatPoint(horizontalScrollOffsetForBanner, 384 FrameView::yPositionForFooterLayer(position, topContentInset, totalContentsSize().height(), footerHeight())); 385 } 386 } 387 388 if (m_verticalScrollbarPainter || m_horizontalScrollbarPainter) { 389 [CATransaction begin]; 390 [CATransaction lock]; 391 392 if ([m_verticalScrollbarPainter shouldUsePresentationValue]) { 393 float presentationValue; 394 float overhangAmount; 395 ScrollableArea::computeScrollbarValueAndOverhang(position.y(), totalContentsSize().height(), viewportRect.height(), presentationValue, overhangAmount); 396 [m_verticalScrollbarPainter setPresentationValue:presentationValue]; 397 } 398 399 if ([m_horizontalScrollbarPainter shouldUsePresentationValue]) { 400 float presentationValue; 401 float overhangAmount; 402 ScrollableArea::computeScrollbarValueAndOverhang(position.x(), totalContentsSize().width(), viewportRect.width(), presentationValue, overhangAmount); 403 [m_horizontalScrollbarPainter setPresentationValue:presentationValue]; 404 } 405 [CATransaction unlock]; 406 [CATransaction commit]; 407 } 408 409 if (!m_children) 410 return; 411 412 viewportRect.setLocation(FloatPoint() + scrollOffsetForFixedChildren); 413 414 for (auto& child : *m_children) 415 child->updateLayersAfterAncestorChange(*this, viewportRect, FloatSize()); 416} 417 418void ScrollingTreeFrameScrollingNodeMac::updateLayersAfterViewportChange(const FloatRect&, double) 419{ 420 ASSERT_NOT_REACHED(); 421} 422 423FloatPoint ScrollingTreeFrameScrollingNodeMac::minimumScrollPosition() const 424{ 425 FloatPoint position; 426 427 if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToBottom) 428 position.setY(maximumScrollPosition().y()); 429 430 return position; 431} 432 433FloatPoint ScrollingTreeFrameScrollingNodeMac::maximumScrollPosition() const 434{ 435 FloatPoint position(totalContentsSizeForRubberBand() - scrollableAreaSize()); 436 position = position.expandedTo(FloatPoint()); 437 438 if (scrollingTree().rootNode() == this && scrollingTree().scrollPinningBehavior() == PinToTop) 439 position.setY(minimumScrollPosition().y()); 440 441 return position; 442} 443 444void ScrollingTreeFrameScrollingNodeMac::updateMainFramePinState(const FloatPoint& scrollPosition) 445{ 446 bool pinnedToTheLeft = scrollPosition.x() <= minimumScrollPosition().x(); 447 bool pinnedToTheRight = scrollPosition.x() >= maximumScrollPosition().x(); 448 bool pinnedToTheTop = scrollPosition.y() <= minimumScrollPosition().y(); 449 bool pinnedToTheBottom = scrollPosition.y() >= maximumScrollPosition().y(); 450 451 scrollingTree().setMainFramePinState(pinnedToTheLeft, pinnedToTheRight, pinnedToTheTop, pinnedToTheBottom); 452} 453 454void ScrollingTreeFrameScrollingNodeMac::logExposedUnfilledArea() 455{ 456 Region paintedVisibleTiles; 457 458 Deque<CALayer*> layerQueue; 459 layerQueue.append(m_scrollLayer.get()); 460 PlatformLayerList tiles; 461 462 while (!layerQueue.isEmpty() && tiles.isEmpty()) { 463 CALayer* layer = layerQueue.takeFirst(); 464 NSArray* sublayers = [[layer sublayers] copy]; 465 466 // If this layer is the parent of a tile, it is the parent of all of the tiles and nothing else. 467 if ([[[sublayers objectAtIndex:0] valueForKey:@"isTile"] boolValue]) { 468 for (CALayer* sublayer in sublayers) 469 tiles.append(sublayer); 470 } else { 471 for (CALayer* sublayer in sublayers) 472 layerQueue.append(sublayer); 473 } 474 475 [sublayers release]; 476 } 477 478 FloatPoint scrollPosition = this->scrollPosition(); 479 FloatRect viewPortRect(FloatPoint(), scrollableAreaSize()); 480 unsigned unfilledArea = TileController::blankPixelCountForTiles(tiles, viewPortRect, IntPoint(-scrollPosition.x(), -scrollPosition.y())); 481 482 if (unfilledArea || m_lastScrollHadUnfilledPixels) 483 WTFLogAlways("SCROLLING: Exposed tileless area. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), unfilledArea); 484 485 m_lastScrollHadUnfilledPixels = unfilledArea; 486} 487 488static void logThreadedScrollingMode(unsigned synchronousScrollingReasons) 489{ 490 if (synchronousScrollingReasons) { 491 StringBuilder reasonsDescription; 492 493 if (synchronousScrollingReasons & ScrollingCoordinator::ForcedOnMainThread) 494 reasonsDescription.append("forced,"); 495 if (synchronousScrollingReasons & ScrollingCoordinator::HasSlowRepaintObjects) 496 reasonsDescription.append("slow-repaint objects,"); 497 if (synchronousScrollingReasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers) 498 reasonsDescription.append("viewport-constrained objects,"); 499 if (synchronousScrollingReasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects) 500 reasonsDescription.append("non-layer viewport-constrained objects,"); 501 if (synchronousScrollingReasons & ScrollingCoordinator::IsImageDocument) 502 reasonsDescription.append("image document,"); 503 504 // Strip the trailing comma. 505 String reasonsDescriptionTrimmed = reasonsDescription.toString().left(reasonsDescription.length() - 1); 506 507 WTFLogAlways("SCROLLING: Switching to main-thread scrolling mode. Time: %f Reason(s): %s\n", WTF::monotonicallyIncreasingTime(), reasonsDescriptionTrimmed.ascii().data()); 508 } else 509 WTFLogAlways("SCROLLING: Switching to threaded scrolling mode. Time: %f\n", WTF::monotonicallyIncreasingTime()); 510} 511 512void logWheelEventHandlerCountChanged(unsigned count) 513{ 514 WTFLogAlways("SCROLLING: Wheel event handler count changed. Time: %f Count: %u\n", WTF::monotonicallyIncreasingTime(), count); 515} 516 517} // namespace WebCore 518 519#endif // ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC) 520