Bug 1330409 - Convert CastingApps events to bundle events; r=sebastian
authorJim Chen <nchen@mozilla.com>
Wed, 25 Jan 2017 18:53:57 -0500
changeset 331151 2740c0e269054538fe69c6ed5b6afe82d9407370
parent 331150 6057588e27441ec4ce22e05b5a9f7eee7774995b
child 331152 65b07eac83b329667ae01b036e7e5f5b067c31c4
push id31261
push usercbook@mozilla.com
push dateThu, 26 Jan 2017 11:32:02 +0000
treeherdermozilla-central@a338e596b1d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssebastian
bugs1330409
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1330409 - Convert CastingApps events to bundle events; r=sebastian Convert the events used in MediaCastingBar and ChromeCastPlayer to GeckoBundle/BundleEventListener events. UI thread events are used because the listener performs operations on the UI thread.
mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
mobile/android/chrome/content/CastingApps.js
mobile/android/modules/MediaPlayerApp.jsm
mobile/android/modules/TabMirror.jsm
widget/android/nsIAndroidBridge.idl
--- a/mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
@@ -3,16 +3,17 @@
  * 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/. */
 
 package org.mozilla.gecko;
 
 import java.io.IOException;
 
 import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.GeckoBundle;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import com.google.android.gms.cast.Cast.MessageReceivedCallback;
 import com.google.android.gms.cast.ApplicationMetadata;
 import com.google.android.gms.cast.Cast;
 import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
 import com.google.android.gms.cast.CastDevice;
@@ -84,25 +85,25 @@ class ChromeCastPlayer implements GeckoM
         }
 
         @Override
         public void onStatusUpdated() {
             MediaStatus mediaStatus = remoteMediaPlayer.getMediaStatus();
 
             switch (mediaStatus.getPlayerState()) {
             case MediaStatus.PLAYER_STATE_PLAYING:
-                GeckoAppShell.notifyObservers("MediaPlayer:Playing", null);
+                EventDispatcher.getInstance().dispatch("MediaPlayer:Playing", null);
                 break;
             case MediaStatus.PLAYER_STATE_PAUSED:
-                GeckoAppShell.notifyObservers("MediaPlayer:Paused", null);
+                EventDispatcher.getInstance().dispatch("MediaPlayer:Paused", null);
                 break;
             case MediaStatus.PLAYER_STATE_IDLE:
                 // TODO: Do we want to shutdown when there are errors?
                 if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
-                    GeckoAppShell.notifyObservers("Casting:Stop", null);
+                    EventDispatcher.getInstance().dispatch("Casting:Stop", null);
                 }
                 break;
             default:
                 // TODO: Do we need to handle other status such as buffering / unknown?
                 break;
             }
         }
 
@@ -383,17 +384,19 @@ class ChromeCastPlayer implements GeckoM
         }
 
         /*
          * Receive message from the receiver app
          */
         @Override
         public void onMessageReceived(CastDevice castDevice, String namespace,
                                       String message) {
-            GeckoAppShell.notifyObservers("MediaPlayer:Response", message);
+            final GeckoBundle data = new GeckoBundle(1);
+            data.putString("message", message);
+            EventDispatcher.getInstance().dispatch("MediaPlayer:Response", data);
         }
 
         public void sendMessage(String message) {
             if (apiClient != null && mMirrorChannel != null) {
                 try {
                     Cast.CastApi.sendMessage(apiClient, mMirrorChannel.getNamespace(), message)
                         .setResultCallback(
                                            new ResultCallback<Status>() {
@@ -432,17 +435,19 @@ class ChromeCastPlayer implements GeckoM
                                                              mMirrorChannel
                                                              .getNamespace(),
                                                              mMirrorChannel);
                     sendSuccess(callback, null);
                 } catch (IOException e) {
                     Log.e(LOGTAG, "Exception while creating channel", e);
                 }
 
-                GeckoAppShell.notifyObservers("Casting:Mirror", route.getId());
+                final GeckoBundle message = new GeckoBundle(1);
+                message.putString("id", route.getId());
+                EventDispatcher.getInstance().dispatch("Casting:Mirror", message);
             } else {
                 sendError(callback, status.toString());
             }
         }
     }
 
     @Override
     public void message(String msg, final EventCallback callback) {
--- a/mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
@@ -1,44 +1,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.GeckoApp;
-import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.BundleEventListener;
+import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
-import org.json.JSONObject;
-
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageButton;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, GeckoEventListener  {
+public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, BundleEventListener  {
     private static final String LOGTAG = "GeckoMediaCastingBar";
 
     private TextView mCastingTo;
     private ImageButton mMediaPlay;
     private ImageButton mMediaPause;
     private ImageButton mMediaStop;
 
     private boolean mInflated;
 
     public MediaCastingBar(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
+        EventDispatcher.getInstance().registerUiThreadListener(this,
             "Casting:Started",
             "Casting:Paused",
             "Casting:Playing",
             "Casting:Stopped");
     }
 
     public void inflateContent() {
         LayoutInflater inflater = LayoutInflater.from(getContext());
@@ -67,65 +67,55 @@ public class MediaCastingBar extends Rel
         setVisibility(VISIBLE);
     }
 
     public void hide() {
         setVisibility(GONE);
     }
 
     public void onDestroy() {
-        EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
+        EventDispatcher.getInstance().unregisterUiThreadListener(this,
             "Casting:Started",
             "Casting:Paused",
             "Casting:Playing",
             "Casting:Stopped");
     }
 
     // View.OnClickListener implementation
     @Override
     public void onClick(View v) {
         final int viewId = v.getId();
 
         if (viewId == R.id.media_play) {
-            GeckoAppShell.notifyObservers("Casting:Play", "");
+            EventDispatcher.getInstance().dispatch("Casting:Play", null);
             mMediaPlay.setVisibility(GONE);
             mMediaPause.setVisibility(VISIBLE);
         } else if (viewId == R.id.media_pause) {
-            GeckoAppShell.notifyObservers("Casting:Pause", "");
+            EventDispatcher.getInstance().dispatch("Casting:Pause", null);
             mMediaPause.setVisibility(GONE);
             mMediaPlay.setVisibility(VISIBLE);
         } else if (viewId == R.id.media_stop) {
-            GeckoAppShell.notifyObservers("Casting:Stop", "");
+            EventDispatcher.getInstance().dispatch("Casting:Stop", null);
         }
     }
 
-    // GeckoEventListener implementation
-    @Override
-    public void handleMessage(final String event, final JSONObject message) {
-        final String device = message.optString("device");
+    @Override // BundleEventListener
+    public void handleMessage(final String event, final GeckoBundle message,
+                              final EventCallback callback) {
+        if ("Casting:Started".equals(event)) {
+            show();
+            mCastingTo.setText(message.getString("device", ""));
+            mMediaPlay.setVisibility(GONE);
+            mMediaPause.setVisibility(VISIBLE);
 
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                if (event.equals("Casting:Started")) {
-                    show();
-                    if (!TextUtils.isEmpty(device)) {
-                        mCastingTo.setText(device);
-                    } else {
-                        // Should not happen
-                        mCastingTo.setText("");
-                        Log.d(LOGTAG, "Device name is empty.");
-                    }
-                    mMediaPlay.setVisibility(GONE);
-                    mMediaPause.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Paused")) {
-                    mMediaPause.setVisibility(GONE);
-                    mMediaPlay.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Playing")) {
-                    mMediaPlay.setVisibility(GONE);
-                    mMediaPause.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Stopped")) {
-                    hide();
-                }
-            }
-        });
+        } else if ("Casting:Paused".equals(event)) {
+            mMediaPause.setVisibility(GONE);
+            mMediaPlay.setVisibility(VISIBLE);
+
+        } else if ("Casting:Playing".equals(event)) {
+            mMediaPlay.setVisibility(GONE);
+            mMediaPause.setVisibility(VISIBLE);
+
+        } else if ("Casting:Stopped".equals(event)) {
+            hide();
+        }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
@@ -1,32 +1,25 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 package org.mozilla.gecko;
 
-import org.json.JSONObject;
-import org.json.JSONException;
-
-import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.PresentationView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ScreenManagerHelper;
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSObject;
 
 import com.google.android.gms.cast.CastMediaControlIntent;
 import com.google.android.gms.cast.CastPresentation;
 import com.google.android.gms.cast.CastRemoteDisplayLocalService;
 import com.google.android.gms.common.ConnectionResult;
 import com.google.android.gms.common.GooglePlayServicesUtil;
 
 import android.app.Activity;
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -83,20 +83,23 @@ var CastingApps = {
     SimpleServiceDiscovery.search(this._interval);
 
     this._castMenuId = NativeWindow.contextmenus.add(
       Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
       this.filterCast,
       this.handleContextMenu.bind(this)
     );
 
-    Services.obs.addObserver(this, "Casting:Play", false);
-    Services.obs.addObserver(this, "Casting:Pause", false);
-    Services.obs.addObserver(this, "Casting:Stop", false);
-    Services.obs.addObserver(this, "Casting:Mirror", false);
+    GlobalEventDispatcher.registerListener(this, [
+      "Casting:Play",
+      "Casting:Pause",
+      "Casting:Stop",
+      "Casting:Mirror",
+    ]);
+
     Services.obs.addObserver(this, "ssdp-service-found", false);
     Services.obs.addObserver(this, "ssdp-service-lost", false);
     Services.obs.addObserver(this, "application-background", false);
     Services.obs.addObserver(this, "application-foreground", false);
 
     BrowserApp.deck.addEventListener("TabSelect", this, true);
     BrowserApp.deck.addEventListener("pageshow", this, true);
     BrowserApp.deck.addEventListener("playing", this, true);
@@ -167,18 +170,18 @@ var CastingApps = {
   isCastingEnabled: function isCastingEnabled() {
     return Services.prefs.getBoolPref("browser.casting.enabled");
   },
 
   isMirroringEnabled: function isMirroringEnabled() {
     return Services.prefs.getBoolPref("browser.mirroring.enabled");
   },
 
-  observe: function (aSubject, aTopic, aData) {
-    switch (aTopic) {
+  onEvent: function (event, message, callback) {
+    switch (event) {
       case "Casting:Play":
         if (this.session && this.session.remoteMedia.status == "paused") {
           this.session.remoteMedia.play();
         }
         break;
       case "Casting:Pause":
         if (this.session && this.session.remoteMedia.status == "started") {
           this.session.remoteMedia.pause();
@@ -187,21 +190,26 @@ var CastingApps = {
       case "Casting:Stop":
         if (this.session) {
           this.closeExternal();
         }
         break;
       case "Casting:Mirror":
         {
           Cu.import("resource://gre/modules/TabMirror.jsm");
-          this.tabMirror = new TabMirror(aData, window);
+          this.tabMirror = new TabMirror(message.id, window);
           NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false });
           NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true });
         }
         break;
+    }
+  },
+
+  observe: function (aSubject, aTopic, aData) {
+    switch (aTopic) {
       case "ssdp-service-found":
         this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
         break;
       case "ssdp-service-lost":
         this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
         break;
       case "application-background":
         // Turn off polling while in the background
@@ -746,41 +754,44 @@ var CastingApps = {
 
   // RemoteMedia callback API methods
   onRemoteMediaStart: function(aRemoteMedia) {
     if (!this.session) {
       return;
     }
 
     aRemoteMedia.load(this.session.data);
-    Messaging.sendRequest({ type: "Casting:Started", device: this.session.service.friendlyName });
+    GlobalEventDispatcher.sendRequest({
+        type: "Casting:Started",
+        device: this.session.service.friendlyName,
+    });
 
     let video = this.session.videoRef.get();
     if (video) {
       this._sendEventToVideo(video, { active: true });
       this._updatePageAction(video);
     }
   },
 
   onRemoteMediaStop: function(aRemoteMedia) {
-    Messaging.sendRequest({ type: "Casting:Stopped" });
+    GlobalEventDispatcher.sendRequest({ type: "Casting:Stopped" });
     this._shutdown();
   },
 
   onRemoteMediaStatus: function(aRemoteMedia) {
     if (!this.session) {
       return;
     }
 
     let status = aRemoteMedia.status;
     switch (status) {
       case "started":
-        Messaging.sendRequest({ type: "Casting:Playing" });
+        GlobalEventDispatcher.sendRequest({ type: "Casting:Playing" });
         break;
       case "paused":
-        Messaging.sendRequest({ type: "Casting:Paused" });
+        GlobalEventDispatcher.sendRequest({ type: "Casting:Paused" });
         break;
       case "completed":
         this.closeExternal();
         break;
     }
   }
 };
--- a/mobile/android/modules/MediaPlayerApp.jsm
+++ b/mobile/android/modules/MediaPlayerApp.jsm
@@ -78,18 +78,20 @@ function RemoteMedia(id, listener) {
     Services.tm.mainThread.dispatch((function() {
       this._listener.onRemoteMediaStart(this);
     }).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
   }
 }
 
 RemoteMedia.prototype = {
   shutdown: function shutdown() {
-    Services.obs.removeObserver(this, "MediaPlayer:Playing");
-    Services.obs.removeObserver(this, "MediaPlayer:Paused");
+    EventDispatcher.instance.unregisterListener(this, [
+      "MediaPlayer:Playing",
+      "MediaPlayer:Paused",
+    ]);
 
     this._send("MediaPlayer:End", {}, (result, err) => {
       this._status = "shutdown";
       if ("onRemoteMediaStop" in this._listener) {
         this._listener.onRemoteMediaStop(this);
       }
     });
   },
@@ -121,46 +123,46 @@ RemoteMedia.prototype = {
   load: function load(aData) {
     this._send("MediaPlayer:Load", aData, (result, err) => {
       if (err) {
         Cu.reportError("Can't load " + err);
         this.shutdown();
         return;
       }
 
-      Services.obs.addObserver(this, "MediaPlayer:Playing", false);
-      Services.obs.addObserver(this, "MediaPlayer:Paused", false);
+      EventDispatcher.instance.registerListener(this, [
+        "MediaPlayer:Playing",
+        "MediaPlayer:Paused",
+      ]);
       this._status = "started";
     })
   },
 
   get status() {
     return this._status;
   },
 
-  observe: function (aSubject, aTopic, aData) {
-    switch (aTopic) {
+  onEvent: function (event, message, callback) {
+    switch (event) {
       case "MediaPlayer:Playing":
         if (this._status !== "started") {
           this._status = "started";
           if ("onRemoteMediaStatus" in this._listener) {
             this._listener.onRemoteMediaStatus(this);
           }
         }
         break;
       case "MediaPlayer:Paused":
         if (this._status !== "paused") {
           this._status = "paused";
           if ("onRemoteMediaStatus" in this._listener) {
             this._listener.onRemoteMediaStatus(this);
           }
         }
         break;
-      default:
-        break;
     }
   },
 
   _send: function(msg, data, callback) {
     data.id = this._id;
     send(msg, data, callback);
   }
 }
--- a/mobile/android/modules/TabMirror.jsm
+++ b/mobile/android/modules/TabMirror.jsm
@@ -18,17 +18,19 @@ var failure = function(x) {
 
 var TabMirror = function(deviceId, window) {
 
   this.deviceId = deviceId;
   // Save RTCSessionDescription and RTCIceCandidate for later when the window object is not available.
   this.RTCSessionDescription = window.RTCSessionDescription;
   this.RTCIceCandidate = window.RTCIceCandidate;
 
-  Services.obs.addObserver((aSubject, aTopic, aData) => this._processMessage(aData), "MediaPlayer:Response", false);
+  EventDispatcher.instance.registerListener(
+      (event, data, callback) => this._processMessage(data.message), "MediaPlayer:Response");
+
   this._sendMessage({ start: true });
   this._window = window;
   this._pc = new window.RTCPeerConnection(CONFIG, {});
   if (!this._pc) {
     throw "Failure creating Webrtc object";
   }
 
 };
@@ -53,23 +55,17 @@ TabMirror.prototype = {
           { aspectRatio: this._screenSize.width / this._screenSize.height }
         ]
       }
     };
 
     this._window.navigator.mozGetUserMedia(constraints, this._onGumSuccess.bind(this), this._onGumFailure.bind(this));
   },
 
-  _processMessage: function(data) {
-    if (!data) {
-      return;
-    }
-
-    let msg = JSON.parse(data);
-
+  _processMessage: function(msg) {
     if (!msg) {
       return;
     }
 
     if (msg.sdp && msg.type === "answer") {
       this._processAnswer(msg);
     } else if (msg.type == "size") {
       if (msg.height) {
@@ -139,15 +135,15 @@ TabMirror.prototype = {
   },
 
   stop: function() {
     if (this.deviceId) {
       let obj = {
         type: "MediaPlayer:End",
         id: this.deviceId
       };
-      Services.androidBridge.handleGeckoMessage(obj);
+      EventDispatcher.instance.sendRequest(obj);
     }
   },
 };
 
 
 this.EXPORTED_SYMBOLS = ["TabMirror"];
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -34,17 +34,17 @@ interface nsIAndroidBrowserApp : nsISupp
 
 [scriptable, uuid(e64c39b8-b8ec-477d-aef5-89d517ff9219)]
 interface nsIAndroidEventCallback : nsISupports
 {
   void onSuccess([optional] in jsval data);
   void onError([optional] in jsval data);
 };
 
-[scriptable, uuid(73569a75-78eb-4c7f-82b9-2d4f5ccf44c3)]
+[scriptable, function, uuid(73569a75-78eb-4c7f-82b9-2d4f5ccf44c3)]
 interface nsIAndroidEventListener : nsISupports
 {
   void onEvent(in AString event,
                [optional] in jsval data,
                [optional] in nsIAndroidEventCallback callback);
 };
 
 [scriptable, uuid(e98bf792-4145-411e-b298-8219d9b03817)]