зеркало из https://github.com/mozilla/gecko-dev.git
Bug 984048: Part 3 - Update algorithm. r=ehsan,khuey
--HG-- extra : rebase_source : 5e80b118faeea419cfebbc2d1060cd2a1a61bcba
This commit is contained in:
Родитель
92fe6235b6
Коммит
08d119358f
|
@ -11,20 +11,242 @@
|
||||||
|
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
#include "mozilla/dom/DOMError.h"
|
||||||
|
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsCxPusher.h"
|
#include "nsCxPusher.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
|
#include "nsProxyRelease.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
|
|
||||||
#include "RuntimeService.h"
|
#include "RuntimeService.h"
|
||||||
#include "ServiceWorker.h"
|
#include "ServiceWorker.h"
|
||||||
#include "WorkerInlines.h"
|
#include "WorkerInlines.h"
|
||||||
|
#include "WorkerPrivate.h"
|
||||||
|
#include "WorkerRunnable.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
BEGIN_WORKERS_NAMESPACE
|
BEGIN_WORKERS_NAMESPACE
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS0(ServiceWorkerRegistration)
|
||||||
|
|
||||||
|
UpdatePromise::UpdatePromise()
|
||||||
|
: mState(Pending)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(UpdatePromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePromise::~UpdatePromise()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(UpdatePromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UpdatePromise::AddPromise(Promise* aPromise)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mState == Pending);
|
||||||
|
mPromises.AppendElement(aPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UpdatePromise::ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
MOZ_ASSERT(mState == Pending);
|
||||||
|
mState = Resolved;
|
||||||
|
RuntimeService* rs = RuntimeService::GetOrCreateService();
|
||||||
|
MOZ_ASSERT(rs);
|
||||||
|
|
||||||
|
nsTArray<nsTWeakRef<Promise>> array;
|
||||||
|
array.SwapElements(mPromises);
|
||||||
|
for (uint32_t i = 0; i < array.Length(); ++i) {
|
||||||
|
nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
|
||||||
|
if (pendingPromise) {
|
||||||
|
nsCOMPtr<nsIGlobalObject> go =
|
||||||
|
do_QueryInterface(pendingPromise->GetParentObject());
|
||||||
|
MOZ_ASSERT(go);
|
||||||
|
|
||||||
|
AutoSafeJSContext cx;
|
||||||
|
JS::Rooted<JSObject*> global(cx, go->GetGlobalJSObject());
|
||||||
|
JSAutoCompartment ac(cx, global);
|
||||||
|
|
||||||
|
GlobalObject domGlobal(cx, global);
|
||||||
|
|
||||||
|
nsRefPtr<ServiceWorker> serviceWorker;
|
||||||
|
nsresult rv = rs->CreateServiceWorker(domGlobal,
|
||||||
|
NS_ConvertUTF8toUTF16(aScriptSpec),
|
||||||
|
aScope,
|
||||||
|
getter_AddRefs(serviceWorker));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
pendingPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPromise->MaybeResolve(serviceWorker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UpdatePromise::RejectAllPromises(nsresult aRv)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
MOZ_ASSERT(mState == Pending);
|
||||||
|
mState = Rejected;
|
||||||
|
|
||||||
|
nsTArray<nsTWeakRef<Promise>> array;
|
||||||
|
array.SwapElements(mPromises);
|
||||||
|
for (uint32_t i = 0; i < array.Length(); ++i) {
|
||||||
|
nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
|
||||||
|
if (pendingPromise) {
|
||||||
|
// Since ServiceWorkerContainer is only exposed to windows we can be
|
||||||
|
// certain about this cast.
|
||||||
|
nsCOMPtr<nsPIDOMWindow> window =
|
||||||
|
do_QueryInterface(pendingPromise->GetParentObject());
|
||||||
|
MOZ_ASSERT(window);
|
||||||
|
nsRefPtr<DOMError> domError = new DOMError(window, aRv);
|
||||||
|
pendingPromise->MaybeRejectBrokenly(domError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FinishFetchOnMainThreadRunnable : public nsRunnable
|
||||||
|
{
|
||||||
|
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||||
|
public:
|
||||||
|
FinishFetchOnMainThreadRunnable
|
||||||
|
(const nsMainThreadPtrHandle<ServiceWorkerUpdateInstance>& aUpdateInstance)
|
||||||
|
: mUpdateInstance(aUpdateInstance)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
Run() MOZ_OVERRIDE;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FinishSuccessfulFetchWorkerRunnable : public WorkerRunnable
|
||||||
|
{
|
||||||
|
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||||
|
public:
|
||||||
|
FinishSuccessfulFetchWorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
|
const nsMainThreadPtrHandle<ServiceWorkerUpdateInstance>& aUpdateInstance)
|
||||||
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
|
||||||
|
mUpdateInstance(aUpdateInstance)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||||
|
{
|
||||||
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
if (!aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<FinishFetchOnMainThreadRunnable> r =
|
||||||
|
new FinishFetchOnMainThreadRunnable(mUpdateInstance);
|
||||||
|
NS_DispatchToMainThread(r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allows newer calls to Update() to 'abort' older calls.
|
||||||
|
// Each call to Update() creates the instance which handles creating the
|
||||||
|
// worker and queues up a runnable to resolve the update promise once the
|
||||||
|
// worker has successfully been parsed.
|
||||||
|
class ServiceWorkerUpdateInstance MOZ_FINAL : public nsISupports
|
||||||
|
{
|
||||||
|
// Owner of this instance.
|
||||||
|
ServiceWorkerRegistration* mRegistration;
|
||||||
|
nsCString mScriptSpec;
|
||||||
|
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||||
|
|
||||||
|
bool mAborted;
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
|
ServiceWorkerUpdateInstance(ServiceWorkerRegistration *aRegistration,
|
||||||
|
nsPIDOMWindow* aWindow)
|
||||||
|
: mRegistration(aRegistration),
|
||||||
|
// Capture the current script spec in case register() gets called.
|
||||||
|
mScriptSpec(aRegistration->mScriptSpec),
|
||||||
|
mWindow(aWindow),
|
||||||
|
mAborted(false)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Abort()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mAborted);
|
||||||
|
mAborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Update()
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||||
|
MOZ_ASSERT(swm);
|
||||||
|
|
||||||
|
nsRefPtr<ServiceWorker> serviceWorker;
|
||||||
|
nsresult rv = swm->CreateServiceWorkerForWindow(mWindow,
|
||||||
|
mScriptSpec,
|
||||||
|
mRegistration->mScope,
|
||||||
|
getter_AddRefs(serviceWorker));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
swm->RejectUpdatePromiseObservers(mRegistration, rv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsMainThreadPtrHandle<ServiceWorkerUpdateInstance> handle =
|
||||||
|
new nsMainThreadPtrHolder<ServiceWorkerUpdateInstance>(this);
|
||||||
|
// FIXME(nsm): Deal with error case (worker failed to download, redirect,
|
||||||
|
// parse) in error handler patch.
|
||||||
|
nsRefPtr<FinishSuccessfulFetchWorkerRunnable> r =
|
||||||
|
new FinishSuccessfulFetchWorkerRunnable(serviceWorker->GetWorkerPrivate(), handle);
|
||||||
|
|
||||||
|
AutoSafeJSContext cx;
|
||||||
|
if (!r->Dispatch(cx)) {
|
||||||
|
swm->RejectUpdatePromiseObservers(mRegistration, NS_ERROR_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FetchDone()
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
if (mAborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||||
|
MOZ_ASSERT(swm);
|
||||||
|
swm->FinishFetch(mRegistration, mWindow);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS0(ServiceWorkerUpdateInstance)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
FinishFetchOnMainThreadRunnable::Run()
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
mUpdateInstance->FetchDone();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceWorkerRegistration::ServiceWorkerRegistration(const nsACString& aScope)
|
||||||
|
: mScope(aScope),
|
||||||
|
mPendingUninstall(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ServiceWorkerRegistration::~ServiceWorkerRegistration()
|
||||||
|
{ }
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// ServiceWorkerManager //
|
// ServiceWorkerManager //
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -89,13 +311,15 @@ public:
|
||||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||||
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
|
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
|
||||||
swm->mDomainMap.Get(domain);
|
swm->mDomainMap.Get(domain);
|
||||||
// FIXME(nsm): Refactor this pattern.
|
// XXXnsm: This pattern can be refactored if we end up using it
|
||||||
|
// often enough.
|
||||||
if (!swm->mDomainMap.Get(domain, &domainInfo)) {
|
if (!swm->mDomainMap.Get(domain, &domainInfo)) {
|
||||||
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
|
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
|
||||||
swm->mDomainMap.Put(domain, domainInfo);
|
swm->mDomainMap.Put(domain, domainInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRefPtr<ServiceWorkerRegistration> registration = domainInfo->GetRegistration(mScope);
|
nsRefPtr<ServiceWorkerRegistration> registration =
|
||||||
|
domainInfo->GetRegistration(mScope);
|
||||||
|
|
||||||
nsCString spec;
|
nsCString spec;
|
||||||
rv = mScriptURI->GetSpec(spec);
|
rv = mScriptURI->GetSpec(spec);
|
||||||
|
@ -107,9 +331,6 @@ public:
|
||||||
if (registration) {
|
if (registration) {
|
||||||
registration->mPendingUninstall = false;
|
registration->mPendingUninstall = false;
|
||||||
if (spec.Equals(registration->mScriptSpec)) {
|
if (spec.Equals(registration->mScriptSpec)) {
|
||||||
// FIXME(nsm): Force update on Shift+Reload. Algorithm not specified for
|
|
||||||
// that yet.
|
|
||||||
|
|
||||||
// There is an existing update in progress. Resolve with whatever it
|
// There is an existing update in progress. Resolve with whatever it
|
||||||
// results in.
|
// results in.
|
||||||
if (registration->HasUpdatePromise()) {
|
if (registration->HasUpdatePromise()) {
|
||||||
|
@ -117,8 +338,8 @@ public:
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is no update in progress and since SW updating is upto the UA, we
|
// There is no update in progress and since SW updating is upto the UA,
|
||||||
// will not update right now. Simply resolve with whatever worker we
|
// we will not update right now. Simply resolve with whatever worker we
|
||||||
// have.
|
// have.
|
||||||
ServiceWorkerInfo info = registration->Newest();
|
ServiceWorkerInfo info = registration->Newest();
|
||||||
if (info.IsValid()) {
|
if (info.IsValid()) {
|
||||||
|
@ -143,10 +364,16 @@ public:
|
||||||
|
|
||||||
registration->mScriptSpec = spec;
|
registration->mScriptSpec = spec;
|
||||||
|
|
||||||
// FIXME(nsm): Call Update. Same bug, different patch.
|
rv = swm->Update(registration, mWindow);
|
||||||
// For now if the registration reaches this spot, the promise remains
|
MOZ_ASSERT(registration->HasUpdatePromise());
|
||||||
// unresolved.
|
|
||||||
return NS_OK;
|
// We append this register() call's promise after calling Update() because
|
||||||
|
// we don't want this one to be aborted when the others (existing updates
|
||||||
|
// for the same registration) are aborted. Update() sets a new
|
||||||
|
// UpdatePromise on the registration.
|
||||||
|
registration->mUpdatePromise->AddPromise(mPromise);
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,7 +381,8 @@ public:
|
||||||
// automatically reject the Promise.
|
// automatically reject the Promise.
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||||
const nsAString& aScriptURL, nsISupports** aPromise)
|
const nsAString& aScriptURL,
|
||||||
|
nsISupports** aPromise)
|
||||||
{
|
{
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
MOZ_ASSERT(aWindow);
|
MOZ_ASSERT(aWindow);
|
||||||
|
@ -206,10 +434,9 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/slightlyoff/ServiceWorker/issues/262
|
// Data URLs are not allowed.
|
||||||
// allowIfInheritsPrincipal: allow data: URLs for now.
|
|
||||||
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
|
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
|
||||||
true /* allowIfInheritsPrincipal */);
|
false /* allowIfInheritsPrincipal */);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
@ -238,11 +465,51 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||||
return NS_DispatchToCurrentThread(registerRunnable);
|
return NS_DispatchToCurrentThread(registerRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ServiceWorkerManager::RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
|
||||||
|
nsresult aRv)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||||
|
aRegistration->mUpdatePromise->RejectAllPromises(aRv);
|
||||||
|
aRegistration->mUpdatePromise = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update() does not return the Promise that the spec says it should. Callers
|
||||||
|
* may access the registration's (new) Promise after calling this method.
|
||||||
|
*/
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
|
ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
|
||||||
nsPIDOMWindow* aWindow)
|
nsPIDOMWindow* aWindow)
|
||||||
{
|
{
|
||||||
// FIXME(nsm): Same bug, different patch.
|
if (aRegistration->HasUpdatePromise()) {
|
||||||
|
NS_WARNING("Already had a UpdatePromise. Aborting that one!");
|
||||||
|
RejectUpdatePromiseObservers(aRegistration, NS_ERROR_DOM_ABORT_ERR);
|
||||||
|
MOZ_ASSERT(aRegistration->mUpdateInstance);
|
||||||
|
aRegistration->mUpdateInstance->Abort();
|
||||||
|
aRegistration->mUpdateInstance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aRegistration->mInstallingWorker.IsValid()) {
|
||||||
|
// FIXME(nsm): Terminate the worker. We still haven't figured out worker
|
||||||
|
// instance ownership when not associated with a window, so let's wait on
|
||||||
|
// this.
|
||||||
|
// FIXME(nsm): We should be setting the state on the actual worker
|
||||||
|
// instance.
|
||||||
|
// FIXME(nsm): Fire "statechange" on installing worker instance.
|
||||||
|
aRegistration->mInstallingWorker.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
aRegistration->mUpdatePromise = new UpdatePromise();
|
||||||
|
// FIXME(nsm): Bug 931249. If we don't need to fetch & install, resolve
|
||||||
|
// promise and skip this.
|
||||||
|
// FIXME(nsm): Bug 931249. Force cache update if > 1 day.
|
||||||
|
|
||||||
|
aRegistration->mUpdateInstance =
|
||||||
|
new ServiceWorkerUpdateInstance(aRegistration, aWindow);
|
||||||
|
aRegistration->mUpdateInstance->Update();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,11 +533,69 @@ ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||||
already_AddRefed<ServiceWorkerManager>
|
already_AddRefed<ServiceWorkerManager>
|
||||||
ServiceWorkerManager::GetInstance()
|
ServiceWorkerManager::GetInstance()
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||||
|
do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||||
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
||||||
return concrete.forget();
|
return concrete.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ServiceWorkerManager::ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
|
||||||
|
const nsACString& aWorkerScriptSpec)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||||
|
if (aRegistration->mUpdatePromise->IsRejected()) {
|
||||||
|
aRegistration->mUpdatePromise = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aRegistration->mUpdatePromise->ResolveAllPromises(aWorkerScriptSpec,
|
||||||
|
aRegistration->mScope);
|
||||||
|
aRegistration->mUpdatePromise = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must NS_Free() aString
|
||||||
|
void
|
||||||
|
ServiceWorkerManager::FinishFetch(ServiceWorkerRegistration* aRegistration,
|
||||||
|
nsPIDOMWindow* aWindow)
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
|
MOZ_ASSERT(aRegistration->HasUpdatePromise());
|
||||||
|
MOZ_ASSERT(aRegistration->mUpdateInstance);
|
||||||
|
aRegistration->mUpdateInstance = nullptr;
|
||||||
|
if (aRegistration->mUpdatePromise->IsRejected()) {
|
||||||
|
aRegistration->mUpdatePromise = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have skipped Steps 3-8.3 of the Update algorithm here!
|
||||||
|
|
||||||
|
nsRefPtr<ServiceWorker> worker;
|
||||||
|
nsresult rv = CreateServiceWorkerForWindow(aWindow,
|
||||||
|
aRegistration->mScriptSpec,
|
||||||
|
aRegistration->mScope,
|
||||||
|
getter_AddRefs(worker));
|
||||||
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
RejectUpdatePromiseObservers(aRegistration, rv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolveRegisterPromises(aRegistration, aRegistration->mScriptSpec);
|
||||||
|
|
||||||
|
ServiceWorkerInfo info(aRegistration->mScriptSpec);
|
||||||
|
Install(aRegistration, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ServiceWorkerManager::Install(ServiceWorkerRegistration* aRegistration,
|
||||||
|
ServiceWorkerInfo aServiceWorkerInfo)
|
||||||
|
{
|
||||||
|
// FIXME(nsm): Same bug, different patch.
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||||
const nsACString& aScriptSpec,
|
const nsACString& aScriptSpec,
|
||||||
|
|
|
@ -23,6 +23,44 @@ namespace dom {
|
||||||
namespace workers {
|
namespace workers {
|
||||||
|
|
||||||
class ServiceWorker;
|
class ServiceWorker;
|
||||||
|
class ServiceWorkerUpdateInstance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdatePromise is a utility class that sort of imitates Promise, but not
|
||||||
|
* completely. Using DOM Promise from C++ is a pain when we know the precise types
|
||||||
|
* we're dealing with since it involves dealing with JSAPI. In this case we
|
||||||
|
* also don't (yet) need the 'thenables added after resolution should trigger
|
||||||
|
* immediately' support and other things like that. All we want is something
|
||||||
|
* that works reasonably Promise like and can resolve real DOM Promises added
|
||||||
|
* pre-emptively.
|
||||||
|
*/
|
||||||
|
class UpdatePromise MOZ_FINAL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UpdatePromise();
|
||||||
|
~UpdatePromise();
|
||||||
|
|
||||||
|
void AddPromise(Promise* aPromise);
|
||||||
|
void ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope);
|
||||||
|
void RejectAllPromises(nsresult aRv);
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsRejected() const
|
||||||
|
{
|
||||||
|
return mState == Rejected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum {
|
||||||
|
Pending,
|
||||||
|
Resolved,
|
||||||
|
Rejected
|
||||||
|
} mState;
|
||||||
|
|
||||||
|
// XXXnsm: Right now we don't need to support AddPromise() after
|
||||||
|
// already being resolved (i.e. true Promise-like behaviour).
|
||||||
|
nsTArray<nsTWeakRef<Promise>> mPromises;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wherever the spec treats a worker instance and a description of said worker
|
* Wherever the spec treats a worker instance and a description of said worker
|
||||||
|
@ -32,7 +70,7 @@ class ServiceWorker;
|
||||||
*/
|
*/
|
||||||
class ServiceWorkerInfo
|
class ServiceWorkerInfo
|
||||||
{
|
{
|
||||||
const nsCString mScriptSpec;
|
nsCString mScriptSpec;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -41,6 +79,12 @@ public:
|
||||||
return !mScriptSpec.IsVoid();
|
return !mScriptSpec.IsVoid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Invalidate()
|
||||||
|
{
|
||||||
|
mScriptSpec.SetIsVoid(true);
|
||||||
|
}
|
||||||
|
|
||||||
const nsCString&
|
const nsCString&
|
||||||
GetScriptSpec() const
|
GetScriptSpec() const
|
||||||
{
|
{
|
||||||
|
@ -49,19 +93,23 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceWorkerInfo()
|
ServiceWorkerInfo()
|
||||||
{ }
|
{
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
|
explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
|
||||||
: mScriptSpec(aScriptSpec)
|
: mScriptSpec(aScriptSpec)
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServiceWorkerRegistration
|
// Needs to inherit from nsISupports because NS_ProxyRelease() does not support
|
||||||
|
// non-ISupports classes.
|
||||||
|
class ServiceWorkerRegistration MOZ_FINAL : public nsISupports
|
||||||
{
|
{
|
||||||
private:
|
virtual ~ServiceWorkerRegistration();
|
||||||
~ServiceWorkerRegistration() {}
|
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration)
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
nsCString mScope;
|
nsCString mScope;
|
||||||
// The scriptURL for the registration. This may be completely different from
|
// The scriptURL for the registration. This may be completely different from
|
||||||
|
@ -72,18 +120,20 @@ public:
|
||||||
ServiceWorkerInfo mWaitingWorker;
|
ServiceWorkerInfo mWaitingWorker;
|
||||||
ServiceWorkerInfo mInstallingWorker;
|
ServiceWorkerInfo mInstallingWorker;
|
||||||
|
|
||||||
bool mHasUpdatePromise;
|
nsAutoPtr<UpdatePromise> mUpdatePromise;
|
||||||
|
nsRefPtr<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||||
|
|
||||||
void
|
void
|
||||||
AddUpdatePromiseObserver(Promise* aPromise)
|
AddUpdatePromiseObserver(Promise* aPromise)
|
||||||
{
|
{
|
||||||
// FIXME(nsm): Same bug, different patch.
|
MOZ_ASSERT(HasUpdatePromise());
|
||||||
|
mUpdatePromise->AddPromise(aPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HasUpdatePromise()
|
HasUpdatePromise()
|
||||||
{
|
{
|
||||||
return mHasUpdatePromise;
|
return mUpdatePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When unregister() is called on a registration, it is not immediately
|
// When unregister() is called on a registration, it is not immediately
|
||||||
|
@ -91,11 +141,7 @@ public:
|
||||||
// pendingUninstall and when all controlling documents go away, removed.
|
// pendingUninstall and when all controlling documents go away, removed.
|
||||||
bool mPendingUninstall;
|
bool mPendingUninstall;
|
||||||
|
|
||||||
explicit ServiceWorkerRegistration(const nsACString& aScope)
|
explicit ServiceWorkerRegistration(const nsACString& aScope);
|
||||||
: mScope(aScope),
|
|
||||||
mHasUpdatePromise(false),
|
|
||||||
mPendingUninstall(false)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
ServiceWorkerInfo
|
ServiceWorkerInfo
|
||||||
Newest() const
|
Newest() const
|
||||||
|
@ -126,6 +172,8 @@ public:
|
||||||
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
||||||
{
|
{
|
||||||
friend class RegisterRunnable;
|
friend class RegisterRunnable;
|
||||||
|
friend class CallInstallRunnable;
|
||||||
|
friend class ServiceWorkerUpdateInstance;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
|
@ -174,12 +222,17 @@ public:
|
||||||
|
|
||||||
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
||||||
|
|
||||||
// FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*"
|
void
|
||||||
// }) and then another page calls register("/bar_worker.js", { scope: "/*" })
|
ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
|
||||||
// while the install is in progress. The async install steps for register
|
const nsACString& aWorkerScriptSpec);
|
||||||
// bar_worker.js could finish before foo_worker.js, but bar_worker still has
|
|
||||||
// to be the winning controller.
|
void
|
||||||
// FIXME(nsm): Move this into per domain?
|
RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
|
||||||
|
nsresult aResult);
|
||||||
|
|
||||||
|
void
|
||||||
|
FinishFetch(ServiceWorkerRegistration* aRegistration,
|
||||||
|
nsPIDOMWindow* aWindow);
|
||||||
|
|
||||||
static already_AddRefed<ServiceWorkerManager>
|
static already_AddRefed<ServiceWorkerManager>
|
||||||
GetInstance();
|
GetInstance();
|
||||||
|
@ -191,6 +244,10 @@ private:
|
||||||
NS_IMETHOD
|
NS_IMETHOD
|
||||||
Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
|
Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
|
||||||
|
|
||||||
|
void
|
||||||
|
Install(ServiceWorkerRegistration* aRegistration,
|
||||||
|
ServiceWorkerInfo aServiceWorkerInfo);
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||||
const nsACString& aScriptSpec,
|
const nsACString& aScriptSpec,
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
|
[DEFAULT]
|
||||||
|
support-files =
|
||||||
|
worker.js
|
||||||
|
worker2.js
|
||||||
|
worker3.js
|
||||||
|
|
||||||
[test_installation_simple.html]
|
[test_installation_simple.html]
|
||||||
[test_navigator.html]
|
[test_navigator.html]
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<script class="testbody" type="text/javascript">
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
function simpleRegister() {
|
function simpleRegister() {
|
||||||
var p = navigator.serviceWorker.register("/fake_worker.js");
|
var p = navigator.serviceWorker.register("worker.js");
|
||||||
ok(p instanceof Promise, "register() should return a Promise");
|
ok(p instanceof Promise, "register() should return a Promise");
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -50,14 +50,60 @@
|
||||||
ok(false, "non-HTTPS pages cannot register ServiceWorkers");
|
ok(false, "non-HTTPS pages cannot register ServiceWorkers");
|
||||||
}, function(e) {
|
}, function(e) {
|
||||||
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
ok(e.name === "SecurityError", "Should fail with a SecurityError");
|
||||||
|
}).then(function() {
|
||||||
|
return new Promise((resolve) => SpecialPowers.popPrefEnv(resolve));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function realWorker() {
|
||||||
|
var p = navigator.serviceWorker.register("worker.js");
|
||||||
|
return p.then(function(w) {
|
||||||
|
ok(w instanceof ServiceWorker, "Register a ServiceWorker");
|
||||||
|
info(w.scope);
|
||||||
|
ok(w.scope == (new URL("/*", document.baseURI)).href, "Scope should match");
|
||||||
|
ok(w.url == (new URL("worker.js", document.baseURI)).href, "URL should be of the worker");
|
||||||
|
}, function(e) {
|
||||||
|
info(e.name);
|
||||||
|
ok(false, "Registration should have succeeded!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function abortPrevious() {
|
||||||
|
var p = navigator.serviceWorker.register("worker2.js", { scope: "foo/*" });
|
||||||
|
var q = navigator.serviceWorker.register("worker3.js", { scope: "foo/*" });
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
p.then(function(w) {
|
||||||
|
ok(false, "First registration should fail with AbortError");
|
||||||
|
}, function(e) {
|
||||||
|
ok(e.name === "AbortError", "First registration should fail with AbortError");
|
||||||
|
}),
|
||||||
|
|
||||||
|
q.then(function(w) {
|
||||||
|
ok(w instanceof ServiceWorker, "Second registration should succeed");
|
||||||
|
}, function(e) {
|
||||||
|
ok(false, "Second registration should succeed");
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function networkError404() {
|
||||||
|
return navigator.serviceWorker.register("404.js").then(function(w) {
|
||||||
|
todo(false, "Should fail with NetworkError");
|
||||||
|
}, function(e) {
|
||||||
|
todo(e.name === "NetworkError", "Should fail with NetworkError");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function runTest() {
|
function runTest() {
|
||||||
simpleRegister()
|
simpleRegister()
|
||||||
.then(sameOriginWorker)
|
.then(sameOriginWorker)
|
||||||
.then(sameOriginScope)
|
.then(sameOriginScope)
|
||||||
.then(httpsOnly)
|
.then(httpsOnly)
|
||||||
|
.then(realWorker)
|
||||||
|
.then(abortPrevious)
|
||||||
|
// FIXME(nsm): Uncomment once we have the error trapping patch from Bug 984048.
|
||||||
|
// .then(networkError404)
|
||||||
// put more tests here.
|
// put more tests here.
|
||||||
.then(function() {
|
.then(function() {
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
// empty worker, always succeed!
|
|
@ -0,0 +1 @@
|
||||||
|
// worker2.js
|
|
@ -0,0 +1 @@
|
||||||
|
// worker3.js
|
Загрузка…
Ссылка в новой задаче