2012-03-22 08:19:57 -07:00
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/ * T h i s S o u r c e C o d e F o r m i s s u b j e c t t o t h e t e r m s o f t h e M o z i l l a P u b l i c
* 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/. */
2012-05-11 13:06:53 -07:00
let { classes : Cc , interfaces : Ci , utils : Cu , results : Cr } = Components ;
let uuidGen = Cc [ "@mozilla.org/uuid-generator;1" ]
. getService ( Ci . nsIUUIDGenerator ) ;
let loader = Cc [ "@mozilla.org/moz/jssubscript-loader;1" ]
. getService ( Ci . mozIJSSubScriptLoader ) ;
2012-03-22 08:19:57 -07:00
loader . loadSubScript ( "chrome://marionette/content/marionette-simpletest.js" ) ;
2013-06-28 14:07:55 -07:00
loader . loadSubScript ( "chrome://marionette/content/marionette-common.js" ) ;
2012-05-11 13:06:53 -07:00
Cu . import ( "chrome://marionette/content/marionette-elements.js" ) ;
2012-06-04 10:50:06 -07:00
Cu . import ( "resource://gre/modules/FileUtils.jsm" ) ;
2013-01-23 11:26:33 -08:00
Cu . import ( "resource://gre/modules/NetUtil.jsm" ) ;
Cu . import ( "resource://gre/modules/XPCOMUtils.jsm" ) ;
2012-04-10 17:28:08 -07:00
let utils = { } ;
utils . window = content ;
2012-05-11 13:06:53 -07:00
// Load Event/ChromeUtils for use with JS scripts:
loader . loadSubScript ( "chrome://marionette/content/EventUtils.js" , utils ) ;
2012-04-10 17:28:08 -07:00
loader . loadSubScript ( "chrome://marionette/content/ChromeUtils.js" , utils ) ;
loader . loadSubScript ( "chrome://marionette/content/atoms.js" , utils ) ;
2012-08-05 12:49:42 -07:00
loader . loadSubScript ( "chrome://marionette/content/marionette-sendkeys.js" , utils ) ;
2012-05-18 13:30:13 -07:00
loader . loadSubScript ( "chrome://specialpowers/content/specialpowersAPI.js" ) ;
loader . loadSubScript ( "chrome://specialpowers/content/specialpowers.js" ) ;
2012-03-22 08:19:57 -07:00
let marionetteLogObj = new MarionetteLogObj ( ) ;
let isB2G = false ;
2012-10-31 15:36:57 -07:00
let marionetteTestName ;
2012-05-11 13:06:53 -07:00
let winUtil = content . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIDOMWindowUtils ) ;
2012-03-22 08:19:57 -07:00
let listenerId = null ; //unique ID of this listener
2013-09-19 10:35:19 -07:00
let curFrame = content ;
let previousFrame = null ;
2012-03-22 08:19:57 -07:00
let elementManager = new ElementManager ( [ ] ) ;
2012-12-14 08:11:02 -08:00
let importedScripts = null ;
2014-01-15 06:28:04 -08:00
let inputSource = null ;
2012-03-22 08:19:57 -07:00
2012-05-09 12:05:39 -07:00
// The sandbox we execute test scripts in. Gets lazily created in
// createExecuteContentSandbox().
let sandbox ;
2013-02-12 10:10:03 -08:00
// the unload handler
let onunload ;
2012-05-09 12:05:39 -07:00
// Flag to indicate whether an async script is currently running or not.
let asyncTestRunning = false ;
let asyncTestCommandId ;
let asyncTestTimeoutId ;
2013-08-28 10:06:03 -07:00
let inactivityTimeoutId = null ;
let heartbeatCallback = function ( ) { } ; // Called by the simpletest methods.
2012-12-18 06:21:28 -08:00
let originalOnError ;
2012-11-14 10:35:44 -08:00
//timer for doc changes
let checkTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
2013-10-17 08:25:11 -07:00
//timer for readystate
let readyStateTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
2013-01-22 11:27:44 -08:00
// Send move events about this often
let EVENT _INTERVAL = 30 ; // milliseconds
// For assigning unique ids to all touches
let nextTouchId = 1000 ;
2013-05-27 10:12:13 -07:00
//Keep track of active Touches
2013-03-04 17:09:58 -08:00
let touchIds = { } ;
2013-03-18 13:42:46 -07:00
// last touch for each fingerId
let multiLast = { } ;
2013-05-27 10:12:13 -07:00
let lastCoordinates = null ;
let isTap = false ;
2013-04-10 16:55:43 -07:00
// whether to send mouse event
2013-05-27 10:12:13 -07:00
let mouseEventsOnly = false ;
2013-05-14 13:54:07 -07:00
2013-08-26 11:55:58 -07:00
Cu . import ( "resource://gre/modules/Log.jsm" ) ;
let logger = Log . repository . getLogger ( "Marionette" ) ;
2013-05-14 13:54:07 -07:00
logger . info ( "loaded marionette-listener.js" ) ;
2013-09-19 10:35:19 -07:00
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 ;
} ;
2013-05-14 13:54:07 -07:00
2012-03-22 08:19:57 -07:00
/ * *
2014-01-21 08:40:20 -08:00
* Called when listener is first started up .
2012-03-22 08:19:57 -07:00
* The listener sends its unique window ID and its current URI to the actor .
* If the actor returns an ID , we start the listeners . Otherwise , nothing happens .
* /
function registerSelf ( ) {
2012-11-26 18:19:04 -08:00
let msg = { value : winUtil . outerWindowID , href : content . location . href } ;
let register = sendSyncMessage ( "Marionette:register" , msg ) ;
2012-03-22 08:19:57 -07:00
if ( register [ 0 ] ) {
2012-12-14 08:11:02 -08:00
listenerId = register [ 0 ] . id ;
2013-11-26 14:25:24 -08:00
importedScripts = FileUtils . getDir ( 'TmpD' , [ ] , false ) ;
importedScripts . append ( 'marionetteContentScripts' ) ;
2012-03-22 08:19:57 -07:00
startListeners ( ) ;
}
}
2012-05-11 13:06:53 -07:00
/ * *
* Add a message listener that ' s tied to our listenerId .
* /
function addMessageListenerId ( messageName , handler ) {
addMessageListener ( messageName + listenerId , handler ) ;
}
/ * *
* Remove a message listener that ' s tied to our listenerId .
* /
function removeMessageListenerId ( messageName , handler ) {
removeMessageListener ( messageName + listenerId , handler ) ;
}
2012-03-22 08:19:57 -07:00
/ * *
* Start all message listeners
* /
function startListeners ( ) {
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:newSession" , newSession ) ;
addMessageListenerId ( "Marionette:executeScript" , executeScript ) ;
addMessageListenerId ( "Marionette:executeAsyncScript" , executeAsyncScript ) ;
addMessageListenerId ( "Marionette:executeJSScript" , executeJSScript ) ;
2013-01-22 11:27:44 -08:00
addMessageListenerId ( "Marionette:singleTap" , singleTap ) ;
2013-03-04 17:09:58 -08:00
addMessageListenerId ( "Marionette:actionChain" , actionChain ) ;
2013-03-18 13:42:46 -07:00
addMessageListenerId ( "Marionette:multiAction" , multiAction ) ;
2014-01-24 05:39:23 -08:00
addMessageListenerId ( "Marionette:get" , get ) ;
2014-01-21 10:31:31 -08:00
addMessageListenerId ( "Marionette:getCurrentUrl" , getCurrentUrl ) ;
2012-07-10 13:30:21 -07:00
addMessageListenerId ( "Marionette:getTitle" , getTitle ) ;
2012-08-09 12:31:12 -07:00
addMessageListenerId ( "Marionette:getPageSource" , getPageSource ) ;
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:goBack" , goBack ) ;
addMessageListenerId ( "Marionette:goForward" , goForward ) ;
addMessageListenerId ( "Marionette:refresh" , refresh ) ;
addMessageListenerId ( "Marionette:findElementContent" , findElementContent ) ;
addMessageListenerId ( "Marionette:findElementsContent" , findElementsContent ) ;
2013-01-14 06:57:54 -08:00
addMessageListenerId ( "Marionette:getActiveElement" , getActiveElement ) ;
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:clickElement" , clickElement ) ;
addMessageListenerId ( "Marionette:getElementAttribute" , getElementAttribute ) ;
addMessageListenerId ( "Marionette:getElementText" , getElementText ) ;
2012-08-08 13:21:50 -07:00
addMessageListenerId ( "Marionette:getElementTagName" , getElementTagName ) ;
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:isElementDisplayed" , isElementDisplayed ) ;
2013-10-15 07:40:48 -07:00
addMessageListenerId ( "Marionette:getElementValueOfCssProperty" , getElementValueOfCssProperty ) ;
addMessageListenerId ( "Marionette:submitElement" , submitElement ) ;
2012-10-19 13:59:15 -07:00
addMessageListenerId ( "Marionette:getElementSize" , getElementSize ) ;
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:isElementEnabled" , isElementEnabled ) ;
addMessageListenerId ( "Marionette:isElementSelected" , isElementSelected ) ;
addMessageListenerId ( "Marionette:sendKeysToElement" , sendKeysToElement ) ;
2014-01-22 11:04:04 -08:00
addMessageListenerId ( "Marionette:getElementLocation" , getElementLocation ) ;
2012-05-11 13:06:53 -07:00
addMessageListenerId ( "Marionette:clearElement" , clearElement ) ;
addMessageListenerId ( "Marionette:switchToFrame" , switchToFrame ) ;
addMessageListenerId ( "Marionette:deleteSession" , deleteSession ) ;
addMessageListenerId ( "Marionette:sleepSession" , sleepSession ) ;
2012-05-18 13:30:43 -07:00
addMessageListenerId ( "Marionette:emulatorCmdResult" , emulatorCmdResult ) ;
2012-06-04 10:50:06 -07:00
addMessageListenerId ( "Marionette:importScript" , importScript ) ;
2012-08-23 15:07:16 -07:00
addMessageListenerId ( "Marionette:getAppCacheStatus" , getAppCacheStatus ) ;
2012-10-31 15:36:57 -07:00
addMessageListenerId ( "Marionette:setTestName" , setTestName ) ;
2014-01-24 05:45:58 -08:00
addMessageListenerId ( "Marionette:takeScreenshot" , takeScreenshot ) ;
2012-11-22 07:53:44 -08:00
addMessageListenerId ( "Marionette:addCookie" , addCookie ) ;
2014-01-21 08:40:20 -08:00
addMessageListenerId ( "Marionette:getCookies" , getCookies ) ;
2012-11-22 07:53:44 -08:00
addMessageListenerId ( "Marionette:deleteAllCookies" , deleteAllCookies ) ;
addMessageListenerId ( "Marionette:deleteCookie" , deleteCookie ) ;
2012-03-22 08:19:57 -07:00
}
2013-10-17 08:25:11 -07:00
/ * *
* Used during newSession and restart , called to set up the modal dialog listener in b2g
* /
function waitForReady ( ) {
if ( content . document . readyState == 'complete' ) {
readyStateTimer . cancel ( ) ;
content . addEventListener ( "mozbrowsershowmodalprompt" , modalHandler , false ) ;
content . addEventListener ( "unload" , waitForReady , false ) ;
}
else {
readyStateTimer . initWithCallback ( waitForReady , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
}
2012-03-22 08:19:57 -07:00
/ * *
* Called when we start a new session . It registers the
* current environment , and resets all values
* /
function newSession ( msg ) {
isB2G = msg . json . B2G ;
resetValues ( ) ;
2013-10-17 08:25:11 -07:00
if ( isB2G ) {
readyStateTimer . initWithCallback ( waitForReady , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2014-01-15 06:28:04 -08:00
// We have to set correct mouse event source to MOZ_SOURCE_TOUCH
// to offer a way for event listeners to differentiate
// events being the result of a physical mouse action.
// This is especially important for the touch event shim,
// in order to prevent creating touch event for these fake mouse events.
inputSource = Ci . nsIDOMMouseEvent . MOZ _SOURCE _TOUCH ;
2013-10-17 08:25:11 -07:00
}
2012-03-22 08:19:57 -07:00
}
2014-01-21 08:40:20 -08:00
2012-03-22 08:19:57 -07:00
/ * *
* Puts the current session to sleep , so all listeners are removed except
* for the 'restart' listener . This is used to keep the content listener
* alive for reuse in B2G instead of reloading it each time .
* /
function sleepSession ( msg ) {
deleteSession ( ) ;
addMessageListener ( "Marionette:restart" , restart ) ;
}
/ * *
* Restarts all our listeners after this listener was put to sleep
* /
2012-11-26 18:19:04 -08:00
function restart ( msg ) {
2012-03-22 08:19:57 -07:00
removeMessageListener ( "Marionette:restart" , restart ) ;
2013-10-17 08:25:11 -07:00
if ( isB2G ) {
readyStateTimer . initWithCallback ( waitForReady , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
2012-03-22 08:19:57 -07:00
registerSelf ( ) ;
}
/ * *
* Removes all listeners
* /
function deleteSession ( msg ) {
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:newSession" , newSession ) ;
removeMessageListenerId ( "Marionette:executeScript" , executeScript ) ;
removeMessageListenerId ( "Marionette:executeAsyncScript" , executeAsyncScript ) ;
removeMessageListenerId ( "Marionette:executeJSScript" , executeJSScript ) ;
2013-01-22 11:27:44 -08:00
removeMessageListenerId ( "Marionette:singleTap" , singleTap ) ;
2013-03-04 17:09:58 -08:00
removeMessageListenerId ( "Marionette:actionChain" , actionChain ) ;
2013-03-18 13:42:46 -07:00
removeMessageListenerId ( "Marionette:multiAction" , multiAction ) ;
2014-01-24 05:39:23 -08:00
removeMessageListenerId ( "Marionette:get" , get ) ;
2012-07-10 13:30:21 -07:00
removeMessageListenerId ( "Marionette:getTitle" , getTitle ) ;
2012-08-09 12:31:12 -07:00
removeMessageListenerId ( "Marionette:getPageSource" , getPageSource ) ;
2014-01-21 10:31:31 -08:00
removeMessageListenerId ( "Marionette:getCurrentUrl" , getCurrentUrl ) ;
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:goBack" , goBack ) ;
removeMessageListenerId ( "Marionette:goForward" , goForward ) ;
removeMessageListenerId ( "Marionette:refresh" , refresh ) ;
removeMessageListenerId ( "Marionette:findElementContent" , findElementContent ) ;
removeMessageListenerId ( "Marionette:findElementsContent" , findElementsContent ) ;
2013-01-14 06:57:54 -08:00
removeMessageListenerId ( "Marionette:getActiveElement" , getActiveElement ) ;
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:clickElement" , clickElement ) ;
removeMessageListenerId ( "Marionette:getElementAttribute" , getElementAttribute ) ;
2012-08-08 13:21:50 -07:00
removeMessageListenerId ( "Marionette:getElementTagName" , getElementTagName ) ;
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:isElementDisplayed" , isElementDisplayed ) ;
2013-05-07 14:38:21 -07:00
removeMessageListenerId ( "Marionette:getElementValueOfCssProperty" , getElementValueOfCssProperty ) ;
2013-10-15 07:40:48 -07:00
removeMessageListenerId ( "Marionette:submitElement" , submitElement ) ;
2012-10-19 13:59:15 -07:00
removeMessageListenerId ( "Marionette:getElementSize" , getElementSize ) ;
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:isElementEnabled" , isElementEnabled ) ;
removeMessageListenerId ( "Marionette:isElementSelected" , isElementSelected ) ;
removeMessageListenerId ( "Marionette:sendKeysToElement" , sendKeysToElement ) ;
2014-01-22 11:04:04 -08:00
removeMessageListenerId ( "Marionette:getElementLocation" , getElementLocation ) ;
2012-05-11 13:06:53 -07:00
removeMessageListenerId ( "Marionette:clearElement" , clearElement ) ;
removeMessageListenerId ( "Marionette:switchToFrame" , switchToFrame ) ;
removeMessageListenerId ( "Marionette:deleteSession" , deleteSession ) ;
removeMessageListenerId ( "Marionette:sleepSession" , sleepSession ) ;
2012-05-18 13:30:43 -07:00
removeMessageListenerId ( "Marionette:emulatorCmdResult" , emulatorCmdResult ) ;
2012-06-04 10:50:06 -07:00
removeMessageListenerId ( "Marionette:importScript" , importScript ) ;
2012-08-23 15:07:16 -07:00
removeMessageListenerId ( "Marionette:getAppCacheStatus" , getAppCacheStatus ) ;
2012-10-31 15:36:57 -07:00
removeMessageListenerId ( "Marionette:setTestName" , setTestName ) ;
2014-01-24 05:45:58 -08:00
removeMessageListenerId ( "Marionette:takeScreenshot" , takeScreenshot ) ;
2012-11-22 07:53:44 -08:00
removeMessageListenerId ( "Marionette:addCookie" , addCookie ) ;
2014-01-21 08:40:20 -08:00
removeMessageListenerId ( "Marionette:getCookies" , getCookies ) ;
2012-11-22 07:53:44 -08:00
removeMessageListenerId ( "Marionette:deleteAllCookies" , deleteAllCookies ) ;
removeMessageListenerId ( "Marionette:deleteCookie" , deleteCookie ) ;
2013-10-17 08:25:11 -07:00
if ( isB2G ) {
content . removeEventListener ( "mozbrowsershowmodalprompt" , modalHandler , false ) ;
}
2013-11-23 21:32:27 -08:00
elementManager . reset ( ) ;
2012-11-13 13:51:58 -08:00
// reset frame to the top-most frame
2013-09-19 10:35:19 -07:00
curFrame = content ;
curFrame . focus ( ) ;
2013-03-04 17:09:58 -08:00
touchIds = { } ;
2012-03-22 08:19:57 -07:00
}
/ *
2014-01-21 08:40:20 -08:00
* Helper methods
2012-03-22 08:19:57 -07:00
* /
/ * *
* Generic method to send a message to the server
* /
function sendToServer ( msg , value , command _id ) {
if ( command _id ) {
value . command _id = command _id ;
}
sendAsyncMessage ( msg , value ) ;
}
/ * *
* Send response back to server
* /
function sendResponse ( value , command _id ) {
sendToServer ( "Marionette:done" , value , command _id ) ;
}
/ * *
* Send ack back to server
* /
function sendOk ( command _id ) {
sendToServer ( "Marionette:ok" , { } , command _id ) ;
}
/ * *
* Send log message to server
* /
function sendLog ( msg ) {
sendToServer ( "Marionette:log" , { message : msg } ) ;
}
/ * *
* Send error message to server
* /
function sendError ( message , status , trace , command _id ) {
let error _msg = { message : message , status : status , stacktrace : trace } ;
sendToServer ( "Marionette:error" , error _msg , command _id ) ;
}
/ * *
* Clear test values after completion of test
* /
function resetValues ( ) {
2012-05-09 12:05:39 -07:00
sandbox = null ;
2013-09-19 10:35:19 -07:00
curFrame = content ;
2013-05-27 10:12:13 -07:00
mouseEventsOnly = false ;
2012-03-22 08:19:57 -07:00
}
2013-09-13 00:33:41 -07:00
/ * *
* Dump a logline to stdout . Prepends logline with a timestamp .
* /
function dumpLog ( logline ) {
dump ( Date . now ( ) + " Marionette: " + logline ) ;
}
2013-06-21 17:13:35 -07:00
2013-09-19 10:35:19 -07:00
/ * *
* 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 ;
}
2012-03-22 08:19:57 -07:00
/ *
* Marionette Methods
* /
/ * *
* Returns a content sandbox that can be used by the execute _foo functions .
* /
2013-01-23 11:26:33 -08:00
function createExecuteContentSandbox ( aWindow , timeout ) {
2013-02-14 01:40:15 -08:00
let sandbox = new Cu . Sandbox ( aWindow , { sandboxPrototype : aWindow } ) ;
2012-05-09 12:05:39 -07:00
sandbox . global = sandbox ;
2012-03-22 08:19:57 -07:00
sandbox . window = aWindow ;
sandbox . document = sandbox . window . document ;
sandbox . navigator = sandbox . window . navigator ;
2012-04-10 17:28:08 -07:00
sandbox . testUtils = utils ;
2013-03-25 11:09:16 -07:00
sandbox . asyncTestCommandId = asyncTestCommandId ;
2012-03-22 08:19:57 -07:00
2012-10-31 23:51:30 -07:00
let marionette = new Marionette ( this , aWindow , "content" ,
2013-08-28 10:06:03 -07:00
marionetteLogObj , timeout ,
heartbeatCallback ,
marionetteTestName ) ;
2012-05-09 12:05:39 -07:00
sandbox . marionette = marionette ;
2012-03-22 08:19:57 -07:00
marionette . exports . forEach ( function ( fn ) {
2012-09-14 14:34:38 -07:00
try {
sandbox [ fn ] = marionette [ fn ] . bind ( marionette ) ;
}
catch ( e ) {
sandbox [ fn ] = marionette [ fn ] ;
}
2012-03-22 08:19:57 -07:00
} ) ;
2013-01-23 11:26:33 -08:00
XPCOMUtils . defineLazyGetter ( sandbox , 'SpecialPowers' , function ( ) {
return new SpecialPowers ( aWindow ) ;
} ) ;
2012-05-18 13:30:13 -07:00
2013-03-25 11:09:16 -07:00
sandbox . asyncComplete = function sandbox _asyncComplete ( value , status , stack , commandId ) {
if ( commandId == asyncTestCommandId ) {
2013-09-19 10:35:19 -07:00
curFrame . removeEventListener ( "unload" , onunload , false ) ;
curFrame . clearTimeout ( asyncTestTimeoutId ) ;
2012-05-09 12:05:39 -07:00
2013-08-28 10:06:03 -07:00
if ( inactivityTimeoutId != null ) {
2013-09-19 10:35:19 -07:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 10:06:03 -07:00
}
2013-04-29 09:38:54 -07:00
sendSyncMessage ( "Marionette:shareData" ,
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2013-03-25 11:09:16 -07:00
marionetteLogObj . clearLogs ( ) ;
2012-11-27 13:58:58 -08:00
2013-03-25 11:09:16 -07:00
if ( status == 0 ) {
if ( Object . keys ( _emu _cbs ) . length ) {
_emu _cbs = { } ;
sendError ( "Emulator callback still pending when finish() called" ,
500 , null , commandId ) ;
}
else {
sendResponse ( { value : elementManager . wrapValue ( value ) , status : status } ,
commandId ) ;
}
2012-11-27 13:58:58 -08:00
}
else {
2013-03-25 11:09:16 -07:00
sendError ( value , status , stack , commandId ) ;
2012-11-27 13:58:58 -08:00
}
2012-05-09 12:05:39 -07:00
2013-03-25 11:09:16 -07:00
asyncTestRunning = false ;
asyncTestTimeoutId = undefined ;
asyncTestCommandId = undefined ;
2013-08-28 10:06:03 -07:00
inactivityTimeoutId = null ;
2013-03-25 11:09:16 -07:00
}
2012-05-09 12:05:39 -07:00
} ;
sandbox . finish = function sandbox _finish ( ) {
if ( asyncTestRunning ) {
2013-03-25 11:09:16 -07:00
sandbox . asyncComplete ( marionette . generate _results ( ) , 0 , null , sandbox . asyncTestCommandId ) ;
2012-05-09 12:05:39 -07:00
} else {
return marionette . generate _results ( ) ;
}
} ;
sandbox . marionetteScriptFinished = function sandbox _marionetteScriptFinished ( value ) {
2013-03-25 11:09:16 -07:00
return sandbox . asyncComplete ( value , 0 , null , sandbox . asyncTestCommandId ) ;
2012-05-09 12:05:39 -07:00
} ;
2012-03-22 08:19:57 -07:00
return sandbox ;
}
/ * *
* Execute the given script either as a function body ( executeScript )
* or directly ( for 'mochitest' like JS Marionette tests )
* /
function executeScript ( msg , directInject ) {
2013-08-28 10:06:03 -07:00
// Set up inactivity timeout.
if ( msg . json . inactivityTimeout ) {
let setTimer = function ( ) {
2013-09-19 10:35:19 -07:00
inactivityTimeoutId = curFrame . setTimeout ( function ( ) {
2013-08-28 10:06:03 -07:00
sendError ( 'timed out due to inactivity' , 28 , null , asyncTestCommandId ) ;
} , msg . json . inactivityTimeout ) ;
} ;
setTimer ( ) ;
heartbeatCallback = function resetInactivityTimeout ( ) {
2013-09-19 10:35:19 -07:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 10:06:03 -07:00
setTimer ( ) ;
} ;
}
2013-02-12 10:10:03 -08:00
asyncTestCommandId = msg . json . command _id ;
2013-10-01 08:13:04 -07:00
let script = msg . json . script ;
2012-03-22 08:19:57 -07:00
2012-05-09 12:05:39 -07:00
if ( msg . json . newSandbox || ! sandbox ) {
2013-09-19 10:35:19 -07:00
sandbox = createExecuteContentSandbox ( curFrame ,
2013-01-23 11:26:33 -08:00
msg . json . timeout ) ;
2012-05-09 12:05:39 -07:00
if ( ! sandbox ) {
2013-02-12 10:10:03 -08:00
sendError ( "Could not create sandbox!" , 500 , null , asyncTestCommandId ) ;
2012-05-09 12:05:39 -07:00
return ;
}
}
2013-03-25 11:09:16 -07:00
else {
sandbox . asyncTestCommandId = asyncTestCommandId ;
}
2012-03-22 08:19:57 -07:00
try {
if ( directInject ) {
2012-06-04 10:50:06 -07:00
if ( importedScripts . exists ( ) ) {
2014-01-21 08:40:20 -08:00
let stream = Components . classes [ "@mozilla.org/network/file-input-stream;1" ] .
2012-06-04 10:50:06 -07:00
createInstance ( Components . interfaces . nsIFileInputStream ) ;
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
script = data + script ;
}
2013-06-21 17:13:35 -07:00
let res = Cu . evalInSandbox ( script , sandbox , "1.8" , "dummy file" , 0 ) ;
2013-04-29 09:38:54 -07:00
sendSyncMessage ( "Marionette:shareData" ,
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2012-03-22 08:19:57 -07:00
marionetteLogObj . clearLogs ( ) ;
2013-04-29 09:38:54 -07:00
2012-03-22 08:19:57 -07:00
if ( res == undefined || res . passed == undefined ) {
2012-11-26 18:19:04 -08:00
sendError ( "Marionette.finish() not called" , 17 , null , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
else {
2012-11-26 18:19:04 -08:00
sendResponse ( { value : elementManager . wrapValue ( res ) } , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
}
else {
2012-05-09 12:05:39 -07:00
try {
sandbox . _ _marionetteParams = elementManager . convertWrappedArguments (
2013-09-19 10:35:19 -07:00
msg . json . args , curFrame ) ;
2012-05-09 12:05:39 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , asyncTestCommandId ) ;
2012-05-09 12:05:39 -07:00
return ;
}
2013-06-21 17:13:35 -07:00
script = "let __marionetteFunc = function(){" + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams);" ;
2012-06-04 10:50:06 -07:00
if ( importedScripts . exists ( ) ) {
2014-01-21 08:40:20 -08:00
let stream = Components . classes [ "@mozilla.org/network/file-input-stream;1" ] .
2012-06-04 10:50:06 -07:00
createInstance ( Components . interfaces . nsIFileInputStream ) ;
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
2013-06-21 17:13:35 -07:00
script = data + script ;
2012-06-04 10:50:06 -07:00
}
2013-06-21 17:13:35 -07:00
let res = Cu . evalInSandbox ( script , sandbox , "1.8" , "dummy file" , 0 ) ;
2013-04-29 09:38:54 -07:00
sendSyncMessage ( "Marionette:shareData" ,
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2012-03-22 08:19:57 -07:00
marionetteLogObj . clearLogs ( ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : elementManager . wrapValue ( res ) } , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
}
catch ( e ) {
// 17 = JavascriptException
2013-06-21 17:13:35 -07:00
let error = createStackMessage ( e ,
"execute_script" ,
msg . json . filename ,
msg . json . line ,
script ) ;
sendError ( error [ 0 ] , 17 , error [ 1 ] , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
}
2012-10-31 15:36:57 -07:00
/ * *
* Sets the test name , used in logging messages .
* /
function setTestName ( msg ) {
marionetteTestName = msg . json . value ;
2012-11-26 18:19:04 -08:00
sendOk ( msg . json . command _id ) ;
2012-10-31 15:36:57 -07:00
}
2012-03-22 08:19:57 -07:00
/ * *
* Execute async script
* /
function executeAsyncScript ( msg ) {
executeWithCallback ( msg ) ;
}
/ * *
* Execute pure JS test . Handles both async and sync cases .
* /
function executeJSScript ( msg ) {
2012-11-28 13:31:23 -08:00
if ( msg . json . async ) {
executeWithCallback ( msg , msg . json . async ) ;
2012-03-22 08:19:57 -07:00
}
else {
executeScript ( msg , true ) ;
}
}
/ * *
* This function is used by executeAsync and executeJSScript to execute a script
2014-01-21 08:40:20 -08:00
* in a sandbox .
*
2012-03-22 08:19:57 -07:00
* For executeJSScript , it will return a message only when the finish ( ) method is called .
2014-01-21 08:40:20 -08:00
* For executeAsync , it will return a response when marionetteScriptFinished / arguments [ arguments . length - 1 ]
2012-03-22 08:19:57 -07:00
* method is called , or if it times out .
* /
2012-11-30 07:42:43 -08:00
function executeWithCallback ( msg , useFinish ) {
2013-08-28 10:06:03 -07:00
// Set up inactivity timeout.
if ( msg . json . inactivityTimeout ) {
let setTimer = function ( ) {
2013-09-19 10:35:19 -07:00
inactivityTimeoutId = curFrame . setTimeout ( function ( ) {
2013-08-28 10:06:03 -07:00
sandbox . asyncComplete ( 'timed out due to inactivity' , 28 , null , asyncTestCommandId ) ;
} , msg . json . inactivityTimeout ) ;
} ;
setTimer ( ) ;
heartbeatCallback = function resetInactivityTimeout ( ) {
2013-09-19 10:35:19 -07:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 10:06:03 -07:00
setTimer ( ) ;
} ;
}
2013-10-01 08:13:04 -07:00
let script = msg . json . script ;
2013-02-12 10:10:03 -08:00
asyncTestCommandId = msg . json . command _id ;
onunload = function ( ) {
sendError ( "unload was called" , 17 , null , asyncTestCommandId ) ;
} ;
2013-09-19 10:35:19 -07:00
curFrame . addEventListener ( "unload" , onunload , false ) ;
2012-03-22 08:19:57 -07:00
2012-05-10 16:07:07 -07:00
if ( msg . json . newSandbox || ! sandbox ) {
2013-09-19 10:35:19 -07:00
sandbox = createExecuteContentSandbox ( curFrame ,
2013-01-23 11:26:33 -08:00
msg . json . timeout ) ;
2012-05-10 16:07:07 -07:00
if ( ! sandbox ) {
2013-02-12 10:10:03 -08:00
sendError ( "Could not create sandbox!" , 17 , null , asyncTestCommandId ) ;
2012-05-10 16:07:07 -07:00
return ;
}
}
2013-03-25 11:09:16 -07:00
else {
sandbox . asyncTestCommandId = asyncTestCommandId ;
}
2012-11-26 18:19:04 -08:00
sandbox . tag = script ;
2012-05-10 16:07:07 -07:00
2012-03-22 08:19:57 -07:00
// Error code 28 is scriptTimeout, but spec says execute_async should return 21 (Timeout),
// see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute_async.
// 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.
2013-09-19 10:35:19 -07:00
asyncTestTimeoutId = curFrame . setTimeout ( function ( ) {
2013-02-12 10:10:03 -08:00
sandbox . asyncComplete ( 'timed out' , 28 , null , asyncTestCommandId ) ;
2012-11-28 13:31:23 -08:00
} , msg . json . timeout ) ;
2012-05-10 16:07:07 -07:00
2013-09-19 10:35:19 -07:00
originalOnError = curFrame . onerror ;
curFrame . onerror = function errHandler ( errMsg , url , line ) {
2013-04-05 15:25:46 -07:00
sandbox . asyncComplete ( errMsg , 17 , "@" + url + ", line " + line , asyncTestCommandId ) ;
2013-09-19 10:35:19 -07:00
curFrame . onerror = originalOnError ;
2012-12-18 06:21:28 -08:00
} ;
2012-03-22 08:19:57 -07:00
let scriptSrc ;
2012-11-30 07:42:43 -08:00
if ( useFinish ) {
2012-11-28 13:31:23 -08:00
if ( msg . json . timeout == null || msg . json . timeout == 0 ) {
2012-11-26 18:19:04 -08:00
sendError ( "Please set a timeout" , 21 , null , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
scriptSrc = script ;
}
else {
2012-05-09 12:05:39 -07:00
try {
sandbox . _ _marionetteParams = elementManager . convertWrappedArguments (
2013-09-19 10:35:19 -07:00
msg . json . args , curFrame ) ;
2012-05-09 12:05:39 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , asyncTestCommandId ) ;
2012-05-09 12:05:39 -07:00
return ;
}
scriptSrc = "__marionetteParams.push(marionetteScriptFinished);" +
2012-03-22 08:19:57 -07:00
"let __marionetteFunc = function() { " + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams); " ;
}
try {
2012-05-09 12:05:39 -07:00
asyncTestRunning = true ;
2012-06-04 10:50:06 -07:00
if ( importedScripts . exists ( ) ) {
2012-08-31 13:59:48 -07:00
let stream = Cc [ "@mozilla.org/network/file-input-stream;1" ] .
createInstance ( Ci . nsIFileInputStream ) ;
2012-06-04 10:50:06 -07:00
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
scriptSrc = data + scriptSrc ;
}
2013-06-21 17:13:35 -07:00
Cu . evalInSandbox ( scriptSrc , sandbox , "1.8" , "dummy file" , 0 ) ;
2012-03-22 08:19:57 -07:00
} catch ( e ) {
// 17 = JavascriptException
2013-06-21 17:13:35 -07:00
let error = createStackMessage ( e ,
"execute_async_script" ,
msg . json . filename ,
msg . json . line ,
scriptSrc ) ;
sandbox . asyncComplete ( error [ 0 ] , 17 , error [ 1 ] , asyncTestCommandId ) ;
2012-03-22 08:19:57 -07:00
}
}
2013-01-22 11:27:44 -08:00
/ * *
* This function creates a touch event given a touch type and a touch
* /
function emitTouchEvent ( type , touch ) {
2013-09-19 10:35:19 -07:00
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 ) ;
2014-01-15 06:28:04 -08:00
domWindowUtils . sendTouchEvent ( type , [ touch . identifier ] , [ touch . clientX ] , [ touch . clientY ] , [ touch . radiusX ] , [ touch . radiusY ] , [ touch . rotationAngle ] , [ touch . force ] , 1 , 0 ) ;
2013-09-19 10:35:19 -07:00
}
2013-01-22 11:27:44 -08:00
}
2013-04-10 16:55:43 -07:00
/ * *
* This function emit mouse event
* @ param : doc is the current document
* type is the type of event to dispatch
2014-01-15 06:28:04 -08:00
* clickCount is the number of clicks , button notes the mouse button
2013-04-10 16:55:43 -07:00
* elClientX and elClientY are the coordinates of the mouse relative to the viewport
* /
2014-01-15 06:28:04 -08:00
function emitMouseEvent ( doc , type , elClientX , elClientY , clickCount , button ) {
2013-09-19 10:35:19 -07:00
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 ( ) ;
* /
let win = doc . defaultView ;
2014-01-15 06:28:04 -08:00
let domUtils = win . QueryInterface ( Components . interfaces . nsIInterfaceRequestor ) . getInterface ( Components . interfaces . nsIDOMWindowUtils ) ;
domUtils . sendMouseEvent ( type , elClientX , elClientY , button || 0 , clickCount || 1 , 0 , false , 0 , inputSource ) ;
2013-09-19 10:35:19 -07:00
}
2013-04-10 16:55:43 -07:00
}
/ * *
2013-05-27 10:12:13 -07:00
* Helper function that perform a mouse tap
2013-04-10 16:55:43 -07:00
* /
2013-05-27 10:12:13 -07:00
function mousetap ( doc , x , y ) {
emitMouseEvent ( doc , 'mousemove' , x , y ) ;
emitMouseEvent ( doc , 'mousedown' , x , y ) ;
emitMouseEvent ( doc , 'mouseup' , x , y ) ;
2013-04-10 16:55:43 -07:00
}
2013-01-22 11:27:44 -08:00
/ * *
2013-05-27 10:12:13 -07:00
* This function generates a pair of coordinates relative to the viewport given a
* target element and coordinates relative to that element ' s top - left corner .
* @ param 'x' , and 'y' are the relative to the target .
2013-01-22 11:27:44 -08:00
* If they are not specified , then the center of the target is used .
* /
2013-05-27 10:12:13 -07:00
function coordinates ( target , x , y ) {
2013-03-04 17:09:58 -08:00
let box = target . getBoundingClientRect ( ) ;
2013-05-27 10:12:13 -07:00
if ( x == null ) {
x = box . width / 2 ;
2013-01-22 11:27:44 -08:00
}
2013-05-27 10:12:13 -07:00
if ( y == null ) {
y = box . height / 2 ;
2013-01-22 11:27:44 -08:00
}
2013-05-27 10:12:13 -07:00
let coords = { } ;
coords . x = box . left + x ;
coords . y = box . top + y ;
2013-01-22 11:27:44 -08:00
return coords ;
}
/ * *
2013-08-07 16:19:59 -07:00
* This function returns if the element is in viewport
2013-01-22 11:27:44 -08:00
* /
function elementInViewport ( el ) {
2013-04-17 00:42:10 -07:00
let rect = el . getBoundingClientRect ( ) ;
2013-09-19 10:35:19 -07:00
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 ) ;
2013-01-22 11:27:44 -08:00
}
/ * *
* This function throws the visibility of the element error
* /
2013-05-27 10:12:13 -07:00
function checkVisible ( el ) {
2013-03-01 13:29:10 -08:00
//check if the element is visible
let visible = utils . isElementDisplayed ( el ) ;
if ( ! visible ) {
return false ;
}
2013-04-11 13:52:42 -07:00
if ( el . tagName . toLowerCase ( ) === 'body' ) {
return true ;
}
2013-03-01 13:29:10 -08:00
if ( ! elementInViewport ( el ) ) {
2013-01-22 11:27:44 -08:00
//check if scroll function exist. If so, call it.
if ( el . scrollIntoView ) {
2013-09-05 10:38:36 -07:00
el . scrollIntoView ( false ) ;
2013-03-01 13:29:10 -08:00
if ( ! elementInViewport ( el ) ) {
return false ;
}
2013-01-22 11:27:44 -08:00
}
2013-03-01 13:29:10 -08:00
else {
2013-01-22 11:27:44 -08:00
return false ;
}
2013-03-01 13:29:10 -08:00
}
return true ;
2013-01-22 11:27:44 -08:00
}
2013-05-27 10:12:13 -07:00
//x and y are coordinates relative to the viewport
function generateEvents ( type , x , y , touchId , target ) {
lastCoordinates = [ x , y ] ;
2013-09-19 10:35:19 -07:00
let doc = curFrame . document ;
2013-05-27 10:12:13 -07:00
switch ( type ) {
case 'tap' :
if ( mouseEventsOnly ) {
mousetap ( target . ownerDocument , x , y ) ;
}
else {
let touchId = nextTouchId ++ ;
let touch = createATouch ( target , x , y , touchId ) ;
emitTouchEvent ( 'touchstart' , touch ) ;
emitTouchEvent ( 'touchend' , touch ) ;
mousetap ( target . ownerDocument , x , y ) ;
}
lastCoordinates = null ;
break ;
case 'press' :
isTap = true ;
if ( mouseEventsOnly ) {
emitMouseEvent ( doc , 'mousemove' , x , y ) ;
emitMouseEvent ( doc , 'mousedown' , x , y ) ;
}
else {
let touchId = nextTouchId ++ ;
let touch = createATouch ( target , x , y , touchId ) ;
emitTouchEvent ( 'touchstart' , touch ) ;
touchIds [ touchId ] = touch ;
return touchId ;
}
break ;
case 'release' :
if ( mouseEventsOnly ) {
emitMouseEvent ( doc , 'mouseup' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] ) ;
}
else {
let touch = touchIds [ touchId ] ;
touch = createATouch ( touch . target , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] , touchId ) ;
emitTouchEvent ( 'touchend' , touch ) ;
if ( isTap ) {
mousetap ( touch . target . ownerDocument , touch . clientX , touch . clientY ) ;
}
delete touchIds [ touchId ] ;
}
isTap = false ;
lastCoordinates = null ;
break ;
case 'cancel' :
isTap = false ;
if ( mouseEventsOnly ) {
emitMouseEvent ( doc , 'mouseup' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] ) ;
}
else {
emitTouchEvent ( 'touchcancel' , touchIds [ touchId ] ) ;
delete touchIds [ touchId ] ;
}
lastCoordinates = null ;
break ;
case 'move' :
isTap = false ;
if ( mouseEventsOnly ) {
emitMouseEvent ( doc , 'mousemove' , x , y ) ;
}
else {
touch = createATouch ( touchIds [ touchId ] . target , x , y , touchId ) ;
touchIds [ touchId ] = touch ;
emitTouchEvent ( 'touchmove' , touch ) ;
}
break ;
case 'contextmenu' :
isTap = false ;
2013-09-19 10:35:19 -07:00
let event = curFrame . document . createEvent ( 'HTMLEvents' ) ;
2013-05-27 10:12:13 -07:00
event . initEvent ( 'contextmenu' , true , true ) ;
if ( mouseEventsOnly ) {
target = doc . elementFromPoint ( lastCoordinates [ 0 ] , lastCoordinates [ 1 ] ) ;
}
else {
target = touchIds [ touchId ] . target ;
}
target . dispatchEvent ( event ) ;
break ;
default :
throw { message : "Unknown event type: " + type , code : 500 , stack : null } ;
}
2013-09-19 10:35:19 -07:00
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 } ) ;
}
2013-05-27 10:12:13 -07:00
}
2013-01-22 11:27:44 -08:00
/ * *
* Function that perform a single tap
* /
function singleTap ( msg ) {
let command _id = msg . json . command _id ;
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-05-27 10:12:13 -07:00
// 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 ;
2013-04-10 16:55:43 -07:00
}
2013-09-19 10:35:19 -07:00
if ( ! curFrame . document . createTouch ) {
2013-05-27 10:12:13 -07:00
mouseEventsOnly = true ;
2013-04-10 16:55:43 -07:00
}
2013-05-27 10:12:13 -07:00
let c = coordinates ( el , msg . json . corx , msg . json . cory ) ;
generateEvents ( 'tap' , c . x , c . y , null , el ) ;
2013-01-22 11:27:44 -08:00
sendOk ( msg . json . command _id ) ;
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , msg . json . command _id ) ;
}
}
2013-02-06 11:58:14 -08:00
/ * *
* Function to create a touch based on the element
2013-05-27 10:12:13 -07:00
* corx and cory are relative to the viewport , id is the touchId
2013-02-06 11:58:14 -08:00
* /
2013-05-27 10:12:13 -07:00
function createATouch ( el , corx , cory , touchId ) {
2013-03-04 17:09:58 -08:00
let doc = el . ownerDocument ;
let win = doc . defaultView ;
2013-05-27 10:12:13 -07:00
let clientX = corx ;
let clientY = cory ;
2013-03-04 17:09:58 -08:00
let pageX = clientX + win . pageXOffset ,
2013-02-06 11:58:14 -08:00
pageY = clientY + win . pageYOffset ;
2013-03-04 17:09:58 -08:00
let screenX = clientX + win . mozInnerScreenX ,
2013-02-06 11:58:14 -08:00
screenY = clientY + win . mozInnerScreenY ;
2013-05-27 10:12:13 -07:00
let atouch = doc . createTouch ( win , el , touchId , pageX , pageY , screenX , screenY , clientX , clientY ) ;
2013-02-06 11:58:14 -08:00
return atouch ;
}
2013-03-04 17:09:58 -08:00
/ * *
* Function to emit touch events for each finger . e . g . finger = [ [ 'press' , id ] , [ 'wait' , 5 ] , [ 'release' ] ]
2014-01-21 08:40:20 -08:00
* touchId represents the finger id , i keeps track of the current action of the chain
2013-03-04 17:09:58 -08:00
* /
2013-05-27 10:12:13 -07:00
function actions ( chain , touchId , command _id , i ) {
2013-03-04 17:09:58 -08:00
if ( typeof i === "undefined" ) {
i = 0 ;
}
2013-05-27 10:12:13 -07:00
if ( i == chain . length ) {
2013-03-22 11:29:02 -07:00
sendResponse ( { value : touchId } , command _id ) ;
2013-03-04 17:09:58 -08:00
return ;
}
2013-05-27 10:12:13 -07:00
let pack = chain [ i ] ;
2013-03-04 17:09:58 -08:00
let command = pack [ 0 ] ;
let el ;
2013-05-27 10:12:13 -07:00
let c ;
2013-03-04 17:09:58 -08:00
i ++ ;
2013-05-27 10:12:13 -07:00
if ( command != 'press' ) {
//if mouseEventsOnly, then touchIds isn't used
if ( ! ( touchId in touchIds ) && ! mouseEventsOnly ) {
sendError ( "Element has not been pressed" , 500 , null , command _id ) ;
return ;
}
}
2013-03-04 17:09:58 -08:00
switch ( command ) {
case 'press' :
2013-05-27 10:12:13 -07:00
if ( lastCoordinates ) {
generateEvents ( 'cancel' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] , touchId ) ;
sendError ( "Invalid Command: press cannot follow an active touch event" , 500 , null , command _id ) ;
2013-04-15 13:19:22 -07:00
return ;
}
2013-09-19 10:35:19 -07:00
el = elementManager . getKnownElement ( pack [ 1 ] , curFrame ) ;
2013-05-27 10:12:13 -07:00
c = coordinates ( el , pack [ 2 ] , pack [ 3 ] ) ;
touchId = generateEvents ( 'press' , c . x , c . y , null , el ) ;
actions ( chain , touchId , command _id , i ) ;
2013-03-04 17:09:58 -08:00
break ;
case 'release' :
2013-05-27 10:12:13 -07:00
generateEvents ( 'release' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] , touchId ) ;
actions ( chain , null , command _id , i ) ;
2013-03-04 17:09:58 -08:00
break ;
case 'move' :
2013-09-19 10:35:19 -07:00
el = elementManager . getKnownElement ( pack [ 1 ] , curFrame ) ;
2013-05-27 10:12:13 -07:00
c = coordinates ( el ) ;
generateEvents ( 'move' , c . x , c . y , touchId ) ;
actions ( chain , touchId , command _id , i ) ;
2013-03-04 17:09:58 -08:00
break ;
case 'moveByOffset' :
2013-05-27 10:12:13 -07:00
generateEvents ( 'move' , lastCoordinates [ 0 ] + pack [ 1 ] , lastCoordinates [ 1 ] + pack [ 2 ] , touchId ) ;
actions ( chain , touchId , command _id , i ) ;
2013-03-04 17:09:58 -08:00
break ;
case 'wait' :
if ( pack [ 1 ] != null ) {
let time = pack [ 1 ] * 1000 ;
2013-04-10 16:55:43 -07:00
// standard waiting time to fire contextmenu
2013-05-27 10:12:13 -07:00
let standard = 750 ;
try {
standard = Services . prefs . getIntPref ( "ui.click_hold_context_menus.delay" ) ;
}
catch ( e ) { }
if ( time >= standard && isTap ) {
chain . splice ( i , 0 , [ 'longPress' ] , [ 'wait' , ( time - standard ) / 1000 ] ) ;
2013-04-10 16:55:43 -07:00
time = standard ;
}
2013-05-27 10:12:13 -07:00
checkTimer . initWithCallback ( function ( ) { actions ( chain , touchId , command _id , i ) ; } , time , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2013-03-04 17:09:58 -08:00
}
else {
2013-05-27 10:12:13 -07:00
actions ( chain , touchId , command _id , i ) ;
2013-03-04 17:09:58 -08:00
}
break ;
2013-03-19 14:59:48 -07:00
case 'cancel' :
2013-05-27 10:12:13 -07:00
generateEvents ( 'cancel' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] , touchId ) ;
actions ( chain , touchId , command _id , i ) ;
2013-03-19 14:59:48 -07:00
break ;
2013-04-01 15:21:06 -07:00
case 'longPress' :
2013-05-27 10:12:13 -07:00
generateEvents ( 'contextmenu' , lastCoordinates [ 0 ] , lastCoordinates [ 1 ] , touchId ) ;
actions ( chain , touchId , command _id , i ) ;
2013-04-01 15:21:06 -07:00
break ;
2013-03-04 17:09:58 -08:00
}
}
/ * *
2014-01-21 08:40:20 -08:00
* Function to start action chain on one finger
2013-03-04 17:09:58 -08:00
* /
function actionChain ( msg ) {
let command _id = msg . json . command _id ;
2013-03-22 11:29:02 -07:00
let args = msg . json . chain ;
let touchId = msg . json . nextId ;
2013-03-04 17:09:58 -08:00
try {
2013-09-19 10:35:19 -07:00
let commandArray = elementManager . convertWrappedArguments ( args , curFrame ) ;
2013-03-04 17:09:58 -08:00
// loop the action array [ ['press', id], ['move', id], ['release', id] ]
2013-03-22 11:29:02 -07:00
if ( touchId == null ) {
touchId = nextTouchId ++ ;
}
2013-09-19 10:35:19 -07:00
if ( ! curFrame . document . createTouch ) {
2013-05-27 10:12:13 -07:00
mouseEventsOnly = true ;
}
2013-03-04 17:09:58 -08:00
actions ( commandArray , touchId , command _id ) ;
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , msg . json . command _id ) ;
}
}
2013-03-18 13:42:46 -07:00
/ * *
* Function to emit touch events which allow multi touch on the screen
* @ param type represents the type of event , touch represents the current touch , touches are all pending touches
* /
function emitMultiEvents ( type , touch , touches ) {
let target = touch . target ;
let doc = target . ownerDocument ;
let win = doc . defaultView ;
// touches that are in the same document
let documentTouches = doc . createTouchList ( touches . filter ( function ( t ) {
2013-06-04 10:47:53 -07:00
return ( ( t . target . ownerDocument === doc ) && ( type != 'touchcancel' ) ) ;
2013-03-18 13:42:46 -07:00
} ) ) ;
// touches on the same target
let targetTouches = doc . createTouchList ( touches . filter ( function ( t ) {
2013-06-04 10:47:53 -07:00
return ( ( t . target === target ) && ( ( type != 'touchcancel' ) || ( type != 'touchend' ) ) ) ;
2013-03-18 13:42:46 -07:00
} ) ) ;
// Create changed touches
let changedTouches = doc . createTouchList ( touch ) ;
// Create the event object
2014-01-15 06:28:04 -08:00
let event = doc . createEvent ( 'TouchEvent' ) ;
2013-03-18 13:42:46 -07:00
event . initTouchEvent ( type ,
true ,
true ,
win ,
0 ,
false , false , false , false ,
documentTouches ,
targetTouches ,
changedTouches ) ;
target . dispatchEvent ( event ) ;
}
/ * *
* Function to dispatch one set of actions
* @ param touches represents all pending touches , batchIndex represents the batch we are dispatching right now
* /
function setDispatch ( batches , touches , command _id , batchIndex ) {
if ( typeof batchIndex === "undefined" ) {
batchIndex = 0 ;
}
// check if all the sets have been fired
if ( batchIndex >= batches . length ) {
multiLast = { } ;
sendOk ( command _id ) ;
return ;
}
// a set of actions need to be done
let batch = batches [ batchIndex ] ;
// each action for some finger
let pack ;
// the touch id for the finger (pack)
let touchId ;
// command for the finger
let command ;
// touch that will be created for the finger
let el ;
let corx ;
let cory ;
let touch ;
let lastTouch ;
let touchIndex ;
let waitTime = 0 ;
let maxTime = 0 ;
2013-05-27 10:12:13 -07:00
let c ;
2013-03-18 13:42:46 -07:00
batchIndex ++ ;
// loop through the batch
for ( let i = 0 ; i < batch . length ; i ++ ) {
pack = batch [ i ] ;
touchId = pack [ 0 ] ;
command = pack [ 1 ] ;
switch ( command ) {
case 'press' :
2013-09-19 10:35:19 -07:00
el = elementManager . getKnownElement ( pack [ 2 ] , curFrame ) ;
2013-05-27 10:12:13 -07:00
c = coordinates ( el , pack [ 3 ] , pack [ 4 ] ) ;
touch = createATouch ( el , c . x , c . y , touchId ) ;
2013-03-18 13:42:46 -07:00
multiLast [ touchId ] = touch ;
touches . push ( touch ) ;
emitMultiEvents ( 'touchstart' , touch , touches ) ;
break ;
case 'release' :
touch = multiLast [ touchId ] ;
// the index of the previous touch for the finger may change in the touches array
touchIndex = touches . indexOf ( touch ) ;
touches . splice ( touchIndex , 1 ) ;
emitMultiEvents ( 'touchend' , touch , touches ) ;
break ;
case 'move' :
2013-09-19 10:35:19 -07:00
el = elementManager . getKnownElement ( pack [ 2 ] , curFrame ) ;
2013-05-27 10:12:13 -07:00
c = coordinates ( el ) ;
touch = createATouch ( multiLast [ touchId ] . target , c . x , c . y , touchId ) ;
2013-03-18 13:42:46 -07:00
touchIndex = touches . indexOf ( lastTouch ) ;
touches [ touchIndex ] = touch ;
multiLast [ touchId ] = touch ;
emitMultiEvents ( 'touchmove' , touch , touches ) ;
break ;
case 'moveByOffset' :
el = multiLast [ touchId ] . target ;
lastTouch = multiLast [ touchId ] ;
touchIndex = touches . indexOf ( lastTouch ) ;
let doc = el . ownerDocument ;
let win = doc . defaultView ;
// since x and y are relative to the last touch, therefore, it's relative to the position of the last touch
let clientX = lastTouch . clientX + pack [ 2 ] ,
clientY = lastTouch . clientY + pack [ 3 ] ;
let pageX = clientX + win . pageXOffset ,
pageY = clientY + win . pageYOffset ;
let screenX = clientX + win . mozInnerScreenX ,
screenY = clientY + win . mozInnerScreenY ;
touch = doc . createTouch ( win , el , touchId , pageX , pageY , screenX , screenY , clientX , clientY ) ;
touches [ touchIndex ] = touch ;
multiLast [ touchId ] = touch ;
emitMultiEvents ( 'touchmove' , touch , touches ) ;
break ;
case 'wait' :
if ( pack [ 2 ] != undefined ) {
waitTime = pack [ 2 ] * 1000 ;
if ( waitTime > maxTime ) {
maxTime = waitTime ;
}
}
break ;
} //end of switch block
} //end of for loop
if ( maxTime != 0 ) {
checkTimer . initWithCallback ( function ( ) { setDispatch ( batches , touches , command _id , batchIndex ) ; } , maxTime , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
else {
setDispatch ( batches , touches , command _id , batchIndex ) ;
}
}
/ * *
* Function to start multi - action
* /
function multiAction ( msg ) {
let command _id = msg . json . command _id ;
let args = msg . json . value ;
// maxlen is the longest action chain for one finger
let maxlen = msg . json . maxlen ;
try {
// unwrap the original nested array
2013-09-19 10:35:19 -07:00
let commandArray = elementManager . convertWrappedArguments ( args , curFrame ) ;
2013-03-18 13:42:46 -07:00
let concurrentEvent = [ ] ;
let temp ;
for ( let i = 0 ; i < maxlen ; i ++ ) {
let row = [ ] ;
for ( let j = 0 ; j < commandArray . length ; j ++ ) {
if ( commandArray [ j ] [ i ] != undefined ) {
// add finger id to the front of each action, i.e. [finger_id, action, element]
temp = commandArray [ j ] [ i ] ;
temp . unshift ( j ) ;
row . push ( temp ) ;
}
}
concurrentEvent . push ( row ) ;
}
// now concurrent event is made of sets where each set contain a list of actions that need to be fired.
// note: each action belongs to a different finger
// pendingTouches keeps track of current touches that's on the screen
let pendingTouches = [ ] ;
setDispatch ( concurrentEvent , pendingTouches , command _id ) ;
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , msg . json . command _id ) ;
}
}
2012-03-22 08:19:57 -07:00
/ * *
2014-01-24 05:39:23 -08:00
* Navigate to the given URL . The operation will be performed on the
* current browser context , and handles the case where we navigate
* within an iframe . All other navigation is handled by the server
* ( in chrome space ) .
2012-03-22 08:19:57 -07:00
* /
2014-01-24 05:39:23 -08:00
function get ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2013-06-04 23:59:05 -07:00
let checkTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
let start = new Date ( ) . getTime ( ) ;
let end = null ;
2014-01-24 05:39:23 -08:00
function checkLoad ( ) {
2013-07-17 15:13:09 -07:00
checkTimer . cancel ( ) ;
2013-06-04 23:59:05 -07:00
end = new Date ( ) . getTime ( ) ;
let errorRegex = /about:.+(error)|(blocked)\?/ ;
let elapse = end - start ;
2014-01-24 05:39:23 -08:00
if ( msg . json . pageTimeout == null || elapse <= msg . json . pageTimeout ) {
if ( curFrame . document . readyState == "complete" ) {
2013-07-17 15:13:09 -07:00
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2013-06-04 23:59:05 -07:00
sendOk ( command _id ) ;
}
2014-01-24 05:39:23 -08:00
else if ( curFrame . document . readyState == "interactive" &&
errorRegex . exec ( curFrame . document . baseURI ) ) {
2013-07-17 15:13:09 -07:00
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2013-06-04 23:59:05 -07:00
sendError ( "Error loading page" , 13 , null , command _id ) ;
}
2014-01-24 05:39:23 -08:00
else {
2013-06-04 23:59:05 -07:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
}
2014-01-24 05:39:23 -08:00
else {
2013-07-17 15:13:09 -07:00
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2014-01-24 05:39:23 -08:00
sendError ( "Error loading page, timed out (checkLoad)" , 21 , null ,
command _id ) ;
2013-06-04 23:59:05 -07:00
}
}
2014-01-24 05:39:23 -08:00
// Prevent DOMContentLoaded events from frames from invoking this
// code, unless the event is coming from the frame associated with
// the current window (i.e. someone has used switch_to_frame).
let onDOMContentLoaded = function onDOMContentLoaded ( event ) {
2013-01-11 11:33:58 -08:00
if ( ! event . originalTarget . defaultView . frameElement ||
2014-01-24 05:39:23 -08:00
event . originalTarget . defaultView . frameElement == curFrame . frameElement ) {
2013-06-04 23:59:05 -07:00
checkLoad ( ) ;
2012-08-21 14:00:44 -07:00
}
2013-01-11 11:33:58 -08:00
} ;
2013-06-04 23:59:05 -07:00
2014-01-24 05:39:23 -08:00
function timerFunc ( ) {
2013-01-11 11:33:58 -08:00
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2014-01-24 05:39:23 -08:00
sendError ( "Error loading page, timed out (onDOMContentLoaded)" , 21 ,
null , command _id ) ;
2013-01-11 11:33:58 -08:00
}
2014-01-24 05:39:23 -08:00
if ( msg . json . pageTimeout != null ) {
2013-01-11 11:33:58 -08:00
checkTimer . initWithCallback ( timerFunc , msg . json . pageTimeout , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
addEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2013-10-01 08:13:04 -07:00
curFrame . location = msg . json . url ;
2012-03-22 08:19:57 -07:00
}
/ * *
2014-01-21 10:31:31 -08:00
* Get URL of the top level browsing context .
2012-03-22 08:19:57 -07:00
* /
2014-01-21 10:31:31 -08:00
function getCurrentUrl ( msg ) {
2013-09-19 10:35:19 -07:00
sendResponse ( { value : curFrame . location . href } , msg . json . command _id ) ;
2012-03-22 08:19:57 -07:00
}
2012-07-10 13:30:21 -07:00
/ * *
* Get the current Title of the window
* /
function getTitle ( msg ) {
2013-09-19 10:35:19 -07:00
sendResponse ( { value : curFrame . top . document . title } , msg . json . command _id ) ;
2012-07-10 13:30:21 -07:00
}
2012-08-09 12:31:12 -07:00
/ * *
2014-01-21 08:40:20 -08:00
* Get the current page source
2012-08-09 12:31:12 -07:00
* /
function getPageSource ( msg ) {
2013-09-19 10:35:19 -07:00
var XMLSerializer = curFrame . XMLSerializer ;
var pageSource = new XMLSerializer ( ) . serializeToString ( curFrame . document ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : pageSource } , msg . json . command _id ) ;
2012-08-09 12:31:12 -07:00
}
2012-03-22 08:19:57 -07:00
/ * *
2014-01-21 08:40:20 -08:00
* Go back in history
2012-03-22 08:19:57 -07:00
* /
function goBack ( msg ) {
2013-09-19 10:35:19 -07:00
curFrame . history . back ( ) ;
2012-11-26 18:19:04 -08:00
sendOk ( msg . json . command _id ) ;
2012-03-22 08:19:57 -07:00
}
/ * *
2014-01-21 08:40:20 -08:00
* Go forward in history
2012-03-22 08:19:57 -07:00
* /
function goForward ( msg ) {
2013-09-19 10:35:19 -07:00
curFrame . history . forward ( ) ;
2012-11-26 18:19:04 -08:00
sendOk ( msg . json . command _id ) ;
2012-03-22 08:19:57 -07:00
}
/ * *
* Refresh the page
* /
function refresh ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2013-09-19 10:35:19 -07:00
curFrame . location . reload ( true ) ;
2012-11-26 18:19:04 -08:00
let listen = function ( ) {
removeEventListener ( "DOMContentLoaded" , arguments . callee , false ) ;
sendOk ( command _id ) ;
} ;
2012-03-22 08:19:57 -07:00
addEventListener ( "DOMContentLoaded" , listen , false ) ;
}
/ * *
2014-01-21 08:40:20 -08:00
* Find an element in the document using requested search strategy
2012-03-22 08:19:57 -07:00
* /
function findElementContent ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-03-22 08:19:57 -07:00
try {
2012-11-26 18:19:04 -08:00
let on _success = function ( id , cmd _id ) { sendResponse ( { value : id } , cmd _id ) ; } ;
2012-08-27 13:50:34 -07:00
let on _error = sendError ;
2013-09-19 10:35:19 -07:00
elementManager . find ( curFrame , msg . json , msg . json . searchTimeout ,
2013-05-01 10:16:49 -07:00
on _success , on _error , false , command _id ) ;
2012-03-22 08:19:57 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-03-22 08:19:57 -07:00
}
}
/ * *
2014-01-21 08:40:20 -08:00
* Find elements in the document using requested search strategy
2012-03-22 08:19:57 -07:00
* /
function findElementsContent ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-03-22 08:19:57 -07:00
try {
2012-11-26 18:19:04 -08:00
let on _success = function ( id , cmd _id ) { sendResponse ( { value : id } , cmd _id ) ; } ;
2012-08-27 13:50:34 -07:00
let on _error = sendError ;
2013-09-19 10:35:19 -07:00
elementManager . find ( curFrame , msg . json , msg . json . searchTimeout ,
2013-05-01 10:16:49 -07:00
on _success , on _error , true , command _id ) ;
2012-03-22 08:19:57 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-03-22 08:19:57 -07:00
}
}
2013-01-14 06:57:54 -08:00
/ * *
* Find and return the active element on the page
* /
function getActiveElement ( msg ) {
let command _id = msg . json . command _id ;
2013-09-19 10:35:19 -07:00
var element = curFrame . document . activeElement ;
2013-01-14 06:57:54 -08:00
var id = elementManager . addToKnownElements ( element ) ;
sendResponse ( { value : id } , command _id ) ;
}
2012-03-22 08:19:57 -07:00
/ * *
* Send click event to element
* /
function clickElement ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-03-22 08:19:57 -07:00
let el ;
try {
2013-10-01 08:13:04 -07:00
el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-05-27 10:12:13 -07:00
if ( checkVisible ( el ) ) {
2013-04-02 05:35:27 -07:00
if ( utils . isElementEnabled ( el ) ) {
utils . synthesizeMouseAtCenter ( el , { } , el . ownerDocument . defaultView )
}
else {
sendError ( "Element is not Enabled" , 12 , null , command _id )
}
}
else {
sendError ( "Element is not visible" , 11 , null , command _id )
}
2012-11-26 18:19:04 -08:00
sendOk ( command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
/ * *
* Get a given attribute of an element
* /
2012-05-01 12:37:34 -07:00
function getElementAttribute ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : utils . getElementAttribute ( el , msg . json . name ) } ,
command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
/ * *
* Get the text of this element . This includes text from child elements .
* /
function getElementText ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : utils . getElementText ( el ) } , command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
2012-08-08 13:21:50 -07:00
/ * *
* Get the tag name of an element .
* /
function getElementTagName ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-08-08 13:21:50 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : el . tagName . toLowerCase ( ) } , command _id ) ;
2012-08-08 13:21:50 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-08-08 13:21:50 -07:00
}
}
2012-04-10 17:28:08 -07:00
/ * *
* Check if element is displayed
* /
function isElementDisplayed ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : utils . isElementDisplayed ( el ) } , command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
2013-05-07 14:38:21 -07:00
/ * *
* Return the property of the computed style of an element
*
* @ param object aRequest
* 'element' member holds the reference id to
* the element that will be checked
* 'propertyName' is the CSS rule that is being requested
* /
function getElementValueOfCssProperty ( msg ) {
let command _id = msg . json . command _id ;
let propertyName = msg . json . propertyName ;
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-09-19 10:35:19 -07:00
sendResponse ( { value : curFrame . document . defaultView . getComputedStyle ( el , null ) . getPropertyValue ( propertyName ) } ,
2013-05-07 14:38:21 -07:00
command _id ) ;
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , command _id ) ;
}
}
2013-10-15 07:40:48 -07:00
/ * *
* Submit a form on a content page by either using form or element in a form
* @ param object msg
* 'json' JSON object containing 'id' member of the element
* /
function submitElement ( msg ) {
let command _id = msg . json . command _id ;
try {
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
while ( el . parentNode != null && el . tagName . toLowerCase ( ) != 'form' ) {
el = el . parentNode ;
}
if ( el . tagName && el . tagName . toLowerCase ( ) == 'form' ) {
el . submit ( ) ;
sendOk ( command _id ) ;
}
else {
sendError ( "Element is not a form element or in a form" , 7 , null , command _id ) ;
}
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , command _id ) ;
2013-05-07 14:38:21 -07:00
}
}
2012-10-19 13:59:15 -07:00
/ * *
* Get the size of the element and return it
* /
function getElementSize ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-10-19 13:59:15 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
let clientRect = el . getBoundingClientRect ( ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : { width : clientRect . width , height : clientRect . height } } ,
command _id ) ;
2012-10-19 13:59:15 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-10-19 13:59:15 -07:00
}
}
2012-04-10 17:28:08 -07:00
/ * *
* Check if element is enabled
* /
function isElementEnabled ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : utils . isElementEnabled ( el ) } , command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
/ * *
* Check if element is selected
* /
function isElementSelected ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-11-26 18:19:04 -08:00
sendResponse ( { value : utils . isElementSelected ( el ) } , command _id ) ;
2012-04-10 17:28:08 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-04-10 17:28:08 -07:00
}
}
/ * *
* Send keys to element
* /
function sendKeysToElement ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2013-12-16 08:40:51 -08:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
if ( checkVisible ( el ) ) {
if ( el . mozIsTextField && el . mozIsTextField ( false ) ) {
var currentTextLength = el . value ? el . value . length : 0 ;
el . selectionStart = currentTextLength ;
el . selectionEnd = currentTextLength ;
2013-04-11 13:52:42 -07:00
}
2013-12-16 08:40:51 -08:00
el . focus ( ) ;
var value = msg . json . value . join ( "" ) ;
let hasShift = null ;
let hasCtrl = null ;
let hasAlt = null ;
let hasMeta = null ;
for ( var i = 0 ; i < value . length ; i ++ ) {
let upper = value . charAt ( i ) . toUpperCase ( ) ;
var keyCode = null ;
var c = value . charAt ( i ) ;
switch ( c ) {
case '\uE001' :
keyCode = "VK_CANCEL" ;
break ;
case '\uE002' :
keyCode = "VK_HELP" ;
break ;
case '\uE003' :
keyCode = "VK_BACK_SPACE" ;
break ;
case '\uE004' :
keyCode = "VK_TAB" ;
break ;
case '\uE005' :
keyCode = "VK_CLEAR" ;
break ;
case '\uE006' :
keyCode = "VK_RETURN" ;
break ;
case '\uE007' :
keyCode = "VK_ENTER" ;
break ;
case '\uE008' :
keyCode = "VK_SHIFT" ;
hasShift = ! hasShift ;
break ;
case '\uE009' :
keyCode = "VK_CONTROL" ;
controlKey = ! controlKey ;
break ;
case '\uE00A' :
keyCode = "VK_ALT" ;
altKey = ! altKey ;
break ;
case '\uE03D' :
keyCode = "VK_META" ;
metaKey = ! metaKey ;
break ;
case '\uE00B' :
keyCode = "VK_PAUSE" ;
break ;
case '\uE00C' :
keyCode = "VK_ESCAPE" ;
break ;
case '\uE00D' :
keyCode = "VK_Space" ; // printable
break ;
case '\uE00E' :
keyCode = "VK_PAGE_UP" ;
break ;
case '\uE00F' :
keyCode = "VK_PAGE_DOWN" ;
break ;
case '\uE010' :
keyCode = "VK_END" ;
break ;
case '\uE011' :
keyCode = "VK_HOME" ;
break ;
case '\uE012' :
keyCode = "VK_LEFT" ;
break ;
case '\uE013' :
keyCode = "VK_UP" ;
break ;
case '\uE014' :
keyCode = "VK_RIGHT" ;
break ;
case '\uE015' :
keyCode = "VK_DOWN" ;
break ;
case '\uE016' :
keyCode = "VK_INSERT" ;
break ;
case '\uE017' :
keyCode = "VK_DELETE" ;
break ;
case '\uE018' :
keyCode = "VK_SEMICOLON" ;
break ;
case '\uE019' :
keyCode = "VK_EQUALS" ;
break ;
case '\uE01A' :
keyCode = "VK_NUMPAD0" ;
break ;
case '\uE01B' :
keyCode = "VK_NUMPAD1" ;
break ;
case '\uE01C' :
keyCode = "VK_NUMPAD2" ;
break ;
case '\uE01D' :
keyCode = "VK_NUMPAD3" ;
break ;
case '\uE01E' :
keyCode = "VK_NUMPAD4" ;
break ;
case '\uE01F' :
keyCode = "VK_NUMPAD5" ;
break ;
case '\uE020' :
keyCode = "VK_NUMPAD6" ;
break ;
case '\uE021' :
keyCode = "VK_NUMPAD7" ;
break ;
case '\uE022' :
keyCode = "VK_NUMPAD8" ;
break ;
case '\uE023' :
keyCode = "VK_NUMPAD9" ;
break ;
case '\uE024' :
keyCode = "VK_MULTIPLY" ;
break ;
case '\uE025' :
keyCode = "VK_ADD" ;
break ;
case '\uE026' :
keyCode = "VK_SEPARATOR" ;
break ;
case '\uE027' :
keyCode = "VK_SUBTRACT" ;
break ;
case '\uE028' :
keyCode = "VK_DECIMAL" ;
break ;
case '\uE029' :
keyCode = "VK_DIVIDE" ;
break ;
case '\uE031' :
keyCode = "VK_F1" ;
break ;
case '\uE032' :
keyCode = "VK_F2" ;
break ;
case '\uE033' :
keyCode = "VK_F3" ;
break ;
case '\uE034' :
keyCode = "VK_F4" ;
break ;
case '\uE035' :
keyCode = "VK_F5" ;
break ;
case '\uE036' :
keyCode = "VK_F6" ;
break ;
case '\uE037' :
keyCode = "VK_F7" ;
break ;
case '\uE038' :
keyCode = "VK_F8" ;
break ;
case '\uE039' :
keyCode = "VK_F9" ;
break ;
case '\uE03A' :
keyCode = "VK_F10" ;
break ;
case '\uE03B' :
keyCode = "VK_F11" ;
break ;
case '\uE03C' :
keyCode = "VK_F12" ;
break ;
}
hasShift = value . charAt ( i ) == upper ;
utils . synthesizeKey ( keyCode || value [ i ] ,
{ shiftKey : hasShift , ctrlKey : hasCtrl , altKey : hasAlt , metaKey : hasMeta } ,
curFrame ) ;
} ;
sendOk ( command _id ) ;
2012-04-10 17:28:08 -07:00
}
2013-12-16 08:40:51 -08:00
else {
sendError ( "Element is not visible" , 11 , null , command _id )
2012-04-10 17:28:08 -07:00
}
}
2012-10-19 13:35:30 -07:00
/ * *
2014-01-22 11:04:04 -08:00
* Get the element ' s top left - hand corner point .
2012-10-19 13:35:30 -07:00
* /
2014-01-22 11:04:04 -08:00
function getElementLocation ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2014-01-22 11:04:04 -08:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-04-03 05:14:38 -07:00
let rect = el . getBoundingClientRect ( ) ;
2012-10-19 13:35:30 -07:00
let location = { } ;
2013-04-03 05:14:38 -07:00
location . x = rect . left ;
location . y = rect . top ;
2012-10-19 13:35:30 -07:00
2012-11-26 18:19:04 -08:00
sendResponse ( { value : location } , command _id ) ;
2012-10-19 13:35:30 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-10-19 13:35:30 -07:00
}
}
2012-04-10 17:28:08 -07:00
/ * *
* Clear the text of an element
* /
function clearElement ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-04-10 17:28:08 -07:00
try {
2013-10-01 08:13:04 -07:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2012-04-10 17:28:08 -07:00
utils . clearElement ( el ) ;
2012-11-26 18:19:04 -08:00
sendOk ( command _id ) ;
2012-03-22 08:19:57 -07:00
}
catch ( e ) {
2012-11-26 18:19:04 -08:00
sendError ( e . message , e . code , e . stack , command _id ) ;
2012-03-22 08:19:57 -07:00
}
}
/ * *
* Switch to frame given either the server - assigned element id ,
* its index in window . frames , or the iframe ' s name or id .
* /
function switchToFrame ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2013-09-19 10:35:19 -07:00
function checkLoad ( ) {
2012-11-14 10:35:44 -08:00
let errorRegex = /about:.+(error)|(blocked)\?/ ;
2013-09-19 10:35:19 -07:00
if ( curFrame . document . readyState == "complete" ) {
2012-11-26 18:19:04 -08:00
sendOk ( command _id ) ;
2012-11-14 10:35:44 -08:00
return ;
2014-01-21 08:40:20 -08:00
}
2013-09-19 10:35:19 -07:00
else if ( curFrame . document . readyState == "interactive" && errorRegex . exec ( curFrame . document . baseURI ) ) {
2012-11-26 18:19:04 -08:00
sendError ( "Error loading page" , 13 , null , command _id ) ;
2012-11-14 10:35:44 -08:00
return ;
}
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
2012-03-22 08:19:57 -07:00
let foundFrame = null ;
2013-09-19 10:35:19 -07:00
let frames = [ ] ; //curFrame.document.getElementsByTagName("iframe");
let parWindow = null ; //curFrame.QueryInterface(Ci.nsIInterfaceRequestor)
// Check of the curFrame reference is dead
2013-01-16 07:00:00 -08:00
try {
2013-09-19 10:35:19 -07:00
frames = curFrame . document . getElementsByTagName ( "iframe" ) ;
2013-01-16 07:00:00 -08:00
//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.
2013-09-19 10:35:19 -07:00
parWindow = curFrame . QueryInterface ( Ci . nsIInterfaceRequestor )
2013-01-16 07:00:00 -08:00
. getInterface ( Ci . nsIDOMWindowUtils ) . outerWindowID ;
} catch ( e ) {
// We probably have a dead compartment so accessing it is going to make Firefox
2014-01-21 08:40:20 -08:00
// very upset. Let's now try redirect everything to the top frame even if the
2013-01-16 07:00:00 -08:00
// user has given us a frame since search doesnt look up.
2013-10-01 08:13:04 -07:00
msg . json . id = null ;
2013-01-16 07:00:00 -08:00
msg . json . element = null ;
}
2013-10-01 08:13:04 -07:00
if ( ( msg . json . id == null ) && ( msg . json . element == null ) ) {
2013-07-29 10:46:01 -07:00
// returning to root frame
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : null } ) ;
2013-09-19 10:35:19 -07:00
curFrame = content ;
2012-12-17 01:45:28 -08:00
if ( msg . json . focus == true ) {
2013-09-19 10:35:19 -07:00
curFrame . focus ( ) ;
2012-12-17 01:45:28 -08:00
}
2013-04-05 10:15:19 -07:00
sandbox = null ;
2012-11-14 10:35:44 -08:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2012-03-22 08:19:57 -07:00
return ;
}
if ( msg . json . element != undefined ) {
if ( elementManager . seenItems [ msg . json . element ] != undefined ) {
2013-02-04 11:40:51 -08:00
let wantedFrame ;
try {
2013-09-19 10:35:19 -07:00
wantedFrame = elementManager . getKnownElement ( msg . json . element , curFrame ) ; //HTMLIFrameElement
2013-02-04 11:40:51 -08:00
}
catch ( e ) {
sendError ( e . message , e . code , e . stack , command _id ) ;
}
2012-10-19 11:46:58 -07:00
for ( let i = 0 ; i < frames . length ; i ++ ) {
2013-02-04 11:40:51 -08:00
// use XPCNativeWrapper to compare elements; see bug 834266
if ( XPCNativeWrapper ( frames [ i ] ) == XPCNativeWrapper ( wantedFrame ) ) {
2014-01-21 08:40:20 -08:00
curFrame = frames [ i ] ;
2012-11-06 10:54:09 -08:00
foundFrame = i ;
2012-03-22 08:19:57 -07:00
}
}
}
}
2012-11-06 10:54:09 -08:00
if ( foundFrame == null ) {
2013-10-01 08:13:04 -07:00
switch ( typeof ( msg . json . id ) ) {
2012-11-06 10:54:09 -08:00
case "string" :
let foundById = null ;
for ( let i = 0 ; i < frames . length ; i ++ ) {
//give precedence to name
let frame = frames [ i ] ;
let name = utils . getElementAttribute ( frame , 'name' ) ;
let id = utils . getElementAttribute ( frame , 'id' ) ;
2013-10-01 08:13:04 -07:00
if ( name == msg . json . id ) {
2012-11-06 10:54:09 -08:00
foundFrame = i ;
break ;
2013-10-01 08:13:04 -07:00
} else if ( ( foundById == null ) && ( id == msg . json . id ) ) {
2012-11-06 10:54:09 -08:00
foundById = i ;
}
2012-03-22 08:19:57 -07:00
}
2012-11-06 10:54:09 -08:00
if ( ( foundFrame == null ) && ( foundById != null ) ) {
foundFrame = foundById ;
2013-09-19 10:35:19 -07:00
curFrame = frames [ foundFrame ] ;
2012-11-06 10:54:09 -08:00
}
break ;
case "number" :
2013-10-01 08:13:04 -07:00
if ( frames [ msg . json . id ] != undefined ) {
foundFrame = msg . json . id ;
2013-09-19 10:35:19 -07:00
curFrame = frames [ foundFrame ] ;
2012-11-06 10:54:09 -08:00
}
break ;
}
2012-03-22 08:19:57 -07:00
}
2012-05-09 12:05:39 -07:00
if ( foundFrame == null ) {
2013-10-01 08:13:04 -07:00
sendError ( "Unable to locate frame: " + msg . json . id , 8 , null , command _id ) ;
2012-05-09 12:05:39 -07:00
return ;
2012-03-22 08:19:57 -07:00
}
2012-05-09 12:05:39 -07:00
sandbox = null ;
2012-09-28 16:16:22 -07:00
2013-07-29 10:46:01 -07:00
// send a synchronous message to let the server update the currently active
// frame element (for getActiveFrame)
2013-09-19 10:35:19 -07:00
let frameValue = elementManager . wrapValue ( curFrame . wrappedJSObject ) [ 'ELEMENT' ] ;
2013-07-29 10:46:01 -07:00
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : frameValue } ) ;
2013-09-19 10:35:19 -07:00
if ( curFrame . contentWindow == null ) {
2013-07-29 10:46:01 -07:00
// The frame we want to switch to is a remote (out-of-process) frame;
// notify our parent to handle the switch.
2013-09-19 10:35:19 -07:00
curFrame = content ;
2012-11-26 18:19:04 -08:00
sendToServer ( 'Marionette:switchToFrame' , { frame : foundFrame ,
win : parWindow ,
command _id : command _id } ) ;
2012-09-28 16:16:22 -07:00
}
else {
2013-09-19 10:35:19 -07:00
curFrame = curFrame . contentWindow ;
2012-12-17 01:45:28 -08:00
if ( msg . json . focus == true ) {
2013-09-19 10:35:19 -07:00
curFrame . focus ( ) ;
2012-12-17 01:45:28 -08:00
}
2012-11-14 10:35:44 -08:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2012-09-28 16:16:22 -07:00
}
2012-03-22 08:19:57 -07:00
}
2012-11-22 07:53:44 -08:00
/ * *
* Add a cookie to the document
* /
function addCookie ( msg ) {
cookie = msg . json . cookie ;
if ( ! cookie . expiry ) {
var date = new Date ( ) ;
var thePresent = new Date ( Date . now ( ) ) ;
date . setYear ( thePresent . getFullYear ( ) + 20 ) ;
cookie . expiry = date . getTime ( ) / 1000 ; // Stored in seconds.
}
if ( ! cookie . domain ) {
2013-09-19 10:35:19 -07:00
var location = curFrame . document . location ;
2012-11-22 07:53:44 -08:00
cookie . domain = location . hostname ;
}
else {
2013-09-19 10:35:19 -07:00
var currLocation = curFrame . location ;
2012-11-22 07:53:44 -08:00
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 ) ;
}
}
// The cookie's domain may include a port. Which is bad. Remove it
// We'll catch ip6 addresses by mistake. Since no-one uses those
// this will be okay for now. See Bug 814416
if ( cookie . domain . match ( /:\d+$/ ) ) {
cookie . domain = cookie . domain . replace ( /:\d+$/ , '' ) ;
}
2013-09-19 10:35:19 -07:00
var document = curFrame . document ;
2012-11-22 07:53:44 -08:00
if ( ! document || ! document . contentType . match ( /html/i ) ) {
sendError ( 'You may only set cookies on html documents' , 25 , null , msg . json . command _id ) ;
}
var cookieManager = Cc [ '@mozilla.org/cookiemanager;1' ] .
getService ( Ci . nsICookieManager2 ) ;
cookieManager . add ( cookie . domain , cookie . path , cookie . name , cookie . value ,
cookie . secure , false , false , cookie . expiry ) ;
sendOk ( msg . json . command _id ) ;
}
/ * *
2014-01-21 08:40:20 -08:00
* Get all cookies for the current domain .
2012-11-22 07:53:44 -08:00
* /
2014-01-21 08:40:20 -08:00
function getCookies ( msg ) {
2012-11-22 07:53:44 -08:00
var toReturn = [ ] ;
2013-09-19 10:35:19 -07:00
var cookies = getVisibleCookies ( curFrame . location ) ;
2012-11-22 07:53:44 -08:00
for ( var i = 0 ; i < cookies . length ; i ++ ) {
var cookie = cookies [ i ] ;
var expires = cookie . expires ;
if ( expires == 0 ) { // Session cookie, don't return an expiry.
expires = null ;
} else if ( expires == 1 ) { // Date before epoch time, cap to epoch.
expires = 0 ;
}
toReturn . push ( {
'name' : cookie . name ,
'value' : cookie . value ,
'path' : cookie . path ,
'domain' : cookie . host ,
'secure' : cookie . isSecure ,
'expiry' : expires
} ) ;
}
sendResponse ( { value : toReturn } , msg . json . command _id ) ;
}
/ * *
* Delete a cookie by name
* /
function deleteCookie ( msg ) {
var toDelete = msg . json . name ;
var cookieManager = Cc [ '@mozilla.org/cookiemanager;1' ] .
getService ( Ci . nsICookieManager ) ;
2013-09-19 10:35:19 -07:00
var cookies = getVisibleCookies ( curFrame . location ) ;
2012-11-22 07:53:44 -08:00
for ( var i = 0 ; i < cookies . length ; i ++ ) {
var cookie = cookies [ i ] ;
if ( cookie . name == toDelete ) {
cookieManager . remove ( cookie . host , cookie . name , cookie . path , false ) ;
}
}
sendOk ( msg . json . command _id ) ;
}
/ * *
* Delete all the visibile cookies on a page
* /
function deleteAllCookies ( msg ) {
let cookieManager = Cc [ '@mozilla.org/cookiemanager;1' ] .
getService ( Ci . nsICookieManager ) ;
2013-09-19 10:35:19 -07:00
let cookies = getVisibleCookies ( curFrame . location ) ;
2012-11-22 07:53:44 -08:00
for ( let i = 0 ; i < cookies . length ; i ++ ) {
let cookie = cookies [ i ] ;
cookieManager . remove ( cookie . host , cookie . name , cookie . path , false ) ;
}
sendOk ( msg . json . command _id ) ;
}
/ * *
* Get all the visible cookies from a location
* /
function getVisibleCookies ( location ) {
let results = [ ] ;
let currentPath = location . pathname ;
if ( ! currentPath ) currentPath = '/' ;
let isForCurrentPath = function ( aPath ) {
return currentPath . indexOf ( aPath ) != - 1 ;
}
let cookieManager = Cc [ '@mozilla.org/cookiemanager;1' ] .
getService ( Ci . nsICookieManager ) ;
let enumerator = cookieManager . enumerator ;
while ( enumerator . hasMoreElements ( ) ) {
let cookie = enumerator . getNext ( ) . QueryInterface ( Ci [ 'nsICookie' ] ) ;
// Take the hostname and progressively shorten
let hostname = location . hostname ;
do {
if ( ( cookie . host == '.' + hostname || cookie . host == hostname )
&& isForCurrentPath ( cookie . path ) ) {
results . push ( cookie ) ;
break ;
}
hostname = hostname . replace ( /^.*?\./ , '' ) ;
} while ( hostname . indexOf ( '.' ) != - 1 ) ;
}
return results ;
}
2012-03-22 08:19:57 -07:00
2012-11-26 18:19:04 -08:00
function getAppCacheStatus ( msg ) {
2013-09-19 10:35:19 -07:00
sendResponse ( { value : curFrame . applicationCache . status } ,
2012-11-26 18:19:04 -08:00
msg . json . command _id ) ;
2014-01-21 08:40:20 -08:00
}
2012-08-23 15:07:16 -07:00
2012-05-23 17:23:36 -07:00
// emulator callbacks
2012-05-18 13:30:43 -07:00
let _emu _cb _id = 0 ;
let _emu _cbs = { } ;
2012-05-23 17:23:36 -07:00
2012-05-18 13:30:43 -07:00
function runEmulatorCmd ( cmd , callback ) {
2012-05-25 10:37:21 -07:00
if ( callback ) {
_emu _cbs [ _emu _cb _id ] = callback ;
2012-05-18 13:30:43 -07:00
}
sendAsyncMessage ( "Marionette:runEmulatorCmd" , { emulator _cmd : cmd , id : _emu _cb _id } ) ;
_emu _cb _id += 1 ;
}
function emulatorCmdResult ( msg ) {
let message = msg . json ;
2012-05-25 10:37:21 -07:00
if ( ! sandbox ) {
2012-05-23 17:23:36 -07:00
return ;
}
2012-05-18 13:30:43 -07:00
let cb = _emu _cbs [ message . id ] ;
delete _emu _cbs [ message . id ] ;
2012-05-25 10:37:21 -07:00
if ( ! cb ) {
return ;
}
2012-05-18 13:30:43 -07:00
try {
cb ( message . result ) ;
}
catch ( e ) {
2013-02-12 10:10:03 -08:00
sendError ( e . message , e . code , e . stack , - 1 ) ;
2012-05-18 13:30:43 -07:00
return ;
}
}
2012-06-04 10:50:06 -07:00
function importScript ( msg ) {
2012-11-26 18:19:04 -08:00
let command _id = msg . json . command _id ;
2012-06-04 10:50:06 -07:00
let file ;
if ( importedScripts . exists ( ) ) {
2012-11-26 18:19:04 -08:00
file = FileUtils . openFileOutputStream ( importedScripts ,
FileUtils . MODE _APPEND | FileUtils . MODE _WRONLY ) ;
2012-06-04 10:50:06 -07:00
}
else {
2012-10-24 08:36:43 -07:00
//Note: The permission bits here don't actually get set (bug 804563)
2012-11-26 18:19:04 -08:00
importedScripts . createUnique ( Components . interfaces . nsIFile . NORMAL _FILE _TYPE ,
parseInt ( "0666" , 8 ) ) ;
file = FileUtils . openFileOutputStream ( importedScripts ,
FileUtils . MODE _WRONLY | FileUtils . MODE _CREATE ) ;
2012-10-24 08:36:43 -07:00
importedScripts . permissions = parseInt ( "0666" , 8 ) ; //actually set permissions
2012-06-04 10:50:06 -07:00
}
file . write ( msg . json . script , msg . json . script . length ) ;
file . close ( ) ;
2012-11-26 18:19:04 -08:00
sendOk ( command _id ) ;
2012-06-04 10:50:06 -07:00
}
2012-11-30 15:25:26 -08:00
/ * *
2014-01-24 05:45:58 -08:00
* Takes a screen capture of the given web element if < code > id < / c o d e >
* property exists in the message ' s JSON object , or if null captures
* the bounding box of the current frame .
*
* If given an array of web element references in
* < code > msg . json . highlights < / c o d e > , a r e d b o x w i l l b e p a i n t e d a r o u n d
* them to highlight their position .
2012-11-30 15:25:26 -08:00
* /
2014-01-24 05:45:58 -08:00
function takeScreenshot ( msg ) {
2012-11-30 15:25:26 -08:00
let node = null ;
2013-10-01 08:13:04 -07:00
if ( msg . json . id ) {
2012-11-30 15:25:26 -08:00
try {
2013-10-01 08:13:04 -07:00
node = elementManager . getKnownElement ( msg . json . id , curFrame )
2012-11-30 15:25:26 -08:00
}
catch ( e ) {
2013-02-12 10:10:03 -08:00
sendResponse ( e . message , e . code , e . stack , msg . json . command _id ) ;
2012-11-30 15:25:26 -08:00
return ;
}
}
else {
2014-01-24 05:45:58 -08:00
node = curFrame ;
2012-11-30 15:25:26 -08:00
}
let highlights = msg . json . highlights ;
2013-09-19 10:35:19 -07:00
var document = curFrame . document ;
2013-09-04 01:05:43 -07:00
var rect , win , width , height , left , top ;
2012-11-30 15:25:26 -08:00
// node can be either a window or an arbitrary DOM node
2013-09-19 10:35:19 -07:00
if ( node == curFrame ) {
2012-11-30 15:25:26 -08:00
// node is a window
win = node ;
2014-02-05 13:38:12 -08:00
width = document . body . scrollWidth ;
height = document . body . scrollHeight ;
2012-11-30 15:25:26 -08:00
top = 0 ;
left = 0 ;
}
else {
// node is an arbitrary DOM node
win = node . ownerDocument . defaultView ;
rect = node . getBoundingClientRect ( ) ;
width = rect . width ;
height = rect . height ;
top = rect . top ;
left = rect . left ;
}
2014-01-24 05:45:58 -08:00
var canvas = document . createElementNS ( "http://www.w3.org/1999/xhtml" ,
"canvas" ) ;
2012-11-30 15:25:26 -08:00
canvas . width = width ;
canvas . height = height ;
var ctx = canvas . getContext ( "2d" ) ;
// Draws the DOM contents of the window to the canvas
2014-01-24 05:45:58 -08:00
ctx . drawWindow ( win , left , top , width , height , "rgb(255,255,255)" ) ;
2012-11-30 15:25:26 -08:00
2014-01-24 05:45:58 -08:00
// This section is for drawing a red rectangle around each element
// passed in via the highlights array
2012-11-30 15:25:26 -08:00
if ( highlights ) {
ctx . lineWidth = "2" ;
ctx . strokeStyle = "red" ;
ctx . save ( ) ;
for ( var i = 0 ; i < highlights . length ; ++ i ) {
2014-01-24 05:45:58 -08:00
var elem = elementManager . getKnownElement ( highlights [ i ] , curFrame ) ;
2012-11-30 15:25:26 -08:00
rect = elem . getBoundingClientRect ( ) ;
2013-09-04 01:05:43 -07:00
var offsetY = - top ;
var offsetX = - left ;
2012-11-30 15:25:26 -08:00
// Draw the rectangle
2014-01-24 05:45:58 -08:00
ctx . strokeRect ( rect . left + offsetX ,
rect . top + offsetY ,
rect . width ,
rect . height ) ;
2012-11-30 15:25:26 -08:00
}
}
2014-01-24 05:45:58 -08:00
// Return the Base64 encoded string back to the client so that it
// can save the file to disk if it is required
var dataUrl = canvas . toDataURL ( "image/png" , "" ) ;
var data = dataUrl . substring ( dataUrl . indexOf ( "," ) + 1 ) ;
sendResponse ( { value : data } , msg . json . command _id ) ;
2012-11-30 15:25:26 -08:00
}
2014-01-24 05:45:58 -08:00
// Call register self when we get loaded
2012-03-22 08:19:57 -07:00
registerSelf ( ) ;