зеркало из 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/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "WorkerInlines.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
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 //
|
||||
//////////////////////////
|
||||
|
@ -89,13 +311,15 @@ public:
|
|||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
|
||||
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)) {
|
||||
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
|
||||
swm->mDomainMap.Put(domain, domainInfo);
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerRegistration> registration = domainInfo->GetRegistration(mScope);
|
||||
nsRefPtr<ServiceWorkerRegistration> registration =
|
||||
domainInfo->GetRegistration(mScope);
|
||||
|
||||
nsCString spec;
|
||||
rv = mScriptURI->GetSpec(spec);
|
||||
|
@ -107,9 +331,6 @@ public:
|
|||
if (registration) {
|
||||
registration->mPendingUninstall = false;
|
||||
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
|
||||
// results in.
|
||||
if (registration->HasUpdatePromise()) {
|
||||
|
@ -117,8 +338,8 @@ public:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// There is no update in progress and since SW updating is upto the UA, we
|
||||
// will not update right now. Simply resolve with whatever worker we
|
||||
// There is no update in progress and since SW updating is upto the UA,
|
||||
// we will not update right now. Simply resolve with whatever worker we
|
||||
// have.
|
||||
ServiceWorkerInfo info = registration->Newest();
|
||||
if (info.IsValid()) {
|
||||
|
@ -143,10 +364,16 @@ public:
|
|||
|
||||
registration->mScriptSpec = spec;
|
||||
|
||||
// FIXME(nsm): Call Update. Same bug, different patch.
|
||||
// For now if the registration reaches this spot, the promise remains
|
||||
// unresolved.
|
||||
return NS_OK;
|
||||
rv = swm->Update(registration, mWindow);
|
||||
MOZ_ASSERT(registration->HasUpdatePromise());
|
||||
|
||||
// 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.
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
||||
const nsAString& aScriptURL, nsISupports** aPromise)
|
||||
const nsAString& aScriptURL,
|
||||
nsISupports** aPromise)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
@ -206,10 +434,9 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/262
|
||||
// allowIfInheritsPrincipal: allow data: URLs for now.
|
||||
// Data URLs are not allowed.
|
||||
rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
|
||||
true /* allowIfInheritsPrincipal */);
|
||||
false /* allowIfInheritsPrincipal */);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
@ -238,11 +465,51 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
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
|
||||
ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -266,11 +533,69 @@ ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
|
|||
already_AddRefed<ServiceWorkerManager>
|
||||
ServiceWorkerManager::GetInstance()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||
do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
|
||||
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
|
||||
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
|
|
|
@ -23,6 +23,44 @@ namespace dom {
|
|||
namespace workers {
|
||||
|
||||
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
|
||||
|
@ -32,7 +70,7 @@ class ServiceWorker;
|
|||
*/
|
||||
class ServiceWorkerInfo
|
||||
{
|
||||
const nsCString mScriptSpec;
|
||||
nsCString mScriptSpec;
|
||||
public:
|
||||
|
||||
bool
|
||||
|
@ -41,6 +79,12 @@ public:
|
|||
return !mScriptSpec.IsVoid();
|
||||
}
|
||||
|
||||
void
|
||||
Invalidate()
|
||||
{
|
||||
mScriptSpec.SetIsVoid(true);
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GetScriptSpec() const
|
||||
{
|
||||
|
@ -49,19 +93,23 @@ public:
|
|||
}
|
||||
|
||||
ServiceWorkerInfo()
|
||||
{ }
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
explicit ServiceWorkerInfo(const nsACString& 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:
|
||||
~ServiceWorkerRegistration() {}
|
||||
virtual ~ServiceWorkerRegistration();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration)
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsCString mScope;
|
||||
// The scriptURL for the registration. This may be completely different from
|
||||
|
@ -72,18 +120,20 @@ public:
|
|||
ServiceWorkerInfo mWaitingWorker;
|
||||
ServiceWorkerInfo mInstallingWorker;
|
||||
|
||||
bool mHasUpdatePromise;
|
||||
nsAutoPtr<UpdatePromise> mUpdatePromise;
|
||||
nsRefPtr<ServiceWorkerUpdateInstance> mUpdateInstance;
|
||||
|
||||
void
|
||||
AddUpdatePromiseObserver(Promise* aPromise)
|
||||
{
|
||||
// FIXME(nsm): Same bug, different patch.
|
||||
MOZ_ASSERT(HasUpdatePromise());
|
||||
mUpdatePromise->AddPromise(aPromise);
|
||||
}
|
||||
|
||||
bool
|
||||
HasUpdatePromise()
|
||||
{
|
||||
return mHasUpdatePromise;
|
||||
return mUpdatePromise;
|
||||
}
|
||||
|
||||
// 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.
|
||||
bool mPendingUninstall;
|
||||
|
||||
explicit ServiceWorkerRegistration(const nsACString& aScope)
|
||||
: mScope(aScope),
|
||||
mHasUpdatePromise(false),
|
||||
mPendingUninstall(false)
|
||||
{ }
|
||||
explicit ServiceWorkerRegistration(const nsACString& aScope);
|
||||
|
||||
ServiceWorkerInfo
|
||||
Newest() const
|
||||
|
@ -126,6 +172,8 @@ public:
|
|||
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
||||
{
|
||||
friend class RegisterRunnable;
|
||||
friend class CallInstallRunnable;
|
||||
friend class ServiceWorkerUpdateInstance;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -174,12 +222,17 @@ public:
|
|||
|
||||
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
||||
|
||||
// FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*"
|
||||
// }) and then another page calls register("/bar_worker.js", { scope: "/*" })
|
||||
// while the install is in progress. The async install steps for register
|
||||
// bar_worker.js could finish before foo_worker.js, but bar_worker still has
|
||||
// to be the winning controller.
|
||||
// FIXME(nsm): Move this into per domain?
|
||||
void
|
||||
ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
|
||||
const nsACString& aWorkerScriptSpec);
|
||||
|
||||
void
|
||||
RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
|
||||
nsresult aResult);
|
||||
|
||||
void
|
||||
FinishFetch(ServiceWorkerRegistration* aRegistration,
|
||||
nsPIDOMWindow* aWindow);
|
||||
|
||||
static already_AddRefed<ServiceWorkerManager>
|
||||
GetInstance();
|
||||
|
@ -191,6 +244,10 @@ private:
|
|||
NS_IMETHOD
|
||||
Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
|
||||
|
||||
void
|
||||
Install(ServiceWorkerRegistration* aRegistration,
|
||||
ServiceWorkerInfo aServiceWorkerInfo);
|
||||
|
||||
NS_IMETHODIMP
|
||||
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
worker.js
|
||||
worker2.js
|
||||
worker3.js
|
||||
|
||||
[test_installation_simple.html]
|
||||
[test_navigator.html]
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<script class="testbody" type="text/javascript">
|
||||
|
||||
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");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -50,6 +50,48 @@
|
|||
ok(false, "non-HTTPS pages cannot register ServiceWorkers");
|
||||
}, function(e) {
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,6 +100,10 @@
|
|||
.then(sameOriginWorker)
|
||||
.then(sameOriginScope)
|
||||
.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.
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
// empty worker, always succeed!
|
|
@ -0,0 +1 @@
|
|||
// worker2.js
|
|
@ -0,0 +1 @@
|
|||
// worker3.js
|
Загрузка…
Ссылка в новой задаче