testing/marionette/marionette-frame-manager.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 this.EXPORTED_SYMBOLS = [
     6   "FrameManager"
     7 ];
     9 let FRAME_SCRIPT = "chrome://marionette/content/marionette-listener.js";
    10 Cu.import("resource://gre/modules/Services.jsm");
    11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    13 Cu.import("resource://gre/modules/Log.jsm");
    14 let logger = Log.repository.getLogger("Marionette");
    16 //list of OOP frames that has the frame script loaded
    17 let remoteFrames = [];
    19 /**
    20  * An object representing a frame that Marionette has loaded a
    21  * frame script in.
    22  */
    23 function MarionetteRemoteFrame(windowId, frameId) {
    24   this.windowId = windowId; //outerWindowId relative to main process
    25   this.frameId = frameId ? frameId : null; //actual frame relative to windowId's frames list
    26   this.targetFrameId = this.frameId; //assigned FrameId, used for messaging
    27 };
    29 /**
    30  * The FrameManager will maintain the list of Out Of Process (OOP) frames and will handle
    31  * frame switching between them.
    32  * It handles explicit frame switching (switchToFrame), and implicit frame switching, which
    33  * occurs when a modal dialog is triggered in B2G.
    34  *
    35  */
    36 this.FrameManager = function FrameManager(server) {
    37   //messageManager maintains the messageManager for the current process' chrome frame or the global message manager
    38   this.currentRemoteFrame = null; //holds a member of remoteFrames (for an OOP frame) or null (for the main process)
    39   this.previousRemoteFrame = null; //frame we'll need to restore once interrupt is gone
    40   this.handledModal = false; //set to true when we have been interrupted by a modal
    41   this.server = server; // a reference to the marionette server
    42 };
    44 FrameManager.prototype = {
    45   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
    46                                          Ci.nsISupportsWeakReference]),
    48   /**
    49    * Receives all messages from content messageManager
    50    */
    51   receiveMessage: function FM_receiveMessage(message) {
    52     switch (message.name) {
    53       case "MarionetteFrame:getInterruptedState":
    54         // This will return true if the calling frame was interrupted by a modal dialog
    55         if (this.previousRemoteFrame) {
    56           let interruptedFrame = Services.wm.getOuterWindowWithId(this.previousRemoteFrame.windowId);//get the frame window of the interrupted frame
    57           if (this.previousRemoteFrame.frameId != null) {
    58             interruptedFrame = interruptedFrame.document.getElementsByTagName("iframe")[this.previousRemoteFrame.frameId]; //find the OOP frame
    59           }
    60           //check if the interrupted frame is the same as the calling frame
    61           if (interruptedFrame.src == message.target.src) {
    62             return {value: this.handledModal};
    63           }
    64         }
    65         else if (this.currentRemoteFrame == null) {
    66           // 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.
    67           return {value: this.handledModal};
    68         }
    69         return {value: false};
    70       case "MarionetteFrame:handleModal":
    71         /*
    72          * handleModal is called when we need to switch frames to the main process due to a modal dialog interrupt.
    73          */
    74         // If previousRemoteFrame was set, that means we switched into a remote frame.
    75         // If this is the case, then we want to switch back into the system frame.
    76         // If it isn't the case, then we're in a non-OOP environment, so we don't need to handle remote frames
    77         let isLocal = true;
    78         if (this.currentRemoteFrame != null) {
    79           isLocal = false;
    80           this.removeMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
    81           //store the previous frame so we can switch back to it when the modal is dismissed
    82           this.previousRemoteFrame = this.currentRemoteFrame;
    83           //by setting currentRemoteFrame to null, it signifies we're in the main process
    84           this.currentRemoteFrame = null;
    85           this.server.messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
    86                                        .getService(Ci.nsIMessageBroadcaster);
    87         }
    88         this.handledModal = true;
    89         this.server.sendOk(this.server.command_id);
    90         return {value: isLocal};
    91       case "MarionetteFrame:getCurrentFrameId":
    92         if (this.currentRemoteFrame != null) {
    93           return this.currentRemoteFrame.frameId;
    94         }
    95     }
    96   },
    98   //This is just 'switch to OOP frame'. We're handling this here so we can maintain a list of remoteFrames. 
    99   switchToFrame: function FM_switchToFrame(message) {
   100     // Switch to a remote frame.
   101     let frameWindow = Services.wm.getOuterWindowWithId(message.json.win); //get the original frame window
   102     let oopFrame = frameWindow.document.getElementsByTagName("iframe")[message.json.frame]; //find the OOP frame
   103     let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager; //get the OOP frame's mm
   105     // See if this frame already has our frame script loaded in it; if so,
   106     // just wake it up.
   107     for (let i = 0; i < remoteFrames.length; i++) {
   108       let frame = remoteFrames[i];
   109       let frameMessageManager = frame.messageManager.get();
   110       logger.info("trying remote frame " + i);
   111       try {
   112         frameMessageManager.sendAsyncMessage("aliveCheck", {});
   113       }
   114       catch(e) {
   115         if (e.result ==  Components.results.NS_ERROR_NOT_INITIALIZED) {
   116           logger.info("deleting frame");
   117           remoteFrames.splice(i, 1);
   118           continue;
   119         }
   120       }
   121       if (frameMessageManager == mm) {
   122         this.currentRemoteFrame = frame;
   123         this.addMessageManagerListeners(mm);
   124         mm.sendAsyncMessage("Marionette:restart", {});
   125         return;
   126       }
   127     }
   129     // If we get here, then we need to load the frame script in this frame, 
   130     // and set the frame's ChromeMessageSender as the active message manager the server will listen to
   131     this.addMessageManagerListeners(mm);
   132     logger.info("frame-manager load script: " + mm.toString());
   133     mm.loadFrameScript(FRAME_SCRIPT, true, true);
   134     let aFrame = new MarionetteRemoteFrame(message.json.win, message.json.frame);
   135     aFrame.messageManager = Cu.getWeakReference(mm);
   136     remoteFrames.push(aFrame);
   137     this.currentRemoteFrame = aFrame;
   138   },
   140   /*
   141    * This function handles switching back to the frame that was interrupted by the modal dialog.
   142    * This function gets called by the interrupted frame once the dialog is dismissed and the frame resumes its process
   143    */
   144   switchToModalOrigin: function FM_switchToModalOrigin() {
   145     //only handle this if we indeed switched out of the modal's originating frame
   146     if (this.previousRemoteFrame != null) {
   147       this.currentRemoteFrame = this.previousRemoteFrame;
   148       this.addMessageManagerListeners(this.currentRemoteFrame.messageManager.get());
   149     }
   150     this.handledModal = false;
   151   },
   153   /**
   154    * Adds message listeners to the server, listening for messages from content frame scripts.
   155    * It also adds a "MarionetteFrame:getInterruptedState" message listener to the FrameManager,
   156    * so the frame manager's state can be checked by the frame
   157    *
   158    * @param object messageManager
   159    *        The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
   160    *        to which the listeners should be added.
   161    */
   162   addMessageManagerListeners: function MDA_addMessageManagerListeners(messageManager) {
   163     messageManager.addWeakMessageListener("Marionette:ok", this.server);
   164     messageManager.addWeakMessageListener("Marionette:done", this.server);
   165     messageManager.addWeakMessageListener("Marionette:error", this.server);
   166     messageManager.addWeakMessageListener("Marionette:emitTouchEvent", this.server);
   167     messageManager.addWeakMessageListener("Marionette:log", this.server);
   168     messageManager.addWeakMessageListener("Marionette:register", this.server);
   169     messageManager.addWeakMessageListener("Marionette:runEmulatorCmd", this.server);
   170     messageManager.addWeakMessageListener("Marionette:runEmulatorShell", this.server);
   171     messageManager.addWeakMessageListener("Marionette:shareData", this.server);
   172     messageManager.addWeakMessageListener("Marionette:switchToModalOrigin", this.server);
   173     messageManager.addWeakMessageListener("Marionette:switchToFrame", this.server);
   174     messageManager.addWeakMessageListener("Marionette:switchedToFrame", this.server);
   175     messageManager.addWeakMessageListener("MarionetteFrame:handleModal", this);
   176     messageManager.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
   177     messageManager.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
   178   },
   180   /**
   181    * Removes listeners for messages from content frame scripts.
   182    * We do not remove the "MarionetteFrame:getInterruptedState" or the
   183    * "Marioentte:switchToModalOrigin" message listener,
   184    * because we want to allow all known frames to contact the frame manager so that
   185    * it can check if it was interrupted, and if so, it will call switchToModalOrigin
   186    * when its process gets resumed.
   187    *
   188    * @param object messageManager
   189    *        The messageManager object (ChromeMessageBroadcaster or ChromeMessageSender)
   190    *        from which the listeners should be removed.
   191    */
   192   removeMessageManagerListeners: function MDA_removeMessageManagerListeners(messageManager) {
   193     messageManager.removeWeakMessageListener("Marionette:ok", this.server);
   194     messageManager.removeWeakMessageListener("Marionette:done", this.server);
   195     messageManager.removeWeakMessageListener("Marionette:error", this.server);
   196     messageManager.removeWeakMessageListener("Marionette:log", this.server);
   197     messageManager.removeWeakMessageListener("Marionette:shareData", this.server);
   198     messageManager.removeWeakMessageListener("Marionette:register", this.server);
   199     messageManager.removeWeakMessageListener("Marionette:runEmulatorCmd", this.server);
   200     messageManager.removeWeakMessageListener("Marionette:runEmulatorShell", this.server);
   201     messageManager.removeWeakMessageListener("Marionette:switchToFrame", this.server);
   202     messageManager.removeWeakMessageListener("Marionette:switchedToFrame", this.server);
   203     messageManager.removeWeakMessageListener("MarionetteFrame:handleModal", this);
   204     messageManager.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
   205   },
   207 };

mercurial