2013-09-19 21:35:19 +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/. */
2015-09-15 21:19:45 +03:00
var { classes : Cc , interfaces : Ci , utils : Cu } = Components ;
2015-03-20 00:12:58 +03:00
this . EXPORTED _SYMBOLS = [ "FrameManager" ] ;
2013-09-19 21:35:19 +04:00
2015-09-15 21:19:45 +03:00
var FRAME _SCRIPT = "chrome://marionette/content/listener.js" ;
2015-03-20 00:12:58 +03:00
2013-09-19 21:35:19 +04:00
Cu . import ( "resource://gre/modules/Services.jsm" ) ;
Cu . import ( "resource://gre/modules/XPCOMUtils.jsm" ) ;
2015-09-15 21:19:45 +03:00
var loader = Cc [ "@mozilla.org/moz/jssubscript-loader;1" ]
2014-09-28 18:43:32 +04:00
. getService ( Ci . mozIJSSubScriptLoader ) ;
2013-09-19 21:35:19 +04:00
//list of OOP frames that has the frame script loaded
2015-09-15 21:19:45 +03:00
var remoteFrames = [ ] ;
2013-09-19 21:35:19 +04:00
/ * *
* An object representing a frame that Marionette has loaded a
* frame script in .
* /
function MarionetteRemoteFrame ( windowId , frameId ) {
this . windowId = windowId ; //outerWindowId relative to main process
2014-05-08 01:01:07 +04:00
this . frameId = frameId ; //actual frame relative to windowId's frames list
2013-09-19 21:35:19 +04:00
this . targetFrameId = this . frameId ; //assigned FrameId, used for messaging
} ;
/ * *
* The FrameManager will maintain the list of Out Of Process ( OOP ) frames and will handle
* frame switching between them .
* It handles explicit frame switching ( switchToFrame ) , and implicit frame switching , which
* occurs when a modal dialog is triggered in B2G .
*
* /
this . FrameManager = function FrameManager ( server ) {
//messageManager maintains the messageManager for the current process' chrome frame or the global message manager
this . currentRemoteFrame = null ; //holds a member of remoteFrames (for an OOP frame) or null (for the main process)
this . previousRemoteFrame = null ; //frame we'll need to restore once interrupt is gone
this . handledModal = false ; //set to true when we have been interrupted by a modal
this . server = server ; // a reference to the marionette server
} ;
FrameManager . prototype = {
QueryInterface : XPCOMUtils . generateQI ( [ Ci . nsIMessageListener ,
Ci . nsISupportsWeakReference ] ) ,
/ * *
* Receives all messages from content messageManager
* /
receiveMessage : function FM _receiveMessage ( message ) {
switch ( message . name ) {
case "MarionetteFrame:getInterruptedState" :
// This will return true if the calling frame was interrupted by a modal dialog
if ( this . previousRemoteFrame ) {
let interruptedFrame = Services . wm . getOuterWindowWithId ( this . previousRemoteFrame . windowId ) ; //get the frame window of the interrupted frame
if ( this . previousRemoteFrame . frameId != null ) {
interruptedFrame = interruptedFrame . document . getElementsByTagName ( "iframe" ) [ this . previousRemoteFrame . frameId ] ; //find the OOP frame
}
//check if the interrupted frame is the same as the calling frame
if ( interruptedFrame . src == message . target . src ) {
return { value : this . handledModal } ;
}
}
else if ( this . currentRemoteFrame == null ) {
// we get here if previousRemoteFrame and currentRemoteFrame are null, ie: if we're in a non-OOP process, or we haven't switched into an OOP frame, in which case, handledModal can't be set to true.
return { value : this . handledModal } ;
}
return { value : false } ;
case "MarionetteFrame:handleModal" :
/ *
* handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt .
* /
// If previousRemoteFrame was set, that means we switched into a remote frame.
// If this is the case, then we want to switch back into the system frame.
// If it isn't the case, then we're in a non-OOP environment, so we don't need to handle remote frames
let isLocal = true ;
if ( this . currentRemoteFrame != null ) {
isLocal = false ;
this . removeMessageManagerListeners ( this . currentRemoteFrame . messageManager . get ( ) ) ;
//store the previous frame so we can switch back to it when the modal is dismissed
this . previousRemoteFrame = this . currentRemoteFrame ;
//by setting currentRemoteFrame to null, it signifies we're in the main process
this . currentRemoteFrame = null ;
this . server . messageManager = Cc [ "@mozilla.org/globalmessagemanager;1" ]
. getService ( Ci . nsIMessageBroadcaster ) ;
}
this . handledModal = true ;
this . server . sendOk ( this . server . command _id ) ;
return { value : isLocal } ;
2014-04-10 18:26:08 +04:00
case "MarionetteFrame:getCurrentFrameId" :
if ( this . currentRemoteFrame != null ) {
return this . currentRemoteFrame . frameId ;
}
2013-09-19 21:35:19 +04:00
}
} ,
2015-03-20 00:12:58 +03:00
getOopFrame : function FM _getOopFrame ( winId , frameId ) {
// get original frame window
let outerWin = Services . wm . getOuterWindowWithId ( winId ) ;
// find the OOP frame
let f = outerWin . document . getElementsByTagName ( "iframe" ) [ frameId ] ;
return f ;
} ,
getFrameMM : function FM _getFrameMM ( winId , frameId ) {
let oopFrame = this . getOopFrame ( winId , frameId ) ;
let mm = oopFrame . QueryInterface ( Ci . nsIFrameLoaderOwner )
. frameLoader . messageManager ;
return mm ;
} ,
/ * *
* Switch to OOP frame . We ' re handling this here
* so we can maintain a list of remote frames .
* /
switchToFrame : function FM _switchToFrame ( winId , frameId ) {
let oopFrame = this . getOopFrame ( winId , frameId ) ;
let mm = this . getFrameMM ( winId , frameId ) ;
2013-09-19 21:35:19 +04:00
2015-03-20 00:12:58 +03:00
// See if this frame already has our frame script loaded in it;
// if so, just wake it up.
2013-09-19 21:35:19 +04:00
for ( let i = 0 ; i < remoteFrames . length ; i ++ ) {
let frame = remoteFrames [ i ] ;
let frameMessageManager = frame . messageManager . get ( ) ;
try {
frameMessageManager . sendAsyncMessage ( "aliveCheck" , { } ) ;
2015-03-20 00:12:58 +03:00
} catch ( e ) {
2013-09-19 21:35:19 +04:00
if ( e . result == Components . results . NS _ERROR _NOT _INITIALIZED ) {
2015-05-18 07:04:26 +03:00
remoteFrames . splice ( i -- , 1 ) ;
2013-09-19 21:35:19 +04:00
continue ;
}
}
if ( frameMessageManager == mm ) {
this . currentRemoteFrame = frame ;
this . addMessageManagerListeners ( mm ) ;
2015-03-20 00:12:58 +03:00
mm . sendAsyncMessage ( "Marionette:restart" ) ;
2014-11-28 19:21:43 +03:00
return oopFrame . id ;
2013-09-19 21:35:19 +04:00
}
}
2014-01-19 00:08:36 +04:00
// If we get here, then we need to load the frame script in this frame,
2015-03-20 00:12:58 +03:00
// and set the frame's ChromeMessageSender as the active message manager
// the server will listen to.
2013-09-19 21:35:19 +04:00
this . addMessageManagerListeners ( mm ) ;
2015-03-20 00:12:58 +03:00
let aFrame = new MarionetteRemoteFrame ( winId , frameId ) ;
2013-09-19 21:35:19 +04:00
aFrame . messageManager = Cu . getWeakReference ( mm ) ;
remoteFrames . push ( aFrame ) ;
this . currentRemoteFrame = aFrame ;
2014-09-28 18:43:32 +04:00
2014-10-21 19:22:26 +04:00
mm . loadFrameScript ( FRAME _SCRIPT , true , true ) ;
2014-11-28 19:21:43 +03:00
return oopFrame . id ;
2013-09-19 21:35:19 +04:00
} ,
/ *
* This function handles switching back to the frame that was interrupted by the modal dialog .
* This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
* /
switchToModalOrigin : function FM _switchToModalOrigin ( ) {
//only handle this if we indeed switched out of the modal's originating frame
if ( this . previousRemoteFrame != null ) {
this . currentRemoteFrame = this . previousRemoteFrame ;
this . addMessageManagerListeners ( this . currentRemoteFrame . messageManager . get ( ) ) ;
}
this . handledModal = false ;
} ,
/ * *
2015-03-20 00:12:58 +03:00
* Adds message listeners to the server ,
* listening for messages from content frame scripts .
* It also adds a MarionetteFrame : getInterruptedState
* message listener to the FrameManager ,
* so the frame manager ' s state can be checked by the frame .
2013-09-19 21:35:19 +04:00
*
2015-03-20 00:12:58 +03:00
* @ param { nsIMessageListenerManager } mm
* The message manager object , typically
* ChromeMessageBroadcaster or ChromeMessageSender .
2013-09-19 21:35:19 +04:00
* /
2015-03-20 00:12:58 +03:00
addMessageManagerListeners : function FM _addMessageManagerListeners ( mm ) {
2015-10-30 09:29:14 +03:00
mm . addWeakMessageListener ( "Marionette:ok" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:done" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:error" , this . server ) ;
2015-03-20 00:12:58 +03:00
mm . addWeakMessageListener ( "Marionette:emitTouchEvent" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:log" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:runEmulatorCmd" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:runEmulatorShell" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:shareData" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:switchToModalOrigin" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:switchedToFrame" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:addCookie" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:getVisibleCookies" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:deleteCookie" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:register" , this . server ) ;
mm . addWeakMessageListener ( "Marionette:listenersAttached" , this . server ) ;
2015-05-19 02:36:15 +03:00
mm . addWeakMessageListener ( "Marionette:getFiles" , this . server ) ;
2015-03-20 00:12:58 +03:00
mm . addWeakMessageListener ( "MarionetteFrame:handleModal" , this ) ;
mm . addWeakMessageListener ( "MarionetteFrame:getCurrentFrameId" , this ) ;
mm . addWeakMessageListener ( "MarionetteFrame:getInterruptedState" , this ) ;
2013-09-19 21:35:19 +04:00
} ,
/ * *
* Removes listeners for messages from content frame scripts .
2015-03-20 00:12:58 +03:00
* We do not remove the MarionetteFrame : getInterruptedState
* or the Marionette : switchToModalOrigin message listener ,
* because we want to allow all known frames to contact the frame manager
* so that it can check if it was interrupted , and if so ,
* it will call switchToModalOrigin when its process gets resumed .
2013-09-19 21:35:19 +04:00
*
2015-03-20 00:12:58 +03:00
* @ param { nsIMessageListenerManager } mm
* The message manager object , typically
* ChromeMessageBroadcaster or ChromeMessageSender .
2013-09-19 21:35:19 +04:00
* /
2015-03-20 00:12:58 +03:00
removeMessageManagerListeners : function FM _removeMessageManagerListeners ( mm ) {
2015-10-30 09:29:14 +03:00
mm . removeWeakMessageListener ( "Marionette:ok" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:done" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:error" , this . server ) ;
2015-03-20 00:12:58 +03:00
mm . removeWeakMessageListener ( "Marionette:log" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:shareData" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:runEmulatorCmd" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:runEmulatorShell" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:switchedToFrame" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:addCookie" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:getVisibleCookies" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:deleteCookie" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:listenersAttached" , this . server ) ;
mm . removeWeakMessageListener ( "Marionette:register" , this . server ) ;
2015-05-19 02:36:15 +03:00
mm . removeWeakMessageListener ( "Marionette:getFiles" , this . server ) ;
2015-03-20 00:12:58 +03:00
mm . removeWeakMessageListener ( "MarionetteFrame:handleModal" , this ) ;
mm . removeWeakMessageListener ( "MarionetteFrame:getCurrentFrameId" , this ) ;
}
2013-09-19 21:35:19 +04:00
} ;