dom/workers/remoteworkers/RemoteWorkerController.cpp
author Andrea Marchesini <amarchesini@mozilla.com>
Mon, 19 Nov 2018 15:18:33 -0800
changeset 503571 b3d9a379c852669a1fa005148bee537b99c91c6f
parent 503570 6059af9c85adc62a51d9b38b9cdcba3904d4d824
child 503572 9482aa753a31fd3adc22704e4c3ad1466f110150
permissions -rw-r--r--
Bug 1438945 - Part 9: RemoteWorker in SharedWorkerManager. r=asuth

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "RemoteWorkerController.h"
#include "RemoteWorkerManager.h"
#include "RemoteWorkerParent.h"

namespace mozilla {

using namespace ipc;

namespace dom {

/* static */ already_AddRefed<RemoteWorkerController>
RemoteWorkerController::Create(const RemoteWorkerData& aData)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  RefPtr<RemoteWorkerController> controller = new RemoteWorkerController();

  RefPtr<RemoteWorkerManager> manager = RemoteWorkerManager::GetOrCreate();
  MOZ_ASSERT(manager);

  manager->Launch(controller, aData);

  return controller.forget();
}

RemoteWorkerController::RemoteWorkerController()
  : mState(ePending)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
}

RemoteWorkerController::~RemoteWorkerController()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
}

void
RemoteWorkerController::SetWorkerActor(RemoteWorkerParent* aActor)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(!mActor);
  MOZ_ASSERT(aActor);

  mActor = aActor;
}

void
RemoteWorkerController::CreationFailed()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(mState == ePending || mState == eTerminated);

  // TODO: maybe notification?

  Shutdown();
}

void
RemoteWorkerController::CreationSucceeded()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(mState == ePending || mState == eTerminated);

  if (mState == eTerminated) {
    MOZ_ASSERT(!mActor);
    MOZ_ASSERT(mPendingOps.IsEmpty());
    // Nothing to do.
    return;
  }

  MOZ_ASSERT(mActor);
  mState = eReady;

  // TODO: maybe notification?

  for (UniquePtr<Op>& op : mPendingOps) {
    switch (op->mType) {
      case Op::eTerminate:
        Terminate();
        break;

      case Op::eSuspend:
        Suspend();
        break;

      case Op::eResume:
        Resume();
        break;

      case Op::eFreeze:
        Freeze();
        break;

      case Op::eThaw:
        Thaw();
        break;

      case Op::ePortIdentifier:
        AddPortIdentifier(op->mPortIdentifier);
        break;

      case Op::eAddWindowID:
        AddWindowID(op->mWindowID);
        break;

      case Op::eRemoveWindowID:
        RemoveWindowID(op->mWindowID);
        break;

      default:
        MOZ_CRASH("Unknown op.");
    }

    op->Completed();
  }

  mPendingOps.Clear();
}

void
RemoteWorkerController::ErrorPropagation(const ErrorValue& aValue)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  // TODO: error propagation
}

void
RemoteWorkerController::WorkerTerminated()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(mState == eReady);

  // TODO: worker terminated
  Shutdown();
}

void
RemoteWorkerController::Shutdown()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(mState == ePending || mState == eReady);

  mState = eTerminated;

  mPendingOps.Clear();

  if (mActor) {
    mActor->SetController(nullptr);
    Unused << mActor->SendExecOp(RemoteWorkerTerminateOp());
    mActor = nullptr;
  }
}

void
RemoteWorkerController::AddWindowID(uint64_t aWindowID)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(aWindowID);

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eAddWindowID, aWindowID));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerAddWindowIDOp(aWindowID));
}

void
RemoteWorkerController::RemoveWindowID(uint64_t aWindowID)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(aWindowID);

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eRemoveWindowID, aWindowID));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerRemoveWindowIDOp(aWindowID));
}

void
RemoteWorkerController::AddPortIdentifier(const MessagePortIdentifier& aPortIdentifier)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(aPortIdentifier));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerPortIdentifierOp(aPortIdentifier));
}

void
RemoteWorkerController::Terminate()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == eTerminated) {
    return;
  }

  Shutdown();
}

void
RemoteWorkerController::Suspend()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eSuspend));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerSuspendOp());
}

void
RemoteWorkerController::Resume()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eResume));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerResumeOp());
}

void
RemoteWorkerController::Freeze()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eFreeze));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerFreezeOp());
}

void
RemoteWorkerController::Thaw()
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(XRE_IsParentProcess());

  if (mState == ePending) {
    mPendingOps.AppendElement(new Op(Op::eThaw));
    return;
  }

  if (mState == eTerminated) {
    return;
  }

  MOZ_ASSERT(mState == eReady);
  Unused << mActor->SendExecOp(RemoteWorkerThawOp());
}

RemoteWorkerController::Op::~Op()
{
  MOZ_COUNT_DTOR(Op);

  // We don't want to leak the port if the operation has not been processed.
  if (!mCompleted && mType == ePortIdentifier) {
    MessagePortParent::ForceClose(mPortIdentifier.uuid(),
                                  mPortIdentifier.destinationUuid(),
                                  mPortIdentifier.sequenceId());
  }
}

} // dom namespace
} // mozilla namespace