/* 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 "domstubs.idl"
#include "nsIRequest.idl"

interface mozIDOMWindow;
interface nsPIDOMWindowInner;
interface mozIDOMWindowProxy;
interface nsIArray;
interface nsIInterceptedChannel;
interface nsIPrincipal;
interface nsIPushSubscription;
interface nsIRunnable;
interface nsIURI;
%{C++
namespace mozilla {
namespace dom {
class ClientInfo;
class ServiceWorkerDescriptor;
class IPCNotification;
} // namespace dom
} // namespace mozilla
%}

[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
[ref] native const_IPCNotificationRef(const mozilla::dom::IPCNotification);

[scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
interface nsIServiceWorkerUnregisterCallback : nsISupports
{
  // aState is true if the unregistration succeded.
  // It's false if this ServiceWorkerRegistration doesn't exist.
  void unregisterSucceeded(in boolean aState);
  void unregisterFailed();
};

interface nsIWorkerDebugger;

[scriptable, builtinclass, uuid(76e357ed-208d-4e4c-9165-1c4059707879)]
interface nsIServiceWorkerInfo : nsISupports
{
  // State values below should match the ServiceWorkerState enumeration.
  const unsigned short STATE_PARSED = 0;
  const unsigned short STATE_INSTALLING = 1;
  const unsigned short STATE_INSTALLED = 2;
  const unsigned short STATE_ACTIVATING = 3;
  const unsigned short STATE_ACTIVATED = 4;
  const unsigned short STATE_REDUNDANT = 5;
  const unsigned short STATE_UNKNOWN = 6;

  readonly attribute AString id;

  readonly attribute AString scriptSpec;
  readonly attribute AString cacheName;

  // How many times has this ServiceWorker been launched since the registration
  // was loaded?  This value is not persistent and starts at 0 every time the
  // browser restarts.  The value also inherently starts at 0 for a freshly
  // installed or updated ServiceWorker.
  readonly attribute unsigned long launchCount;

  readonly attribute unsigned short state;

  readonly attribute nsIWorkerDebugger debugger;

  // Return whether the ServiceWorker has a "fetch" event listener. Throws if
  // this is unknown because the worker's main script hasn't finished executing
  // (when exposed as evaluatingWorker).
  readonly attribute boolean handlesFetchEvents;

  readonly attribute PRTime installedTime;
  readonly attribute PRTime activatedTime;
  readonly attribute PRTime redundantTime;

  // Returns the lifetime deadline of the ServiceWorker as the number of
  // milliseconds since the (parent) process was created or 0 if there is no
  // current deadline.  This is primarily intended to allow tests to compare
  // consistent values, not be useful in general.  But note that
  // `ChromeUtils.now()` operates in the same units (milliseconds since
  // current process startup), and so can be used to hackily translate to wall
  // clock time.
  readonly attribute double lifetimeDeadline;

  // Total number of navigation faults experienced by this ServiceWorker since
  // it was loaded from disk at startup or was installed.
  readonly attribute unsigned long navigationFaultCount;

  // Testing mechanism to induce synthetic failure of fetch events.  If set to
  // something other than NS_OK, all fetch events dispatched will be propagated
  // to the content process, but when it comes time to dispatch the fetch event,
  // the cancellation control flow path will be triggered.
  attribute nsresult testingInjectCancellation;

  void attachDebugger();

  void detachDebugger();

  // Forcibly terminate the ServiceWorker if it is running, returning a promise
  // that will be resolved when the worker is fully terminated.  If the
  // ServiceWorker was not running, the promise will be resolved immediately.
  //
  // For the purposes of the ServiceWorkerManager, it's as if the ServiceWorker
  // is already dead when the call completes and returns the Promise.  Any new
  // functional events dispatched at the ServiceWorker will result in a new
  // instance/global being spun up.
  [implicit_jscontext]
  Promise terminateWorker();
};

[scriptable, uuid(87e63548-d440-4b8a-b158-65ad1de0211E)]
interface nsIServiceWorkerRegistrationInfoListener : nsISupports
{
  void onChange();
};

[scriptable, builtinclass, uuid(ddbc1fd4-2f2e-4fca-a395-6e010bbedfe3)]
interface nsIServiceWorkerRegistrationInfo : nsISupports
{
  // State values below should match the ServiceWorkerUpdateViaCache enumeration.
  const unsigned short UPDATE_VIA_CACHE_IMPORTS = 0;
  const unsigned short UPDATE_VIA_CACHE_ALL = 1;
  const unsigned short UPDATE_VIA_CACHE_NONE = 2;

  readonly attribute nsIPrincipal principal;
  readonly attribute boolean unregistered;

  readonly attribute AString scope;
  readonly attribute AString scriptSpec;
  readonly attribute unsigned short updateViaCache;

  readonly attribute PRTime lastUpdateTime;

  readonly attribute nsIServiceWorkerInfo evaluatingWorker;
  readonly attribute nsIServiceWorkerInfo installingWorker;
  readonly attribute nsIServiceWorkerInfo waitingWorker;
  readonly attribute nsIServiceWorkerInfo activeWorker;

  // Exposes the number of times we have ever checked the usage of this origin
  // for the purposes of mitigating ServiceWorker navigation faults that we
  // suspect to be due to quota limit problems.  This should start out 0 and
  // max out at 1 for the time being.
  //
  // Note that the underlying value is tracked on our per-Principal data, but
  // we don't currently expose that data directly via XPCOM so we're exposing
  // this here as the next best thing and because most non-test consumers would
  // work in terms of the registration anyways.
  //
  // This will return -1 if there is no longer any per-origin data because the
  // last registration for the origin (principal) has been unregistered.
  // (Retaining a reference to this interface does not impact anything the
  // underlying scope-to-registration map that is implemented per spec.)
  readonly attribute long quotaUsageCheckCount;

  // Allows to get the related nsIServiceWorkerInfo for a given
  // nsIWorkerDebugger. Over time we shouldn't need this anymore,
  // and instead always control then nsIWorkerDebugger from
  // nsIServiceWorkerInfo and not the other way around.  Returns
  // null if the service worker is no longer registered.
  nsIServiceWorkerInfo getWorkerByID(in unsigned long long aID);

  void addListener(in nsIServiceWorkerRegistrationInfoListener listener);

  void removeListener(in nsIServiceWorkerRegistrationInfoListener listener);

  // Terminate all the service worker relate to this registration.
  // This is used by the WebExtensions framework to shutdown the extension's
  // background service workers as part of shutdown, which happens when:
  // - the extension has been disabled.
  // - the extension is shutting down to be updated.
  // - the extension is shutting down as part of the uninstall flow.
  //
  // All the service workers instances related to this registration are expected
  // to be terminate immediately.
  //
  // TODO - Bug 1638099: This method should also allow the WebExtension framework
  // to mark the registration as disabled (e.g. through an additional parameter),
  // to avoid it to be started again until the WebExtensions framework does explicitly
  // mark it back to enabled.
  void forceShutdown();
};

[scriptable, uuid(9e523e7c-ad6f-4df0-8077-c74aebbc679d)]
interface nsIServiceWorkerManagerListener : nsISupports
{
  void onRegister(in nsIServiceWorkerRegistrationInfo aInfo);

  void onUnregister(in nsIServiceWorkerRegistrationInfo aInfo);

  /**
   * Called by ServiceWorker bypass mitigations when checking whether an
   * origin's quota usage is sufficiently full that we need to clear the origin
   * (and possibly group's) data as part of our mitigation.
   * This notification is provided primarily for testing code that needs to wait
   * for this check to happen but has no other mechanism for knowing it's
   * completed. Probably not relevant to devtools.
   */
  void onQuotaUsageCheckFinish(in nsIServiceWorkerRegistrationInfo aInfo);
};

[scriptable, builtinclass, uuid(7404c8e8-4d47-4449-8ed1-47d1261d4e33)]
interface nsIServiceWorkerManager : nsISupports
{
  /**
   * A testing helper that is meant to only be used in xpcshell-test to test behaviors
   * that would need a browser restart to re-initialize the ServiceWorkerManager from
   * the service worker registration dumped on disk (the one listed in the serviceworker.txt
   * file part of the Firefox profile directory).
   *
   * NOTE: this test helper does
   * - fail if "dom.serviceWorkers.testing.enabled" is not set to true
   * - fail if there are controlled clients (the test case is responsible of making sure that
   *   there is none when this method is being called)
   * - shutdown and clear all service worker registrations (but without removing them from
   *   the registration stored in serviceworker.txt)
   * - force reload the registration data stored in serviceworker.txt (but the test case using
   *   this helper is responsible to be sure that the registrations have been already written
   *   on disk)
   */
  void reloadRegistrationsForTest();

  /**
   * A testing helper that registers a service worker for testing purpose (e.g. used to test
   * a remote worker that has to spawn a new process to be launched).
   * This method can only be used when "dom.serviceWorkers.testing.enabled" is true and
   * it doesn't support all the registration options (e.g. updateViaCache is set automatically
   * to "imports").
   */
  [implicit_jscontext]
  Promise registerForTest(in nsIPrincipal aPrincipal,
                          in AString aScope,
                          in AString aScriptURL);

  /**
   * Register an extension background service worker for a given
   * extension principal and return a promise that resolves to the
   * nsIServiceWorkerRegistrationInfo (or rejects if there was one
   * already registered).
   */
  [implicit_jscontext]
  Promise registerForAddonPrincipal(in nsIPrincipal aPrincipal);

  /**
   * Get an extension background service worker registration for a
   * given extension principal, return an nsIServiceWorkerRegistrationInfo
   * if one exists (or null if no registration has been found).
   */
  void getRegistrationForAddonPrincipal(in nsIPrincipal aPrincipal,
                                        [optional, retval] out nsIServiceWorkerRegistrationInfo regInfo);

  /**
   * Wake up the extension background service worker given its extension base url,
   * for an API event identified by the namespace and event name strings.
   *
   * Returns a Promise which is resolved to true if a listener has been subscribed
   * during the synchronous worker script execution for the expected WebExtensions
   * API event.
   *
   * NOTE: ExtensionBrowser and ExtensionEventManager interfaces are keeping track
   * of these listeners. These are WebExtensions API event listeners and they do not
   * involve any functional events at all.
   */
  [implicit_jscontext]
  Promise wakeForExtensionAPIEvent(in AString aExtensionBaseURL,
                                   in AString aAPINamespace,
                                   in AString aAPIEventName);

  /**
   * Unregister an existing ServiceWorker registration for `aScope`.
   * It keeps aCallback alive until the operation is concluded.
   */
  void unregister(in nsIPrincipal aPrincipal,
                  in nsIServiceWorkerUnregisterCallback aCallback,
                  in AString aScope);

  nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal,
                                                              in AString aScope);

  [notxpcom, nostdcall] boolean StartControlling(in const_ClientInfoRef aClientInfo,
                                                 in const_ServiceWorkerDescriptorRef aServiceWorker);

  // Testing
  AString getScopeForUrl(in nsIPrincipal aPrincipal, in AString aPath);

  // It returns an array of nsIServiceWorkerRegistrationInfos.
  nsIArray getAllRegistrations();

  // For clear-origin-attributes-data
  void removeRegistrationsByOriginAttributes(
      in AString aOriginAttributes,
      [optional] in nsIServiceWorkerUnregisterCallback aCallback);

  // It calls unregister() in each child process. The callback is used to
  // inform when unregister() is completed on the current process.
  void propagateUnregister(in nsIPrincipal aPrincipal,
                           in nsIServiceWorkerUnregisterCallback aCallback,
                           in AString aScope);

  [noscript]
  void sendNotificationClickEvent(in ACString aOriginSuffix,
                                  in AString scope,
                                  in const_IPCNotificationRef aNotification,
                                  in AString aAction);

  [noscript]
  void sendNotificationCloseEvent(in ACString aOriginSuffix,
                                  in AString scope,
                                  in const_IPCNotificationRef aNotification);

  [optional_argc] void sendPushEvent(in ACString aOriginAttributes,
                                     in ACString aScope,
                                     [optional] in Array<uint8_t> aDataBytes);
  void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
                                       in ACString scope,
                                       [optional] in nsIPushSubscription aOldSubscription);

  void addListener(in nsIServiceWorkerManagerListener aListener);

  void removeListener(in nsIServiceWorkerManagerListener aListener);
};

%{ C++
#define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
%}
