mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 20:49:27 +00:00
Bug 779284 - Implement B2G Modal dialog handling to Marionette, r=jgriffin,mdas
This commit is contained in:
parent
aff2242ca9
commit
00bfcedc58
@ -46,6 +46,36 @@ class TestSwitchRemoteFrame(MarionetteTestCase):
|
||||
""")
|
||||
self.assertFalse(main_process)
|
||||
|
||||
def test_remote_frame_revisit(self):
|
||||
# test if we can revisit a remote frame (this takes a different codepath)
|
||||
self.marionette.navigate(self.marionette.absolute_url("test.html"))
|
||||
self.marionette.execute_script("SpecialPowers.addPermission('browser', true, document)")
|
||||
self.marionette.execute_script("""
|
||||
let iframe = document.createElement("iframe");
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
SpecialPowers.wrap(iframe).remote = true;
|
||||
iframe.id = "remote_iframe";
|
||||
iframe.style.height = "100px";
|
||||
iframe.style.width = "100%%";
|
||||
iframe.src = "%s";
|
||||
document.body.appendChild(iframe);
|
||||
""" % self.marionette.absolute_url("test.html"))
|
||||
self.marionette.switch_to_frame("remote_iframe")
|
||||
main_process = self.marionette.execute_script("""
|
||||
return SpecialPowers.isMainProcess();
|
||||
""")
|
||||
self.assertFalse(main_process)
|
||||
self.marionette.switch_to_frame()
|
||||
main_process = self.marionette.execute_script("""
|
||||
return SpecialPowers.isMainProcess();
|
||||
""")
|
||||
self.assertTrue(main_process)
|
||||
self.marionette.switch_to_frame("remote_iframe")
|
||||
main_process = self.marionette.execute_script("""
|
||||
return SpecialPowers.isMainProcess();
|
||||
""")
|
||||
self.assertFalse(main_process)
|
||||
|
||||
def tearDown(self):
|
||||
if self.oop_by_default is None:
|
||||
self.marionette.execute_script("""
|
||||
|
@ -10,6 +10,7 @@ marionette.jar:
|
||||
content/marionette-sendkeys.js (marionette-sendkeys.js)
|
||||
content/marionette-common.js (marionette-common.js)
|
||||
content/marionette-simpletest.js (marionette-simpletest.js)
|
||||
content/marionette-frame-manager.js (marionette-frame-manager.js)
|
||||
content/EventUtils.js (EventUtils.js)
|
||||
content/ChromeUtils.js (ChromeUtils.js)
|
||||
#ifdef ENABLE_TESTS
|
||||
|
198
testing/marionette/marionette-frame-manager.js
Normal file
198
testing/marionette/marionette-frame-manager.js
Normal file
@ -0,0 +1,198 @@
|
||||
/* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"FrameManager"
|
||||
];
|
||||
|
||||
let FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/services-common/log4moz.js");
|
||||
let logger = Log4Moz.repository.getLogger("Marionette");
|
||||
|
||||
//list of OOP frames that has the frame script loaded
|
||||
let 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 ? frameId : null; //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};
|
||||
}
|
||||
},
|
||||
|
||||
//This is just 'switch to OOP frame'. We're handling this here so we can maintain a list of remoteFrames.
|
||||
switchToFrame: function FM_switchToFrame(message) {
|
||||
// Switch to a remote frame.
|
||||
let frameWindow = Services.wm.getOuterWindowWithId(message.json.win); //get the original frame window
|
||||
let oopFrame = frameWindow.document.getElementsByTagName("iframe")[message.json.frame]; //find the OOP frame
|
||||
let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; //get the OOP frame's mm
|
||||
|
||||
// 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();
|
||||
logger.info("trying remote frame " + i);
|
||||
try {
|
||||
frameMessageManager.sendAsyncMessage("aliveCheck", {});
|
||||
}
|
||||
catch(e) {
|
||||
if (e.result == Components.results.NS_ERROR_NOT_INITIALIZED) {
|
||||
logger.info("deleting frame");
|
||||
remoteFrames.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (frameMessageManager == mm) {
|
||||
this.currentRemoteFrame = frame;
|
||||
this.addMessageManagerListeners(mm);
|
||||
mm.sendAsyncMessage("Marionette:restart", {});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
logger.info("frame-manager load script: " + mm.toString());
|
||||
mm.loadFrameScript(FRAME_SCRIPT, true);
|
||||
let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
|
||||
aFrame.messageManager = Cu.getWeakReference(mm);
|
||||
remoteFrames.push(aFrame);
|
||||
this.currentRemoteFrame = aFrame;
|
||||
},
|
||||
|
||||
/*
|
||||
* 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 object messageManager
|
||||
* The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
|
||||
* to which the listeners should be added.
|
||||
*/
|
||||
addMessageManagerListeners: function MDA_addMessageManagerListeners(messageManager) {
|
||||
messageManager.addWeakMessageListener("Marionette:ok", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:done", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:error", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:log", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:shareData", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:register", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:runEmulatorCmd", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:switchToFrame", this.server);
|
||||
messageManager.addWeakMessageListener("Marionette:switchedToFrame", this.server);
|
||||
messageManager.addWeakMessageListener("MarionetteFrame:handleModal", this);
|
||||
messageManager.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listeners for messages from content frame scripts.
|
||||
* We do not remove the "MarionetteFrame:getInterruptedState" or the
|
||||
* "Marioentte: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 object messageManager
|
||||
* The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
|
||||
* from which the listeners should be removed.
|
||||
*/
|
||||
removeMessageManagerListeners: function MDA_removeMessageManagerListeners(messageManager) {
|
||||
messageManager.removeWeakMessageListener("Marionette:ok", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:done", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:error", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:log", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:shareData", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:register", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:switchToFrame", this.server);
|
||||
messageManager.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
|
||||
messageManager.removeWeakMessageListener("MarionetteFrame:handleModal", this);
|
||||
},
|
||||
|
||||
};
|
@ -36,8 +36,8 @@ let marionetteTestName;
|
||||
let winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let listenerId = null; //unique ID of this listener
|
||||
let activeFrame = null;
|
||||
let curWindow = content;
|
||||
let curFrame = content;
|
||||
let previousFrame = null;
|
||||
let elementManager = new ElementManager([]);
|
||||
let importedScripts = null;
|
||||
|
||||
@ -75,6 +75,15 @@ let mouseEventsOnly = false;
|
||||
Cu.import("resource://gre/modules/services-common/log4moz.js");
|
||||
let logger = Log4Moz.repository.getLogger("Marionette");
|
||||
logger.info("loaded marionette-listener.js");
|
||||
let modalHandler = function() {
|
||||
sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true });
|
||||
let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
|
||||
if (isLocal) {
|
||||
previousFrame = curFrame;
|
||||
}
|
||||
curFrame = content;
|
||||
sandbox = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when listener is first started up.
|
||||
@ -160,6 +169,7 @@ function startListeners() {
|
||||
function newSession(msg) {
|
||||
isB2G = msg.json.B2G;
|
||||
resetValues();
|
||||
content.addEventListener("mozbrowsershowmodalprompt", modalHandler, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,6 +187,7 @@ function sleepSession(msg) {
|
||||
*/
|
||||
function restart(msg) {
|
||||
removeMessageListener("Marionette:restart", restart);
|
||||
content.addEventListener("mozbrowsershowmodalprompt", modalHandler, false);
|
||||
registerSelf();
|
||||
}
|
||||
|
||||
@ -224,10 +235,11 @@ function deleteSession(msg) {
|
||||
removeMessageListenerId("Marionette:getAllCookies", getAllCookies);
|
||||
removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookies);
|
||||
removeMessageListenerId("Marionette:deleteCookie", deleteCookie);
|
||||
content.removeEventListener("mozbrowsershowmodalprompt", modalHandler, false);
|
||||
this.elementManager.reset();
|
||||
// reset frame to the top-most frame
|
||||
curWindow = content;
|
||||
curWindow.focus();
|
||||
curFrame = content;
|
||||
curFrame.focus();
|
||||
touchIds = {};
|
||||
}
|
||||
|
||||
@ -279,7 +291,7 @@ function sendError(message, status, trace, command_id) {
|
||||
*/
|
||||
function resetValues() {
|
||||
sandbox = null;
|
||||
curWindow = content;
|
||||
curFrame = content;
|
||||
mouseEventsOnly = false;
|
||||
}
|
||||
|
||||
@ -290,6 +302,22 @@ function dumpLog(logline) {
|
||||
dump(Date.now() + " Marionette: " + logline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our context was interrupted
|
||||
*/
|
||||
function wasInterrupted() {
|
||||
if (previousFrame) {
|
||||
let element = content.document.elementFromPoint((content.innerWidth/2), (content.innerHeight/2));
|
||||
if (element.id.indexOf("modal-dialog") == -1) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return sendSyncMessage("MarionetteFrame:getInterruptedState", {})[0].value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Marionette Methods
|
||||
*/
|
||||
@ -326,11 +354,11 @@ function createExecuteContentSandbox(aWindow, timeout) {
|
||||
|
||||
sandbox.asyncComplete = function sandbox_asyncComplete(value, status, stack, commandId) {
|
||||
if (commandId == asyncTestCommandId) {
|
||||
curWindow.removeEventListener("unload", onunload, false);
|
||||
curWindow.clearTimeout(asyncTestTimeoutId);
|
||||
curFrame.removeEventListener("unload", onunload, false);
|
||||
curFrame.clearTimeout(asyncTestTimeoutId);
|
||||
|
||||
if (inactivityTimeoutId != null) {
|
||||
curWindow.clearTimeout(inactivityTimeoutId);
|
||||
curFrame.clearTimeout(inactivityTimeoutId);
|
||||
}
|
||||
|
||||
|
||||
@ -381,14 +409,14 @@ function executeScript(msg, directInject) {
|
||||
// Set up inactivity timeout.
|
||||
if (msg.json.inactivityTimeout) {
|
||||
let setTimer = function() {
|
||||
inactivityTimeoutId = curWindow.setTimeout(function() {
|
||||
inactivityTimeoutId = curFrame.setTimeout(function() {
|
||||
sendError('timed out due to inactivity', 28, null, asyncTestCommandId);
|
||||
}, msg.json.inactivityTimeout);
|
||||
};
|
||||
|
||||
setTimer();
|
||||
heartbeatCallback = function resetInactivityTimeout() {
|
||||
curWindow.clearTimeout(inactivityTimeoutId);
|
||||
curFrame.clearTimeout(inactivityTimeoutId);
|
||||
setTimer();
|
||||
};
|
||||
}
|
||||
@ -397,7 +425,7 @@ function executeScript(msg, directInject) {
|
||||
let script = msg.json.value;
|
||||
|
||||
if (msg.json.newSandbox || !sandbox) {
|
||||
sandbox = createExecuteContentSandbox(curWindow,
|
||||
sandbox = createExecuteContentSandbox(curFrame,
|
||||
msg.json.timeout);
|
||||
if (!sandbox) {
|
||||
sendError("Could not create sandbox!", 500, null, asyncTestCommandId);
|
||||
@ -432,7 +460,7 @@ function executeScript(msg, directInject) {
|
||||
else {
|
||||
try {
|
||||
sandbox.__marionetteParams = elementManager.convertWrappedArguments(
|
||||
msg.json.args, curWindow);
|
||||
msg.json.args, curFrame);
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.code, e.stack, asyncTestCommandId);
|
||||
@ -505,14 +533,14 @@ function executeWithCallback(msg, useFinish) {
|
||||
// Set up inactivity timeout.
|
||||
if (msg.json.inactivityTimeout) {
|
||||
let setTimer = function() {
|
||||
inactivityTimeoutId = curWindow.setTimeout(function() {
|
||||
inactivityTimeoutId = curFrame.setTimeout(function() {
|
||||
sandbox.asyncComplete('timed out due to inactivity', 28, null, asyncTestCommandId);
|
||||
}, msg.json.inactivityTimeout);
|
||||
};
|
||||
|
||||
setTimer();
|
||||
heartbeatCallback = function resetInactivityTimeout() {
|
||||
curWindow.clearTimeout(inactivityTimeoutId);
|
||||
curFrame.clearTimeout(inactivityTimeoutId);
|
||||
setTimer();
|
||||
};
|
||||
}
|
||||
@ -523,10 +551,10 @@ function executeWithCallback(msg, useFinish) {
|
||||
onunload = function() {
|
||||
sendError("unload was called", 17, null, asyncTestCommandId);
|
||||
};
|
||||
curWindow.addEventListener("unload", onunload, false);
|
||||
curFrame.addEventListener("unload", onunload, false);
|
||||
|
||||
if (msg.json.newSandbox || !sandbox) {
|
||||
sandbox = createExecuteContentSandbox(curWindow,
|
||||
sandbox = createExecuteContentSandbox(curFrame,
|
||||
msg.json.timeout);
|
||||
if (!sandbox) {
|
||||
sendError("Could not create sandbox!", 17, null, asyncTestCommandId);
|
||||
@ -543,14 +571,14 @@ function executeWithCallback(msg, useFinish) {
|
||||
// However Selenium code returns 28, see
|
||||
// http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js.
|
||||
// We'll stay compatible with the Selenium code.
|
||||
asyncTestTimeoutId = curWindow.setTimeout(function() {
|
||||
asyncTestTimeoutId = curFrame.setTimeout(function() {
|
||||
sandbox.asyncComplete('timed out', 28, null, asyncTestCommandId);
|
||||
}, msg.json.timeout);
|
||||
|
||||
originalOnError = curWindow.onerror;
|
||||
curWindow.onerror = function errHandler(errMsg, url, line) {
|
||||
originalOnError = curFrame.onerror;
|
||||
curFrame.onerror = function errHandler(errMsg, url, line) {
|
||||
sandbox.asyncComplete(errMsg, 17, "@" + url + ", line " + line, asyncTestCommandId);
|
||||
curWindow.onerror = originalOnError;
|
||||
curFrame.onerror = originalOnError;
|
||||
};
|
||||
|
||||
let scriptSrc;
|
||||
@ -563,7 +591,7 @@ function executeWithCallback(msg, useFinish) {
|
||||
else {
|
||||
try {
|
||||
sandbox.__marionetteParams = elementManager.convertWrappedArguments(
|
||||
msg.json.args, curWindow);
|
||||
msg.json.args, curFrame);
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.code, e.stack, asyncTestCommandId);
|
||||
@ -600,17 +628,19 @@ function executeWithCallback(msg, useFinish) {
|
||||
* This function creates a touch event given a touch type and a touch
|
||||
*/
|
||||
function emitTouchEvent(type, touch) {
|
||||
let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport";
|
||||
dumpLog(loggingInfo);
|
||||
/*
|
||||
Disabled per bug 888303
|
||||
marionetteLogObj.log(loggingInfo, "TRACE");
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
*/
|
||||
let domWindowUtils = curWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.screenX], [touch.screenY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
|
||||
if (!wasInterrupted()) {
|
||||
let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport";
|
||||
dumpLog(loggingInfo);
|
||||
/*
|
||||
Disabled per bug 888303
|
||||
marionetteLogObj.log(loggingInfo, "TRACE");
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
*/
|
||||
let domWindowUtils = curFrame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.screenX], [touch.screenY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -621,20 +651,22 @@ function emitTouchEvent(type, touch) {
|
||||
* elClientX and elClientY are the coordinates of the mouse relative to the viewport
|
||||
*/
|
||||
function emitMouseEvent(doc, type, elClientX, elClientY, detail, button) {
|
||||
let loggingInfo = "emitting Mouse event of type " + type + " at coordinates (" + elClientX + ", " + elClientY + ") relative to the viewport";
|
||||
dumpLog(loggingInfo);
|
||||
/*
|
||||
Disabled per bug 888303
|
||||
marionetteLogObj.log(loggingInfo, "TRACE");
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
*/
|
||||
detail = detail || 1;
|
||||
button = button || 0;
|
||||
let win = doc.defaultView;
|
||||
// Figure out the element the mouse would be over at (x, y)
|
||||
utils.synthesizeMouseAtPoint(elClientX, elClientY, {type: type, button: button, clickCount: detail}, win);
|
||||
if (!wasInterrupted()) {
|
||||
let loggingInfo = "emitting Mouse event of type " + type + " at coordinates (" + elClientX + ", " + elClientY + ") relative to the viewport";
|
||||
dumpLog(loggingInfo);
|
||||
/*
|
||||
Disabled per bug 888303
|
||||
marionetteLogObj.log(loggingInfo, "TRACE");
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
*/
|
||||
detail = detail || 1;
|
||||
button = button || 0;
|
||||
let win = doc.defaultView;
|
||||
// Figure out the element the mouse would be over at (x, y)
|
||||
utils.synthesizeMouseAtPoint(elClientX, elClientY, {type: type, button: button, clickCount: detail}, win);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -672,14 +704,14 @@ function coordinates(target, x, y) {
|
||||
*/
|
||||
function elementInViewport(el) {
|
||||
let rect = el.getBoundingClientRect();
|
||||
let viewPort = {top: curWindow.pageYOffset,
|
||||
left: curWindow.pageXOffset,
|
||||
bottom: (curWindow.pageYOffset + curWindow.innerHeight),
|
||||
right:(curWindow.pageXOffset + curWindow.innerWidth)};
|
||||
return (viewPort.left <= rect.right + curWindow.pageXOffset &&
|
||||
rect.left + curWindow.pageXOffset <= viewPort.right &&
|
||||
viewPort.top <= rect.bottom + curWindow.pageYOffset &&
|
||||
rect.top + curWindow.pageYOffset <= viewPort.bottom);
|
||||
let viewPort = {top: curFrame.pageYOffset,
|
||||
left: curFrame.pageXOffset,
|
||||
bottom: (curFrame.pageYOffset + curFrame.innerHeight),
|
||||
right:(curFrame.pageXOffset + curFrame.innerWidth)};
|
||||
return (viewPort.left <= rect.right + curFrame.pageXOffset &&
|
||||
rect.left + curFrame.pageXOffset <= viewPort.right &&
|
||||
viewPort.top <= rect.bottom + curFrame.pageYOffset &&
|
||||
rect.top + curFrame.pageYOffset <= viewPort.bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -712,7 +744,7 @@ function checkVisible(el) {
|
||||
//x and y are coordinates relative to the viewport
|
||||
function generateEvents(type, x, y, touchId, target) {
|
||||
lastCoordinates = [x, y];
|
||||
let doc = curWindow.document;
|
||||
let doc = curFrame.document;
|
||||
switch (type) {
|
||||
case 'tap':
|
||||
if (mouseEventsOnly) {
|
||||
@ -781,7 +813,7 @@ function generateEvents(type, x, y, touchId, target) {
|
||||
break;
|
||||
case 'contextmenu':
|
||||
isTap = false;
|
||||
let event = curWindow.document.createEvent('HTMLEvents');
|
||||
let event = curFrame.document.createEvent('HTMLEvents');
|
||||
event.initEvent('contextmenu', true, true);
|
||||
if (mouseEventsOnly) {
|
||||
target = doc.elementFromPoint(lastCoordinates[0], lastCoordinates[1]);
|
||||
@ -794,6 +826,19 @@ function generateEvents(type, x, y, touchId, target) {
|
||||
default:
|
||||
throw {message:"Unknown event type: " + type, code: 500, stack:null};
|
||||
}
|
||||
if (wasInterrupted()) {
|
||||
if (previousFrame) {
|
||||
//if previousFrame is set, then we're in a single process environment
|
||||
curFrame = previousFrame;
|
||||
previousFrame = null;
|
||||
sandbox = null;
|
||||
}
|
||||
else {
|
||||
//else we're in OOP environment, so we'll switch to the original OOP frame
|
||||
sendSyncMessage("Marionette:switchToModalOrigin");
|
||||
}
|
||||
sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -802,13 +847,13 @@ function generateEvents(type, x, y, touchId, target) {
|
||||
function singleTap(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.value, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.value, curFrame);
|
||||
// after this block, the element will be scrolled into view
|
||||
if (!checkVisible(el)) {
|
||||
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
||||
return;
|
||||
}
|
||||
if (!curWindow.document.createTouch) {
|
||||
if (!curFrame.document.createTouch) {
|
||||
mouseEventsOnly = true;
|
||||
}
|
||||
let c = coordinates(el, msg.json.corx, msg.json.cory);
|
||||
@ -868,7 +913,7 @@ function actions(chain, touchId, command_id, i) {
|
||||
sendError("Invalid Command: press cannot follow an active touch event", 500, null, command_id);
|
||||
return;
|
||||
}
|
||||
el = elementManager.getKnownElement(pack[1], curWindow);
|
||||
el = elementManager.getKnownElement(pack[1], curFrame);
|
||||
if (!checkVisible(el)) {
|
||||
sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
|
||||
return;
|
||||
@ -882,7 +927,7 @@ function actions(chain, touchId, command_id, i) {
|
||||
actions(chain, null, command_id, i);
|
||||
break;
|
||||
case 'move':
|
||||
el = elementManager.getKnownElement(pack[1], curWindow);
|
||||
el = elementManager.getKnownElement(pack[1], curFrame);
|
||||
c = coordinates(el);
|
||||
generateEvents('move', c.x, c.y, touchId);
|
||||
actions(chain, touchId, command_id, i);
|
||||
@ -929,12 +974,12 @@ function actionChain(msg) {
|
||||
let args = msg.json.chain;
|
||||
let touchId = msg.json.nextId;
|
||||
try {
|
||||
let commandArray = elementManager.convertWrappedArguments(args, curWindow);
|
||||
let commandArray = elementManager.convertWrappedArguments(args, curFrame);
|
||||
// loop the action array [ ['press', id], ['move', id], ['release', id] ]
|
||||
if (touchId == null) {
|
||||
touchId = nextTouchId++;
|
||||
}
|
||||
if (!curWindow.document.createTouch) {
|
||||
if (!curFrame.document.createTouch) {
|
||||
mouseEventsOnly = true;
|
||||
}
|
||||
actions(commandArray, touchId, command_id);
|
||||
@ -963,7 +1008,7 @@ function emitMultiEvents(type, touch, touches) {
|
||||
// Create changed touches
|
||||
let changedTouches = doc.createTouchList(touch);
|
||||
// Create the event object
|
||||
let event = curWindow.document.createEvent('TouchEvent');
|
||||
let event = curFrame.document.createEvent('TouchEvent');
|
||||
event.initTouchEvent(type,
|
||||
true,
|
||||
true,
|
||||
@ -1016,7 +1061,7 @@ function setDispatch(batches, touches, command_id, batchIndex) {
|
||||
command = pack[1];
|
||||
switch (command) {
|
||||
case 'press':
|
||||
el = elementManager.getKnownElement(pack[2], curWindow);
|
||||
el = elementManager.getKnownElement(pack[2], curFrame);
|
||||
c = coordinates(el, pack[3], pack[4]);
|
||||
touch = createATouch(el, c.x, c.y, touchId);
|
||||
multiLast[touchId] = touch;
|
||||
@ -1031,7 +1076,7 @@ function setDispatch(batches, touches, command_id, batchIndex) {
|
||||
emitMultiEvents('touchend', touch, touches);
|
||||
break;
|
||||
case 'move':
|
||||
el = elementManager.getKnownElement(pack[2], curWindow);
|
||||
el = elementManager.getKnownElement(pack[2], curFrame);
|
||||
c = coordinates(el);
|
||||
touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId);
|
||||
touchIndex = touches.indexOf(lastTouch);
|
||||
@ -1085,7 +1130,7 @@ function multiAction(msg) {
|
||||
let maxlen = msg.json.maxlen;
|
||||
try {
|
||||
// unwrap the original nested array
|
||||
let commandArray = elementManager.convertWrappedArguments(args, curWindow);
|
||||
let commandArray = elementManager.convertWrappedArguments(args, curFrame);
|
||||
let concurrentEvent = [];
|
||||
let temp;
|
||||
for (let i = 0; i < maxlen; i++) {
|
||||
@ -1127,11 +1172,11 @@ function goUrl(msg) {
|
||||
let errorRegex = /about:.+(error)|(blocked)\?/;
|
||||
let elapse = end - start;
|
||||
if (msg.json.pageTimeout == null || elapse <= msg.json.pageTimeout){
|
||||
if (curWindow.document.readyState == "complete"){
|
||||
if (curFrame.document.readyState == "complete"){
|
||||
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
|
||||
sendOk(command_id);
|
||||
}
|
||||
else if (curWindow.document.readyState == "interactive" && errorRegex.exec(curWindow.document.baseURI)){
|
||||
else if (curFrame.document.readyState == "interactive" && errorRegex.exec(curFrame.document.baseURI)){
|
||||
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
|
||||
sendError("Error loading page", 13, null, command_id);
|
||||
}
|
||||
@ -1149,7 +1194,7 @@ function goUrl(msg) {
|
||||
// window (i.e., someone has used switch_to_frame).
|
||||
let onDOMContentLoaded = function onDOMContentLoaded(event){
|
||||
if (!event.originalTarget.defaultView.frameElement ||
|
||||
event.originalTarget.defaultView.frameElement == curWindow.frameElement) {
|
||||
event.originalTarget.defaultView.frameElement == curFrame.frameElement) {
|
||||
checkLoad();
|
||||
}
|
||||
};
|
||||
@ -1162,29 +1207,29 @@ function goUrl(msg) {
|
||||
checkTimer.initWithCallback(timerFunc, msg.json.pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
addEventListener("DOMContentLoaded", onDOMContentLoaded, false);
|
||||
curWindow.location = msg.json.value;
|
||||
curFrame.location = msg.json.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current URI
|
||||
*/
|
||||
function getUrl(msg) {
|
||||
sendResponse({value: curWindow.location.href}, msg.json.command_id);
|
||||
sendResponse({value: curFrame.location.href}, msg.json.command_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Title of the window
|
||||
*/
|
||||
function getTitle(msg) {
|
||||
sendResponse({value: curWindow.top.document.title}, msg.json.command_id);
|
||||
sendResponse({value: curFrame.top.document.title}, msg.json.command_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page source
|
||||
*/
|
||||
function getPageSource(msg) {
|
||||
var XMLSerializer = curWindow.XMLSerializer;
|
||||
var pageSource = new XMLSerializer().serializeToString(curWindow.document);
|
||||
var XMLSerializer = curFrame.XMLSerializer;
|
||||
var pageSource = new XMLSerializer().serializeToString(curFrame.document);
|
||||
sendResponse({value: pageSource}, msg.json.command_id);
|
||||
}
|
||||
|
||||
@ -1192,7 +1237,7 @@ function getPageSource(msg) {
|
||||
* Go back in history
|
||||
*/
|
||||
function goBack(msg) {
|
||||
curWindow.history.back();
|
||||
curFrame.history.back();
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
|
||||
@ -1200,7 +1245,7 @@ function goBack(msg) {
|
||||
* Go forward in history
|
||||
*/
|
||||
function goForward(msg) {
|
||||
curWindow.history.forward();
|
||||
curFrame.history.forward();
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
|
||||
@ -1209,7 +1254,7 @@ function goForward(msg) {
|
||||
*/
|
||||
function refresh(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
curWindow.location.reload(true);
|
||||
curFrame.location.reload(true);
|
||||
let listen = function() {
|
||||
removeEventListener("DOMContentLoaded", arguments.callee, false);
|
||||
sendOk(command_id);
|
||||
@ -1225,7 +1270,7 @@ function findElementContent(msg) {
|
||||
try {
|
||||
let on_success = function(id, cmd_id) { sendResponse({value:id}, cmd_id); };
|
||||
let on_error = sendError;
|
||||
elementManager.find(curWindow, msg.json, msg.json.searchTimeout,
|
||||
elementManager.find(curFrame, msg.json, msg.json.searchTimeout,
|
||||
on_success, on_error, false, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1241,7 +1286,7 @@ function findElementsContent(msg) {
|
||||
try {
|
||||
let on_success = function(id, cmd_id) { sendResponse({value:id}, cmd_id); };
|
||||
let on_error = sendError;
|
||||
elementManager.find(curWindow, msg.json, msg.json.searchTimeout,
|
||||
elementManager.find(curFrame, msg.json, msg.json.searchTimeout,
|
||||
on_success, on_error, true, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1254,7 +1299,7 @@ function findElementsContent(msg) {
|
||||
*/
|
||||
function getActiveElement(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
var element = curWindow.document.activeElement;
|
||||
var element = curFrame.document.activeElement;
|
||||
var id = elementManager.addToKnownElements(element);
|
||||
sendResponse({value: id}, command_id);
|
||||
}
|
||||
@ -1266,7 +1311,7 @@ function clickElement(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
let el;
|
||||
try {
|
||||
el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
if (checkVisible(el)) {
|
||||
if (utils.isElementEnabled(el)) {
|
||||
utils.synthesizeMouseAtCenter(el, {}, el.ownerDocument.defaultView)
|
||||
@ -1291,7 +1336,7 @@ function clickElement(msg) {
|
||||
function getElementAttribute(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: utils.getElementAttribute(el, msg.json.name)},
|
||||
command_id);
|
||||
}
|
||||
@ -1306,7 +1351,7 @@ function getElementAttribute(msg) {
|
||||
function getElementText(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: utils.getElementText(el)}, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1320,7 +1365,7 @@ function getElementText(msg) {
|
||||
function getElementTagName(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: el.tagName.toLowerCase()}, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1334,7 +1379,7 @@ function getElementTagName(msg) {
|
||||
function isElementDisplayed(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: utils.isElementDisplayed(el)}, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1354,8 +1399,8 @@ function getElementValueOfCssProperty(msg){
|
||||
let command_id = msg.json.command_id;
|
||||
let propertyName = msg.json.propertyName;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: curWindow.document.defaultView.getComputedStyle(el, null).getPropertyValue(propertyName)},
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: curFrame.document.defaultView.getComputedStyle(el, null).getPropertyValue(propertyName)},
|
||||
command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1369,7 +1414,7 @@ function getElementValueOfCssProperty(msg){
|
||||
function getElementSize(msg){
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
let clientRect = el.getBoundingClientRect();
|
||||
sendResponse({value: {width: clientRect.width, height: clientRect.height}},
|
||||
command_id);
|
||||
@ -1385,7 +1430,7 @@ function getElementSize(msg){
|
||||
function isElementEnabled(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: utils.isElementEnabled(el)}, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1399,7 +1444,7 @@ function isElementEnabled(msg) {
|
||||
function isElementSelected(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
sendResponse({value: utils.isElementSelected(el)}, command_id);
|
||||
}
|
||||
catch (e) {
|
||||
@ -1413,9 +1458,9 @@ function isElementSelected(msg) {
|
||||
function sendKeysToElement(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
if (checkVisible(el)) {
|
||||
utils.type(curWindow.document, el, msg.json.value.join(""), true);
|
||||
utils.type(curFrame.document, el, msg.json.value.join(""), true);
|
||||
sendOk(command_id);
|
||||
}
|
||||
else {
|
||||
@ -1433,7 +1478,7 @@ function sendKeysToElement(msg) {
|
||||
function getElementPosition(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try{
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
let rect = el.getBoundingClientRect();
|
||||
|
||||
let location = {};
|
||||
@ -1453,7 +1498,7 @@ function getElementPosition(msg) {
|
||||
function clearElement(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curFrame);
|
||||
utils.clearElement(el);
|
||||
sendOk(command_id);
|
||||
}
|
||||
@ -1468,27 +1513,27 @@ function clearElement(msg) {
|
||||
*/
|
||||
function switchToFrame(msg) {
|
||||
let command_id = msg.json.command_id;
|
||||
function checkLoad() {
|
||||
function checkLoad() {
|
||||
let errorRegex = /about:.+(error)|(blocked)\?/;
|
||||
if (curWindow.document.readyState == "complete") {
|
||||
if (curFrame.document.readyState == "complete") {
|
||||
sendOk(command_id);
|
||||
return;
|
||||
}
|
||||
else if (curWindow.document.readyState == "interactive" && errorRegex.exec(curWindow.document.baseURI)) {
|
||||
else if (curFrame.document.readyState == "interactive" && errorRegex.exec(curFrame.document.baseURI)) {
|
||||
sendError("Error loading page", 13, null, command_id);
|
||||
return;
|
||||
}
|
||||
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
let foundFrame = null;
|
||||
let frames = []; //curWindow.document.getElementsByTagName("iframe");
|
||||
let parWindow = null; //curWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
// Check of the curWindow reference is dead
|
||||
let frames = []; //curFrame.document.getElementsByTagName("iframe");
|
||||
let parWindow = null; //curFrame.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
// Check of the curFrame reference is dead
|
||||
try {
|
||||
frames = curWindow.document.getElementsByTagName("iframe");
|
||||
frames = curFrame.document.getElementsByTagName("iframe");
|
||||
//Until Bug 761935 lands, we won't have multiple nested OOP iframes. We will only have one.
|
||||
//parWindow will refer to the iframe above the nested OOP frame.
|
||||
parWindow = curWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
parWindow = curFrame.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
||||
} catch (e) {
|
||||
// We probably have a dead compartment so accessing it is going to make Firefox
|
||||
@ -1501,9 +1546,9 @@ function switchToFrame(msg) {
|
||||
// returning to root frame
|
||||
sendSyncMessage("Marionette:switchedToFrame", { frameValue: null });
|
||||
|
||||
curWindow = content;
|
||||
curFrame = content;
|
||||
if(msg.json.focus == true) {
|
||||
curWindow.focus();
|
||||
curFrame.focus();
|
||||
}
|
||||
sandbox = null;
|
||||
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
@ -1513,7 +1558,7 @@ function switchToFrame(msg) {
|
||||
if (elementManager.seenItems[msg.json.element] != undefined) {
|
||||
let wantedFrame;
|
||||
try {
|
||||
wantedFrame = elementManager.getKnownElement(msg.json.element, curWindow); //HTMLIFrameElement
|
||||
wantedFrame = elementManager.getKnownElement(msg.json.element, curFrame); //HTMLIFrameElement
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.code, e.stack, command_id);
|
||||
@ -1521,7 +1566,7 @@ function switchToFrame(msg) {
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
// use XPCNativeWrapper to compare elements; see bug 834266
|
||||
if (XPCNativeWrapper(frames[i]) == XPCNativeWrapper(wantedFrame)) {
|
||||
curWindow = frames[i];
|
||||
curFrame = frames[i];
|
||||
foundFrame = i;
|
||||
}
|
||||
}
|
||||
@ -1545,13 +1590,13 @@ function switchToFrame(msg) {
|
||||
}
|
||||
if ((foundFrame == null) && (foundById != null)) {
|
||||
foundFrame = foundById;
|
||||
curWindow = frames[foundFrame];
|
||||
curFrame = frames[foundFrame];
|
||||
}
|
||||
break;
|
||||
case "number":
|
||||
if (frames[msg.json.value] != undefined) {
|
||||
foundFrame = msg.json.value;
|
||||
curWindow = frames[foundFrame];
|
||||
curFrame = frames[foundFrame];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1565,21 +1610,21 @@ function switchToFrame(msg) {
|
||||
|
||||
// send a synchronous message to let the server update the currently active
|
||||
// frame element (for getActiveFrame)
|
||||
let frameValue = elementManager.wrapValue(curWindow.wrappedJSObject)['ELEMENT'];
|
||||
let frameValue = elementManager.wrapValue(curFrame.wrappedJSObject)['ELEMENT'];
|
||||
sendSyncMessage("Marionette:switchedToFrame", { frameValue: frameValue });
|
||||
|
||||
if (curWindow.contentWindow == null) {
|
||||
if (curFrame.contentWindow == null) {
|
||||
// The frame we want to switch to is a remote (out-of-process) frame;
|
||||
// notify our parent to handle the switch.
|
||||
curWindow = content;
|
||||
curFrame = content;
|
||||
sendToServer('Marionette:switchToFrame', {frame: foundFrame,
|
||||
win: parWindow,
|
||||
command_id: command_id});
|
||||
}
|
||||
else {
|
||||
curWindow = curWindow.contentWindow;
|
||||
curFrame = curFrame.contentWindow;
|
||||
if(msg.json.focus == true) {
|
||||
curWindow.focus();
|
||||
curFrame.focus();
|
||||
}
|
||||
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
@ -1598,11 +1643,11 @@ function addCookie(msg) {
|
||||
}
|
||||
|
||||
if (!cookie.domain) {
|
||||
var location = curWindow.document.location;
|
||||
var location = curFrame.document.location;
|
||||
cookie.domain = location.hostname;
|
||||
}
|
||||
else {
|
||||
var currLocation = curWindow.location;
|
||||
var currLocation = curFrame.location;
|
||||
var currDomain = currLocation.host;
|
||||
if (currDomain.indexOf(cookie.domain) == -1) {
|
||||
sendError("You may only set cookies for the current domain", 24, null, msg.json.command_id);
|
||||
@ -1616,7 +1661,7 @@ function addCookie(msg) {
|
||||
cookie.domain = cookie.domain.replace(/:\d+$/, '');
|
||||
}
|
||||
|
||||
var document = curWindow.document;
|
||||
var document = curFrame.document;
|
||||
if (!document || !document.contentType.match(/html/i)) {
|
||||
sendError('You may only set cookies on html documents', 25, null, msg.json.command_id);
|
||||
}
|
||||
@ -1632,7 +1677,7 @@ function addCookie(msg) {
|
||||
*/
|
||||
function getAllCookies(msg) {
|
||||
var toReturn = [];
|
||||
var cookies = getVisibleCookies(curWindow.location);
|
||||
var cookies = getVisibleCookies(curFrame.location);
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = cookies[i];
|
||||
var expires = cookie.expires;
|
||||
@ -1662,7 +1707,7 @@ function deleteCookie(msg) {
|
||||
var cookieManager = Cc['@mozilla.org/cookiemanager;1'].
|
||||
getService(Ci.nsICookieManager);
|
||||
|
||||
var cookies = getVisibleCookies(curWindow.location);
|
||||
var cookies = getVisibleCookies(curFrame.location);
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = cookies[i];
|
||||
if (cookie.name == toDelete) {
|
||||
@ -1679,7 +1724,7 @@ function deleteCookie(msg) {
|
||||
function deleteAllCookies(msg) {
|
||||
let cookieManager = Cc['@mozilla.org/cookiemanager;1'].
|
||||
getService(Ci.nsICookieManager);
|
||||
let cookies = getVisibleCookies(curWindow.location);
|
||||
let cookies = getVisibleCookies(curFrame.location);
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
let cookie = cookies[i];
|
||||
cookieManager.remove(cookie.host, cookie.name, cookie.path, false);
|
||||
@ -1720,7 +1765,7 @@ function getVisibleCookies(location) {
|
||||
}
|
||||
|
||||
function getAppCacheStatus(msg) {
|
||||
sendResponse({ value: curWindow.applicationCache.status },
|
||||
sendResponse({ value: curFrame.applicationCache.status },
|
||||
msg.json.command_id);
|
||||
}
|
||||
|
||||
@ -1782,7 +1827,7 @@ function screenShot(msg) {
|
||||
let node = null;
|
||||
if (msg.json.element) {
|
||||
try {
|
||||
node = elementManager.getKnownElement(msg.json.element, curWindow)
|
||||
node = elementManager.getKnownElement(msg.json.element, curFrame)
|
||||
}
|
||||
catch (e) {
|
||||
sendResponse(e.message, e.code, e.stack, msg.json.command_id);
|
||||
@ -1790,14 +1835,14 @@ function screenShot(msg) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
node = curWindow;
|
||||
node = curFrame;
|
||||
}
|
||||
let highlights = msg.json.highlights;
|
||||
|
||||
var document = curWindow.document;
|
||||
var document = curFrame.document;
|
||||
var rect, win, width, height, left, top;
|
||||
// node can be either a window or an arbitrary DOM node
|
||||
if (node == curWindow) {
|
||||
if (node == curFrame) {
|
||||
// node is a window
|
||||
win = node;
|
||||
width = win.innerWidth;
|
||||
@ -1829,7 +1874,7 @@ function screenShot(msg) {
|
||||
ctx.save();
|
||||
|
||||
for (var i = 0; i < highlights.length; ++i) {
|
||||
var elem = elementManager.getKnownElement(highlights[i], curWindow)
|
||||
var elem = elementManager.getKnownElement(highlights[i], curFrame)
|
||||
rect = elem.getBoundingClientRect();
|
||||
|
||||
var offsetY = -top;
|
||||
|
@ -15,6 +15,8 @@ let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-simpletest.js");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-common.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-frame-manager.js");
|
||||
Cu.import("chrome://marionette/content/marionette-elements.js");
|
||||
let utils = {};
|
||||
loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
|
||||
@ -27,7 +29,6 @@ loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.js",
|
||||
specialpowers.specialPowersObserver = new specialpowers.SpecialPowersObserver();
|
||||
specialpowers.specialPowersObserver.init();
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
@ -76,20 +77,6 @@ Services.obs.addObserver(function() {
|
||||
systemMessageListenerReady = true;
|
||||
}, "system-message-listener-ready", false);
|
||||
|
||||
/**
|
||||
* An object representing a frame that Marionette has loaded a
|
||||
* frame script in.
|
||||
*/
|
||||
function MarionetteRemoteFrame(windowId, frameId) {
|
||||
this.windowId = windowId;
|
||||
this.frameId = frameId;
|
||||
this.targetFrameId = null;
|
||||
this.messageManager = null;
|
||||
this.command_id = null;
|
||||
}
|
||||
// persistent list of remote frames that Marionette has loaded a frame script in
|
||||
let remoteFrames = [];
|
||||
|
||||
/*
|
||||
* Custom exceptions
|
||||
*/
|
||||
@ -146,19 +133,19 @@ function MarionetteServerConnection(aPrefix, aTransport, aServer)
|
||||
this.marionetteLog = new MarionetteLogObj();
|
||||
this.command_id = null;
|
||||
this.mainFrame = null; //topmost chrome frame
|
||||
this.curFrame = null; //subframe that currently has focus
|
||||
this.curFrame = null; // chrome iframe that currently has focus
|
||||
this.importedScripts = FileUtils.getFile('TmpD', ['marionettescriptchrome']);
|
||||
this.currentRemoteFrame = null; // a member of remoteFrames
|
||||
this.currentFrameElement = null;
|
||||
this.testName = null;
|
||||
this.mozBrowserClose = null;
|
||||
|
||||
//register all message listeners
|
||||
this.addMessageManagerListeners(this.messageManager);
|
||||
}
|
||||
|
||||
MarionetteServerConnection.prototype = {
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
/**
|
||||
* Debugger transport callbacks:
|
||||
*/
|
||||
@ -197,12 +184,12 @@ MarionetteServerConnection.prototype = {
|
||||
* which removes most of the message listeners from it as well.
|
||||
*/
|
||||
switchToGlobalMessageManager: function MDA_switchToGlobalMM() {
|
||||
if (this.currentRemoteFrame !== null) {
|
||||
this.removeMessageManagerListeners(this.messageManager);
|
||||
if (this.curBrowser && this.curBrowser.frameManager.currentRemoteFrame !== null) {
|
||||
this.curBrowser.frameManager.removeMessageManagerListeners(this.messageManager);
|
||||
this.sendAsync("sleepSession", null, null, true);
|
||||
this.curBrowser.frameManager.currentRemoteFrame = null;
|
||||
}
|
||||
this.messageManager = this.globalMessageManager;
|
||||
this.currentRemoteFrame = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -218,10 +205,10 @@ MarionetteServerConnection.prototype = {
|
||||
if (values instanceof Object && commandId) {
|
||||
values.command_id = commandId;
|
||||
}
|
||||
if (this.currentRemoteFrame !== null) {
|
||||
if (this.curBrowser.frameManager.currentRemoteFrame !== null) {
|
||||
try {
|
||||
this.messageManager.sendAsyncMessage(
|
||||
"Marionette:" + name + this.currentRemoteFrame.targetFrameId, values);
|
||||
"Marionette:" + name + this.curBrowser.frameManager.currentRemoteFrame.targetFrameId, values);
|
||||
}
|
||||
catch(e) {
|
||||
if (!ignoreFailure) {
|
||||
@ -229,10 +216,10 @@ MarionetteServerConnection.prototype = {
|
||||
let error = e;
|
||||
switch(e.result) {
|
||||
case Components.results.NS_ERROR_FAILURE:
|
||||
error = new FrameSendFailureError(this.currentRemoteFrame);
|
||||
error = new FrameSendFailureError(this.curBrowser.frameManager.currentRemoteFrame);
|
||||
break;
|
||||
case Components.results.NS_ERROR_NOT_INITIALIZED:
|
||||
error = new FrameSendNotInitializedError(this.currentRemoteFrame);
|
||||
error = new FrameSendNotInitializedError(this.curBrowser.frameManager.currentRemoteFrame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -249,44 +236,6 @@ MarionetteServerConnection.prototype = {
|
||||
return success;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds listeners for messages from content frame scripts.
|
||||
*
|
||||
* @param object messageManager
|
||||
* The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
|
||||
* to which the listeners should be added.
|
||||
*/
|
||||
addMessageManagerListeners: function MDA_addMessageManagerListeners(messageManager) {
|
||||
messageManager.addMessageListener("Marionette:ok", this);
|
||||
messageManager.addMessageListener("Marionette:done", this);
|
||||
messageManager.addMessageListener("Marionette:error", this);
|
||||
messageManager.addMessageListener("Marionette:log", this);
|
||||
messageManager.addMessageListener("Marionette:shareData", this);
|
||||
messageManager.addMessageListener("Marionette:register", this);
|
||||
messageManager.addMessageListener("Marionette:runEmulatorCmd", this);
|
||||
messageManager.addMessageListener("Marionette:switchToFrame", this);
|
||||
messageManager.addMessageListener("Marionette:switchedToFrame", this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listeners for messages from content frame scripts.
|
||||
*
|
||||
* @param object messageManager
|
||||
* The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
|
||||
* from which the listeners should be removed.
|
||||
*/
|
||||
removeMessageManagerListeners: function MDA_removeMessageManagerListeners(messageManager) {
|
||||
messageManager.removeMessageListener("Marionette:ok", this);
|
||||
messageManager.removeMessageListener("Marionette:done", this);
|
||||
messageManager.removeMessageListener("Marionette:error", this);
|
||||
messageManager.removeMessageListener("Marionette:log", this);
|
||||
messageManager.removeMessageListener("Marionette:shareData", this);
|
||||
messageManager.removeMessageListener("Marionette:register", this);
|
||||
messageManager.removeMessageListener("Marionette:runEmulatorCmd", this);
|
||||
messageManager.removeMessageListener("Marionette:switchToFrame", this);
|
||||
messageManager.removeMessageListener("Marionette:switchedToFrame", this);
|
||||
},
|
||||
|
||||
logRequest: function MDA_logRequest(type, data) {
|
||||
logger.debug("Got request: " + type + ", data: " + JSON.stringify(data) + ", id: " + this.command_id);
|
||||
},
|
||||
@ -431,7 +380,7 @@ MarionetteServerConnection.prototype = {
|
||||
* Returns the unique server-assigned ID of the window
|
||||
*/
|
||||
addBrowser: function MDA_addBrowser(win) {
|
||||
let browser = new BrowserObj(win);
|
||||
let browser = new BrowserObj(win, this);
|
||||
let winId = win.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
||||
winId = winId + ((appName == "B2G") ? '-b2g' : '');
|
||||
@ -554,7 +503,6 @@ MarionetteServerConnection.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this.switchToGlobalMessageManager();
|
||||
|
||||
if (!Services.prefs.getBoolPref("marionette.contentListener")) {
|
||||
waitForWindow.call(this);
|
||||
@ -568,6 +516,7 @@ MarionetteServerConnection.prototype = {
|
||||
else {
|
||||
this.sendError("Session already running", 500, null, this.command_id);
|
||||
}
|
||||
this.switchToGlobalMessageManager();
|
||||
},
|
||||
|
||||
getSessionCapabilities: function MDA_getSessionCapabilities(){
|
||||
@ -1363,7 +1312,7 @@ MarionetteServerConnection.prototype = {
|
||||
}
|
||||
else {
|
||||
if ((!aRequest.value) && (!aRequest.element) &&
|
||||
(this.currentRemoteFrame !== null)) {
|
||||
(this.curBrowser.frameManager.currentRemoteFrame !== null)) {
|
||||
// We're currently using a ChromeMessageSender for a remote frame, so this
|
||||
// request indicates we need to switch back to the top-level (parent) frame.
|
||||
// We'll first switch to the parent's (global) ChromeMessageBroadcaster, so
|
||||
@ -2033,15 +1982,14 @@ MarionetteServerConnection.prototype = {
|
||||
while (winEnum.hasMoreElements()) {
|
||||
winEnum.getNext().messageManager.removeDelayedFrameScript(FRAME_SCRIPT);
|
||||
}
|
||||
this.curBrowser.frameManager.removeMessageManagerListeners(this.globalMessageManager);
|
||||
}
|
||||
this.removeMessageManagerListeners(this.globalMessageManager);
|
||||
this.switchToGlobalMessageManager();
|
||||
// reset frame to the top-most frame
|
||||
this.curFrame = null;
|
||||
if (this.mainFrame) {
|
||||
this.mainFrame.focus();
|
||||
}
|
||||
this.curBrowser = null;
|
||||
try {
|
||||
this.importedScripts.remove(false);
|
||||
}
|
||||
@ -2189,60 +2137,50 @@ MarionetteServerConnection.prototype = {
|
||||
this.sendToClient(message.json, -1);
|
||||
break;
|
||||
case "Marionette:switchToFrame":
|
||||
// Switch to a remote frame.
|
||||
let frameWindow = Services.wm.getOuterWindowWithId(message.json.win);
|
||||
let thisFrame = frameWindow.document.getElementsByTagName("iframe")[message.json.frame];
|
||||
let mm = thisFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
|
||||
// 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];
|
||||
if ((frame.messageManager == mm)) {
|
||||
this.currentRemoteFrame = frame;
|
||||
this.currentRemoteFrame.command_id = message.json.command_id;
|
||||
this.messageManager = frame.messageManager;
|
||||
this.addMessageManagerListeners(this.messageManager);
|
||||
this.messageManager.sendAsyncMessage("Marionette:restart", {});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the frame script in this frame, and set the frame's ChromeMessageSender
|
||||
// as the active message manager.
|
||||
this.addMessageManagerListeners(mm);
|
||||
mm.loadFrameScript(FRAME_SCRIPT, true);
|
||||
this.messageManager = mm;
|
||||
let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
|
||||
aFrame.messageManager = this.messageManager;
|
||||
aFrame.command_id = message.json.command_id;
|
||||
remoteFrames.push(aFrame);
|
||||
this.currentRemoteFrame = aFrame;
|
||||
this.curBrowser.frameManager.switchToFrame(message);
|
||||
this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager.get();
|
||||
break;
|
||||
case "Marionette:switchToModalOrigin":
|
||||
this.curBrowser.frameManager.switchToModalOrigin(message);
|
||||
this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager.get();
|
||||
break;
|
||||
case "Marionette:switchedToFrame":
|
||||
logger.info("Switched to frame: " + JSON.stringify(message.json));
|
||||
this.currentFrameElement = message.json.frameValue;
|
||||
if (message.json.restorePrevious) {
|
||||
this.currentFrameElement = this.previousFrameElement;
|
||||
}
|
||||
else {
|
||||
if (message.json.storePrevious) {
|
||||
// we don't arbitrarily save previousFrameElement, since
|
||||
// we allow frame switching after modals appear, which would
|
||||
// override this value and we'd lose our reference
|
||||
this.previousFrameElement = this.currentFrameElement;
|
||||
}
|
||||
this.currentFrameElement = message.json.frameValue;
|
||||
}
|
||||
break;
|
||||
case "Marionette:register":
|
||||
// This code processes the content listener's registration information
|
||||
// and either accepts the listener, or ignores it
|
||||
let nullPrevious = (this.curBrowser.curFrameId == null);
|
||||
let listenerWindow =
|
||||
Services.wm.getOuterWindowWithId(message.json.value);
|
||||
let listenerWindow =
|
||||
Services.wm.getOuterWindowWithId(message.json.value);
|
||||
|
||||
//go in here if we're already in a remote frame.
|
||||
if (!listenerWindow || (listenerWindow.location.href != message.json.href) &&
|
||||
(this.currentRemoteFrame !== null)) {
|
||||
(this.curBrowser.frameManager.currentRemoteFrame !== null)) {
|
||||
// The outerWindowID from an OOP frame will not be meaningful to
|
||||
// the parent process here, since each process maintains its own
|
||||
// independent window list. So, it will either be null (!listenerWindow)
|
||||
// if we're already in a remote frame,
|
||||
// or it will point to some random window, which will hopefully
|
||||
// cause an href mistmach. Currently this only happens
|
||||
// cause an href mismatch. Currently this only happens
|
||||
// in B2G for OOP frames registered in Marionette:switchToFrame, so
|
||||
// we'll acknowledge the switchToFrame message here.
|
||||
// XXX: Should have a better way of determining that this message
|
||||
// is from a remote frame.
|
||||
this.currentRemoteFrame.targetFrameId = this.generateFrameId(message.json.value);
|
||||
this.sendOk(this.currentRemoteFrame.command_id);
|
||||
this.curBrowser.frameManager.currentRemoteFrame.targetFrameId = this.generateFrameId(message.json.value);
|
||||
this.sendOk(this.command_id);
|
||||
}
|
||||
|
||||
let browserType;
|
||||
@ -2253,6 +2191,7 @@ MarionetteServerConnection.prototype = {
|
||||
}
|
||||
let reg = {};
|
||||
if (!browserType || browserType != "content") {
|
||||
//curBrowser holds all the registered frames in knownFrames
|
||||
reg.id = this.curBrowser.register(this.generateFrameId(message.json.value),
|
||||
listenerWindow);
|
||||
}
|
||||
@ -2341,11 +2280,11 @@ MarionetteServerConnection.prototype.requestTypes = {
|
||||
* The window whose browser needs to be accessed
|
||||
*/
|
||||
|
||||
function BrowserObj(win) {
|
||||
function BrowserObj(win, server) {
|
||||
this.DESKTOP = "desktop";
|
||||
this.B2G = "B2G";
|
||||
this.browser;
|
||||
this.tab = null;
|
||||
this.tab = null; //Holds a reference to the created tab, if any
|
||||
this.window = win;
|
||||
this.knownFrames = [];
|
||||
this.curFrameId = null;
|
||||
@ -2354,6 +2293,10 @@ function BrowserObj(win) {
|
||||
this.newSession = true; //used to set curFrameId upon new session
|
||||
this.elementManager = new ElementManager([SELECTOR, NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
|
||||
this.setBrowser(win);
|
||||
this.frameManager = new FrameManager(server); //We should have one FM per BO so that we can handle modals in each Browser
|
||||
|
||||
//register all message listeners
|
||||
this.frameManager.addMessageManagerListeners(server.messageManager);
|
||||
}
|
||||
|
||||
BrowserObj.prototype = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user