mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1080109 - Clear ServiceWorkers when clearing history or forgetting about site. r=baku,ehsan
This commit is contained in:
parent
c06477822f
commit
a5f604fd62
@ -92,6 +92,8 @@ http://sub2.test1.example.com:80 privileged
|
||||
http://sub2.test2.example.com:80 privileged
|
||||
http://noxul.example.com:80 privileged,noxul
|
||||
http://example.net:80 privileged
|
||||
# Used to test that clearing Service Workers for domain example.com, does not clear prefixexample.com
|
||||
http://prefixexample.com:80
|
||||
|
||||
https://example.com:443 privileged
|
||||
https://test1.example.com:443 privileged
|
||||
|
@ -33,7 +33,7 @@ interface nsIServiceWorkerInfo : nsISupports
|
||||
readonly attribute DOMString waitingCacheName;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(ff13ee00-5485-4551-af6f-1ab6de843365)]
|
||||
[scriptable, builtinclass, uuid(384c9aec-29e5-4bdb-abc2-fba10da83e17)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -111,6 +111,22 @@ interface nsIServiceWorkerManager : nsISupports
|
||||
*/
|
||||
void softUpdate(in DOMString aScope);
|
||||
|
||||
/*
|
||||
* Clears ServiceWorker registrations from memory and disk for the specified
|
||||
* host.
|
||||
* - All ServiceWorker instances change their state to redundant.
|
||||
* - Existing ServiceWorker instances handling fetches will keep running.
|
||||
* - All documents will immediately stop being controlled.
|
||||
* - Unregister jobs will be queued for all registrations.
|
||||
* This eventually results in the registration being deleted from disk too.
|
||||
*/
|
||||
void remove(in AUTF8String aHost);
|
||||
|
||||
/*
|
||||
* Clear all registrations for all hosts. See remove().
|
||||
*/
|
||||
void removeAll();
|
||||
|
||||
// Testing
|
||||
DOMString getScopeForUrl(in DOMString path);
|
||||
|
||||
|
@ -1253,6 +1253,26 @@ ContentChild::RecvUpdateServiceWorkerRegistrations()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvRemoveServiceWorkerRegistrationsForDomain(const nsString& aDomain)
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->Remove(NS_ConvertUTF16toUTF8(aDomain));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvRemoveServiceWorkerRegistrations()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
swm->RemoveAll();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static CancelableTask* sFirstIdleTask;
|
||||
|
||||
static void FirstIdle(void)
|
||||
|
@ -302,6 +302,10 @@ public:
|
||||
|
||||
virtual bool RecvUpdateServiceWorkerRegistrations() override;
|
||||
|
||||
virtual bool RecvRemoveServiceWorkerRegistrationsForDomain(const nsString& aDomain) override;
|
||||
|
||||
virtual bool RecvRemoveServiceWorkerRegistrations() override;
|
||||
|
||||
virtual bool RecvNotifyVisited(const URIParams& aURI) override;
|
||||
// auto remove when alertfinished is received.
|
||||
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
|
||||
|
@ -505,6 +505,10 @@ child:
|
||||
|
||||
async UpdateServiceWorkerRegistrations();
|
||||
|
||||
async RemoveServiceWorkerRegistrationsForDomain(nsString aDomain);
|
||||
|
||||
async RemoveServiceWorkerRegistrations();
|
||||
|
||||
async DataStoreNotify(uint32_t aAppId, nsString aName,
|
||||
nsString aManifestURL);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "mozilla/ErrorNames.h"
|
||||
#include "mozilla/LoadContext.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
@ -68,6 +69,9 @@ using namespace mozilla::ipc;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
#define PURGE_DOMAIN_DATA "browser:purge-domain-data"
|
||||
#define PURGE_SESSION_HISTORY "browser:purge-session-history"
|
||||
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
|
||||
"RequestMode enumeration value should match Necko CORS mode value.");
|
||||
static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
|
||||
@ -217,6 +221,7 @@ NS_IMPL_RELEASE(ServiceWorkerManager)
|
||||
NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
if (aIID.Equals(NS_GET_IID(ServiceWorkerManager)))
|
||||
foundInterface = static_cast<nsIServiceWorkerManager*>(this);
|
||||
else
|
||||
@ -236,6 +241,17 @@ ServiceWorkerManager::ServiceWorkerManager()
|
||||
nsTArray<ServiceWorkerRegistrationData> data;
|
||||
swr->GetRegistrations(data);
|
||||
LoadRegistrations(data);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
DebugOnly<nsresult> rv;
|
||||
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,6 +552,8 @@ class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
|
||||
UPDATE_JOB = 1,
|
||||
} mJobType;
|
||||
|
||||
bool mCanceled;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
@ -551,6 +569,7 @@ public:
|
||||
, mCallback(aCallback)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mJobType(REGISTER_JOB)
|
||||
, mCanceled(false)
|
||||
{ }
|
||||
|
||||
// [[Update]]
|
||||
@ -561,12 +580,27 @@ public:
|
||||
, mRegistration(aRegistration)
|
||||
, mCallback(aCallback)
|
||||
, mJobType(UPDATE_JOB)
|
||||
, mCanceled(false)
|
||||
{ }
|
||||
|
||||
bool
|
||||
IsRegisterJob() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Cancel()
|
||||
{
|
||||
mQueue = nullptr;
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
void
|
||||
Start() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm->HasBackgroundActor()) {
|
||||
@ -605,6 +639,12 @@ public:
|
||||
void
|
||||
ComparisonResult(nsresult aStatus, bool aInCacheAndEqual, const nsAString& aNewCacheName) override
|
||||
{
|
||||
nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
if (mCanceled) {
|
||||
Fail(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
||||
Fail(NS_ERROR_DOM_TYPE_ERR);
|
||||
return;
|
||||
@ -672,11 +712,19 @@ public:
|
||||
}
|
||||
|
||||
// Public so our error handling code can use it.
|
||||
// Callers MUST hold a strong ref before calling this!
|
||||
void
|
||||
Fail(const ErrorEventInit& aError)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->UpdateFailed(aError);
|
||||
nsRefPtr<ServiceWorkerUpdateFinishCallback> callback = mCallback.forget();
|
||||
// With cancellation support, we may only be running with one reference
|
||||
// from another object like a stream loader or something.
|
||||
// UpdateFailed may do something with that, so hold a ref to ourself since
|
||||
// FailCommon relies on it.
|
||||
// FailCommon does check for cancellation, but let's be safe here.
|
||||
nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
callback->UpdateFailed(aError);
|
||||
FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
|
||||
}
|
||||
|
||||
@ -684,12 +732,19 @@ public:
|
||||
void
|
||||
ContinueInstall()
|
||||
{
|
||||
// Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated
|
||||
// first.
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
|
||||
swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
|
||||
// This is effectively the end of Step 4.3 of the [[Update]] algorithm.
|
||||
// The invocation of [[Install]] is not part of the atomic block.
|
||||
|
||||
nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
if (mCanceled) {
|
||||
return Fail(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
// Begin [[Install]] atomic step 4.
|
||||
if (mRegistration->mInstallingWorker) {
|
||||
// FIXME(nsm): Terminate and stuff
|
||||
@ -703,6 +758,7 @@ public:
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
|
||||
|
||||
Succeed();
|
||||
// The job should NOT call fail from this point on.
|
||||
|
||||
// Step 4.6 "Queue a task..." for updatefound.
|
||||
nsCOMPtr<nsIRunnable> upr =
|
||||
@ -743,6 +799,8 @@ private:
|
||||
void
|
||||
Update()
|
||||
{
|
||||
// Since Update() is called synchronously from Start(), we can assert this.
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
MOZ_ASSERT(mRegistration);
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::ContinueUpdate);
|
||||
@ -755,6 +813,11 @@ private:
|
||||
ContinueUpdate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
if (mCanceled) {
|
||||
return Fail(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
if (mRegistration->mInstallingWorker) {
|
||||
// FIXME(nsm): "Terminate installing worker".
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
@ -800,7 +863,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
mCallback = nullptr;
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->MaybeRemoveRegistration(mRegistration);
|
||||
// Ensures that the job can't do anything useful from this point on.
|
||||
@ -812,18 +874,31 @@ private:
|
||||
// This MUST only be called when the job is still performing actions related
|
||||
// to registration or update. After the spec resolves the update promise, use
|
||||
// Done() with the failure code instead.
|
||||
// Callers MUST hold a strong ref before calling this!
|
||||
void
|
||||
Fail(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->UpdateFailed(aRv);
|
||||
nsRefPtr<ServiceWorkerUpdateFinishCallback> callback = mCallback.forget();
|
||||
// With cancellation support, we may only be running with one reference
|
||||
// from another object like a stream loader or something.
|
||||
// UpdateFailed may do something with that, so hold a ref to ourself since
|
||||
// FailCommon relies on it.
|
||||
// FailCommon does check for cancellation, but let's be safe here.
|
||||
nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
|
||||
callback->UpdateFailed(aRv);
|
||||
FailCommon(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
ContinueAfterInstallEvent(bool aInstallEventSuccess, bool aActivateImmediately)
|
||||
{
|
||||
if (NS_WARN_IF(!mRegistration->mInstallingWorker)) {
|
||||
if (mCanceled) {
|
||||
return Done(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
if (!mRegistration->mInstallingWorker) {
|
||||
NS_WARNING("mInstallingWorker was null.");
|
||||
return Done(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
@ -866,6 +941,28 @@ private:
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerRegisterJob, ServiceWorkerJob);
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::CancelJobs()
|
||||
{
|
||||
if (mJobs.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We have to treat the first job specially. It is the running job and needs
|
||||
// to be notified correctly.
|
||||
nsRefPtr<ServiceWorkerJob> runningJob = mJobs[0];
|
||||
// We can just let an Unregister job run to completion.
|
||||
if (runningJob->IsRegisterJob()) {
|
||||
ServiceWorkerRegisterJob* job = static_cast<ServiceWorkerRegisterJob*>(runningJob.get());
|
||||
job->Cancel();
|
||||
}
|
||||
|
||||
// Get rid of everything. Non-main thread objects may still be holding a ref
|
||||
// to the running register job. Since we called Cancel() on it, the job's
|
||||
// main thread functions will just exit.
|
||||
mJobs.Clear();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContinueUpdateRunnable::Run()
|
||||
{
|
||||
@ -1907,20 +2004,23 @@ ServiceWorkerManager::HandleError(JSContext* aCx,
|
||||
ServiceWorkerJobQueue* queue = mJobQueues.Get(aScope);
|
||||
MOZ_ASSERT(queue);
|
||||
ServiceWorkerJob* job = queue->Peek();
|
||||
ServiceWorkerRegisterJob* regJob = static_cast<ServiceWorkerRegisterJob*>(job);
|
||||
MOZ_ASSERT(regJob);
|
||||
if (job) {
|
||||
MOZ_ASSERT(job->IsRegisterJob());
|
||||
nsRefPtr<ServiceWorkerRegisterJob> regJob = static_cast<ServiceWorkerRegisterJob*>(job);
|
||||
|
||||
RootedDictionary<ErrorEventInit> init(aCx);
|
||||
init.mMessage = aMessage;
|
||||
init.mFilename = aFilename;
|
||||
init.mLineno = aLineNumber;
|
||||
init.mColno = aColumnNumber;
|
||||
RootedDictionary<ErrorEventInit> init(aCx);
|
||||
init.mMessage = aMessage;
|
||||
init.mFilename = aFilename;
|
||||
init.mLineno = aLineNumber;
|
||||
init.mColno = aColumnNumber;
|
||||
|
||||
NS_WARNING(nsPrintfCString(
|
||||
"Script error caused ServiceWorker registration to fail: %s:%u '%s'",
|
||||
NS_ConvertUTF16toUTF8(aFilename).get(), aLineNumber,
|
||||
NS_ConvertUTF16toUTF8(aMessage).get()).get());
|
||||
regJob->Fail(init);
|
||||
regJob->Fail(init);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2987,19 +3087,13 @@ ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRe
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
ServiceWorkerManager::RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
MOZ_ASSERT(aRegistration);
|
||||
MOZ_ASSERT(!aRegistration->IsControllingDocuments());
|
||||
MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
|
||||
ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
|
||||
|
||||
// Hold a ref since the hashtable may be the last ref.
|
||||
nsRefPtr<ServiceWorkerRegistrationInfo> reg;
|
||||
mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope,
|
||||
getter_AddRefs(reg));
|
||||
MOZ_ASSERT(reg);
|
||||
|
||||
// All callers should be either from a job in which case the actor is
|
||||
// available, or from MaybeStopControlling(), in which case, this will only be
|
||||
// called if a valid registration is found. If a valid registration exists,
|
||||
@ -3009,14 +3103,14 @@ ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistr
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
PrincipalInfo principalInfo;
|
||||
if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(reg->mPrincipal,
|
||||
if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aRegistration->mPrincipal,
|
||||
&principalInfo)))) {
|
||||
//XXXnsm I can't think of any other reason a stored principal would fail to
|
||||
//convert.
|
||||
NS_WARNING("Unable to unregister serviceworker due to possible OOM");
|
||||
return;
|
||||
}
|
||||
mActor->SendUnregisterServiceWorker(principalInfo, NS_ConvertUTF8toUTF16(reg->mScope));
|
||||
mActor->SendUnregisterServiceWorker(principalInfo, NS_ConvertUTF8toUTF16(aRegistration->mScope));
|
||||
}
|
||||
|
||||
class ServiceWorkerDataInfo final : public nsIServiceWorkerInfo
|
||||
@ -3042,7 +3136,86 @@ private:
|
||||
nsString mActiveCacheName;
|
||||
nsString mWaitingCacheName;
|
||||
};
|
||||
void
|
||||
ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
RemoveRegistrationInternal(aRegistration);
|
||||
MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
|
||||
mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* See browser/components/sessionstore/Utils.jsm function hasRootDomain().
|
||||
*
|
||||
* Returns true if the |url| passed in is part of the given root |domain|.
|
||||
* For example, if |url| is "www.mozilla.org", and we pass in |domain| as
|
||||
* "mozilla.org", this will return true. It would return false the other way
|
||||
* around.
|
||||
*/
|
||||
bool
|
||||
HasRootDomain(nsIURI* aURI, const nsACString& aDomain)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aURI);
|
||||
|
||||
nsAutoCString host;
|
||||
nsresult rv = aURI->GetHost(host);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsACString::const_iterator start, end;
|
||||
host.BeginReading(start);
|
||||
host.EndReading(end);
|
||||
if (!FindInReadable(aDomain, start, end)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host.Equals(aDomain)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Beginning of the string matches, can't look at the previous char.
|
||||
if (start.get() == host.BeginReading()) {
|
||||
// Equals failed so this is fine.
|
||||
return false;
|
||||
}
|
||||
|
||||
char prevChar = *(--start);
|
||||
return prevChar == '.';
|
||||
}
|
||||
|
||||
// If host/aData is null, unconditionally unregisters.
|
||||
PLDHashOperator
|
||||
UnregisterIfMatchesHost(const nsACString& aScope,
|
||||
ServiceWorkerRegistrationInfo* aReg,
|
||||
void* aData)
|
||||
{
|
||||
// We avoid setting toRemove = aReg by default since there is a possibility
|
||||
// of failure when aData is passed, in which case we don't want to remove the
|
||||
// registration.
|
||||
ServiceWorkerRegistrationInfo* toRemove = nullptr;
|
||||
if (aData) {
|
||||
const nsACString& domain = *static_cast<nsACString*>(aData);
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
|
||||
// This way subdomains are also cleared.
|
||||
if (NS_SUCCEEDED(rv) && HasRootDomain(scopeURI, domain)) {
|
||||
toRemove = aReg;
|
||||
}
|
||||
} else {
|
||||
toRemove = aReg;
|
||||
}
|
||||
|
||||
if (toRemove) {
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->ForceUnregister(toRemove);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
} // anonymous namespace
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerDataInfo, nsIServiceWorkerInfo)
|
||||
|
||||
/* static */ already_AddRefed<ServiceWorkerDataInfo>
|
||||
@ -3143,6 +3316,38 @@ ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
|
||||
void
|
||||
ServiceWorkerManager::ForceUnregister(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
MOZ_ASSERT(aRegistration);
|
||||
|
||||
ServiceWorkerJobQueue* mQueue;
|
||||
mJobQueues.Get(aRegistration->mScope, &mQueue);
|
||||
if (mQueue) {
|
||||
mQueue->CancelJobs();
|
||||
}
|
||||
|
||||
// Since Unregister is async, it is ok to call it in an enumeration.
|
||||
Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Remove(const nsACString& aHost)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mServiceWorkerRegistrationInfos.EnumerateRead(UnregisterIfMatchesHost, &const_cast<nsACString&>(aHost));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::RemoveAll()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mServiceWorkerRegistrationInfos.EnumerateRead(UnregisterIfMatchesHost, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
UpdateEachRegistration(const nsACString& aKey,
|
||||
ServiceWorkerRegistrationInfo* aInfo,
|
||||
@ -3165,6 +3370,43 @@ ServiceWorkerManager::UpdateAllRegistrations()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
|
||||
|
||||
nsAutoTArray<ContentParent*,1> children;
|
||||
ContentParent::GetAll(children);
|
||||
|
||||
if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
unused << children[i]->SendRemoveServiceWorkerRegistrations();
|
||||
}
|
||||
|
||||
RemoveAll();
|
||||
} else if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) {
|
||||
nsAutoString domain(aData);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
unused << children[i]->SendRemoveServiceWorkerRegistrationsForDomain(domain);
|
||||
}
|
||||
|
||||
Remove(NS_ConvertUTF16toUTF8(domain));
|
||||
} else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
|
||||
obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
|
||||
}
|
||||
} else {
|
||||
MOZ_CRASH("Received message we aren't supposed to be registered for!");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
@ -60,6 +61,9 @@ public:
|
||||
|
||||
virtual void Start() = 0;
|
||||
|
||||
virtual bool
|
||||
IsRegisterJob() const { return false; }
|
||||
|
||||
protected:
|
||||
explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue)
|
||||
: mQueue(aQueue)
|
||||
@ -78,8 +82,13 @@ class ServiceWorkerJobQueue final
|
||||
friend class ServiceWorkerJob;
|
||||
|
||||
nsTArray<nsRefPtr<ServiceWorkerJob>> mJobs;
|
||||
bool mPopping;
|
||||
|
||||
public:
|
||||
explicit ServiceWorkerJobQueue()
|
||||
: mPopping(false)
|
||||
{}
|
||||
|
||||
~ServiceWorkerJobQueue()
|
||||
{
|
||||
if (!mJobs.IsEmpty()) {
|
||||
@ -99,11 +108,16 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CancelJobs();
|
||||
|
||||
// Only used by HandleError, keep it that way!
|
||||
ServiceWorkerJob*
|
||||
Peek()
|
||||
{
|
||||
MOZ_ASSERT(!mJobs.IsEmpty());
|
||||
if (mJobs.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return mJobs[0];
|
||||
}
|
||||
|
||||
@ -111,6 +125,10 @@ private:
|
||||
void
|
||||
Pop()
|
||||
{
|
||||
MOZ_ASSERT(!mPopping,
|
||||
"Pop() called recursively, did you write a job which calls Done() synchronously from Start()?");
|
||||
AutoRestore<bool> savePopping(mPopping);
|
||||
mPopping = true;
|
||||
MOZ_ASSERT(!mJobs.IsEmpty());
|
||||
mJobs.RemoveElementAt(0);
|
||||
if (!mJobs.IsEmpty()) {
|
||||
@ -304,6 +322,7 @@ public:
|
||||
class ServiceWorkerManager final
|
||||
: public nsIServiceWorkerManager
|
||||
, public nsIIPCBackgroundChildCreateCallback
|
||||
, public nsIObserver
|
||||
{
|
||||
friend class GetReadyPromiseRunnable;
|
||||
friend class GetRegistrationsRunnable;
|
||||
@ -316,6 +335,7 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERMANAGER
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
|
||||
|
||||
static ServiceWorkerManager* FactoryCreate()
|
||||
@ -336,7 +356,7 @@ public:
|
||||
// memmoves associated with inserting stuff in the middle of the array.
|
||||
nsTArray<nsCString> mOrderedScopes;
|
||||
|
||||
// Scope to registration.
|
||||
// Scope to registration.
|
||||
// The scope should be a fully qualified valid URL.
|
||||
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mServiceWorkerRegistrationInfos;
|
||||
|
||||
@ -398,6 +418,11 @@ public:
|
||||
void LoadRegistrations(
|
||||
const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
|
||||
|
||||
// Used by remove() and removeAll() when clearing history.
|
||||
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
|
||||
void
|
||||
ForceUnregister(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
NS_IMETHOD
|
||||
AddRegistrationEventListener(const nsAString& aScope,
|
||||
ServiceWorkerRegistrationListener* aListener);
|
||||
@ -503,10 +528,18 @@ private:
|
||||
void* aUnused);
|
||||
|
||||
nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
|
||||
|
||||
|
||||
void
|
||||
MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
// Does all cleanup except removing the registration from
|
||||
// mServiceWorkerRegistrationInfos. This is useful when we clear
|
||||
// registrations via remove()/removeAll() since we are iterating over the
|
||||
// hashtable and can cleanly remove within the hashtable enumeration
|
||||
// function.
|
||||
void
|
||||
RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
mozilla::ipc::PBackgroundChild* mActor;
|
||||
|
||||
struct PendingOperation;
|
||||
|
@ -78,6 +78,10 @@ support-files =
|
||||
periodic/register.html
|
||||
periodic/wait_for_update.html
|
||||
periodic/unregister.html
|
||||
sanitize/frame.html
|
||||
sanitize/register.html
|
||||
sanitize/example_check_and_unregister.html
|
||||
sanitize_worker.js
|
||||
|
||||
[test_unregister.html]
|
||||
[test_installation_simple.html]
|
||||
@ -110,3 +114,5 @@ support-files =
|
||||
[test_empty_serviceworker.html]
|
||||
[test_periodic_update.html]
|
||||
[test_periodic_https_update.html]
|
||||
[test_sanitize.html]
|
||||
[test_sanitize_domain.html]
|
||||
|
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
function done(exists) {
|
||||
parent.postMessage(exists, '*');
|
||||
}
|
||||
|
||||
function fail() {
|
||||
parent.postMessage("FAIL", '*');
|
||||
}
|
||||
|
||||
navigator.serviceWorker.getRegistration(".").then(function(reg) {
|
||||
if (reg) {
|
||||
reg.unregister().then(done.bind(undefined, true), fail);
|
||||
} else {
|
||||
dump("getRegistration() returned undefined registration\n");
|
||||
done(false);
|
||||
}
|
||||
}, function(e) {
|
||||
dump("getRegistration() failed\n");
|
||||
fail();
|
||||
});
|
||||
</script>
|
||||
|
11
dom/workers/test/serviceworkers/sanitize/frame.html
Normal file
11
dom/workers/test/serviceworkers/sanitize/frame.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
fetch("intercept-this").then(function(r) {
|
||||
if (!r.ok) {
|
||||
return "FAIL";
|
||||
}
|
||||
return r.text();
|
||||
}).then(function(body) {
|
||||
parent.postMessage(body, '*');
|
||||
});
|
||||
</script>
|
10
dom/workers/test/serviceworkers/sanitize/register.html
Normal file
10
dom/workers/test/serviceworkers/sanitize/register.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
function done() {
|
||||
parent.postMessage('', '*');
|
||||
}
|
||||
|
||||
navigator.serviceWorker.ready.then(done);
|
||||
navigator.serviceWorker.register("../sanitize_worker.js", {scope: "."});
|
||||
</script>
|
||||
|
5
dom/workers/test/serviceworkers/sanitize_worker.js
Normal file
5
dom/workers/test/serviceworkers/sanitize_worker.js
Normal file
@ -0,0 +1,5 @@
|
||||
onfetch = function(e) {
|
||||
if (e.request.url.indexOf("intercept-this") != -1) {
|
||||
e.respondWith(new Response("intercepted"));
|
||||
}
|
||||
}
|
87
dom/workers/test/serviceworkers/test_sanitize.html
Normal file
87
dom/workers/test/serviceworkers/test_sanitize.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1080109 - Clear ServiceWorker registrations for all domains</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function start() {
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
|
||||
function testNotIntercepted() {
|
||||
testFrame("sanitize/frame.html").then(function(body) {
|
||||
is(body, "FAIL", "Expected frame to not be controlled");
|
||||
// No need to unregister since that already happened.
|
||||
navigator.serviceWorker.getRegistration("sanitize/foo").then(function(reg) {
|
||||
ok(reg === undefined, "There should no longer be a valid registration");
|
||||
}, function(e) {
|
||||
ok(false, "getRegistration() should not error");
|
||||
}).then(function(e) {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
registerSW().then(function() {
|
||||
return testFrame("sanitize/frame.html").then(function(body) {
|
||||
is(body, "intercepted", "Expected serviceworker to intercept request");
|
||||
});
|
||||
}).then(function() {
|
||||
return navigator.serviceWorker.getRegistration("sanitize/foo");
|
||||
}).then(function(reg) {
|
||||
reg.active.onstatechange = function(e) {
|
||||
e.target.onstatechange = null;
|
||||
ok(e.target.state, "redundant", "On clearing data, serviceworker should become redundant");
|
||||
testNotIntercepted();
|
||||
};
|
||||
}).then(function() {
|
||||
SpecialPowers.removeAllServiceWorkerData();
|
||||
});
|
||||
}
|
||||
|
||||
function testFrame(src) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = src;
|
||||
window.onmessage = function(message) {
|
||||
window.onmessage = null;
|
||||
iframe.src = "about:blank";
|
||||
document.body.removeChild(iframe);
|
||||
iframe = null;
|
||||
SpecialPowers.exactGC(window, function() {
|
||||
resolve(message.data);
|
||||
});
|
||||
};
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
function registerSW() {
|
||||
return testFrame("sanitize/register.html");
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]}, function() {
|
||||
start();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
90
dom/workers/test/serviceworkers/test_sanitize_domain.html
Normal file
90
dom/workers/test/serviceworkers/test_sanitize_domain.html
Normal file
@ -0,0 +1,90 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1080109 - Clear ServiceWorker registrations for specific domains</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function start() {
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
|
||||
function checkDomainRegistration(domain, exists) {
|
||||
return testFrame("http://" + domain + "/tests/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html").then(function(body) {
|
||||
if (body === "FAIL") {
|
||||
ok(false, "Error acquiring registration or unregistering for " + domain);
|
||||
} else {
|
||||
if (exists) {
|
||||
ok(body === true, "Expected " + domain + " to still have a registration.");
|
||||
} else {
|
||||
ok(body === false, "Expected " + domain + " to have no registration.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registerSW().then(function() {
|
||||
return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/frame.html").then(function(body) {
|
||||
is(body, "intercepted", "Expected serviceworker to intercept request");
|
||||
});
|
||||
}).then(function() {
|
||||
SpecialPowers.removeServiceWorkerDataForExampleDomain();
|
||||
}).then(function() {
|
||||
return checkDomainRegistration("prefixexample.com", true /* exists */)
|
||||
.then(function(e) {
|
||||
return checkDomainRegistration("example.com", false /* exists */);
|
||||
}).then(function(e) {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function testFrame(src) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.src = src;
|
||||
window.onmessage = function(message) {
|
||||
window.onmessage = null;
|
||||
iframe.src = "about:blank";
|
||||
document.body.removeChild(iframe);
|
||||
iframe = null;
|
||||
SpecialPowers.exactGC(window, function() {
|
||||
resolve(message.data);
|
||||
});
|
||||
};
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
function registerSW() {
|
||||
return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/register.html")
|
||||
.then(function(e) {
|
||||
// Register for prefixexample.com and then ensure it does not get unregistered.
|
||||
return testFrame("http://prefixexample.com/tests/dom/workers/test/serviceworkers/sanitize/register.html");
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]}, function() {
|
||||
start();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1926,6 +1926,14 @@ SpecialPowersAPI.prototype = {
|
||||
startPeriodicServiceWorkerUpdates: function() {
|
||||
return this._sendSyncMessage('SPPeriodicServiceWorkerUpdates', {});
|
||||
},
|
||||
|
||||
removeAllServiceWorkerData: function() {
|
||||
this.notifyObserversInParentProcess(null, "browser:purge-session-history", "");
|
||||
},
|
||||
|
||||
removeServiceWorkerDataForExampleDomain: function() {
|
||||
this.notifyObserversInParentProcess(null, "browser:purge-domain-data", "example.com");
|
||||
},
|
||||
};
|
||||
|
||||
this.SpecialPowersAPI = SpecialPowersAPI;
|
||||
|
Loading…
Reference in New Issue
Block a user