Bug 1133493: add necessary actions to start sharing a browser tab and pass respective parameters to the OpenTok SDK. r=Standard8, a=lsblakk
authorMike de Boer <mdeboer@mozilla.com>
Tue, 24 Feb 2015 17:16:30 +0100
changeset 248115 8f1e24aa5874eaec3afb142923d9398e50abb465
parent 248114 8b92dc52aab2ecbb98b62e1f8c9f25ba2a2c9000
child 248116 d12f76b7e524fd9ae6f1545cbc1cf9d864ee1777
push id7762
push usermdeboer@mozilla.com
push dateMon, 16 Mar 2015 15:07:09 +0000
treeherdermozilla-aurora@9b59f3a2743d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8, lsblakk
bugs1133493
milestone38.0a2
Bug 1133493: add necessary actions to start sharing a browser tab and pass respective parameters to the OpenTok SDK. r=Standard8, a=lsblakk
browser/components/loop/content/shared/js/activeRoomStore.js
browser/components/loop/content/shared/js/otSdkDriver.js
browser/components/loop/test/shared/activeRoomStore_test.js
browser/components/loop/test/shared/otSdkDriver_test.js
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -125,17 +125,19 @@ loop.store.ActiveRoomStore = (function()
         "setMute",
         "screenSharingState",
         "receivingScreenShare",
         "remotePeerDisconnected",
         "remotePeerConnected",
         "windowUnload",
         "leaveRoom",
         "feedbackComplete",
-        "videoDimensionsChanged"
+        "videoDimensionsChanged",
+        "startScreenShare",
+        "endScreenShare"
       ]);
     },
 
     /**
      * Execute setupWindowData event action from the dispatcher. This gets
      * the room data from the mozLoop api, and dispatches an UpdateRoomInfo event.
      * It also dispatches JoinRoom as this action is only applicable to the desktop
      * client, and needs to auto-join.
@@ -406,16 +408,59 @@ loop.store.ActiveRoomStore = (function()
     /**
      * Used to note the current state of receiving screenshare data.
      */
     receivingScreenShare: function(actionData) {
       this.setStoreState({receivingScreenShare: actionData.receiving});
     },
 
     /**
+     * Initiates a screen sharing publisher.
+     *
+     * @param {sharedActions.StartScreenShare} actionData
+     */
+    startScreenShare: function(actionData) {
+      this.dispatchAction(new sharedActions.ScreenSharingState({
+        state: SCREEN_SHARE_STATES.PENDING
+      }));
+
+      var options = {
+        videoSource: actionData.type
+      };
+      if (options.videoSource === "browser") {
+        this._mozLoop.getActiveTabWindowId(function(err, windowId) {
+          if (err || !windowId) {
+            this.dispatchAction(new sharedActions.ScreenSharingState({
+              state: SCREEN_SHARE_STATES.INACTIVE
+            }));
+            return;
+          }
+          options.constraints = {
+            browserWindow: windowId,
+            scrollWithPage: true
+          };
+          this._sdkDriver.startScreenShare(options);
+        }.bind(this));
+      } else {
+        this._sdkDriver.startScreenShare(options);
+      }
+    },
+
+    /**
+     * Ends an active screenshare session.
+     */
+    endScreenShare: function() {
+      if (this._sdkDriver.endScreenShare()) {
+        this.dispatchAction(new sharedActions.ScreenSharingState({
+          state: SCREEN_SHARE_STATES.INACTIVE
+        }));
+      }
+    },
+
+    /**
      * Handles recording when a remote peer has connected to the servers.
      */
     remotePeerConnected: function() {
       this.setStoreState({
         roomState: ROOM_STATES.HAS_PARTICIPANTS,
         used: true
       });
 
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -26,19 +26,17 @@ loop.OTSdkDriver = (function() {
 
       this.dispatcher = options.dispatcher;
       this.sdk = options.sdk;
 
       this.connections = {};
 
       this.dispatcher.register(this, [
         "setupStreamElements",
-        "setMute",
-        "startScreenShare",
-        "endScreenShare"
+        "setMute"
       ]);
 
     /**
      * XXX This is a workaround for desktop machines that do not have a
      * camera installed. As we don't yet have device enumeration, when
      * we do, this can be removed (bug 1138851), and the sdk should handle it.
      */
     if ("isDesktop" in options && options.isDesktop &&
@@ -119,46 +117,54 @@ loop.OTSdkDriver = (function() {
         this.publisher.publishAudio(actionData.enabled);
       } else {
         this.publisher.publishVideo(actionData.enabled);
       }
     },
 
     /**
      * Initiates a screen sharing publisher.
+     *
+     * options items:
+     *  - {String}  videoSource    The type of screen to share. Values of 'screen',
+     *                             'window', 'application' and 'browser' are
+     *                             currently supported.
+     *  - {mixed}   browserWindow  The unique identifier of a browser window. May
+     *                             be passed when `videoSource` is 'browser'.
+     *  - {Boolean} scrollWithPage Flag to signal that scrolling a page should
+     *                             update the stream. May be passed when
+     *                             `videoSource` is 'browser'.
+     *
+     * @param {Object} options Hash containing options for the SDK
      */
-    startScreenShare: function(actionData) {
-      this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
-        state: SCREEN_SHARE_STATES.PENDING
-      }));
-
-      var config = this._getCopyPublisherConfig();
-      config.videoSource = actionData.type;
+    startScreenShare: function(options) {
+      var config = _.extend(this._getCopyPublisherConfig(), options);
 
       this.screenshare = this.sdk.initPublisher(this.getScreenShareElementFunc(),
         config);
       this.screenshare.on("accessAllowed", this._onScreenShareGranted.bind(this));
       this.screenshare.on("accessDenied", this._onScreenShareDenied.bind(this));
     },
 
     /**
-     * Ends an active screenshare session.
+     * Ends an active screenshare session. Return `true` when an active screen-
+     * sharing session was ended or `false` when no session is active.
+     *
+     * @type {Boolean}
      */
     endScreenShare: function() {
       if (!this.screenshare) {
-        return;
+        return false;
       }
 
       this.session.unpublish(this.screenshare);
       this.screenshare.off("accessAllowed accessDenied");
       this.screenshare.destroy();
       delete this.screenshare;
-      this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
-        state: SCREEN_SHARE_STATES.INACTIVE
-      }));
+      return true;
     },
 
     /**
      * Connects a session for the SDK, listening to the required events.
      *
      * sessionData items:
      * - sessionId: The OT session ID
      * - apiKey: The OT API key
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -26,24 +26,27 @@ describe("loop.store.ActiveRoomStore", f
       rooms: {
         get: sinon.stub(),
         join: sinon.stub(),
         refreshMembership: sinon.stub(),
         leave: sinon.stub(),
         on: sinon.stub(),
         off: sinon.stub()
       },
-      setScreenShareState: sinon.stub()
+      setScreenShareState: sinon.stub(),
+      getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42)
     };
 
     fakeSdkDriver = {
       connectSession: sandbox.stub(),
       disconnectSession: sandbox.stub(),
       forceDisconnectAll: sandbox.stub().callsArg(0),
-      retryPublishWithoutVideo: sinon.stub()
+      retryPublishWithoutVideo: sinon.stub(),
+      startScreenShare: sandbox.stub(),
+      endScreenShare: sandbox.stub().returns(true)
     };
 
     fakeMultiplexGum = {
         reset: sandbox.spy()
     };
 
     loop.standaloneMedia = {
       multiplexGum: fakeMultiplexGum
@@ -706,16 +709,70 @@ describe("loop.store.ActiveRoomStore", f
       store.receivingScreenShare(new sharedActions.ReceivingScreenShare({
         receiving: true
       }));
 
       expect(store.getStoreState().receivingScreenShare).eql(true);
     });
   });
 
+  describe("#startScreenShare", function() {
+    it("should set the state to 'pending'", function() {
+      store.startScreenShare(new sharedActions.StartScreenShare({
+        type: "window"
+      }));
+
+      sinon.assert.calledOnce(dispatcher.dispatch);
+      sinon.assert.calledWith(dispatcher.dispatch,
+        new sharedActions.ScreenSharingState({
+          state: SCREEN_SHARE_STATES.PENDING
+        }));
+    });
+
+    it("should invoke the SDK driver with the correct options for window sharing", function() {
+      store.startScreenShare(new sharedActions.StartScreenShare({
+        type: "window"
+      }));
+
+      sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
+      sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
+        videoSource: "window"
+      });
+    });
+
+    it("should invoke the SDK driver with the correct options for tab sharing", function() {
+      store.startScreenShare(new sharedActions.StartScreenShare({
+        type: "browser"
+      }));
+
+      sinon.assert.calledOnce(fakeMozLoop.getActiveTabWindowId);
+
+      sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
+      sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
+        videoSource: "browser",
+        constraints: {
+          browserWindow: 42,
+          scrollWithPage: true
+        }
+      });
+    })
+  });
+
+  describe("#endScreenShare", function() {
+    it("should set the state to 'inactive'", function() {
+      store.endScreenShare();
+
+      sinon.assert.calledOnce(dispatcher.dispatch);
+      sinon.assert.calledWith(dispatcher.dispatch,
+        new sharedActions.ScreenSharingState({
+          state: SCREEN_SHARE_STATES.INACTIVE
+        }));
+    });
+  });
+
   describe("#remotePeerConnected", function() {
     it("should set the state to `HAS_PARTICIPANTS`", function() {
       store.remotePeerConnected();
 
       expect(store.getStoreState().roomState).eql(ROOM_STATES.HAS_PARTICIPANTS);
     });
 
     it("should set the pref for ToS to `seen`", function() {
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -171,73 +171,55 @@ describe("loop.OTSdkDriver", function ()
         className: "fakeVideo"
       };
 
       driver.getScreenShareElementFunc = function() {
         return fakeElement;
       };
     });
 
-    it("should dispatch a `ScreenSharingState` action", function() {
-      driver.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
-
-      sinon.assert.calledOnce(dispatcher.dispatch);
-      sinon.assert.calledWithExactly(dispatcher.dispatch,
-        new sharedActions.ScreenSharingState({
-          state: SCREEN_SHARE_STATES.PENDING
-        }));
-    });
-
     it("should initialize a publisher", function() {
-      driver.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
+      // We're testing with `videoSource` set to 'browser', not 'window', as it
+      // has multiple options.
+      var options = {
+        videoSource: "browser",
+        browserWindow: 42,
+        scrollWithPage: true
+      };
+      driver.startScreenShare(options);
 
       sinon.assert.calledOnce(sdk.initPublisher);
-      sinon.assert.calledWithMatch(sdk.initPublisher,
-        fakeElement, {videoSource: "window"});
+      sinon.assert.calledWithMatch(sdk.initPublisher, fakeElement, options);
     });
   });
 
   describe("#endScreenShare", function() {
     beforeEach(function() {
       driver.getScreenShareElementFunc = function() {};
 
-      driver.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
+      driver.startScreenShare({
+        videoSource: "window"
+      });
 
       sandbox.stub(dispatcher, "dispatch");
 
       driver.session = session;
     });
 
     it("should unpublish the share", function() {
       driver.endScreenShare(new sharedActions.EndScreenShare());
 
       sinon.assert.calledOnce(session.unpublish);
     });
 
     it("should destroy the share", function() {
-      driver.endScreenShare(new sharedActions.EndScreenShare());
+      expect(driver.endScreenShare()).to.equal(true);
 
       sinon.assert.calledOnce(publisher.destroy);
     });
-
-    it("should dispatch a `ScreenSharingState` action", function() {
-      driver.endScreenShare(new sharedActions.EndScreenShare());
-
-      sinon.assert.calledOnce(dispatcher.dispatch);
-      sinon.assert.calledWithExactly(dispatcher.dispatch,
-        new sharedActions.ScreenSharingState({
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-    });
   });
 
   describe("#connectSession", function() {
     it("should initialise a new session", function() {
       driver.connectSession(sessionData);
 
       sinon.assert.calledOnce(sdk.initSession);
       sinon.assert.calledWithExactly(sdk.initSession, "3216549870");
@@ -676,19 +658,19 @@ describe("loop.OTSdkDriver", function ()
   });
 
   describe("Events (screenshare)", function() {
     beforeEach(function() {
       driver.connectSession(sessionData);
 
       driver.getScreenShareElementFunc = function() {};
 
-      driver.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
+      driver.startScreenShare({
+        videoSource: "window"
+      });
 
       sandbox.stub(dispatcher, "dispatch");
     });
 
     describe("accessAllowed", function() {
       it("should publish the stream", function() {
         publisher.trigger("accessAllowed", fakeEvent);