Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
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 : * Chrome and each tab have their own RefreshDriver, which in turn
12 : * hooks into one of a few global timer based on RefreshDriverTimer,
13 : * defined below. There are two main global timers -- one for active
14 : * animations, and one for inactive ones. These are implemented as
15 : * subclasses of RefreshDriverTimer; see below for a description of
16 : * their implementations. In the future, additional timer types may
17 : * implement things like blocking on vsync.
18 : */
19 :
20 : #ifdef XP_WIN
21 : #include <windows.h>
22 : // mmsystem isn't part of WIN32_LEAN_AND_MEAN, so we have
23 : // to manually include it
24 : #include <mmsystem.h>
25 : #include "WinUtils.h"
26 : #endif
27 :
28 : #include "mozilla/ArrayUtils.h"
29 : #include "mozilla/AutoRestore.h"
30 : #include "mozilla/IntegerRange.h"
31 : #include "nsHostObjectProtocolHandler.h"
32 : #include "nsRefreshDriver.h"
33 : #include "nsITimer.h"
34 : #include "nsLayoutUtils.h"
35 : #include "nsPresContext.h"
36 : #include "nsComponentManagerUtils.h"
37 : #include "mozilla/Logging.h"
38 : #include "nsAutoPtr.h"
39 : #include "nsIDocument.h"
40 : #include "nsIXULRuntime.h"
41 : #include "jsapi.h"
42 : #include "nsContentUtils.h"
43 : #include "mozilla/PendingAnimationTracker.h"
44 : #include "mozilla/Preferences.h"
45 : #include "nsViewManager.h"
46 : #include "GeckoProfiler.h"
47 : #include "nsNPAPIPluginInstance.h"
48 : #include "mozilla/dom/Performance.h"
49 : #include "mozilla/dom/Selection.h"
50 : #include "mozilla/dom/WindowBinding.h"
51 : #include "mozilla/GeckoRestyleManager.h"
52 : #include "mozilla/RestyleManager.h"
53 : #include "mozilla/RestyleManagerInlines.h"
54 : #include "Layers.h"
55 : #include "imgIContainer.h"
56 : #include "mozilla/dom/ScriptSettings.h"
57 : #include "nsDocShell.h"
58 : #include "nsISimpleEnumerator.h"
59 : #include "nsJSEnvironment.h"
60 : #include "mozilla/Telemetry.h"
61 : #include "gfxPrefs.h"
62 : #include "BackgroundChild.h"
63 : #include "mozilla/ipc/PBackgroundChild.h"
64 : #include "nsIIPCBackgroundChildCreateCallback.h"
65 : #include "mozilla/layout/VsyncChild.h"
66 : #include "VsyncSource.h"
67 : #include "mozilla/VsyncDispatcher.h"
68 : #include "nsThreadUtils.h"
69 : #include "mozilla/Unused.h"
70 : #include "mozilla/TimelineConsumers.h"
71 : #include "nsAnimationManager.h"
72 : #include "nsIDOMEvent.h"
73 : #include "nsDisplayList.h"
74 :
75 : #ifdef MOZ_XUL
76 : #include "nsXULPopupManager.h"
77 : #endif
78 :
79 : using namespace mozilla;
80 : using namespace mozilla::widget;
81 : using namespace mozilla::ipc;
82 : using namespace mozilla::layout;
83 :
84 : static mozilla::LazyLogModule sRefreshDriverLog("nsRefreshDriver");
85 : #define LOG(...) MOZ_LOG(sRefreshDriverLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
86 :
87 : #define DEFAULT_THROTTLED_FRAME_RATE 1
88 : #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
89 : #define DEFAULT_NOTIFY_INTERSECTION_OBSERVERS_INTERVAL_MS 100
90 : // after 10 minutes, stop firing off inactive timers
91 : #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
92 :
93 : // The number of seconds spent skipping frames because we are waiting for the compositor
94 : // before logging.
95 : #if defined(MOZ_ASAN)
96 : # define REFRESH_WAIT_WARNING 5
97 : #elif defined(DEBUG) && !defined(MOZ_VALGRIND)
98 : # define REFRESH_WAIT_WARNING 5
99 : #elif defined(DEBUG) && defined(MOZ_VALGRIND)
100 : # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
101 : #elif defined(MOZ_VALGRIND)
102 : # define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
103 : #else
104 : # define REFRESH_WAIT_WARNING 1
105 : #endif
106 :
107 : namespace {
108 : // `true` if we are currently in jank-critical mode.
109 : //
110 : // In jank-critical mode, any iteration of the event loop that takes
111 : // more than 16ms to compute will cause an ongoing animation to miss
112 : // frames.
113 : //
114 : // For simplicity, the current implementation assumes that we are in
115 : // jank-critical mode if and only if at least one vsync driver has
116 : // at least one observer.
117 : static uint64_t sActiveVsyncTimers = 0;
118 :
119 : // The latest value of process-wide jank levels.
120 : //
121 : // For each i, sJankLevels[i] counts the number of times delivery of
122 : // vsync to the main thread has been delayed by at least 2^i ms. Use
123 : // GetJankLevels to grab a copy of this array.
124 : uint64_t sJankLevels[12];
125 :
126 : // The number outstanding nsRefreshDrivers (that have been created but not
127 : // disconnected). When this reaches zero we will call
128 : // nsRefreshDriver::Shutdown.
129 : static uint32_t sRefreshDriverCount = 0;
130 : }
131 :
132 : namespace mozilla {
133 :
134 : /*
135 : * The base class for all global refresh driver timers. It takes care
136 : * of managing the list of refresh drivers attached to them and
137 : * provides interfaces for querying/setting the rate and actually
138 : * running a timer 'Tick'. Subclasses must implement StartTimer(),
139 : * StopTimer(), and ScheduleNextTick() -- the first two just
140 : * start/stop whatever timer mechanism is in use, and ScheduleNextTick
141 : * is called at the start of the Tick() implementation to set a time
142 : * for the next tick.
143 : */
144 : class RefreshDriverTimer {
145 : public:
146 2 : RefreshDriverTimer()
147 2 : : mLastFireEpoch(0)
148 2 : , mLastFireSkipped(false)
149 : {
150 2 : }
151 :
152 0 : virtual ~RefreshDriverTimer()
153 0 : {
154 0 : MOZ_ASSERT(mContentRefreshDrivers.Length() == 0, "Should have removed all content refresh drivers from here by now!");
155 0 : MOZ_ASSERT(mRootRefreshDrivers.Length() == 0, "Should have removed all root refresh drivers from here by now!");
156 0 : }
157 :
158 8 : virtual void AddRefreshDriver(nsRefreshDriver* aDriver)
159 : {
160 8 : LOG("[%p] AddRefreshDriver %p", this, aDriver);
161 :
162 8 : bool startTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
163 8 : if (IsRootRefreshDriver(aDriver)) {
164 8 : NS_ASSERTION(!mRootRefreshDrivers.Contains(aDriver), "Adding a duplicate root refresh driver!");
165 8 : mRootRefreshDrivers.AppendElement(aDriver);
166 : } else {
167 0 : NS_ASSERTION(!mContentRefreshDrivers.Contains(aDriver), "Adding a duplicate content refresh driver!");
168 0 : mContentRefreshDrivers.AppendElement(aDriver);
169 : }
170 :
171 8 : if (startTimer) {
172 6 : StartTimer();
173 : }
174 8 : }
175 :
176 8 : virtual void RemoveRefreshDriver(nsRefreshDriver* aDriver)
177 : {
178 8 : LOG("[%p] RemoveRefreshDriver %p", this, aDriver);
179 :
180 8 : if (IsRootRefreshDriver(aDriver)) {
181 8 : NS_ASSERTION(mRootRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a refresh driver that's not in the root refresh list!");
182 8 : mRootRefreshDrivers.RemoveElement(aDriver);
183 : } else {
184 0 : nsPresContext* pc = aDriver->GetPresContext();
185 0 : nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
186 : // During PresContext shutdown, we can't accurately detect
187 : // if a root refresh driver exists or not. Therefore, we have to
188 : // search and find out which list this driver exists in.
189 0 : if (!rootContext) {
190 0 : if (mRootRefreshDrivers.Contains(aDriver)) {
191 0 : mRootRefreshDrivers.RemoveElement(aDriver);
192 : } else {
193 0 : NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver),
194 : "RemoveRefreshDriver without a display root for a driver that is not in the content refresh list");
195 0 : mContentRefreshDrivers.RemoveElement(aDriver);
196 : }
197 : } else {
198 0 : NS_ASSERTION(mContentRefreshDrivers.Contains(aDriver), "RemoveRefreshDriver for a driver that is not in the content refresh list");
199 0 : mContentRefreshDrivers.RemoveElement(aDriver);
200 : }
201 : }
202 :
203 8 : bool stopTimer = mContentRefreshDrivers.IsEmpty() && mRootRefreshDrivers.IsEmpty();
204 8 : if (stopTimer) {
205 6 : StopTimer();
206 : }
207 8 : }
208 :
209 45 : TimeStamp MostRecentRefresh() const { return mLastFireTime; }
210 4 : int64_t MostRecentRefreshEpochTime() const { return mLastFireEpoch; }
211 :
212 0 : void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer)
213 : {
214 0 : MOZ_ASSERT(NS_IsMainThread());
215 :
216 0 : for (nsRefreshDriver* driver : mContentRefreshDrivers) {
217 0 : aNewTimer->AddRefreshDriver(driver);
218 0 : driver->mActiveTimer = aNewTimer;
219 : }
220 0 : mContentRefreshDrivers.Clear();
221 :
222 0 : for (nsRefreshDriver* driver : mRootRefreshDrivers) {
223 0 : aNewTimer->AddRefreshDriver(driver);
224 0 : driver->mActiveTimer = aNewTimer;
225 : }
226 0 : mRootRefreshDrivers.Clear();
227 :
228 0 : aNewTimer->mLastFireEpoch = mLastFireEpoch;
229 0 : aNewTimer->mLastFireTime = mLastFireTime;
230 0 : }
231 :
232 : virtual TimeDuration GetTimerRate() = 0;
233 :
234 : bool LastTickSkippedAnyPaints() const
235 : {
236 : return mLastFireSkipped;
237 : }
238 :
239 41 : TimeStamp GetIdleDeadlineHint(TimeStamp aDefault)
240 : {
241 41 : MOZ_ASSERT(NS_IsMainThread());
242 :
243 41 : TimeStamp mostRecentRefresh = MostRecentRefresh();
244 41 : TimeDuration refreshRate = GetTimerRate();
245 41 : TimeStamp idleEnd = mostRecentRefresh + refreshRate;
246 :
247 82 : if (idleEnd +
248 82 : refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
249 82 : TimeStamp::Now()) {
250 15 : return aDefault;
251 : }
252 :
253 52 : idleEnd = idleEnd - TimeDuration::FromMilliseconds(
254 52 : nsLayoutUtils::IdlePeriodDeadlineLimit());
255 26 : return idleEnd < aDefault ? idleEnd : aDefault;
256 : }
257 :
258 : protected:
259 : virtual void StartTimer() = 0;
260 : virtual void StopTimer() = 0;
261 : virtual void ScheduleNextTick(TimeStamp aNowTime) = 0;
262 :
263 16 : bool IsRootRefreshDriver(nsRefreshDriver* aDriver)
264 : {
265 16 : nsPresContext* pc = aDriver->GetPresContext();
266 16 : nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
267 16 : if (!rootContext) {
268 0 : return false;
269 : }
270 :
271 16 : return aDriver == rootContext->RefreshDriver();
272 : }
273 :
274 : /*
275 : * Actually runs a tick, poking all the attached RefreshDrivers.
276 : * Grabs the "now" time via JS_Now and TimeStamp::Now().
277 : */
278 0 : void Tick()
279 : {
280 0 : int64_t jsnow = JS_Now();
281 0 : TimeStamp now = TimeStamp::Now();
282 0 : Tick(jsnow, now);
283 0 : }
284 :
285 142 : void TickRefreshDrivers(int64_t aJsNow, TimeStamp aNow, nsTArray<RefPtr<nsRefreshDriver>>& aDrivers)
286 : {
287 142 : if (aDrivers.IsEmpty()) {
288 71 : return;
289 : }
290 :
291 142 : nsTArray<RefPtr<nsRefreshDriver> > drivers(aDrivers);
292 144 : for (nsRefreshDriver* driver : drivers) {
293 : // don't poke this driver if it's in test mode
294 73 : if (driver->IsTestControllingRefreshesEnabled()) {
295 0 : continue;
296 : }
297 :
298 73 : TickDriver(driver, aJsNow, aNow);
299 :
300 73 : mLastFireSkipped = mLastFireSkipped || driver->mSkippedPaints;
301 : }
302 : }
303 :
304 : /*
305 : * Tick the refresh drivers based on the given timestamp.
306 : */
307 71 : void Tick(int64_t jsnow, TimeStamp now)
308 : {
309 71 : ScheduleNextTick(now);
310 :
311 71 : mLastFireEpoch = jsnow;
312 71 : mLastFireTime = now;
313 71 : mLastFireSkipped = false;
314 :
315 71 : LOG("[%p] ticking drivers...", this);
316 : // RD is short for RefreshDriver
317 142 : AutoProfilerTracing tracing("Paint", "RefreshDriverTick");
318 :
319 71 : TickRefreshDrivers(jsnow, now, mContentRefreshDrivers);
320 71 : TickRefreshDrivers(jsnow, now, mRootRefreshDrivers);
321 :
322 71 : LOG("[%p] done.", this);
323 71 : }
324 :
325 73 : static void TickDriver(nsRefreshDriver* driver, int64_t jsnow, TimeStamp now)
326 : {
327 73 : LOG(">> TickDriver: %p (jsnow: %" PRId64 ")", driver, jsnow);
328 73 : driver->Tick(jsnow, now);
329 73 : }
330 :
331 : int64_t mLastFireEpoch;
332 : bool mLastFireSkipped;
333 : TimeStamp mLastFireTime;
334 : TimeStamp mTargetTime;
335 :
336 : nsTArray<RefPtr<nsRefreshDriver> > mContentRefreshDrivers;
337 : nsTArray<RefPtr<nsRefreshDriver> > mRootRefreshDrivers;
338 :
339 : // useful callback for nsITimer-based derived classes, here
340 : // bacause of c++ protected shenanigans
341 0 : static void TimerTick(nsITimer* aTimer, void* aClosure)
342 : {
343 0 : RefreshDriverTimer *timer = static_cast<RefreshDriverTimer*>(aClosure);
344 0 : timer->Tick();
345 0 : }
346 : };
347 :
348 : /*
349 : * A RefreshDriverTimer that uses a nsITimer as the underlying timer. Note that
350 : * this is a ONE_SHOT timer, not a repeating one! Subclasses are expected to
351 : * implement ScheduleNextTick and intelligently calculate the next time to tick,
352 : * and to reset mTimer. Using a repeating nsITimer gets us into a lot of pain
353 : * with its attempt at intelligent slack removal and such, so we don't do it.
354 : */
355 : class SimpleTimerBasedRefreshDriverTimer :
356 : public RefreshDriverTimer
357 : {
358 : public:
359 : /*
360 : * aRate -- the delay, in milliseconds, requested between timer firings
361 : */
362 0 : explicit SimpleTimerBasedRefreshDriverTimer(double aRate)
363 0 : {
364 0 : SetRate(aRate);
365 0 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
366 0 : }
367 :
368 0 : ~SimpleTimerBasedRefreshDriverTimer() override
369 0 : {
370 0 : StopTimer();
371 0 : }
372 :
373 : // will take effect at next timer tick
374 0 : virtual void SetRate(double aNewRate)
375 : {
376 0 : mRateMilliseconds = aNewRate;
377 0 : mRateDuration = TimeDuration::FromMilliseconds(mRateMilliseconds);
378 0 : }
379 :
380 : double GetRate() const
381 : {
382 : return mRateMilliseconds;
383 : }
384 :
385 0 : TimeDuration GetTimerRate() override
386 : {
387 0 : return mRateDuration;
388 : }
389 :
390 : protected:
391 :
392 0 : void StartTimer() override
393 : {
394 : // pretend we just fired, and we schedule the next tick normally
395 0 : mLastFireEpoch = JS_Now();
396 0 : mLastFireTime = TimeStamp::Now();
397 :
398 0 : mTargetTime = mLastFireTime + mRateDuration;
399 :
400 0 : uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
401 0 : mTimer->InitWithNamedFuncCallback(
402 : TimerTick,
403 : this,
404 : delay,
405 : nsITimer::TYPE_ONE_SHOT,
406 0 : "SimpleTimerBasedRefreshDriverTimer::StartTimer");
407 0 : }
408 :
409 0 : void StopTimer() override
410 : {
411 0 : mTimer->Cancel();
412 0 : }
413 :
414 : double mRateMilliseconds;
415 : TimeDuration mRateDuration;
416 : RefPtr<nsITimer> mTimer;
417 : };
418 :
419 : /*
420 : * A refresh driver that listens to vsync events and ticks the refresh driver
421 : * on vsync intervals. We throttle the refresh driver if we get too many
422 : * vsync events and wait to catch up again.
423 : */
424 : class VsyncRefreshDriverTimer : public RefreshDriverTimer
425 : {
426 : public:
427 1 : VsyncRefreshDriverTimer()
428 1 : : mVsyncChild(nullptr)
429 : {
430 1 : MOZ_ASSERT(XRE_IsParentProcess());
431 1 : MOZ_ASSERT(NS_IsMainThread());
432 1 : mVsyncObserver = new RefreshDriverVsyncObserver(this);
433 2 : RefPtr<mozilla::gfx::VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
434 1 : MOZ_ALWAYS_TRUE(mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher());
435 1 : mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
436 1 : mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate();
437 1 : }
438 :
439 1 : explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild)
440 1 : : mVsyncChild(aVsyncChild)
441 : {
442 1 : MOZ_ASSERT(!XRE_IsParentProcess());
443 1 : MOZ_ASSERT(NS_IsMainThread());
444 1 : MOZ_ASSERT(mVsyncChild);
445 1 : mVsyncObserver = new RefreshDriverVsyncObserver(this);
446 1 : mVsyncChild->SetVsyncObserver(mVsyncObserver);
447 1 : mVsyncRate = mVsyncChild->GetVsyncRate();
448 1 : }
449 :
450 41 : TimeDuration GetTimerRate() override
451 : {
452 41 : if (mVsyncRate != TimeDuration::Forever()) {
453 40 : return mVsyncRate;
454 : }
455 :
456 1 : if (mVsyncChild) {
457 : // VsyncChild::VsyncRate() is a simple getter for the cached
458 : // hardware vsync rate. We depend on that
459 : // VsyncChild::GetVsyncRate() being called in the constructor
460 : // will result in a response with the actual vsync rate sooner
461 : // or later. Until that happens VsyncChild::VsyncRate() returns
462 : // TimeDuration::Forever() and we have to guess below.
463 1 : mVsyncRate = mVsyncChild->VsyncRate();
464 : }
465 :
466 : // If hardware queries fail / are unsupported, we have to just guess.
467 2 : return mVsyncRate != TimeDuration::Forever()
468 : ? mVsyncRate
469 1 : : TimeDuration::FromMilliseconds(1000.0 / 60.0);
470 : }
471 :
472 : private:
473 : // Since VsyncObservers are refCounted, but the RefreshDriverTimer are
474 : // explicitly shutdown. We create an inner class that has the VsyncObserver
475 : // and is shutdown when the RefreshDriverTimer is deleted. The alternative is
476 : // to (a) make all RefreshDriverTimer RefCounted or (b) use different
477 : // VsyncObserver types.
478 : class RefreshDriverVsyncObserver final : public VsyncObserver
479 : {
480 : public:
481 2 : explicit RefreshDriverVsyncObserver(VsyncRefreshDriverTimer* aVsyncRefreshDriverTimer)
482 2 : : mVsyncRefreshDriverTimer(aVsyncRefreshDriverTimer)
483 : , mRefreshTickLock("RefreshTickLock")
484 : , mRecentVsync(TimeStamp::Now())
485 : , mLastChildTick(TimeStamp::Now())
486 : , mVsyncRate(TimeDuration::Forever())
487 2 : , mProcessedVsync(true)
488 : {
489 2 : MOZ_ASSERT(NS_IsMainThread());
490 2 : }
491 :
492 : class ParentProcessVsyncNotifier final: public Runnable,
493 : public nsIRunnablePriority
494 : {
495 : public:
496 67 : ParentProcessVsyncNotifier(RefreshDriverVsyncObserver* aObserver,
497 : TimeStamp aVsyncTimestamp)
498 67 : : Runnable("VsyncRefreshDriverTimer::RefreshDriverVsyncObserver::"
499 : "ParentProcessVsyncNotifier")
500 : , mObserver(aObserver)
501 67 : , mVsyncTimestamp(aVsyncTimestamp)
502 : {
503 67 : }
504 :
505 : NS_DECL_ISUPPORTS_INHERITED
506 :
507 67 : NS_IMETHOD Run() override
508 : {
509 67 : MOZ_ASSERT(NS_IsMainThread());
510 : static bool sCacheInitialized = false;
511 : static bool sHighPriorityPrefValue = false;
512 67 : if (!sCacheInitialized) {
513 1 : sCacheInitialized = true;
514 1 : Preferences::AddBoolVarCache(&sHighPriorityPrefValue,
515 : "vsync.parentProcess.highPriority",
516 2 : mozilla::BrowserTabsRemoteAutostart());
517 : }
518 67 : sHighPriorityEnabled = sHighPriorityPrefValue;
519 :
520 67 : mObserver->TickRefreshDriver(mVsyncTimestamp);
521 67 : return NS_OK;
522 : }
523 :
524 67 : NS_IMETHOD GetPriority(uint32_t* aPriority) override
525 : {
526 67 : *aPriority =
527 67 : sHighPriorityEnabled ? nsIRunnablePriority::PRIORITY_HIGH :
528 67 : nsIRunnablePriority::PRIORITY_NORMAL;
529 67 : return NS_OK;
530 : }
531 :
532 : private:
533 201 : ~ParentProcessVsyncNotifier() {}
534 : RefPtr<RefreshDriverVsyncObserver> mObserver;
535 : TimeStamp mVsyncTimestamp;
536 : static mozilla::Atomic<bool> sHighPriorityEnabled;
537 : };
538 :
539 511 : bool NotifyVsync(TimeStamp aVsyncTimestamp) override
540 : {
541 511 : if (!NS_IsMainThread()) {
542 507 : MOZ_ASSERT(XRE_IsParentProcess());
543 : // Compress vsync notifications such that only 1 may run at a time
544 : // This is so that we don't flood the refresh driver with vsync messages
545 : // if the main thread is blocked for long periods of time
546 : { // scope lock
547 574 : MonitorAutoLock lock(mRefreshTickLock);
548 507 : mRecentVsync = aVsyncTimestamp;
549 507 : if (!mProcessedVsync) {
550 440 : return true;
551 : }
552 67 : mProcessedVsync = false;
553 : }
554 :
555 : nsCOMPtr<nsIRunnable> vsyncEvent =
556 201 : new ParentProcessVsyncNotifier(this, aVsyncTimestamp);
557 67 : NS_DispatchToMainThread(vsyncEvent);
558 : } else {
559 4 : mRecentVsync = aVsyncTimestamp;
560 4 : if (!mBlockUntil.IsNull() && mBlockUntil > aVsyncTimestamp) {
561 0 : if (mProcessedVsync) {
562 : // Re-post vsync update as a normal priority runnable. This way
563 : // runnables already in normal priority queue get processed.
564 0 : mProcessedVsync = false;
565 : nsCOMPtr<nsIRunnable> vsyncEvent =
566 0 : NewRunnableMethod<>(
567 : "RefreshDriverVsyncObserver::NormalPriorityNotify",
568 0 : this, &RefreshDriverVsyncObserver::NormalPriorityNotify);
569 0 : NS_DispatchToMainThread(vsyncEvent);
570 : }
571 :
572 0 : return true;
573 : }
574 :
575 4 : TickRefreshDriver(aVsyncTimestamp);
576 : }
577 :
578 71 : return true;
579 : }
580 :
581 0 : void Shutdown()
582 : {
583 0 : MOZ_ASSERT(NS_IsMainThread());
584 0 : mVsyncRefreshDriverTimer = nullptr;
585 0 : }
586 :
587 2 : void OnTimerStart()
588 : {
589 2 : if (!XRE_IsParentProcess()) {
590 2 : mLastChildTick = TimeStamp::Now();
591 : }
592 2 : }
593 :
594 0 : void NormalPriorityNotify()
595 : {
596 0 : if (mLastProcessedTickInChildProcess.IsNull() ||
597 0 : mRecentVsync > mLastProcessedTickInChildProcess) {
598 : // mBlockUntil is for high priority vsync notifications only.
599 0 : mBlockUntil = TimeStamp();
600 0 : TickRefreshDriver(mRecentVsync);
601 : }
602 :
603 0 : mProcessedVsync = true;
604 0 : }
605 :
606 : private:
607 0 : ~RefreshDriverVsyncObserver() = default;
608 :
609 71 : void RecordTelemetryProbes(TimeStamp aVsyncTimestamp)
610 : {
611 71 : MOZ_ASSERT(NS_IsMainThread());
612 : #ifndef ANDROID /* bug 1142079 */
613 71 : if (XRE_IsParentProcess()) {
614 67 : TimeDuration vsyncLatency = TimeStamp::Now() - aVsyncTimestamp;
615 67 : uint32_t sample = (uint32_t)vsyncLatency.ToMilliseconds();
616 : Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS,
617 67 : sample);
618 : Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
619 67 : sample);
620 67 : RecordJank(sample);
621 4 : } else if (mVsyncRate != TimeDuration::Forever()) {
622 3 : TimeDuration contentDelay = (TimeStamp::Now() - mLastChildTick) - mVsyncRate;
623 3 : if (contentDelay.ToMilliseconds() < 0 ){
624 : // Vsyncs are noisy and some can come at a rate quicker than
625 : // the reported hardware rate. In those cases, consider that we have 0 delay.
626 1 : contentDelay = TimeDuration::FromMilliseconds(0);
627 : }
628 3 : uint32_t sample = (uint32_t)contentDelay.ToMilliseconds();
629 : Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_CONTENT_FRAME_DELAY_MS,
630 3 : sample);
631 : Telemetry::Accumulate(Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
632 3 : sample);
633 3 : RecordJank(sample);
634 : } else {
635 : // Request the vsync rate from the parent process. Might be a few vsyncs
636 : // until the parent responds.
637 1 : if (mVsyncRefreshDriverTimer) {
638 1 : mVsyncRate = mVsyncRefreshDriverTimer->mVsyncChild->GetVsyncRate();
639 : }
640 : }
641 : #endif
642 71 : }
643 :
644 70 : void RecordJank(uint32_t aJankMS)
645 : {
646 70 : uint32_t duration = 1 /* ms */;
647 1073 : for (size_t i = 0;
648 381 : i < mozilla::ArrayLength(sJankLevels) && duration < aJankMS;
649 311 : ++i, duration *= 2) {
650 311 : sJankLevels[i]++;
651 : }
652 70 : }
653 :
654 71 : void TickRefreshDriver(TimeStamp aVsyncTimestamp)
655 : {
656 71 : MOZ_ASSERT(NS_IsMainThread());
657 :
658 71 : RecordTelemetryProbes(aVsyncTimestamp);
659 71 : if (XRE_IsParentProcess()) {
660 134 : MonitorAutoLock lock(mRefreshTickLock);
661 67 : aVsyncTimestamp = mRecentVsync;
662 67 : mProcessedVsync = true;
663 : } else {
664 :
665 4 : mLastChildTick = TimeStamp::Now();
666 4 : mLastProcessedTickInChildProcess = aVsyncTimestamp;
667 : }
668 71 : MOZ_ASSERT(aVsyncTimestamp <= TimeStamp::Now());
669 :
670 : // We might have a problem that we call ~VsyncRefreshDriverTimer() before
671 : // the scheduled TickRefreshDriver() runs. Check mVsyncRefreshDriverTimer
672 : // before use.
673 71 : if (mVsyncRefreshDriverTimer) {
674 71 : mVsyncRefreshDriverTimer->RunRefreshDrivers(aVsyncTimestamp);
675 : }
676 :
677 71 : if (!XRE_IsParentProcess()) {
678 4 : TimeDuration tickDuration = TimeStamp::Now() - mLastChildTick;
679 4 : mBlockUntil = aVsyncTimestamp + tickDuration;
680 : }
681 71 : }
682 :
683 : // VsyncRefreshDriverTimer holds this RefreshDriverVsyncObserver and it will
684 : // be always available before Shutdown(). We can just use the raw pointer
685 : // here.
686 : VsyncRefreshDriverTimer* mVsyncRefreshDriverTimer;
687 : Monitor mRefreshTickLock;
688 : TimeStamp mRecentVsync;
689 : TimeStamp mLastChildTick;
690 : TimeStamp mLastProcessedTickInChildProcess;
691 : TimeStamp mBlockUntil;
692 : TimeDuration mVsyncRate;
693 : bool mProcessedVsync;
694 : }; // RefreshDriverVsyncObserver
695 :
696 0 : ~VsyncRefreshDriverTimer() override
697 0 : {
698 0 : if (XRE_IsParentProcess()) {
699 0 : mVsyncDispatcher->SetParentRefreshTimer(nullptr);
700 0 : mVsyncDispatcher = nullptr;
701 : } else {
702 : // Since the PVsyncChild actors live through the life of the process, just
703 : // send the unobserveVsync message to disable vsync event. We don't need
704 : // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy()
705 : // will be called and clean up this actor.
706 0 : Unused << mVsyncChild->SendUnobserve();
707 0 : mVsyncChild->SetVsyncObserver(nullptr);
708 0 : mVsyncChild = nullptr;
709 : }
710 :
711 : // Detach current vsync timer from this VsyncObserver. The observer will no
712 : // longer tick this timer.
713 0 : mVsyncObserver->Shutdown();
714 0 : mVsyncObserver = nullptr;
715 0 : }
716 :
717 6 : void StartTimer() override
718 : {
719 : // Protect updates to `sActiveVsyncTimers`.
720 6 : MOZ_ASSERT(NS_IsMainThread());
721 :
722 6 : mLastFireEpoch = JS_Now();
723 6 : mLastFireTime = TimeStamp::Now();
724 :
725 6 : if (XRE_IsParentProcess()) {
726 4 : mVsyncDispatcher->SetParentRefreshTimer(mVsyncObserver);
727 : } else {
728 2 : Unused << mVsyncChild->SendObserve();
729 2 : mVsyncObserver->OnTimerStart();
730 : }
731 :
732 6 : ++sActiveVsyncTimers;
733 6 : }
734 :
735 6 : void StopTimer() override
736 : {
737 : // Protect updates to `sActiveVsyncTimers`.
738 6 : MOZ_ASSERT(NS_IsMainThread());
739 :
740 6 : if (XRE_IsParentProcess()) {
741 4 : mVsyncDispatcher->SetParentRefreshTimer(nullptr);
742 : } else {
743 2 : Unused << mVsyncChild->SendUnobserve();
744 : }
745 :
746 6 : MOZ_ASSERT(sActiveVsyncTimers > 0);
747 6 : --sActiveVsyncTimers;
748 6 : }
749 :
750 71 : void ScheduleNextTick(TimeStamp aNowTime) override
751 : {
752 : // Do nothing since we just wait for the next vsync from
753 : // RefreshDriverVsyncObserver.
754 71 : }
755 :
756 71 : void RunRefreshDrivers(TimeStamp aTimeStamp)
757 : {
758 71 : int64_t jsnow = JS_Now();
759 71 : TimeDuration diff = TimeStamp::Now() - aTimeStamp;
760 71 : int64_t vsyncJsNow = jsnow - diff.ToMicroseconds();
761 71 : Tick(vsyncJsNow, aTimeStamp);
762 71 : }
763 :
764 : RefPtr<RefreshDriverVsyncObserver> mVsyncObserver;
765 : // Used for parent process.
766 : RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
767 : // Used for child process.
768 : // The mVsyncChild will be always available before VsncChild::ActorDestroy().
769 : // After ActorDestroy(), StartTimer() and StopTimer() calls will be non-op.
770 : RefPtr<VsyncChild> mVsyncChild;
771 : TimeDuration mVsyncRate;
772 : }; // VsyncRefreshDriverTimer
773 :
774 1340 : NS_IMPL_ISUPPORTS_INHERITED(VsyncRefreshDriverTimer::
775 : RefreshDriverVsyncObserver::
776 : ParentProcessVsyncNotifier,
777 : Runnable, nsIRunnablePriority)
778 :
779 : mozilla::Atomic<bool>
780 : VsyncRefreshDriverTimer::
781 : RefreshDriverVsyncObserver::
782 : ParentProcessVsyncNotifier::sHighPriorityEnabled(false);
783 :
784 : /**
785 : * Since the content process takes some time to setup
786 : * the vsync IPC connection, this timer is used
787 : * during the intial startup process.
788 : * During initial startup, the refresh drivers
789 : * are ticked off this timer, and are swapped out once content
790 : * vsync IPC connection is established.
791 : */
792 0 : class StartupRefreshDriverTimer :
793 : public SimpleTimerBasedRefreshDriverTimer
794 : {
795 : public:
796 0 : explicit StartupRefreshDriverTimer(double aRate)
797 0 : : SimpleTimerBasedRefreshDriverTimer(aRate)
798 : {
799 0 : }
800 :
801 : protected:
802 0 : void ScheduleNextTick(TimeStamp aNowTime) override
803 : {
804 : // Since this is only used for startup, it isn't super critical
805 : // that we tick at consistent intervals.
806 0 : TimeStamp newTarget = aNowTime + mRateDuration;
807 0 : uint32_t delay = static_cast<uint32_t>((newTarget - aNowTime).ToMilliseconds());
808 0 : mTimer->InitWithNamedFuncCallback(
809 : TimerTick,
810 : this,
811 : delay,
812 : nsITimer::TYPE_ONE_SHOT,
813 0 : "StartupRefreshDriverTimer::ScheduleNextTick");
814 0 : mTargetTime = newTarget;
815 0 : }
816 : };
817 :
818 : /*
819 : * A RefreshDriverTimer for inactive documents. When a new refresh driver is
820 : * added, the rate is reset to the base (normally 1s/1fps). Every time
821 : * it ticks, a single refresh driver is poked. Once they have all been poked,
822 : * the duration between ticks doubles, up to mDisableAfterMilliseconds. At that point,
823 : * the timer is quiet and doesn't tick (until something is added to it again).
824 : *
825 : * When a timer is removed, there is a possibility of another timer
826 : * being skipped for one cycle. We could avoid this by adjusting
827 : * mNextDriverIndex in RemoveRefreshDriver, but there's little need to
828 : * add that complexity. All we want is for inactive drivers to tick
829 : * at some point, but we don't care too much about how often.
830 : */
831 0 : class InactiveRefreshDriverTimer final :
832 : public SimpleTimerBasedRefreshDriverTimer
833 : {
834 : public:
835 : explicit InactiveRefreshDriverTimer(double aRate)
836 : : SimpleTimerBasedRefreshDriverTimer(aRate),
837 : mNextTickDuration(aRate),
838 : mDisableAfterMilliseconds(-1.0),
839 : mNextDriverIndex(0)
840 : {
841 : }
842 :
843 0 : InactiveRefreshDriverTimer(double aRate, double aDisableAfterMilliseconds)
844 0 : : SimpleTimerBasedRefreshDriverTimer(aRate),
845 : mNextTickDuration(aRate),
846 : mDisableAfterMilliseconds(aDisableAfterMilliseconds),
847 0 : mNextDriverIndex(0)
848 : {
849 0 : }
850 :
851 0 : void AddRefreshDriver(nsRefreshDriver* aDriver) override
852 : {
853 0 : RefreshDriverTimer::AddRefreshDriver(aDriver);
854 :
855 0 : LOG("[%p] inactive timer got new refresh driver %p, resetting rate",
856 : this, aDriver);
857 :
858 : // reset the timer, and start with the newly added one next time.
859 0 : mNextTickDuration = mRateMilliseconds;
860 :
861 : // we don't really have to start with the newly added one, but we may as well
862 : // not tick the old ones at the fastest rate any more than we need to.
863 0 : mNextDriverIndex = GetRefreshDriverCount() - 1;
864 :
865 0 : StopTimer();
866 0 : StartTimer();
867 0 : }
868 :
869 0 : TimeDuration GetTimerRate() override
870 : {
871 0 : return TimeDuration::FromMilliseconds(mNextTickDuration);
872 : }
873 :
874 : protected:
875 0 : uint32_t GetRefreshDriverCount()
876 : {
877 0 : return mContentRefreshDrivers.Length() + mRootRefreshDrivers.Length();
878 : }
879 :
880 0 : void StartTimer() override
881 : {
882 0 : mLastFireEpoch = JS_Now();
883 0 : mLastFireTime = TimeStamp::Now();
884 :
885 0 : mTargetTime = mLastFireTime + mRateDuration;
886 :
887 0 : uint32_t delay = static_cast<uint32_t>(mRateMilliseconds);
888 0 : mTimer->InitWithNamedFuncCallback(TimerTickOne,
889 : this,
890 : delay,
891 : nsITimer::TYPE_ONE_SHOT,
892 0 : "InactiveRefreshDriverTimer::StartTimer");
893 0 : }
894 :
895 0 : void StopTimer() override
896 : {
897 0 : mTimer->Cancel();
898 0 : }
899 :
900 0 : void ScheduleNextTick(TimeStamp aNowTime) override
901 : {
902 0 : if (mDisableAfterMilliseconds > 0.0 &&
903 0 : mNextTickDuration > mDisableAfterMilliseconds)
904 : {
905 : // We hit the time after which we should disable
906 : // inactive window refreshes; don't schedule anything
907 : // until we get kicked by an AddRefreshDriver call.
908 0 : return;
909 : }
910 :
911 : // double the next tick time if we've already gone through all of them once
912 0 : if (mNextDriverIndex >= GetRefreshDriverCount()) {
913 0 : mNextTickDuration *= 2.0;
914 0 : mNextDriverIndex = 0;
915 : }
916 :
917 : // this doesn't need to be precise; do a simple schedule
918 0 : uint32_t delay = static_cast<uint32_t>(mNextTickDuration);
919 0 : mTimer->InitWithNamedFuncCallback(
920 : TimerTickOne,
921 : this,
922 : delay,
923 : nsITimer::TYPE_ONE_SHOT,
924 0 : "InactiveRefreshDriverTimer::ScheduleNextTick");
925 :
926 0 : LOG("[%p] inactive timer next tick in %f ms [index %d/%d]", this, mNextTickDuration,
927 : mNextDriverIndex, GetRefreshDriverCount());
928 : }
929 :
930 : /* Runs just one driver's tick. */
931 0 : void TickOne()
932 : {
933 0 : int64_t jsnow = JS_Now();
934 0 : TimeStamp now = TimeStamp::Now();
935 :
936 0 : ScheduleNextTick(now);
937 :
938 0 : mLastFireEpoch = jsnow;
939 0 : mLastFireTime = now;
940 0 : mLastFireSkipped = false;
941 :
942 0 : nsTArray<RefPtr<nsRefreshDriver> > drivers(mContentRefreshDrivers);
943 0 : drivers.AppendElements(mRootRefreshDrivers);
944 0 : size_t index = mNextDriverIndex;
945 :
946 0 : if (index < drivers.Length() &&
947 0 : !drivers[index]->IsTestControllingRefreshesEnabled())
948 : {
949 0 : TickDriver(drivers[index], jsnow, now);
950 0 : mLastFireSkipped = mLastFireSkipped || drivers[index]->SkippedPaints();
951 : }
952 :
953 0 : mNextDriverIndex++;
954 0 : }
955 :
956 0 : static void TimerTickOne(nsITimer* aTimer, void* aClosure)
957 : {
958 0 : InactiveRefreshDriverTimer *timer = static_cast<InactiveRefreshDriverTimer*>(aClosure);
959 0 : timer->TickOne();
960 0 : }
961 :
962 : double mNextTickDuration;
963 : double mDisableAfterMilliseconds;
964 : uint32_t mNextDriverIndex;
965 : };
966 :
967 : // The PBackground protocol connection callback. It will be called when
968 : // PBackground is ready. Then we create the PVsync sub-protocol for our
969 : // vsync-base RefreshTimer.
970 : class VsyncChildCreateCallback final : public nsIIPCBackgroundChildCreateCallback
971 : {
972 : NS_DECL_ISUPPORTS
973 :
974 : public:
975 0 : VsyncChildCreateCallback()
976 0 : {
977 0 : MOZ_ASSERT(NS_IsMainThread());
978 0 : }
979 :
980 1 : static void CreateVsyncActor(PBackgroundChild* aPBackgroundChild)
981 : {
982 1 : MOZ_ASSERT(NS_IsMainThread());
983 1 : MOZ_ASSERT(aPBackgroundChild);
984 :
985 1 : layout::PVsyncChild* actor = aPBackgroundChild->SendPVsyncConstructor();
986 1 : layout::VsyncChild* child = static_cast<layout::VsyncChild*>(actor);
987 1 : nsRefreshDriver::PVsyncActorCreated(child);
988 1 : }
989 :
990 : private:
991 0 : virtual ~VsyncChildCreateCallback() = default;
992 :
993 0 : void ActorCreated(PBackgroundChild* aPBackgroundChild) override
994 : {
995 0 : MOZ_ASSERT(NS_IsMainThread());
996 0 : MOZ_ASSERT(aPBackgroundChild);
997 0 : CreateVsyncActor(aPBackgroundChild);
998 0 : }
999 :
1000 0 : void ActorFailed() override
1001 : {
1002 0 : MOZ_ASSERT(NS_IsMainThread());
1003 0 : MOZ_CRASH("Failed To Create VsyncChild Actor");
1004 : }
1005 : }; // VsyncChildCreateCallback
1006 0 : NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback)
1007 :
1008 : } // namespace mozilla
1009 :
1010 : static RefreshDriverTimer* sRegularRateTimer;
1011 : static InactiveRefreshDriverTimer* sThrottledRateTimer;
1012 :
1013 : static void
1014 1 : CreateContentVsyncRefreshTimer(void*)
1015 : {
1016 1 : MOZ_ASSERT(NS_IsMainThread());
1017 1 : MOZ_ASSERT(!XRE_IsParentProcess());
1018 :
1019 : // Create the PVsync actor child for vsync-base refresh timer.
1020 : // PBackgroundChild is created asynchronously. If PBackgroundChild is still
1021 : // unavailable, setup VsyncChildCreateCallback callback to handle the async
1022 : // connect. We will still use software timer before PVsync ready, and change
1023 : // to use hw timer when the connection is done. Please check
1024 : // VsyncChildCreateCallback::CreateVsyncActor() and
1025 : // nsRefreshDriver::PVsyncActorCreated().
1026 1 : PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
1027 1 : if (backgroundChild) {
1028 : // If we already have PBackgroundChild, create the
1029 : // child VsyncRefreshDriverTimer here.
1030 1 : VsyncChildCreateCallback::CreateVsyncActor(backgroundChild);
1031 1 : return;
1032 : }
1033 : // Setup VsyncChildCreateCallback callback
1034 0 : RefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback();
1035 0 : if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
1036 0 : MOZ_CRASH("PVsync actor create failed!");
1037 : }
1038 : }
1039 :
1040 : static void
1041 2 : CreateVsyncRefreshTimer()
1042 : {
1043 2 : MOZ_ASSERT(NS_IsMainThread());
1044 :
1045 2 : PodArrayZero(sJankLevels);
1046 : // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
1047 : // ready.
1048 2 : gfxPrefs::GetSingleton();
1049 :
1050 2 : if (gfxPlatform::IsInLayoutAsapMode()) {
1051 0 : return;
1052 : }
1053 :
1054 2 : if (XRE_IsParentProcess()) {
1055 : // Make sure all vsync systems are ready.
1056 1 : gfxPlatform::GetPlatform();
1057 : // In parent process, we don't need to use ipc. We can create the
1058 : // VsyncRefreshDriverTimer directly.
1059 1 : sRegularRateTimer = new VsyncRefreshDriverTimer();
1060 1 : return;
1061 : }
1062 :
1063 : // If this process is not created by NUWA, just create the vsync timer here.
1064 1 : CreateContentVsyncRefreshTimer(nullptr);
1065 : }
1066 :
1067 : static uint32_t
1068 4 : GetFirstFrameDelay(imgIRequest* req)
1069 : {
1070 8 : nsCOMPtr<imgIContainer> container;
1071 4 : if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
1072 0 : return 0;
1073 : }
1074 :
1075 : // If this image isn't animated, there isn't a first frame delay.
1076 4 : int32_t delay = container->GetFirstFrameDelay();
1077 4 : if (delay < 0)
1078 0 : return 0;
1079 :
1080 4 : return static_cast<uint32_t>(delay);
1081 : }
1082 :
1083 : /* static */ void
1084 0 : nsRefreshDriver::Shutdown()
1085 : {
1086 : // clean up our timers
1087 0 : delete sRegularRateTimer;
1088 0 : delete sThrottledRateTimer;
1089 :
1090 0 : sRegularRateTimer = nullptr;
1091 0 : sThrottledRateTimer = nullptr;
1092 0 : }
1093 :
1094 : /* static */ int32_t
1095 1 : nsRefreshDriver::DefaultInterval()
1096 : {
1097 1 : return NSToIntRound(1000.0 / gfxPlatform::GetDefaultFrameRate());
1098 : }
1099 :
1100 : // Compute the interval to use for the refresh driver timer, in milliseconds.
1101 : // outIsDefault indicates that rate was not explicitly set by the user
1102 : // so we might choose other, more appropriate rates (e.g. vsync, etc)
1103 : // layout.frame_rate=0 indicates "ASAP mode".
1104 : // In ASAP mode rendering is iterated as fast as possible (typically for stress testing).
1105 : // A target rate of 10k is used internally instead of special-handling 0.
1106 : // Backends which block on swap/present/etc should try to not block
1107 : // when layout.frame_rate=0 - to comply with "ASAP" as much as possible.
1108 : double
1109 2 : nsRefreshDriver::GetRegularTimerInterval(bool *outIsDefault) const
1110 : {
1111 2 : int32_t rate = Preferences::GetInt("layout.frame_rate", -1);
1112 2 : if (rate < 0) {
1113 2 : rate = gfxPlatform::GetDefaultFrameRate();
1114 2 : if (outIsDefault) {
1115 2 : *outIsDefault = true;
1116 : }
1117 : } else {
1118 0 : if (outIsDefault) {
1119 0 : *outIsDefault = false;
1120 : }
1121 : }
1122 :
1123 2 : if (rate == 0) {
1124 0 : rate = 10000;
1125 : }
1126 :
1127 2 : return 1000.0 / rate;
1128 : }
1129 :
1130 : /* static */ double
1131 28 : nsRefreshDriver::GetThrottledTimerInterval()
1132 : {
1133 28 : int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
1134 28 : if (rate <= 0) {
1135 28 : rate = DEFAULT_THROTTLED_FRAME_RATE;
1136 : }
1137 28 : return 1000.0 / rate;
1138 : }
1139 :
1140 : /* static */ mozilla::TimeDuration
1141 28 : nsRefreshDriver::GetMinRecomputeVisibilityInterval()
1142 : {
1143 : int32_t interval =
1144 28 : Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
1145 28 : if (interval <= 0) {
1146 28 : interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
1147 : }
1148 28 : return TimeDuration::FromMilliseconds(interval);
1149 : }
1150 :
1151 : double
1152 0 : nsRefreshDriver::GetRefreshTimerInterval() const
1153 : {
1154 0 : return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
1155 : }
1156 :
1157 : RefreshDriverTimer*
1158 8 : nsRefreshDriver::ChooseTimer() const
1159 : {
1160 8 : if (mThrottled) {
1161 0 : if (!sThrottledRateTimer)
1162 0 : sThrottledRateTimer = new InactiveRefreshDriverTimer(GetThrottledTimerInterval(),
1163 0 : DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
1164 0 : return sThrottledRateTimer;
1165 : }
1166 :
1167 8 : if (!sRegularRateTimer) {
1168 2 : bool isDefault = true;
1169 2 : double rate = GetRegularTimerInterval(&isDefault);
1170 :
1171 : // Try to use vsync-base refresh timer first for sRegularRateTimer.
1172 2 : CreateVsyncRefreshTimer();
1173 :
1174 2 : if (!sRegularRateTimer) {
1175 0 : sRegularRateTimer = new StartupRefreshDriverTimer(rate);
1176 : }
1177 : }
1178 8 : return sRegularRateTimer;
1179 : }
1180 :
1181 28 : nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
1182 : : mActiveTimer(nullptr),
1183 : mPresContext(aPresContext),
1184 : mRootRefresh(nullptr),
1185 : mPendingTransaction(0),
1186 : mCompletedTransaction(0),
1187 : mFreezeCount(0),
1188 : mThrottledFrameRequestInterval(TimeDuration::FromMilliseconds(
1189 28 : GetThrottledTimerInterval())),
1190 : mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
1191 : mThrottled(false),
1192 : mNeedToRecomputeVisibility(false),
1193 : mTestControllingRefreshes(false),
1194 : mViewManagerFlushIsPending(false),
1195 : mInRefresh(false),
1196 : mWaitingForTransaction(false),
1197 : mSkippedPaints(false),
1198 : mResizeSuppressed(false),
1199 56 : mWarningThreshold(REFRESH_WAIT_WARNING)
1200 : {
1201 28 : MOZ_ASSERT(NS_IsMainThread());
1202 28 : MOZ_ASSERT(mPresContext,
1203 : "Need a pres context to tell us to call Disconnect() later "
1204 : "and decrement sRefreshDriverCount.");
1205 28 : mMostRecentRefreshEpochTime = JS_Now();
1206 28 : mMostRecentRefresh = TimeStamp::Now();
1207 28 : mMostRecentTick = mMostRecentRefresh;
1208 28 : mNextThrottledFrameRequestTick = mMostRecentTick;
1209 28 : mNextRecomputeVisibilityTick = mMostRecentTick;
1210 :
1211 28 : ++sRefreshDriverCount;
1212 28 : }
1213 :
1214 0 : nsRefreshDriver::~nsRefreshDriver()
1215 : {
1216 0 : MOZ_ASSERT(NS_IsMainThread());
1217 0 : MOZ_ASSERT(ObserverCount() == mEarlyRunners.Length(),
1218 : "observers, except pending selection scrolls, "
1219 : "should have been unregistered");
1220 0 : MOZ_ASSERT(!mActiveTimer, "timer should be gone");
1221 0 : MOZ_ASSERT(!mPresContext,
1222 : "Should have called Disconnect() and decremented "
1223 : "sRefreshDriverCount!");
1224 :
1225 0 : if (mRootRefresh) {
1226 0 : mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1227 0 : mRootRefresh = nullptr;
1228 : }
1229 0 : }
1230 :
1231 : // Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
1232 : // for description.
1233 : void
1234 0 : nsRefreshDriver::AdvanceTimeAndRefresh(int64_t aMilliseconds)
1235 : {
1236 : // ensure that we're removed from our driver
1237 0 : StopTimer();
1238 :
1239 0 : if (!mTestControllingRefreshes) {
1240 0 : mMostRecentRefreshEpochTime = JS_Now();
1241 0 : mMostRecentRefresh = TimeStamp::Now();
1242 :
1243 0 : mTestControllingRefreshes = true;
1244 0 : if (mWaitingForTransaction) {
1245 : // Disable any refresh driver throttling when entering test mode
1246 0 : mWaitingForTransaction = false;
1247 0 : mSkippedPaints = false;
1248 0 : mWarningThreshold = REFRESH_WAIT_WARNING;
1249 : }
1250 : }
1251 :
1252 0 : mMostRecentRefreshEpochTime += aMilliseconds * 1000;
1253 0 : mMostRecentRefresh += TimeDuration::FromMilliseconds((double) aMilliseconds);
1254 :
1255 0 : mozilla::dom::AutoNoJSAPI nojsapi;
1256 0 : DoTick();
1257 0 : }
1258 :
1259 : void
1260 0 : nsRefreshDriver::RestoreNormalRefresh()
1261 : {
1262 0 : mTestControllingRefreshes = false;
1263 0 : EnsureTimerStarted(eAllowTimeToGoBackwards);
1264 0 : mCompletedTransaction = mPendingTransaction;
1265 0 : }
1266 :
1267 : TimeStamp
1268 117 : nsRefreshDriver::MostRecentRefresh() const
1269 : {
1270 : // In case of stylo traversal, we have already activated the refresh driver in
1271 : // ServoRestyleManager::ProcessPendingRestyles().
1272 117 : if (!ServoStyleSet::IsInServoTraversal()) {
1273 117 : const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1274 : }
1275 :
1276 117 : return mMostRecentRefresh;
1277 : }
1278 :
1279 : int64_t
1280 0 : nsRefreshDriver::MostRecentRefreshEpochTime() const
1281 : {
1282 0 : const_cast<nsRefreshDriver*>(this)->EnsureTimerStarted();
1283 :
1284 0 : return mMostRecentRefreshEpochTime;
1285 : }
1286 :
1287 : bool
1288 2 : nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
1289 : FlushType aFlushType)
1290 : {
1291 2 : ObserverArray& array = ArrayFor(aFlushType);
1292 2 : bool success = array.AppendElement(aObserver) != nullptr;
1293 2 : EnsureTimerStarted();
1294 2 : return success;
1295 : }
1296 :
1297 : bool
1298 2 : nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
1299 : FlushType aFlushType)
1300 : {
1301 2 : ObserverArray& array = ArrayFor(aFlushType);
1302 2 : return array.RemoveElement(aObserver);
1303 : }
1304 :
1305 : void
1306 0 : nsRefreshDriver::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
1307 : {
1308 0 : mPostRefreshObservers.AppendElement(aObserver);
1309 0 : }
1310 :
1311 : void
1312 0 : nsRefreshDriver::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
1313 : {
1314 0 : mPostRefreshObservers.RemoveElement(aObserver);
1315 0 : }
1316 :
1317 : bool
1318 2 : nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
1319 : {
1320 2 : uint32_t delay = GetFirstFrameDelay(aRequest);
1321 2 : if (delay == 0) {
1322 0 : mRequests.PutEntry(aRequest);
1323 : } else {
1324 4 : ImageStartData* start = mStartTable.LookupForAdd(delay).OrInsert(
1325 6 : [] () { return new ImageStartData(); });
1326 2 : start->mEntries.PutEntry(aRequest);
1327 : }
1328 :
1329 2 : EnsureTimerStarted();
1330 :
1331 2 : return true;
1332 : }
1333 :
1334 : void
1335 2 : nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
1336 : {
1337 : // Try to remove from both places, just in case, because we can't tell
1338 : // whether RemoveEntry() succeeds.
1339 2 : mRequests.RemoveEntry(aRequest);
1340 2 : uint32_t delay = GetFirstFrameDelay(aRequest);
1341 2 : if (delay != 0) {
1342 2 : ImageStartData* start = mStartTable.Get(delay);
1343 2 : if (start) {
1344 2 : start->mEntries.RemoveEntry(aRequest);
1345 : }
1346 : }
1347 2 : }
1348 :
1349 : void
1350 444 : nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags)
1351 : {
1352 : // FIXME: Bug 1346065: We should also assert the case where we have
1353 : // STYLO_THREADS=1.
1354 444 : MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal() || NS_IsMainThread(),
1355 : "EnsureTimerStarted should be called only when we are not "
1356 : "in servo traversal or on the main-thread");
1357 :
1358 444 : if (mTestControllingRefreshes)
1359 0 : return;
1360 :
1361 : // will it already fire, and no other changes needed?
1362 444 : if (mActiveTimer && !(aFlags & eForceAdjustTimer))
1363 394 : return;
1364 :
1365 50 : if (IsFrozen() || !mPresContext) {
1366 : // If we don't want to start it now, or we've been disconnected.
1367 0 : StopTimer();
1368 0 : return;
1369 : }
1370 :
1371 50 : if (mPresContext->Document()->IsBeingUsedAsImage()) {
1372 : // Image documents receive ticks from clients' refresh drivers.
1373 : // XXXdholbert Exclude SVG-in-opentype fonts from this optimization, until
1374 : // they receive refresh-driver ticks from their client docs (bug 1107252).
1375 42 : nsIURI* uri = mPresContext->Document()->GetDocumentURI();
1376 42 : if (!uri || !IsFontTableURI(uri)) {
1377 42 : MOZ_ASSERT(!mActiveTimer,
1378 : "image doc refresh driver should never have its own timer");
1379 42 : return;
1380 : }
1381 : }
1382 :
1383 : // We got here because we're either adjusting the time *or* we're
1384 : // starting it for the first time. Add to the right timer,
1385 : // prehaps removing it from a previously-set one.
1386 8 : RefreshDriverTimer *newTimer = ChooseTimer();
1387 8 : if (newTimer != mActiveTimer) {
1388 8 : if (mActiveTimer)
1389 0 : mActiveTimer->RemoveRefreshDriver(this);
1390 8 : mActiveTimer = newTimer;
1391 8 : mActiveTimer->AddRefreshDriver(this);
1392 : }
1393 :
1394 : // When switching from an inactive timer to an active timer, the root
1395 : // refresh driver is skipped due to being set to the content refresh
1396 : // driver's timestamp. In case of EnsureTimerStarted is called from
1397 : // ScheduleViewManagerFlush, we should avoid this behavior to flush
1398 : // a paint in the same tick on the root refresh driver.
1399 8 : if (aFlags & eNeverAdjustTimer) {
1400 4 : return;
1401 : }
1402 :
1403 : // Since the different timers are sampled at different rates, when switching
1404 : // timers, the most recent refresh of the new timer may be *before* the
1405 : // most recent refresh of the old timer. However, the refresh driver time
1406 : // should not go backwards so we clamp the most recent refresh time.
1407 : //
1408 : // The one exception to this is when we are restoring the refresh driver
1409 : // from test control in which case the time is expected to go backwards
1410 : // (see bug 1043078).
1411 : mMostRecentRefresh =
1412 4 : aFlags & eAllowTimeToGoBackwards
1413 0 : ? mActiveTimer->MostRecentRefresh()
1414 4 : : std::max(mActiveTimer->MostRecentRefresh(), mMostRecentRefresh);
1415 4 : mMostRecentRefreshEpochTime =
1416 4 : aFlags & eAllowTimeToGoBackwards
1417 12 : ? mActiveTimer->MostRecentRefreshEpochTime()
1418 12 : : std::max(mActiveTimer->MostRecentRefreshEpochTime(),
1419 12 : mMostRecentRefreshEpochTime);
1420 : }
1421 :
1422 : void
1423 12 : nsRefreshDriver::StopTimer()
1424 : {
1425 12 : if (!mActiveTimer)
1426 4 : return;
1427 :
1428 8 : mActiveTimer->RemoveRefreshDriver(this);
1429 8 : mActiveTimer = nullptr;
1430 : }
1431 :
1432 : uint32_t
1433 70 : nsRefreshDriver::ObserverCount() const
1434 : {
1435 70 : uint32_t sum = 0;
1436 280 : for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
1437 210 : sum += mObservers[i].Length();
1438 : }
1439 :
1440 : // Even while throttled, we need to process layout and style changes. Style
1441 : // changes can trigger transitions which fire events when they complete, and
1442 : // layout changes can affect media queries on child documents, triggering
1443 : // style changes, etc.
1444 70 : sum += mStyleFlushObservers.Length();
1445 70 : sum += mLayoutFlushObservers.Length();
1446 70 : sum += mPendingEvents.Length();
1447 70 : sum += mFrameRequestCallbackDocs.Length();
1448 70 : sum += mThrottledFrameRequestCallbackDocs.Length();
1449 70 : sum += mViewManagerFlushIsPending;
1450 70 : sum += mEarlyRunners.Length();
1451 70 : return sum;
1452 : }
1453 :
1454 : uint32_t
1455 19 : nsRefreshDriver::ImageRequestCount() const
1456 : {
1457 19 : uint32_t count = 0;
1458 40 : for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) {
1459 21 : count += iter.UserData()->mEntries.Count();
1460 : }
1461 19 : return count + mRequests.Count();
1462 : }
1463 :
1464 : nsRefreshDriver::ObserverArray&
1465 4 : nsRefreshDriver::ArrayFor(FlushType aFlushType)
1466 : {
1467 4 : switch (aFlushType) {
1468 : case FlushType::Style:
1469 2 : return mObservers[0];
1470 : case FlushType::Layout:
1471 0 : return mObservers[1];
1472 : case FlushType::Display:
1473 2 : return mObservers[2];
1474 : default:
1475 0 : MOZ_CRASH("We don't track refresh observers for this flush type");
1476 : }
1477 : }
1478 :
1479 : /*
1480 : * nsITimerCallback implementation
1481 : */
1482 :
1483 : void
1484 16 : nsRefreshDriver::DoTick()
1485 : {
1486 16 : NS_PRECONDITION(!IsFrozen(), "Why are we notified while frozen?");
1487 16 : NS_PRECONDITION(mPresContext, "Why are we notified after disconnection?");
1488 16 : NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
1489 : "Shouldn't have a JSContext on the stack");
1490 :
1491 16 : if (mTestControllingRefreshes) {
1492 0 : Tick(mMostRecentRefreshEpochTime, mMostRecentRefresh);
1493 : } else {
1494 16 : Tick(JS_Now(), TimeStamp::Now());
1495 : }
1496 16 : }
1497 :
1498 4 : struct DocumentFrameCallbacks {
1499 4 : explicit DocumentFrameCallbacks(nsIDocument* aDocument) :
1500 4 : mDocument(aDocument)
1501 4 : {}
1502 :
1503 : nsCOMPtr<nsIDocument> mDocument;
1504 : nsIDocument::FrameRequestCallbackList mCallbacks;
1505 : };
1506 :
1507 32 : static nsDocShell* GetDocShell(nsPresContext* aPresContext)
1508 : {
1509 32 : return static_cast<nsDocShell*>(aPresContext->GetDocShell());
1510 : }
1511 :
1512 : static bool
1513 24 : HasPendingAnimations(nsIPresShell* aShell)
1514 : {
1515 24 : nsIDocument* doc = aShell->GetDocument();
1516 24 : if (!doc) {
1517 0 : return false;
1518 : }
1519 :
1520 24 : PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();
1521 24 : return tracker && tracker->HasPendingAnimations();
1522 : }
1523 :
1524 : /**
1525 : * Return a list of all the child docShells in a given root docShell that are
1526 : * visible and are recording markers for the profilingTimeline
1527 : */
1528 32 : static void GetProfileTimelineSubDocShells(nsDocShell* aRootDocShell,
1529 : nsTArray<nsDocShell*>& aShells)
1530 : {
1531 32 : if (!aRootDocShell) {
1532 32 : return;
1533 : }
1534 :
1535 32 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1536 32 : if (!timelines || timelines->IsEmpty()) {
1537 32 : return;
1538 : }
1539 :
1540 0 : nsCOMPtr<nsISimpleEnumerator> enumerator;
1541 0 : nsresult rv = aRootDocShell->GetDocShellEnumerator(
1542 : nsIDocShellTreeItem::typeAll,
1543 : nsIDocShell::ENUMERATE_BACKWARDS,
1544 0 : getter_AddRefs(enumerator));
1545 :
1546 0 : if (NS_FAILED(rv)) {
1547 0 : return;
1548 : }
1549 :
1550 0 : nsCOMPtr<nsIDocShell> curItem;
1551 0 : bool hasMore = false;
1552 0 : while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
1553 0 : nsCOMPtr<nsISupports> curSupports;
1554 0 : enumerator->GetNext(getter_AddRefs(curSupports));
1555 0 : curItem = do_QueryInterface(curSupports);
1556 :
1557 0 : if (!curItem || !curItem->GetRecordProfileTimelineMarkers()) {
1558 0 : continue;
1559 : }
1560 :
1561 0 : nsDocShell* shell = static_cast<nsDocShell*>(curItem.get());
1562 0 : bool isVisible = false;
1563 0 : shell->GetVisibility(&isVisible);
1564 0 : if (!isVisible) {
1565 0 : continue;
1566 : }
1567 :
1568 0 : aShells.AppendElement(shell);
1569 : }
1570 : }
1571 :
1572 : static void
1573 4 : TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
1574 : nsTArray<DocumentFrameCallbacks>& aTarget)
1575 : {
1576 4 : aTarget.AppendElement(aDocument);
1577 4 : aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
1578 4 : }
1579 :
1580 : void
1581 42 : nsRefreshDriver::DispatchPendingEvents()
1582 : {
1583 : // Swap out the current pending events
1584 84 : nsTArray<PendingEvent> pendingEvents(Move(mPendingEvents));
1585 42 : for (PendingEvent& event : pendingEvents) {
1586 : bool dummy;
1587 0 : event.mTarget->DispatchEvent(event.mEvent, &dummy);
1588 : }
1589 42 : }
1590 :
1591 : static bool
1592 148 : CollectDocuments(nsIDocument* aDocument, void* aDocArray)
1593 : {
1594 : static_cast<AutoTArray<nsCOMPtr<nsIDocument>, 32>*>(aDocArray)->
1595 148 : AppendElement(aDocument);
1596 148 : aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray);
1597 148 : return true;
1598 : }
1599 :
1600 : void
1601 42 : nsRefreshDriver::DispatchAnimationEvents()
1602 : {
1603 42 : if (!mPresContext) {
1604 0 : return;
1605 : }
1606 :
1607 84 : AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
1608 42 : CollectDocuments(mPresContext->Document(), &documents);
1609 :
1610 116 : for (uint32_t i = 0; i < documents.Length(); ++i) {
1611 74 : nsIDocument* doc = documents[i];
1612 74 : nsIPresShell* shell = doc->GetShell();
1613 74 : if (!shell) {
1614 64 : continue;
1615 : }
1616 :
1617 84 : RefPtr<nsPresContext> context = shell->GetPresContext();
1618 42 : if (!context || context->RefreshDriver() != this) {
1619 0 : continue;
1620 : }
1621 :
1622 42 : context->TransitionManager()->SortEvents();
1623 42 : context->AnimationManager()->SortEvents();
1624 :
1625 : // Dispatch transition events first since transitions conceptually sit
1626 : // below animations in terms of compositing order.
1627 42 : context->TransitionManager()->DispatchEvents();
1628 : // Check that the presshell has not been destroyed
1629 42 : if (context->GetPresShell()) {
1630 42 : context->AnimationManager()->DispatchEvents();
1631 : }
1632 : }
1633 : }
1634 :
1635 : void
1636 42 : nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
1637 : {
1638 : // Grab all of our frame request callbacks up front.
1639 : nsTArray<DocumentFrameCallbacks>
1640 42 : frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
1641 84 : mThrottledFrameRequestCallbackDocs.Length());
1642 :
1643 : // First, grab throttled frame request callbacks.
1644 : {
1645 84 : nsTArray<nsIDocument*> docsToRemove;
1646 :
1647 : // We always tick throttled frame requests if the entire refresh driver is
1648 : // throttled, because in that situation throttled frame requests tick at the
1649 : // same frequency as non-throttled frame requests.
1650 42 : bool tickThrottledFrameRequests = mThrottled;
1651 :
1652 84 : if (!tickThrottledFrameRequests &&
1653 42 : aNowTime >= mNextThrottledFrameRequestTick) {
1654 7 : mNextThrottledFrameRequestTick = aNowTime + mThrottledFrameRequestInterval;
1655 7 : tickThrottledFrameRequests = true;
1656 : }
1657 :
1658 43 : for (nsIDocument* doc : mThrottledFrameRequestCallbackDocs) {
1659 1 : if (tickThrottledFrameRequests) {
1660 : // We're ticking throttled documents, so grab this document's requests.
1661 : // We don't bother appending to docsToRemove because we're going to
1662 : // clear mThrottledFrameRequestCallbackDocs anyway.
1663 0 : TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1664 1 : } else if (!doc->ShouldThrottleFrameRequests()) {
1665 : // This document is no longer throttled, so grab its requests even
1666 : // though we're not ticking throttled frame requests right now. If
1667 : // this is the first unthrottled document with frame requests, we'll
1668 : // enter high precision mode the next time the callback is scheduled.
1669 1 : TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1670 1 : docsToRemove.AppendElement(doc);
1671 : }
1672 : }
1673 :
1674 : // Remove all the documents we're ticking from
1675 : // mThrottledFrameRequestCallbackDocs so they can be readded as needed.
1676 42 : if (tickThrottledFrameRequests) {
1677 7 : mThrottledFrameRequestCallbackDocs.Clear();
1678 : } else {
1679 : // XXX(seth): We're using this approach to avoid concurrent modification
1680 : // of mThrottledFrameRequestCallbackDocs. docsToRemove usually has either
1681 : // zero elements or a very small number, so this should be OK in practice.
1682 36 : for (nsIDocument* doc : docsToRemove) {
1683 1 : mThrottledFrameRequestCallbackDocs.RemoveElement(doc);
1684 : }
1685 : }
1686 : }
1687 :
1688 : // Now grab unthrottled frame request callbacks.
1689 45 : for (nsIDocument* doc : mFrameRequestCallbackDocs) {
1690 3 : TakeFrameRequestCallbacksFrom(doc, frameRequestCallbacks);
1691 : }
1692 :
1693 : // Reset mFrameRequestCallbackDocs so they can be readded as needed.
1694 42 : mFrameRequestCallbackDocs.Clear();
1695 :
1696 42 : if (!frameRequestCallbacks.IsEmpty()) {
1697 8 : AutoProfilerTracing tracing("Paint", "Scripts");
1698 8 : for (const DocumentFrameCallbacks& docCallbacks : frameRequestCallbacks) {
1699 : // XXXbz Bug 863140: GetInnerWindow can return the outer
1700 : // window in some cases.
1701 : nsPIDOMWindowInner* innerWindow =
1702 4 : docCallbacks.mDocument->GetInnerWindow();
1703 4 : DOMHighResTimeStamp timeStamp = 0;
1704 4 : if (innerWindow && innerWindow->IsInnerWindow()) {
1705 4 : mozilla::dom::Performance* perf = innerWindow->GetPerformance();
1706 4 : if (perf) {
1707 4 : timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aNowTime);
1708 : }
1709 : // else window is partially torn down already
1710 : }
1711 8 : for (auto& callback : docCallbacks.mCallbacks) {
1712 4 : callback->Call(timeStamp);
1713 : }
1714 : }
1715 : }
1716 42 : }
1717 :
1718 8 : struct RunnableWithDelay
1719 : {
1720 : nsCOMPtr<nsIRunnable> mRunnable;
1721 : uint32_t mDelay;
1722 : };
1723 :
1724 : static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
1725 :
1726 : void
1727 3 : nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
1728 : uint32_t aDelay)
1729 : {
1730 3 : if (!sPendingIdleRunnables) {
1731 3 : sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
1732 : }
1733 :
1734 6 : RunnableWithDelay rwd = {aRunnable, aDelay};
1735 3 : sPendingIdleRunnables->AppendElement(rwd);
1736 3 : }
1737 :
1738 : void
1739 6 : nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
1740 : {
1741 6 : if (!sPendingIdleRunnables) {
1742 6 : return;
1743 : }
1744 :
1745 0 : for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
1746 0 : if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
1747 0 : sPendingIdleRunnables->RemoveElementAt(i);
1748 0 : break;
1749 : }
1750 : }
1751 :
1752 0 : if (sPendingIdleRunnables->IsEmpty()) {
1753 0 : delete sPendingIdleRunnables;
1754 0 : sPendingIdleRunnables = nullptr;
1755 : }
1756 : }
1757 :
1758 : void
1759 89 : nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
1760 : {
1761 89 : NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
1762 : "Shouldn't have a JSContext on the stack");
1763 :
1764 89 : if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
1765 0 : NS_ERROR("Refresh driver should not run during plugin call!");
1766 : // Try to survive this by just ignoring the refresh tick.
1767 0 : return;
1768 : }
1769 :
1770 131 : AUTO_PROFILER_LABEL("nsRefreshDriver::Tick", GRAPHICS);
1771 :
1772 : // We're either frozen or we were disconnected (likely in the middle
1773 : // of a tick iteration). Just do nothing here, since our
1774 : // prescontext went away.
1775 89 : if (IsFrozen() || !mPresContext) {
1776 0 : return;
1777 : }
1778 :
1779 : // We can have a race condition where the vsync timestamp
1780 : // is before the most recent refresh due to a forced refresh.
1781 : // The underlying assumption is that the refresh driver tick can only
1782 : // go forward in time, not backwards. To prevent the refresh
1783 : // driver from going back in time, just skip this tick and
1784 : // wait until the next tick.
1785 89 : if ((aNowTime <= mMostRecentRefresh) && !mTestControllingRefreshes) {
1786 0 : return;
1787 : }
1788 :
1789 89 : TimeStamp previousRefresh = mMostRecentRefresh;
1790 :
1791 89 : mMostRecentRefresh = aNowTime;
1792 89 : mMostRecentRefreshEpochTime = aNowEpoch;
1793 :
1794 89 : if (IsWaitingForPaint(aNowTime)) {
1795 : // We're currently suspended waiting for earlier Tick's to
1796 : // be completed (on the Compositor). Mark that we missed the paint
1797 : // and keep waiting.
1798 39 : return;
1799 : }
1800 50 : mMostRecentTick = aNowTime;
1801 50 : if (mRootRefresh) {
1802 0 : mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
1803 0 : mRootRefresh = nullptr;
1804 : }
1805 50 : mSkippedPaints = false;
1806 50 : mWarningThreshold = 1;
1807 :
1808 92 : nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
1809 50 : if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
1810 : // Things are being destroyed, or we no longer have any observers.
1811 : // We don't want to stop the timer when observers are initially
1812 : // removed, because sometimes observers can be added and removed
1813 : // often depending on what other things are going on and in that
1814 : // situation we don't want to thrash our timer. So instead we
1815 : // wait until we get a Notify() call when we have no observers
1816 : // before stopping the timer.
1817 8 : StopTimer();
1818 8 : return;
1819 : }
1820 :
1821 42 : mResizeSuppressed = false;
1822 :
1823 84 : AutoRestore<bool> restoreInRefresh(mInRefresh);
1824 42 : mInRefresh = true;
1825 :
1826 84 : AutoRestore<TimeStamp> restoreTickStart(mTickStart);
1827 42 : mTickStart = TimeStamp::Now();
1828 :
1829 42 : gfxPlatform::GetPlatform()->SchedulePaintIfDeviceReset();
1830 :
1831 : // We want to process any pending APZ metrics ahead of their positions
1832 : // in the queue. This will prevent us from spending precious time
1833 : // painting a stale displayport.
1834 42 : if (gfxPrefs::APZPeekMessages()) {
1835 42 : nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages();
1836 : }
1837 :
1838 84 : AutoTArray<nsCOMPtr<nsIRunnable>, 16> earlyRunners;
1839 42 : earlyRunners.SwapElements(mEarlyRunners);
1840 44 : for (uint32_t i = 0; i < earlyRunners.Length(); ++i) {
1841 2 : earlyRunners[i]->Run();
1842 : }
1843 :
1844 : /*
1845 : * The timer holds a reference to |this| while calling |Notify|.
1846 : * However, implementations of |WillRefresh| are permitted to destroy
1847 : * the pres context, which will cause our |mPresContext| to become
1848 : * null. If this happens, we must stop notifying observers.
1849 : */
1850 168 : for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
1851 252 : ObserverArray::EndLimitedIterator etor(mObservers[i]);
1852 152 : while (etor.HasMore()) {
1853 26 : RefPtr<nsARefreshObserver> obs = etor.GetNext();
1854 13 : obs->WillRefresh(aNowTime);
1855 :
1856 13 : if (!mPresContext || !mPresContext->GetPresShell()) {
1857 0 : StopTimer();
1858 0 : return;
1859 : }
1860 : }
1861 :
1862 126 : if (i == 0) {
1863 : // This is the FlushType::Style case.
1864 :
1865 42 : DispatchAnimationEvents();
1866 42 : DispatchPendingEvents();
1867 42 : RunFrameRequestCallbacks(aNowTime);
1868 :
1869 42 : if (mPresContext && mPresContext->GetPresShell()) {
1870 84 : Maybe<AutoProfilerTracing> tracingStyleFlush;
1871 84 : AutoTArray<nsIPresShell*, 16> observers;
1872 42 : observers.AppendElements(mStyleFlushObservers);
1873 118 : for (uint32_t j = observers.Length();
1874 59 : j && mPresContext && mPresContext->GetPresShell(); --j) {
1875 : // Make sure to not process observers which might have been removed
1876 : // during previous iterations.
1877 17 : nsIPresShell* shell = observers[j - 1];
1878 17 : if (!mStyleFlushObservers.RemoveElement(shell))
1879 0 : continue;
1880 :
1881 17 : if (!tracingStyleFlush) {
1882 17 : tracingStyleFlush.emplace("Paint", "Styles", Move(mStyleCause));
1883 17 : mStyleCause = nullptr;
1884 : }
1885 :
1886 34 : nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
1887 17 : shell->mObservingStyleFlushes = false;
1888 17 : shell->FlushPendingNotifications(ChangesToFlush(FlushType::Style, false));
1889 : // Inform the FontFaceSet that we ticked, so that it can resolve its
1890 : // ready promise if it needs to (though it might still be waiting on
1891 : // a layout flush).
1892 17 : nsPresContext* presContext = shell->GetPresContext();
1893 17 : if (presContext) {
1894 17 : presContext->NotifyFontFaceSetOnRefresh();
1895 : }
1896 17 : mNeedToRecomputeVisibility = true;
1897 : }
1898 : }
1899 84 : } else if (i == 1) {
1900 : // This is the FlushType::Layout case.
1901 84 : Maybe<AutoProfilerTracing> tracingLayoutFlush;
1902 84 : AutoTArray<nsIPresShell*, 16> observers;
1903 42 : observers.AppendElements(mLayoutFlushObservers);
1904 132 : for (uint32_t j = observers.Length();
1905 66 : j && mPresContext && mPresContext->GetPresShell(); --j) {
1906 : // Make sure to not process observers which might have been removed
1907 : // during previous iterations.
1908 24 : nsIPresShell* shell = observers[j - 1];
1909 24 : if (!mLayoutFlushObservers.RemoveElement(shell))
1910 0 : continue;
1911 :
1912 24 : if (!tracingLayoutFlush) {
1913 24 : tracingLayoutFlush.emplace("Paint", "Reflow", Move(mReflowCause));
1914 24 : mReflowCause = nullptr;
1915 : }
1916 :
1917 48 : nsCOMPtr<nsIPresShell> shellKungFuDeathGrip(shell);
1918 24 : shell->mObservingLayoutFlushes = false;
1919 24 : shell->mSuppressInterruptibleReflows = false;
1920 24 : FlushType flushType = HasPendingAnimations(shell)
1921 24 : ? FlushType::Layout
1922 24 : : FlushType::InterruptibleLayout;
1923 24 : shell->FlushPendingNotifications(ChangesToFlush(flushType, false));
1924 : // Inform the FontFaceSet that we ticked, so that it can resolve its
1925 : // ready promise if it needs to.
1926 24 : nsPresContext* presContext = shell->GetPresContext();
1927 24 : if (presContext) {
1928 24 : presContext->NotifyFontFaceSetOnRefresh();
1929 : }
1930 24 : mNeedToRecomputeVisibility = true;
1931 : }
1932 : }
1933 :
1934 : // The pres context may be destroyed during we do the flushing.
1935 126 : if (!mPresContext || !mPresContext->GetPresShell()) {
1936 0 : StopTimer();
1937 0 : return;
1938 : }
1939 : }
1940 :
1941 : // Recompute approximate frame visibility if it's necessary and enough time
1942 : // has passed since the last time we did it.
1943 168 : if (mNeedToRecomputeVisibility && !mThrottled &&
1944 99 : aNowTime >= mNextRecomputeVisibilityTick &&
1945 15 : !presShell->IsPaintingSuppressed()) {
1946 7 : mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
1947 7 : mNeedToRecomputeVisibility = false;
1948 :
1949 7 : presShell->ScheduleApproximateFrameVisibilityUpdateNow();
1950 : }
1951 :
1952 : #ifdef MOZ_XUL
1953 : // Update any popups that may need to be moved or hidden due to their
1954 : // anchor changing.
1955 42 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1956 42 : if (pm) {
1957 42 : pm->UpdatePopupPositions(this);
1958 : }
1959 : #endif
1960 :
1961 84 : AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
1962 42 : CollectDocuments(mPresContext->Document(), &documents);
1963 116 : for (uint32_t i = 0; i < documents.Length(); ++i) {
1964 74 : nsIDocument* doc = documents[i];
1965 74 : doc->UpdateIntersectionObservations();
1966 74 : doc->ScheduleIntersectionObserverNotification();
1967 : }
1968 :
1969 : /*
1970 : * Perform notification to imgIRequests subscribed to listen
1971 : * for refresh events.
1972 : */
1973 :
1974 71 : for (auto iter = mStartTable.Iter(); !iter.Done(); iter.Next()) {
1975 29 : const uint32_t& delay = iter.Key();
1976 29 : ImageStartData* data = iter.UserData();
1977 :
1978 29 : if (data->mStartTime) {
1979 27 : TimeStamp& start = *data->mStartTime;
1980 27 : TimeDuration prev = previousRefresh - start;
1981 27 : TimeDuration curr = aNowTime - start;
1982 27 : uint32_t prevMultiple = uint32_t(prev.ToMilliseconds()) / delay;
1983 :
1984 : // We want to trigger images' refresh if we've just crossed over a
1985 : // multiple of the first image's start time. If so, set the animation
1986 : // start time to the nearest multiple of the delay and move all the
1987 : // images in this table to the main requests table.
1988 27 : if (prevMultiple != uint32_t(curr.ToMilliseconds()) / delay) {
1989 : mozilla::TimeStamp desired =
1990 9 : start + TimeDuration::FromMilliseconds(prevMultiple * delay);
1991 9 : BeginRefreshingImages(data->mEntries, desired);
1992 : }
1993 : } else {
1994 : // This is the very first time we've drawn images with this time delay.
1995 : // Set the animation start time to "now" and move all the images in this
1996 : // table to the main requests table.
1997 2 : mozilla::TimeStamp desired = aNowTime;
1998 2 : BeginRefreshingImages(data->mEntries, desired);
1999 2 : data->mStartTime.emplace(aNowTime);
2000 : }
2001 : }
2002 :
2003 42 : if (mRequests.Count()) {
2004 : // RequestRefresh may run scripts, so it's not safe to directly call it
2005 : // while using a hashtable enumerator to enumerate mRequests in case
2006 : // script modifies the hashtable. Instead, we build a (local) array of
2007 : // images to refresh, and then we refresh each image in that array.
2008 30 : nsCOMArray<imgIContainer> imagesToRefresh(mRequests.Count());
2009 :
2010 30 : for (auto iter = mRequests.Iter(); !iter.Done(); iter.Next()) {
2011 15 : nsISupportsHashKey* entry = iter.Get();
2012 15 : auto req = static_cast<imgIRequest*>(entry->GetKey());
2013 15 : MOZ_ASSERT(req, "Unable to retrieve the image request");
2014 30 : nsCOMPtr<imgIContainer> image;
2015 15 : if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2016 15 : imagesToRefresh.AppendElement(image.forget());
2017 : }
2018 : }
2019 :
2020 30 : for (uint32_t i = 0; i < imagesToRefresh.Length(); i++) {
2021 15 : imagesToRefresh[i]->RequestRefresh(aNowTime);
2022 : }
2023 : }
2024 :
2025 42 : bool dispatchRunnablesAfterTick = false;
2026 42 : if (mViewManagerFlushIsPending) {
2027 64 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2028 :
2029 64 : nsTArray<nsDocShell*> profilingDocShells;
2030 32 : GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
2031 32 : for (nsDocShell* docShell : profilingDocShells) {
2032 : // For the sake of the profile timeline's simplicity, this is flagged as
2033 : // paint even if it includes creating display lists
2034 0 : MOZ_ASSERT(timelines);
2035 0 : MOZ_ASSERT(timelines->HasConsumer(docShell));
2036 0 : timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::START);
2037 : }
2038 :
2039 : #ifdef MOZ_DUMP_PAINTING
2040 32 : if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2041 0 : printf_stderr("Starting ProcessPendingUpdates\n");
2042 : }
2043 : #endif
2044 :
2045 32 : mViewManagerFlushIsPending = false;
2046 64 : RefPtr<nsViewManager> vm = mPresContext->GetPresShell()->GetViewManager();
2047 : {
2048 64 : PaintTelemetry::AutoRecordPaint record;
2049 32 : vm->ProcessPendingUpdates();
2050 : }
2051 :
2052 : #ifdef MOZ_DUMP_PAINTING
2053 32 : if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
2054 0 : printf_stderr("Ending ProcessPendingUpdates\n");
2055 : }
2056 : #endif
2057 :
2058 32 : for (nsDocShell* docShell : profilingDocShells) {
2059 0 : MOZ_ASSERT(timelines);
2060 0 : MOZ_ASSERT(timelines->HasConsumer(docShell));
2061 0 : timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
2062 : }
2063 :
2064 32 : dispatchRunnablesAfterTick = true;
2065 : }
2066 :
2067 : #ifndef ANDROID /* bug 1142079 */
2068 42 : mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
2069 : #endif
2070 :
2071 84 : nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(mPostRefreshObservers);
2072 42 : while (iter.HasMore()) {
2073 0 : nsAPostRefreshObserver* observer = iter.GetNext();
2074 0 : observer->DidRefresh();
2075 : }
2076 :
2077 42 : NS_ASSERTION(mInRefresh, "Still in refresh");
2078 :
2079 42 : if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
2080 0 : ScheduleViewManagerFlush();
2081 : }
2082 :
2083 42 : if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
2084 2 : AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
2085 2 : sPendingIdleRunnables = nullptr;
2086 4 : for (uint32_t i = 0; i < runnables->Length(); ++i) {
2087 4 : NS_IdleDispatchToCurrentThread((*runnables)[i].mRunnable.forget(),
2088 4 : (*runnables)[i].mDelay);
2089 : }
2090 2 : delete runnables;
2091 : }
2092 : }
2093 :
2094 : void
2095 11 : nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
2096 : mozilla::TimeStamp aDesired)
2097 : {
2098 13 : for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
2099 2 : auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
2100 2 : MOZ_ASSERT(req, "Unable to retrieve the image request");
2101 :
2102 2 : mRequests.PutEntry(req);
2103 :
2104 4 : nsCOMPtr<imgIContainer> image;
2105 2 : if (NS_SUCCEEDED(req->GetImage(getter_AddRefs(image)))) {
2106 2 : image->SetAnimationStartTime(aDesired);
2107 : }
2108 : }
2109 11 : aEntries.Clear();
2110 11 : }
2111 :
2112 : void
2113 0 : nsRefreshDriver::Freeze()
2114 : {
2115 0 : StopTimer();
2116 0 : mFreezeCount++;
2117 0 : }
2118 :
2119 : void
2120 0 : nsRefreshDriver::Thaw()
2121 : {
2122 0 : NS_ASSERTION(mFreezeCount > 0, "Thaw() called on an unfrozen refresh driver");
2123 :
2124 0 : if (mFreezeCount > 0) {
2125 0 : mFreezeCount--;
2126 : }
2127 :
2128 0 : if (mFreezeCount == 0) {
2129 0 : if (ObserverCount() || ImageRequestCount()) {
2130 : // FIXME: This isn't quite right, since our EnsureTimerStarted call
2131 : // updates our mMostRecentRefresh, but the DoRefresh call won't run
2132 : // and notify our observers until we get back to the event loop.
2133 : // Thus MostRecentRefresh() will lie between now and the DoRefresh.
2134 0 : RefPtr<nsRunnableMethod<nsRefreshDriver>> event = NewRunnableMethod(
2135 0 : "nsRefreshDriver::DoRefresh", this, &nsRefreshDriver::DoRefresh);
2136 0 : nsPresContext* pc = GetPresContext();
2137 0 : if (pc) {
2138 0 : pc->Document()->Dispatch("nsRefreshDriver::DoRefresh",
2139 : TaskCategory::Other,
2140 0 : event.forget());
2141 0 : EnsureTimerStarted();
2142 : } else {
2143 0 : NS_ERROR("Thawing while document is being destroyed");
2144 : }
2145 : }
2146 : }
2147 0 : }
2148 :
2149 : void
2150 21 : nsRefreshDriver::FinishedWaitingForTransaction()
2151 : {
2152 21 : mWaitingForTransaction = false;
2153 62 : if (mSkippedPaints &&
2154 57 : !IsInRefresh() &&
2155 27 : (ObserverCount() || ImageRequestCount())) {
2156 32 : AutoProfilerTracing tracing("Paint", "RefreshDriverTick");
2157 16 : DoRefresh();
2158 : }
2159 21 : mSkippedPaints = false;
2160 21 : mWarningThreshold = 1;
2161 21 : }
2162 :
2163 : uint64_t
2164 29 : nsRefreshDriver::GetTransactionId(bool aThrottle)
2165 : {
2166 29 : ++mPendingTransaction;
2167 :
2168 58 : if (aThrottle &&
2169 51 : mPendingTransaction >= mCompletedTransaction + 2 &&
2170 43 : !mWaitingForTransaction &&
2171 21 : !mTestControllingRefreshes) {
2172 21 : mWaitingForTransaction = true;
2173 21 : mSkippedPaints = false;
2174 21 : mWarningThreshold = 1;
2175 : }
2176 :
2177 29 : return mPendingTransaction;
2178 : }
2179 :
2180 : uint64_t
2181 1 : nsRefreshDriver::LastTransactionId() const
2182 : {
2183 1 : return mPendingTransaction;
2184 : }
2185 :
2186 : void
2187 1 : nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId)
2188 : {
2189 1 : MOZ_ASSERT(aTransactionId == mPendingTransaction);
2190 2 : if (mPendingTransaction == mCompletedTransaction + 2 &&
2191 1 : mWaitingForTransaction) {
2192 1 : MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
2193 1 : FinishedWaitingForTransaction();
2194 : }
2195 1 : mPendingTransaction--;
2196 1 : }
2197 :
2198 : void
2199 1 : nsRefreshDriver::ClearPendingTransactions()
2200 : {
2201 1 : mCompletedTransaction = mPendingTransaction;
2202 1 : mWaitingForTransaction = false;
2203 1 : }
2204 :
2205 : void
2206 1 : nsRefreshDriver::ResetInitialTransactionId(uint64_t aTransactionId)
2207 : {
2208 1 : mCompletedTransaction = mPendingTransaction = aTransactionId;
2209 1 : }
2210 :
2211 : mozilla::TimeStamp
2212 55 : nsRefreshDriver::GetTransactionStart()
2213 : {
2214 55 : return mTickStart;
2215 : }
2216 :
2217 : void
2218 27 : nsRefreshDriver::NotifyTransactionCompleted(uint64_t aTransactionId)
2219 : {
2220 27 : if (aTransactionId > mCompletedTransaction) {
2221 48 : if (mPendingTransaction > mCompletedTransaction + 1 &&
2222 21 : mWaitingForTransaction) {
2223 20 : mCompletedTransaction = aTransactionId;
2224 20 : FinishedWaitingForTransaction();
2225 : } else {
2226 7 : mCompletedTransaction = aTransactionId;
2227 : }
2228 : }
2229 27 : }
2230 :
2231 : void
2232 0 : nsRefreshDriver::WillRefresh(mozilla::TimeStamp aTime)
2233 : {
2234 0 : mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2235 0 : mRootRefresh = nullptr;
2236 0 : if (mSkippedPaints) {
2237 0 : DoRefresh();
2238 : }
2239 0 : }
2240 :
2241 : bool
2242 89 : nsRefreshDriver::IsWaitingForPaint(mozilla::TimeStamp aTime)
2243 : {
2244 89 : if (mTestControllingRefreshes) {
2245 0 : return false;
2246 : }
2247 :
2248 89 : if (mWaitingForTransaction) {
2249 39 : if (mSkippedPaints && aTime > (mMostRecentTick + TimeDuration::FromMilliseconds(mWarningThreshold * 1000))) {
2250 : // XXX - Bug 1303369 - too many false positives.
2251 : //gfxCriticalNote << "Refresh driver waiting for the compositor for "
2252 : // << (aTime - mMostRecentTick).ToSeconds()
2253 : // << " seconds.";
2254 0 : mWarningThreshold *= 2;
2255 : }
2256 :
2257 39 : mSkippedPaints = true;
2258 39 : return true;
2259 : }
2260 :
2261 : // Try find the 'root' refresh driver for the current window and check
2262 : // if that is waiting for a paint.
2263 50 : nsPresContext* pc = GetPresContext();
2264 50 : nsPresContext* rootContext = pc ? pc->GetRootPresContext() : nullptr;
2265 50 : if (rootContext) {
2266 50 : nsRefreshDriver *rootRefresh = rootContext->RefreshDriver();
2267 50 : if (rootRefresh && rootRefresh != this) {
2268 0 : if (rootRefresh->IsWaitingForPaint(aTime)) {
2269 0 : if (mRootRefresh != rootRefresh) {
2270 0 : if (mRootRefresh) {
2271 0 : mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
2272 : }
2273 0 : rootRefresh->AddRefreshObserver(this, FlushType::Style);
2274 0 : mRootRefresh = rootRefresh;
2275 : }
2276 0 : mSkippedPaints = true;
2277 0 : return true;
2278 : }
2279 : }
2280 : }
2281 50 : return false;
2282 : }
2283 :
2284 : void
2285 7 : nsRefreshDriver::SetThrottled(bool aThrottled)
2286 : {
2287 7 : if (aThrottled != mThrottled) {
2288 0 : mThrottled = aThrottled;
2289 0 : if (mActiveTimer) {
2290 : // We want to switch our timer type here, so just stop and
2291 : // restart the timer.
2292 0 : EnsureTimerStarted(eForceAdjustTimer);
2293 : }
2294 : }
2295 7 : }
2296 :
2297 : /*static*/ void
2298 1 : nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild)
2299 : {
2300 1 : MOZ_ASSERT(NS_IsMainThread());
2301 1 : MOZ_ASSERT(!XRE_IsParentProcess());
2302 : auto* vsyncRefreshDriverTimer =
2303 1 : new VsyncRefreshDriverTimer(aVsyncChild);
2304 :
2305 : // If we are using software timer, swap current timer to
2306 : // VsyncRefreshDriverTimer.
2307 1 : if (sRegularRateTimer) {
2308 0 : sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer);
2309 0 : delete sRegularRateTimer;
2310 : }
2311 1 : sRegularRateTimer = vsyncRefreshDriverTimer;
2312 1 : }
2313 :
2314 : void
2315 16 : nsRefreshDriver::DoRefresh()
2316 : {
2317 : // Don't do a refresh unless we're in a state where we should be refreshing.
2318 16 : if (!IsFrozen() && mPresContext && mActiveTimer) {
2319 16 : DoTick();
2320 : }
2321 16 : }
2322 :
2323 : #ifdef DEBUG
2324 : bool
2325 0 : nsRefreshDriver::IsRefreshObserver(nsARefreshObserver* aObserver,
2326 : FlushType aFlushType)
2327 : {
2328 0 : ObserverArray& array = ArrayFor(aFlushType);
2329 0 : return array.Contains(aObserver);
2330 : }
2331 : #endif
2332 :
2333 : void
2334 234 : nsRefreshDriver::ScheduleViewManagerFlush()
2335 : {
2336 234 : NS_ASSERTION(mPresContext->IsRoot(),
2337 : "Should only schedule view manager flush on root prescontexts");
2338 234 : mViewManagerFlushIsPending = true;
2339 234 : EnsureTimerStarted(eNeverAdjustTimer);
2340 234 : }
2341 :
2342 : void
2343 4 : nsRefreshDriver::ScheduleFrameRequestCallbacks(nsIDocument* aDocument)
2344 : {
2345 4 : NS_ASSERTION(mFrameRequestCallbackDocs.IndexOf(aDocument) ==
2346 : mFrameRequestCallbackDocs.NoIndex &&
2347 : mThrottledFrameRequestCallbackDocs.IndexOf(aDocument) ==
2348 : mThrottledFrameRequestCallbackDocs.NoIndex,
2349 : "Don't schedule the same document multiple times");
2350 4 : if (aDocument->ShouldThrottleFrameRequests()) {
2351 1 : mThrottledFrameRequestCallbackDocs.AppendElement(aDocument);
2352 : } else {
2353 3 : mFrameRequestCallbackDocs.AppendElement(aDocument);
2354 : }
2355 :
2356 : // make sure that the timer is running
2357 4 : EnsureTimerStarted();
2358 4 : }
2359 :
2360 : void
2361 0 : nsRefreshDriver::RevokeFrameRequestCallbacks(nsIDocument* aDocument)
2362 : {
2363 0 : mFrameRequestCallbackDocs.RemoveElement(aDocument);
2364 0 : mThrottledFrameRequestCallbackDocs.RemoveElement(aDocument);
2365 : // No need to worry about restarting our timer in slack mode if it's already
2366 : // running; that will happen automatically when it fires.
2367 0 : }
2368 :
2369 : void
2370 0 : nsRefreshDriver::ScheduleEventDispatch(nsINode* aTarget, nsIDOMEvent* aEvent)
2371 : {
2372 0 : mPendingEvents.AppendElement(PendingEvent{aTarget, aEvent});
2373 : // make sure that the timer is running
2374 0 : EnsureTimerStarted();
2375 0 : }
2376 :
2377 : void
2378 4 : nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument)
2379 : {
2380 4 : for (auto i : Reversed(IntegerRange(mPendingEvents.Length()))) {
2381 0 : if (mPendingEvents[i].mTarget->OwnerDoc() == aDocument) {
2382 0 : mPendingEvents.RemoveElementAt(i);
2383 : }
2384 : }
2385 4 : }
2386 :
2387 : /* static */ TimeStamp
2388 165 : nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault)
2389 : {
2390 165 : MOZ_ASSERT(NS_IsMainThread());
2391 165 : MOZ_ASSERT(!aDefault.IsNull());
2392 :
2393 165 : if (!sRegularRateTimer) {
2394 124 : return aDefault;
2395 : }
2396 :
2397 : // For computing idleness of refresh drivers we only care about
2398 : // sRegularRateTimer, since we consider refresh drivers attached to
2399 : // sThrottledRateTimer to be inactive. This implies that tasks
2400 : // resulting from a tick on the sRegularRateTimer counts as being
2401 : // busy but tasks resulting from a tick on sThrottledRateTimer
2402 : // counts as being idle.
2403 41 : return sRegularRateTimer->GetIdleDeadlineHint(aDefault);
2404 : }
2405 :
2406 : void
2407 4 : nsRefreshDriver::Disconnect()
2408 : {
2409 4 : MOZ_ASSERT(NS_IsMainThread());
2410 :
2411 4 : StopTimer();
2412 :
2413 4 : if (mPresContext) {
2414 4 : mPresContext = nullptr;
2415 4 : if (--sRefreshDriverCount == 0) {
2416 0 : Shutdown();
2417 : }
2418 : }
2419 4 : }
2420 :
2421 : /* static */ bool
2422 0 : nsRefreshDriver::IsJankCritical()
2423 : {
2424 0 : MOZ_ASSERT(NS_IsMainThread());
2425 0 : return sActiveVsyncTimers > 0;
2426 : }
2427 :
2428 : /* static */ bool
2429 0 : nsRefreshDriver::GetJankLevels(Vector<uint64_t>& aJank) {
2430 0 : aJank.clear();
2431 0 : return aJank.append(sJankLevels, ArrayLength(sJankLevels));
2432 : }
2433 :
2434 : #undef LOG
|