Bug 1025077 - Implement ServiceWorkerContainer.ready, r=nsm

This commit is contained in:
Andrea Marchesini 2014-08-26 09:16:03 +01:00
parent f980f5fb99
commit 51c5e60fe1
8 changed files with 255 additions and 20 deletions

View File

@ -8,7 +8,7 @@
interface nsIDocument;
interface nsIURI;
[uuid(c7132f91-c46c-4e01-b75a-43babb254d93)]
[uuid(91b9d3fc-1654-44da-b438-15123cdbe7aa)]
interface nsIServiceWorkerManager : nsISupports
{
/**
@ -36,6 +36,12 @@ interface nsIServiceWorkerManager : nsISupports
// Returns a Promise
nsISupports getRegistration(in nsIDOMWindow aWindow, in DOMString aScope);
// Returns a Promise
nsISupports getReadyPromise(in nsIDOMWindow aWindow);
// Remove ready pending Promise
void removeReadyPromise(in nsIDOMWindow aWindow);
// aTarget MUST be a ServiceWorkerRegistration.
[noscript] void AddRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
[noscript] void RemoveRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);

View File

@ -30,18 +30,28 @@ NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
mControllerWorker)
mControllerWorker, mReadyPromise)
ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
: DOMEventTargetHelper(aWindow)
{
SetIsDOMBinding();
}
ServiceWorkerContainer::~ServiceWorkerContainer()
{
}
void
ServiceWorkerContainer::DisconnectFromOwner()
{
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
MOZ_ASSERT(swm);
swm->RemoveReadyPromise(GetOwner());
DOMEventTargetHelper::DisconnectFromOwner();
}
JSObject*
ServiceWorkerContainer::WrapObject(JSContext* aCx)
{
@ -82,7 +92,7 @@ ServiceWorkerContainer::GetController()
}
nsCOMPtr<nsISupports> serviceWorker;
rv = swm->GetDocumentController(mWindow, getter_AddRefs(serviceWorker));
rv = swm->GetDocumentController(GetOwner(), getter_AddRefs(serviceWorker));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
@ -106,7 +116,7 @@ ServiceWorkerContainer::GetRegistrations(ErrorResult& aRv)
}
nsCOMPtr<nsISupports> promise;
aRv = swm->GetRegistrations(mWindow, getter_AddRefs(promise));
aRv = swm->GetRegistrations(GetOwner(), getter_AddRefs(promise));
if (aRv.Failed()) {
return nullptr;
}
@ -128,7 +138,7 @@ ServiceWorkerContainer::GetRegistration(const nsAString& aDocumentURL,
}
nsCOMPtr<nsISupports> promise;
aRv = swm->GetRegistration(mWindow, aDocumentURL, getter_AddRefs(promise));
aRv = swm->GetRegistration(GetOwner(), aDocumentURL, getter_AddRefs(promise));
if (aRv.Failed()) {
return nullptr;
}
@ -138,12 +148,24 @@ ServiceWorkerContainer::GetRegistration(const nsAString& aDocumentURL,
return ret.forget();
}
already_AddRefed<Promise>
Promise*
ServiceWorkerContainer::GetReady(ErrorResult& aRv)
{
// FIXME(nsm): Bug 1025077
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
return Promise::Create(global, aRv);
if (mReadyPromise) {
return mReadyPromise;
}
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
if (!swm) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsISupports> promise;
aRv = swm->GetReadyPromise(GetOwner(), getter_AddRefs(promise));
mReadyPromise = static_cast<Promise*>(promise.get());
return mReadyPromise;
}
// Testing only.

View File

@ -34,12 +34,6 @@ public:
explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow);
nsPIDOMWindow*
GetParentObject() const
{
return mWindow;
}
JSObject*
WrapObject(JSContext* aCx);
@ -58,7 +52,7 @@ public:
already_AddRefed<Promise>
GetRegistrations(ErrorResult& aRv);
already_AddRefed<Promise>
Promise*
GetReady(ErrorResult& aRv);
// Testing only.
@ -74,15 +68,19 @@ public:
GetControllingWorkerScriptURLForPath(const nsAString& aPath,
nsString& aScriptURL,
ErrorResult& aRv);
// DOMEventTargetHelper
void DisconnectFromOwner() MOZ_OVERRIDE;
private:
~ServiceWorkerContainer();
nsCOMPtr<nsPIDOMWindow> mWindow;
// This only changes when a worker hijacks everything in its scope by calling
// replace().
// FIXME(nsm): Bug 982711. Provide API to let SWM invalidate this.
nsRefPtr<workers::ServiceWorker> mControllerWorker;
nsRefPtr<Promise> mReadyPromise;
};
} // namespace dom

View File

@ -820,6 +820,142 @@ ServiceWorkerManager::GetRegistration(nsIDOMWindow* aWindow,
return NS_DispatchToCurrentThread(runnable);
}
class GetReadyPromiseRunnable : public nsRunnable
{
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<Promise> mPromise;
public:
GetReadyPromiseRunnable(nsPIDOMWindow* aWindow, Promise* aPromise)
: mWindow(aWindow), mPromise(aPromise)
{ }
NS_IMETHODIMP
Run()
{
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
nsIDocument* doc = mWindow->GetExtantDoc();
if (!doc) {
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
return NS_OK;
}
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
if (!docURI) {
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
return NS_OK;
}
if (!swm->CheckReadyPromise(mWindow, docURI, mPromise)) {
swm->StorePendingReadyPromise(mWindow, docURI, mPromise);
}
return NS_OK;
}
};
NS_IMETHODIMP
ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
nsISupports** aPromise)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
// XXXnsm Don't allow chrome callers for now, we don't support chrome
// ServiceWorkers.
MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
if (!window) {
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(!mPendingReadyPromises.Contains(window));
nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
ErrorResult result;
nsRefPtr<Promise> promise = Promise::Create(sgo, result);
if (result.Failed()) {
return result.ErrorCode();
}
nsRefPtr<nsIRunnable> runnable =
new GetReadyPromiseRunnable(window, promise);
promise.forget(aPromise);
return NS_DispatchToCurrentThread(runnable);
}
NS_IMETHODIMP
ServiceWorkerManager::RemoveReadyPromise(nsIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
if (!window) {
return NS_ERROR_FAILURE;
}
mPendingReadyPromises.Remove(aWindow);
return NS_OK;
}
void
ServiceWorkerManager::StorePendingReadyPromise(nsPIDOMWindow* aWindow,
nsIURI* aURI,
Promise* aPromise)
{
PendingReadyPromise* data;
// We should not have 2 pending promises for the same window.
MOZ_ASSERT(!mPendingReadyPromises.Get(aWindow, &data));
data = new PendingReadyPromise(aURI, aPromise);
mPendingReadyPromises.Put(aWindow, data);
}
void
ServiceWorkerManager::CheckPendingReadyPromises()
{
mPendingReadyPromises.Enumerate(CheckPendingReadyPromisesEnumerator, this);
}
PLDHashOperator
ServiceWorkerManager::CheckPendingReadyPromisesEnumerator(
nsISupports* aSupports,
nsAutoPtr<PendingReadyPromise>& aData,
void* aPtr)
{
ServiceWorkerManager* aSwm = static_cast<ServiceWorkerManager*>(aPtr);
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSupports);
if (aSwm->CheckReadyPromise(window, aData->mURI, aData->mPromise)) {
return PL_DHASH_REMOVE;
}
return PL_DHASH_NEXT;
}
bool
ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindow* aWindow,
nsIURI* aURI, Promise* aPromise)
{
nsRefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(aURI);
if (registration && registration->mCurrentWorker) {
NS_ConvertUTF8toUTF16 scope(registration->mScope);
nsRefPtr<ServiceWorkerRegistration> swr =
new ServiceWorkerRegistration(aWindow, scope);
aPromise->MaybeResolve(swr);
return true;
}
return false;
}
void
ServiceWorkerManager::RejectUpdatePromiseObservers(ServiceWorkerRegistrationInfo* aRegistration,
nsresult aRv)
@ -1406,6 +1542,8 @@ public:
return NS_OK;
}
swm->CheckPendingReadyPromises();
// FIXME(nsm): Steps 7 of the algorithm.
swm->FireEventOnServiceWorkerRegistrations(mRegistration,

View File

@ -20,6 +20,7 @@
#include "nsRefPtrHashtable.h"
#include "nsTArrayForwardDeclare.h"
#include "nsTObserverArray.h"
#include "nsClassHashtable.h"
class nsIScriptError;
@ -199,6 +200,7 @@ class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
friend class CancelServiceWorkerInstallationRunnable;
friend class ServiceWorkerRegistrationInfo;
friend class ServiceWorkerUpdateInstance;
friend class GetReadyPromiseRunnable;
friend class GetRegistrationsRunnable;
friend class GetRegistrationRunnable;
friend class UnregisterRunnable;
@ -389,6 +391,31 @@ private:
FireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
const nsAString& aName);
void
StorePendingReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
void
CheckPendingReadyPromises();
bool
CheckReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
struct PendingReadyPromise
{
PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
: mURI(aURI), mPromise(aPromise)
{ }
nsCOMPtr<nsIURI> mURI;
nsRefPtr<Promise> mPromise;
};
static PLDHashOperator
CheckPendingReadyPromisesEnumerator(nsISupports* aSupports,
nsAutoPtr<PendingReadyPromise>& aData,
void* aUnused);
nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
};
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,

View File

@ -7,6 +7,7 @@ support-files =
parse_error_worker.js
install_event_worker.js
simpleregister/index.html
simpleregister/ready.html
controller/index.html
unregister/index.html

View File

@ -0,0 +1,15 @@
<html>
<head></head>
<body>
<script type="text/javascript">
window.addEventListener('message', function(evt) {
navigator.serviceWorker.ready.then(function() {
evt.ports[0].postMessage("WOW!");
});
}, false);
</script>
</body>
</html>

View File

@ -141,8 +141,34 @@
return p;
}
var readyPromiseResolved = false;
function readyPromise() {
var frame = document.createElement("iframe");
frame.setAttribute("id", "simpleregister-frame-ready");
frame.setAttribute("src", new URL("simpleregister/ready.html", document.baseURI).href);
document.body.appendChild(frame);
var channel = new MessageChannel();
frame.addEventListener('load', function() {
frame.contentWindow.postMessage('your port!', '*', [channel.port2]);
}, false);
channel.port1.onmessage = function() {
readyPromiseResolved = true;
}
return Promise.resolve();
}
function checkReadyPromise() {
ok(readyPromiseResolved, "The ready promise has been resolved!");
return Promise.resolve();
}
function runTest() {
simpleRegister()
.then(readyPromise)
.then(sameOriginWorker)
.then(sameOriginScope)
.then(httpsOnly)
@ -151,6 +177,7 @@
.then(networkError404)
.then(parseError)
.then(updatefound)
.then(checkReadyPromise)
// put more tests here.
.then(function() {
SimpleTest.finish();
@ -162,6 +189,7 @@
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.messageChannel.enabled", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);