2013-09-19 10:35:19 -07: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/. */
this . EXPORTED _SYMBOLS = [
"FrameManager"
] ;
let FRAME _SCRIPT = "chrome://marionette/content/marionette-listener.js" ;
Cu . import ( "resource://gre/modules/Services.jsm" ) ;
Cu . import ( "resource://gre/modules/XPCOMUtils.jsm" ) ;
2013-08-26 11:55:58 -07:00
Cu . import ( "resource://gre/modules/Log.jsm" ) ;
let logger = Log . repository . getLogger ( "Marionette" ) ;
2013-09-19 10:35:19 -07:00
//list of OOP frames that has the frame script loaded
let remoteFrames = [ ] ;
/ * *
* 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-07 14:01:07 -07:00
this . frameId = frameId ; //actual frame relative to windowId's frames list
2013-09-19 10:35:19 -07: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 07:26:08 -07:00
case "MarionetteFrame:getCurrentFrameId" :
if ( this . currentRemoteFrame != null ) {
return this . currentRemoteFrame . frameId ;
}
2013-09-19 10:35:19 -07:00
}
} ,
2014-01-18 12:08:36 -08:00
//This is just 'switch to OOP frame'. We're handling this here so we can maintain a list of remoteFrames.
2013-09-19 10:35:19 -07:00
switchToFrame : function FM _switchToFrame ( message ) {
// Switch to a remote frame.
let frameWindow = Services . wm . getOuterWindowWithId ( message . json . win ) ; //get the original frame window
let oopFrame = frameWindow . document . getElementsByTagName ( "iframe" ) [ message . json . frame ] ; //find the OOP frame
let mm = oopFrame . QueryInterface ( Ci . nsIFrameLoaderOwner ) . frameLoader . messageManager ; //get the OOP frame's mm
// See if this frame already has our frame script loaded in it; if so,
// just wake it up.
for ( let i = 0 ; i < remoteFrames . length ; i ++ ) {
let frame = remoteFrames [ i ] ;
let frameMessageManager = frame . messageManager . get ( ) ;
logger . info ( "trying remote frame " + i ) ;
try {
frameMessageManager . sendAsyncMessage ( "aliveCheck" , { } ) ;
}
catch ( e ) {
if ( e . result == Components . results . NS _ERROR _NOT _INITIALIZED ) {
logger . info ( "deleting frame" ) ;
remoteFrames . splice ( i , 1 ) ;
continue ;
}
}
if ( frameMessageManager == mm ) {
this . currentRemoteFrame = frame ;
this . addMessageManagerListeners ( mm ) ;
mm . sendAsyncMessage ( "Marionette:restart" , { } ) ;
return ;
}
}
2014-01-18 12:08:36 -08:00
// If we get here, then we need to load the frame script in this frame,
2013-09-19 10:35:19 -07:00
// and set the frame's ChromeMessageSender as the active message manager the server will listen to
this . addMessageManagerListeners ( mm ) ;
logger . info ( "frame-manager load script: " + mm . toString ( ) ) ;
2013-11-23 21:32:39 -08:00
mm . loadFrameScript ( FRAME _SCRIPT , true , true ) ;
2013-09-19 10:35:19 -07:00
let aFrame = new MarionetteRemoteFrame ( message . json . win , message . json . frame ) ;
aFrame . messageManager = Cu . getWeakReference ( mm ) ;
remoteFrames . push ( aFrame ) ;
this . currentRemoteFrame = aFrame ;
} ,
/ *
* This function handles switching back to the frame that was interrupted by the modal dialog .
* This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
* /
switchToModalOrigin : function FM _switchToModalOrigin ( ) {
//only handle this if we indeed switched out of the modal's originating frame
if ( this . previousRemoteFrame != null ) {
this . currentRemoteFrame = this . previousRemoteFrame ;
this . addMessageManagerListeners ( this . currentRemoteFrame . messageManager . get ( ) ) ;
}
this . handledModal = false ;
} ,
/ * *
* Adds message listeners to the server , listening for messages from content frame scripts .
* It also adds a "MarionetteFrame:getInterruptedState" message listener to the FrameManager ,
* so the frame manager ' s state can be checked by the frame
*
* @ param object messageManager
* The messageManager object ( ChromeMessageBroadcaster or ChromeMessageSender )
* to which the listeners should be added .
* /
addMessageManagerListeners : function MDA _addMessageManagerListeners ( messageManager ) {
messageManager . addWeakMessageListener ( "Marionette:ok" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:done" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:error" , this . server ) ;
2014-04-10 07:26:08 -07:00
messageManager . addWeakMessageListener ( "Marionette:emitTouchEvent" , this . server ) ;
2013-09-19 10:35:19 -07:00
messageManager . addWeakMessageListener ( "Marionette:log" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:register" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:runEmulatorCmd" , this . server ) ;
2014-03-19 22:01:25 -07:00
messageManager . addWeakMessageListener ( "Marionette:runEmulatorShell" , this . server ) ;
2014-04-10 07:26:08 -07:00
messageManager . addWeakMessageListener ( "Marionette:shareData" , this . server ) ;
2013-09-19 10:35:19 -07:00
messageManager . addWeakMessageListener ( "Marionette:switchToModalOrigin" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:switchToFrame" , this . server ) ;
messageManager . addWeakMessageListener ( "Marionette:switchedToFrame" , this . server ) ;
messageManager . addWeakMessageListener ( "MarionetteFrame:handleModal" , this ) ;
2014-04-10 07:26:08 -07:00
messageManager . addWeakMessageListener ( "MarionetteFrame:getCurrentFrameId" , this ) ;
2013-09-19 10:35:19 -07:00
messageManager . addWeakMessageListener ( "MarionetteFrame:getInterruptedState" , this ) ;
} ,
/ * *
* Removes listeners for messages from content frame scripts .
* We do not remove the "MarionetteFrame:getInterruptedState" or the
* "Marioentte:switchToModalOrigin" message listener ,
* because we want to allow all known frames to contact the frame manager so that
* it can check if it was interrupted , and if so , it will call switchToModalOrigin
* when its process gets resumed .
*
* @ param object messageManager
* The messageManager object ( ChromeMessageBroadcaster or ChromeMessageSender )
* from which the listeners should be removed .
* /
removeMessageManagerListeners : function MDA _removeMessageManagerListeners ( messageManager ) {
messageManager . removeWeakMessageListener ( "Marionette:ok" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:done" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:error" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:log" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:shareData" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:register" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:runEmulatorCmd" , this . server ) ;
2014-03-19 22:01:25 -07:00
messageManager . removeWeakMessageListener ( "Marionette:runEmulatorShell" , this . server ) ;
2013-09-19 10:35:19 -07:00
messageManager . removeWeakMessageListener ( "Marionette:switchToFrame" , this . server ) ;
messageManager . removeWeakMessageListener ( "Marionette:switchedToFrame" , this . server ) ;
messageManager . removeWeakMessageListener ( "MarionetteFrame:handleModal" , this ) ;
2014-04-10 07:26:08 -07:00
messageManager . removeWeakMessageListener ( "MarionetteFrame:getCurrentFrameId" , this ) ;
2013-09-19 10:35:19 -07:00
} ,
} ;