Bug 1092954: oust all users from a room when it's deleted. r=Standard8 a=sylvestre
authorMike de Boer <mdeboer@mozilla.com>
Mon, 08 Dec 2014 17:45:32 +0100
changeset 235636 3e51010c845422df4eddc157ee290e61fd8f4c45
parent 235635 531e6d104e4138fbd22ba212f30b3eae427a85f1
child 235637 9240c2ca5cf7538e7950652db490a5e5ff6f2596
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8, sylvestre
bugs1092954
milestone35.0
Bug 1092954: oust all users from a room when it's deleted. r=Standard8 a=sylvestre
browser/components/loop/LoopRooms.jsm
browser/components/loop/content/shared/js/activeRoomStore.js
browser/components/loop/content/shared/js/otSdkDriver.js
--- a/browser/components/loop/LoopRooms.jsm
+++ b/browser/components/loop/LoopRooms.jsm
@@ -343,16 +343,17 @@ let LoopRoomsInternal = {
     // XXX bug 1092954: Before deleting a room, the client should check room
     //     membership and forceDisconnect() all current participants.
     let room = this.rooms.get(roomToken);
     let url = "/rooms/" + encodeURIComponent(roomToken);
     MozLoopService.hawkRequest(this.sessionType, url, "DELETE")
       .then(response => {
         this.rooms.delete(roomToken);
         eventEmitter.emit("delete", room);
+        eventEmitter.emit("delete:" + room.roomToken, room);
         callback(null, room);
       }, error => callback(error)).catch(error => callback(error));
   },
 
   /**
    * Internal function to handle POSTs to a room.
    *
    * @param {String} roomToken  The room token.
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -206,16 +206,18 @@ loop.store.ActiveRoomStore = (function()
 
       this.setStoreState({
         roomToken: actionData.token,
         roomState: ROOM_STATES.READY
       });
 
       this._mozLoop.rooms.on("update:" + actionData.roomToken,
         this._handleRoomUpdate.bind(this));
+      this._mozLoop.rooms.on("delete:" + actionData.roomToken,
+        this._handleRoomDelete.bind(this));
     },
 
     /**
      * Handles the setupRoomInfo action. Sets up the initial room data and
      * sets the state to `READY`.
      *
      * @param {sharedActions.SetupRoomInfo} actionData
      */
@@ -225,16 +227,18 @@ loop.store.ActiveRoomStore = (function()
         roomOwner: actionData.roomOwner,
         roomState: ROOM_STATES.READY,
         roomToken: actionData.roomToken,
         roomUrl: actionData.roomUrl
       });
 
       this._mozLoop.rooms.on("update:" + actionData.roomToken,
         this._handleRoomUpdate.bind(this));
+      this._mozLoop.rooms.on("delete:" + actionData.roomToken,
+        this._handleRoomDelete.bind(this));
     },
 
     /**
      * Handles the updateRoomInfo action. Updates the room data.
      *
      * @param {sharedActions.UpdateRoomInfo} actionData
      */
     updateRoomInfo: function(actionData) {
@@ -255,16 +259,28 @@ loop.store.ActiveRoomStore = (function()
       this.dispatchAction(new sharedActions.UpdateRoomInfo({
         roomName: roomData.roomName,
         roomOwner: roomData.roomOwner,
         roomUrl: roomData.roomUrl
       }));
     },
 
     /**
+     * Handles the deletion of a room, notified by the mozLoop rooms API.
+     *
+     * @param {String} eventName The name of the event
+     * @param {Object} roomData  The roomData of the deleted room
+     */
+    _handleRoomDelete: function(eventName, roomData) {
+      this._sdkDriver.forceDisconnectAll(function() {
+        window.close();
+      });
+    },
+
+    /**
      * Handles the action to join to a room.
      */
     joinRoom: function() {
       // Reset the failure reason if necessary.
       if (this.getStoreState().failureReason) {
         this.setStoreState({failureReason: undefined});
       }
 
@@ -386,18 +402,19 @@ loop.store.ActiveRoomStore = (function()
 
     /**
      * Handles the window being unloaded. Ensures the room is left.
      */
     windowUnload: function() {
       this._leaveRoom(ROOM_STATES.CLOSING);
 
       // If we're closing the window, we can stop listening to updates.
-      this._mozLoop.rooms.off("update:" + this.getStoreState().roomToken,
-        this._handleRoomUpdate.bind(this));
+      var roomToken = this.getStoreState().roomToken;
+      this._mozLoop.rooms.off("update:" + roomToken);
+      this._mozLoop.rooms.off("delete:" + roomToken);
     },
 
     /**
      * Handles a room being left.
      */
     leaveRoom: function() {
       this._leaveRoom();
     },
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -20,16 +20,18 @@ loop.OTSdkDriver = (function() {
       }
       if (!options.sdk) {
         throw new Error("Missing option sdk");
       }
 
       this.dispatcher = options.dispatcher;
       this.sdk = options.sdk;
 
+      this.connections = {};
+
       this.dispatcher.register(this, [
         "setupStreamElements",
         "setMute"
       ]);
   };
 
   OTSdkDriver.prototype = {
     /**
@@ -110,16 +112,48 @@ loop.OTSdkDriver = (function() {
         delete this.publisher;
       }
 
       // Also, tidy these variables ready for next time.
       delete this._sessionConnected;
       delete this._publisherReady;
       delete this._publishedLocalStream;
       delete this._subscribedRemoteStream;
+      this.connections = {};
+    },
+
+    /**
+     * Oust all users from an ongoing session. This is typically done when a room
+     * owner deletes the room.
+     *
+     * @param {Function} callback Function to be invoked once all connections are
+     *                            ousted
+     */
+    forceDisconnectAll: function(callback) {
+      if (!this._sessionConnected) {
+        callback();
+        return;
+      }
+
+      var connectionNames = Object.keys(this.connections);
+      if (connectionNames.length === 0) {
+        callback();
+        return;
+      }
+      var disconnectCount = 0;
+      connectionNames.forEach(function(id) {
+        var connection = this.connections[id];
+        this.session.forceDisconnect(connection, function() {
+          // When all connections have disconnected, call the callback, since
+          // we're done.
+          if (++disconnectCount === connectionNames.length) {
+            callback();
+          }
+        });
+      }, this);
     },
 
     /**
      * Called once the session has finished connecting.
      *
      * @param {Error} error An OT error object, null if there was no error.
      */
     _onConnectionComplete: function(error) {
@@ -134,20 +168,24 @@ loop.OTSdkDriver = (function() {
       this.dispatcher.dispatch(new sharedActions.ConnectedToSdkServers());
       this._sessionConnected = true;
       this._maybePublishLocalStream();
     },
 
     /**
      * Handles the connection event for a peer's connection being dropped.
      *
-     * @param {SessionDisconnectEvent} event The event details
-     * https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
+     * @param {ConnectionEvent} event The event details
+     * https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
      */
     _onConnectionDestroyed: function(event) {
+      var connection = event.connection;
+      if (connection && (connection.id in this.connections)) {
+        delete this.connections[connection.id];
+      }
       this.dispatcher.dispatch(new sharedActions.RemotePeerDisconnected({
         peerHungup: event.reason === "clientDisconnected"
       }));
     },
 
     /**
      * Handles the session event for the connection for this client being
      * destroyed.
@@ -159,21 +197,28 @@ loop.OTSdkDriver = (function() {
       // We only need to worry about the network disconnected reason here.
       if (event.reason === "networkDisconnected") {
         this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
           reason: FAILURE_REASONS.NETWORK_DISCONNECTED
         }));
       }
     },
 
+    /**
+     * Handles the connection event for a newly connecting peer.
+     *
+     * @param {ConnectionEvent} event The event details
+     * https://tokbox.com/opentok/libraries/client/js/reference/ConnectionEvent.html
+     */
     _onConnectionCreated: function(event) {
-      if (this.session.connection.id === event.connection.id) {
+      var connection = event.connection;
+      if (this.session.connection.id === connection.id) {
         return;
       }
-
+      this.connections[connection.id] = connection;
       this.dispatcher.dispatch(new sharedActions.RemotePeerConnected());
     },
 
     /**
      * Handles the event when the remote stream is created.
      *
      * @param {StreamEvent} event The event details:
      * https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html