2014-06-25 09:12:07 +04:00
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2012-03-22 19:19:57 +04:00
/ * 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-12 00:06:53 +04: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 19:19:57 +04:00
2015-03-24 00:32:03 +03:00
loader . loadSubScript ( "chrome://marionette/content/simpletest.js" ) ;
loader . loadSubScript ( "chrome://marionette/content/common.js" ) ;
loader . loadSubScript ( "chrome://marionette/content/actions.js" ) ;
Cu . import ( "chrome://marionette/content/elements.js" ) ;
2015-04-15 14:18:00 +03:00
Cu . import ( "chrome://marionette/content/error.js" ) ;
2012-06-04 21:50:06 +04:00
Cu . import ( "resource://gre/modules/FileUtils.jsm" ) ;
2013-01-23 23:26:33 +04:00
Cu . import ( "resource://gre/modules/NetUtil.jsm" ) ;
Cu . import ( "resource://gre/modules/XPCOMUtils.jsm" ) ;
2012-04-11 04:28:08 +04:00
let utils = { } ;
utils . window = content ;
2012-05-12 00:06:53 +04:00
// Load Event/ChromeUtils for use with JS scripts:
loader . loadSubScript ( "chrome://marionette/content/EventUtils.js" , utils ) ;
2012-04-11 04:28:08 +04:00
loader . loadSubScript ( "chrome://marionette/content/ChromeUtils.js" , utils ) ;
loader . loadSubScript ( "chrome://marionette/content/atoms.js" , utils ) ;
2015-03-24 00:32:03 +03:00
loader . loadSubScript ( "chrome://marionette/content/sendkeys.js" , utils ) ;
2012-05-19 00:30:13 +04:00
2012-03-22 19:19:57 +04:00
let marionetteLogObj = new MarionetteLogObj ( ) ;
let isB2G = false ;
2012-11-01 02:36:57 +04:00
let marionetteTestName ;
2012-05-12 00:06:53 +04:00
let winUtil = content . QueryInterface ( Ci . nsIInterfaceRequestor )
2015-04-02 17:16:00 +03:00
. getInterface ( Ci . nsIDOMWindowUtils ) ;
let listenerId = null ; // unique ID of this listener
2013-09-19 21:35:19 +04:00
let curFrame = content ;
2015-04-02 17:16:00 +03:00
let isRemoteBrowser = ( ) => curFrame . contentWindow !== null ;
2013-09-19 21:35:19 +04:00
let previousFrame = null ;
2012-03-22 19:19:57 +04:00
let elementManager = new ElementManager ( [ ] ) ;
2014-12-23 00:15:19 +03:00
let accessibility = new Accessibility ( ) ;
2015-03-20 04:41:19 +03:00
let actions = new ActionChain ( utils , checkForInterrupted ) ;
2012-12-14 20:11:02 +04:00
let importedScripts = null ;
2012-03-22 19:19:57 +04:00
2015-05-19 02:36:15 +03:00
// Contains the last file input element that was the target of
// sendKeysToElement.
let fileInputElement ;
2015-04-23 23:39:38 +03:00
// A dict of sandboxes used this session
let sandboxes = { } ;
// The name of the current sandbox
let sandboxName = 'default' ;
2012-05-09 23:05:39 +04:00
2013-02-12 22:10:03 +04:00
// the unload handler
let onunload ;
2012-05-09 23:05:39 +04:00
// Flag to indicate whether an async script is currently running or not.
let asyncTestRunning = false ;
let asyncTestCommandId ;
let asyncTestTimeoutId ;
2013-08-28 21:06:03 +04:00
let inactivityTimeoutId = null ;
let heartbeatCallback = function ( ) { } ; // Called by the simpletest methods.
2012-12-18 18:21:28 +04:00
let originalOnError ;
2012-11-14 22:35:44 +04:00
//timer for doc changes
let checkTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
2013-10-17 19:25:11 +04:00
//timer for readystate
let readyStateTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
2015-03-11 02:19:40 +03:00
// timer for navigation commands.
let navTimer = Cc [ "@mozilla.org/timer;1" ] . createInstance ( Ci . nsITimer ) ;
let onDOMContentLoaded ;
2013-01-22 23:27:44 +04:00
// Send move events about this often
let EVENT _INTERVAL = 30 ; // milliseconds
2013-03-19 00:42:46 +04:00
// last touch for each fingerId
let multiLast = { } ;
2013-05-15 00:54:07 +04:00
2013-08-26 22:55:58 +04:00
Cu . import ( "resource://gre/modules/Log.jsm" ) ;
let logger = Log . repository . getLogger ( "Marionette" ) ;
2015-03-24 00:32:03 +03:00
logger . info ( "loaded listener.js" ) ;
2013-09-19 21:35:19 +04:00
let modalHandler = function ( ) {
2014-04-10 18:26:08 +04:00
// This gets called on the system app only since it receives the mozbrowserprompt event
2013-09-19 21:35:19 +04:00
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : null , storePrevious : true } ) ;
let isLocal = sendSyncMessage ( "MarionetteFrame:handleModal" , { } ) [ 0 ] . value ;
if ( isLocal ) {
previousFrame = curFrame ;
}
curFrame = content ;
} ;
2013-05-15 00:54:07 +04:00
2012-03-22 19:19:57 +04:00
/ * *
2014-01-21 20:40:20 +04:00
* Called when listener is first started up .
2012-03-22 19:19:57 +04: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 ( ) {
2015-04-15 14:18:00 +03:00
let msg = { value : winUtil . outerWindowID } ;
2014-04-10 18:26:08 +04:00
// register will have the ID and a boolean describing if this is the main process or not
2012-11-27 06:19:04 +04:00
let register = sendSyncMessage ( "Marionette:register" , msg ) ;
2012-03-22 19:19:57 +04:00
if ( register [ 0 ] ) {
2015-03-11 02:19:40 +03:00
let { id , remotenessChange } = register [ 0 ] [ 0 ] ;
listenerId = id ;
if ( typeof id != "undefined" ) {
2014-10-21 19:22:26 +04:00
// check if we're the main process
if ( register [ 0 ] [ 1 ] == true ) {
addMessageListener ( "MarionetteMainListener:emitTouchEvent" , emitTouchEventForIFrame ) ;
}
importedScripts = FileUtils . getDir ( 'TmpD' , [ ] , false ) ;
importedScripts . append ( 'marionetteContentScripts' ) ;
startListeners ( ) ;
2015-03-20 00:12:58 +03:00
let rv = { } ;
2015-03-11 02:19:40 +03:00
if ( remotenessChange ) {
2015-03-20 00:12:58 +03:00
rv . listenerId = id ;
2015-03-11 02:19:40 +03:00
}
2015-03-20 00:12:58 +03:00
sendAsyncMessage ( "Marionette:listenersAttached" , rv ) ;
2014-04-10 18:26:08 +04:00
}
2012-03-22 19:19:57 +04:00
}
}
2014-04-10 18:26:08 +04:00
function emitTouchEventForIFrame ( message ) {
Bug 1001090 - Part 4: Fix errors in chrome code. (r=zombie,gavin,fitzgen,dcamp,bgrins,fabrice,gwagner,margaret,mrbkap,mak,njn,vicamo)
2014-09-16 03:30:46 +04:00
message = message . json ;
2015-03-20 04:41:19 +03:00
let identifier = actions . nextTouchId ;
2015-01-20 01:12:00 +03:00
let domWindowUtils = curFrame .
QueryInterface ( Components . interfaces . nsIInterfaceRequestor ) .
getInterface ( Components . interfaces . nsIDOMWindowUtils ) ;
var ratio = domWindowUtils . screenPixelsPerCSSPixel ;
var typeForUtils ;
switch ( message . type ) {
case 'touchstart' :
typeForUtils = domWindowUtils . TOUCH _CONTACT ;
break ;
case 'touchend' :
typeForUtils = domWindowUtils . TOUCH _REMOVE ;
break ;
case 'touchcancel' :
typeForUtils = domWindowUtils . TOUCH _CANCEL ;
break ;
case 'touchmove' :
typeForUtils = domWindowUtils . TOUCH _CONTACT ;
break ;
}
domWindowUtils . sendNativeTouchPoint ( identifier , typeForUtils ,
Math . round ( message . screenX * ratio ) , Math . round ( message . screenY * ratio ) ,
message . force , 90 ) ;
2014-04-10 18:26:08 +04:00
}
2015-04-15 14:18:00 +03:00
function dispatch ( fn ) {
return function ( msg ) {
let id = msg . json . command _id ;
try {
let rv ;
if ( typeof msg . json == "undefined" || msg . json instanceof Array ) {
rv = fn . apply ( null , msg . json ) ;
} else {
rv = fn ( msg . json ) ;
}
if ( typeof rv == "undefined" ) {
sendOk ( id ) ;
} else {
sendResponse ( { value : rv } , id ) ;
}
} catch ( e ) {
sendError ( e , id ) ;
}
} ;
}
2012-05-12 00:06:53 +04: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 ) ;
}
2015-04-15 14:18:00 +03:00
let getElementSizeFn = dispatch ( getElementSize ) ;
let getActiveElementFn = dispatch ( getActiveElement ) ;
let clickElementFn = dispatch ( clickElement ) ;
let getElementAttributeFn = dispatch ( getElementAttribute ) ;
let getElementTextFn = dispatch ( getElementText ) ;
let getElementTagNameFn = dispatch ( getElementTagName ) ;
let getElementRectFn = dispatch ( getElementRect ) ;
let isElementEnabledFn = dispatch ( isElementEnabled ) ;
2012-03-22 19:19:57 +04:00
/ * *
* Start all message listeners
* /
function startListeners ( ) {
2015-05-19 02:36:15 +03:00
addMessageListenerId ( "Marionette:receiveFiles" , receiveFiles ) ;
2012-05-12 00:06:53 +04:00
addMessageListenerId ( "Marionette:newSession" , newSession ) ;
addMessageListenerId ( "Marionette:executeScript" , executeScript ) ;
addMessageListenerId ( "Marionette:executeAsyncScript" , executeAsyncScript ) ;
addMessageListenerId ( "Marionette:executeJSScript" , executeJSScript ) ;
2013-01-22 23:27:44 +04:00
addMessageListenerId ( "Marionette:singleTap" , singleTap ) ;
2013-03-05 05:09:58 +04:00
addMessageListenerId ( "Marionette:actionChain" , actionChain ) ;
2013-03-19 00:42:46 +04:00
addMessageListenerId ( "Marionette:multiAction" , multiAction ) ;
2014-01-24 17:39:23 +04:00
addMessageListenerId ( "Marionette:get" , get ) ;
2015-03-11 02:19:40 +03:00
addMessageListenerId ( "Marionette:pollForReadyState" , pollForReadyState ) ;
addMessageListenerId ( "Marionette:cancelRequest" , cancelRequest ) ;
2014-01-21 22:31:31 +04:00
addMessageListenerId ( "Marionette:getCurrentUrl" , getCurrentUrl ) ;
2012-07-11 00:30:21 +04:00
addMessageListenerId ( "Marionette:getTitle" , getTitle ) ;
2012-08-09 23:31:12 +04:00
addMessageListenerId ( "Marionette:getPageSource" , getPageSource ) ;
2012-05-12 00:06:53 +04:00
addMessageListenerId ( "Marionette:goBack" , goBack ) ;
addMessageListenerId ( "Marionette:goForward" , goForward ) ;
addMessageListenerId ( "Marionette:refresh" , refresh ) ;
addMessageListenerId ( "Marionette:findElementContent" , findElementContent ) ;
addMessageListenerId ( "Marionette:findElementsContent" , findElementsContent ) ;
2015-04-15 14:18:00 +03:00
addMessageListenerId ( "Marionette:getActiveElement" , getActiveElementFn ) ;
addMessageListenerId ( "Marionette:clickElement" , clickElementFn ) ;
addMessageListenerId ( "Marionette:getElementAttribute" , getElementAttributeFn ) ;
addMessageListenerId ( "Marionette:getElementText" , getElementTextFn ) ;
addMessageListenerId ( "Marionette:getElementTagName" , getElementTagNameFn ) ;
2012-05-12 00:06:53 +04:00
addMessageListenerId ( "Marionette:isElementDisplayed" , isElementDisplayed ) ;
2013-10-15 18:40:48 +04:00
addMessageListenerId ( "Marionette:getElementValueOfCssProperty" , getElementValueOfCssProperty ) ;
2015-04-15 14:18:00 +03:00
addMessageListenerId ( "Marionette:getElementSize" , getElementSizeFn ) ; // deprecated
addMessageListenerId ( "Marionette:getElementRect" , getElementRectFn ) ;
addMessageListenerId ( "Marionette:isElementEnabled" , isElementEnabledFn ) ;
2012-05-12 00:06:53 +04:00
addMessageListenerId ( "Marionette:isElementSelected" , isElementSelected ) ;
addMessageListenerId ( "Marionette:sendKeysToElement" , sendKeysToElement ) ;
2014-07-17 19:01:36 +04:00
addMessageListenerId ( "Marionette:getElementLocation" , getElementLocation ) ; //deprecated
2012-05-12 00:06:53 +04:00
addMessageListenerId ( "Marionette:clearElement" , clearElement ) ;
addMessageListenerId ( "Marionette:switchToFrame" , switchToFrame ) ;
addMessageListenerId ( "Marionette:deleteSession" , deleteSession ) ;
addMessageListenerId ( "Marionette:sleepSession" , sleepSession ) ;
2012-05-19 00:30:43 +04:00
addMessageListenerId ( "Marionette:emulatorCmdResult" , emulatorCmdResult ) ;
2012-06-04 21:50:06 +04:00
addMessageListenerId ( "Marionette:importScript" , importScript ) ;
2012-08-24 02:07:16 +04:00
addMessageListenerId ( "Marionette:getAppCacheStatus" , getAppCacheStatus ) ;
2012-11-01 02:36:57 +04:00
addMessageListenerId ( "Marionette:setTestName" , setTestName ) ;
2014-01-24 17:45:58 +04:00
addMessageListenerId ( "Marionette:takeScreenshot" , takeScreenshot ) ;
2012-11-22 19:53:44 +04:00
addMessageListenerId ( "Marionette:addCookie" , addCookie ) ;
2014-01-21 20:40:20 +04:00
addMessageListenerId ( "Marionette:getCookies" , getCookies ) ;
2012-11-22 19:53:44 +04:00
addMessageListenerId ( "Marionette:deleteAllCookies" , deleteAllCookies ) ;
addMessageListenerId ( "Marionette:deleteCookie" , deleteCookie ) ;
2012-03-22 19:19:57 +04:00
}
2013-10-17 19:25:11 +04: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 19:19:57 +04:00
/ * *
* Called when we start a new session . It registers the
* current environment , and resets all values
* /
function newSession ( msg ) {
isB2G = msg . json . B2G ;
2014-12-23 00:15:19 +03:00
accessibility . strict = msg . json . raisesAccessibilityExceptions ;
2012-03-22 19:19:57 +04:00
resetValues ( ) ;
2013-10-17 19:25:11 +04:00
if ( isB2G ) {
readyStateTimer . initWithCallback ( waitForReady , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2014-01-15 18:28:04 +04: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.
2015-03-20 04:41:19 +03:00
actions . inputSource = Ci . nsIDOMMouseEvent . MOZ _SOURCE _TOUCH ;
2013-10-17 19:25:11 +04:00
}
2012-03-22 19:19:57 +04:00
}
2014-01-21 20:40:20 +04:00
2012-03-22 19:19:57 +04: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-27 06:19:04 +04:00
function restart ( msg ) {
2012-03-22 19:19:57 +04:00
removeMessageListener ( "Marionette:restart" , restart ) ;
2013-10-17 19:25:11 +04:00
if ( isB2G ) {
readyStateTimer . initWithCallback ( waitForReady , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
2012-03-22 19:19:57 +04:00
registerSelf ( ) ;
}
/ * *
* Removes all listeners
* /
function deleteSession ( msg ) {
2015-05-19 02:36:15 +03:00
removeMessageListenerId ( "Marionette:receiveFiles" , receiveFiles ) ;
2012-05-12 00:06:53 +04:00
removeMessageListenerId ( "Marionette:newSession" , newSession ) ;
removeMessageListenerId ( "Marionette:executeScript" , executeScript ) ;
removeMessageListenerId ( "Marionette:executeAsyncScript" , executeAsyncScript ) ;
removeMessageListenerId ( "Marionette:executeJSScript" , executeJSScript ) ;
2013-01-22 23:27:44 +04:00
removeMessageListenerId ( "Marionette:singleTap" , singleTap ) ;
2013-03-05 05:09:58 +04:00
removeMessageListenerId ( "Marionette:actionChain" , actionChain ) ;
2013-03-19 00:42:46 +04:00
removeMessageListenerId ( "Marionette:multiAction" , multiAction ) ;
2014-01-24 17:39:23 +04:00
removeMessageListenerId ( "Marionette:get" , get ) ;
2015-03-11 02:19:40 +03:00
removeMessageListenerId ( "Marionette:pollForReadyState" , pollForReadyState ) ;
removeMessageListenerId ( "Marionette:cancelRequest" , cancelRequest ) ;
2012-07-11 00:30:21 +04:00
removeMessageListenerId ( "Marionette:getTitle" , getTitle ) ;
2012-08-09 23:31:12 +04:00
removeMessageListenerId ( "Marionette:getPageSource" , getPageSource ) ;
2014-01-21 22:31:31 +04:00
removeMessageListenerId ( "Marionette:getCurrentUrl" , getCurrentUrl ) ;
2012-05-12 00:06:53 +04:00
removeMessageListenerId ( "Marionette:goBack" , goBack ) ;
removeMessageListenerId ( "Marionette:goForward" , goForward ) ;
removeMessageListenerId ( "Marionette:refresh" , refresh ) ;
removeMessageListenerId ( "Marionette:findElementContent" , findElementContent ) ;
removeMessageListenerId ( "Marionette:findElementsContent" , findElementsContent ) ;
2015-04-15 14:18:00 +03:00
removeMessageListenerId ( "Marionette:getActiveElement" , getActiveElementFn ) ;
removeMessageListenerId ( "Marionette:clickElement" , clickElementFn ) ;
removeMessageListenerId ( "Marionette:getElementAttribute" , getElementAttributeFn ) ;
removeMessageListenerId ( "Marionette:getElementText" , getElementTextFn ) ;
removeMessageListenerId ( "Marionette:getElementTagName" , getElementTagNameFn ) ;
2012-05-12 00:06:53 +04:00
removeMessageListenerId ( "Marionette:isElementDisplayed" , isElementDisplayed ) ;
2013-05-08 01:38:21 +04:00
removeMessageListenerId ( "Marionette:getElementValueOfCssProperty" , getElementValueOfCssProperty ) ;
2015-04-15 14:18:00 +03:00
removeMessageListenerId ( "Marionette:getElementSize" , getElementSizeFn ) ; // deprecated
removeMessageListenerId ( "Marionette:getElementRect" , getElementRectFn ) ;
removeMessageListenerId ( "Marionette:isElementEnabled" , isElementEnabledFn ) ;
2012-05-12 00:06:53 +04:00
removeMessageListenerId ( "Marionette:isElementSelected" , isElementSelected ) ;
removeMessageListenerId ( "Marionette:sendKeysToElement" , sendKeysToElement ) ;
2014-01-22 23:04:04 +04:00
removeMessageListenerId ( "Marionette:getElementLocation" , getElementLocation ) ;
2012-05-12 00:06:53 +04:00
removeMessageListenerId ( "Marionette:clearElement" , clearElement ) ;
removeMessageListenerId ( "Marionette:switchToFrame" , switchToFrame ) ;
removeMessageListenerId ( "Marionette:deleteSession" , deleteSession ) ;
removeMessageListenerId ( "Marionette:sleepSession" , sleepSession ) ;
2012-05-19 00:30:43 +04:00
removeMessageListenerId ( "Marionette:emulatorCmdResult" , emulatorCmdResult ) ;
2012-06-04 21:50:06 +04:00
removeMessageListenerId ( "Marionette:importScript" , importScript ) ;
2012-08-24 02:07:16 +04:00
removeMessageListenerId ( "Marionette:getAppCacheStatus" , getAppCacheStatus ) ;
2012-11-01 02:36:57 +04:00
removeMessageListenerId ( "Marionette:setTestName" , setTestName ) ;
2014-01-24 17:45:58 +04:00
removeMessageListenerId ( "Marionette:takeScreenshot" , takeScreenshot ) ;
2012-11-22 19:53:44 +04:00
removeMessageListenerId ( "Marionette:addCookie" , addCookie ) ;
2014-01-21 20:40:20 +04:00
removeMessageListenerId ( "Marionette:getCookies" , getCookies ) ;
2012-11-22 19:53:44 +04:00
removeMessageListenerId ( "Marionette:deleteAllCookies" , deleteAllCookies ) ;
removeMessageListenerId ( "Marionette:deleteCookie" , deleteCookie ) ;
2013-10-17 19:25:11 +04:00
if ( isB2G ) {
content . removeEventListener ( "mozbrowsershowmodalprompt" , modalHandler , false ) ;
}
2013-11-24 09:32:27 +04:00
elementManager . reset ( ) ;
2012-11-14 01:51:58 +04:00
// reset frame to the top-most frame
2013-09-19 21:35:19 +04:00
curFrame = content ;
curFrame . focus ( ) ;
2015-03-20 04:41:19 +03:00
actions . touchIds = { } ;
2012-03-22 19:19:57 +04:00
}
/ *
2014-01-21 20:40:20 +04:00
* Helper methods
2012-03-22 19:19:57 +04:00
* /
/ * *
* Generic method to send a message to the server
* /
2015-04-15 14:18:00 +03:00
function sendToServer ( name , data , objs , id ) {
if ( ! data ) {
data = { }
2012-03-22 19:19:57 +04:00
}
2015-04-15 14:18:00 +03:00
if ( id ) {
data . command _id = id ;
}
sendAsyncMessage ( name , data , objs ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Send response back to server
* /
function sendResponse ( value , command _id ) {
2015-04-15 14:18:00 +03:00
sendToServer ( "Marionette:done" , value , null , command _id ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Send ack back to server
* /
function sendOk ( command _id ) {
2015-04-15 14:18:00 +03:00
sendToServer ( "Marionette:ok" , null , null , command _id ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Send log message to server
* /
function sendLog ( msg ) {
2015-04-15 14:18:00 +03:00
sendToServer ( "Marionette:log" , { message : msg } ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Send error message to server
* /
2015-04-15 14:18:00 +03:00
function sendError ( err , cmdId ) {
sendToServer ( "Marionette:error" , null , { error : err } , cmdId ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Clear test values after completion of test
* /
function resetValues ( ) {
2015-04-23 23:39:38 +03:00
sandboxes = { } ;
2013-09-19 21:35:19 +04:00
curFrame = content ;
2015-03-20 04:41:19 +03:00
actions . mouseEventsOnly = false ;
2012-03-22 19:19:57 +04:00
}
2013-09-13 11:33:41 +04:00
/ * *
* Dump a logline to stdout . Prepends logline with a timestamp .
* /
function dumpLog ( logline ) {
dump ( Date . now ( ) + " Marionette: " + logline ) ;
}
2013-06-22 04:13:35 +04:00
2013-09-19 21:35:19 +04: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 ;
}
2015-03-20 04:41:19 +03:00
function checkForInterrupted ( ) {
if ( wasInterrupted ( ) ) {
if ( previousFrame ) {
//if previousFrame is set, then we're in a single process environment
2015-04-22 20:22:26 +03:00
curFrame = actions . frame = previousFrame ;
2015-03-20 04:41:19 +03:00
previousFrame = 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 } ) ;
}
}
2012-03-22 19:19:57 +04:00
/ *
* Marionette Methods
* /
/ * *
* Returns a content sandbox that can be used by the execute _foo functions .
* /
2015-04-28 21:16:46 +03:00
function createExecuteContentSandbox ( win , timeout ) {
let mn = new Marionette (
this ,
win ,
"content" ,
marionetteLogObj ,
timeout ,
heartbeatCallback ,
marionetteTestName ) ;
mn . runEmulatorCmd = ( cmd , cb ) => this . runEmulatorCmd ( cmd , cb ) ;
mn . runEmulatorShell = ( args , cb ) => this . runEmulatorShell ( args , cb ) ;
2015-04-23 23:39:38 +03:00
let principal = win ;
if ( sandboxName == 'system' ) {
principal = Cc [ "@mozilla.org/systemprincipal;1" ] .
createInstance ( Ci . nsIPrincipal ) ;
}
let sandbox = new Cu . Sandbox ( principal , { sandboxPrototype : win } ) ;
2012-05-09 23:05:39 +04:00
sandbox . global = sandbox ;
2015-04-28 21:16:46 +03:00
sandbox . window = win ;
2012-03-22 19:19:57 +04:00
sandbox . document = sandbox . window . document ;
sandbox . navigator = sandbox . window . navigator ;
2012-04-11 04:28:08 +04:00
sandbox . testUtils = utils ;
2013-03-25 22:09:16 +04:00
sandbox . asyncTestCommandId = asyncTestCommandId ;
2015-04-28 21:16:46 +03:00
sandbox . marionette = mn ;
2012-03-22 19:19:57 +04:00
2015-04-28 21:16:46 +03:00
mn . exports . forEach ( fn => {
if ( typeof mn [ fn ] == "function" ) {
sandbox [ fn ] = mn [ fn ] . bind ( mn ) ;
} else {
sandbox [ fn ] = mn [ fn ] ;
2012-09-15 01:34:38 +04:00
}
2012-03-22 19:19:57 +04:00
} ) ;
2015-04-28 21:16:46 +03:00
sandbox . asyncComplete = ( obj , id ) => {
2015-04-15 14:18:00 +03:00
if ( id == asyncTestCommandId ) {
2013-09-19 21:35:19 +04:00
curFrame . removeEventListener ( "unload" , onunload , false ) ;
curFrame . clearTimeout ( asyncTestTimeoutId ) ;
2012-05-09 23:05:39 +04:00
2013-08-28 21:06:03 +04:00
if ( inactivityTimeoutId != null ) {
2013-09-19 21:35:19 +04:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 21:06:03 +04:00
}
2013-04-29 20:38:54 +04:00
sendSyncMessage ( "Marionette:shareData" ,
2015-04-15 14:18:00 +03:00
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2013-03-25 22:09:16 +04:00
marionetteLogObj . clearLogs ( ) ;
2012-11-28 01:58:58 +04:00
2015-04-15 14:18:00 +03:00
if ( error . isError ( obj ) ) {
sendError ( obj , id ) ;
} else {
2013-03-25 22:09:16 +04:00
if ( Object . keys ( _emu _cbs ) . length ) {
_emu _cbs = { } ;
2015-04-15 14:18:00 +03:00
sendError ( new WebDriverError ( "Emulator callback still pending when finish() called" ) , id ) ;
} else {
sendResponse ( { value : elementManager . wrapValue ( obj ) } , id ) ;
2013-03-25 22:09:16 +04:00
}
2012-11-28 01:58:58 +04:00
}
2012-05-09 23:05:39 +04:00
2013-03-25 22:09:16 +04:00
asyncTestRunning = false ;
asyncTestTimeoutId = undefined ;
asyncTestCommandId = undefined ;
2013-08-28 21:06:03 +04:00
inactivityTimeoutId = null ;
2013-03-25 22:09:16 +04:00
}
2012-05-09 23:05:39 +04:00
} ;
2015-04-08 21:02:34 +03:00
sandbox . finish = function ( ) {
2012-05-09 23:05:39 +04:00
if ( asyncTestRunning ) {
2015-04-28 21:16:46 +03:00
sandbox . asyncComplete ( mn . generate _results ( ) , sandbox . asyncTestCommandId ) ;
2012-05-09 23:05:39 +04:00
} else {
2015-04-28 21:16:46 +03:00
return mn . generate _results ( ) ;
2012-05-09 23:05:39 +04:00
}
} ;
2015-04-15 14:18:00 +03:00
sandbox . marionetteScriptFinished = val =>
sandbox . asyncComplete ( val , sandbox . asyncTestCommandId ) ;
2012-05-09 23:05:39 +04:00
2015-04-23 23:39:38 +03:00
sandboxes [ sandboxName ] = sandbox ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Execute the given script either as a function body ( executeScript )
2015-04-15 14:18:00 +03:00
* or directly ( for mochitest like JS Marionette tests ) .
2012-03-22 19:19:57 +04:00
* /
function executeScript ( msg , directInject ) {
2013-08-28 21:06:03 +04:00
// Set up inactivity timeout.
if ( msg . json . inactivityTimeout ) {
let setTimer = function ( ) {
2015-04-15 14:18:00 +03:00
inactivityTimeoutId = curFrame . setTimeout ( function ( ) {
sendError ( new ScriptTimeoutError ( "timed out due to inactivity" ) , asyncTestCommandId ) ;
2013-08-28 21:06:03 +04:00
} , msg . json . inactivityTimeout ) ;
} ;
setTimer ( ) ;
2015-04-15 14:18:00 +03:00
heartbeatCallback = function ( ) {
2013-09-19 21:35:19 +04:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 21:06:03 +04:00
setTimer ( ) ;
} ;
}
2013-02-12 22:10:03 +04:00
asyncTestCommandId = msg . json . command _id ;
2013-10-01 19:13:04 +04:00
let script = msg . json . script ;
2015-04-23 23:39:38 +03:00
sandboxName = msg . json . sandboxName ;
2012-03-22 19:19:57 +04:00
2015-04-23 23:39:38 +03:00
if ( msg . json . newSandbox ||
! ( sandboxName in sandboxes ) ||
( sandboxes [ sandboxName ] . window != curFrame ) ) {
createExecuteContentSandbox ( curFrame , msg . json . timeout ) ;
if ( ! sandboxes [ sandboxName ] ) {
2015-04-15 14:18:00 +03:00
sendError ( new WebDriverError ( "Could not create sandbox!" ) , asyncTestCommandId ) ;
2012-05-09 23:05:39 +04:00
return ;
}
2015-04-15 14:18:00 +03:00
} else {
2015-04-23 23:39:38 +03:00
sandboxes [ sandboxName ] . asyncTestCommandId = asyncTestCommandId ;
2013-03-25 22:09:16 +04:00
}
2012-03-22 19:19:57 +04:00
2015-04-23 23:39:38 +03:00
let sandbox = sandboxes [ sandboxName ] ;
2012-03-22 19:19:57 +04:00
try {
if ( directInject ) {
2012-06-04 21:50:06 +04:00
if ( importedScripts . exists ( ) ) {
2014-01-21 20:40:20 +04:00
let stream = Components . classes [ "@mozilla.org/network/file-input-stream;1" ] .
2012-06-04 21:50:06 +04:00
createInstance ( Components . interfaces . nsIFileInputStream ) ;
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
2014-06-11 22:26:28 +04:00
stream . close ( ) ;
2012-06-04 21:50:06 +04:00
script = data + script ;
}
2013-06-22 04:13:35 +04:00
let res = Cu . evalInSandbox ( script , sandbox , "1.8" , "dummy file" , 0 ) ;
2013-04-29 20:38:54 +04:00
sendSyncMessage ( "Marionette:shareData" ,
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2012-03-22 19:19:57 +04:00
marionetteLogObj . clearLogs ( ) ;
2013-04-29 20:38:54 +04:00
2012-03-22 19:19:57 +04:00
if ( res == undefined || res . passed == undefined ) {
2015-04-15 14:18:00 +03:00
sendError ( new JavaScriptError ( "Marionette.finish() not called" ) , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
else {
2012-11-27 06:19:04 +04:00
sendResponse ( { value : elementManager . wrapValue ( res ) } , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
}
else {
2012-05-09 23:05:39 +04:00
try {
2014-07-21 01:36:31 +04:00
sandbox . _ _marionetteParams = Cu . cloneInto ( elementManager . convertWrappedArguments (
msg . json . args , curFrame ) , sandbox , { wrapReflectors : true } ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , asyncTestCommandId ) ;
2012-05-09 23:05:39 +04:00
return ;
}
2013-06-22 04:13:35 +04:00
script = "let __marionetteFunc = function(){" + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams);" ;
2012-06-04 21:50:06 +04:00
if ( importedScripts . exists ( ) ) {
2014-01-21 20:40:20 +04:00
let stream = Components . classes [ "@mozilla.org/network/file-input-stream;1" ] .
2012-06-04 21:50:06 +04:00
createInstance ( Components . interfaces . nsIFileInputStream ) ;
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
2014-06-11 22:26:28 +04:00
stream . close ( ) ;
2013-06-22 04:13:35 +04:00
script = data + script ;
2012-06-04 21:50:06 +04:00
}
2013-06-22 04:13:35 +04:00
let res = Cu . evalInSandbox ( script , sandbox , "1.8" , "dummy file" , 0 ) ;
2013-04-29 20:38:54 +04:00
sendSyncMessage ( "Marionette:shareData" ,
{ log : elementManager . wrapValue ( marionetteLogObj . getLogs ( ) ) } ) ;
2012-03-22 19:19:57 +04:00
marionetteLogObj . clearLogs ( ) ;
2012-11-27 06:19:04 +04:00
sendResponse ( { value : elementManager . wrapValue ( res ) } , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
2015-04-15 14:18:00 +03:00
} catch ( e ) {
let err = new JavaScriptError (
e ,
"execute_script" ,
msg . json . filename ,
msg . json . line ,
script ) ;
sendError ( err , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
}
2012-11-01 02:36:57 +04:00
/ * *
* Sets the test name , used in logging messages .
* /
function setTestName ( msg ) {
marionetteTestName = msg . json . value ;
2012-11-27 06:19:04 +04:00
sendOk ( msg . json . command _id ) ;
2012-11-01 02:36:57 +04:00
}
2012-03-22 19:19:57 +04:00
/ * *
* Execute async script
* /
function executeAsyncScript ( msg ) {
executeWithCallback ( msg ) ;
}
2015-05-19 02:36:15 +03:00
/ * *
* Receive file objects from chrome in order to complete a
* sendKeysToElement action on a file input element .
* /
function receiveFiles ( msg ) {
if ( 'error' in msg . json ) {
let err = new InvalidArgumentError ( msg . json . error ) ;
sendError ( err , msg . json . command _id ) ;
return ;
}
if ( ! fileInputElement ) {
let err = new InvalidElementStateError ( "receiveFiles called with no valid fileInputElement" ) ;
sendError ( err , msg . json . command _id ) ;
return ;
}
let fs = Array . prototype . slice . call ( fileInputElement . files ) ;
fs . push ( msg . json . file ) ;
fileInputElement . mozSetFileArray ( fs ) ;
fileInputElement = null ;
sendOk ( msg . json . command _id ) ;
}
2012-03-22 19:19:57 +04:00
/ * *
* Execute pure JS test . Handles both async and sync cases .
* /
function executeJSScript ( msg ) {
2012-11-29 01:31:23 +04:00
if ( msg . json . async ) {
executeWithCallback ( msg , msg . json . async ) ;
2012-03-22 19:19:57 +04:00
}
else {
executeScript ( msg , true ) ;
}
}
/ * *
* This function is used by executeAsync and executeJSScript to execute a script
2014-01-21 20:40:20 +04:00
* in a sandbox .
*
2012-03-22 19:19:57 +04:00
* For executeJSScript , it will return a message only when the finish ( ) method is called .
2014-01-21 20:40:20 +04:00
* For executeAsync , it will return a response when marionetteScriptFinished / arguments [ arguments . length - 1 ]
2012-03-22 19:19:57 +04:00
* method is called , or if it times out .
* /
2012-11-30 19:42:43 +04:00
function executeWithCallback ( msg , useFinish ) {
2013-08-28 21:06:03 +04:00
// Set up inactivity timeout.
if ( msg . json . inactivityTimeout ) {
let setTimer = function ( ) {
2013-09-19 21:35:19 +04:00
inactivityTimeoutId = curFrame . setTimeout ( function ( ) {
2015-04-28 20:59:07 +03:00
sandbox . asyncComplete ( new ScriptTimeoutError ( "timed out due to inactivity" ) , asyncTestCommandId ) ;
2013-08-28 21:06:03 +04:00
} , msg . json . inactivityTimeout ) ;
} ;
setTimer ( ) ;
2015-04-15 14:18:00 +03:00
heartbeatCallback = function ( ) {
2013-09-19 21:35:19 +04:00
curFrame . clearTimeout ( inactivityTimeoutId ) ;
2013-08-28 21:06:03 +04:00
setTimer ( ) ;
} ;
}
2013-10-01 19:13:04 +04:00
let script = msg . json . script ;
2013-02-12 22:10:03 +04:00
asyncTestCommandId = msg . json . command _id ;
2015-04-23 23:39:38 +03:00
sandboxName = msg . json . sandboxName ;
2013-02-12 22:10:03 +04:00
onunload = function ( ) {
2015-04-15 14:18:00 +03:00
sendError ( new JavaScriptError ( "unload was called" ) , asyncTestCommandId ) ;
2013-02-12 22:10:03 +04:00
} ;
2013-09-19 21:35:19 +04:00
curFrame . addEventListener ( "unload" , onunload , false ) ;
2012-03-22 19:19:57 +04:00
2015-04-23 23:39:38 +03:00
if ( msg . json . newSandbox ||
! ( sandboxName in sandboxes ) ||
( sandboxes [ sandboxName ] . window != curFrame ) ) {
createExecuteContentSandbox ( curFrame , msg . json . timeout ) ;
if ( ! sandboxes [ sandboxName ] ) {
2015-04-15 14:18:00 +03:00
sendError ( new JavaScriptError ( "Could not create sandbox!" ) , asyncTestCommandId ) ;
2012-05-11 03:07:07 +04:00
return ;
}
}
2013-03-25 22:09:16 +04:00
else {
2015-04-23 23:39:38 +03:00
sandboxes [ sandboxName ] . asyncTestCommandId = asyncTestCommandId ;
2013-03-25 22:09:16 +04:00
}
2015-04-23 23:39:38 +03:00
let sandbox = sandboxes [ sandboxName ] ;
2012-11-27 06:19:04 +04:00
sandbox . tag = script ;
2012-05-11 03:07:07 +04:00
2013-09-19 21:35:19 +04:00
asyncTestTimeoutId = curFrame . setTimeout ( function ( ) {
2015-04-15 14:18:00 +03:00
sandbox . asyncComplete ( new ScriptTimeoutError ( "timed out" ) , asyncTestCommandId ) ;
2012-11-29 01:31:23 +04:00
} , msg . json . timeout ) ;
2012-05-11 03:07:07 +04:00
2013-09-19 21:35:19 +04:00
originalOnError = curFrame . onerror ;
2015-04-15 14:18:00 +03:00
curFrame . onerror = function errHandler ( msg , url , line ) {
sandbox . asyncComplete ( new JavaScriptError ( msg + "@" + url + ", line " + line ) , asyncTestCommandId ) ;
2013-09-19 21:35:19 +04:00
curFrame . onerror = originalOnError ;
2012-12-18 18:21:28 +04:00
} ;
2012-03-22 19:19:57 +04:00
let scriptSrc ;
2012-11-30 19:42:43 +04:00
if ( useFinish ) {
2012-11-29 01:31:23 +04:00
if ( msg . json . timeout == null || msg . json . timeout == 0 ) {
2015-04-15 14:18:00 +03:00
sendError ( new TimeoutError ( "Please set a timeout" ) , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
scriptSrc = script ;
}
else {
2012-05-09 23:05:39 +04:00
try {
2014-07-21 01:36:31 +04:00
sandbox . _ _marionetteParams = Cu . cloneInto ( elementManager . convertWrappedArguments (
msg . json . args , curFrame ) , sandbox , { wrapReflectors : true } ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , asyncTestCommandId ) ;
2012-05-09 23:05:39 +04:00
return ;
}
scriptSrc = "__marionetteParams.push(marionetteScriptFinished);" +
2012-03-22 19:19:57 +04:00
"let __marionetteFunc = function() { " + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams); " ;
}
try {
2012-05-09 23:05:39 +04:00
asyncTestRunning = true ;
2012-06-04 21:50:06 +04:00
if ( importedScripts . exists ( ) ) {
2012-09-01 00:59:48 +04:00
let stream = Cc [ "@mozilla.org/network/file-input-stream;1" ] .
createInstance ( Ci . nsIFileInputStream ) ;
2012-06-04 21:50:06 +04:00
stream . init ( importedScripts , - 1 , 0 , 0 ) ;
let data = NetUtil . readInputStreamToString ( stream , stream . available ( ) ) ;
2014-06-11 22:26:28 +04:00
stream . close ( ) ;
2012-06-04 21:50:06 +04:00
scriptSrc = data + scriptSrc ;
}
2013-06-22 04:13:35 +04:00
Cu . evalInSandbox ( scriptSrc , sandbox , "1.8" , "dummy file" , 0 ) ;
2012-03-22 19:19:57 +04:00
} catch ( e ) {
2015-04-15 14:18:00 +03:00
let err = new JavaScriptError (
e ,
"execute_async_script" ,
msg . json . filename ,
msg . json . line ,
scriptSrc ) ;
sandbox . asyncComplete ( err , asyncTestCommandId ) ;
2012-03-22 19:19:57 +04:00
}
}
2013-01-22 23:27:44 +04:00
/ * *
* This function creates a touch event given a touch type and a touch
* /
2014-06-10 19:34:27 +04:00
function emitTouchEvent ( type , touch ) {
2013-09-19 21:35:19 +04:00
if ( ! wasInterrupted ( ) ) {
2014-06-10 19:34:27 +04:00
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 ) ;
2014-04-10 18:26:08 +04:00
var docShell = curFrame . document . defaultView .
QueryInterface ( Components . interfaces . nsIInterfaceRequestor ) .
getInterface ( Components . interfaces . nsIWebNavigation ) .
QueryInterface ( Components . interfaces . nsIDocShell ) ;
2015-03-20 04:41:19 +03:00
if ( docShell . asyncPanZoomEnabled && actions . scrolling ) {
2014-04-10 18:26:08 +04:00
// if we're in APZ and we're scrolling, we must use injectTouchEvent to dispatch our touchmove events
let index = sendSyncMessage ( "MarionetteFrame:getCurrentFrameId" ) ;
// only call emitTouchEventForIFrame if we're inside an iframe.
if ( index != null ) {
2015-01-20 01:12:00 +03:00
sendSyncMessage ( "Marionette:emitTouchEvent" ,
{ index : index , type : type , id : touch . identifier ,
clientX : touch . clientX , clientY : touch . clientY ,
screenX : touch . screenX , screenY : touch . screenY ,
radiusX : touch . radiusX , radiusY : touch . radiusY ,
rotation : touch . rotationAngle , force : touch . force } ) ;
2014-06-10 19:34:27 +04:00
return ;
2014-04-10 18:26:08 +04:00
}
}
// we get here if we're not in asyncPacZoomEnabled land, or if we're the main process
2013-09-19 21:35:19 +04:00
/ *
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 18:28:04 +04:00
domWindowUtils . sendTouchEvent ( type , [ touch . identifier ] , [ touch . clientX ] , [ touch . clientY ] , [ touch . radiusX ] , [ touch . radiusY ] , [ touch . rotationAngle ] , [ touch . force ] , 1 , 0 ) ;
2013-09-19 21:35:19 +04:00
}
2013-01-22 23:27:44 +04:00
}
/ * *
2013-05-27 21:12:13 +04: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 23:27:44 +04:00
* If they are not specified , then the center of the target is used .
* /
2013-05-27 21:12:13 +04:00
function coordinates ( target , x , y ) {
2013-03-05 05:09:58 +04:00
let box = target . getBoundingClientRect ( ) ;
2013-05-27 21:12:13 +04:00
if ( x == null ) {
x = box . width / 2 ;
2013-01-22 23:27:44 +04:00
}
2013-05-27 21:12:13 +04:00
if ( y == null ) {
y = box . height / 2 ;
2013-01-22 23:27:44 +04:00
}
2013-05-27 21:12:13 +04:00
let coords = { } ;
coords . x = box . left + x ;
coords . y = box . top + y ;
2013-01-22 23:27:44 +04:00
return coords ;
}
2015-03-20 04:41:19 +03:00
2013-01-22 23:27:44 +04:00
/ * *
2014-10-16 12:32:00 +04:00
* This function returns true if the given coordinates are in the viewport .
* @ param 'x' , and 'y' are the coordinates relative to the target .
* If they are not specified , then the center of the target is used .
2013-01-22 23:27:44 +04:00
* /
2014-10-16 12:32:00 +04:00
function elementInViewport ( el , x , y ) {
let c = coordinates ( el , x , y ) ;
2013-09-19 21:35:19 +04:00
let viewPort = { top : curFrame . pageYOffset ,
left : curFrame . pageXOffset ,
bottom : ( curFrame . pageYOffset + curFrame . innerHeight ) ,
right : ( curFrame . pageXOffset + curFrame . innerWidth ) } ;
2014-10-16 12:32:00 +04:00
return ( viewPort . left <= c . x + curFrame . pageXOffset &&
c . x + curFrame . pageXOffset <= viewPort . right &&
viewPort . top <= c . y + curFrame . pageYOffset &&
c . y + curFrame . pageYOffset <= viewPort . bottom ) ;
2013-01-22 23:27:44 +04:00
}
/ * *
2014-10-16 12:32:00 +04:00
* This function throws the visibility of the element error if the element is
* not displayed or the given coordinates are not within the viewport .
* @ param 'x' , and 'y' are the coordinates relative to the target .
* If they are not specified , then the center of the target is used .
2013-01-22 23:27:44 +04:00
* /
2014-10-16 12:32:00 +04:00
function checkVisible ( el , x , y ) {
2014-11-11 00:44:00 +03:00
// Bug 1094246 - Webdriver's isShown doesn't work with content xul
if ( utils . getElementAttribute ( el , "namespaceURI" ) . indexOf ( "there.is.only.xul" ) == - 1 ) {
//check if the element is visible
let visible = utils . isElementDisplayed ( el ) ;
if ( ! visible ) {
return false ;
}
2013-03-02 01:29:10 +04:00
}
2014-11-11 00:44:00 +03:00
2013-04-12 00:52:42 +04:00
if ( el . tagName . toLowerCase ( ) === 'body' ) {
return true ;
}
2014-10-16 12:32:00 +04:00
if ( ! elementInViewport ( el , x , y ) ) {
2013-01-22 23:27:44 +04:00
//check if scroll function exist. If so, call it.
if ( el . scrollIntoView ) {
2013-09-05 21:38:36 +04:00
el . scrollIntoView ( false ) ;
2013-03-02 01:29:10 +04:00
if ( ! elementInViewport ( el ) ) {
return false ;
}
2013-01-22 23:27:44 +04:00
}
2013-03-02 01:29:10 +04:00
else {
2013-01-22 23:27:44 +04:00
return false ;
}
2013-03-02 01:29:10 +04:00
}
return true ;
2013-01-22 23:27:44 +04:00
}
2013-05-27 21:12:13 +04:00
2013-01-22 23:27:44 +04:00
/ * *
* Function that perform a single tap
* /
function singleTap ( msg ) {
let command _id = msg . json . command _id ;
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2014-12-23 00:15:19 +03:00
let acc = accessibility . getAccessibleObject ( el , true ) ;
2013-05-27 21:12:13 +04:00
// after this block, the element will be scrolled into view
2014-12-23 00:15:19 +03:00
let visible = checkVisible ( el , msg . json . corx , msg . json . cory ) ;
checkVisibleAccessibility ( acc , visible ) ;
if ( ! visible ) {
2015-04-15 14:18:00 +03:00
sendError ( new ElementNotVisibleError ( "Element is not currently visible and may not be manipulated" ) , command _id ) ;
2014-12-23 00:15:19 +03:00
return ;
2013-04-11 03:55:43 +04:00
}
2014-12-23 00:15:19 +03:00
checkActionableAccessibility ( acc ) ;
2013-09-19 21:35:19 +04:00
if ( ! curFrame . document . createTouch ) {
2015-03-20 04:41:19 +03:00
actions . mouseEventsOnly = true ;
}
let c = coordinates ( el , msg . json . corx , msg . json . cory ) ;
if ( ! actions . mouseEventsOnly ) {
let touchId = actions . nextTouchId ++ ;
let touch = createATouch ( el , c . x , c . y , touchId ) ;
emitTouchEvent ( 'touchstart' , touch ) ;
emitTouchEvent ( 'touchend' , touch ) ;
2013-04-11 03:55:43 +04:00
}
2015-03-20 04:41:19 +03:00
actions . mouseTap ( el . ownerDocument , c . x , c . y ) ;
2015-04-15 14:18:00 +03:00
sendOk ( command _id ) ;
} catch ( e ) {
sendError ( e , command _id ) ;
2013-01-22 23:27:44 +04:00
}
}
2014-12-23 00:16:59 +03:00
/ * *
* Check if the element ' s unavailable accessibility state matches the enabled
* state
* @ param nsIAccessible object
2015-08-05 21:40:18 +03:00
* @ param WebElement corresponding to nsIAccessible object
2014-12-23 00:16:59 +03:00
* @ param Boolean enabled element ' s enabled state
* /
2015-08-05 21:40:18 +03:00
function checkEnabledAccessibility ( accesible , element , enabled ) {
2014-12-23 00:16:59 +03:00
if ( ! accesible ) {
return ;
}
2015-08-05 21:40:18 +03:00
let disabledAccessibility = accessibility . matchState (
accesible , 'STATE_UNAVAILABLE' ) ;
let explorable = curFrame . document . defaultView . getComputedStyle (
element , null ) . getPropertyValue ( 'pointer-events' ) !== 'none' ;
let message ;
if ( ! explorable && ! disabledAccessibility ) {
message = 'Element is enabled but is not explorable via the ' +
'accessibility API' ;
} else if ( enabled && disabledAccessibility ) {
message = 'Element is enabled but disabled via the accessibility API' ;
} else if ( ! enabled && ! disabledAccessibility ) {
message = 'Element is disabled but enabled via the accessibility API' ;
2014-12-23 00:16:59 +03:00
}
2015-08-05 21:40:18 +03:00
accessibility . handleErrorMessage ( message ) ;
2014-12-23 00:16:59 +03:00
}
2014-12-23 00:15:19 +03:00
/ * *
* Check if the element ' s visible state corresponds to its accessibility API
* visibility
* @ param nsIAccessible object
* @ param Boolean visible element ' s visibility state
* /
function checkVisibleAccessibility ( accesible , visible ) {
if ( ! accesible ) {
return ;
}
let hiddenAccessibility = accessibility . isHidden ( accesible ) ;
let message ;
if ( visible && hiddenAccessibility ) {
message = 'Element is not currently visible via the accessibility API ' +
'and may not be manipulated by it' ;
} else if ( ! visible && ! hiddenAccessibility ) {
message = 'Element is currently only visible via the accessibility API ' +
'and can be manipulated by it' ;
}
accessibility . handleErrorMessage ( message ) ;
}
/ * *
* Check if it is possible to activate an element with the accessibility API
* @ param nsIAccessible object
* /
function checkActionableAccessibility ( accesible ) {
if ( ! accesible ) {
return ;
}
let message ;
if ( ! accessibility . hasActionCount ( accesible ) ) {
message = 'Element does not support any accessible actions' ;
} else if ( ! accessibility . isActionableRole ( accesible ) ) {
message = 'Element does not have a correct accessibility role ' +
'and may not be manipulated via the accessibility API' ;
} else if ( ! accessibility . hasValidName ( accesible ) ) {
message = 'Element is missing an accesible name' ;
2015-08-05 21:40:18 +03:00
} else if ( ! accessibility . matchState ( accesible , 'STATE_FOCUSABLE' ) ) {
message = 'Element is not focusable via the accessibility API' ;
}
accessibility . handleErrorMessage ( message ) ;
}
/ * *
* Check if element ' s selected state corresponds to its accessibility API
* selected state .
* @ param nsIAccessible object
* @ param Boolean selected element ' s selected state
* /
function checkSelectedAccessibility ( accessible , selected ) {
if ( ! accessible ) {
return ;
}
if ( ! accessibility . matchState ( accessible , 'STATE_SELECTABLE' ) ) {
// Element is not selectable via the accessibility API
return ;
}
let selectedAccessibility = accessibility . matchState (
accessible , 'STATE_SELECTED' ) ;
let message ;
if ( selected && ! selectedAccessibility ) {
message = 'Element is selected but not selected via the accessibility API' ;
} else if ( ! selected && selectedAccessibility ) {
message = 'Element is not selected but selected via the accessibility API' ;
2014-12-23 00:15:19 +03:00
}
accessibility . handleErrorMessage ( message ) ;
}
2014-09-22 19:41:51 +04:00
2013-02-06 23:58:14 +04:00
/ * *
* Function to create a touch based on the element
2013-05-27 21:12:13 +04:00
* corx and cory are relative to the viewport , id is the touchId
2013-02-06 23:58:14 +04:00
* /
2013-05-27 21:12:13 +04:00
function createATouch ( el , corx , cory , touchId ) {
2013-03-05 05:09:58 +04:00
let doc = el . ownerDocument ;
let win = doc . defaultView ;
2014-09-22 19:41:51 +04:00
let [ clientX , clientY , pageX , pageY , screenX , screenY ] =
2015-03-20 04:41:19 +03:00
actions . getCoordinateInfo ( el , corx , cory ) ;
2013-05-27 21:12:13 +04:00
let atouch = doc . createTouch ( win , el , touchId , pageX , pageY , screenX , screenY , clientX , clientY ) ;
2013-02-06 23:58:14 +04:00
return atouch ;
}
2013-03-05 05:09:58 +04:00
/ * *
2014-01-21 20:40:20 +04:00
* Function to start action chain on one finger
2013-03-05 05:09:58 +04:00
* /
function actionChain ( msg ) {
let command _id = msg . json . command _id ;
2013-03-22 22:29:02 +04:00
let args = msg . json . chain ;
let touchId = msg . json . nextId ;
2015-03-20 04:41:19 +03:00
let callbacks = { } ;
2015-04-15 14:18:00 +03:00
callbacks . onSuccess = value => sendResponse ( value , command _id ) ;
callbacks . onError = err => sendError ( err , command _id ) ;
2015-03-20 04:41:19 +03:00
let touchProvider = { } ;
touchProvider . createATouch = createATouch ;
touchProvider . emitTouchEvent = emitTouchEvent ;
2015-03-24 18:35:58 +03:00
try {
actions . dispatchActions (
args ,
touchId ,
curFrame ,
elementManager ,
callbacks ,
touchProvider ) ;
} catch ( e ) {
2015-04-15 14:18:00 +03:00
sendError ( e , command _id ) ;
2015-03-24 18:35:58 +03:00
}
2013-03-05 05:09:58 +04:00
}
2013-03-19 00:42:46 +04: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 21:47:53 +04:00
return ( ( t . target . ownerDocument === doc ) && ( type != 'touchcancel' ) ) ;
2013-03-19 00:42:46 +04:00
} ) ) ;
// touches on the same target
let targetTouches = doc . createTouchList ( touches . filter ( function ( t ) {
2013-06-04 21:47:53 +04:00
return ( ( t . target === target ) && ( ( type != 'touchcancel' ) || ( type != 'touchend' ) ) ) ;
2013-03-19 00:42:46 +04:00
} ) ) ;
// Create changed touches
let changedTouches = doc . createTouchList ( touch ) ;
// Create the event object
2014-01-15 18:28:04 +04:00
let event = doc . createEvent ( 'TouchEvent' ) ;
2013-03-19 00:42:46 +04: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 21:12:13 +04:00
let c ;
2013-03-19 00:42:46 +04: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 21:35:19 +04:00
el = elementManager . getKnownElement ( pack [ 2 ] , curFrame ) ;
2013-05-27 21:12:13 +04:00
c = coordinates ( el , pack [ 3 ] , pack [ 4 ] ) ;
touch = createATouch ( el , c . x , c . y , touchId ) ;
2013-03-19 00:42:46 +04: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 21:35:19 +04:00
el = elementManager . getKnownElement ( pack [ 2 ] , curFrame ) ;
2013-05-27 21:12:13 +04:00
c = coordinates ( el ) ;
touch = createATouch ( multiLast [ touchId ] . target , c . x , c . y , touchId ) ;
2013-03-19 00:42:46 +04: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 21:35:19 +04:00
let commandArray = elementManager . convertWrappedArguments ( args , curFrame ) ;
2013-03-19 00:42:46 +04: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 ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2013-03-19 00:42:46 +04:00
}
}
2015-03-11 02:19:40 +03:00
/ *
* This implements the latter part of a get request ( for the case we need to resume one
* when a remoteness update happens in the middle of a navigate request ) . This is most of
* of the work of a navigate request , but doesn ' t assume DOMContentLoaded is yet to fire .
2012-03-22 19:19:57 +04:00
* /
2015-03-11 02:19:40 +03:00
function pollForReadyState ( msg , start , callback ) {
let { pageTimeout , url , command _id } = msg . json ;
start = start ? start : new Date ( ) . getTime ( ) ;
if ( ! callback ) {
callback = ( ) => { } ;
}
2013-06-05 10:59:05 +04:00
let end = null ;
2014-01-24 17:39:23 +04:00
function checkLoad ( ) {
2015-03-11 02:19:40 +03:00
navTimer . cancel ( ) ;
2013-06-05 10:59:05 +04:00
end = new Date ( ) . getTime ( ) ;
2014-12-12 20:08:32 +03:00
let aboutErrorRegex = /about:.+(error)\?/ ;
2013-06-05 10:59:05 +04:00
let elapse = end - start ;
2015-03-11 02:19:40 +03:00
if ( pageTimeout == null || elapse <= pageTimeout ) {
2014-01-24 17:39:23 +04:00
if ( curFrame . document . readyState == "complete" ) {
2015-03-11 02:19:40 +03:00
callback ( ) ;
2013-06-05 10:59:05 +04:00
sendOk ( command _id ) ;
2014-12-12 20:08:32 +03:00
} else if ( curFrame . document . readyState == "interactive" &&
aboutErrorRegex . exec ( curFrame . document . baseURI ) &&
2015-03-11 02:19:40 +03:00
! curFrame . document . baseURI . startsWith ( url ) ) {
2014-12-12 20:08:32 +03:00
// We have reached an error url without requesting it.
2015-03-11 02:19:40 +03:00
callback ( ) ;
2015-04-15 14:18:00 +03:00
sendError ( new UnknownError ( "Error loading page" ) , command _id ) ;
2014-12-12 20:08:32 +03:00
} else if ( curFrame . document . readyState == "interactive" &&
curFrame . document . baseURI . startsWith ( "about:" ) ) {
2015-03-11 02:19:40 +03:00
callback ( ) ;
2014-12-12 20:08:32 +03:00
sendOk ( command _id ) ;
} else {
2015-03-11 02:19:40 +03:00
navTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2013-06-05 10:59:05 +04:00
}
2015-04-15 14:18:00 +03:00
} else {
2015-03-11 02:19:40 +03:00
callback ( ) ;
2015-04-15 14:18:00 +03:00
sendError ( new TimeoutError ( "Error loading page, timed out (checkLoad)" ) , command _id ) ;
2013-06-05 10:59:05 +04:00
}
}
2015-03-11 02:19:40 +03:00
checkLoad ( ) ;
}
/ * *
* Navigate to the given URL . The operation will be performed on the
2015-05-04 13:25:03 +03:00
* current browsing context , which means it handles the case where we
* navigate within an iframe . All other navigation is handled by the
* driver ( in chrome space ) .
2015-03-11 02:19:40 +03:00
* /
function get ( msg ) {
let start = new Date ( ) . getTime ( ) ;
2014-01-24 17:39:23 +04: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).
2015-03-11 02:19:40 +03:00
onDOMContentLoaded = function onDOMContentLoaded ( event ) {
2013-01-11 23:33:58 +04:00
if ( ! event . originalTarget . defaultView . frameElement ||
2014-01-24 17:39:23 +04:00
event . originalTarget . defaultView . frameElement == curFrame . frameElement ) {
2015-03-11 02:19:40 +03:00
pollForReadyState ( msg , start , ( ) => {
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
onDOMContentLoaded = null ;
} ) ;
2012-08-22 01:00:44 +04:00
}
2013-01-11 23:33:58 +04:00
} ;
2013-06-05 10:59:05 +04:00
2014-01-24 17:39:23 +04:00
function timerFunc ( ) {
2013-01-11 23:33:58 +04:00
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2015-04-15 14:18:00 +03:00
sendError ( new TimeoutError ( "Error loading page, timed out (onDOMContentLoaded)" ) , msg . json . command _id ) ;
2013-01-11 23:33:58 +04:00
}
2014-01-24 17:39:23 +04:00
if ( msg . json . pageTimeout != null ) {
2015-03-11 02:19:40 +03:00
navTimer . initWithCallback ( timerFunc , msg . json . pageTimeout , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2013-01-11 23:33:58 +04:00
}
addEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
2013-10-01 19:13:04 +04:00
curFrame . location = msg . json . url ;
2012-03-22 19:19:57 +04:00
}
2015-03-11 02:19:40 +03:00
/ * *
* Cancel the polling and remove the event listener associated with a current
* navigation request in case we ' re interupted by an onbeforeunload handler
* and navigation doesn ' t complete .
* /
function cancelRequest ( ) {
navTimer . cancel ( ) ;
if ( onDOMContentLoaded ) {
removeEventListener ( "DOMContentLoaded" , onDOMContentLoaded , false ) ;
}
}
2012-03-22 19:19:57 +04:00
/ * *
2014-01-21 22:31:31 +04:00
* Get URL of the top level browsing context .
2012-03-22 19:19:57 +04:00
* /
2014-01-21 22:31:31 +04:00
function getCurrentUrl ( msg ) {
2015-03-11 02:19:40 +03:00
let url ;
if ( msg . json . isB2G ) {
url = curFrame . location . href ;
} else {
url = content . location . href ;
}
sendResponse ( { value : url } , msg . json . command _id ) ;
2012-03-22 19:19:57 +04:00
}
2012-07-11 00:30:21 +04:00
/ * *
* Get the current Title of the window
* /
function getTitle ( msg ) {
2013-09-19 21:35:19 +04:00
sendResponse ( { value : curFrame . top . document . title } , msg . json . command _id ) ;
2012-07-11 00:30:21 +04:00
}
2012-08-09 23:31:12 +04:00
/ * *
2014-01-21 20:40:20 +04:00
* Get the current page source
2012-08-09 23:31:12 +04:00
* /
function getPageSource ( msg ) {
2013-09-19 21:35:19 +04:00
var XMLSerializer = curFrame . XMLSerializer ;
var pageSource = new XMLSerializer ( ) . serializeToString ( curFrame . document ) ;
2012-11-27 06:19:04 +04:00
sendResponse ( { value : pageSource } , msg . json . command _id ) ;
2012-08-09 23:31:12 +04:00
}
2012-03-22 19:19:57 +04:00
/ * *
2014-01-21 20:40:20 +04:00
* Go back in history
2012-03-22 19:19:57 +04:00
* /
function goBack ( msg ) {
2013-09-19 21:35:19 +04:00
curFrame . history . back ( ) ;
2012-11-27 06:19:04 +04:00
sendOk ( msg . json . command _id ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
2014-01-21 20:40:20 +04:00
* Go forward in history
2012-03-22 19:19:57 +04:00
* /
function goForward ( msg ) {
2013-09-19 21:35:19 +04:00
curFrame . history . forward ( ) ;
2012-11-27 06:19:04 +04:00
sendOk ( msg . json . command _id ) ;
2012-03-22 19:19:57 +04:00
}
/ * *
* Refresh the page
* /
function refresh ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2013-09-19 21:35:19 +04:00
curFrame . location . reload ( true ) ;
2012-11-27 06:19:04 +04:00
let listen = function ( ) {
removeEventListener ( "DOMContentLoaded" , arguments . callee , false ) ;
sendOk ( command _id ) ;
} ;
2012-03-22 19:19:57 +04:00
addEventListener ( "DOMContentLoaded" , listen , false ) ;
}
/ * *
2014-01-21 20:40:20 +04:00
* Find an element in the document using requested search strategy
2012-03-22 19:19:57 +04:00
* /
function findElementContent ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-03-22 19:19:57 +04:00
try {
2015-04-15 14:18:00 +03:00
let onSuccess = ( el , id ) => sendResponse ( { value : el } , id ) ;
let onError = ( err , id ) => sendError ( err , id ) ;
2013-09-19 21:35:19 +04:00
elementManager . find ( curFrame , msg . json , msg . json . searchTimeout ,
2015-04-15 14:18:00 +03:00
false /* all */ , onSuccess , onError , command _id ) ;
} catch ( e ) {
sendError ( e , command _id ) ;
2012-03-22 19:19:57 +04:00
}
}
/ * *
2014-01-21 20:40:20 +04:00
* Find elements in the document using requested search strategy
2012-03-22 19:19:57 +04:00
* /
function findElementsContent ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-03-22 19:19:57 +04:00
try {
2015-04-15 14:18:00 +03:00
let onSuccess = ( els , id ) => sendResponse ( { value : els } , id ) ;
let onError = ( err , id ) => sendError ( err , id ) ;
2013-09-19 21:35:19 +04:00
elementManager . find ( curFrame , msg . json , msg . json . searchTimeout ,
2015-04-15 14:18:00 +03:00
true /* all */ , onSuccess , onError , command _id ) ;
} catch ( e ) {
sendError ( e , command _id ) ;
2012-03-22 19:19:57 +04:00
}
}
2013-01-14 18:57:54 +04:00
/ * *
2015-04-15 14:18:00 +03:00
* Find and return the active element on the page .
*
* @ return { WebElement }
* Reference to web element .
2013-01-14 18:57:54 +04:00
* /
2015-04-15 14:18:00 +03:00
function getActiveElement ( ) {
let el = curFrame . document . activeElement ;
return elementManager . addToKnownElements ( el ) ;
2013-01-14 18:57:54 +04:00
}
2012-03-22 19:19:57 +04:00
/ * *
2015-04-15 14:18:00 +03:00
* Send click event to element .
*
* @ param { WebElement } id
* Reference to the web element to click .
* /
function clickElement ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
let acc = accessibility . getAccessibleObject ( el , true ) ;
let visible = checkVisible ( el ) ;
checkVisibleAccessibility ( acc , visible ) ;
if ( ! visible ) {
throw new ElementNotVisibleError ( "Element is not visible" ) ;
}
checkActionableAccessibility ( acc ) ;
if ( utils . isElementEnabled ( el ) ) {
2015-08-05 21:40:18 +03:00
checkEnabledAccessibility ( acc , el , true ) ;
2015-04-15 14:18:00 +03:00
utils . synthesizeMouseAtCenter ( el , { } , el . ownerDocument . defaultView ) ;
} else {
throw new InvalidElementStateError ( "Element is not Enabled" ) ;
2012-04-11 04:28:08 +04:00
}
}
/ * *
2015-04-15 14:18:00 +03:00
* Get a given attribute of an element .
*
* @ param { WebElement } id
* Reference to the web element to get the attribute of .
* @ param { string } name
* Name of the attribute .
*
* @ return { string }
* The value of the attribute .
2012-04-11 04:28:08 +04:00
* /
2015-04-15 14:18:00 +03:00
function getElementAttribute ( id , name ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
return utils . getElementAttribute ( el , name ) ;
2012-04-11 04:28:08 +04:00
}
/ * *
* Get the text of this element . This includes text from child elements .
2015-04-15 14:18:00 +03:00
*
* @ param { WebElement } id
* Reference to web element .
*
* @ return { string }
* Text of element .
2012-04-11 04:28:08 +04:00
* /
2015-04-15 14:18:00 +03:00
function getElementText ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
return utils . getElementText ( el ) ;
2012-04-11 04:28:08 +04:00
}
2012-08-09 00:21:50 +04:00
/ * *
* Get the tag name of an element .
2015-04-15 14:18:00 +03:00
*
* @ param { WebElement } id
* Reference to web element .
*
* @ return { string }
* Tag name of element .
2012-08-09 00:21:50 +04:00
* /
2015-04-15 14:18:00 +03:00
function getElementTagName ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
return el . tagName . toLowerCase ( ) ;
2012-08-09 00:21:50 +04:00
}
2012-04-11 04:28:08 +04:00
/ * *
* Check if element is displayed
* /
function isElementDisplayed ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-04-11 04:28:08 +04:00
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2014-12-23 00:16:37 +03:00
let displayed = utils . isElementDisplayed ( el ) ;
checkVisibleAccessibility ( accessibility . getAccessibleObject ( el ) , displayed ) ;
sendResponse ( { value : displayed } , command _id ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2012-04-11 04:28:08 +04:00
}
}
2013-05-08 01:38:21 +04: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
* /
2015-04-08 21:02:34 +03:00
function getElementValueOfCssProperty ( msg ) {
2013-05-08 01:38:21 +04:00
let command _id = msg . json . command _id ;
let propertyName = msg . json . propertyName ;
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-09-19 21:35:19 +04:00
sendResponse ( { value : curFrame . document . defaultView . getComputedStyle ( el , null ) . getPropertyValue ( propertyName ) } ,
2013-05-08 01:38:21 +04:00
command _id ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2013-05-08 01:38:21 +04:00
}
}
2012-10-20 00:59:15 +04:00
/ * *
2015-04-15 14:18:00 +03:00
* Get the size of the element .
*
* @ param { WebElement } id
* Web element reference .
*
* @ return { Object . < string , number > }
* The width / height dimensions of th element .
2012-10-20 00:59:15 +04:00
* /
2015-04-15 14:18:00 +03:00
function getElementSize ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
let clientRect = el . getBoundingClientRect ( ) ;
return { width : clientRect . width , height : clientRect . height } ;
2012-10-20 00:59:15 +04:00
}
2014-07-16 23:58:37 +04:00
/ * *
2015-04-15 14:18:00 +03:00
* Get the size of the element .
*
* @ param { WebElement } id
* Reference to web element .
*
* @ return { Object . < string , number > }
* The x , y , width , and height properties of the element .
* /
function getElementRect ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
let clientRect = el . getBoundingClientRect ( ) ;
return {
x : clientRect . x + curFrame . pageXOffset ,
y : clientRect . y + curFrame . pageYOffset ,
width : clientRect . width ,
height : clientRect . height
} ;
2014-07-16 23:58:37 +04:00
}
2012-04-11 04:28:08 +04:00
/ * *
2015-04-15 14:18:00 +03:00
* Check if element is enabled .
*
* @ param { WebElement } id
* Reference to web element .
*
* @ return { boolean }
* True if enabled , false otherwise .
2012-04-11 04:28:08 +04:00
* /
2015-04-15 14:18:00 +03:00
function isElementEnabled ( id ) {
let el = elementManager . getKnownElement ( id , curFrame ) ;
let enabled = utils . isElementEnabled ( el ) ;
2015-08-05 21:40:18 +03:00
checkEnabledAccessibility (
accessibility . getAccessibleObject ( el ) , el , enabled ) ;
2015-04-15 14:18:00 +03:00
return enabled ;
2012-04-11 04:28:08 +04:00
}
/ * *
* Check if element is selected
* /
function isElementSelected ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-04-11 04:28:08 +04:00
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2015-08-05 21:40:18 +03:00
let selected = utils . isElementSelected ( el ) ;
checkSelectedAccessibility ( accessibility . getAccessibleObject ( el ) , selected ) ;
sendResponse ( { value : selected } , command _id ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2012-04-11 04:28:08 +04:00
}
}
/ * *
* Send keys to element
* /
function sendKeysToElement ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2015-04-02 17:16:00 +03:00
let val = msg . json . value ;
2013-12-16 20:40:51 +04:00
2015-08-05 21:40:18 +03:00
try {
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
// Element should be actionable from the accessibility standpoint to be able
// to send keys to it.
checkActionableAccessibility ( accessibility . getAccessibleObject ( el , true ) ) ;
if ( el . type == "file" ) {
let p = val . join ( "" ) ;
fileInputElement = el ;
// In e10s, we can only construct File objects in the parent process,
// so pass the filename to driver.js, which in turn passes them back
// to this frame script in receiveFiles.
sendSyncMessage ( "Marionette:getFiles" ,
{ value : p , command _id : command _id } ) ;
} else {
utils . sendKeysToElement ( curFrame , el , val , sendOk , sendError , command _id ) ;
}
} catch ( e ) {
sendError ( e , command _id ) ;
2015-04-02 17:16:00 +03:00
}
2012-04-11 04:28:08 +04:00
}
2012-10-20 00:35:30 +04:00
/ * *
2014-01-22 23:04:04 +04:00
* Get the element ' s top left - hand corner point .
2012-10-20 00:35:30 +04:00
* /
2014-01-22 23:04:04 +04:00
function getElementLocation ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2014-01-22 23:04:04 +04:00
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2013-04-03 16:14:38 +04:00
let rect = el . getBoundingClientRect ( ) ;
2012-10-20 00:35:30 +04:00
let location = { } ;
2013-04-03 16:14:38 +04:00
location . x = rect . left ;
location . y = rect . top ;
2012-10-20 00:35:30 +04:00
2012-11-27 06:19:04 +04:00
sendResponse ( { value : location } , command _id ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2012-10-20 00:35:30 +04:00
}
}
2012-04-11 04:28:08 +04:00
/ * *
* Clear the text of an element
* /
function clearElement ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-04-11 04:28:08 +04:00
try {
2013-10-01 19:13:04 +04:00
let el = elementManager . getKnownElement ( msg . json . id , curFrame ) ;
2015-04-02 17:16:00 +03:00
if ( el . type == "file" ) {
el . value = null ;
} else {
utils . clearElement ( el ) ;
}
2012-11-27 06:19:04 +04:00
sendOk ( command _id ) ;
2015-04-02 17:16:00 +03:00
} catch ( e ) {
2015-04-08 21:02:34 +03:00
// Bug 964738: Newer atoms contain status codes which makes wrapping
// this in an error prototype that has a status property unnecessary
if ( e . name == "InvalidElementStateError" ) {
e = new InvalidElementStateError ( e . message ) ;
}
2015-04-15 14:18:00 +03:00
sendError ( e , command _id ) ;
2012-03-22 19:19:57 +04: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-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2013-09-19 21:35:19 +04:00
function checkLoad ( ) {
2012-11-14 22:35:44 +04:00
let errorRegex = /about:.+(error)|(blocked)\?/ ;
2013-09-19 21:35:19 +04:00
if ( curFrame . document . readyState == "complete" ) {
2012-11-27 06:19:04 +04:00
sendOk ( command _id ) ;
2012-11-14 22:35:44 +04:00
return ;
2015-04-15 14:18:00 +03:00
} else if ( curFrame . document . readyState == "interactive" &&
errorRegex . exec ( curFrame . document . baseURI ) ) {
sendError ( new UnknownError ( "Error loading page" ) , command _id ) ;
2012-11-14 22:35:44 +04:00
return ;
}
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
}
2012-03-22 19:19:57 +04:00
let foundFrame = null ;
2014-01-19 00:08:36 +04:00
let frames = [ ] ;
let parWindow = null ;
2013-09-19 21:35:19 +04:00
// Check of the curFrame reference is dead
2013-01-16 19:00:00 +04:00
try {
2014-01-19 00:08:36 +04:00
frames = curFrame . frames ;
2013-01-16 19:00:00 +04: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 21:35:19 +04:00
parWindow = curFrame . QueryInterface ( Ci . nsIInterfaceRequestor )
2013-01-16 19:00:00 +04:00
. getInterface ( Ci . nsIDOMWindowUtils ) . outerWindowID ;
} catch ( e ) {
// We probably have a dead compartment so accessing it is going to make Firefox
2014-01-21 20:40:20 +04:00
// very upset. Let's now try redirect everything to the top frame even if the
2013-01-16 19:00:00 +04:00
// user has given us a frame since search doesnt look up.
2013-10-01 19:13:04 +04:00
msg . json . id = null ;
2013-01-16 19:00:00 +04:00
msg . json . element = null ;
}
2014-01-19 00:08:36 +04:00
if ( ( msg . json . id === null || msg . json . id === undefined ) && ( msg . json . element == null ) ) {
2013-07-29 21:46:01 +04:00
// returning to root frame
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : null } ) ;
2013-09-19 21:35:19 +04:00
curFrame = content ;
2012-12-17 13:45:28 +04:00
if ( msg . json . focus == true ) {
2013-09-19 21:35:19 +04:00
curFrame . focus ( ) ;
2012-12-17 13:45:28 +04:00
}
2015-04-23 23:39:38 +03:00
2012-11-14 22:35:44 +04:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2012-03-22 19:19:57 +04:00
return ;
}
if ( msg . json . element != undefined ) {
if ( elementManager . seenItems [ msg . json . element ] != undefined ) {
2013-02-04 23:40:51 +04:00
let wantedFrame ;
try {
2014-01-19 00:08:36 +04:00
wantedFrame = elementManager . getKnownElement ( msg . json . element , curFrame ) ; //Frame Element
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , command _id ) ;
2013-02-04 23:40:51 +04:00
}
2014-01-19 00:08:36 +04:00
if ( frames . length > 0 ) {
2014-04-25 17:51:24 +04:00
for ( let i = 0 ; i < frames . length ; i ++ ) {
2014-01-19 00:08:36 +04:00
// use XPCNativeWrapper to compare elements; see bug 834266
if ( XPCNativeWrapper ( frames [ i ] . frameElement ) == XPCNativeWrapper ( wantedFrame ) ) {
curFrame = frames [ i ] . frameElement ;
2014-04-25 17:51:24 +04:00
foundFrame = i ;
}
}
2014-01-19 00:08:36 +04:00
}
2014-05-09 18:01:34 +04:00
if ( foundFrame === null ) {
2014-01-19 00:08:36 +04:00
// Either the frame has been removed or we have a OOP frame
// so lets just get all the iframes and do a quick loop before
// throwing in the towel
let iframes = curFrame . document . getElementsByTagName ( "iframe" ) ;
for ( var i = 0 ; i < iframes . length ; i ++ ) {
if ( XPCNativeWrapper ( iframes [ i ] ) == XPCNativeWrapper ( wantedFrame ) ) {
curFrame = iframes [ i ] ;
foundFrame = i ;
}
2014-04-25 17:51:24 +04:00
}
2014-01-19 00:08:36 +04:00
}
2014-01-19 00:08:36 +04:00
}
}
2014-05-09 18:01:34 +04:00
if ( foundFrame === null ) {
2014-01-19 00:08:36 +04:00
if ( typeof ( msg . json . id ) === 'number' ) {
2014-05-01 01:59:25 +04:00
try {
foundFrame = frames [ msg . json . id ] . frameElement ;
2014-05-09 18:01:34 +04:00
if ( foundFrame !== null ) {
curFrame = foundFrame ;
foundFrame = elementManager . addToKnownElements ( curFrame ) ;
}
else {
// If foundFrame is null at this point then we have the top level browsing
// context so should treat it accordingly.
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : null } ) ;
curFrame = content ;
if ( msg . json . focus == true ) {
curFrame . focus ( ) ;
}
2015-04-23 23:39:38 +03:00
2014-05-09 18:01:34 +04:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
return ;
}
2014-05-01 01:59:25 +04:00
} catch ( e ) {
// Since window.frames does not return OOP frames it will throw
// and we land up here. Let's not give up and check if there are
// iframes and switch to the indexed frame there
let iframes = curFrame . document . getElementsByTagName ( "iframe" ) ;
2014-10-28 12:41:00 +03:00
if ( msg . json . id >= 0 && msg . json . id < iframes . length ) {
curFrame = iframes [ msg . json . id ] ;
foundFrame = msg . json . id ;
}
2014-05-01 01:59:25 +04:00
}
2014-01-19 00:08:36 +04:00
}
}
2015-04-15 14:18:00 +03:00
2014-05-09 18:01:34 +04:00
if ( foundFrame === null ) {
2015-04-15 14:18:00 +03:00
sendError ( new NoSuchFrameError ( "Unable to locate frame: " + ( msg . json . id || msg . json . element ) ) , command _id ) ;
2014-01-19 00:08:36 +04:00
return true ;
2012-03-22 19:19:57 +04:00
}
2012-05-09 23:05:39 +04:00
2013-07-29 21:46:01 +04:00
// send a synchronous message to let the server update the currently active
// frame element (for getActiveFrame)
2013-09-19 21:35:19 +04:00
let frameValue = elementManager . wrapValue ( curFrame . wrappedJSObject ) [ 'ELEMENT' ] ;
2013-07-29 21:46:01 +04:00
sendSyncMessage ( "Marionette:switchedToFrame" , { frameValue : frameValue } ) ;
2015-03-20 00:12:58 +03:00
let rv = null ;
if ( curFrame . contentWindow === null ) {
// The frame we want to switch to is a remote/OOP frame;
// notify our parent to handle the switch
2013-09-19 21:35:19 +04:00
curFrame = content ;
2015-03-20 00:12:58 +03:00
rv = { win : parWindow , frame : foundFrame } ;
} else {
2013-09-19 21:35:19 +04:00
curFrame = curFrame . contentWindow ;
2015-03-20 00:12:58 +03:00
if ( msg . json . focus )
2013-09-19 21:35:19 +04:00
curFrame . focus ( ) ;
2012-11-14 22:35:44 +04:00
checkTimer . initWithCallback ( checkLoad , 100 , Ci . nsITimer . TYPE _ONE _SHOT ) ;
2012-09-29 03:16:22 +04:00
}
2015-03-20 00:12:58 +03:00
sendResponse ( { value : rv } , command _id ) ;
2012-03-22 19:19:57 +04:00
}
2012-11-22 19:53:44 +04:00
/ * *
* Add a cookie to the document
* /
function addCookie ( msg ) {
2014-11-11 21:15:02 +03:00
let cookie = msg . json . cookie ;
2012-11-22 19:53:44 +04:00
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 21:35:19 +04:00
var location = curFrame . document . location ;
2012-11-22 19:53:44 +04:00
cookie . domain = location . hostname ;
2015-04-15 14:18:00 +03:00
} else {
2013-09-19 21:35:19 +04:00
var currLocation = curFrame . location ;
2012-11-22 19:53:44 +04:00
var currDomain = currLocation . host ;
if ( currDomain . indexOf ( cookie . domain ) == - 1 ) {
2015-04-15 14:18:00 +03:00
sendError ( new InvalidCookieDomainError ( "You may only set cookies for the current domain" ) , msg . json . command _id ) ;
2012-11-22 19:53:44 +04:00
}
}
// 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 21:35:19 +04:00
var document = curFrame . document ;
2012-11-22 19:53:44 +04:00
if ( ! document || ! document . contentType . match ( /html/i ) ) {
2015-04-28 20:53:51 +03:00
sendError ( new UnableToSetCookieError ( "You may only set cookies on html documents" ) , msg . json . command _id ) ;
2012-11-22 19:53:44 +04:00
}
2014-11-11 21:15:02 +03:00
let added = sendSyncMessage ( "Marionette:addCookie" , { value : cookie } ) ;
if ( added [ 0 ] !== true ) {
2015-04-20 15:53:51 +03:00
sendError ( new UnableToSetCookieError ( ) , msg . json . command _id ) ;
2014-10-21 19:22:26 +04:00
return ;
}
2012-11-22 19:53:44 +04:00
sendOk ( msg . json . command _id ) ;
}
/ * *
2014-01-21 20:40:20 +04:00
* Get all cookies for the current domain .
2012-11-22 19:53:44 +04:00
* /
2014-01-21 20:40:20 +04:00
function getCookies ( msg ) {
2012-11-22 19:53:44 +04:00
var toReturn = [ ] ;
2013-09-19 21:35:19 +04:00
var cookies = getVisibleCookies ( curFrame . location ) ;
2014-11-11 21:15:02 +03:00
for ( let cookie of cookies ) {
2012-11-22 19:53:44 +04:00
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 ,
2015-07-16 01:51:51 +03:00
'httpOnly' : cookie . httpOnly ,
2012-11-22 19:53:44 +04:00
'expiry' : expires
} ) ;
}
sendResponse ( { value : toReturn } , msg . json . command _id ) ;
}
/ * *
* Delete a cookie by name
* /
function deleteCookie ( msg ) {
2014-11-11 21:15:02 +03:00
let toDelete = msg . json . name ;
let cookies = getVisibleCookies ( curFrame . location ) ;
for ( let cookie of cookies ) {
2012-11-22 19:53:44 +04:00
if ( cookie . name == toDelete ) {
2014-11-11 21:15:02 +03:00
let deleted = sendSyncMessage ( "Marionette:deleteCookie" , { value : cookie } ) ;
if ( deleted [ 0 ] !== true ) {
2015-04-15 14:18:00 +03:00
sendError ( new UnknownError ( "Could not delete cookie: " + msg . json . name ) , msg . json . command _id ) ;
2014-11-11 21:15:02 +03:00
return ;
}
2012-11-22 19:53:44 +04:00
}
}
sendOk ( msg . json . command _id ) ;
}
/ * *
* Delete all the visibile cookies on a page
* /
function deleteAllCookies ( msg ) {
2013-09-19 21:35:19 +04:00
let cookies = getVisibleCookies ( curFrame . location ) ;
2014-11-11 21:15:02 +03:00
for ( let cookie of cookies ) {
let deleted = sendSyncMessage ( "Marionette:deleteCookie" , { value : cookie } ) ;
if ( ! deleted [ 0 ] ) {
2015-04-15 14:18:00 +03:00
sendError ( new UnknownError ( "Could not delete cookie: " + JSON . stringify ( cookie ) ) , msg . json . command _id ) ;
2014-11-11 21:15:02 +03:00
return ;
}
2012-11-22 19:53:44 +04:00
}
sendOk ( msg . json . command _id ) ;
}
/ * *
* Get all the visible cookies from a location
* /
function getVisibleCookies ( location ) {
2014-11-11 21:15:02 +03:00
let currentPath = location . pathname || '/' ;
let result = sendSyncMessage ( "Marionette:getVisibleCookies" ,
{ value : [ currentPath , location . hostname ] } ) ;
return result [ 0 ] ;
2012-11-22 19:53:44 +04:00
}
2012-03-22 19:19:57 +04:00
2012-11-27 06:19:04 +04:00
function getAppCacheStatus ( msg ) {
2013-09-19 21:35:19 +04:00
sendResponse ( { value : curFrame . applicationCache . status } ,
2012-11-27 06:19:04 +04:00
msg . json . command _id ) ;
2014-01-21 20:40:20 +04:00
}
2012-08-24 02:07:16 +04:00
2012-05-24 04:23:36 +04:00
// emulator callbacks
2012-05-19 00:30:43 +04:00
let _emu _cb _id = 0 ;
let _emu _cbs = { } ;
2012-05-24 04:23:36 +04:00
2012-05-19 00:30:43 +04:00
function runEmulatorCmd ( cmd , callback ) {
2012-05-25 21:37:21 +04:00
if ( callback ) {
_emu _cbs [ _emu _cb _id ] = callback ;
2012-05-19 00:30:43 +04:00
}
sendAsyncMessage ( "Marionette:runEmulatorCmd" , { emulator _cmd : cmd , id : _emu _cb _id } ) ;
_emu _cb _id += 1 ;
}
2014-03-20 09:01:25 +04:00
function runEmulatorShell ( args , callback ) {
if ( callback ) {
_emu _cbs [ _emu _cb _id ] = callback ;
}
sendAsyncMessage ( "Marionette:runEmulatorShell" , { emulator _shell : args , id : _emu _cb _id } ) ;
_emu _cb _id += 1 ;
}
2012-05-19 00:30:43 +04:00
function emulatorCmdResult ( msg ) {
let message = msg . json ;
2015-04-23 23:39:38 +03:00
if ( ! sandboxes [ sandboxName ] ) {
2012-05-24 04:23:36 +04:00
return ;
}
2012-05-19 00:30:43 +04:00
let cb = _emu _cbs [ message . id ] ;
delete _emu _cbs [ message . id ] ;
2012-05-25 21:37:21 +04:00
if ( ! cb ) {
return ;
}
2012-05-19 00:30:43 +04:00
try {
cb ( message . result ) ;
2015-04-15 14:18:00 +03:00
} catch ( e ) {
sendError ( e , - 1 ) ;
2012-05-19 00:30:43 +04:00
return ;
}
}
2012-06-04 21:50:06 +04:00
function importScript ( msg ) {
2012-11-27 06:19:04 +04:00
let command _id = msg . json . command _id ;
2012-06-04 21:50:06 +04:00
let file ;
if ( importedScripts . exists ( ) ) {
2012-11-27 06:19:04 +04:00
file = FileUtils . openFileOutputStream ( importedScripts ,
FileUtils . MODE _APPEND | FileUtils . MODE _WRONLY ) ;
2012-06-04 21:50:06 +04:00
}
else {
2012-10-24 19:36:43 +04:00
//Note: The permission bits here don't actually get set (bug 804563)
2012-11-27 06:19:04 +04: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 19:36:43 +04:00
importedScripts . permissions = parseInt ( "0666" , 8 ) ; //actually set permissions
2012-06-04 21:50:06 +04:00
}
file . write ( msg . json . script , msg . json . script . length ) ;
file . close ( ) ;
2012-11-27 06:19:04 +04:00
sendOk ( command _id ) ;
2012-06-04 21:50:06 +04:00
}
2012-12-01 03:25:26 +04:00
/ * *
2014-01-24 17:45:58 +04: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-12-01 03:25:26 +04:00
* /
2014-01-24 17:45:58 +04:00
function takeScreenshot ( msg ) {
2012-12-01 03:25:26 +04:00
let node = null ;
2013-10-01 19:13:04 +04:00
if ( msg . json . id ) {
2012-12-01 03:25:26 +04:00
try {
2013-10-01 19:13:04 +04:00
node = elementManager . getKnownElement ( msg . json . id , curFrame )
2012-12-01 03:25:26 +04:00
}
catch ( e ) {
2013-02-12 22:10:03 +04:00
sendResponse ( e . message , e . code , e . stack , msg . json . command _id ) ;
2012-12-01 03:25:26 +04:00
return ;
}
}
else {
2014-01-24 17:45:58 +04:00
node = curFrame ;
2012-12-01 03:25:26 +04:00
}
let highlights = msg . json . highlights ;
2013-09-19 21:35:19 +04:00
var document = curFrame . document ;
2013-09-04 12:05:43 +04:00
var rect , win , width , height , left , top ;
2012-12-01 03:25:26 +04:00
// node can be either a window or an arbitrary DOM node
2013-09-19 21:35:19 +04:00
if ( node == curFrame ) {
2012-12-01 03:25:26 +04:00
// node is a window
win = node ;
2015-02-02 15:57:00 +03:00
if ( msg . json . full ) {
// the full window
width = document . body . scrollWidth ;
height = document . body . scrollHeight ;
top = 0 ;
left = 0 ;
}
else {
// only the viewport
width = document . documentElement . clientWidth ;
height = document . documentElement . clientHeight ;
left = curFrame . pageXOffset ;
top = curFrame . pageYOffset ;
}
2012-12-01 03:25:26 +04:00
}
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 17:45:58 +04:00
var canvas = document . createElementNS ( "http://www.w3.org/1999/xhtml" ,
"canvas" ) ;
2012-12-01 03:25:26 +04: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 17:45:58 +04:00
ctx . drawWindow ( win , left , top , width , height , "rgb(255,255,255)" ) ;
2012-12-01 03:25:26 +04:00
2014-01-24 17:45:58 +04:00
// This section is for drawing a red rectangle around each element
// passed in via the highlights array
2012-12-01 03:25:26 +04:00
if ( highlights ) {
ctx . lineWidth = "2" ;
ctx . strokeStyle = "red" ;
ctx . save ( ) ;
for ( var i = 0 ; i < highlights . length ; ++ i ) {
2014-01-24 17:45:58 +04:00
var elem = elementManager . getKnownElement ( highlights [ i ] , curFrame ) ;
2012-12-01 03:25:26 +04:00
rect = elem . getBoundingClientRect ( ) ;
2013-09-04 12:05:43 +04:00
var offsetY = - top ;
var offsetX = - left ;
2012-12-01 03:25:26 +04:00
// Draw the rectangle
2014-01-24 17:45:58 +04:00
ctx . strokeRect ( rect . left + offsetX ,
rect . top + offsetY ,
rect . width ,
rect . height ) ;
2012-12-01 03:25:26 +04:00
}
}
2014-01-24 17:45:58 +04: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-12-01 03:25:26 +04:00
}
2014-01-24 17:45:58 +04:00
// Call register self when we get loaded
2012-03-22 19:19:57 +04:00
registerSelf ( ) ;