/* -*- 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 nsIDOMDOMStringList; 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 * [, ]. */ [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); };