Bug 983497 - Patch 1: Infrastructure to fire events on ServiceWorkerContainers. r=ehsan,smaug

--HG--
extra : transplant_source : S1z%F3%86%E5%CF%D6%3BWW%8E%1A%90%A3%94%7E%FE%E3z
This commit is contained in:
Nikhil Marathe 2014-07-14 14:15:23 -07:00
parent fae4030844
commit 1d4f2c7f6d
6 changed files with 191 additions and 27 deletions

View File

@ -141,14 +141,15 @@ public:
virtual void EventListenerWasRemoved(const nsAString& aType,
ErrorResult& aRv,
JSCompartment* aCompartment = nullptr) {}
// Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
nsresult DispatchTrustedEvent(const nsAString& aEventName);
protected:
virtual ~DOMEventTargetHelper();
nsresult WantsUntrusted(bool* aRetVal);
nsRefPtr<EventListenerManager> mListenerManager;
// Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
nsresult DispatchTrustedEvent(const nsAString& aEventName);
// Make |event| trusted and dispatch |aEvent| to |this|.
nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);

View File

@ -5,7 +5,9 @@
#include "domstubs.idl"
[uuid(44ef0b7e-92c0-48a7-a092-5a49f2533792)]
interface nsIURI;
[uuid(6117cdf1-cb10-42a3-9901-4f1bab7ffa4d)]
interface nsIServiceWorkerManager : nsISupports
{
// Returns a Promise
@ -14,6 +16,10 @@ interface nsIServiceWorkerManager : nsISupports
// Returns a Promise
nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
// aTarget MUST be a ServiceWorkerContainer.
[noscript] void AddContainerEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
[noscript] void RemoveContainerEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
// Testing
DOMString getScopeForUrl(in DOMString path);
};

View File

@ -127,6 +127,26 @@ ServiceWorkerContainer::Ready()
return promise.forget();
}
// XXXnsm, maybe this can be optimized to only add when a event handler is
// registered.
void
ServiceWorkerContainer::StartListeningForEvents()
{
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
if (swm) {
swm->AddContainerEventListener(mWindow->GetDocumentURI(), this);
}
}
void
ServiceWorkerContainer::StopListeningForEvents()
{
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
if (swm) {
swm->RemoveContainerEventListener(mWindow->GetDocumentURI(), this);
}
}
// Testing only.
already_AddRefed<Promise>
ServiceWorkerContainer::ClearAllServiceWorkerData(ErrorResult& aRv)

View File

@ -36,8 +36,8 @@ public:
explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
// FIXME(nsm): Bug 983497. Here the NSW should hook into SWM to be notified of events.
SetIsDOMBinding();
StartListeningForEvents();
}
nsPIDOMWindow*
@ -75,6 +75,12 @@ public:
already_AddRefed<Promise>
Ready();
nsIURI*
GetDocumentURI() const
{
return mWindow->GetDocumentURI();
}
// Testing only.
already_AddRefed<Promise>
ClearAllServiceWorkerData(ErrorResult& aRv);
@ -91,9 +97,15 @@ public:
private:
~ServiceWorkerContainer()
{
// FIXME(nsm): Bug 983497. Unhook from events.
StopListeningForEvents();
}
void
StartListeningForEvents();
void
StopListeningForEvents();
nsCOMPtr<nsPIDOMWindow> mWindow;
};

View File

@ -4,6 +4,7 @@
#include "ServiceWorkerManager.h"
#include "nsIDOMEventTarget.h"
#include "nsIDocument.h"
#include "nsIScriptSecurityManager.h"
#include "nsPIDOMWindow.h"
@ -367,10 +368,10 @@ public:
}
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo;
nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo;
// 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, getter_AddRefs(domainInfo))) {
domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
swm->mDomainMap.Put(domain, domainInfo);
}
@ -681,8 +682,8 @@ ServiceWorkerManager::HandleError(JSContext* aCx,
return;
}
ServiceWorkerDomainInfo* domainInfo;
if (!mDomainMap.Get(domain, &domainInfo)) {
nsRefPtr<ServiceWorkerDomainInfo> domainInfo;
if (!mDomainMap.Get(domain, getter_AddRefs(domainInfo))) {
return;
}
@ -1003,26 +1004,13 @@ ServiceWorkerManager::GetServiceWorkerRegistration(nsIDocument* aDoc)
already_AddRefed<ServiceWorkerRegistration>
ServiceWorkerManager::GetServiceWorkerRegistration(nsIURI* aURI)
{
if (!aURI) {
return nullptr;
}
nsCString domain;
nsresult rv = aURI->GetHost(domain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
ServiceWorkerDomainInfo* domainInfo = mDomainMap.Get(domain);
// XXXnsm: This pattern can be refactored if we end up using it
// often enough.
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aURI);
if (!domainInfo) {
return nullptr;
}
nsCString spec;
rv = aURI->GetSpec(spec);
nsresult rv = aURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
@ -1126,6 +1114,45 @@ ServiceWorkerManager::RemoveScope(nsTArray<nsCString>& aList, const nsACString&
aList.RemoveElement(aScope);
}
already_AddRefed<ServiceWorkerManager::ServiceWorkerDomainInfo>
ServiceWorkerManager::GetDomainInfo(nsIDocument* aDoc)
{
AssertIsOnMainThread();
MOZ_ASSERT(aDoc);
nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
return GetDomainInfo(documentURI);
}
already_AddRefed<ServiceWorkerManager::ServiceWorkerDomainInfo>
ServiceWorkerManager::GetDomainInfo(nsIURI* aURI)
{
AssertIsOnMainThread();
MOZ_ASSERT(aURI);
nsCString domain;
nsresult rv = aURI->GetHost(domain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
nsRefPtr<ServiceWorkerDomainInfo> domainInfo;
mDomainMap.Get(domain, getter_AddRefs(domainInfo));
return domainInfo.forget();
}
already_AddRefed<ServiceWorkerManager::ServiceWorkerDomainInfo>
ServiceWorkerManager::GetDomainInfo(const nsCString& aURL)
{
AssertIsOnMainThread();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return GetDomainInfo(uri);
}
NS_IMETHODIMP
ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope)
{
@ -1143,6 +1170,79 @@ ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope)
aScope = NS_ConvertUTF8toUTF16(r->mScope);
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerManager::AddContainerEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
{
MOZ_ASSERT(aDocumentURI);
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
if (!domainInfo) {
nsCString domain;
nsresult rv = aDocumentURI->GetHost(domain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
domainInfo = new ServiceWorkerDomainInfo;
mDomainMap.Put(domain, domainInfo);
}
MOZ_ASSERT(domainInfo);
ServiceWorkerContainer* container = static_cast<ServiceWorkerContainer*>(aListener);
domainInfo->mServiceWorkerContainers.AppendElement(container);
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerManager::RemoveContainerEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
{
MOZ_ASSERT(aDocumentURI);
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
if (!domainInfo) {
return NS_OK;
}
ServiceWorkerContainer* container = static_cast<ServiceWorkerContainer*>(aListener);
domainInfo->mServiceWorkerContainers.RemoveElement(container);
return NS_OK;
}
void
ServiceWorkerManager::FireEventOnServiceWorkerContainers(
ServiceWorkerRegistration* aRegistration,
const nsAString& aName)
{
AssertIsOnMainThread();
nsRefPtr<ServiceWorkerDomainInfo> domainInfo =
GetDomainInfo(aRegistration->mScriptSpec);
if (domainInfo) {
nsTObserverArray<ServiceWorkerContainer*>::ForwardIterator it(domainInfo->mServiceWorkerContainers);
while (it.HasMore()) {
nsRefPtr<ServiceWorkerContainer> target = it.GetNext();
nsIURI* targetURI = target->GetDocumentURI();
if (!targetURI) {
NS_WARNING("Controlled domain cannot have page with null URI!");
continue;
}
nsCString path;
nsresult rv = targetURI->GetSpec(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
if (scope.IsEmpty() ||
!scope.Equals(aRegistration->mScope)) {
continue;
}
target->DispatchTrustedEvent(aName);
}
}
}
NS_IMETHODIMP
ServiceWorkerManager::CreateServiceWorker(const nsACString& aScriptSpec,
const nsACString& aScope,

View File

@ -12,10 +12,10 @@
#include "mozilla/LinkedList.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Promise.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "nsRefPtrHashtable.h"
#include "nsTArrayForwardDeclare.h"
#include "nsTObserverArray.h"
#include "nsTWeakRef.h"
class nsIScriptError;
@ -210,6 +210,12 @@ public:
// Scope to registration.
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistration> mServiceWorkerRegistrations;
// This array can't be stored in ServiceWorkerRegistration because one may
// not exist when a certain window is opened, but we still want that
// window's container to be notified if it's in scope.
// The containers inform the SWM on creation and destruction.
nsTObserverArray<ServiceWorkerContainer*> mServiceWorkerContainers;
ServiceWorkerDomainInfo()
{ }
@ -232,9 +238,15 @@ public:
ServiceWorkerManager::AddScope(mOrderedScopes, aScope);
return registration;
}
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerDomainInfo)
private:
~ServiceWorkerDomainInfo()
{ }
};
nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
void
ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
@ -296,6 +308,15 @@ private:
ServiceWorkerDomainInfo* aDomainInfo,
void *aUnused);
already_AddRefed<ServiceWorkerDomainInfo>
GetDomainInfo(nsIDocument* aDoc);
already_AddRefed<ServiceWorkerDomainInfo>
GetDomainInfo(nsIURI* aURI);
already_AddRefed<ServiceWorkerDomainInfo>
GetDomainInfo(const nsCString& aURL);
already_AddRefed<ServiceWorkerRegistration>
GetServiceWorkerRegistration(nsPIDOMWindow* aWindow);
@ -314,6 +335,10 @@ private:
static void
RemoveScope(nsTArray<nsCString>& aList, const nsACString& aScope);
void
FireEventOnServiceWorkerContainers(ServiceWorkerRegistration* aRegistration,
const nsAString& aName);
};
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,