зеркало из https://github.com/mozilla/gecko-dev.git
Bug 779284 - Implement Modal dialog handling to Marionette, r=jgriffin
This commit is contained in:
Родитель
094325dac6
Коммит
8c49af2321
|
@ -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
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
/* 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");
|
||||
|
||||
/**
|
||||
* 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
|
||||
this.messageManager = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The FrameManager will maintain the list of Out Of Process (OOP) frames and will handle
|
||||
* frame switching between them.
|
||||
* It handles explicit frame switching (switchToRemoteFrame), 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.messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageBroadcaster);
|
||||
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.remoteFrames = []; //list of OOP frames that has the frame script loaded
|
||||
this.server = server; // a reference to the marionette server
|
||||
};
|
||||
|
||||
FrameManager.prototype = {
|
||||
/**
|
||||
* 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);
|
||||
//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.
|
||||
switchToRemoteFrame: function FM_switchToRemoteFrame(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 < this.remoteFrames.length; i++) {
|
||||
let frame = this.remoteFrames[i];
|
||||
if (frame.messageManager == mm) {
|
||||
this.currentRemoteFrame = frame;
|
||||
mm = frame.messageManager;
|
||||
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);
|
||||
mm.loadFrameScript(FRAME_SCRIPT, true);
|
||||
let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
|
||||
aFrame.messageManager = mm;
|
||||
this.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);
|
||||
}
|
||||
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.addMessageListener("Marionette:ok", this.server);
|
||||
messageManager.addMessageListener("Marionette:done", this.server);
|
||||
messageManager.addMessageListener("Marionette:error", this.server);
|
||||
messageManager.addMessageListener("Marionette:log", this.server);
|
||||
messageManager.addMessageListener("Marionette:shareData", this.server);
|
||||
messageManager.addMessageListener("Marionette:register", this.server);
|
||||
messageManager.addMessageListener("Marionette:runEmulatorCmd", this.server);
|
||||
messageManager.addMessageListener("Marionette:switchToModalOrigin", this.server);
|
||||
messageManager.addMessageListener("Marionette:switchToFrame", this.server);
|
||||
messageManager.addMessageListener("Marionette:switchedToFrame", this.server);
|
||||
messageManager.addMessageListener("MarionetteFrame:handleModal", this);
|
||||
messageManager.addMessageListener("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.removeMessageListener("Marionette:ok", this.server);
|
||||
messageManager.removeMessageListener("Marionette:done", this.server);
|
||||
messageManager.removeMessageListener("Marionette:error", this.server);
|
||||
messageManager.removeMessageListener("Marionette:log", this.server);
|
||||
messageManager.removeMessageListener("Marionette:shareData", this.server);
|
||||
messageManager.removeMessageListener("Marionette:register", this.server);
|
||||
messageManager.removeMessageListener("Marionette:runEmulatorCmd", this.server);
|
||||
messageManager.removeMessageListener("Marionette:switchToFrame", this.server);
|
||||
messageManager.removeMessageListener("Marionette:switchedToFrame", this.server);
|
||||
messageManager.removeMessageListener("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;
|
||||
|
||||
|
@ -72,6 +72,24 @@ Cu.import("resource://gre/modules/services-common/log4moz.js");
|
|||
let logger = Log4Moz.repository.getLogger("Marionette");
|
||||
logger.info("loaded marionette-listener.js");
|
||||
|
||||
/*
|
||||
* This is a callback used with the 'mozbrowsershowmodalprompt' event
|
||||
* and is used to handle modal dialogs in B2G.
|
||||
* When a modal dialog is triggered in B2G, we want to switch into
|
||||
* the frame of the modal dialog instead of remaining in the current
|
||||
* frame, since the current frame's process will be frozen until the
|
||||
* modal dialog is dismissed.
|
||||
*/
|
||||
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.
|
||||
* The listener sends its unique window ID and its current URI to the actor.
|
||||
|
@ -156,6 +174,7 @@ function startListeners() {
|
|||
function newSession(msg) {
|
||||
isB2G = msg.json.B2G;
|
||||
resetValues();
|
||||
content.addEventListener("mozbrowsershowmodalprompt", modalHandler, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,6 +192,7 @@ function sleepSession(msg) {
|
|||
*/
|
||||
function restart(msg) {
|
||||
removeMessageListener("Marionette:restart", restart);
|
||||
content.addEventListener("mozbrowsershowmodalprompt", modalHandler, false);
|
||||
registerSelf();
|
||||
}
|
||||
|
||||
|
@ -220,10 +240,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 = {};
|
||||
}
|
||||
|
||||
|
@ -275,10 +296,25 @@ function sendError(message, status, trace, command_id) {
|
|||
*/
|
||||
function resetValues() {
|
||||
sandbox = null;
|
||||
curWindow = content;
|
||||
curFrame = content;
|
||||
mouseEventsOnly = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -315,8 +351,8 @@ 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);
|
||||
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
|
@ -365,7 +401,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);
|
||||
|
@ -400,7 +436,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);
|
||||
|
@ -476,10 +512,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);
|
||||
|
@ -496,14 +532,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;
|
||||
|
@ -516,7 +552,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);
|
||||
|
@ -553,17 +589,19 @@ function executeWithCallback(msg, useFinish) {
|
|||
* This function creates a touch event given a touch type and a touch
|
||||
*/
|
||||
function emitTouchEvent(type, touch) {
|
||||
let loggingInfo = "Marionette: 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";
|
||||
dump(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 = "Marionette: 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";
|
||||
dump(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -574,20 +612,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 = "Marionette: emitting Mouse event of type " + type + " at coordinates (" + elClientX + ", " + elClientY + ") relative to the viewport";
|
||||
dump(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 = "Marionette: emitting Mouse event of type " + type + " at coordinates (" + elClientX + ", " + elClientY + ") relative to the viewport";
|
||||
dump(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -626,30 +666,30 @@ function coordinates(target, x, y) {
|
|||
function elementInViewport(el) {
|
||||
let rect = el.getBoundingClientRect();
|
||||
return (/* Top left corner is in view */
|
||||
(rect.top >= curWindow.pageYOffset &&
|
||||
rect.top <= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
rect.left >= curWindow.pageXOffset &&
|
||||
rect.left <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
|
||||
(rect.top >= curFrame.pageYOffset &&
|
||||
rect.top <= (curFrame.pageYOffset + curFrame.innerHeight) &&
|
||||
rect.left >= curFrame.pageXOffset &&
|
||||
rect.left <= (curFrame.pageXOffset + curFrame.innerWidth)) ||
|
||||
/* Top right corner is in view */
|
||||
(rect.top >= curWindow.pageYOffset &&
|
||||
rect.top <= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
rect.right >= curWindow.pageXOffset &&
|
||||
rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
|
||||
(rect.top >= curFrame.pageYOffset &&
|
||||
rect.top <= (curFrame.pageYOffset + curFrame.innerHeight) &&
|
||||
rect.right >= curFrame.pageXOffset &&
|
||||
rect.right <= (curFrame.pageXOffset + curFrame.innerWidth)) ||
|
||||
/* Bottom right corner is in view */
|
||||
(rect.bottom >= curWindow.pageYOffset &&
|
||||
rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
rect.right >= curWindow.pageXOffset &&
|
||||
rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
|
||||
(rect.bottom >= curFrame.pageYOffset &&
|
||||
rect.bottom <= (curFrame.pageYOffset + curFrame.innerHeight) &&
|
||||
rect.right >= curFrame.pageXOffset &&
|
||||
rect.right <= (curFrame.pageXOffset + curFrame.innerWidth)) ||
|
||||
/* Bottom left corner is in view */
|
||||
(rect.bottom >= curWindow.pageYOffset &&
|
||||
rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
rect.left >= curWindow.pageXOffset &&
|
||||
rect.left <= (curWindow.pageXOffset + curWindow.innerWidth)) ||
|
||||
(rect.bottom >= curFrame.pageYOffset &&
|
||||
rect.bottom <= (curFrame.pageYOffset + curFrame.innerHeight) &&
|
||||
rect.left >= curFrame.pageXOffset &&
|
||||
rect.left <= (curFrame.pageXOffset + curFrame.innerWidth)) ||
|
||||
/* Center of the element is in view if element larger than viewport */
|
||||
((rect.top + (rect.height/2)) <= curWindow.pageYOffset &&
|
||||
(rect.top + (rect.height/2)) >= (curWindow.pageYOffset + curWindow.innerHeight) &&
|
||||
(rect.left + (rect.width/2)) <= curWindow.pageXOffset &&
|
||||
(rect.left + (rect.width/2)) >= (curWindow.pageXOffset + curWindow.innerWidth))
|
||||
((rect.top + (rect.height/2)) <= curFrame.pageYOffset &&
|
||||
(rect.top + (rect.height/2)) >= (curFrame.pageYOffset + curFrame.innerHeight) &&
|
||||
(rect.left + (rect.width/2)) <= curFrame.pageXOffset &&
|
||||
(rect.left + (rect.width/2)) >= (curFrame.pageXOffset + curFrame.innerWidth))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -683,7 +723,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) {
|
||||
|
@ -752,7 +792,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]);
|
||||
|
@ -765,6 +805,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 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -773,13 +826,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);
|
||||
|
@ -839,7 +892,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;
|
||||
|
@ -853,7 +906,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);
|
||||
|
@ -900,12 +953,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);
|
||||
|
@ -934,7 +987,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,
|
||||
|
@ -987,7 +1040,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;
|
||||
|
@ -1002,7 +1055,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);
|
||||
|
@ -1056,7 +1109,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++) {
|
||||
|
@ -1098,11 +1151,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);
|
||||
}
|
||||
|
@ -1120,7 +1173,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();
|
||||
}
|
||||
};
|
||||
|
@ -1133,29 +1186,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);
|
||||
}
|
||||
|
||||
|
@ -1163,7 +1216,7 @@ function getPageSource(msg) {
|
|||
* Go back in history
|
||||
*/
|
||||
function goBack(msg) {
|
||||
curWindow.history.back();
|
||||
curFrame.history.back();
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
|
||||
|
@ -1171,7 +1224,7 @@ function goBack(msg) {
|
|||
* Go forward in history
|
||||
*/
|
||||
function goForward(msg) {
|
||||
curWindow.history.forward();
|
||||
curFrame.history.forward();
|
||||
sendOk(msg.json.command_id);
|
||||
}
|
||||
|
||||
|
@ -1180,7 +1233,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);
|
||||
|
@ -1196,7 +1249,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) {
|
||||
|
@ -1212,7 +1265,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) {
|
||||
|
@ -1225,7 +1278,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);
|
||||
}
|
||||
|
@ -1237,7 +1290,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)
|
||||
|
@ -1262,7 +1315,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);
|
||||
}
|
||||
|
@ -1277,7 +1330,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) {
|
||||
|
@ -1291,7 +1344,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) {
|
||||
|
@ -1305,7 +1358,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) {
|
||||
|
@ -1325,8 +1378,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) {
|
||||
|
@ -1340,7 +1393,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);
|
||||
|
@ -1356,7 +1409,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) {
|
||||
|
@ -1370,7 +1423,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) {
|
||||
|
@ -1384,9 +1437,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 {
|
||||
|
@ -1404,7 +1457,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 = {};
|
||||
|
@ -1424,7 +1477,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);
|
||||
}
|
||||
|
@ -1439,27 +1492,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
|
||||
|
@ -1472,9 +1525,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);
|
||||
|
@ -1484,7 +1537,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);
|
||||
|
@ -1492,7 +1545,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;
|
||||
}
|
||||
}
|
||||
|
@ -1516,13 +1569,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;
|
||||
}
|
||||
|
@ -1536,21 +1589,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);
|
||||
}
|
||||
|
@ -1569,11 +1622,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);
|
||||
|
@ -1587,7 +1640,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);
|
||||
}
|
||||
|
@ -1603,7 +1656,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;
|
||||
|
@ -1633,7 +1686,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) {
|
||||
|
@ -1650,7 +1703,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);
|
||||
|
@ -1691,7 +1744,7 @@ function getVisibleCookies(location) {
|
|||
}
|
||||
|
||||
function getAppCacheStatus(msg) {
|
||||
sendResponse({ value: curWindow.applicationCache.status },
|
||||
sendResponse({ value: curFrame.applicationCache.status },
|
||||
msg.json.command_id);
|
||||
}
|
||||
|
||||
|
@ -1753,7 +1806,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);
|
||||
|
@ -1761,14 +1814,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, needsOffset;
|
||||
// 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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -144,15 +131,11 @@ 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 = {
|
||||
|
@ -195,12 +178,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.frameManager.currentRemoteFrame !== null) {
|
||||
this.curBrowser.frameManager.removeMessageManagerListeners(this.messageManager);
|
||||
this.sendAsync("sleepSession", null, null, true);
|
||||
}
|
||||
this.messageManager = this.globalMessageManager;
|
||||
this.currentRemoteFrame = null;
|
||||
this.curBrowser.frameManager.currentRemoteFrame = null;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -216,10 +199,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) {
|
||||
|
@ -227,10 +210,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;
|
||||
|
@ -247,44 +230,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);
|
||||
},
|
||||
|
@ -429,7 +374,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' : '');
|
||||
|
@ -552,7 +497,6 @@ MarionetteServerConnection.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
this.switchToGlobalMessageManager();
|
||||
|
||||
if (!Services.prefs.getBoolPref("marionette.contentListener")) {
|
||||
waitForWindow.call(this);
|
||||
|
@ -566,6 +510,7 @@ MarionetteServerConnection.prototype = {
|
|||
else {
|
||||
this.sendError("Session already running", 500, null, this.command_id);
|
||||
}
|
||||
this.switchToGlobalMessageManager();
|
||||
},
|
||||
|
||||
getSessionCapabilities: function MDA_getSessionCapabilities(){
|
||||
|
@ -1307,7 +1252,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
|
||||
|
@ -1977,15 +1922,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);
|
||||
}
|
||||
|
@ -2133,60 +2077,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.switchToRemoteFrame(message);
|
||||
this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager;
|
||||
break;
|
||||
case "Marionette:switchToModalOrigin":
|
||||
this.curBrowser.frameManager.switchToModalOrigin(message);
|
||||
this.messageManager = this.curBrowser.frameManager.currentRemoteFrame.messageManager;
|
||||
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);
|
||||
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;
|
||||
|
@ -2197,6 +2131,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);
|
||||
}
|
||||
|
@ -2285,11 +2220,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;
|
||||
|
@ -2298,6 +2233,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 = {
|
||||
|
|
Загрузка…
Ссылка в новой задаче