Bug 984048: Part 3 - Update algorithm. r=ehsan,khuey

--HG--
extra : rebase_source : 5e80b118faeea419cfebbc2d1060cd2a1a61bcba
This commit is contained in:
Nikhil Marathe 2014-06-11 09:12:56 -07:00
Родитель 92fe6235b6
Коммит 08d119358f
7 изменённых файлов: 475 добавлений и 38 удалений

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

@ -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