dom/workers/Worker.cpp
author Dave Townsend <dtownsend@oxymoronical.com>
Wed, 06 Feb 2019 11:09:06 -0800
changeset 520643 fbef638149b4426032f58903a9abf480341a3ff8
parent 519769 f41cee9bf14931b453838d1bbcbda528e3b064e8
permissions -rw-r--r--
Bug 1518639: Add boilerplate support for a windows remote client and server. r=jimm,froydnj Adds build config and stubs for a windows implementation of the remote client and server. Differential Revision: https://phabricator.services.mozilla.com/D19074

/* -*- 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 "Worker.h"

#include "MessageEventRunnable.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/TimelineConsumers.h"
#include "mozilla/WorkerTimelineMarker.h"
#include "nsContentUtils.h"
#include "WorkerPrivate.h"

#ifdef XP_WIN
#  undef PostMessage
#endif

namespace mozilla {
namespace dom {

/* static */
already_AddRefed<Worker> Worker::Constructor(const GlobalObject& aGlobal,
                                             const nsAString& aScriptURL,
                                             const WorkerOptions& aOptions,
                                             ErrorResult& aRv) {
  JSContext* cx = aGlobal.Context();

  RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
      cx, aScriptURL, false /* aIsChromeWorker */, WorkerTypeDedicated,
      aOptions.mName, VoidCString(), nullptr /*aLoadInfo */, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }

  nsCOMPtr<nsIGlobalObject> globalObject =
      do_QueryInterface(aGlobal.GetAsSupports());

  RefPtr<Worker> worker = new Worker(globalObject, workerPrivate.forget());
  return worker.forget();
}

Worker::Worker(nsIGlobalObject* aGlobalObject,
               already_AddRefed<WorkerPrivate> aWorkerPrivate)
    : DOMEventTargetHelper(aGlobalObject),
      mWorkerPrivate(std::move(aWorkerPrivate)) {
  MOZ_ASSERT(mWorkerPrivate);
  mWorkerPrivate->SetParentEventTargetRef(this);
}

Worker::~Worker() { Terminate(); }

JSObject* Worker::WrapObject(JSContext* aCx,
                             JS::Handle<JSObject*> aGivenProto) {
  JS::Rooted<JSObject*> wrapper(aCx,
                                Worker_Binding::Wrap(aCx, this, aGivenProto));
  if (wrapper) {
    // Most DOM objects don't assume they have a reflector. If they don't have
    // one and need one, they create it. But in workers code, we assume that the
    // reflector is always present.  In order to guarantee that it's always
    // present, we have to preserve it. Otherwise the GC will happily collect it
    // as needed.
    MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
  }

  return wrapper;
}

void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                         const Sequence<JSObject*>& aTransferable,
                         ErrorResult& aRv) {
  NS_ASSERT_OWNINGTHREAD(Worker);

  if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
    return;
  }

  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());

  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
                                                          &transferable);
  if (NS_WARN_IF(aRv.Failed())) {
    return;
  }

  RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
      mWorkerPrivate, WorkerRunnable::WorkerThreadModifyBusyCount);

  UniquePtr<AbstractTimelineMarker> start;
  UniquePtr<AbstractTimelineMarker> end;
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
  bool isTimelineRecording = timelines && !timelines->IsEmpty();

  if (isTimelineRecording) {
    start = MakeUnique<WorkerTimelineMarker>(
        NS_IsMainThread()
            ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
            : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
        MarkerTracingType::START);
  }

  runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);

  if (isTimelineRecording) {
    end = MakeUnique<WorkerTimelineMarker>(
        NS_IsMainThread()
            ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
            : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
        MarkerTracingType::END);
    timelines->AddMarkerForAllObservedDocShells(start);
    timelines->AddMarkerForAllObservedDocShells(end);
  }

  if (NS_WARN_IF(aRv.Failed())) {
    return;
  }

  if (!runnable->Dispatch()) {
    aRv.Throw(NS_ERROR_FAILURE);
  }
}

void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                         const PostMessageOptions& aOptions, ErrorResult& aRv) {
  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
}

void Worker::Terminate() {
  NS_ASSERT_OWNINGTHREAD(Worker);

  if (mWorkerPrivate) {
    mWorkerPrivate->Cancel();
    mWorkerPrivate = nullptr;
  }
}

NS_IMPL_CYCLE_COLLECTION_CLASS(Worker)

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
  if (tmp->mWorkerPrivate) {
    tmp->mWorkerPrivate->Traverse(cb);
  }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
  tmp->Terminate();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)

NS_IMPL_ADDREF_INHERITED(Worker, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Worker, DOMEventTargetHelper)

}  // namespace dom
}  // namespace mozilla