michael@0: /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsISupports.idl" michael@0: michael@0: interface nsIDOMWindow; michael@0: interface nsIDocShell; michael@0: interface nsIContent; michael@0: interface nsIPrincipal; michael@0: michael@0: /** michael@0: * Message managers provide a way for chrome-privileged JS code to michael@0: * communicate with each other, even across process boundaries. michael@0: * michael@0: * Message managers are separated into "parent side" and "child side". michael@0: * These don't always correspond to process boundaries, but can. For michael@0: * each child-side message manager, there is always exactly one michael@0: * corresponding parent-side message manager that it sends messages michael@0: * to. However, for each parent-side message manager, there may be michael@0: * either one or many child-side managers it can message. michael@0: * michael@0: * Message managers that always have exactly one "other side" are of michael@0: * type nsIMessageSender. Parent-side message managers that have many michael@0: * "other sides" are of type nsIMessageBroadcaster. michael@0: * michael@0: * Child-side message managers can send synchronous messages to their michael@0: * parent side, but not the other way around. michael@0: * michael@0: * There are two realms of message manager hierarchies. One realm michael@0: * approximately corresponds to DOM elements, the other corresponds to michael@0: * process boundaries. michael@0: * michael@0: * Message managers corresponding to DOM elements michael@0: * ============================================== michael@0: * michael@0: * In this realm of message managers, there are michael@0: * - "frame message managers" which correspond to frame elements michael@0: * - "window message managers" which correspond to top-level chrome michael@0: * windows michael@0: * - the "global message manager", on the parent side. See below. michael@0: * michael@0: * The DOM-realm message managers can communicate in the ways shown by michael@0: * the following diagram. The parent side and child side can michael@0: * correspond to process boundaries, but don't always. michael@0: * michael@0: * Parent side Child side michael@0: * ------------- ------------ michael@0: * global MMg michael@0: * | michael@0: * +-->window MMw1 michael@0: * | | michael@0: * | +-->frame MMp1_1<------------>frame MMc1_1 michael@0: * | | michael@0: * | +-->frame MMp1_2<------------>frame MMc1_2 michael@0: * | ... michael@0: * | michael@0: * +-->window MMw2 michael@0: * ... michael@0: * michael@0: * For example: a message sent from MMc1_1, from the child side, is michael@0: * sent only to MMp1_1 on the parent side. However, note that all michael@0: * message managers in the hierarchy above MMp1_1, in this diagram michael@0: * MMw1 and MMg, will also notify their message listeners when the michael@0: * message arrives. michael@0: michael@0: * For example: a message broadcast through the global MMg on the michael@0: * parent side would be broadcast to MMw1, which would transitively michael@0: * broadcast it to MMp1_1, MM1p_2". The message would next be michael@0: * broadcast to MMw2, and so on down the hierarchy. michael@0: * michael@0: * ***** PERFORMANCE AND SECURITY WARNING ***** michael@0: * Messages broadcast through the global MM and window MMs can result michael@0: * in messages being dispatched across many OS processes, and to many michael@0: * processes with different permissions. Great care should be taken michael@0: * when broadcasting. michael@0: * michael@0: * Interfaces michael@0: * ---------- michael@0: * michael@0: * The global MMg and window MMw's are message broadcasters implementing michael@0: * nsIMessageBroadcaster while the frame MMp's are simple message senders michael@0: * (nsIMessageSender). Their counterparts in the content processes are michael@0: * message senders implementing nsIContentFrameMessageManager. michael@0: * michael@0: * nsIMessageListenerManager michael@0: * / \ michael@0: * nsIMessageSender nsIMessageBroadcaster michael@0: * | michael@0: * nsISyncMessageSender (content process/in-process only) michael@0: * | michael@0: * nsIContentFrameMessageManager (content process/in-process only) michael@0: * | michael@0: * nsIInProcessContentFrameMessageManager (in-process only) michael@0: * michael@0: * michael@0: * Message managers in the chrome process can also be QI'ed to nsIFrameScriptLoader. michael@0: * michael@0: * michael@0: * Message managers corresponding to process boundaries michael@0: * ==================================================== michael@0: * michael@0: * The second realm of message managers is the "process message michael@0: * managers". With one exception, these always correspond to process michael@0: * boundaries. The picture looks like michael@0: * michael@0: * Parent process Child processes michael@0: * ---------------- ----------------- michael@0: * global PPMM michael@0: * | michael@0: * +<----> child PPMM michael@0: * | michael@0: * +-->parent PMM1<------------------>child process CMM1 michael@0: * | michael@0: * +-->parent PMM2<------------------>child process PMM2 michael@0: * ... michael@0: * michael@0: * For example: the parent-process PMM1 sends messages directly to michael@0: * only the child-process CMM1. michael@0: * michael@0: * For example: CMM1 sends messages directly to PMM1. The global PPMM michael@0: * will also notify their message listeners when the message arrives. michael@0: * michael@0: * For example: messages sent through the global PPMM will be michael@0: * dispatched to the listeners of the same-process, "child PPMM". michael@0: * They will also be broadcast to PPM1, PPM2, etc. michael@0: * michael@0: * ***** PERFORMANCE AND SECURITY WARNING ***** michael@0: * Messages broadcast through the global PPMM can result in messages michael@0: * being dispatched across many OS processes, and to many processes michael@0: * with different permissions. Great care should be taken when michael@0: * broadcasting. michael@0: * michael@0: * Requests sent to parent-process message listeners should usually michael@0: * have replies scoped to the requesting CPMM. The following pattern michael@0: * is common michael@0: * michael@0: * const ParentProcessListener = { michael@0: * receiveMessage: function(aMessage) { michael@0: * let childMM = aMessage.target.QueryInterface(Ci.nsIMessageSender); michael@0: * switch (aMessage.name) { michael@0: * case "Foo:Request": michael@0: * // service request michael@0: * childMM.sendAsyncMessage("Foo:Response", { data }); michael@0: * } michael@0: * } michael@0: * }; michael@0: */ michael@0: michael@0: [scriptable, function, uuid(2b44eb57-a9c6-4773-9a1e-fe0818739a4c)] michael@0: interface nsIMessageListener : nsISupports michael@0: { michael@0: /** michael@0: * This is for JS only. michael@0: * receiveMessage is called with one parameter, which has the following michael@0: * properties: michael@0: * { michael@0: * target: %the target of the message. Either an element owning michael@0: * the message manager, or message manager itself if no michael@0: * element owns it% michael@0: * name: %message name%, michael@0: * sync: %true or false%. michael@0: * data: %structured clone of the sent message data%, michael@0: * json: %same as .data, deprecated%, michael@0: * objects: %named table of jsvals/objects, or null% michael@0: * principal: %principal for the window app michael@0: * } michael@0: * michael@0: * Each listener is invoked with its own copy of the message michael@0: * parameter. michael@0: * michael@0: * When the listener is called, 'this' value is the target of the message. michael@0: * michael@0: * If the message is synchronous, the possible return value is michael@0: * returned as JSON (will be changed to use structured clones). michael@0: * When there are multiple listeners to sync messages, each michael@0: * listener's return value is sent back as an array. |undefined| michael@0: * return values show up as undefined values in the array. michael@0: */ michael@0: void receiveMessage(); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(aae827bd-acf1-45fe-a556-ea545d4c0804)] michael@0: interface nsIMessageListenerManager : nsISupports michael@0: { michael@0: /** michael@0: * Register |listener| to receive |messageName|. All listener michael@0: * callbacks for a particular message are invoked when that message michael@0: * is received. michael@0: * michael@0: * The message manager holds a strong ref to |listener|. michael@0: * michael@0: * If the same listener registers twice for the same message, the michael@0: * second registration is ignored. michael@0: */ michael@0: void addMessageListener(in AString messageName, michael@0: in nsIMessageListener listener); michael@0: michael@0: /** michael@0: * Undo an |addMessageListener| call -- that is, calling this causes us to no michael@0: * longer invoke |listener| when |messageName| is received. michael@0: * michael@0: * removeMessageListener does not remove a message listener added via michael@0: * addWeakMessageListener; use removeWeakMessageListener for that. michael@0: */ michael@0: void removeMessageListener(in AString messageName, michael@0: in nsIMessageListener listener); michael@0: michael@0: /** michael@0: * This is just like addMessageListener, except the message manager holds a michael@0: * weak ref to |listener|. michael@0: * michael@0: * If you have two weak message listeners for the same message, they may be michael@0: * called in any order. michael@0: */ michael@0: void addWeakMessageListener(in AString messageName, michael@0: in nsIMessageListener listener); michael@0: michael@0: /** michael@0: * This undoes an |addWeakMessageListener| call. michael@0: */ michael@0: void removeWeakMessageListener(in AString messageName, michael@0: in nsIMessageListener listener); michael@0: michael@0: [notxpcom] boolean markForCC(); michael@0: }; michael@0: michael@0: /** michael@0: * Message "senders" have a single "other side" to which messages are michael@0: * sent. For example, a child-process message manager will send michael@0: * messages that are only delivered to its one parent-process message michael@0: * manager. michael@0: */ michael@0: [scriptable, builtinclass, uuid(d6b0d851-43e6-426d-9f13-054bc0198175)] michael@0: interface nsIMessageSender : nsIMessageListenerManager michael@0: { michael@0: /** michael@0: * Send |messageName| and |obj| to the "other side" of this message michael@0: * manager. This invokes listeners who registered for michael@0: * |messageName|. michael@0: * michael@0: * See nsIMessageListener::receiveMessage() for the format of the michael@0: * data delivered to listeners. michael@0: * @throws NS_ERROR_NOT_INITIALIZED if the sender is not initialized. For michael@0: * example, we will throw NS_ERROR_NOT_INITIALIZED if we try to send michael@0: * a message to a cross-process frame but the other process has not michael@0: * yet been set up. michael@0: * @throws NS_ERROR_FAILURE when the message receiver cannot be found. For michael@0: * example, we will throw NS_ERROR_FAILURE if we try to send a message michael@0: * to a cross-process frame whose process has crashed. michael@0: */ michael@0: [implicit_jscontext, optional_argc] michael@0: void sendAsyncMessage([optional] in AString messageName, michael@0: [optional] in jsval obj, michael@0: [optional] in jsval objects, michael@0: [optional] in nsIPrincipal principal); michael@0: }; michael@0: michael@0: /** michael@0: * Message "broadcasters" don't have a single "other side" that they michael@0: * send messages to, but rather a set of subordinate message managers. michael@0: * For example, broadcasting a message through a window message michael@0: * manager will broadcast the message to all frame message managers michael@0: * within its window. michael@0: */ michael@0: [scriptable, builtinclass, uuid(d36346b9-5d3b-497d-9c28-ffbc3e4f6d0d)] michael@0: interface nsIMessageBroadcaster : nsIMessageListenerManager michael@0: { michael@0: /** michael@0: * Like |sendAsyncMessage()|, but also broadcasts this message to michael@0: * all "child" message managers of this message manager. See long michael@0: * comment above for details. michael@0: * michael@0: * WARNING: broadcasting messages can be very expensive and leak michael@0: * sensitive data. Use with extreme caution. michael@0: */ michael@0: [implicit_jscontext, optional_argc] michael@0: void broadcastAsyncMessage([optional] in AString messageName, michael@0: [optional] in jsval obj, michael@0: [optional] in jsval objects); michael@0: michael@0: /** michael@0: * Number of subordinate message managers. michael@0: */ michael@0: readonly attribute unsigned long childCount; michael@0: michael@0: /** michael@0: * Return a single subordinate message manager. michael@0: */ michael@0: nsIMessageListenerManager getChildAt(in unsigned long aIndex); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(7fda0941-9dcc-448b-bd39-16373c5b4003)] michael@0: interface nsISyncMessageSender : nsIMessageSender michael@0: { michael@0: /** michael@0: * Like |sendAsyncMessage()|, except blocks the sender until all michael@0: * listeners of the message have been invoked. Returns an array michael@0: * containing return values from each listener invoked. michael@0: */ michael@0: [implicit_jscontext, optional_argc] michael@0: jsval sendSyncMessage([optional] in AString messageName, michael@0: [optional] in jsval obj, michael@0: [optional] in jsval objects, michael@0: [optional] in nsIPrincipal principal); michael@0: michael@0: /** michael@0: * Like |sendSyncMessage()|, except re-entrant. New RPC messages may be michael@0: * issued even if, earlier on the call stack, we are waiting for a reply michael@0: * to an earlier sendRpcMessage() call. michael@0: * michael@0: * Both sendSyncMessage and sendRpcMessage will block until a reply is michael@0: * received, but they may be temporarily interrupted to process an urgent michael@0: * incoming message (such as a CPOW request). michael@0: */ michael@0: [implicit_jscontext, optional_argc] michael@0: jsval sendRpcMessage([optional] in AString messageName, michael@0: [optional] in jsval obj, michael@0: [optional] in jsval objects, michael@0: [optional] in nsIPrincipal principal); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(894ff2d4-39a3-4df8-9d76-8ee329975488)] michael@0: interface nsIContentFrameMessageManager : nsISyncMessageSender michael@0: { michael@0: /** michael@0: * The current top level window in the frame or null. michael@0: */ michael@0: readonly attribute nsIDOMWindow content; michael@0: michael@0: /** michael@0: * The top level docshell or null. michael@0: */ michael@0: readonly attribute nsIDocShell docShell; michael@0: michael@0: /** michael@0: * Print a string to stdout. michael@0: */ michael@0: void dump(in DOMString aStr); michael@0: michael@0: /** michael@0: * If leak detection is enabled, print a note to the leak log that this michael@0: * process will intentionally crash. michael@0: */ michael@0: void privateNoteIntentionalCrash(); michael@0: michael@0: /** michael@0: * Ascii base64 data to binary data and vice versa michael@0: */ michael@0: DOMString atob(in DOMString aAsciiString); michael@0: DOMString btoa(in DOMString aBase64Data); michael@0: }; michael@0: michael@0: [uuid(a2325927-9c0c-437d-9215-749c79235031)] michael@0: interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager michael@0: { michael@0: [notxpcom] nsIContent getOwnerContent(); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(6fb78110-45ae-11e3-8f96-0800200c9a66)] michael@0: interface nsIFrameScriptLoader : nsISupports michael@0: { michael@0: /** michael@0: * Load a script in the (remote) frame. aURL must be the absolute URL. michael@0: * data: URLs are also supported. For example data:,dump("foo\n"); michael@0: * If aAllowDelayedLoad is true, script will be loaded when the michael@0: * remote frame becomes available. Otherwise the script will be loaded michael@0: * only if the frame is already available. michael@0: */ michael@0: void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad, michael@0: [optional] in boolean aRunInGlobalScope); michael@0: michael@0: /** michael@0: * Removes aURL from the list of scripts which support delayed load. michael@0: */ michael@0: void removeDelayedFrameScript(in AString aURL); michael@0: michael@0: /** michael@0: * Returns all delayed scripts that will be loaded once a (remote) michael@0: * frame becomes available. The return value is a list of pairs michael@0: * [, ]. michael@0: */ michael@0: [implicit_jscontext] michael@0: jsval getDelayedFrameScripts(); michael@0: }; michael@0: michael@0: [scriptable, builtinclass, uuid(ad57800b-ff21-4e2f-91d3-e68615ae8afe)] michael@0: interface nsIProcessChecker : nsISupports michael@0: { michael@0: michael@0: /** michael@0: * Return true if the "remote" process has |aPermission|. This is michael@0: * intended to be used by JS implementations of cross-process DOM michael@0: * APIs, like so michael@0: * michael@0: * recvFooRequest: function(message) { michael@0: * if (!message.target.assertPermission("foo")) { michael@0: * return false; michael@0: * } michael@0: * // service foo request michael@0: * michael@0: * This interface only returns meaningful data when our content is michael@0: * in a separate process. If it shares the same OS process as us, michael@0: * then applying this permission check doesn't add any security, michael@0: * though it doesn't hurt anything either. michael@0: * michael@0: * Note: If the remote content process does *not* have |aPermission|, michael@0: * it will be killed as a precaution. michael@0: */ michael@0: boolean assertPermission(in DOMString aPermission); michael@0: michael@0: /** michael@0: * Return true if the "remote" process has |aManifestURL|. This is michael@0: * intended to be used by JS implementations of cross-process DOM michael@0: * APIs, like so michael@0: * michael@0: * recvFooRequest: function(message) { michael@0: * if (!message.target.assertContainApp("foo")) { michael@0: * return false; michael@0: * } michael@0: * // service foo request michael@0: * michael@0: * This interface only returns meaningful data when our content is michael@0: * in a separate process. If it shares the same OS process as us, michael@0: * then applying this manifest URL check doesn't add any security, michael@0: * though it doesn't hurt anything either. michael@0: * michael@0: * Note: If the remote content process does *not* contain |aManifestURL|, michael@0: * it will be killed as a precaution. michael@0: */ michael@0: boolean assertContainApp(in DOMString aManifestURL); michael@0: michael@0: boolean assertAppHasPermission(in DOMString aPermission); michael@0: michael@0: /** michael@0: * Return true if the "remote" process' principal has an appStatus equal to michael@0: * |aStatus|. michael@0: * michael@0: * This interface only returns meaningful data when our content is michael@0: * in a separate process. If it shares the same OS process as us, michael@0: * then applying this permission check doesn't add any security, michael@0: * though it doesn't hurt anything either. michael@0: * michael@0: * Note: If the remote content process does *not* has the |aStatus|, michael@0: * it will be killed as a precaution. michael@0: */ michael@0: boolean assertAppHasStatus(in unsigned short aStatus); michael@0: michael@0: };