testing/marionette/marionette-frame-manager.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 this.EXPORTED_SYMBOLS = [
michael@0 6 "FrameManager"
michael@0 7 ];
michael@0 8
michael@0 9 let FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
michael@0 10 Cu.import("resource://gre/modules/Services.jsm");
michael@0 11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/Log.jsm");
michael@0 14 let logger = Log.repository.getLogger("Marionette");
michael@0 15
michael@0 16 //list of OOP frames that has the frame script loaded
michael@0 17 let remoteFrames = [];
michael@0 18
michael@0 19 /**
michael@0 20 * An object representing a frame that Marionette has loaded a
michael@0 21 * frame script in.
michael@0 22 */
michael@0 23 function MarionetteRemoteFrame(windowId, frameId) {
michael@0 24 this.windowId = windowId; //outerWindowId relative to main process
michael@0 25 this.frameId = frameId ? frameId : null; //actual frame relative to windowId's frames list
michael@0 26 this.targetFrameId = this.frameId; //assigned FrameId, used for messaging
michael@0 27 };
michael@0 28
michael@0 29 /**
michael@0 30 * The FrameManager will maintain the list of Out Of Process (OOP) frames and will handle
michael@0 31 * frame switching between them.
michael@0 32 * It handles explicit frame switching (switchToFrame), and implicit frame switching, which
michael@0 33 * occurs when a modal dialog is triggered in B2G.
michael@0 34 *
michael@0 35 */
michael@0 36 this.FrameManager = function FrameManager(server) {
michael@0 37 //messageManager maintains the messageManager for the current process' chrome frame or the global message manager
michael@0 38 this.currentRemoteFrame = null; //holds a member of remoteFrames (for an OOP frame) or null (for the main process)
michael@0 39 this.previousRemoteFrame = null; //frame we'll need to restore once interrupt is gone
michael@0 40 this.handledModal = false; //set to true when we have been interrupted by a modal
michael@0 41 this.server = server; // a reference to the marionette server
michael@0 42 };
michael@0 43
michael@0 44 FrameManager.prototype = {
michael@0 45 QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
michael@0 46 Ci.nsISupportsWeakReference]),
michael@0 47
michael@0 48 /**
michael@0 49 * Receives all messages from content messageManager
michael@0 50 */
michael@0 51 receiveMessage: function FM_receiveMessage(message) {
michael@0 52 switch (message.name) {
michael@0 53 case "MarionetteFrame:getInterruptedState":
michael@0 54 // This will return true if the calling frame was interrupted by a modal dialog
michael@0 55 if (this.previousRemoteFrame) {
michael@0 56 let interruptedFrame = Services.wm.getOuterWindowWithId(this.previousRemoteFrame.windowId);//get the frame window of the interrupted frame
michael@0 57 if (this.previousRemoteFrame.frameId != null) {
michael@0 58 interruptedFrame = interruptedFrame.document.getElementsByTagName("iframe")[this.previousRemoteFrame.frameId]; //find the OOP frame
michael@0 59 }
michael@0 60 //check if the interrupted frame is the same as the calling frame
michael@0 61 if (interruptedFrame.src == message.target.src) {
michael@0 62 return {value: this.handledModal};
michael@0 63 }
michael@0 64 }
michael@0 65 else if (this.currentRemoteFrame == null) {
michael@0 66 // we get here if previousRemoteFrame and currentRemoteFrame are null, ie: if we're in a non-OOP process, or we haven't switched into an OOP frame, in which case, handledModal can't be set to true.
michael@0 67 return {value: this.handledModal};
michael@0 68 }
michael@0 69 return {value: false};
michael@0 70 case "MarionetteFrame:handleModal":
michael@0 71 /*
michael@0 72 * handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt.
michael@0 73 */
michael@0 74 // If previousRemoteFrame was set, that means we switched into a remote frame.
michael@0 75 // If this is the case, then we want to switch back into the system frame.
michael@0 76 // If it isn't the case, then we're in a non-OOP environment, so we don't need to handle remote frames
michael@0 77 let isLocal = true;
michael@0 78 if (this.currentRemoteFrame != null) {
michael@0 79 isLocal = false;
michael@0 80 this.removeMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
michael@0 81 //store the previous frame so we can switch back to it when the modal is dismissed
michael@0 82 this.previousRemoteFrame = this.currentRemoteFrame;
michael@0 83 //by setting currentRemoteFrame to null, it signifies we're in the main process
michael@0 84 this.currentRemoteFrame = null;
michael@0 85 this.server.messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
michael@0 86 .getService(Ci.nsIMessageBroadcaster);
michael@0 87 }
michael@0 88 this.handledModal = true;
michael@0 89 this.server.sendOk(this.server.command_id);
michael@0 90 return {value: isLocal};
michael@0 91 case "MarionetteFrame:getCurrentFrameId":
michael@0 92 if (this.currentRemoteFrame != null) {
michael@0 93 return this.currentRemoteFrame.frameId;
michael@0 94 }
michael@0 95 }
michael@0 96 },
michael@0 97
michael@0 98 //This is just 'switch to OOP frame'. We're handling this here so we can maintain a list of remoteFrames.
michael@0 99 switchToFrame: function FM_switchToFrame(message) {
michael@0 100 // Switch to a remote frame.
michael@0 101 let frameWindow = Services.wm.getOuterWindowWithId(message.json.win); //get the original frame window
michael@0 102 let oopFrame = frameWindow.document.getElementsByTagName("iframe")[message.json.frame]; //find the OOP frame
michael@0 103 let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; //get the OOP frame's mm
michael@0 104
michael@0 105 // See if this frame already has our frame script loaded in it; if so,
michael@0 106 // just wake it up.
michael@0 107 for (let i = 0; i < remoteFrames.length; i++) {
michael@0 108 let frame = remoteFrames[i];
michael@0 109 let frameMessageManager = frame.messageManager.get();
michael@0 110 logger.info("trying remote frame " + i);
michael@0 111 try {
michael@0 112 frameMessageManager.sendAsyncMessage("aliveCheck", {});
michael@0 113 }
michael@0 114 catch(e) {
michael@0 115 if (e.result == Components.results.NS_ERROR_NOT_INITIALIZED) {
michael@0 116 logger.info("deleting frame");
michael@0 117 remoteFrames.splice(i, 1);
michael@0 118 continue;
michael@0 119 }
michael@0 120 }
michael@0 121 if (frameMessageManager == mm) {
michael@0 122 this.currentRemoteFrame = frame;
michael@0 123 this.addMessageManagerListeners(mm);
michael@0 124 mm.sendAsyncMessage("Marionette:restart", {});
michael@0 125 return;
michael@0 126 }
michael@0 127 }
michael@0 128
michael@0 129 // If we get here, then we need to load the frame script in this frame,
michael@0 130 // and set the frame's ChromeMessageSender as the active message manager the server will listen to
michael@0 131 this.addMessageManagerListeners(mm);
michael@0 132 logger.info("frame-manager load script: " + mm.toString());
michael@0 133 mm.loadFrameScript(FRAME_SCRIPT, true, true);
michael@0 134 let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
michael@0 135 aFrame.messageManager = Cu.getWeakReference(mm);
michael@0 136 remoteFrames.push(aFrame);
michael@0 137 this.currentRemoteFrame = aFrame;
michael@0 138 },
michael@0 139
michael@0 140 /*
michael@0 141 * This function handles switching back to the frame that was interrupted by the modal dialog.
michael@0 142 * This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
michael@0 143 */
michael@0 144 switchToModalOrigin: function FM_switchToModalOrigin() {
michael@0 145 //only handle this if we indeed switched out of the modal's originating frame
michael@0 146 if (this.previousRemoteFrame != null) {
michael@0 147 this.currentRemoteFrame = this.previousRemoteFrame;
michael@0 148 this.addMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
michael@0 149 }
michael@0 150 this.handledModal = false;
michael@0 151 },
michael@0 152
michael@0 153 /**
michael@0 154 * Adds message listeners to the server, listening for messages from content frame scripts.
michael@0 155 * It also adds a "MarionetteFrame:getInterruptedState" message listener to the FrameManager,
michael@0 156 * so the frame manager's state can be checked by the frame
michael@0 157 *
michael@0 158 * @param object messageManager
michael@0 159 * The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
michael@0 160 * to which the listeners should be added.
michael@0 161 */
michael@0 162 addMessageManagerListeners: function MDA_addMessageManagerListeners(messageManager) {
michael@0 163 messageManager.addWeakMessageListener("Marionette:ok", this.server);
michael@0 164 messageManager.addWeakMessageListener("Marionette:done", this.server);
michael@0 165 messageManager.addWeakMessageListener("Marionette:error", this.server);
michael@0 166 messageManager.addWeakMessageListener("Marionette:emitTouchEvent", this.server);
michael@0 167 messageManager.addWeakMessageListener("Marionette:log", this.server);
michael@0 168 messageManager.addWeakMessageListener("Marionette:register", this.server);
michael@0 169 messageManager.addWeakMessageListener("Marionette:runEmulatorCmd", this.server);
michael@0 170 messageManager.addWeakMessageListener("Marionette:runEmulatorShell", this.server);
michael@0 171 messageManager.addWeakMessageListener("Marionette:shareData", this.server);
michael@0 172 messageManager.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
michael@0 173 messageManager.addWeakMessageListener("Marionette:switchToFrame", this.server);
michael@0 174 messageManager.addWeakMessageListener("Marionette:switchedToFrame", this.server);
michael@0 175 messageManager.addWeakMessageListener("MarionetteFrame:handleModal", this);
michael@0 176 messageManager.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
michael@0 177 messageManager.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
michael@0 178 },
michael@0 179
michael@0 180 /**
michael@0 181 * Removes listeners for messages from content frame scripts.
michael@0 182 * We do not remove the "MarionetteFrame:getInterruptedState" or the
michael@0 183 * "Marioentte:switchToModalOrigin" message listener,
michael@0 184 * because we want to allow all known frames to contact the frame manager so that
michael@0 185 * it can check if it was interrupted, and if so, it will call switchToModalOrigin
michael@0 186 * when its process gets resumed.
michael@0 187 *
michael@0 188 * @param object messageManager
michael@0 189 * The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
michael@0 190 * from which the listeners should be removed.
michael@0 191 */
michael@0 192 removeMessageManagerListeners: function MDA_removeMessageManagerListeners(messageManager) {
michael@0 193 messageManager.removeWeakMessageListener("Marionette:ok", this.server);
michael@0 194 messageManager.removeWeakMessageListener("Marionette:done", this.server);
michael@0 195 messageManager.removeWeakMessageListener("Marionette:error", this.server);
michael@0 196 messageManager.removeWeakMessageListener("Marionette:log", this.server);
michael@0 197 messageManager.removeWeakMessageListener("Marionette:shareData", this.server);
michael@0 198 messageManager.removeWeakMessageListener("Marionette:register", this.server);
michael@0 199 messageManager.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server);
michael@0 200 messageManager.removeWeakMessageListener("Marionette:runEmulatorShell", this.server);
michael@0 201 messageManager.removeWeakMessageListener("Marionette:switchToFrame", this.server);
michael@0 202 messageManager.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
michael@0 203 messageManager.removeWeakMessageListener("MarionetteFrame:handleModal", this);
michael@0 204 messageManager.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
michael@0 205 },
michael@0 206
michael@0 207 };

mercurial