author Manuel Rego Casasnovas <>
Thu, 13 Sep 2018 02:59:55 +0000
changeset 492332 16fb60d25c1d5c2375477f97495615b145356189
parent 475188 bb85c5ee5afc151be0d07ecc48318dc69cfef446
child 503396 0ceae9db9ec0be18daa1a279511ad305723185d4
permissions -rw-r--r--
Bug 1490634 [wpt PR 8579] - [css-grid-1] Add tests for percentage tracks inside shrinkwrapped width/height., a=testonly Automatic update from web-platform-testsMerge pull request #8579 from fantasai/indefinite-percentage-tracks [css-grid-1] Add tests for percentage tracks inside shrinkwrapped width/height. -- wpt-commits: 584149f5256d7f8243f715dad07f8529a9ea30ee wpt-pr: 8579

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at */

#if !defined(MediaTimer_h_)
#define MediaTimer_h_

#include "mozilla/AbstractThread.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "nsITimer.h"
#include <queue>

namespace mozilla {

extern LazyLogModule gMediaTimerLog;

#define TIMER_LOG(x, ...) \
  MOZ_ASSERT(gMediaTimerLog); \
  MOZ_LOG(gMediaTimerLog, LogLevel::Debug, ("[MediaTimer=%p relative_t=%" PRId64 "]" x, this, \
                                        RelativeMicroseconds(TimeStamp::Now()), ##__VA_ARGS__))

// This promise type is only exclusive because so far there isn't a reason for
// it not to be. Feel free to change that.
typedef MozPromise<bool, bool, /* IsExclusive = */ true> MediaTimerPromise;

// Timers only know how to fire at a given thread, which creates an impedence
// mismatch with code that operates with TaskQueues. This class solves
// that mismatch with a dedicated (but shared) thread and a nice MozPromise-y
// interface.
class MediaTimer
  explicit MediaTimer(bool aFuzzy = false);

  // We use a release with a custom Destroy().
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
  NS_IMETHOD_(MozExternalRefCountType) Release(void);

  RefPtr<MediaTimerPromise> WaitFor(const TimeDuration& aDuration, const char* aCallSite);
  RefPtr<MediaTimerPromise> WaitUntil(const TimeStamp& aTimeStamp, const char* aCallSite);
  void Cancel(); // Cancel and reject any unresolved promises with false.

  virtual ~MediaTimer() { MOZ_ASSERT(OnMediaTimerThread()); }

  void DispatchDestroy(); // Invoked by Release on an arbitrary thread.
  void Destroy(); // Runs on the timer thread.

  bool OnMediaTimerThread();
  void ScheduleUpdate();
  void Update();
  void UpdateLocked();
  bool IsExpired(const TimeStamp& aTarget, const TimeStamp& aNow);
  void Reject();

  static void TimerCallback(nsITimer* aTimer, void* aClosure);
  void TimerFired();
  void ArmTimer(const TimeStamp& aTarget, const TimeStamp& aNow);

  bool TimerIsArmed()
    return !mCurrentTimerTarget.IsNull();

  void CancelTimerIfArmed()
    if (TimerIsArmed()) {
      TIMER_LOG("MediaTimer::CancelTimerIfArmed canceling timer");
      mCurrentTimerTarget = TimeStamp();

  struct Entry
    TimeStamp mTimeStamp;
    RefPtr<MediaTimerPromise::Private> mPromise;

    explicit Entry(const TimeStamp& aTimeStamp, const char* aCallSite)
      : mTimeStamp(aTimeStamp)
      , mPromise(new MediaTimerPromise::Private(aCallSite))

    // Define a < overload that reverses ordering because std::priority_queue
    // provides access to the largest element, and we want the smallest
    // (i.e. the soonest).
    bool operator<(const Entry& aOther) const
      return mTimeStamp > aOther.mTimeStamp;

  ThreadSafeAutoRefCnt mRefCnt;
  nsCOMPtr<nsIEventTarget> mThread;
  std::priority_queue<Entry> mEntries;
  Monitor mMonitor;
  nsCOMPtr<nsITimer> mTimer;
  TimeStamp mCurrentTimerTarget;

  // Timestamps only have relative meaning, so we need a base timestamp for
  // logging purposes.
  TimeStamp mCreationTimeStamp;
  int64_t RelativeMicroseconds(const TimeStamp& aTimeStamp)
    return (int64_t) (aTimeStamp - mCreationTimeStamp).ToMicroseconds();

  bool mUpdateScheduled;
  const bool mFuzzy;

// Class for managing delayed dispatches on target thread.
class DelayedScheduler {
  explicit DelayedScheduler(AbstractThread* aTargetThread, bool aFuzzy = false)
    : mTargetThread(aTargetThread), mMediaTimer(new MediaTimer(aFuzzy))

  bool IsScheduled() const { return !mTarget.IsNull(); }

  void Reset()
      "Must be on target thread to disconnect");
    if (IsScheduled()) {
      mTarget = TimeStamp();

  template <typename ResolveFunc, typename RejectFunc>
  void Ensure(mozilla::TimeStamp& aTarget,
              ResolveFunc&& aResolver,
              RejectFunc&& aRejector)
    if (IsScheduled() && mTarget <= aTarget) {
    mTarget = aTarget;
    mMediaTimer->WaitUntil(mTarget, __func__)->Then(
      mTargetThread, __func__,

  void CompleteRequest()
    mTarget = TimeStamp();

  RefPtr<AbstractThread> mTargetThread;
  RefPtr<MediaTimer> mMediaTimer;
  MozPromiseRequestHolder<mozilla::MediaTimerPromise> mRequest;
  TimeStamp mTarget;

} // namespace mozilla