Commit 63c97930 authored by Ben Kelly's avatar Ben Kelly
Browse files

Bug 1293277 P7 Make ServiceWorkerManager use ClientHandle::Control() for...

Bug 1293277 P7 Make ServiceWorkerManager use ClientHandle::Control() for claiming and ClientSource::SetController() fire the controller change event. r=baku
parent 890e126a
Loading
Loading
Loading
Loading
+48 −3
Original line number Diff line number Diff line
@@ -361,6 +361,23 @@ ClientSource::SetController(const ServiceWorkerDescriptor& aServiceWorker)

  mController.reset();
  mController.emplace(aServiceWorker);

  RefPtr<ServiceWorkerContainer> swc;
  nsPIDOMWindowInner* window = GetInnerWindow();
  if (window) {
    RefPtr<Navigator> navigator =
      static_cast<Navigator*>(window->GetNavigator());
    if (navigator) {
      swc = navigator->ServiceWorker();
    }
  }

  // TODO: Also self.navigator.serviceWorker on workers when its exposed there

  if (swc && nsContentUtils::IsSafeToRunScript()) {
    IgnoredErrorResult ignored;
    swc->ControllerChanged(ignored);
  }
}

RefPtr<ClientOpPromise>
@@ -555,11 +572,39 @@ ClientSource::PostMessage(const ClientPostMessageArgs& aArgs)
RefPtr<ClientOpPromise>
ClientSource::Claim(const ClientClaimArgs& aArgs)
{
  SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
  RefPtr<ClientOpPromise> ref;

  RefPtr<ClientOpPromise> ref =
    ClientOpPromise::CreateAndResolve(NS_OK, __func__);
  ServiceWorkerDescriptor swd(aArgs.serviceWorker());

  // Today the ServiceWorkerManager maintains its own list of
  // nsIDocument objects controlled by each service worker.  We
  // need to try to update that data structure for now.  If we
  // can't, however, then simply mark the Client as controlled.
  // In the future this will be enough for the SWM as well since
  // it will eventually hold ClientHandle objects instead of
  // nsIDocuments.
  nsPIDOMWindowInner* innerWindow = GetInnerWindow();
  nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
  RefPtr<ServiceWorkerManager> swm = doc ? ServiceWorkerManager::GetInstance()
                                         : nullptr;
  if (!swm || !doc) {
    SetController(swd);
    ref = ClientOpPromise::CreateAndResolve(NS_OK, __func__);
    return ref.forget();
  }

  RefPtr<ClientOpPromise::Private> outerPromise =
    new ClientOpPromise::Private(__func__);

  RefPtr<GenericPromise> p = swm->MaybeClaimClient(doc, swd);
  p->Then(mEventTarget, __func__,
    [outerPromise] (bool aResult) {
      outerPromise->Resolve(NS_OK, __func__);
    }, [outerPromise] (nsresult aResult) {
      outerPromise->Reject(aResult, __func__);
    });

  ref = outerPromise;
  return ref.forget();
}

+79 −47
Original line number Diff line number Diff line
@@ -2838,22 +2838,42 @@ ServiceWorkerManager::GetDocumentController(nsPIDOMWindowInner* aWindow,
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  Maybe<ServiceWorkerDescriptor> controller = aWindow->GetController();
  if (controller.isNothing()) {
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
  if (!doc) {
  if (NS_WARN_IF(!doc)) {
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  RefPtr<ServiceWorkerRegistrationInfo> registration;
  nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
  if (NS_WARN_IF(!principal)) {
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  nsAutoCString scopeKey;
  nsresult rv = PrincipalToScopeKey(principal, scopeKey);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  MOZ_ASSERT(registration->GetActive());
  RefPtr<ServiceWorker> serviceWorker =
    registration->GetActive()->GetOrCreateInstance(aWindow);
  RefPtr<ServiceWorkerRegistrationInfo> registration =
    GetRegistration(scopeKey, controller.ref().Scope());
  if (NS_WARN_IF(!registration)) {
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  RefPtr<ServiceWorkerInfo> active = registration->GetActive();
  if (NS_WARN_IF(!active) ||
      NS_WARN_IF(active->Descriptor().Id() != controller.ref().Id())) {
    return NS_ERROR_DOM_INVALID_STATE_ERR;
  }

  RefPtr<ServiceWorker> serviceWorker = active->GetOrCreateInstance(aWindow);
  serviceWorker.forget(aServiceWorker);

  return NS_OK;
}

@@ -3158,46 +3178,19 @@ ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
  queue->ScheduleJob(job);
}

namespace {

static void
FireControllerChangeOnDocument(nsIDocument* aDocument)
{
  AssertIsOnMainThread();
  MOZ_ASSERT(aDocument);

  nsCOMPtr<nsPIDOMWindowInner> w = aDocument->GetInnerWindow();
  if (!w) {
    NS_WARNING("Failed to dispatch controllerchange event");
    return;
  }

  auto* window = nsGlobalWindowInner::Cast(w.get());
  dom::Navigator* navigator = window->Navigator();
  if (!navigator) {
    return;
  }

  RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
  ErrorResult result;
  container->ControllerChanged(result);
  if (result.Failed()) {
    NS_WARNING("Failed to dispatch controllerchange event");
  }
}

} // anonymous namespace

void
already_AddRefed<GenericPromise>
ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
                                       ServiceWorkerRegistrationInfo* aWorkerRegistration)
{
  MOZ_ASSERT(aWorkerRegistration);
  MOZ_ASSERT(aWorkerRegistration->GetActive());

  RefPtr<GenericPromise> ref;

  // Same origin check
  if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
    return;
    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
    return ref.forget();
  }

  // The registration that should be controlling the client
@@ -3210,15 +3203,40 @@ ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,

  if (aWorkerRegistration != matchingRegistration ||
      aWorkerRegistration == controllingRegistration) {
    return;
    ref = GenericPromise::CreateAndResolve(true, __func__);
    return ref.forget();
  }

  if (controllingRegistration) {
    StopControllingADocument(controllingRegistration);
  }

  StartControllingADocument(aWorkerRegistration, aDocument);
  FireControllerChangeOnDocument(aDocument);
  ref = StartControllingADocument(aWorkerRegistration, aDocument);
  return ref.forget();
}

already_AddRefed<GenericPromise>
ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
                                       const ServiceWorkerDescriptor& aServiceWorker)
{
  RefPtr<GenericPromise> ref;

  nsCOMPtr<nsIPrincipal> principal =
    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
  if (!principal) {
    ref = GenericPromise::CreateAndResolve(false, __func__);
    return ref.forget();
  }

  RefPtr<ServiceWorkerRegistrationInfo> registration =
    GetRegistration(principal, aServiceWorker.Scope());
  if (!registration) {
    ref = GenericPromise::CreateAndResolve(false, __func__);
    return ref.forget();
  }

  ref = MaybeClaimClient(aDoc, registration);
  return ref.forget();
}

void
@@ -3247,11 +3265,14 @@ ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
}

void
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
{
  AssertIsOnMainThread();

  AutoTArray<nsCOMPtr<nsIDocument>, 16> documents;
  RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
  MOZ_DIAGNOSTIC_ASSERT(activeWorker);

  AutoTArray<nsCOMPtr<nsPIDOMWindowInner>, 16> innerWindows;
  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    if (iter.UserData() != aRegistration) {
      continue;
@@ -3262,13 +3283,24 @@ ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegis
      continue;
    }

    documents.AppendElement(doc);
    nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow();
    if (NS_WARN_IF(!innerWindow)) {
      continue;
    }

    innerWindows.AppendElement(innerWindow);
  }

  // Fire event after iterating mControlledDocuments is done to prevent
  // modification by reentering from the event handlers during iteration.
  for (auto& doc : documents) {
    FireControllerChangeOnDocument(doc);
  for (auto& innerWindow : innerWindows) {
    Maybe<ClientInfo> clientInfo = innerWindow->GetClientInfo();
    if (clientInfo.isSome()) {
      RefPtr<ClientHandle> clientHandle =
        ClientManager::CreateHandle(clientInfo.ref(),
                                    innerWindow->EventTargetFor(TaskCategory::Other));
      clientHandle->Control(activeWorker->Descriptor());
    }
  }
}

+6 −2
Original line number Diff line number Diff line
@@ -271,10 +271,14 @@ public:
              uint32_t aFlags,
              JSExnType aExnType);

  void
  already_AddRefed<GenericPromise>
  MaybeClaimClient(nsIDocument* aDocument,
                   ServiceWorkerRegistrationInfo* aWorkerRegistration);

  already_AddRefed<GenericPromise>
  MaybeClaimClient(nsIDocument* aDoc,
                   const ServiceWorkerDescriptor& aServiceWorker);

  void
  SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
                     uint64_t aServiceWorkerID);
@@ -423,7 +427,7 @@ private:
  FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration);

  void
  FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
  UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration);

  void
  StorePendingReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
+2 −7
Original line number Diff line number Diff line
@@ -274,13 +274,8 @@ ServiceWorkerRegistrationInfo::Activate()
  // FIXME(nsm): Unlink appcache if there is one.

  // "Queue a task to fire a simple event named controllerchange..."
  nsCOMPtr<nsIRunnable> controllerChangeRunnable =
    NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
      "dom::workers::ServiceWorkerManager::FireControllerChange",
      swm,
      &ServiceWorkerManager::FireControllerChange,
      this);
  NS_DispatchToMainThread(controllerChangeRunnable);
  MOZ_DIAGNOSTIC_ASSERT(mActiveWorker);
  swm->UpdateClientControllers(this);

  nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
    "dom::workers::ServiceWorkerRegistrationInfo::FinishActivate",