Backed out 14 changesets (bug 1425975) for M(5) permafails mochitest/test_ext_webrequest_filter.html. r=backout a=backout

Backed out changeset e6f4a2d1df9a (bug 1425975)
Backed out changeset 1e657fa97b71 (bug 1425975)
Backed out changeset 9e1544ec814d (bug 1425975)
Backed out changeset 0e50d9d1d069 (bug 1425975)
Backed out changeset fb89dbd922ba (bug 1425975)
Backed out changeset f2b451ce55d4 (bug 1425975)
Backed out changeset 4ce186c6d8f5 (bug 1425975)
Backed out changeset 6f520ab76d6a (bug 1425975)
Backed out changeset f091f5e182c4 (bug 1425975)
Backed out changeset 82d39ed8c831 (bug 1425975)
Backed out changeset df13eba47970 (bug 1425975)
Backed out changeset 553628a56e6a (bug 1425975)
Backed out changeset acf4d61babab (bug 1425975)
Backed out changeset b7ae11b5bce8 (bug 1425975)
This commit is contained in:
Csoregi Natalia 2017-12-24 00:23:57 +02:00
parent 842032609a
commit 0bf10c4d4e
20 changed files with 158 additions and 421 deletions

View File

@ -155,7 +155,6 @@
#include "mozIAsyncFavicons.h"
#endif
#include "nsINetworkPredictor.h"
#include "nsIServiceWorkerManager.h"
// Editor-related
#include "nsIEditingSession.h"
@ -3418,7 +3417,7 @@ nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
// Don't pre-allocate the client when we are sandboxed. The inherited
// principal does not take sandboxing into account.
// TODO: Refactor sandboxing principal code out so we can use it here.
if (!aPrincipal && mSandboxFlags) {
if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
return;
}
@ -3460,35 +3459,18 @@ nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
return;
}
// We're done if there is no parent controller. Also, don't inherit
// the controller if we're sandboxed. This matches our behavior in
// ShouldPrepareForIntercept(),
Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
if (controller.isNothing() || mSandboxFlags) {
return;
}
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
if (!swm) {
if (controller.isNothing()) {
return;
}
// If the parent is controlled then propagate that controller to the
// initial about:blank client as well. This will set the controller
// in the ClientManagerService in the parent.
//
// Note: If the registration is missing from the SWM we avoid setting
// the controller on the client. We can do this synchronously
// for now since SWM is in the child process. In the future
// when SWM is in the parent process we will probably have to
// always set the initial client source and then somehow clear
// it if we find the registration is acutally gone. Its also
// possible this race only occurs in cases where the resulting
// window is no longer exposed. For example, in theory the SW
// should not go away if our parent window is controlled.
if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
return;
}
RefPtr<ClientHandle> handle =
ClientManager::CreateHandle(mInitialClientSource->Info(),
parentInner->EventTargetFor(TaskCategory::Other));
handle->Control(controller.ref());
// Also mark the ClientSource as controlled directly in case script
// immediately accesses navigator.serviceWorker.controller.

View File

@ -5599,10 +5599,7 @@ nsDocument::DispatchContentLoadedEvents()
using mozilla::dom::workers::ServiceWorkerManager;
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (swm) {
Maybe<ClientInfo> clientInfo = GetClientInfo();
if (clientInfo.isSome()) {
swm->MaybeCheckNavigationUpdate(clientInfo.ref());
}
swm->MaybeCheckNavigationUpdate(this);
}
}

View File

@ -1786,34 +1786,11 @@ nsGlobalWindowInner::EnsureClientSource()
}
}
// Verify the final ClientSource principal matches the final document
// principal. The ClientChannelHelper handles things like network
// redirects, but there are other ways the document principal can change.
// For example, if something sets the nsIChannel.owner property, then
// the final channel principal can be anything. Unfortunately there is
// no good way to detect this until after the channel completes loading.
//
// For now we handle this just by reseting the ClientSource. This will
// result in a new ClientSource with the correct principal being created.
// To APIs like ServiceWorker and Clients API it will look like there was
// an initial content page created that was then immediately replaced.
// This is pretty close to what we are actually doing.
if (mClientSource) {
nsCOMPtr<nsIPrincipal> clientPrincipal(mClientSource->Info().GetPrincipal());
if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
mClientSource.reset();
}
}
// If we don't have a reserved client or an initial client, then create
// one now. This can happen in certain cases where we avoid preallocating
// the client in the docshell. This mainly occurs in situations where
// the principal is not clearly inherited from the parent; e.g. sandboxed
// iframes, window.open(), etc.
//
// We also do this late ClientSource creation if the final document ended
// up with a different principal.
//
// TODO: We may not be marking initial about:blank documents created
// this way as controlled by a service worker properly. The
// controller should be coming from the same place as the inheritted
@ -1827,23 +1804,13 @@ nsGlobalWindowInner::EnsureClientSource()
mDoc->NodePrincipal());
MOZ_DIAGNOSTIC_ASSERT(mClientSource);
newClientSource = true;
// Note, we don't apply the loadinfo controller below if we create
// the ClientSource here.
}
// The load may have started controlling the Client as well. If
// so, mark it as controlled immediately here. The actor may
// or may not have been notified by the parent side about being
// controlled yet.
//
// Note: We should be careful not to control a client that was created late.
// These clients were not seen by the ServiceWorkerManager when it
// marked the LoadInfo controlled and it won't know about them. Its
// also possible we are creating the client late due to the final
// principal changing and these clients should definitely not be
// controlled by a service worker with a different principal.
else if (loadInfo) {
if (loadInfo) {
const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
if (controller.isSome()) {
mClientSource->SetController(controller.ref());

View File

@ -65,16 +65,6 @@ ClientHandle::StartOp(const ClientOpConstructorArgs& aArgs)
return ref.forget();
}
void
ClientHandle::OnShutdownThing()
{
NS_ASSERT_OWNINGTHREAD(ClientHandle);
if (!mDetachPromise) {
return;
}
mDetachPromise->Resolve(true, __func__);
}
ClientHandle::ClientHandle(ClientManager* aManager,
nsISerialEventTarget* aSerialEventTarget,
const ClientInfo& aClientInfo)
@ -192,21 +182,5 @@ ClientHandle::PostMessage(StructuredCloneData& aData,
return ref.forget();
}
RefPtr<GenericPromise>
ClientHandle::OnDetach()
{
NS_ASSERT_OWNINGTHREAD(ClientSource);
if (!mDetachPromise) {
mDetachPromise = new GenericPromise::Private(__func__);
if (IsShutdown()) {
mDetachPromise->Resolve(true, __func__);
}
}
RefPtr<GenericPromise> ref(mDetachPromise);
return Move(ref);
}
} // namespace dom
} // namespace mozilla

View File

@ -42,7 +42,6 @@ class ClientHandle final : public ClientThing<ClientHandleChild>
RefPtr<ClientManager> mManager;
nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
RefPtr<GenericPromise::Private> mDetachPromise;
ClientInfo mClientInfo;
~ClientHandle();
@ -53,10 +52,6 @@ class ClientHandle final : public ClientThing<ClientHandleChild>
already_AddRefed<ClientOpPromise>
StartOp(const ClientOpConstructorArgs& aArgs);
// ClientThing interface
void
OnShutdownThing() override;
// Private methods called by ClientHandleChild
void
ExecutionReady(const ClientInfo& aClientInfo);
@ -95,17 +90,6 @@ public:
PostMessage(ipc::StructuredCloneData& aData,
const ServiceWorkerDescriptor& aSource);
// Return a Promise that resolves when the ClientHandle object is detached
// from its remote actors. This will happen if the ClientSource is destroyed
// and triggers the cleanup of the handle actors. It will also naturally
// happen when the ClientHandle is de-referenced and tears down its own
// actors.
//
// Note: This method can only be called on the ClientHandle owning thread,
// but the MozPromise lets you Then() to another thread.
RefPtr<GenericPromise>
OnDetach();
NS_INLINE_DECL_REFCOUNTING(ClientHandle);
};

View File

@ -7,13 +7,11 @@
#include "ClientInfo.h"
#include "mozilla/dom/ClientIPCTypes.h"
#include "mozilla/ipc/BackgroundUtils.h"
namespace mozilla {
namespace dom {
using mozilla::ipc::PrincipalInfo;
using mozilla::ipc::PrincipalInfoToPrincipal;
ClientInfo::ClientInfo(const nsID& aId,
ClientType aType,
@ -140,13 +138,5 @@ ClientInfo::IsPrivateBrowsing() const
}
}
nsCOMPtr<nsIPrincipal>
ClientInfo::GetPrincipal() const
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
return Move(ref);
}
} // namespace dom
} // namespace mozilla

View File

@ -95,11 +95,6 @@ public:
// Determine if the client is in private browsing mode.
bool
IsPrivateBrowsing() const;
// Get a main-thread nsIPrincipal for the client. This may return nullptr
// if the PrincipalInfo() fails to deserialize for some reason.
nsCOMPtr<nsIPrincipal>
GetPrincipal() const;
};
} // namespace dom

View File

@ -206,14 +206,6 @@ ClientSource::WorkerExecutionReady(WorkerPrivate* aWorkerPrivate)
return;
}
// A client without access to storage should never be controlled by
// a service worker. Check this here in case we were controlled before
// execution ready. We can't reliably determine what our storage policy
// is before execution ready, unfortunately.
if (mController.isSome()) {
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate->IsStorageAllowed());
}
// Its safe to store the WorkerPrivate* here because the ClientSource
// is explicitly destroyed by WorkerPrivate before exiting its run loop.
MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
@ -243,15 +235,6 @@ ClientSource::WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow)
return NS_ERROR_UNEXPECTED;
}
// A client without access to storage should never be controlled by
// a service worker. Check this here in case we were controlled before
// execution ready. We can't reliably determine what our storage policy
// is before execution ready, unfortunately.
if (mController.isSome()) {
MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(aInnerWindow) ==
nsContentUtils::StorageAccess::eAllow);
}
// Don't use nsAutoCString here since IPC requires a full nsCString anyway.
nsCString spec;
@ -307,10 +290,6 @@ ClientSource::DocShellExecutionReady(nsIDocShell* aDocShell)
return NS_ERROR_UNEXPECTED;
}
// Note: We don't assert storage access for a controlled client. If
// the about:blank actually gets used then WindowExecutionReady() will
// get called which asserts storage access.
// TODO: dedupe this with WindowExecutionReady
FrameType frameType = FrameType::Top_level;
if (!outer->IsTopLevelWindow()) {
@ -381,16 +360,6 @@ ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)
// this invariant.
MOZ_DIAGNOSTIC_ASSERT(!mClientInfo.IsPrivateBrowsing());
// A client without access to storage should never be controlled a
// a service worker. If we are already execution ready with a real
// window or worker, then verify assert the storage policy is correct.
if (GetInnerWindow()) {
MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(GetInnerWindow()) ==
nsContentUtils::StorageAccess::eAllow);
} else if (GetWorkerPrivate()) {
MOZ_DIAGNOSTIC_ASSERT(GetWorkerPrivate()->IsStorageAllowed());
}
if (mController.isSome() && mController.ref() == aServiceWorker) {
return;
}

View File

@ -90,15 +90,6 @@ protected:
mActor->MaybeStartTeardown();
mActor = nullptr;
}
OnShutdownThing();
}
// Allow extending classes to take action when shutdown.
virtual void
OnShutdownThing()
{
// by default do nothing
}
public:
@ -115,8 +106,6 @@ public:
// instead of calling ShutdownThing() to avoid calling MaybeStartTeardown()
// on the destroyed actor.
mShutdown = true;
OnShutdownThing();
}
};

View File

@ -15,17 +15,6 @@ interface nsIInterceptedChannel;
interface nsIPrincipal;
interface nsIRunnable;
interface nsIURI;
%{C++
namespace mozilla {
namespace dom {
class ClientInfo;
class ServiceWorkerDescriptor;
} // namespace dom
} // namespace mozilla
%}
[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
[scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
interface nsIServiceWorkerUnregisterCallback : nsISupports
@ -161,9 +150,6 @@ interface nsIServiceWorkerManager : nsISupports
*/
[notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
[notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
in const_ServiceWorkerDescriptorRef aServiceWorker);
/**
* Documents that have called MaybeStartControlling() should call this when
* they are destroyed. This function may be called multiple times, and is

View File

@ -313,75 +313,6 @@ ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
mActor = static_cast<ServiceWorkerManagerChild*>(actor);
}
RefPtr<GenericPromise>
ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
ServiceWorkerRegistrationInfo* aRegistrationInfo)
{
MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
RefPtr<GenericPromise> ref;
const ServiceWorkerDescriptor& active =
aRegistrationInfo->GetActive()->Descriptor();
auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
if (entry) {
RefPtr<ServiceWorkerRegistrationInfo> old =
entry.Data()->mRegistrationInfo.forget();
ref = Move(entry.Data()->mClientHandle->Control(active));
entry.Data()->mRegistrationInfo = aRegistrationInfo;
if (old != aRegistrationInfo) {
StopControllingRegistration(old);
aRegistrationInfo->StartControllingClient();
}
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
return Move(ref);
}
RefPtr<ClientHandle> clientHandle =
ClientManager::CreateHandle(aClientInfo,
SystemGroup::EventTargetFor(TaskCategory::Other));
ref = Move(clientHandle->Control(active));
aRegistrationInfo->StartControllingClient();
entry.OrInsert([&] {
return new ControlledClientData(clientHandle, aRegistrationInfo);
});
RefPtr<ServiceWorkerManager> self(this);
clientHandle->OnDetach()->Then(
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
[self = Move(self), aClientInfo] {
self->StopControllingClient(aClientInfo);
});
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
return Move(ref);
}
void
ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
{
auto entry = mControlledClients.Lookup(aClientInfo.Id());
if (!entry) {
return;
}
RefPtr<ServiceWorkerRegistrationInfo> reg =
entry.Data()->mRegistrationInfo.forget();
entry.Remove();
StopControllingRegistration(reg);
}
void
ServiceWorkerManager::MaybeStartShutdown()
{
@ -1501,13 +1432,8 @@ ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
{
AssertIsOnMainThread();
Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
if (clientInfo.isNothing()) {
return nullptr;
}
RefPtr<ServiceWorkerRegistrationInfo> registration;
GetClientRegistration(clientInfo.ref(), getter_AddRefs(registration));
GetDocumentRegistration(aDocument, getter_AddRefs(registration));
if (!registration) {
return nullptr;
@ -1652,7 +1578,7 @@ ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker)
return;
}
if (!reg->IsControllingClients() && reg->mPendingUninstall) {
if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
RemoveRegistration(reg);
return;
}
@ -2343,25 +2269,15 @@ ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo*
entry.Remove();
}
// Verify there are no controlled clients for the purged registration.
for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
auto& reg = iter.UserData()->mRegistrationInfo;
if (reg->mScope.Equals(aRegistration->mScope)) {
MOZ_DIAGNOSTIC_ASSERT(false,
"controlled client when removing registration");
iter.Remove();
break;
}
}
// Registration lifecycle is managed via mControlledClients now. Do not
// assert on on mControlledDocuments as races may cause this to still be
// set when the registration is destroyed.
// The registration should generally only be removed if there are no controlled
// documents, but mControlledDocuments can contain references to potentially
// controlled docs. This happens when the service worker is not active yet.
// We must purge these references since we are evicting the registration.
for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerRegistrationInfo* reg = iter.UserData();
MOZ_ASSERT(reg);
if (reg->mScope.Equals(aRegistration->mScope)) {
iter.Remove();
break;
}
}
@ -2392,49 +2308,32 @@ ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
MOZ_ASSERT(aDoc);
RefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(aDoc);
if (registration && registration->GetActive() &&
aDoc->GetSandboxFlags() == 0) {
if (registration) {
MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
StartControllingADocument(registration, aDoc);
}
}
bool
ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
const ServiceWorkerDescriptor& aServiceWorker)
{
AssertIsOnMainThread();
nsCOMPtr<nsIPrincipal> principal =
PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
NS_ENSURE_TRUE(principal, false);
nsCOMPtr<nsIURI> scope;
nsresult rv =
NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
NS_ENSURE_SUCCESS(rv, false);
RefPtr<ServiceWorkerRegistrationInfo> registration =
GetServiceWorkerRegistrationInfo(principal, scope);
NS_ENSURE_TRUE(registration, false);
StartControllingClient(aClientInfo, registration);
return true;
}
void
ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
{
AssertIsOnMainThread();
MOZ_ASSERT(aDoc);
mControlledDocuments.Remove(aDoc);
RefPtr<ServiceWorkerRegistrationInfo> registration;
mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
// A document which was uncontrolled does not maintain that state itself, so
// it will always call MaybeStopControlling() even if there isn't an
// associated registration. So this check is required.
if (registration) {
StopControllingADocument(registration);
}
}
void
ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
{
AssertIsOnMainThread();
MOZ_ASSERT(aDoc);
// We perform these success path navigation update steps when the
// document tells us its more or less done loading. This avoids
// slowing down page load and also lets pages consistently get
@ -2444,27 +2343,52 @@ ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
// 9.8.22 Else: (respondWith was entered and succeeded)
// If request is a non-subresource request, then: Invoke Soft Update
// algorithm.
ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
if (data && data->mRegistrationInfo) {
data->mRegistrationInfo->MaybeScheduleUpdate();
RefPtr<ServiceWorkerRegistrationInfo> registration;
mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
if (registration) {
registration->MaybeScheduleUpdate();
}
}
void
RefPtr<GenericPromise>
ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
nsIDocument* aDoc)
{
MOZ_ASSERT(aRegistration);
MOZ_ASSERT(aDoc);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
aRegistration->StartControllingADocument();
mControlledDocuments.Put(aDoc, aRegistration);
// Mark the document's ClientSource as controlled using the ClientHandle
// interface. While we could get at the ClientSource directly from the
// document here, our goal is to move ServiceWorkerManager to a separate
// process. Using the ClientHandle supports this remote operation.
ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
if (activeWorker && clientInfo.isSome()) {
RefPtr<ClientHandle> clientHandle =
ClientManager::CreateHandle(clientInfo.ref(),
SystemGroup::EventTargetFor(TaskCategory::Other));
ref = Move(clientHandle->Control(activeWorker->Descriptor()));
}
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
return Move(ref);
}
void
ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
{
aRegistration->StopControllingClient();
if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
aRegistration->StopControllingADocument();
if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
return;
}
@ -2794,7 +2718,10 @@ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttribut
// First, attempt to mark the reserved client controlled directly. This
// will update the controlled status in the ClientManagerService in the
// parent. It will also eventually propagate back to the ClientSource.
StartControllingClient(clientInfo.ref(), registration);
RefPtr<ClientHandle> clientHandle =
ClientManager::CreateHandle(clientInfo.ref(),
SystemGroup::EventTargetFor(TaskCategory::Other));
clientHandle->Control(serviceWorker->Descriptor());
}
// But we also note the reserved state on the LoadInfo. This allows the
@ -2858,21 +2785,20 @@ ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal,
}
nsresult
ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
ServiceWorkerRegistrationInfo** aRegistrationInfo)
ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
ServiceWorkerRegistrationInfo** aRegistrationInfo)
{
ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
if (!data || !data->mRegistrationInfo) {
RefPtr<ServiceWorkerRegistrationInfo> registration;
if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
return NS_ERROR_NOT_AVAILABLE;
}
// If the document is controlled, the current worker MUST be non-null.
if (!data->mRegistrationInfo->GetActive()) {
if (!registration->GetActive()) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
ref.forget(aRegistrationInfo);
registration.forget(aRegistrationInfo);
return NS_OK;
}
@ -3243,21 +3169,13 @@ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
return ref.forget();
}
Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
if (NS_WARN_IF(clientInfo.isNothing())) {
ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
__func__);
return ref.forget();
}
// The registration that should be controlling the client
RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
GetServiceWorkerRegistrationInfo(aDocument);
// The registration currently controlling the client
RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
GetClientRegistration(clientInfo.ref(),
getter_AddRefs(controllingRegistration));
GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
if (aWorkerRegistration != matchingRegistration ||
aWorkerRegistration == controllingRegistration) {
@ -3265,8 +3183,11 @@ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
return ref.forget();
}
StartControllingADocument(aWorkerRegistration, aDocument);
ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
if (controllingRegistration) {
StopControllingADocument(controllingRegistration);
}
ref = StartControllingADocument(aWorkerRegistration, aDocument);
return ref.forget();
}
@ -3327,19 +3248,31 @@ ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRe
RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
MOZ_DIAGNOSTIC_ASSERT(activeWorker);
AutoTArray<RefPtr<ClientHandle>, 16> handleList;
for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
if (iter.UserData()->mRegistrationInfo != aRegistration) {
AutoTArray<nsCOMPtr<nsIDocument>, 16> docList;
for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
if (iter.UserData() != aRegistration) {
continue;
}
handleList.AppendElement(iter.UserData()->mClientHandle);
nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
if (NS_WARN_IF(!doc)) {
continue;
}
docList.AppendElement(doc.forget());
}
// Fire event after iterating mControlledClients is done to prevent
// Fire event after iterating mControlledDocuments is done to prevent
// modification by reentering from the event handlers during iteration.
for (auto& handle : handleList) {
handle->Control(activeWorker->Descriptor());
for (auto& doc : docList) {
Maybe<ClientInfo> clientInfo = doc->GetClientInfo();
if (clientInfo.isNothing()) {
continue;
}
RefPtr<ClientHandle> clientHandle =
ClientManager::CreateHandle(clientInfo.ref(),
SystemGroup::EventTargetFor(TaskCategory::Other));
clientHandle->Control(activeWorker->Descriptor());
}
}

View File

@ -21,7 +21,6 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ClientHandle.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ServiceWorkerCommon.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
@ -106,21 +105,6 @@ public:
nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
struct ControlledClientData
{
RefPtr<ClientHandle> mClientHandle;
RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
ControlledClientData(ClientHandle* aClientHandle,
ServiceWorkerRegistrationInfo* aRegistrationInfo)
: mClientHandle(aClientHandle)
, mRegistrationInfo(aRegistrationInfo)
{
}
};
nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
// Track all documents that have attempted to register a service worker for a
// given scope.
typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
@ -320,7 +304,7 @@ public:
ServiceWorkerRegistrationListener* aListener);
void
MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
MaybeCheckNavigationUpdate(nsIDocument* aDoc);
nsresult
SendPushEvent(const nsACString& aOriginAttributes,
@ -344,13 +328,6 @@ private:
void
Init(ServiceWorkerRegistrar* aRegistrar);
RefPtr<GenericPromise>
StartControllingClient(const ClientInfo& aClientInfo,
ServiceWorkerRegistrationInfo* aRegistrationInfo);
void
StopControllingClient(const ClientInfo& aClientInfo);
void
MaybeStartShutdown();
@ -372,8 +349,8 @@ private:
Update(ServiceWorkerRegistrationInfo* aRegistration);
nsresult
GetClientRegistration(const ClientInfo& aClientInfo,
ServiceWorkerRegistrationInfo** aRegistrationInfo);
GetDocumentRegistration(nsIDocument* aDoc,
ServiceWorkerRegistrationInfo** aRegistrationInfo);
nsresult
GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
@ -398,12 +375,12 @@ private:
void
NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
void
RefPtr<GenericPromise>
StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
nsIDocument* aDoc);
void
StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
already_AddRefed<ServiceWorkerRegistrationInfo>
GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);

View File

@ -81,7 +81,7 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
const nsACString& aScope,
nsIPrincipal* aPrincipal,
ServiceWorkerUpdateViaCache aUpdateViaCache)
: mControlledClientsCounter(0)
: mControlledDocumentsCounter(0)
, mUpdateState(NoUpdate)
, mCreationTime(PR_Now())
, mCreationTimeStamp(TimeStamp::Now())
@ -94,7 +94,9 @@ ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
{
MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
if (IsControllingDocuments()) {
NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
}
}
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
@ -246,7 +248,7 @@ void
ServiceWorkerRegistrationInfo::TryToActivate()
{
AssertIsOnMainThread();
bool controlling = IsControllingClients();
bool controlling = IsControllingDocuments();
bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
bool idle = IsIdle();
if (idle && (!controlling || skipWaiting)) {

View File

@ -17,7 +17,7 @@ namespace workers {
class ServiceWorkerRegistrationInfo final
: public nsIServiceWorkerRegistrationInfo
{
uint32_t mControlledClientsCounter;
uint32_t mControlledDocumentsCounter;
enum
{
@ -79,22 +79,22 @@ public:
GetServiceWorkerInfoById(uint64_t aId);
void
StartControllingClient()
StartControllingADocument()
{
++mControlledClientsCounter;
++mControlledDocumentsCounter;
}
void
StopControllingClient()
StopControllingADocument()
{
MOZ_ASSERT(mControlledClientsCounter);
--mControlledClientsCounter;
MOZ_ASSERT(mControlledDocumentsCounter);
--mControlledDocumentsCounter;
}
bool
IsControllingClients() const
IsControllingDocuments() const
{
return mActiveWorker && mControlledClientsCounter;
return mActiveWorker && mControlledDocumentsCounter;
}
void

View File

@ -138,7 +138,7 @@ ServiceWorkerUnregisterJob::Unregister()
InvokeResultCallbacks(NS_OK);
// "If no service worker client is using registration..."
if (!registration->IsControllingClients() && registration->IsIdle()) {
if (!registration->IsControllingDocuments() && registration->IsIdle()) {
// "Invoke [[Clear Registration]]..."
swm->RemoveRegistration(registration);
}

View File

@ -19,6 +19,10 @@
info("skip_waiting_scope/index.html shouldn't be launched directly!");
}
navigator.serviceWorker.ready.then(function() {
parent.postMessage("READY", "*");
});
navigator.serviceWorker.oncontrollerchange = function() {
parent.postMessage({
event: "controllerchange",

View File

@ -13,7 +13,6 @@
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script src="utils.js"></script>
<script class="testbody" type="text/javascript">
var registration, iframe, content;
@ -22,9 +21,19 @@
{scope: "./skip_waiting_scope/"});
}
async function waitForActivated(swr) {
function waitForActivated(swr) {
registration = swr;
await waitForState(registration.installing, "activated")
var promise = new Promise(function(resolve, reject) {
window.onmessage = function(e) {
if (e.data === "READY") {
ok(true, "Active worker is activated now");
resolve();
} else {
ok(false, "Wrong value. Somenting went wrong");
resolve();
}
}
});
iframe = document.createElement("iframe");
iframe.setAttribute("src", "skip_waiting_scope/index.html");
@ -32,7 +41,7 @@
content = document.getElementById("content");
content.appendChild(iframe);
await new Promise(resolve => iframe.onload = resolve);
return promise;
}
function checkWhetherItSkippedWaiting() {

View File

@ -13,15 +13,14 @@
<p id="display"></p>
<div id="content"></div>
<pre id="test"></pre>
<script src="utils.js"></script>
<script class="testbody" type="text/javascript">
var registration;
var promise;
async function start() {
registration = await navigator.serviceWorker.register("worker_updatefoundevent.js",
{ scope: "./updatefoundevent.html" })
await waitForState(registration.installing, 'activated');
function start() {
return navigator.serviceWorker.register("worker_updatefoundevent.js",
{ scope: "./updatefoundevent.html" })
.then((swr) => registration = swr);
}
function startWaitForUpdateFound() {

View File

@ -3,16 +3,21 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
registration.onupdatefound = function(e) {
clients.matchAll().then(function(clients) {
if (!clients.length) {
reject("No clients found");
}
onactivate = function(e) {
e.waitUntil(new Promise(function(resolve, reject) {
registration.onupdatefound = function(e) {
clients.matchAll().then(function(clients) {
if (!clients.length) {
reject("No clients found");
}
if (registration.scope.match(/updatefoundevent\.html$/)) {
clients[0].postMessage("finish");
} else {
dump("Scope did not match");
if (registration.scope.match(/updatefoundevent\.html$/)) {
clients[0].postMessage("finish");
resolve();
} else {
dump("Scope did not match");
}
}, reject);
}
});
}));
}

View File

@ -90,12 +90,9 @@ async_test(function(t) {
return registration.unregister();
})
.then(function() {
// Step 5.1 of Register clears the uninstall flag before fetching
// the script:
//
// https://w3c.github.io/ServiceWorker/#register-algorithm
var promise = navigator.serviceWorker.register('this-will-404',
{ scope: scope });
iframe.remove();
return promise;
})
.then(
@ -103,28 +100,17 @@ async_test(function(t) {
assert_unreached('register should reject the promise');
},
function() {
assert_equals(registration.installing, null,
'registration.installing');
assert_equals(registration.waiting, null,
'registration.waiting');
assert_equals(registration.active.scriptURL, normalizeURL(worker_url),
'registration.active');
iframe.remove();
return with_iframe(scope);
})
.then(function(frame) {
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(worker_url),
'the original worker should control a new document');
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
null,
'document should not load with a controller');
frame.remove();
return registration.unregister();
})
.then(function() {
t.done();
})
.catch(unreached_rejection(t));
}, 'Registering a new script URL that 404s does resurrect an ' +
}, 'Registering a new script URL that 404s does not resurrect an ' +
'unregistered registration');
async_test(function(t) {
@ -145,12 +131,9 @@ async_test(function(t) {
return registration.unregister();
})
.then(function() {
// Step 5.1 of Register clears the uninstall flag before firing
// the install event:
//
// https://w3c.github.io/ServiceWorker/#register-algorithm
var promise = navigator.serviceWorker.register(
'resources/reject-install-worker.js', { scope: scope });
iframe.remove();
return promise;
})
.then(function(r) {
@ -158,20 +141,12 @@ async_test(function(t) {
return wait_for_state(t, r.installing, 'redundant');
})
.then(function() {
assert_equals(registration.installing, null,
'registration.installing');
assert_equals(registration.waiting, null,
'registration.waiting');
assert_equals(registration.active.scriptURL, normalizeURL(worker_url),
'registration.active');
iframe.remove();
return with_iframe(scope);
})
.then(function(frame) {
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
normalizeURL(worker_url),
'the original worker should control a new document');
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
null,
'document should not load with a controller');
frame.remove();
return registration.unregister();
})
@ -179,6 +154,6 @@ async_test(function(t) {
t.done();
})
.catch(unreached_rejection(t));
}, 'Registering a new script URL that fails to install does resurrect ' +
}, 'Registering a new script URL that fails to install does not resurrect ' +
'an unregistered registration');
</script>