/* -*- 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 mozIDOMWindowProxy; interface nsIDocShell; interface nsIContent; interface nsIFrameLoader; 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 * - "group message managers" which correspond to named message * managers with a specific window MM as the parent * - 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 * | | * | +-->group MMgr1 * | | | * | | +-->frame MMp2_1<------->frame MMc2_1 * | | | * | | +-->frame MMp2_2<------->frame MMc2_2 * | | * | +-->group MMgr2 * | | ... * | | * | ... * | * +-->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. * * A message sent from MMc2_1 will be sent to MMp2_1 and also notify * all message managers in the hierarchy above that, including the * group message manager MMgr1. * 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 MMgr1, which would broadcast it to MMp2_1 and MMp2_2. * After that it would broadcast to MMgr2 and then to MMw2, and so * on down the hierarchy. * * ***** PERFORMANCE AND SECURITY WARNING ***** * Messages broadcast through the global MM and window or group 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 (GPPMM) * | * +-->parent in-process PIPMM<-->child in-process CIPPMM * | * +-->parent (PPMM1)<------------------>child (CPMM1) * | * +-->parent (PPMM2)<------------------>child (CPMM2) * ... * * Note, PIPMM and CIPPMM both run in the parent process. * * For example: the parent-process PPMM1 sends messages to the * child-process CPMM1. * * For example: CPMM1 sends messages directly to PPMM1. The global GPPMM * will also notify their message listeners when the message arrives. * * For example: messages sent through the global GPPMM will be * dispatched to the listeners of the same-process, CIPPMM, CPMM1, * CPMM2, etc. * * ***** PERFORMANCE AND SECURITY WARNING ***** * Messages broadcast through the GPPMM 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(b949bfec-bb7d-47bc-b387-ac6a9b655072)] 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. * * Pass true for listenWhenClosed if you want to receive messages * during the short period after a frame has been removed from the * DOM and before its frame script has finished unloading. This * parameter only has an effect for frame message managers in * the main process. Default is false. */ void addMessageListener(in AString messageName, in nsIMessageListener listener, [optional] in boolean listenWhenClosed); /** * 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(bb5d79e4-e73c-45e7-9651-4d718f4b994c)] 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(4d7d62ad-4725-4f39-86cf-8fb22bf9c1d8)] 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(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)] 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(13f3555f-769e-44ea-b607-5239230c3162)] interface nsIMessageManagerGlobal : nsISyncMessageSender { /** * 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); }; [scriptable, builtinclass, uuid(694e367c-aa25-4446-8499-2c527c4bd838)] interface nsIContentFrameMessageManager : nsIMessageManagerGlobal { /** * The current top level window in the frame or null. */ readonly attribute mozIDOMWindowProxy content; /** * The top level docshell or null. */ readonly attribute nsIDocShell docShell; }; [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)] interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager { [notxpcom] nsIContent getOwnerContent(); [notxpcom] void cacheFrameLoader(in nsIFrameLoader aFrameLoader); }; [scriptable, builtinclass, uuid(6d12e467-2446-46db-9965-e4e93cb87ca5)] interface nsIContentProcessMessageManager : nsIMessageManagerGlobal { /** * Read out a copy of the object that was initialized in the parent * process via nsIProcessScriptLoader.initialProcessData. */ [implicit_jscontext] readonly attribute jsval initialProcessData; }; [scriptable, builtinclass, uuid(bf61446b-ba24-4b1d-88c7-4f94724b9ce1)] 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 * [, ]. */ [implicit_jscontext] jsval getDelayedFrameScripts(); }; [scriptable, builtinclass, uuid(7e1e1a20-b24f-11e4-ab27-0800200c9a66)] interface nsIProcessScriptLoader : nsISupports { /** * Load a script in the (possibly remote) process. 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 loadProcessScript(in AString aURL, in boolean aAllowDelayedLoad); /** * Removes aURL from the list of scripts which support delayed load. */ void removeDelayedProcessScript(in AString aURL); /** * Returns all delayed scripts that will be loaded once a (remote) * frame becomes available. The return value is a list of URLs. */ [implicit_jscontext] jsval getDelayedProcessScripts(); }; [scriptable, builtinclass, uuid(5b390753-abb3-49b0-ae3b-b803dab58144)] interface nsIGlobalProcessScriptLoader : nsIProcessScriptLoader { /** * Allows the parent process to set the initial process data for * new, not-yet-created child processes. This attribute should only * be used by the global parent process message manager. When a new * process is created, it gets a copy of this data (via structured * cloning). It can access the data via the initialProcessData * attribute of its childprocessmessagemanager. * * This value will always be a JS object. Different users are * expected to set properties on this object. The property name * should be unique enough that other Gecko consumers won't * accidentally choose it. */ [implicit_jscontext] readonly attribute jsval initialProcessData; }; [scriptable, builtinclass, uuid(637e8538-4f8f-4a3d-8510-e74386233e19)] interface nsIProcessChecker : nsISupports { bool killChild(); /** * 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); };