gecko/testing/marionette/frame-manager.js
Carsten "Tomcat" Book 8fa824bcaf Backed out 16 changesets (bug 1245153) for mochitest test-bustage on a CLOSED TREE
Backed out changeset 30c8ec933737 (bug 1245153)
Backed out changeset 4c2b1902d7cd (bug 1245153)
Backed out changeset 1be5f60393a0 (bug 1245153)
Backed out changeset 22321e6b65e9 (bug 1245153)
Backed out changeset c1e0abbfa66e (bug 1245153)
Backed out changeset 80ae953819c8 (bug 1245153)
Backed out changeset 3edb67388ad6 (bug 1245153)
Backed out changeset 55f64197f6b5 (bug 1245153)
Backed out changeset 6bb93562a576 (bug 1245153)
Backed out changeset 5da7628c3767 (bug 1245153)
Backed out changeset bd41e4ab829d (bug 1245153)
Backed out changeset ee7ee24cc65e (bug 1245153)
Backed out changeset 4b2a5ee7199e (bug 1245153)
Backed out changeset d75ad1397656 (bug 1245153)
Backed out changeset 1f5e37f8e446 (bug 1245153)
Backed out changeset aec0a0166685 (bug 1245153)
2016-02-05 14:18:19 +01:00

231 lines
10 KiB
JavaScript

/* 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/. */
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
this.EXPORTED_SYMBOLS = ["FrameManager"];
var FRAME_SCRIPT = "chrome://marionette/content/listener.js";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
//list of OOP frames that has the frame script loaded
var remoteFrames = [];
/**
* An object representing a frame that Marionette has loaded a
* frame script in.
*/
function MarionetteRemoteFrame(windowId, frameId) {
this.windowId = windowId; //outerWindowId relative to main process
this.frameId = frameId; //actual frame relative to windowId's frames list
this.targetFrameId = this.frameId; //assigned FrameId, used for messaging
};
/**
* The FrameManager will maintain the list of Out Of Process (OOP) frames and will handle
* frame switching between them.
* It handles explicit frame switching (switchToFrame), and implicit frame switching, which
* occurs when a modal dialog is triggered in B2G.
*
*/
this.FrameManager = function FrameManager(server) {
//messageManager maintains the messageManager for the current process' chrome frame or the global message manager
this.currentRemoteFrame = null; //holds a member of remoteFrames (for an OOP frame) or null (for the main process)
this.previousRemoteFrame = null; //frame we'll need to restore once interrupt is gone
this.handledModal = false; //set to true when we have been interrupted by a modal
this.server = server; // a reference to the marionette server
};
FrameManager.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsISupportsWeakReference]),
/**
* Receives all messages from content messageManager
*/
receiveMessage: function FM_receiveMessage(message) {
switch (message.name) {
case "MarionetteFrame:getInterruptedState":
// This will return true if the calling frame was interrupted by a modal dialog
if (this.previousRemoteFrame) {
let interruptedFrame = Services.wm.getOuterWindowWithId(this.previousRemoteFrame.windowId);//get the frame window of the interrupted frame
if (this.previousRemoteFrame.frameId != null) {
interruptedFrame = interruptedFrame.document.getElementsByTagName("iframe")[this.previousRemoteFrame.frameId]; //find the OOP frame
}
//check if the interrupted frame is the same as the calling frame
if (interruptedFrame.src == message.target.src) {
return {value: this.handledModal};
}
}
else if (this.currentRemoteFrame == null) {
// 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.
return {value: this.handledModal};
}
return {value: false};
case "MarionetteFrame:handleModal":
/*
* handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt.
*/
// If previousRemoteFrame was set, that means we switched into a remote frame.
// If this is the case, then we want to switch back into the system frame.
// If it isn't the case, then we're in a non-OOP environment, so we don't need to handle remote frames
let isLocal = true;
if (this.currentRemoteFrame != null) {
isLocal = false;
this.removeMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
//store the previous frame so we can switch back to it when the modal is dismissed
this.previousRemoteFrame = this.currentRemoteFrame;
//by setting currentRemoteFrame to null, it signifies we're in the main process
this.currentRemoteFrame = null;
this.server.messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
}
this.handledModal = true;
this.server.sendOk(this.server.command_id);
return {value: isLocal};
case "MarionetteFrame:getCurrentFrameId":
if (this.currentRemoteFrame != null) {
return this.currentRemoteFrame.frameId;
}
}
},
getOopFrame: function FM_getOopFrame(winId, frameId) {
// get original frame window
let outerWin = Services.wm.getOuterWindowWithId(winId);
// find the OOP frame
let f = outerWin.document.getElementsByTagName("iframe")[frameId];
return f;
},
getFrameMM: function FM_getFrameMM(winId, frameId) {
let oopFrame = this.getOopFrame(winId, frameId);
let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader.messageManager;
return mm;
},
/**
* Switch to OOP frame. We're handling this here
* so we can maintain a list of remote frames.
*/
switchToFrame: function FM_switchToFrame(winId, frameId) {
let oopFrame = this.getOopFrame(winId, frameId);
let mm = this.getFrameMM(winId, frameId);
// See if this frame already has our frame script loaded in it;
// if so, just wake it up.
for (let i = 0; i < remoteFrames.length; i++) {
let frame = remoteFrames[i];
let frameMessageManager = frame.messageManager.get();
try {
frameMessageManager.sendAsyncMessage("aliveCheck", {});
} catch (e) {
if (e.result == Components.results.NS_ERROR_NOT_INITIALIZED) {
remoteFrames.splice(i--, 1);
continue;
}
}
if (frameMessageManager == mm) {
this.currentRemoteFrame = frame;
this.addMessageManagerListeners(mm);
mm.sendAsyncMessage("Marionette:restart");
return oopFrame.id;
}
}
// If we get here, then we need to load the frame script in this frame,
// and set the frame's ChromeMessageSender as the active message manager
// the server will listen to.
this.addMessageManagerListeners(mm);
let aFrame = new MarionetteRemoteFrame(winId, frameId);
aFrame.messageManager = Cu.getWeakReference(mm);
remoteFrames.push(aFrame);
this.currentRemoteFrame = aFrame;
mm.loadFrameScript(FRAME_SCRIPT, true, true);
return oopFrame.id;
},
/*
* This function handles switching back to the frame that was interrupted by the modal dialog.
* This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
*/
switchToModalOrigin: function FM_switchToModalOrigin() {
//only handle this if we indeed switched out of the modal's originating frame
if (this.previousRemoteFrame != null) {
this.currentRemoteFrame = this.previousRemoteFrame;
this.addMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
}
this.handledModal = false;
},
/**
* Adds message listeners to the server,
* listening for messages from content frame scripts.
* It also adds a MarionetteFrame:getInterruptedState
* message listener to the FrameManager,
* so the frame manager's state can be checked by the frame.
*
* @param {nsIMessageListenerManager} mm
* The message manager object, typically
* ChromeMessageBroadcaster or ChromeMessageSender.
*/
addMessageManagerListeners: function FM_addMessageManagerListeners(mm) {
mm.addWeakMessageListener("Marionette:ok", this.server);
mm.addWeakMessageListener("Marionette:done", this.server);
mm.addWeakMessageListener("Marionette:error", this.server);
mm.addWeakMessageListener("Marionette:emitTouchEvent", this.server);
mm.addWeakMessageListener("Marionette:log", this.server);
mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.server.emulator);
mm.addWeakMessageListener("Marionette:runEmulatorShell", this.server.emulator);
mm.addWeakMessageListener("Marionette:shareData", this.server);
mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
mm.addWeakMessageListener("Marionette:switchedToFrame", this.server);
mm.addWeakMessageListener("Marionette:getVisibleCookies", this.server);
mm.addWeakMessageListener("Marionette:register", this.server);
mm.addWeakMessageListener("Marionette:listenersAttached", this.server);
mm.addWeakMessageListener("Marionette:getFiles", this.server);
mm.addWeakMessageListener("MarionetteFrame:handleModal", this);
mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
},
/**
* Removes listeners for messages from content frame scripts.
* We do not remove the MarionetteFrame:getInterruptedState
* or the Marionette:switchToModalOrigin message listener,
* because we want to allow all known frames to contact the frame manager
* so that it can check if it was interrupted, and if so,
* it will call switchToModalOrigin when its process gets resumed.
*
* @param {nsIMessageListenerManager} mm
* The message manager object, typically
* ChromeMessageBroadcaster or ChromeMessageSender.
*/
removeMessageManagerListeners: function FM_removeMessageManagerListeners(mm) {
mm.removeWeakMessageListener("Marionette:ok", this.server);
mm.removeWeakMessageListener("Marionette:done", this.server);
mm.removeWeakMessageListener("Marionette:error", this.server);
mm.removeWeakMessageListener("Marionette:log", this.server);
mm.removeWeakMessageListener("Marionette:shareData", this.server);
mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server.emulator);
mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.server.emulator);
mm.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.server);
mm.removeWeakMessageListener("Marionette:listenersAttached", this.server);
mm.removeWeakMessageListener("Marionette:register", this.server);
mm.removeWeakMessageListener("Marionette:getFiles", this.server);
mm.removeWeakMessageListener("MarionetteFrame:handleModal", this);
mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
}
};