Bug 1114554 - Patch 4 - ServiceWorkerRegistration.getNotifications() on main thread. r=wchen

This commit is contained in:
Nikhil Marathe 2015-06-25 18:50:25 -07:00
parent a191f384d8
commit 79c4d29dde
6 changed files with 95 additions and 38 deletions

View File

@ -4,7 +4,7 @@
#include "domstubs.idl" #include "domstubs.idl"
[scriptable, uuid(9f1c43b9-f01b-4c87-ad3d-1a86520c2159)] [scriptable, uuid(c1622232-259c-43b0-b52e-89c39dcd9796)]
interface nsINotificationStorageCallback : nsISupports interface nsINotificationStorageCallback : nsISupports
{ {
/** /**
@ -28,7 +28,8 @@ interface nsINotificationStorageCallback : nsISupports
in DOMString tag, in DOMString tag,
in DOMString icon, in DOMString icon,
in DOMString data, in DOMString data,
in DOMString behavior); in DOMString behavior,
in DOMString serviceWorkerRegistrationID);
/** /**
* Callback function used to notify C++ the we have returned * Callback function used to notify C++ the we have returned
@ -41,7 +42,7 @@ interface nsINotificationStorageCallback : nsISupports
/** /**
* Interface for notification persistence layer. * Interface for notification persistence layer.
*/ */
[scriptable, uuid(2f8f84b7-70b5-4673-98d8-fd3f9f8e0e5c)] [scriptable, uuid(17f85e52-fe57-440e-9ba1-5c312ca02b95)]
interface nsINotificationStorage : nsISupports interface nsINotificationStorage : nsISupports
{ {
@ -61,6 +62,10 @@ interface nsINotificationStorage : nsISupports
* Stored in the database to avoid re-computing * Stored in the database to avoid re-computing
* it. Built from origin and tag or id depending * it. Built from origin and tag or id depending
* whether there is a tag defined. * 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, void put(in DOMString origin,
in DOMString id, in DOMString id,
@ -72,7 +77,8 @@ interface nsINotificationStorage : nsISupports
in DOMString icon, in DOMString icon,
in DOMString alertName, in DOMString alertName,
in DOMString data, in DOMString data,
in DOMString behavior); in DOMString behavior,
in DOMString serviceWorkerRegistrationID);
/** /**
* Retrieve a list of notifications. * Retrieve a list of notifications.

View File

@ -56,17 +56,21 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback) 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), : mCount(0),
mGlobal(aGlobal.Get()),
mWindow(aWindow), mWindow(aWindow),
mPromise(aPromise) mPromise(aPromise),
mScope(aScope)
{ {
AssertIsOnMainThread();
MOZ_ASSERT(aWindow); MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise); MOZ_ASSERT(aPromise);
JSContext* cx = aGlobal.Context(); AutoJSAPI jsapi;
JSAutoCompartment ac(cx, mGlobal); DebugOnly<bool> ok = jsapi.Init(aWindow);
mNotifications = JS_NewArrayObject(cx, 0); MOZ_ASSERT(ok);
// Created in the compartment of the window.
mNotifications = JS_NewArrayObject(jsapi.cx(), 0);
HoldData(); HoldData();
} }
@ -79,10 +83,17 @@ public:
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData, const nsAString& aData,
const nsAString& aBehavior, const nsAString& aBehavior,
const nsAString& aServiceWorkerRegistrationID,
JSContext* aCx) override JSContext* aCx) override
{ {
AssertIsOnMainThread();
MOZ_ASSERT(!aID.IsEmpty()); 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); RootedDictionary<NotificationOptions> options(aCx);
options.mDir = Notification::StringToDirection(nsString(aDir)); options.mDir = Notification::StringToDirection(nsString(aDir));
options.mLang = aLang; options.mLang = aLang;
@ -91,7 +102,6 @@ public:
options.mIcon = aIcon; options.mIcon = aIcon;
options.mMozbehavior.Init(aBehavior); options.mMozbehavior.Init(aBehavior);
nsRefPtr<Notification> notification; nsRefPtr<Notification> notification;
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
notification = Notification::CreateInternal(aID, notification = Notification::CreateInternal(aID,
aTitle, aTitle,
options); options);
@ -102,10 +112,14 @@ public:
return rv.StealNSResult(); return rv.StealNSResult();
} }
notification->SetScope(aServiceWorkerRegistrationID);
notification->SetStoredState(true); notification->SetStoredState(true);
JSAutoCompartment ac(aCx, mGlobal); AutoJSAPI jsapi;
JS::Rooted<JSObject*> element(aCx, notification->WrapObject(aCx, nullptr)); DebugOnly<bool> ok = jsapi.Init(mWindow, aCx);
MOZ_ASSERT(ok);
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
JS::Rooted<JSObject*> notifications(aCx, mNotifications); JS::Rooted<JSObject*> notifications(aCx, mNotifications);
@ -117,7 +131,10 @@ public:
NS_IMETHOD Done(JSContext* aCx) override 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)); JS::Rooted<JS::Value> result(aCx, JS::ObjectValue(*mNotifications));
mPromise->MaybeResolve(aCx, result); mPromise->MaybeResolve(aCx, result);
return NS_OK; return NS_OK;
@ -136,16 +153,15 @@ private:
void DropData() void DropData()
{ {
mGlobal = nullptr;
mNotifications = nullptr; mNotifications = nullptr;
mozilla::DropJSObjects(this); mozilla::DropJSObjects(this);
} }
uint32_t mCount; uint32_t mCount;
JS::Heap<JSObject *> mGlobal; nsCOMPtr<nsIGlobalObject> mWindow;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<Promise> mPromise; nsRefPtr<Promise> mPromise;
JS::Heap<JSObject *> mNotifications; JS::Heap<JSObject *> mNotifications;
const nsString mScope;
}; };
NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback) NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
@ -158,7 +174,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(NotificationStorageCallback) 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_JS_MEMBER_CALLBACK(mNotifications)
NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRACE_END
@ -750,6 +765,7 @@ Notification::ConstructFromFields(
const nsAString& aTag, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData, const nsAString& aData,
const nsAString& aScope,
ErrorResult& aRv) ErrorResult& aRv)
{ {
MOZ_ASSERT(aGlobal); MOZ_ASSERT(aGlobal);
@ -778,6 +794,8 @@ Notification::ConstructFromFields(
return nullptr; return nullptr;
} }
notification->SetScope(aScope);
return notification.forget(); return notification.forget();
} }
@ -824,7 +842,8 @@ Notification::PersistNotification()
mIconUrl, mIconUrl,
alertName, alertName,
dataString, dataString,
behavior); behavior,
mScope);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -1206,9 +1225,11 @@ public:
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData, const nsAString& aData,
const nsAString& aBehavior, const nsAString& aBehavior,
const nsAString& aServiceWorkerRegistrationID,
JSContext* aCx) override JSContext* aCx) override
{ {
MOZ_ASSERT(!aID.IsEmpty()); MOZ_ASSERT(!aID.IsEmpty());
MOZ_ASSERT(mScope.Equals(aServiceWorkerRegistrationID));
AssertIsOnMainThread(); AssertIsOnMainThread();
@ -1621,18 +1642,14 @@ Notification::ResolveIconAndSoundURL(nsString& iconUrl, nsString& soundUrl)
} }
already_AddRefed<Promise> already_AddRefed<Promise>
Notification::Get(const GlobalObject& aGlobal, Notification::Get(nsPIDOMWindow* aWindow,
const GetNotificationOptions& aFilter, const GetNotificationOptions& aFilter,
const nsAString& aScope,
ErrorResult& aRv) ErrorResult& aRv)
{ {
AssertIsOnMainThread(); MOZ_ASSERT(aWindow);
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
MOZ_ASSERT(window);
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
if (!doc) { if (!doc) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr; return nullptr;
@ -1644,13 +1661,14 @@ Notification::Get(const GlobalObject& aGlobal,
return nullptr; return nullptr;
} }
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
nsRefPtr<Promise> promise = Promise::Create(global, aRv); nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }
nsCOMPtr<nsINotificationStorageCallback> callback = nsCOMPtr<nsINotificationStorageCallback> callback =
new NotificationStorageCallback(aGlobal, window, promise); new NotificationStorageCallback(global, aScope, promise);
nsRefPtr<NotificationGetRunnable> r = nsRefPtr<NotificationGetRunnable> r =
new NotificationGetRunnable(origin, aFilter.mTag, callback); new NotificationGetRunnable(origin, aFilter.mTag, callback);
@ -1663,6 +1681,20 @@ Notification::Get(const GlobalObject& aGlobal,
return promise.forget(); 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* JSObject*
Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{ {

View File

@ -157,6 +157,7 @@ public:
const nsAString& aTag, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData, const nsAString& aData,
const nsAString& aScope,
ErrorResult& aRv); ErrorResult& aRv);
void GetID(nsAString& aRetval) { void GetID(nsAString& aRetval) {
@ -214,6 +215,12 @@ public:
static NotificationPermission GetPermission(const GlobalObject& aGlobal, static NotificationPermission GetPermission(const GlobalObject& aGlobal,
ErrorResult& aRv); 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, static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
const GetNotificationOptions& aFilter, const GetNotificationOptions& aFilter,
ErrorResult& aRv); ErrorResult& aRv);

View File

@ -84,7 +84,7 @@ NotificationStorage.prototype = {
}, },
put: function(origin, id, title, dir, lang, body, tag, icon, alertName, put: function(origin, id, title, dir, lang, body, tag, icon, alertName,
data, behavior) { data, behavior, serviceWorkerRegistrationID) {
if (DEBUG) { debug("PUT: " + origin + " " + id + ": " + title); } if (DEBUG) { debug("PUT: " + origin + " " + id + ": " + title); }
var notification = { var notification = {
id: id, id: id,
@ -98,7 +98,8 @@ NotificationStorage.prototype = {
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
origin: origin, origin: origin,
data: data, data: data,
mozbehavior: behavior mozbehavior: behavior,
serviceWorkerRegistrationID: serviceWorkerRegistrationID,
}; };
this._notifications[id] = notification; this._notifications[id] = notification;
@ -140,9 +141,9 @@ NotificationStorage.prototype = {
this.searchID = id; this.searchID = id;
this.originalCallback = originalCallback; this.originalCallback = originalCallback;
var self = this; 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) { 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() { this.done = function() {
@ -244,7 +245,8 @@ NotificationStorage.prototype = {
notification.tag, notification.tag,
notification.icon, notification.icon,
notification.data, notification.data,
notification.mozbehavior), notification.mozbehavior,
notification.serviceWorkerRegistrationID),
Ci.nsIThread.DISPATCH_NORMAL); Ci.nsIThread.DISPATCH_NORMAL);
} catch (e) { } catch (e) {
if (DEBUG) { debug("Error calling callback handle: " + e); } if (DEBUG) { debug("Error calling callback handle: " + e); }

View File

@ -2295,6 +2295,7 @@ class SendNotificationClickEventRunnable final : public WorkerRunnable
const nsString mIcon; const nsString mIcon;
const nsString mData; const nsString mData;
const nsString mBehavior; const nsString mBehavior;
const nsString mScope;
public: public:
SendNotificationClickEventRunnable( SendNotificationClickEventRunnable(
@ -2308,7 +2309,8 @@ public:
const nsAString& aTag, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aIcon,
const nsAString& aData, const nsAString& aData,
const nsAString& aBehavior) const nsAString& aBehavior,
const nsAString& aScope)
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
, mServiceWorker(aServiceWorker) , mServiceWorker(aServiceWorker)
, mID(aID) , mID(aID)
@ -2320,6 +2322,7 @@ public:
, mIcon(aIcon) , mIcon(aIcon)
, mData(aData) , mData(aData)
, mBehavior(aBehavior) , mBehavior(aBehavior)
, mScope(aScope)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate); MOZ_ASSERT(aWorkerPrivate);
@ -2335,7 +2338,9 @@ public:
ErrorResult result; ErrorResult result;
nsRefPtr<Notification> notification = 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())) { if (NS_WARN_IF(result.Failed())) {
return false; return false;
} }
@ -2383,7 +2388,11 @@ ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix
new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
nsRefPtr<SendNotificationClickEventRunnable> r = 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; AutoJSAPI jsapi;
jsapi.Init(); jsapi.Init();

View File

@ -637,8 +637,9 @@ ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
already_AddRefed<Promise> already_AddRefed<Promise>
ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv) ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
{ {
MOZ_ASSERT(false); AssertIsOnMainThread();
return nullptr; nsCOMPtr<nsPIDOMWindow> window = GetOwner();
return Notification::Get(window, aOptions, mScope, aRv);
} }
already_AddRefed<PushManager> already_AddRefed<PushManager>