Bug 1224771 - Close all web notifications when the originating tab is closed. r=wchen
--- 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)