Bug 1288821 - Convert callObserver/removeObserver to native method; r=snorp
authorJim Chen <nchen@mozilla.com>
Thu, 04 Aug 2016 09:17:54 -0400
changeset 308210 51eca6620dc5fa304712700b78a1ef24b1cef547
parent 308209 ca7a13d72c4132cc6375ccf067100a14ac34842c
child 308211 983694d60aa56b4ab7f312c7704974a39dd2787c
push id31092
push usercbook@mozilla.com
push dateFri, 05 Aug 2016 10:16:59 +0000
treeherderautoland@b97dd7dd3cb9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1288821
milestone51.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 1288821 - Convert callObserver/removeObserver to native method; r=snorp Combine the callObserver and removeObserver methods used for notifying alert listeners into the native method GeckoAppShell.notifyAlertListener. Keep track of the listener and the alert cookie on the native side so that we don't need GeckoAppShell.ALERT_COOKIES anymore.
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
widget/android/AndroidAlerts.cpp
widget/android/AndroidAlerts.h
widget/android/nsAppShell.cpp
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -174,18 +174,16 @@ public class GeckoAppShell
         }
     };
 
     public static CrashHandler ensureCrashHandling() {
         // Crash handling is automatically enabled when GeckoAppShell is loaded.
         return CRASH_HANDLER;
     }
 
-    private static final Map<String, String> ALERT_COOKIES = new ConcurrentHashMap<String, String>();
-
     private static volatile boolean locationHighAccuracyEnabled;
 
     // Accessed by NotificationHelper. This should be encapsulated.
     /* package */ static NotificationClient notificationClient;
 
     public static NotificationClient getNotificationClient() {
         return notificationClient;
     }
@@ -1024,31 +1022,33 @@ public class GeckoAppShell
                 getApplicationContext(), type, persistentData);
         intent.setData(u);
 
         return PendingIntent.getService(getApplicationContext(), 0, intent,
                                         PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     @WrapForJNI
+    private static native void notifyAlertListener(String name, String topic);
+
+    @WrapForJNI
     public static void showAlertNotification(String imageUrl, String alertTitle, String alertText,
                                              String alertCookie, String alertName, String host,
                                              String persistentData) {
         final int notificationID = alertName.hashCode();
         final PendingIntent clickIntent, closeIntent;
 
         if (persistentData != null) {
             clickIntent = makePersistentNotificationIntent(
                     notificationID, "persistent-notification-click", persistentData);
             closeIntent = makePersistentNotificationIntent(
                     notificationID, "persistent-notification-close", persistentData);
 
         } else {
-            ALERT_COOKIES.put(alertName, alertCookie);
-            callObserver(alertName, "alertshow", alertCookie);
+            notifyAlertListener(alertName, "alertshow");
 
             // The intent to launch when the user clicks the expanded notification
             final Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
             notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
                                             AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
             notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
             // Put the strings into the intent as an URI
@@ -1067,33 +1067,27 @@ public class GeckoAppShell
         }
 
         notificationClient.add(notificationID, imageUrl, host, alertTitle,
                                alertText, clickIntent, closeIntent);
     }
 
     @WrapForJNI
     public static void closeNotification(String alertName) {
-        final String alertCookie = ALERT_COOKIES.get(alertName);
-        if (alertCookie != null) {
-            callObserver(alertName, "alertfinished", alertCookie);
-            ALERT_COOKIES.remove(alertName);
-        }
-
-        removeObserver(alertName);
+        notifyAlertListener(alertName, "alertfinished");
 
         final int notificationID = alertName.hashCode();
         notificationClient.remove(notificationID);
     }
 
     public static void handleNotification(String action, String alertName, String alertCookie) {
         final int notificationID = alertName.hashCode();
 
         if (GeckoApp.ACTION_ALERT_CALLBACK.equals(action)) {
-            callObserver(alertName, "alertclickcallback", alertCookie);
+            notifyAlertListener(alertName, "alertclickcallback");
 
             if (notificationClient.isOngoing(notificationID)) {
                 // When clicked, keep the notification if it displays progress
                 return;
             }
         }
 
         closeNotification(alertName);
--- a/widget/android/AndroidAlerts.cpp
+++ b/widget/android/AndroidAlerts.cpp
@@ -1,23 +1,24 @@
 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
 /* 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/. */
 
 #include "AndroidAlerts.h"
 #include "GeneratedJNIWrappers.h"
 #include "nsAlertsUtils.h"
-#include "nsAppShell.h"
 
 namespace mozilla {
 namespace widget {
 
 NS_IMPL_ISUPPORTS(AndroidAlerts, nsIAlertsService)
 
+StaticAutoPtr<AndroidAlerts::AlertInfoMap> AndroidAlerts::sAlertInfoMap;
+
 NS_IMETHODIMP
 AndroidAlerts::ShowAlertNotification(const nsAString & aImageUrl,
                                      const nsAString & aAlertTitle,
                                      const nsAString & aAlertText,
                                      bool aAlertTextClickable,
                                      const nsAString & aAlertCookie,
                                      nsIObserver * aAlertListener,
                                      const nsAString & aAlertName,
@@ -71,29 +72,56 @@ AndroidAlerts::ShowPersistentNotificatio
     nsCOMPtr<nsIPrincipal> principal;
     rv = aAlert->GetPrincipal(getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, NS_OK);
 
     nsAutoString host;
     nsAlertsUtils::GetSourceHostPort(principal, host);
 
     if (aPersistentData.IsEmpty() && aAlertListener) {
-        // This will remove any observers already registered for this id
-        nsAppShell::PostEvent(AndroidGeckoEvent::MakeAddObserver(name, aAlertListener));
+        if (!sAlertInfoMap) {
+            sAlertInfoMap = new AlertInfoMap();
+        }
+        // This will remove any observers already registered for this name.
+        sAlertInfoMap->Put(name, new AlertInfo{aAlertListener, cookie});
     }
 
     java::GeckoAppShell::ShowAlertNotification(
             imageUrl, title, text, cookie, name, host,
             !aPersistentData.IsEmpty() ? jni::StringParam(aPersistentData)
                                        : jni::StringParam(nullptr));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 AndroidAlerts::CloseAlert(const nsAString& aAlertName,
                           nsIPrincipal* aPrincipal)
 {
+    // We delete the entry in sAlertInfoMap later, when CloseNotification calls
+    // NotifyListener.
     java::GeckoAppShell::CloseNotification(aAlertName);
     return NS_OK;
 }
 
+void
+AndroidAlerts::NotifyListener(const nsAString& aName, const char* aTopic)
+{
+    if (!sAlertInfoMap) {
+        return;
+    }
+
+    const auto pAlertInfo = sAlertInfoMap->Get(aName);
+    if (!pAlertInfo) {
+        return;
+    }
+
+    if (pAlertInfo->listener) {
+        pAlertInfo->listener->Observe(
+                nullptr, aTopic, pAlertInfo->cookie.get());
+    }
+
+    if (NS_LITERAL_CSTRING("alertfinished").Equals(aTopic)) {
+        sAlertInfoMap->Remove(aName);
+    }
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/android/AndroidAlerts.h
+++ b/widget/android/AndroidAlerts.h
@@ -1,29 +1,49 @@
 /* -*- Mode: c++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
 /* 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/. */
 
 #ifndef mozilla_widget_AndroidAlerts_h__
 #define mozilla_widget_AndroidAlerts_h__
 
+#include "nsClassHashtable.h"
+#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
 #include "nsIAlertsService.h"
+#include "nsIObserver.h"
+
+#include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 namespace widget {
 
 class AndroidAlerts : public nsIAlertsService
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIALERTSSERVICE
 
   AndroidAlerts() {}
 
+  static void NotifyListener(const nsAString& aName, const char* aTopic);
+
 protected:
-  virtual ~AndroidAlerts() {}
+  virtual ~AndroidAlerts()
+  {
+      sAlertInfoMap = nullptr;
+  }
+
+  struct AlertInfo
+  {
+      nsCOMPtr<nsIObserver> listener;
+      nsString cookie;
+  };
+
+  using AlertInfoMap = nsClassHashtable<nsStringHashKey, AlertInfo>;
+  static StaticAutoPtr<AlertInfoMap> sAlertInfoMap;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // nsAndroidAlerts_h__
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -48,16 +48,17 @@
 #include "nsIURI.h"
 #include "IHistory.h"
 #endif
 
 #ifdef MOZ_LOGGING
 #include "mozilla/Logging.h"
 #endif
 
+#include "AndroidAlerts.h"
 #include "ANRReporter.h"
 #include "GeckoNetworkManager.h"
 #include "GeckoScreenOrientation.h"
 #include "PrefsHelper.h"
 #include "Telemetry.h"
 #include "ThumbnailHelper.h"
 
 #ifdef DEBUG_ANDROID_EVENTS
@@ -317,16 +318,27 @@ public:
         nsCOMPtr<nsIURI> visitedURI;
         if (history &&
             NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI),
                                    aUri->ToString()))) {
             history->NotifyVisited(visitedURI);
         }
 #endif
     }
+
+    static void NotifyAlertListener(jni::String::Param aName,
+                                    jni::String::Param aTopic)
+    {
+        if (!aName || !aTopic) {
+            return;
+        }
+
+        AndroidAlerts::NotifyListener(
+                aName->ToString(), aTopic->ToCString().get());
+    }
 };
 
 nsAppShell::nsAppShell()
     : mSyncRunFinished(*(sAppShellLock = new Mutex("nsAppShell")),
                        "nsAppShell.SyncRun")
     , mSyncRunQuit(false)
 {
     {