void
ServiceWorkerUpdateJob::Install(ServiceWorkerManager* aSWM)
{
  AssertIsOnMainThread();
  MOZ_DIAGNOSTIC_ASSERT(!Canceled());
  MOZ_DIAGNOSTIC_ASSERT(aSWM);

  MOZ_ASSERT(!mRegistration->GetInstalling());

  // Begin step 2 of the Install algorithm.
  //
  //  https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm

  mRegistration->TransitionEvaluatingToInstalling();

  // Step 6 of the Install algorithm resolving the job promise.
  InvokeResultCallbacks(NS_OK);

  // The job promise cannot be rejected after this point, but the job can
  // still fail; e.g. if the install event handler throws, etc.

  // fire the updatefound event
  nsCOMPtr<nsIRunnable> upr =
    NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
      "dom::workers::ServiceWorkerManager::"
      "FireUpdateFoundOnServiceWorkerRegistrations",
      aSWM,
      &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
      mRegistration);
  NS_DispatchToMainThread(upr);

  // Call ContinueAfterInstallEvent(false) on main thread if the SW
  // script fails to load.
  nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
    "dom::workers::ServiceWorkerUpdateJob::ContinueAfterInstallEvent",
    this,
    &ServiceWorkerUpdateJob::ContinueAfterInstallEvent,
    false);

  nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
    new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(
      "ServiceWorkerUpdateJob", this));
  RefPtr<LifeCycleEventCallback> callback = new ContinueInstallRunnable(handle);

  // Send the install event to the worker thread
  ServiceWorkerPrivate* workerPrivate =
    mRegistration->GetInstalling()->WorkerPrivate();
  nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
                                                  callback, failRunnable);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ContinueAfterInstallEvent(false /* aSuccess */);
  }
}
void
ServiceWorkerUnregisterJob::AsyncExecute()
{
  AssertIsOnMainThread();

  if (Canceled()) {
    Finish(NS_ERROR_DOM_ABORT_ERR);
    return;
  }

  // Step 1 of the Unregister algorithm requires checking that the
  // client origin matches the scope's origin.  We perform this in
  // registration->update() method directly since we don't have that
  // client information available here.

  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();

  // "Let registration be the result of running [[Get Registration]]
  // algorithm passing scope as the argument."
  RefPtr<ServiceWorkerRegistrationInfo> registration =
    swm->GetRegistration(mPrincipal, mScope);
  if (!registration) {
    // "If registration is null, then, resolve promise with false."
    Finish(NS_OK);
    return;
  }

  // Note, we send the message to remove the registration from disk now even
  // though we may only set the mPendingUninstall flag below.  This is
  // necessary to ensure the registration is removed if the controlled
  // clients are closed by shutting down the browser.  If the registration
  // is resurrected by clearing mPendingUninstall then it should be saved
  // to disk again.
  if (mSendToParent && !registration->mPendingUninstall) {
    swm->MaybeSendUnregister(mPrincipal, mScope);
  }

  // "Set registration's uninstalling flag."
  registration->mPendingUninstall = true;

  // "Resolve promise with true"
  mResult = true;
  InvokeResultCallbacks(NS_OK);

  // "If no service worker client is using registration..."
  if (!registration->IsControllingDocuments()) {
    // "Invoke [[Clear Registration]]..."
    swm->RemoveRegistration(registration);
  }

  Finish(NS_OK);
}
void
ServiceWorkerJob::Finish(ErrorResult& aRv)
{
  AssertIsOnMainThread();
  MOZ_ASSERT(mState == State::Started);

  // Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
  if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
                      !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
                      !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {

    // Remove the old error code so we can replace it with a TypeError.
    aRv.SuppressException();

    NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
    NS_ConvertUTF8toUTF16 scope(mScope);

    // Throw the type error with a generic error message.
    aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
  }

  // The final callback may drop the last ref to this object.
  RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;

  if (!mResultCallbacksInvoked) {
    InvokeResultCallbacks(aRv);
  }

  mState = State::Finished;

  mFinalCallback->JobFinished(this, aRv);
  mFinalCallback = nullptr;

  // The callback might not consume the error.
  aRv.SuppressException();

  // Async release this object to ensure that our caller methods complete
  // as well.
  NS_ReleaseOnMainThread(kungFuDeathGrip.forget(), true /* always proxy */);
}
void
ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
{
  ErrorResult converted(aRv);
  InvokeResultCallbacks(converted);
}