Bug 1114554 - Patch 5 - getNotifications() on worker thread. r=wchen

--HG--
extra : rebase_source : b3f68e725be5cbfedc2995c9b20450e4bcaa1aae
This commit is contained in:
Nikhil Marathe 2015-05-04 09:04:25 -04:00
Родитель 54e0ea7180
Коммит 811edc788a
4 изменённых файлов: 316 добавлений и 108 удалений

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/OwningNonNull.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/Move.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
@ -50,29 +51,29 @@ namespace dom {
using namespace workers;
class NotificationStorageCallback final : public nsINotificationStorageCallback
struct NotificationStrings
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(NotificationStorageCallback)
const nsString mID;
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 mServiceWorkerRegistrationID;
};
NotificationStorageCallback(nsIGlobalObject* aWindow, const nsAString& aScope,
Promise* aPromise)
: mCount(0),
mWindow(aWindow),
mPromise(aPromise),
mScope(aScope)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
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();
}
class ScopeCheckingGetCallback : public nsINotificationStorageCallback
{
const nsString mScope;
public:
NS_DECL_ISUPPORTS
explicit ScopeCheckingGetCallback(const nsAString& aScope)
: mScope(aScope)
{}
NS_IMETHOD Handle(const nsAString& aID,
const nsAString& aTitle,
@ -84,7 +85,7 @@ public:
const nsAString& aData,
const nsAString& aBehavior,
const nsAString& aServiceWorkerRegistrationID,
JSContext* aCx) override
JSContext* aCx) final
{
AssertIsOnMainThread();
MOZ_ASSERT(!aID.IsEmpty());
@ -94,100 +95,102 @@ public:
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;
notification = Notification::CreateInternal(aID,
aTitle,
options);
notification->BindToOwner(mWindow);
ErrorResult rv;
notification->InitFromBase64(aCx, aData, rv);
if (rv.Failed()) {
return rv.StealNSResult();
}
NotificationStrings strings = {
nsString(aID),
nsString(aTitle),
nsString(aDir),
nsString(aLang),
nsString(aBody),
nsString(aTag),
nsString(aIcon),
nsString(aData),
nsString(aBehavior),
nsString(aServiceWorkerRegistrationID),
};
notification->SetScope(aServiceWorkerRegistrationID);
notification->SetStoredState(true);
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;
}
mStrings.AppendElement(Move(strings));
return NS_OK;
}
NS_IMETHOD Done(JSContext* aCx) override
NS_IMETHOD Done(JSContext* aCx) override = 0;
protected:
virtual ~ScopeCheckingGetCallback()
{}
nsTArray<NotificationStrings> mStrings;
};
NS_IMPL_ISUPPORTS(ScopeCheckingGetCallback, nsINotificationStorageCallback)
class NotificationStorageCallback final : public ScopeCheckingGetCallback
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
NotificationStorageCallback(nsIGlobalObject* aWindow, const nsAString& aScope,
Promise* aPromise)
: ScopeCheckingGetCallback(aScope),
mWindow(aWindow),
mPromise(aPromise)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
}
NS_IMETHOD Done(JSContext* aCx) final
{
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);
ErrorResult result;
nsAutoTArray<nsRefPtr<Notification>, 5> notifications;
for (uint32_t i = 0; i < mStrings.Length(); ++i) {
nsRefPtr<Notification> n =
Notification::ConstructFromFields(mWindow,
mStrings[i].mID,
mStrings[i].mTitle,
mStrings[i].mDir,
mStrings[i].mLang,
mStrings[i].mBody,
mStrings[i].mTag,
mStrings[i].mIcon,
mStrings[i].mData,
/* mStrings[i].mBehavior, not
* supported */
mStrings[i].mServiceWorkerRegistrationID,
result);
n->SetStoredState(true);
unused << NS_WARN_IF(result.Failed());
if (!result.Failed()) {
notifications.AppendElement(n.forget());
}
}
mPromise->MaybeResolve(notifications);
return NS_OK;
}
private:
virtual ~NotificationStorageCallback()
{
DropData();
}
{}
void HoldData()
{
mozilla::HoldJSObjects(this);
}
void DropData()
{
mNotifications = nullptr;
mozilla::DropJSObjects(this);
}
uint32_t mCount;
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_IMPL_CYCLE_COLLECTION(NotificationStorageCallback, mWindow, mPromise);
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(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
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationStorageCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
tmp->DropData();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_END_INHERITING(ScopeCheckingGetCallback)
class NotificationGetRunnable final : public nsRunnable
{
@ -369,7 +372,7 @@ protected:
{
aWorkerPrivate->AssertIsOnWorkerThread();
aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true);
WorkerRunInternal();
WorkerRunInternal(aCx, aWorkerPrivate);
return true;
}
@ -381,7 +384,7 @@ protected:
}
virtual void
WorkerRunInternal() = 0;
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
};
// Overrides dispatch and run handlers so we can directly dispatch from main
@ -399,7 +402,7 @@ public:
{}
void
WorkerRunInternal() override
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
mNotification->DispatchTrustedEvent(mEventName);
}
@ -707,6 +710,10 @@ void
Notification::SetAlertName()
{
AssertIsOnMainThread();
if (!mAlertName.IsEmpty()) {
return;
}
nsAutoString alertName;
DebugOnly<nsresult> rv = GetOrigin(GetPrincipal(), alertName);
MOZ_ASSERT(NS_SUCCEEDED(rv), "GetOrigin should not have failed");
@ -743,7 +750,7 @@ Notification::Constructor(const GlobalObject& aGlobal,
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
nsRefPtr<Notification> notification =
CreateAndShow(global, aTitle, aOptions, aRv);
CreateAndShow(global, aTitle, aOptions, EmptyString(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -765,7 +772,7 @@ Notification::ConstructFromFields(
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aScope,
const nsAString& aServiceWorkerRegistrationID,
ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
@ -794,7 +801,7 @@ Notification::ConstructFromFields(
return nullptr;
}
notification->SetScope(aScope);
notification->SetScope(aServiceWorkerRegistrationID);
return notification.forget();
}
@ -990,7 +997,7 @@ protected:
{}
void
WorkerRunInternal() override
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
UniquePtr<NotificationRef> ref;
mozilla::Swap(ref, mNotificationRef);
@ -1101,7 +1108,7 @@ public:
}
void
WorkerRunInternal() override
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
bool doDefaultAction = mNotification->DispatchClickEvent();
MOZ_ASSERT_IF(mWorkerPrivate->IsServiceWorker(), !doDefaultAction);
@ -1695,6 +1702,198 @@ Notification::Get(const GlobalObject& aGlobal,
return Get(window, aFilter, EmptyString(), aRv);
}
class WorkerGetResultRunnable final : public NotificationWorkerRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
const nsTArray<NotificationStrings> mStrings;
public:
WorkerGetResultRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aPromiseProxy,
const nsTArray<NotificationStrings>&& aStrings)
: NotificationWorkerRunnable(aWorkerPrivate)
, mPromiseProxy(aPromiseProxy)
, mStrings(Move(aStrings))
{
}
void
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
nsRefPtr<Promise> workerPromise = mPromiseProxy->GetWorkerPromise();
ErrorResult result;
nsAutoTArray<nsRefPtr<Notification>, 5> notifications;
for (uint32_t i = 0; i < mStrings.Length(); ++i) {
nsRefPtr<Notification> n =
Notification::ConstructFromFields(aWorkerPrivate->GlobalScope(),
mStrings[i].mID,
mStrings[i].mTitle,
mStrings[i].mDir,
mStrings[i].mLang,
mStrings[i].mBody,
mStrings[i].mTag,
mStrings[i].mIcon,
mStrings[i].mData,
/* mStrings[i].mBehavior, not
* supported */
mStrings[i].mServiceWorkerRegistrationID,
result);
n->SetStoredState(true);
unused << NS_WARN_IF(result.Failed());
if (!result.Failed()) {
notifications.AppendElement(n.forget());
}
}
workerPromise->MaybeResolve(notifications);
mPromiseProxy->CleanUp(aCx);
}
};
class WorkerGetCallback final : public ScopeCheckingGetCallback
{
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
public:
NS_DECL_ISUPPORTS_INHERITED
WorkerGetCallback(PromiseWorkerProxy* aProxy, const nsAString& aScope)
: ScopeCheckingGetCallback(aScope), mPromiseProxy(aProxy)
{
AssertIsOnMainThread();
MOZ_ASSERT(aProxy);
}
NS_IMETHOD Done(JSContext* aCx) final
{
AssertIsOnMainThread();
MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
return NS_OK;
}
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate());
nsRefPtr<WorkerGetResultRunnable> r =
new WorkerGetResultRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy,
Move(mStrings));
if (!r->Dispatch(aCx)) {
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(mPromiseProxy->GetWorkerPrivate(),
mPromiseProxy);
DebugOnly<bool> ok = cr->Dispatch(aCx);
MOZ_ASSERT(ok);
}
mPromiseProxy = nullptr;
return NS_OK;
}
private:
~WorkerGetCallback()
{}
};
NS_IMPL_ISUPPORTS_INHERITED0(WorkerGetCallback, ScopeCheckingGetCallback)
class WorkerGetRunnable final : public nsRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
const nsString mTag;
const nsString mScope;
public:
WorkerGetRunnable(WorkerPrivate* aWorkerPrivate,
Promise* aWorkerPromise,
const nsAString& aTag,
const nsAString& aScope)
: mTag(aTag), mScope(aScope)
{
aWorkerPrivate->AssertIsOnWorkerThread();
mPromiseProxy =
PromiseWorkerProxy::Create(aWorkerPrivate,
aWorkerPromise);
if (!mPromiseProxy || !mPromiseProxy->GetWorkerPromise()) {
aWorkerPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
mPromiseProxy = nullptr;
}
}
NS_IMETHOD
Run() override
{
AssertIsOnMainThread();
if (!mPromiseProxy) {
return NS_OK;
}
nsCOMPtr<nsINotificationStorageCallback> callback =
new WorkerGetCallback(mPromiseProxy, mScope);
AutoJSAPI jsapi;
jsapi.Init();
nsresult rv;
nsCOMPtr<nsINotificationStorage> notificationStorage =
do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done(jsapi.cx());
return rv;
}
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
if (mPromiseProxy->IsClean()) {
return NS_OK;
}
nsString origin;
rv =
Notification::GetOrigin(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done(jsapi.cx());
return rv;
}
rv = notificationStorage->Get(origin, mTag, callback);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done(jsapi.cx());
return rv;
}
return NS_OK;
}
private:
~WorkerGetRunnable()
{}
};
already_AddRefed<Promise>
Notification::WorkerGet(WorkerPrivate* aWorkerPrivate,
const GetNotificationOptions& aFilter,
const nsAString& aScope,
ErrorResult& aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> p = Promise::Create(aWorkerPrivate->GlobalScope(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsRefPtr<WorkerGetRunnable> r =
new WorkerGetRunnable(aWorkerPrivate, p, aFilter.mTag, aScope);
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
return p.forget();
}
JSObject*
Notification::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
@ -1724,12 +1923,14 @@ Notification::Close()
void
Notification::CloseInternal()
{
AssertIsOnMainThread();
// Transfer ownership (if any) to local scope so we can release it at the end
// of this function. This is relevant when the call is from
// NotificationTask::Run().
UniquePtr<NotificationRef> ownership;
mozilla::Swap(ownership, mTempRef);
SetAlertName();
UnpersistNotification();
if (!mIsClosed) {
nsCOMPtr<nsIAlertsService> alertService =
@ -2052,13 +2253,11 @@ Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal,
p->MaybeResolve(JS::UndefinedHandleValue);
nsRefPtr<Notification> notification =
CreateAndShow(aGlobal, aTitle, aOptions, aRv);
CreateAndShow(aGlobal, aTitle, aOptions, aScope, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
notification->SetScope(aScope);
return p.forget();
}
@ -2066,6 +2265,7 @@ Notification::ShowPersistentNotification(nsIGlobalObject *aGlobal,
Notification::CreateAndShow(nsIGlobalObject* aGlobal,
const nsAString& aTitle,
const NotificationOptions& aOptions,
const nsAString& aScope,
ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
@ -2087,6 +2287,8 @@ Notification::CreateAndShow(nsIGlobalObject* aGlobal,
return nullptr;
}
notification->SetScope(aScope);
auto ref = MakeUnique<NotificationRef>(notification);
if (!ref->Initialized()) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);

Просмотреть файл

@ -116,6 +116,7 @@ class Notification : public DOMEventTargetHelper
friend class NotificationObserver;
friend class NotificationStorageCallback;
friend class ServiceWorkerNotificationObserver;
friend class WorkerGetRunnable;
friend class WorkerNotificationObserver;
public:
@ -157,7 +158,7 @@ public:
const nsAString& aTag,
const nsAString& aIcon,
const nsAString& aData,
const nsAString& aScope,
const nsAString& aServiceWorkerRegistrationID,
ErrorResult& aRv);
void GetID(nsAString& aRetval) {
@ -225,6 +226,11 @@ public:
const GetNotificationOptions& aFilter,
ErrorResult& aRv);
static already_AddRefed<Promise> WorkerGet(workers::WorkerPrivate* aWorkerPrivate,
const GetNotificationOptions& aFilter,
const nsAString& aScope,
ErrorResult& aRv);
// Notification implementation of
// ServiceWorkerRegistration.showNotification.
static already_AddRefed<Promise>
@ -384,6 +390,7 @@ private:
CreateAndShow(nsIGlobalObject* aGlobal,
const nsAString& aTitle,
const NotificationOptions& aOptions,
const nsAString& aScope,
ErrorResult& aRv);
nsIPrincipal* GetPrincipal();

Просмотреть файл

@ -1611,9 +1611,9 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx,
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mCleanUpLock);
MutexAutoLock lock(GetCleanUpLock());
// If the worker thread's been cancelled we don't need to resolve the Promise.
if (mCleanedUp) {
if (IsClean()) {
return;
}

Просмотреть файл

@ -1063,8 +1063,7 @@ ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
already_AddRefed<Promise>
ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
{
MOZ_ASSERT(false);
return nullptr;
return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv);
}
} // dom namespace