/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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"

interface nsIDOMWindow;
interface nsIDocShell;
interface nsIContent;
interface nsIPrincipal;

/**
 * Message managers provide a way for chrome-privileged JS code to
 * communicate with each other, even across process boundaries.
 *
 * Message managers are separated into "parent side" and "child side".
 * These don't always correspond to process boundaries, but can.  For
 * each child-side message manager, there is always exactly one
 * corresponding parent-side message manager that it sends messages
 * to.  However, for each parent-side message manager, there may be
 * either one or many child-side managers it can message.
 *
 * Message managers that always have exactly one "other side" are of
 * type nsIMessageSender.  Parent-side message managers that have many
 * "other sides" are of type nsIMessageBroadcaster.
 *
 * Child-side message managers can send synchronous messages to their
 * parent side, but not the other way around.
 *
 * There are two realms of message manager hierarchies.  One realm
 * approximately corresponds to DOM elements, the other corresponds to
 * process boundaries.
 *
 * Message managers corresponding to DOM elements
 * ==============================================
 *
 * In this realm of message managers, there are
 *  - "frame message managers" which correspond to frame elements
 *  - "window message managers" which correspond to top-level chrome
 *    windows
 *  - the "global message manager", on the parent side.  See below.
 *
 * The DOM-realm message managers can communicate in the ways shown by
 * the following diagram.  The parent side and child side can
 * correspond to process boundaries, but don't always.
 *
 *  Parent side                         Child side
 * -------------                       ------------
 *  global MMg
 *   |
 *   +-->window MMw1
 *   |    |
 *   |    +-->frame MMp1_1<------------>frame MMc1_1
 *   |    |
 *   |    +-->frame MMp1_2<------------>frame MMc1_2
 *   |    ...
 *   |
 *   +-->window MMw2
 *   ...
 *
 * For example: a message sent from MMc1_1, from the child side, is
 * sent only to MMp1_1 on the parent side.  However, note that all
 * message managers in the hierarchy above MMp1_1, in this diagram
 * MMw1 and MMg, will also notify their message listeners when the
 * message arrives.

 * For example: a message broadcast through the global MMg on the
 * parent side would be broadcast to MMw1, which would transitively
 * broadcast it to MMp1_1, MM1p_2".  The message would next be
 * broadcast to MMw2, and so on down the hierarchy.
 *
 *   ***** PERFORMANCE AND SECURITY WARNING *****
 * Messages broadcast through the global MM and window MMs can result
 * in messages being dispatched across many OS processes, and to many
 * processes with different permissions.  Great care should be taken
 * when broadcasting.
 *
 * Interfaces
 * ----------
 *
 * The global MMg and window MMw's are message broadcasters implementing
 * nsIMessageBroadcaster while the frame MMp's are simple message senders
 * (nsIMessageSender). Their counterparts in the content processes are
 * message senders implementing nsIContentFrameMessageManager.
 *
 *                    nsIMessageListenerManager
 *                  /                           \
 * nsIMessageSender                               nsIMessageBroadcaster
 *       |
 * nsISyncMessageSender (content process/in-process only)
 *       |
 * nsIContentFrameMessageManager (content process/in-process only)
 *       |
 * nsIInProcessContentFrameMessageManager (in-process only)
 *
 *
 * Message managers in the chrome process can also be QI'ed to nsIFrameScriptLoader.
 *
 *
 * Message managers corresponding to process boundaries
 * ====================================================
 *
 * The second realm of message managers is the "process message
 * managers".  With one exception, these always correspond to process
 * boundaries.  The picture looks like
 *
 *  Parent process                      Child processes
 * ----------------                    -----------------
 *  global PPMM
 *   |
 *   +<----> child PPMM
 *   |
 *   +-->parent PMM1<------------------>child process CMM1
 *   |
 *   +-->parent PMM2<------------------>child process PMM2
 *   ...
 *
 * For example: the parent-process PMM1 sends messages directly to
 * only the child-process CMM1.
 *
 * For example: CMM1 sends messages directly to PMM1.  The global PPMM
 * will also notify their message listeners when the message arrives.
 *
 * For example: messages sent through the global PPMM will be
 * dispatched to the listeners of the same-process, "child PPMM".
 * They will also be broadcast to PPM1, PPM2, etc.
 *
 *   ***** PERFORMANCE AND SECURITY WARNING *****
 * Messages broadcast through the global PPMM can result in messages
 * being dispatched across many OS processes, and to many processes
 * with different permissions.  Great care should be taken when
 * broadcasting.
 *
 * Requests sent to parent-process message listeners should usually
 * have replies scoped to the requesting CPMM.  The following pattern
 * is common
 *
 *  const ParentProcessListener = {
 *    receiveMessage: function(aMessage) {
 *      let childMM = aMessage.target.QueryInterface(Ci.nsIMessageSender);
 *      switch (aMessage.name) {
 *      case "Foo:Request":
 *        // service request
 *        childMM.sendAsyncMessage("Foo:Response", { data });
 *      }
 *    }
 *  };
 */

[scriptable, function, uuid(2b44eb57-a9c6-4773-9a1e-fe0818739a4c)]
interface nsIMessageListener : nsISupports
{
  /**
   * This is for JS only.
   * receiveMessage is called with one parameter, which has the following
   * properties:
   *   {
   *     target:    %the target of the message. Either an element owning
   *                 the message manager, or message manager itself if no
   *                 element owns it%
   *     name:      %message name%,
   *     sync:      %true or false%.
   *     data:      %structured clone of the sent message data%,
   *     json:      %same as .data, deprecated%,
   *     objects:   %named table of jsvals/objects, or null%
   *     principal: %principal for the window app
   *   }
   *
   * Each listener is invoked with its own copy of the message
   * parameter.
   *
   * When the listener is called, 'this' value is the target of the message.
   *
   * If the message is synchronous, the possible return value is
   * returned as JSON (will be changed to use structured clones).
   * When there are multiple listeners to sync messages, each
   * listener's return value is sent back as an array.  |undefined|
   * return values show up as undefined values in the array.
   */
  void receiveMessage();
};

[scriptable, builtinclass, uuid(aae827bd-acf1-45fe-a556-ea545d4c0804)]
interface nsIMessageListenerManager : nsISupports
{
  /**
   * Register |listener| to receive |messageName|.  All listener
   * callbacks for a particular message are invoked when that message
   * is received.
   *
   * The message manager holds a strong ref to |listener|.
   *
   * If the same listener registers twice for the same message, the
   * second registration is ignored.
   */
  void addMessageListener(in AString messageName,
                          in nsIMessageListener listener);

  /**
   * Undo an |addMessageListener| call -- that is, calling this causes us to no
   * longer invoke |listener| when |messageName| is received.
   *
   * removeMessageListener does not remove a message listener added via
   * addWeakMessageListener; use removeWeakMessageListener for that.
   */
  void removeMessageListener(in AString messageName,
                             in nsIMessageListener listener);

  /**
   * This is just like addMessageListener, except the message manager holds a
   * weak ref to |listener|.
   *
   * If you have two weak message listeners for the same message, they may be
   * called in any order.
   */
  void addWeakMessageListener(in AString messageName,
                              in nsIMessageListener listener);

  /**
   * This undoes an |addWeakMessageListener| call.
   */
  void removeWeakMessageListener(in AString messageName,
                                 in nsIMessageListener listener);

  [notxpcom] boolean markForCC();
};

/**
 * Message "senders" have a single "other side" to which messages are
 * sent.  For example, a child-process message manager will send
 * messages that are only delivered to its one parent-process message
 * manager.
 */
[scriptable, builtinclass, uuid(d6b0d851-43e6-426d-9f13-054bc0198175)]
interface nsIMessageSender : nsIMessageListenerManager
{
  /**
   * Send |messageName| and |obj| to the "other side" of this message
   * manager.  This invokes listeners who registered for
   * |messageName|.
   *
   * See nsIMessageListener::receiveMessage() for the format of the
   * data delivered to listeners.
   * @throws NS_ERROR_NOT_INITIALIZED if the sender is not initialized.  For
   *         example, we will throw NS_ERROR_NOT_INITIALIZED if we try to send
   *         a message to a cross-process frame but the other process has not
   *         yet been set up.
   * @throws NS_ERROR_FAILURE when the message receiver cannot be found.  For
   *         example, we will throw NS_ERROR_FAILURE if we try to send a message
   *         to a cross-process frame whose process has crashed.
   */
  [implicit_jscontext, optional_argc]
  void sendAsyncMessage([optional] in AString messageName,
                        [optional] in jsval obj,
                        [optional] in jsval objects,
                        [optional] in nsIPrincipal principal);
};

/**
 * Message "broadcasters" don't have a single "other side" that they
 * send messages to, but rather a set of subordinate message managers.
 * For example, broadcasting a message through a window message
 * manager will broadcast the message to all frame message managers
 * within its window.
 */
[scriptable, builtinclass, uuid(d36346b9-5d3b-497d-9c28-ffbc3e4f6d0d)]
interface nsIMessageBroadcaster : nsIMessageListenerManager
{
  /**
   * Like |sendAsyncMessage()|, but also broadcasts this message to
   * all "child" message managers of this message manager.  See long
   * comment above for details.
   *
   * WARNING: broadcasting messages can be very expensive and leak
   * sensitive data.  Use with extreme caution.
   */
  [implicit_jscontext, optional_argc]
  void broadcastAsyncMessage([optional] in AString messageName,
                             [optional] in jsval obj,
                             [optional] in jsval objects);

  /**
   * Number of subordinate message managers.
   */
  readonly attribute unsigned long childCount;

  /**
   * Return a single subordinate message manager.
   */
  nsIMessageListenerManager getChildAt(in unsigned long aIndex);
};

[scriptable, builtinclass, uuid(7fda0941-9dcc-448b-bd39-16373c5b4003)]
interface nsISyncMessageSender : nsIMessageSender
{
  /**
   * Like |sendAsyncMessage()|, except blocks the sender until all
   * listeners of the message have been invoked.  Returns an array
   * containing return values from each listener invoked.
   */
  [implicit_jscontext, optional_argc]
  jsval sendSyncMessage([optional] in AString messageName,
                        [optional] in jsval obj,
                        [optional] in jsval objects,
                        [optional] in nsIPrincipal principal);

  /**
   * Like |sendSyncMessage()|, except re-entrant. New RPC messages may be
   * issued even if, earlier on the call stack, we are waiting for a reply
   * to an earlier sendRpcMessage() call.
   *
   * Both sendSyncMessage and sendRpcMessage will block until a reply is
   * received, but they may be temporarily interrupted to process an urgent
   * incoming message (such as a CPOW request).
   */
  [implicit_jscontext, optional_argc]
  jsval sendRpcMessage([optional] in AString messageName,
                       [optional] in jsval obj,
                       [optional] in jsval objects,
                       [optional] in nsIPrincipal principal);
};

[scriptable, builtinclass, uuid(894ff2d4-39a3-4df8-9d76-8ee329975488)]
interface nsIContentFrameMessageManager : nsISyncMessageSender
{
  /**
   * The current top level window in the frame or null.
   */
  readonly attribute nsIDOMWindow content;

  /**
   * The top level docshell or null.
   */
  readonly attribute nsIDocShell docShell;

  /**
   * Print a string to stdout.
   */
  void dump(in DOMString aStr);

  /**
   * If leak detection is enabled, print a note to the leak log that this
   * process will intentionally crash.
   */
  void privateNoteIntentionalCrash();

   /**
    * Ascii base64 data to binary data and vice versa
    */
   DOMString atob(in DOMString aAsciiString);
   DOMString btoa(in DOMString aBase64Data);
};

[uuid(a2325927-9c0c-437d-9215-749c79235031)]
interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
{
  [notxpcom] nsIContent getOwnerContent();
};

[scriptable, builtinclass, uuid(6fb78110-45ae-11e3-8f96-0800200c9a66)]
interface nsIFrameScriptLoader : nsISupports
{
  /**
   * Load a script in the (remote) frame. aURL must be the absolute URL.
   * data: URLs are also supported. For example data:,dump("foo\n");
   * If aAllowDelayedLoad is true, script will be loaded when the
   * remote frame becomes available. Otherwise the script will be loaded
   * only if the frame is already available.
   */
  void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad,
                       [optional] in boolean aRunInGlobalScope);

  /**
   * Removes aURL from the list of scripts which support delayed load.
   */
  void removeDelayedFrameScript(in AString aURL);

  /**
   * Returns all delayed scripts that will be loaded once a (remote)
   * frame becomes available. The return value is a list of pairs
   * [<URL>, <WasLoadedInGlobalScope>].
   */
  [implicit_jscontext]
  jsval getDelayedFrameScripts();
};

[scriptable, builtinclass, uuid(ad57800b-ff21-4e2f-91d3-e68615ae8afe)]
interface nsIProcessChecker : nsISupports
{

  /**
   * Return true if the "remote" process has |aPermission|.  This is
   * intended to be used by JS implementations of cross-process DOM
   * APIs, like so
   *
   *   recvFooRequest: function(message) {
   *     if (!message.target.assertPermission("foo")) {
   *       return false;
   *     }
   *     // service foo request
   *
   * This interface only returns meaningful data when our content is
   * in a separate process.  If it shares the same OS process as us,
   * then applying this permission check doesn't add any security,
   * though it doesn't hurt anything either.
   *
   * Note: If the remote content process does *not* have |aPermission|,
   * it will be killed as a precaution.
   */
  boolean assertPermission(in DOMString aPermission);

  /**
   * Return true if the "remote" process has |aManifestURL|.  This is
   * intended to be used by JS implementations of cross-process DOM
   * APIs, like so
   *
   *   recvFooRequest: function(message) {
   *     if (!message.target.assertContainApp("foo")) {
   *       return false;
   *     }
   *     // service foo request
   *
   * This interface only returns meaningful data when our content is
   * in a separate process.  If it shares the same OS process as us,
   * then applying this manifest URL check doesn't add any security,
   * though it doesn't hurt anything either.
   *
   * Note: If the remote content process does *not* contain |aManifestURL|,
   * it will be killed as a precaution.
   */
  boolean assertContainApp(in DOMString aManifestURL);

  boolean assertAppHasPermission(in DOMString aPermission);

  /**
   * Return true if the "remote" process' principal has an appStatus equal to
   * |aStatus|.
   *
   * This interface only returns meaningful data when our content is
   * in a separate process.  If it shares the same OS process as us,
   * then applying this permission check doesn't add any security,
   * though it doesn't hurt anything either.
   *
   * Note: If the remote content process does *not* has the |aStatus|,
   * it will be killed as a precaution.
   */
  boolean assertAppHasStatus(in unsigned short aStatus);

};
