Bug 1224771 - Close all web notifications when the originating tab is closed. r=wchen
☠☠ backed out by c342816f76c0 ☠ ☠
authorKit Cambridge <kcambridge@mozilla.com>
Tue, 29 Dec 2015 15:06:28 -0700
changeset 319132 df94eb632b9c65d4e7056b7a461514a109dd6809
parent 319131 3c7d6b80345e197eea1d13bc32ebf3439bbc89fb
child 319133 695b6cd72017f040d4d9da5ddd2a96c70916320c
push id8984
push userahunt@mozilla.com
push dateTue, 05 Jan 2016 23:06:12 +0000
reviewerswchen
bugs1224771
milestone46.0a1
Bug 1224771 - Close all web notifications when the originating tab is closed. r=wchen
dom/notification/Notification.cpp
dom/notification/Notification.h
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -976,16 +976,33 @@ Notification::Notification(nsIGlobalObje
     // fetch the atom for the event name which asserts main thread only.
     BindToOwner(aGlobal);
   } else {
     mWorkerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(mWorkerPrivate);
   }
 }
 
+nsresult
+Notification::Init()
+{
+  if (!mWorkerPrivate) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
+
+    nsresult rv = obs->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = obs->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
 void
 Notification::SetAlertName()
 {
   AssertIsOnMainThread();
   if (!mAlertName.IsEmpty()) {
     return;
   }
 
@@ -1145,40 +1162,43 @@ Notification::UnpersistNotification()
 }
 
 already_AddRefed<Notification>
 Notification::CreateInternal(nsIGlobalObject* aGlobal,
                              const nsAString& aID,
                              const nsAString& aTitle,
                              const NotificationOptions& aOptions)
 {
+  nsresult rv;
   nsString id;
   if (!aID.IsEmpty()) {
     id = aID;
   } else {
     nsCOMPtr<nsIUUIDGenerator> uuidgen =
       do_GetService("@mozilla.org/uuid-generator;1");
     NS_ENSURE_TRUE(uuidgen, nullptr);
     nsID uuid;
-    nsresult rv = uuidgen->GenerateUUIDInPlace(&uuid);
+    rv = uuidgen->GenerateUUIDInPlace(&uuid);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     char buffer[NSID_LENGTH];
     uuid.ToProvidedString(buffer);
     NS_ConvertASCIItoUTF16 convertedID(buffer);
     id = convertedID;
   }
 
   RefPtr<Notification> notification = new Notification(aGlobal, id, aTitle,
                                                          aOptions.mBody,
                                                          aOptions.mDir,
                                                          aOptions.mLang,
                                                          aOptions.mTag,
                                                          aOptions.mIcon,
                                                          aOptions.mMozbehavior);
+  rv = notification->Init();
+  NS_ENSURE_SUCCESS(rv, nullptr);
   return notification.forget();
 }
 
 Notification::~Notification()
 {
   mData.setUndefined();
   mozilla::DropJSObjects(this);
   AssertIsOnTargetThread();
@@ -1197,16 +1217,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 nsIPrincipal*
 Notification::GetPrincipal()
 {
   AssertIsOnMainThread();
   if (mWorkerPrivate) {
     return mWorkerPrivate->GetPrincipal();
@@ -2717,11 +2739,34 @@ Notification::OpenSettings(nsIPrincipal*
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
   // Notify other observers so they can show settings UI.
   obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+Notification::Observe(nsISupports* aSubject, const char* aTopic,
+                      const char16_t* aData)
+{
+  AssertIsOnMainThread();
+
+  if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) ||
+      !strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC)) {
+
+    if (SameCOMIdentity(aSubject, GetOwner())) {
+      nsCOMPtr<nsIObserverService> obs =
+        mozilla::services::GetObserverService();
+      if (obs) {
+        obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
+        obs->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
+      }
+      CloseInternal();
+    }
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -12,16 +12,17 @@
 #include "mozilla/dom/NotificationBinding.h"
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 
 #include "nsIObserver.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
+#include "nsWeakReference.h"
 
 #define NOTIFICATIONTELEMETRYSERVICE_CONTRACTID \
   "@mozilla.org/notificationTelemetryService;1"
 
 class nsIPrincipal;
 class nsIVariant;
 
 namespace mozilla {
@@ -127,16 +128,18 @@ private:
  * the Notification, the NotificationRef destructor will first try to release
  * the Notification by dispatching a normal runnable to the worker so that it is
  * queued after any event runnables. If that dispatch fails, it means the worker
  * is no longer running and queued WorkerRunnables will be canceled, so we
  * dispatch a control runnable instead.
  *
  */
 class Notification : public DOMEventTargetHelper
+                   , public nsIObserver
+                   , public nsSupportsWeakReference
 {
   friend class CloseNotificationRunnable;
   friend class NotificationTask;
   friend class NotificationPermissionRequest;
   friend class MainThreadNotificationObserver;
   friend class NotificationStorageCallback;
   friend class ServiceWorkerNotificationObserver;
   friend class WorkerGetRunnable;
@@ -146,16 +149,17 @@ class Notification : public DOMEventTarg
 public:
   IMPL_EVENT_HANDLER(click)
   IMPL_EVENT_HANDLER(show)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(close)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper)
+  NS_DECL_NSIOBSERVER
 
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
   // Returns if Notification.get() is allowed for the current global.
   static bool IsGetEnabled(JSContext* aCx, JSObject* aObj);
 
   static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
                                                     const nsAString& aTitle,
                                                     const NotificationOptions& aOption,
@@ -320,16 +324,17 @@ protected:
                const nsAString& aTag, const nsAString& aIconUrl,
                const NotificationBehavior& aBehavior);
 
   static already_AddRefed<Notification> CreateInternal(nsIGlobalObject* aGlobal,
                                                        const nsAString& aID,
                                                        const nsAString& aTitle,
                                                        const NotificationOptions& aOptions);
 
+  nsresult Init();
   bool IsInPrivateBrowsing();
   void ShowInternal();
   void CloseInternal();
 
   static NotificationPermission GetPermissionInternal(nsISupports* aGlobal,
                                                       ErrorResult& rv);
 
   static const nsString DirectionToString(NotificationDirection aDirection)