/* 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 http://mozilla.org/MPL/2.0/. */

#include "nsISupports.idl"
#include "nsINamed.idl"

interface nsIObserver;
interface nsIEventTarget;

%{C++
#include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h"
#include <functional>

/**
 * The signature of the timer callback function passed to
 * initWithNamedFuncCallback and similar functions. This is the function that
 * will get called when the timer expires if the timer is initialized via
 * initWithNamedFuncCallback.
 *
 * @param aTimer the timer which has expired
 * @param aClosure opaque parameter passed to initWithNamedFuncCallback
 */
class nsITimer;
typedef void (*nsTimerCallbackFunc) (nsITimer* aTimer, void* aClosure);
%}

native MallocSizeOf(mozilla::MallocSizeOf);
native nsTimerCallbackFunc(nsTimerCallbackFunc);
[ref] native TimeDuration(mozilla::TimeDuration);

/**
 * The callback interface for timers.
 */
interface nsITimer;

[function, scriptable, uuid(a796816d-7d47-4348-9ab8-c7aeb3216a7d)]
interface nsITimerCallback : nsISupports
{
  /**
   * @param aTimer the timer which has expired
   */
  void notify(in nsITimer timer);
};

%{C++
// Two timer deadlines must differ by less than half the PRIntervalTime domain.
#define DELAY_INTERVAL_LIMIT    PR_BIT(8 * sizeof(PRIntervalTime) - 1)
%}

/**
 * nsITimer instances must be initialized by calling one of the "init" methods
 * documented below.  You may also re-initialize (using one of the init()
 * methods) an existing instance to avoid the overhead of destroying and
 * creating a timer.  It is not necessary to cancel the timer in that case.
 *
 * By default a timer will fire on the thread that created it.  Set the .target
 * attribute to fire on a different thread.  Once you have set a timer's .target
 * and called one of its init functions, any further interactions with the timer
 * (calling cancel(), changing member fields, etc) should only be done by the
 * target thread, or races may occur with bad results like timers firing after
 * they've been canceled, and/or not firing after re-initiatization.
 */
[scriptable, builtinclass, uuid(3de4b105-363c-482c-a409-baac83a01bfc)]
interface nsITimer : nsISupports
{
  /* Timer types */

  /**
   * Type of a timer that fires once only.
   */
  const short TYPE_ONE_SHOT = 0;

  /**
   * After firing, a TYPE_REPEATING_SLACK timer is stopped and not restarted
   * until its callback completes.  Specified timer period will be at least
   * the time between when processing for last firing the callback completes
   * and when the next firing occurs.
   *
   * This is the preferable repeating type for most situations.
   */
  const short TYPE_REPEATING_SLACK = 1;

  /**
   * TYPE_REPEATING_PRECISE is just a synonym for
   * TYPE_REPEATING_PRECISE_CAN_SKIP. They used to be distinct, but the old
   * TYPE_REPEATING_PRECISE kind was similar to TYPE_REPEATING_PRECISE_CAN_SKIP
   * while also being less useful. So the distinction was removed.
   */
  const short TYPE_REPEATING_PRECISE = 2;

  /**
   * A TYPE_REPEATING_PRECISE_CAN_SKIP repeating timer aims to have constant
   * period between firings.  The processing time for each timer callback will
   * not influence the timer period.  If the callback finishes after the next
   * firing(s) should have happened (either because the callback took a long
   * time, or the callback was called extremely late), that firing(s) is
   * skipped, but the following sequence of firing times will not be altered.
   * This timer type guarantees that it will not queue up new events to fire
   * the callback until the previous callback event finishes firing.  This is
   * the only non-slack timer available.
   */
  const short TYPE_REPEATING_PRECISE_CAN_SKIP = 3;

  /**
   * Same as TYPE_REPEATING_SLACK with the exception that idle events
   * won't yield to timers with this type.  Use this when you want an
   * idle callback to be scheduled to run even though this timer is
   * about to fire.
   */
  const short TYPE_REPEATING_SLACK_LOW_PRIORITY = 4;

  /**
   * Same as TYPE_ONE_SHOT with the exception that idle events won't
   * yield to timers with this type.  Use this when you want an idle
   * callback to be scheduled to run even though this timer is about
   * to fire.
   */
  const short TYPE_ONE_SHOT_LOW_PRIORITY = 5;

  /**
   * Initialize a timer that will fire after the said delay.
   * A user must keep a reference to this timer till it is
   * is no longer needed or has been cancelled.
   *
   * @param aObserver   the callback object that observes the
   *                    ``timer-callback'' topic with the subject being
   *                    the timer itself when the timer fires:
   *
   *                    observe(nsISupports aSubject, => nsITimer
   *                            string aTopic,        => ``timer-callback''
   *                            wstring data          =>  null
   *
   * @param aDelayInMs  delay in milliseconds for timer to fire
   * @param aType       timer type per TYPE* consts defined above
   */
  void init(in nsIObserver aObserver, in unsigned long aDelayInMs,
            in unsigned long aType);


  /**
   * Initialize a timer to fire after the given millisecond interval.
   * This version takes a callback object.
   *
   * @param aFunc       nsITimerCallback interface to call when timer expires
   * @param aDelayInMs  The millisecond interval
   * @param aType       Timer type per TYPE* consts defined above
   */
  void initWithCallback(in nsITimerCallback aCallback,
                        in unsigned long aDelayInMs,
                        in unsigned long aType);

  /**
   * Initialize a timer to fire after the high resolution TimeDuration.
   * This version takes a callback object.
   *
   * @param aFunc      nsITimerCallback interface to call when timer expires
   * @param aDelay     The high resolution interval
   * @param aType      Timer type per TYPE* consts defined above
   */
  [noscript] void initHighResolutionWithCallback(in nsITimerCallback aCallback,
                                                 [const] in TimeDuration aDelay,
                                                 in unsigned long aType);

  /**
   * Cancel the timer.  This method works on all types, not just on repeating
   * timers -- you might want to cancel a TYPE_ONE_SHOT timer, and even reuse
   * it by re-initializing it (to avoid object destruction and creation costs
   * by conserving one timer instance).
   */
  void cancel();

  /**
   * Initialize a timer to fire after the given millisecond interval.
   * This version takes a named function callback.
   *
   * @param aFunc      The function to invoke
   * @param aClosure   An opaque pointer to pass to that function
   * @param aDelay     The millisecond interval
   * @param aType      Timer type per TYPE* consts defined above
   * @param aName      The timer's name
   */
  [noscript] void initWithNamedFuncCallback(in nsTimerCallbackFunc aCallback,
                                            in voidPtr aClosure,
                                            in unsigned long aDelay,
                                            in unsigned long aType,
                                            in ACString aName);

  /**
   * Initialize a timer to fire after the high resolution TimeDuration.
   * This version takes a named function callback.
   *
   * @param aFunc      The function to invoke
   * @param aClosure   An opaque pointer to pass to that function
   * @param aDelay     The high resolution interval
   * @param aType      Timer type per TYPE* consts defined above
   * @param aName      The timer's name
   */
  [noscript] void initHighResolutionWithNamedFuncCallback(
                      in nsTimerCallbackFunc aCallback,
                      in voidPtr aClosure,
                      [const] in TimeDuration aDelay,
                      in unsigned long aType,
                      in ACString aName);

  /**
   * The millisecond delay of the timeout.
   *
   * Setting the delay on a canceled or never initialized timer will not
   * (re-)schedule the timer.
   *
   * Setting the delay on a one-shot timer that has been canceled or already
   * fired doesn't restart the timer and returns an error. It is recommended
   * to use one of the initWithX() methods with one-shot timers instead of
   * setting the delay.
   */
  attribute unsigned long delay;

  /**
   * The timer type - one of the above TYPE_* constants.
   */
  attribute unsigned long type;

  /**
   * The opaque pointer passed to initWithNamedFuncCallback.
   */
  [noscript] readonly attribute voidPtr closure;

  /**
   * The nsITimerCallback object passed to initWithCallback.
   */
  readonly attribute nsITimerCallback callback;

  /**
   * The nsIEventTarget where the callback will be dispatched. Note that this
   * target may only be set before the call to one of the init methods above.
   *
   * By default the target is the thread that created the timer.
   */
  attribute nsIEventTarget target;

  readonly attribute ACString name;

  /**
   * The number of microseconds this nsITimer implementation can possibly
   * fire early.
   */
  [noscript] readonly attribute unsigned long allowedEarlyFiringMicroseconds;

  [notxpcom, nostdcall] size_t sizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
};

%{C++
#include "nsCOMPtr.h"

already_AddRefed<nsITimer> NS_NewTimer();

already_AddRefed<nsITimer> NS_NewTimer(nsIEventTarget* aTarget);

nsresult
NS_NewTimerWithObserver(nsITimer** aTimer,
                        nsIObserver* aObserver,
                        uint32_t aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithObserver(nsIObserver* aObserver,
                        uint32_t aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithCallback(nsITimer** aTimer,
                        nsITimerCallback* aCallback,
                        uint32_t aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithCallback(nsITimerCallback* aCallback,
                        uint32_t aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithCallback(nsITimer** aTimer,
                        nsITimerCallback* aCallback,
                        const mozilla::TimeDuration& aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithCallback(nsITimerCallback* aCallback,
                        const mozilla::TimeDuration& aDelay,
                        uint32_t aType,
                        nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithCallback(nsITimer** aTimer,
                        std::function<void(nsITimer*)>&& aCallback,
                        uint32_t aDelay,
                        uint32_t aType,
                        const nsACString& aNameString,
                        nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithCallback(std::function<void(nsITimer*)>&& aCallback,
                        uint32_t aDelay,
                        uint32_t aType,
                        const nsACString& aNameString,
                        nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithCallback(nsITimer** aTimer,
                        std::function<void(nsITimer*)>&& aCallback,
                        const mozilla::TimeDuration& aDelay,
                        uint32_t aType,
                        const nsACString& aNameString,
                        nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithCallback(std::function<void(nsITimer*)>&& aCallback,
                        const mozilla::TimeDuration& aDelay,
                        uint32_t aType,
                        const nsACString& aNameString,
                        nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithFuncCallback(nsITimer** aTimer,
                            nsTimerCallbackFunc aCallback,
                            void* aClosure,
                            uint32_t aDelay,
                            uint32_t aType,
                            const nsACString& aNameString,
                            nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithFuncCallback(nsTimerCallbackFunc aCallback,
                            void* aClosure,
                            uint32_t aDelay,
                            uint32_t aType,
                            const nsACString& aNameString,
                            nsIEventTarget* aTarget = nullptr);

nsresult
NS_NewTimerWithFuncCallback(nsITimer** aTimer,
                            nsTimerCallbackFunc aCallback,
                            void* aClosure,
                            const mozilla::TimeDuration& aDelay,
                            uint32_t aType,
                            const nsACString& aNameString,
                            nsIEventTarget* aTarget = nullptr);
mozilla::Result<nsCOMPtr<nsITimer>, nsresult>
NS_NewTimerWithFuncCallback(nsTimerCallbackFunc aCallback,
                            void* aClosure,
                            const mozilla::TimeDuration& aDelay,
                            uint32_t aType,
                            const nsACString& aNameString,
                            nsIEventTarget* aTarget = nullptr);

#define NS_TIMER_CALLBACK_TOPIC "timer-callback"

#ifndef RELEASE_OR_BETA
#undef NS_DECL_NSITIMERCALLBACK
#define NS_DECL_NSITIMERCALLBACK                                        \
  NS_IMETHOD Notify(nsITimer *timer) override;                          \
  inline void _ensure_GetName_exists(void) {                            \
    static_assert(std::is_convertible_v<decltype(this), nsINamed*>,     \
                  "nsITimerCallback implementations must also implement nsINamed");     \
  }
#endif
%}

[scriptable, builtinclass, uuid(5482506d-1d21-4d08-b01c-95c87e1295ad)]
interface nsITimerManager : nsISupports
{
  /**
   * Returns a read-only list of nsITimer objects, implementing only the name,
   * delay and type attribute getters.
   * This is meant to be used for tests, to verify that no timer is leftover
   * at the end of a test. */
  Array<nsITimer> getTimers();
};
