nsRefreshDriver.h (21201B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Code to notify things that animate before a refresh, at an appropriate 9 * refresh rate. (Perhaps temporary, until replaced by compositor.) 10 */ 11 12 #ifndef nsRefreshDriver_h_ 13 #define nsRefreshDriver_h_ 14 15 #include "GeckoProfiler.h" // for ProfileChunkedBuffer 16 #include "LayersTypes.h" 17 #include "mozilla/Attributes.h" 18 #include "mozilla/FlushType.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/RenderingPhase.h" 21 #include "mozilla/TimeStamp.h" 22 #include "mozilla/UniquePtr.h" 23 #include "mozilla/WeakPtr.h" 24 #include "mozilla/layers/TransactionIdAllocator.h" 25 #include "nsClassHashtable.h" 26 #include "nsHashKeys.h" 27 #include "nsRefreshObservers.h" 28 #include "nsTArray.h" 29 #include "nsTHashSet.h" 30 #include "nsTObserverArray.h" 31 #include "nsThreadUtils.h" 32 33 class nsPresContext; 34 35 class imgIRequest; 36 class nsIRunnable; 37 38 struct DocumentFrameCallbacks; 39 40 namespace mozilla { 41 class AnimationEventDispatcher; 42 class PresShell; 43 class RefreshDriverTimer; 44 class Runnable; 45 class Task; 46 47 namespace dom { 48 class Document; 49 } 50 51 } // namespace mozilla 52 53 class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, 54 public nsARefreshObserver { 55 using Document = mozilla::dom::Document; 56 using TransactionId = mozilla::layers::TransactionId; 57 using LogPresShellObserver = mozilla::LogPresShellObserver; 58 59 public: 60 explicit nsRefreshDriver(nsPresContext* aPresContext); 61 ~nsRefreshDriver(); 62 63 /** 64 * Methods for testing, exposed via nsIDOMWindowUtils. See 65 * nsIDOMWindowUtils.advanceTimeAndRefresh for description. 66 */ 67 void AdvanceTimeAndRefresh(int64_t aMilliseconds); 68 void RestoreNormalRefresh(); 69 void DoTick(); 70 bool IsTestControllingRefreshesEnabled() const { 71 return mTestControllingRefreshes; 72 } 73 74 /** 75 * Return the time of the most recent refresh. This is intended to be 76 * used by callers who want to start an animation now and want to know 77 * what time to consider the start of the animation. (This helps 78 * ensure that multiple animations started during the same event off 79 * the main event loop have the same start time.) 80 */ 81 mozilla::TimeStamp MostRecentRefresh() const { return mMostRecentRefresh; } 82 83 /** 84 * Add / remove refresh observers. 85 * RemoveRefreshObserver returns true if aObserver was found. 86 * 87 * The flush type affects: 88 * + the order in which the observers are notified (lowest flush 89 * type to highest, in order registered) 90 * + (in the future) which observers are suppressed when the display 91 * doesn't require current position data or isn't currently 92 * painting, and, correspondingly, which get notified when there 93 * is a flush during such suppression 94 * and it must be FlushType::Style, or FlushType::Display. 95 * 96 * The refresh driver does NOT own a reference to these observers; 97 * they must remove themselves before they are destroyed. 98 * 99 * The observer will be called even if there is no other activity. 100 */ 101 void AddRefreshObserver(nsARefreshObserver* aObserver, 102 mozilla::FlushType aFlushType, 103 const char* aObserverDescription); 104 bool RemoveRefreshObserver(nsARefreshObserver* aObserver, 105 mozilla::FlushType aFlushType); 106 107 MOZ_CAN_RUN_SCRIPT void FlushLayoutOnPendingDocsAndFixUpFocus(); 108 109 /** 110 * Add an observer that will be called after each refresh. The caller 111 * must remove the observer before it is deleted. This does not trigger 112 * refresh driver ticks. 113 */ 114 void AddPostRefreshObserver(nsAPostRefreshObserver* aObserver); 115 void AddPostRefreshObserver(mozilla::ManagedPostRefreshObserver*) = delete; 116 void RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver); 117 void RemovePostRefreshObserver(mozilla::ManagedPostRefreshObserver*) = delete; 118 119 /** 120 * Add/Remove imgIRequest versions of observers. 121 * 122 * These are used for hooking into the refresh driver for 123 * controlling animated images. 124 * 125 * @note The refresh driver owns a reference to these listeners. 126 * 127 * @note Technically, imgIRequest objects are not nsARefreshObservers, but 128 * for controlling animated image repaint events, we subscribe the 129 * imgIRequests to the nsRefreshDriver for notification of paint events. 130 */ 131 void AddImageRequest(imgIRequest* aRequest); 132 void RemoveImageRequest(imgIRequest* aRequest); 133 void StartTimerForAnimatedImagesIfNeeded(); 134 void StopTimerForAnimatedImagesIfNeeded(); 135 136 /** 137 * Marks that we're currently in the middle of processing user input. 138 * Called by EventDispatcher when it's handling an input event. 139 */ 140 void EnterUserInputProcessing() { mUserInputProcessingCount++; } 141 void ExitUserInputProcessing() { 142 MOZ_ASSERT(mUserInputProcessingCount > 0); 143 mUserInputProcessingCount--; 144 } 145 146 /** 147 * "Early Runner" runnables will be called as the first step when refresh 148 * driver tick is triggered. Runners shouldn't keep other objects alive, 149 * since it isn't guaranteed they will ever get called. 150 */ 151 void AddEarlyRunner(nsIRunnable* aRunnable) { 152 mEarlyRunners.AppendElement(aRunnable); 153 EnsureTimerStarted(); 154 } 155 156 // Remember whether our presshell's view manager needs a flush 157 void SchedulePaint(); 158 bool IsPaintPending() const { 159 return mRenderingPhasesNeeded.contains(mozilla::RenderingPhase::Paint); 160 } 161 162 // Returns true if a paint actually occurred. 163 MOZ_CAN_RUN_SCRIPT bool PaintIfNeeded(); 164 165 /** 166 * Schedule a frame visibility update "soon", subject to the heuristics and 167 * throttling we apply to visibility updates. 168 */ 169 void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; } 170 171 /** 172 * Tell the refresh driver that it is done driving refreshes and 173 * should stop its timer and forget about its pres context. This may 174 * be called from within a refresh. 175 */ 176 void Disconnect(); 177 178 bool IsFrozen() const { return mFreezeCount > 0; } 179 180 bool IsThrottled() const { return mThrottled; } 181 182 /** 183 * Freeze the refresh driver. It should stop delivering future 184 * refreshes until thawed. Note that the number of calls to Freeze() must 185 * match the number of calls to Thaw() in order for the refresh driver to 186 * be un-frozen. 187 */ 188 void Freeze(); 189 190 /** 191 * Thaw the refresh driver. If the number of calls to Freeze() matches the 192 * number of calls to this function, the refresh driver should start 193 * delivering refreshes again. 194 */ 195 void Thaw(); 196 197 /** 198 * Throttle or unthrottle the refresh driver. This is done if the 199 * corresponding presshell is hidden or shown. 200 */ 201 void SetActivity(bool aIsActive); 202 203 /** 204 * Return the prescontext we were initialized with 205 */ 206 nsPresContext* GetPresContext() const; 207 208 void CreateVsyncRefreshTimer(); 209 210 #ifdef DEBUG 211 /** 212 * Check whether the given observer is an observer for the given flush type 213 */ 214 bool IsRefreshObserver(nsARefreshObserver* aObserver, 215 mozilla::FlushType aFlushType); 216 #endif 217 218 /** 219 * Default interval the refresh driver uses, in ms. 220 */ 221 static int32_t DefaultInterval(); 222 223 /** 224 * Returns 1.0 if a recent rate wasn't smaller than 225 * DefaultInterval(). Otherwise return rate / DefaultInterval(); 226 * So the return value is (0-1]. 227 * 228 */ 229 static double HighRateMultiplier(); 230 231 bool IsInRefresh() { return mInRefresh; } 232 233 void SetIsResizeSuppressed() { mResizeSuppressed = true; } 234 bool IsResizeSuppressed() const { return mResizeSuppressed; } 235 236 // mozilla::layers::TransactionIdAllocator 237 TransactionId GetTransactionId(bool aThrottle) override; 238 TransactionId LastTransactionId() const override; 239 void NotifyTransactionCompleted(TransactionId aTransactionId) override; 240 void RevokeTransactionId(TransactionId aTransactionId) override; 241 void ClearPendingTransactions() override; 242 void ResetInitialTransactionId(TransactionId aTransactionId) override; 243 mozilla::TimeStamp GetTransactionStart() override; 244 mozilla::VsyncId GetVsyncId() override; 245 mozilla::TimeStamp GetVsyncStart() override; 246 247 bool IsWaitingForPaint(mozilla::TimeStamp aTime); 248 void ScheduleAutoFocusFlush() { 249 ScheduleRenderingPhase(mozilla::RenderingPhase::FlushAutoFocusCandidates); 250 } 251 252 // nsARefreshObserver 253 NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { 254 return TransactionIdAllocator::AddRef(); 255 } 256 NS_IMETHOD_(MozExternalRefCountType) Release(void) override { 257 return TransactionIdAllocator::Release(); 258 } 259 virtual void WillRefresh(mozilla::TimeStamp aTime) override; 260 261 /** 262 * Compute the time when the currently active refresh driver timer 263 * will start its next tick. 264 * 265 * Expects a non-null default value that is the upper bound of the 266 * expected deadline. If the next expected deadline is later than 267 * the default value, the default value is returned. 268 * 269 * If we're animating and we have skipped paints a time in the past 270 * is returned. 271 * 272 * If aCheckType is AllVsyncListeners and we're in the parent process, 273 * which doesn't have a RefreshDriver ticking, but some other process does 274 * have, the return value is 275 * (now + refreshrate - layout.idle_period.time_limit) as an approximation 276 * when something will happen. 277 * This can be useful check when parent process tries to avoid having too 278 * long idle periods for example when it is sending input events to an 279 * active child process. 280 */ 281 enum IdleCheck { OnlyThisProcessRefreshDriver, AllVsyncListeners }; 282 static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault, 283 IdleCheck aCheckType); 284 285 /** 286 * It returns the expected timestamp of the next tick or nothing if the next 287 * tick is missed. 288 */ 289 static mozilla::Maybe<mozilla::TimeStamp> GetNextTickHint(); 290 291 static bool IsRegularRateTimerTicking(); 292 293 static void DispatchIdleTaskAfterTickUnlessExists(mozilla::Task* aTask); 294 static void CancelIdleTask(mozilla::Task* aTask); 295 296 void InitializeTimer() { 297 MOZ_ASSERT(!mActiveTimer); 298 EnsureTimerStarted(); 299 } 300 301 bool HasPendingTick() const { return mActiveTimer; } 302 303 void ScheduleRenderingPhases(mozilla::RenderingPhases aPhases) { 304 mRenderingPhasesNeeded += aPhases; 305 EnsureTimerStarted(); 306 } 307 308 void ScheduleRenderingPhase(mozilla::RenderingPhase aPhase) { 309 ScheduleRenderingPhases({aPhase}); 310 } 311 312 void EnsureIntersectionObservationsUpdateHappens() { 313 // This is enough to make sure that UpdateIntersectionObservations runs at 314 // least once. This is presumably the intent of step 5 in [1]: 315 // 316 // Schedule an iteration of the event loop in the root's browsing 317 // context. 318 // 319 // Though the wording of it is not quite clear to me... 320 // 321 // [1]: 322 // https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-observe 323 ScheduleRenderingPhase( 324 mozilla::RenderingPhase::UpdateIntersectionObservations); 325 } 326 327 void EnsureViewTransitionOperationsHappen() { 328 ScheduleRenderingPhase(mozilla::RenderingPhase::ViewTransitionOperations); 329 } 330 331 void EnsureAnimationUpdate() { 332 ScheduleRenderingPhase( 333 mozilla::RenderingPhase::UpdateAnimationsAndSendEvents); 334 } 335 336 void ScheduleMediaQueryListenerUpdate() { 337 ScheduleRenderingPhase( 338 mozilla::RenderingPhase::EvaluateMediaQueriesAndReportChanges); 339 } 340 341 // Register a composition payload that will be forwarded to the layer manager 342 // if the current or upcoming refresh tick does a paint. 343 // If no paint happens, the payload is discarded. 344 // Should only be called on root refresh drivers. 345 void RegisterCompositionPayload( 346 const mozilla::layers::CompositionPayload& aPayload); 347 348 enum class TickReasons : uint32_t { 349 None = 0, 350 HasObservers = 1 << 0, 351 HasImageAnimations = 1 << 1, 352 HasPendingRenderingSteps = 1 << 2, 353 RootNeedsMoreTicksForUserInput = 1 << 3, 354 }; 355 356 void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext); 357 void FlushForceNotifyContentfulPaintPresContext(); 358 359 // Mark that we've just run a tick from vsync, used to throttle 'extra' 360 // paints to one per vsync (see CanDoExtraTick). 361 void FinishedVsyncTick() { mAttemptedExtraTickSinceLastVsync = false; } 362 363 private: 364 using RequestTable = nsTHashSet<RefPtr<imgIRequest>>; 365 struct ImageStartData { 366 ImageStartData() = default; 367 368 mozilla::Maybe<mozilla::TimeStamp> mStartTime; 369 RequestTable mEntries; 370 }; 371 using ImageStartTable = nsClassHashtable<nsUint32HashKey, ImageStartData>; 372 373 struct ObserverData { 374 nsARefreshObserver* mObserver; 375 const char* mDescription; 376 mozilla::TimeStamp mRegisterTime; 377 mozilla::MarkerInnerWindowId mInnerWindowId; 378 mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> mCause; 379 mozilla::FlushType mFlushType; 380 381 bool operator==(nsARefreshObserver* aObserver) const { 382 return mObserver == aObserver; 383 } 384 operator RefPtr<nsARefreshObserver>() { return mObserver; } 385 }; 386 using ObserverArray = nsTObserverArray<ObserverData>; 387 void RunFullscreenSteps(); 388 389 MOZ_CAN_RUN_SCRIPT 390 void RunVideoAndFrameRequestCallbacks(mozilla::TimeStamp aNowTime); 391 MOZ_CAN_RUN_SCRIPT 392 void RunVideoFrameCallbacks(const nsTArray<RefPtr<Document>>&, 393 mozilla::TimeStamp aNowTime); 394 MOZ_CAN_RUN_SCRIPT 395 void RunFrameRequestCallbacks(const nsTArray<RefPtr<Document>>&, 396 mozilla::TimeStamp aNowTime); 397 void UpdateRemoteFrameEffects(); 398 void UpdateRelevancyOfContentVisibilityAutoFrames(); 399 MOZ_CAN_RUN_SCRIPT void PerformPendingViewTransitionOperations(); 400 void MaybeIncreaseMeasuredTicksSinceLoading(); 401 void EvaluateMediaQueriesAndReportChanges(); 402 403 enum class IsExtraTick { 404 No, 405 Yes, 406 }; 407 408 // Helper for Tick, to call WillRefresh(aNowTime) on each entry in 409 // mObservers[aIdx] and then potentially do some additional post-notification 410 // work that's associated with the FlushType corresponding to aIdx. 411 // 412 // Returns true on success, or false if one of our calls has destroyed our 413 // pres context (in which case our callsite Tick() should immediately bail). 414 MOZ_CAN_RUN_SCRIPT 415 bool TickObserverArray(uint32_t aIdx, mozilla::TimeStamp aNowTime); 416 417 MOZ_CAN_RUN_SCRIPT_BOUNDARY 418 void Tick(mozilla::VsyncId aId, mozilla::TimeStamp aNowTime, 419 IsExtraTick aIsExtraTick = IsExtraTick::No); 420 421 enum EnsureTimerStartedFlags { 422 eNone = 0, 423 eForceAdjustTimer = 1 << 0, 424 eAllowTimeToGoBackwards = 1 << 1, 425 }; 426 void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone); 427 void StopTimer(); 428 429 void UpdateThrottledState(); 430 431 bool HasObservers() const; 432 void AppendObserverDescriptionsToString(nsACString& aStr) const; 433 // Note: This should only be called in the dtor of nsRefreshDriver. 434 uint32_t ObserverCount() const; 435 bool ComputeHasImageAnimations() const; 436 bool ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint(); 437 bool ShouldKeepTimerRunningAfterPageLoad(); 438 ObserverArray& ArrayFor(mozilla::FlushType aFlushType); 439 // Trigger a refresh immediately, if haven't been disconnected or frozen. 440 void DoRefresh(); 441 442 // Starts pending image animations, and refreshes ongoing animations. 443 void UpdateAnimatedImages(mozilla::TimeStamp aPreviousRefresh, 444 mozilla::TimeStamp aNowTime); 445 446 bool HasReasonsToTick() const { 447 return GetReasonsToTick() != TickReasons::None; 448 } 449 TickReasons GetReasonsToTick() const; 450 void AppendTickReasonsToString(TickReasons aReasons, nsACString& aStr) const; 451 452 double GetRegularTimerInterval() const; 453 static double GetThrottledTimerInterval(); 454 455 static mozilla::TimeDuration GetMinRecomputeVisibilityInterval(); 456 457 void FinishedWaitingForTransaction(); 458 459 /** 460 * Returns true if we didn't tick on the most recent vsync, but we think 461 * we could run one now instead in order to reduce latency. 462 */ 463 bool CanDoCatchUpTick(); 464 /** 465 * Returns true if we think it's possible to run an repeat tick (between 466 * vsyncs) to hopefully replace the original tick's paint on the compositor. 467 * We allow this sometimes for tick requests coming for user input handling 468 * to reduce latency. 469 */ 470 bool CanDoExtraTick(); 471 472 bool AtPendingTransactionLimit() { 473 return mPendingTransactions.Length() == 2; 474 } 475 bool TooManyPendingTransactions() { 476 return mPendingTransactions.Length() >= 2; 477 } 478 479 mozilla::RefreshDriverTimer* ChooseTimer(); 480 mozilla::RefreshDriverTimer* mActiveTimer; 481 RefPtr<mozilla::RefreshDriverTimer> mOwnTimer; 482 mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> mRefreshTimerStartedCause; 483 484 // nsPresContext passed in constructor and unset in Disconnect. 485 mozilla::WeakPtr<nsPresContext> mPresContext; 486 487 RefPtr<nsRefreshDriver> mRootRefresh; 488 489 // The most recently allocated transaction id. 490 TransactionId mNextTransactionId; 491 AutoTArray<TransactionId, 3> mPendingTransactions; 492 493 uint32_t mFreezeCount; 494 uint32_t mUserInputProcessingCount = 0; 495 496 // How long we wait between ticks for throttled (which generally means 497 // non-visible) documents registered with a non-throttled refresh driver. 498 const mozilla::TimeDuration mThrottledFrameRequestInterval; 499 500 // How long we wait, at a minimum, before recomputing approximate frame 501 // visibility information. This is a minimum because, regardless of this 502 // interval, we only recompute visibility when we've seen a layout or style 503 // flush since the last time we did it. 504 const mozilla::TimeDuration mMinRecomputeVisibilityInterval; 505 506 mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> mPaintCause; 507 508 bool mThrottled : 1; 509 bool mNeedToRecomputeVisibility : 1; 510 bool mTestControllingRefreshes : 1; 511 bool mInRefresh : 1; 512 513 // True if the refresh driver is suspended waiting for transaction 514 // id's to be returned and shouldn't do any work during Tick(). 515 bool mWaitingForTransaction : 1; 516 // True if Tick() was skipped because of mWaitingForTransaction and 517 // we should schedule a new Tick immediately when resumed instead 518 // of waiting until the next interval. 519 bool mSkippedPaints : 1; 520 521 // True if view managers should delay any resize request until the 522 // next tick by the refresh driver. This flag will be reset at the 523 // start of every tick. 524 bool mResizeSuppressed : 1; 525 526 // True if we may need to run any frame callback. 527 bool mNeedToRunFrameRequestCallbacks : 1; 528 529 // True if we're currently within the scope of Tick() handling a normal 530 // (timer-driven) tick. 531 bool mInNormalTick : 1; 532 533 // True if we attempted an extra tick (see CanDoExtraTick) since the last 534 // vsync and thus shouldn't allow another. 535 bool mAttemptedExtraTickSinceLastVsync : 1; 536 537 bool mHasExceededAfterLoadTickPeriod : 1; 538 539 bool mHasImageAnimations : 1; 540 541 bool mHasStartedTimerAtLeastOnce : 1; 542 543 mozilla::TimeStamp mMostRecentRefresh; 544 mozilla::TimeStamp mTickStart; 545 mozilla::VsyncId mTickVsyncId; 546 mozilla::TimeStamp mTickVsyncTime; 547 mozilla::TimeStamp mNextThrottledFrameRequestTick; 548 mozilla::TimeStamp mNextRecomputeVisibilityTick; 549 mozilla::TimeStamp mBeforeFirstContentfulPaintTimerRunningLimit; 550 551 // The "Update the rendering" phases we need to run on our documents. 552 mozilla::EnumSet<mozilla::RenderingPhase, uint16_t> mRenderingPhasesNeeded; 553 554 // Runs a single update the rendering phase, at once, rather than filtering 555 // the docs as per spec. 556 // 557 // TODO(emilio): This should be removed, ideally. 558 template <typename Callback> 559 MOZ_CAN_RUN_SCRIPT void RunRenderingPhaseLegacy(mozilla::RenderingPhase, 560 Callback&&); 561 562 using DocFilter = bool (*)(const Document&); 563 564 // Runs a single update the rendering phase with the callback called for each 565 // document, as per spec. 566 template <typename Callback> 567 MOZ_CAN_RUN_SCRIPT void RunRenderingPhase(mozilla::RenderingPhase, Callback&&, 568 DocFilter = nullptr); 569 570 // separate arrays for each flush type we support 571 ObserverArray mObservers[3]; 572 nsTArray<mozilla::layers::CompositionPayload> mCompositionPayloads; 573 RequestTable mRequests; 574 ImageStartTable mStartTable; 575 AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners; 576 nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers; 577 578 // nsPresContexts which `NotifyContentfulPaint` have been called, 579 // however the corresponding paint doesn't come from a regular 580 // rendering steps(aka tick). 581 // 582 // For these nsPresContexts, we invoke 583 // `FlushForceNotifyContentfulPaintPresContext` in the next tick 584 // to force notify contentful paint, regardless whether the tick paints 585 // or not. 586 nsTArray<mozilla::WeakPtr<nsPresContext>> 587 mForceNotifyContentfulPaintPresContexts; 588 589 void BeginRefreshingImages(RequestTable& aEntries, 590 mozilla::TimeStamp aDesired); 591 592 friend class mozilla::RefreshDriverTimer; 593 594 static void Shutdown(); 595 }; 596 597 #endif /* !defined(nsRefreshDriver_h_) */