Bug 1114554 - Patch 4 - ServiceWorkerRegistration.getNotifications() on main thread. r=wchen
☠☠ backed out by 531f100d2bd8 ☠ ☠
authorNikhil Marathe <nsm.nikhil@gmail.com>
Mon, 27 Apr 2015 14:18:54 -0700
changeset 281049 f92abe5ec78447a2bcd9c06658dae0df914f68a5
parent 281048 75324b6862a8545eeeb1e1fddff80f93dc1c5754
child 281050 15bdf9c78e6e7b69199f1b6a61e43f558afae1ab
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen
bugs1114554
milestone41.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 1114554 - Patch 4 - ServiceWorkerRegistration.getNotifications() on main thread. r=wchen
dom/interfaces/notification/nsINotificationStorage.idl
dom/notification/Notification.cpp
dom/notification/Notification.h
dom/notification/NotificationStorage.js
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerRegistration.cpp
--- a/dom/interfaces/notification/nsINotificationStorage.idl
+++ b/dom/interfaces/notification/nsINotificationStorage.idl
@@ -1,15 +1,15 @@
 /* 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 "domstubs.idl"
 
-[scriptable, uuid(9f1c43b9-f01b-4c87-ad3d-1a86520c2159)]
+[scriptable, uuid(c1622232-259c-43b0-b52e-89c39dcd9796)]
 interface nsINotificationStorageCallback : nsISupports
 {
   /**
    * Callback function used to pass single notification back
    * into C++ land for Notification.get return data.
    *
    * @param id: a uuid for this notification
    * @param title: the notification title
@@ -23,30 +23,31 @@ interface nsINotificationStorageCallback
   void handle(in DOMString id,
               in DOMString title,
               in DOMString dir,
               in DOMString lang,
               in DOMString body,
               in DOMString tag,
               in DOMString icon,
               in DOMString data,
-              in DOMString behavior);
+              in DOMString behavior,
+              in DOMString serviceWorkerRegistrationID);
 
   /**
    * Callback function used to notify C++ the we have returned
    * all notification objects for this Notification.get call.
    */
   [implicit_jscontext]
   void done();
 };
 
 /**
  * Interface for notification persistence layer.
  */
-[scriptable, uuid(2f8f84b7-70b5-4673-98d8-fd3f9f8e0e5c)]
+[scriptable, uuid(17f85e52-fe57-440e-9ba1-5c312ca02b95)]
 interface nsINotificationStorage : nsISupports
 {
 
   /**
    * Add/replace a notification to the persistence layer.
    *
    * @param origin: the origin/app of this notification
    * @param id: a uuid for this notification
@@ -56,28 +57,33 @@ interface nsINotificationStorage : nsISu
    * @param lang: the notification language
    * @param body: the notification body
    * @param tag: notification tag, will replace any existing
    *             notifications with same origin/tag pair
    * @param alertName: the alert identifier as used by system app.
    *                   Stored in the database to avoid re-computing
    *                   it. Built from origin and tag or id depending
    *                   whether there is a tag defined.
+   * @param registrationID: Opaque string that identifies the service worker
+   *                        registration this Notification is associated with.
+   *                        May be empty. Only set for Notifications created by
+   *                        showNotification().
    */
   void put(in DOMString origin,
            in DOMString id,
            in DOMString title,
            in DOMString dir,
            in DOMString lang,
            in DOMString body,
            in DOMString tag,
            in DOMString icon,
            in DOMString alertName,
            in DOMString data,
-           in DOMString behavior);
+           in DOMString behavior,
+           in DOMString serviceWorkerRegistrationID);
 
   /**
    * Retrieve a list of notifications.
    *
    * @param origin: the origin/app for which to fetch notifications from
    * @param tag: used to fetch only a specific tag
    * @param callback: nsINotificationStorageCallback, used for
    *                  returning notifications objects
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -51,78 +51,95 @@ namespace dom {
 using namespace workers;
 
 class NotificationStorageCallback final : public nsINotificationStorageCallback
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
 
-  NotificationStorageCallback(const GlobalObject& aGlobal, nsPIDOMWindow* aWindow, Promise* aPromise)
+  NotificationStorageCallback(nsIGlobalObject* aWindow, const nsAString& aScope,
+                              Promise* aPromise)
     : mCount(0),
-      mGlobal(aGlobal.Get()),
       mWindow(aWindow),
-      mPromise(aPromise)
+      mPromise(aPromise),
+      mScope(aScope)
   {
+    AssertIsOnMainThread();
     MOZ_ASSERT(aWindow);
     MOZ_ASSERT(aPromise);
-    JSContext* cx = aGlobal.Context();
-    JSAutoCompartment ac(cx, mGlobal);
-    mNotifications = JS_NewArrayObject(cx, 0);
+    AutoJSAPI jsapi;
+    DebugOnly<bool> ok = jsapi.Init(aWindow);
+    MOZ_ASSERT(ok);
+    // Created in the compartment of the window.
+    mNotifications = JS_NewArrayObject(jsapi.cx(), 0);
     HoldData();
   }
 
   NS_IMETHOD Handle(const nsAString& aID,
                     const nsAString& aTitle,
                     const nsAString& aDir,
                     const nsAString& aLang,
                     const nsAString& aBody,
                     const nsAString& aTag,
                     const nsAString& aIcon,
                     const nsAString& aData,
                     const nsAString& aBehavior,
+                    const nsAString& aServiceWorkerRegistrationID,
                     JSContext* aCx) override
   {
+    AssertIsOnMainThread();
     MOZ_ASSERT(!aID.IsEmpty());
 
+    // Skip scopes that don't match when called from getNotifications().
+    if (!mScope.IsEmpty() && !mScope.Equals(aServiceWorkerRegistrationID)) {
+      return NS_OK;
+    }
+
     RootedDictionary<NotificationOptions> options(aCx);
     options.mDir = Notification::StringToDirection(nsString(aDir));
     options.mLang = aLang;
     options.mBody = aBody;
     options.mTag = aTag;
     options.mIcon = aIcon;
     options.mMozbehavior.Init(aBehavior);
     nsRefPtr<Notification> notification;
-    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
     notification = Notification::CreateInternal(aID,
                                                 aTitle,
                                                 options);
     notification->BindToOwner(mWindow);
     ErrorResult rv;
     notification->InitFromBase64(aCx, aData, rv);
     if (rv.Failed()) {
       return rv.StealNSResult();
     }
 
+    notification->SetScope(aServiceWorkerRegistrationID);
+
     notification->SetStoredState(true);
 
-    JSAutoCompartment ac(aCx, mGlobal);
-    JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx, nullptr));
+    AutoJSAPI jsapi;
+    DebugOnly<bool> ok = jsapi.Init(mWindow, aCx);
+    MOZ_ASSERT(ok);
+
     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
 
     JS::Rooted<JSObject*> notifications(aCx, mNotifications);
     if (!JS_DefineElement(aCx, notifications, mCount++, element, 0)) {
       return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
   NS_IMETHOD Done(JSContext* aCx) override
   {
-    JSAutoCompartment ac(aCx, mGlobal);
+    AutoJSAPI jsapi;
+    DebugOnly<bool> ok = jsapi.Init(mWindow, aCx);
+    MOZ_ASSERT(ok);
+
     JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications));
     mPromise->MaybeResolve(aCx, result);
     return NS_OK;
   }
 
 private:
   virtual ~NotificationStorageCallback()
   {
@@ -131,39 +148,37 @@ private:
 
   void HoldData()
   {
     mozilla::HoldJSObjects(this);
   }
 
   void DropData()
   {
-    mGlobal = nullptr;
     mNotifications = nullptr;
     mozilla::DropJSObjects(this);
   }
 
   uint32_t  mCount;
-  JS::Heap<JSObject *> mGlobal;
-  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIGlobalObject> mWindow;
   nsRefPtr<Promise> mPromise;
   JS::Heap<JSObject *> mNotifications;
+  const nsString mScope;
 };
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
 NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
   NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mNotifications)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationStorageCallback)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -745,16 +760,17 @@ Notification::ConstructFromFields(
     const nsAString& aID,
     const nsAString& aTitle,
     const nsAString& aDir,
     const nsAString& aLang,
     const nsAString& aBody,
     const nsAString& aTag,
     const nsAString& aIcon,
     const nsAString& aData,
+    const nsAString& aScope,
     ErrorResult& aRv)
 {
   MOZ_ASSERT(aGlobal);
 
   AutoJSAPI jsapi;
   DebugOnly<bool> ok = jsapi.Init(aGlobal);
   MOZ_ASSERT(ok);
 
@@ -773,16 +789,18 @@ Notification::ConstructFromFields(
     notification->BindToOwner(aGlobal);
   }
 
   notification->InitFromBase64(jsapi.cx(), aData, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  notification->SetScope(aScope);
+
   return notification.forget();
 }
 
 nsresult
 Notification::PersistNotification()
 {
   AssertIsOnMainThread();
   nsresult rv;
@@ -819,17 +837,18 @@ Notification::PersistNotification()
                                 mTitle,
                                 DirectionToString(mDir),
                                 mLang,
                                 mBody,
                                 mTag,
                                 mIconUrl,
                                 alertName,
                                 dataString,
-                                behavior);
+                                behavior,
+                                mScope);
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   SetStoredState(true);
   return NS_OK;
 }
@@ -1201,19 +1220,21 @@ public:
                     const nsAString& aTitle,
                     const nsAString& aDir,
                     const nsAString& aLang,
                     const nsAString& aBody,
                     const nsAString& aTag,
                     const nsAString& aIcon,
                     const nsAString& aData,
                     const nsAString& aBehavior,
+                    const nsAString& aServiceWorkerRegistrationID,
                     JSContext* aCx) override
   {
     MOZ_ASSERT(!aID.IsEmpty());
+    MOZ_ASSERT(mScope.Equals(aServiceWorkerRegistrationID));
 
     AssertIsOnMainThread();
 
     nsAutoCString originSuffix;
     nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -1616,58 +1637,69 @@ Notification::ResolveIconAndSoundURL(nsS
       }
     }
   }
 
   return rv;
 }
 
 already_AddRefed<Promise>
-Notification::Get(const GlobalObject& aGlobal,
+Notification::Get(nsPIDOMWindow* aWindow,
                   const GetNotificationOptions& aFilter,
+                  const nsAString& aScope,
                   ErrorResult& aRv)
 {
-  AssertIsOnMainThread();
-  nsCOMPtr<nsIGlobalObject> global =
-    do_QueryInterface(aGlobal.GetAsSupports());
-  MOZ_ASSERT(global);
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
-  MOZ_ASSERT(window);
+  MOZ_ASSERT(aWindow);
 
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (!doc) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsString origin;
   aRv = GetOrigin(doc->NodePrincipal(), origin);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   nsCOMPtr<nsINotificationStorageCallback> callback =
-    new NotificationStorageCallback(aGlobal, window, promise);
+    new NotificationStorageCallback(global, aScope, promise);
 
   nsRefPtr<NotificationGetRunnable> r =
     new NotificationGetRunnable(origin, aFilter.mTag, callback);
 
   aRv = NS_DispatchToMainThread(r);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
+already_AddRefed<Promise>
+Notification::Get(const GlobalObject& aGlobal,
+                  const GetNotificationOptions& aFilter,
+                  ErrorResult& aRv)
+{
+  AssertIsOnMainThread();
+  nsCOMPtr<nsIGlobalObject> global =
+    do_QueryInterface(aGlobal.GetAsSupports());
+  MOZ_ASSERT(global);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
+
+  return Get(window, aFilter, EmptyString(), aRv);
+}
+
 JSObject*
 Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::NotificationBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 Notification::Close()
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -152,16 +152,17 @@ public:
     const nsAString& aID,
     const nsAString& aTitle,
     const nsAString& aDir,
     const nsAString& aLang,
     const nsAString& aBody,
     const nsAString& aTag,
     const nsAString& aIcon,
     const nsAString& aData,
+    const nsAString& aScope,
     ErrorResult& aRv);
 
   void GetID(nsAString& aRetval) {
     aRetval = mID;
   }
 
   void GetTitle(nsAString& aRetval)
   {
@@ -209,16 +210,22 @@ public:
 
   static void RequestPermission(const GlobalObject& aGlobal,
                                 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
                                 ErrorResult& aRv);
 
   static NotificationPermission GetPermission(const GlobalObject& aGlobal,
                                               ErrorResult& aRv);
 
+  static already_AddRefed<Promise>
+  Get(nsPIDOMWindow* aWindow,
+      const GetNotificationOptions& aFilter,
+      const nsAString& aScope,
+      ErrorResult& aRv);
+
   static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
                                        const GetNotificationOptions& aFilter,
                                        ErrorResult& aRv);
 
   // Notification implementation of
   // ServiceWorkerRegistration.showNotification.
   static already_AddRefed<Promise>
   ShowPersistentNotification(nsIGlobalObject* aGlobal,
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -79,31 +79,32 @@ NotificationStorage.prototype = {
   canPut: function(aOrigin) {
     if (DEBUG) debug("Querying appService for: " + aOrigin);
     let rv = !!appsService.getAppByManifestURL(aOrigin);
     if (DEBUG) debug("appService returned: " + rv);
     return rv;
   },
 
   put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
-                data, behavior) {
+                data, behavior, serviceWorkerRegistrationID) {
     if (DEBUG) { debug("PUT: " + origin + " " + id + ": " + title); }
     var notification = {
       id: id,
       title: title,
       dir: dir,
       lang: lang,
       body: body,
       tag: tag,
       icon: icon,
       alertName: alertName,
       timestamp: new Date().getTime(),
       origin: origin,
       data: data,
-      mozbehavior: behavior
+      mozbehavior: behavior,
+      serviceWorkerRegistrationID: serviceWorkerRegistrationID,
     };
 
     this._notifications[id] = notification;
     if (tag) {
       if (!this._byTag[origin]) {
         this._byTag[origin] = {};
       }
 
@@ -135,19 +136,19 @@ NotificationStorage.prototype = {
   },
 
   getByID: function(origin, id, callback) {
     if (DEBUG) { debug("GETBYID: " + origin + " " + id); }
     var GetByIDProxyCallback = function(id, originalCallback) {
       this.searchID = id;
       this.originalCallback = originalCallback;
       var self = this;
-      this.handle = function(id, title, dir, lang, body, tag, icon, data, behavior) {
+      this.handle = function(id, title, dir, lang, body, tag, icon, data, behavior, serviceWorkerRegistrationID) {
         if (id == this.searchID) {
-          self.originalCallback.handle(id, title, dir, lang, body, tag, icon, data, behavior);
+          self.originalCallback.handle(id, title, dir, lang, body, tag, icon, data, behavior, serviceWorkerRegistrationID);
         }
       };
       this.done = function() {
         self.originalCallback.done();
       };
     };
 
     return this.get(origin, "", new GetByIDProxyCallback(id, callback));
@@ -239,17 +240,18 @@ NotificationStorage.prototype = {
                                notification.id,
                                notification.title,
                                notification.dir,
                                notification.lang,
                                notification.body,
                                notification.tag,
                                notification.icon,
                                notification.data,
-                               notification.mozbehavior),
+                               notification.mozbehavior,
+                               notification.serviceWorkerRegistrationID),
           Ci.nsIThread.DISPATCH_NORMAL);
       } catch (e) {
         if (DEBUG) { debug("Error calling callback handle: " + e); }
       }
     });
     try {
       Services.tm.currentThread.dispatch(callback.done,
                                          Ci.nsIThread.DISPATCH_NORMAL);
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2277,57 +2277,62 @@ class SendNotificationClickEventRunnable
   const nsString mTitle;
   const nsString mDir;
   const nsString mLang;
   const nsString mBody;
   const nsString mTag;
   const nsString mIcon;
   const nsString mData;
   const nsString mBehavior;
+  const nsString mScope;
 
 public:
   SendNotificationClickEventRunnable(
     WorkerPrivate* aWorkerPrivate,
     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
     const nsAString& aID,
     const nsAString& aTitle,
     const nsAString& aDir,
     const nsAString& aLang,
     const nsAString& aBody,
     const nsAString& aTag,
     const nsAString& aIcon,
     const nsAString& aData,
-    const nsAString& aBehavior)
+    const nsAString& aBehavior,
+    const nsAString& aScope)
       : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
       , mServiceWorker(aServiceWorker)
       , mID(aID)
       , mTitle(aTitle)
       , mDir(aDir)
       , mLang(aLang)
       , mBody(aBody)
       , mTag(aTag)
       , mIcon(aIcon)
       , mData(aData)
       , mBehavior(aBehavior)
+      , mScope(aScope)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
 
     nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
 
     ErrorResult result;
     nsRefPtr<Notification> notification =
-      Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID, mTitle, mDir, mLang, mBody, mTag, mIcon, mData, result);
+      Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(), mID,
+                                        mTitle, mDir, mLang, mBody, mTag, mIcon,
+                                        mData, mScope, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
     NotificationEventInit nei;
     nei.mNotification = notification;
     nei.mBubbles = false;
     nei.mCancelable = true;
@@ -2365,17 +2370,21 @@ ServiceWorkerManager::SendNotificationCl
   nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(attrs, aScope);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendNotificationClickEventRunnable> r =
-    new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(), serviceWorkerHandle, aID, aTitle, aDir, aLang, aBody, aTag, aIcon, aData, aBehavior);
+    new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(),
+                                           serviceWorkerHandle, aID, aTitle,
+                                           aDir, aLang, aBody, aTag, aIcon,
+                                           aData, aBehavior,
+                                           NS_ConvertUTF8toUTF16(aScope));
 
   AutoJSAPI jsapi;
   jsapi.Init();
   if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -625,18 +625,19 @@ ServiceWorkerRegistrationMainThread::Sho
   }
 
   return p.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
-  MOZ_ASSERT(false);
-  return nullptr;
+  AssertIsOnMainThread();
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  return Notification::Get(window, aOptions, mScope, aRv);
 }
 
 already_AddRefed<PushManager>
 ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
 #ifdef MOZ_SIMPLEPUSH