Bug 741598 - Enable finding elements of child nodes, r=jgriffin

This commit is contained in:
Malini Das 2012-04-03 11:08:49 -07:00
Родитель 3d76ad5939
Коммит bb96b17e38
3 изменённых файлов: 118 добавлений и 66 удалений

Просмотреть файл

@ -112,7 +112,6 @@ function MarionetteDriverActor(aConnection)
this.curBrowser = null; // points to current browser
this.context = "content";
this.scriptTimeout = null;
this.elementManager = new ElementManager([SELECTOR, NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
this.timer = null;
this.marionetteLog = new MarionetteLogObj();
this.command_id = null;
@ -141,7 +140,7 @@ MarionetteDriverActor.prototype = {
* Object to send to the listener
*/
sendAsync: function MDA_sendAsync(name, values) {
this.messageManager.sendAsyncMessage("Marionette:" + name + this.browsers[this.curBrowser].curFrameId, values);
this.messageManager.sendAsyncMessage("Marionette:" + name + this.curBrowser.curFrameId, values);
},
/**
@ -249,9 +248,11 @@ MarionetteDriverActor.prototype = {
let winId = win.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
winId = winId + ((appName == "B2G") ? '-b2g' : '');
if (this.elementManager.seenItems[winId] == undefined) {
this.browsers[winId] = browser;
this.curBrowser = this.browsers[winId];
if (this.curBrowser.elementManager.seenItems[winId] == undefined) {
//add this to seenItems so we can guarantee the user will get winId as this window's id
this.elementManager.seenItems[winId] = win;
this.curBrowser.elementManager.seenItems[winId] = win;
}
this.browsers[winId] = browser;
return winId;
@ -270,11 +271,10 @@ MarionetteDriverActor.prototype = {
* True if this is the first time we're talking to this browser
*/
startBrowser: function MDA_startBrowser(win, newSession) {
let winId = this.addBrowser(win);
this.curBrowser = winId;
this.browsers[this.curBrowser].newSession = newSession;
this.browsers[this.curBrowser].startSession(newSession);
this.browsers[this.curBrowser].loadFrameScript("chrome://marionette/content/marionette-listener.js", win);
this.addBrowser(win);
this.curBrowser.newSession = newSession;
this.curBrowser.startSession(newSession);
this.curBrowser.loadFrameScript("chrome://marionette/content/marionette-listener.js", win);
},
/**
@ -292,11 +292,10 @@ MarionetteDriverActor.prototype = {
if (!prefs.getBoolPref("marionette.contentListener")) {
this.startBrowser(this.getCurrentWindow(), true);
}
else if ((appName == "B2G")&& (this.curBrowser == null)) {
else if ((appName == "B2G") && (this.curBrowser == null)) {
//if there is a content listener, then we just wake it up
let winId = this.addBrowser(this.getCurrentWindow());
this.curBrowser = winId;
this.browsers[this.curBrowser].startSession(false);
this.addBrowser(this.getCurrentWindow());
this.curBrowser.startSession(false);
this.messageManager.sendAsyncMessage("Marionette:restart", {});
}
else {
@ -354,7 +353,7 @@ MarionetteDriverActor.prototype = {
*/
createExecuteSandbox: function MDA_createExecuteSandbox(aWindow, marionette, args) {
try {
args = this.elementManager.convertWrappedArguments(args, aWindow);
args = this.curBrowser.elementManager.convertWrappedArguments(args, aWindow);
}
catch(e) {
this.sendError(e.message, e.num, e.stack);
@ -363,7 +362,7 @@ MarionetteDriverActor.prototype = {
let _chromeSandbox = new Cu.Sandbox(aWindow,
{ sandboxPrototype: aWindow, wantXrays: false, sandboxName: ''});
_chromeSandbox.__namedArgs = this.elementManager.applyNamedArgs(args);
_chromeSandbox.__namedArgs = this.curBrowser.elementManager.applyNamedArgs(args);
_chromeSandbox.__marionetteParams = args;
marionette.exports.forEach(function(fn) {
@ -405,7 +404,7 @@ MarionetteDriverActor.prototype = {
}
if (!async) {
this.sendResponse(this.elementManager.wrapValue(res));
this.sendResponse(this.curBrowser.elementManager.wrapValue(res));
}
}
catch (e) {
@ -542,7 +541,7 @@ MarionetteDriverActor.prototype = {
curWindow.onerror = original_onerror;
if (status == 0 || status == undefined) {
that.sendToClient({from: that.actorID, value: that.elementManager.wrapValue(value), status: status},
that.sendToClient({from: that.actorID, value: that.curBrowser.elementManager.wrapValue(value), status: status},
marionette.command_id);
}
else {
@ -642,7 +641,11 @@ MarionetteDriverActor.prototype = {
* Get the current window's server-assigned ID
*/
getWindow: function MDA_getWindow() {
this.sendResponse(this.curBrowser);
for (let i in this.browsers) {
if (this.curBrowser == this.browsers[i]) {
this.sendResponse(i);
}
}
},
/**
@ -679,14 +682,14 @@ MarionetteDriverActor.prototype = {
this.startBrowser(foundWin, false);
}
foundWin.focus();
this.curBrowser = winId;
this.curBrowser = this.browsers[winId];
this.sendOk();
return;
}
}
this.sendError("Unable to locate window " + aRequest.value, 23, null);
},
/**
* Switch to a given frame within the current window
*
@ -706,7 +709,7 @@ MarionetteDriverActor.prototype = {
setSearchTimeout: function MDA_setSearchTimeout(aRequest) {
if (this.context == "chrome") {
try {
this.elementManager.setSearchTimeout(aRequest.value);
this.curBrowser.elementManager.setSearchTimeout(aRequest.value);
this.sendOk();
}
catch (e) {
@ -730,7 +733,7 @@ MarionetteDriverActor.prototype = {
let id;
try {
let notify = this.sendResponse.bind(this);
id = this.elementManager.find(aRequest, this.getCurrentWindow().document, notify, false);
id = this.curBrowser.elementManager.find(this.getCurrentWindow(),aRequest, notify, false);
}
catch (e) {
this.sendError(e.message, e.num, e.stack);
@ -754,7 +757,7 @@ MarionetteDriverActor.prototype = {
let id;
try {
let notify = this.sendResponse.bind(this);
id = this.elementManager.find(aRequest, this.getCurrentWindow().document, notify, true);
id = this.curBrowser.elementManager.find(this.getCurrentWindow(), aRequest, notify, true);
}
catch (e) {
this.sendError(e.message, e.num, e.stack);
@ -787,16 +790,16 @@ MarionetteDriverActor.prototype = {
* and can safely be reused.
*/
deleteSession: function MDA_deleteSession() {
if (this.browsers[this.curBrowser] != null) {
if (this.curBrowser != null) {
if (appName == "B2G") {
this.messageManager.sendAsyncMessage("Marionette:sleepSession" + this.browsers[this.curBrowser].mainContentId, {});
this.browsers[this.curBrowser].knownFrames.splice(this.browsers[this.curBrowser].knownFrames.indexOf(this.browsers[this.curBrowser].mainContentId), 1);
this.messageManager.sendAsyncMessage("Marionette:sleepSession" + this.curBrowser.mainContentId, {});
this.curBrowser.knownFrames.splice(this.curBrowser.knownFrames.indexOf(this.curBrowser.mainContentId), 1);
}
else {
//don't set this pref for B2G since the framescript can be safely reused
prefs.setBoolPref("marionette.contentListener", false);
}
this.browsers[this.curBrowser].closeTab();
this.curBrowser.closeTab();
//delete session in each frame in each browser
for (let win in this.browsers) {
for (let i in this.browsers[win].knownFrames) {
@ -817,7 +820,6 @@ MarionetteDriverActor.prototype = {
this.messageManager.removeMessageListener("Marionette:register", this);
this.messageManager.removeMessageListener("Marionette:goUrl", this);
this.curBrowser = null;
this.elementManager.reset();
},
/**
@ -849,15 +851,15 @@ MarionetteDriverActor.prototype = {
case "Marionette:register":
// This code processes the content listener's registration information
// and either accepts the listener, or ignores it
let nullPrevious= (this.browsers[this.curBrowser].curFrameId == null);
let nullPrevious = (this.curBrowser.curFrameId == null);
let curWin = this.getCurrentWindow();
let frameObject = curWin.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils).getOuterWindowWithId(message.json.value);
let reg = this.browsers[this.curBrowser].register(message.json.value, message.json.href);
let reg = this.curBrowser.register(message.json.value, message.json.href);
if (reg) {
this.elementManager.seenItems[reg] = frameObject; //add to seenItems
if (nullPrevious && (this.browsers[this.curBrowser].curFrameId != null)) {
this.curBrowser.elementManager.seenItems[reg] = frameObject; //add to seenItems
if (nullPrevious && (this.curBrowser.curFrameId != null)) {
this.sendAsync("newSession", {B2G: (appName == "B2G")});
if (this.browsers[this.curBrowser].newSession) {
if (this.curBrowser.newSession) {
this.sendResponse(reg);
}
}
@ -866,7 +868,7 @@ MarionetteDriverActor.prototype = {
case "Marionette:goUrl":
// if content determines that the goUrl call is directed at a top level window (not an iframe)
// it calls back into chrome to load the uri.
this.browsers[this.curBrowser].loadURI(message.json.value, this);
this.curBrowser.loadURI(message.json.value, this);
break;
}
},
@ -876,7 +878,7 @@ MarionetteDriverActor.prototype = {
handleEvent: function MDA_handleEvent(evt) {
if (evt.type == "DOMContentLoaded") {
this.sendOk();
this.browsers[this.curBrowser].browser.removeEventListener("DOMContentLoaded", this, false);
this.curBrowser.browser.removeEventListener("DOMContentLoaded", this, false);
}
},
};
@ -927,6 +929,7 @@ function BrowserObj(win) {
this.messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIChromeFrameMessageManager);
this.newSession = true; //used to set curFrameId upon new session
this.elementManager = new ElementManager([SELECTOR, NAME, LINK_TEXT, PARTIAL_LINK_TEXT]);
this.setBrowser(win);
}

Просмотреть файл

@ -125,7 +125,6 @@ ElementManager.prototype = {
else if (val == null) {
result = null;
}
// nodeType 1 == 'element'
else if (val.nodeType == 1) {
for(let i in this.seenItems) {
if (this.seenItems[i] == val) {
@ -222,18 +221,20 @@ ElementManager.prototype = {
},
/**
* Find an element or elements starting at the document root
* using the given search strategy. Search
* Find an element or elements starting at the document root or
* given node, using the given search strategy. Search
* will continue until the search timelimit has been reached.
*
* @param nsIDOMWindow win
* The window to search in
* @param object values
* The 'using' member of values will tell us which search
* method to use. The 'value' member tells us the value we
* are looking for.
* If this object has an 'element' member, this will be used
* as the start node instead of the document root
* If this object has a 'time' member, this number will be
* used to see if we have hit the search timelimit.
* @param nsIDOMElement rootNode
* The document root
* @param function notify
* The notification callback used when we are returning
* @param boolean all
@ -243,12 +244,13 @@ ElementManager.prototype = {
* @return nsIDOMElement or list of nsIDOMElements
* Returns the element(s) by calling the notify function.
*/
find: function EM_find(values, rootNode, notify, all) {
find: function EM_find(win, values, notify, all) {
let startTime = values.time ? values.time : new Date().getTime();
let startNode = (values.element != undefined) ? this.getKnownElement(values.element, win) : win.document;
if (this.elementStrategies.indexOf(values.using) < 0) {
throw new ElementException("No such strategy.", 17, null);
}
let found = all ? this.findElements(values.using, values.value, rootNode) : this.findElement(values.using, values.value, rootNode);
let found = all ? this.findElements(values.using, values.value, win.document, startNode) : this.findElement(values.using, values.value, win.document, startNode);
if (found) {
let type = Object.prototype.toString.call(found);
if ((type == '[object Array]') || (type == '[object HTMLCollection]')) {
@ -268,10 +270,53 @@ ElementManager.prototype = {
throw new ElementException("Unable to locate element: " + values.value, 7, null);
} else {
values.time = startTime;
this.timer.initWithCallback(this.find.bind(this, values, rootNode, notify, all), 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
this.timer.initWithCallback(this.find.bind(this, win, values, notify, all), 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}
}
},
/**
* Find a value by XPATH
*
* @param nsIDOMElement root
* Document root
* @param string value
* XPATH search string
* @param nsIDOMElement node
* start node
*
* @return nsIDOMElement
* returns the found element
*/
findByXPath: function EM_findByXPath(root, value, node) {
return root.evaluate(value, node, null,
Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
},
/**
* Find values by XPATH
*
* @param nsIDOMElement root
* Document root
* @param string value
* XPATH search string
* @param nsIDOMElement node
* start node
*
* @return object
* returns a list of found nsIDOMElements
*/
findByXPathAll: function EM_findByXPathAll(root, value, node) {
let values = root.evaluate(value, node, null,
Components.interfaces.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
let elements = [];
let element = values.iterateNext();
while (element) {
elements.push(element);
element = values.iterateNext();
}
return elements;
},
/**
* Helper method to find. Finds one element using find's criteria
@ -282,33 +327,37 @@ ElementManager.prototype = {
* Value to look for
* @param nsIDOMElement rootNode
* Document root
* @param nsIDOMElement startNode
* Node from which we start searching
*
* @return nsIDOMElement
* Returns found element or throws Exception if not found
*/
findElement: function EM_findElement(using, value, rootNode) {
findElement: function EM_findElement(using, value, rootNode, startNode) {
let element;
switch (using) {
case ID:
element = rootNode.getElementById(value);
element = startNode.getElementById ?
startNode.getElementById(value) :
this.findByXPath(rootNode, './/*[@id="' + value + '"]', startNode);
break;
case NAME:
element = rootNode.getElementsByName(value)[0];
element = startNode.getElementsByName ?
startNode.getElementsByName(value)[0] :
this.findByXPath(rootNode, './/*[@name="' + value + '"]', startNode);
break;
case CLASS_NAME:
element = rootNode.getElementsByClassName(value)[0];
element = startNode.getElementsByClassName(value)[0]; //works for >=FF3
break;
case TAG:
element = rootNode.getElementsByTagName(value)[0];
element = startNode.getElementsByTagName(value)[0]; //works for all elements
break;
case XPATH:
element = rootNode.evaluate(value, rootNode, null,
Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).
singleNodeValue;
element = this.findByXPath(rootNode, value, startNode);
break;
case LINK_TEXT:
case PARTIAL_LINK_TEXT:
let allLinks = rootNode.getElementsByTagName('A');
let allLinks = startNode.getElementsByTagName('A');
for (let i = 0; i < allLinks.length && !element; i++) {
let text = allLinks[i].text;
if (PARTIAL_LINK_TEXT == using) {
@ -321,7 +370,7 @@ ElementManager.prototype = {
}
break;
case SELECTOR:
element = rootNode.querySelector(value);
element = startNode.querySelector(value);
break;
default:
throw new ElementException("No such strategy", 500, null);
@ -338,32 +387,30 @@ ElementManager.prototype = {
* Value to look for
* @param nsIDOMElement rootNode
* Document root
* @param nsIDOMElement startNode
* Node from which we start searching
*
* @return nsIDOMElement
* Returns found elements or throws Exception if not found
*/
findElements: function EM_findElements(using, value, rootNode) {
findElements: function EM_findElements(using, value, rootNode, startNode) {
let elements = [];
switch (using) {
case ID:
value = './/*[@id="' + value + '"]';
case XPATH:
values = rootNode.evaluate(value, rootNode, null,
Components.interfaces.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null)
let element = values.iterateNext();
while (element) {
elements.push(element);
element = values.iterateNext();
}
elements = this.findByXPathAll(rootNode, value, startNode);
break;
case NAME:
elements = rootNode.getElementsByName(value);
element = startNode.getElementsByName ?
startNode.getElementsByName(value)[0] :
this.findByXPathAll(rootNode, './/*[@name="' + value + '"]', startNode);
break;
case CLASS_NAME:
elements = rootNode.getElementsByClassName(value);
elements = startNode.getElementsByClassName(value);
break;
case TAG:
elements = rootNode.getElementsByTagName(value);
elements = startNode.getElementsByTagName(value);
break;
case LINK_TEXT:
case PARTIAL_LINK_TEXT:

Просмотреть файл

@ -425,7 +425,8 @@ function findElementContent(msg) {
let id;
try {
let notify = function(id) { sendResponse({value:id});};
id = elementManager.find(msg.json, win.document, notify, false);
let curWin = activeFrame ? win.frames[activeFrame] : win;
id = elementManager.find(curWin, msg.json, notify, false);
}
catch (e) {
sendError(e.message, e.num, e.stack);
@ -441,7 +442,8 @@ function findElementsContent(msg) {
let id;
try {
let notify = function(id) { sendResponse({value:id});};
id = elementManager.find(msg.json, win.document, notify, true);
let curWin = activeFrame ? win.frames[activeFrame] : win;
id = elementManager.find(curWin, msg.json, notify, true);
}
catch (e) {
sendError(e.message, e.num, e.stack);