tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Worker.cpp (9016B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "Worker.h"
      8 
      9 #include "EventWithOptionsRunnable.h"
     10 #include "MessageEventRunnable.h"
     11 #include "WorkerPrivate.h"
     12 #include "js/RootingAPI.h"
     13 #include "mozilla/ProfilerLabels.h"
     14 #include "mozilla/ProfilerMarkers.h"
     15 #include "mozilla/RefPtr.h"
     16 #include "mozilla/dom/BindingDeclarations.h"
     17 #include "mozilla/dom/TrustedScriptURL.h"
     18 #include "mozilla/dom/TrustedTypeUtils.h"
     19 #include "mozilla/dom/TrustedTypesConstants.h"
     20 #include "mozilla/dom/WorkerBinding.h"
     21 #include "mozilla/dom/WorkerStatus.h"
     22 #include "nsContentUtils.h"
     23 #include "nsDebug.h"
     24 #include "nsGlobalWindowInner.h"
     25 #include "nsISupports.h"
     26 
     27 #ifdef XP_WIN
     28 #  undef PostMessage
     29 #endif
     30 
     31 namespace mozilla::dom {
     32 
     33 /* static */
     34 already_AddRefed<Worker> Worker::Constructor(
     35    const GlobalObject& aGlobal, const TrustedScriptURLOrUSVString& aScriptURL,
     36    const WorkerOptions& aOptions, ErrorResult& aRv) {
     37  JSContext* cx = aGlobal.Context();
     38 
     39  nsCOMPtr<nsIGlobalObject> globalObject =
     40      do_QueryInterface(aGlobal.GetAsSupports());
     41 
     42  nsPIDOMWindowInner* innerWindow = globalObject->GetAsInnerWindow();
     43  if (innerWindow && !innerWindow->IsCurrentInnerWindow()) {
     44    aRv.ThrowInvalidStateError(
     45        "Cannot create worker for a going to be discarded document");
     46    return nullptr;
     47  }
     48 
     49  // TODO(Bug 1963277) This doen't work for content scripts.
     50  nsCOMPtr<nsIPrincipal> principal = aGlobal.GetSubjectPrincipal();
     51 
     52  // The spec only mentions Window and WorkerGlobalScope global objects, but
     53  // Gecko can actually call the constructor with other ones, so we just skip
     54  // trusted types handling in that case.
     55  // https://html.spec.whatwg.org/multipage/workers.html#dedicated-workers-and-the-worker-interface
     56  const nsAString* compliantString = nullptr;
     57  bool performTrustedTypeConversion = innerWindow;
     58  if (!performTrustedTypeConversion) {
     59    if (JSObject* globalJSObject = globalObject->GetGlobalJSObject()) {
     60      performTrustedTypeConversion = IsWorkerGlobal(globalJSObject);
     61    }
     62  }
     63  Maybe<nsAutoString> compliantStringHolder;
     64  if (performTrustedTypeConversion) {
     65    constexpr nsLiteralString sink = u"Worker constructor"_ns;
     66    compliantString = TrustedTypeUtils::GetTrustedTypesCompliantString(
     67        aScriptURL, sink, kTrustedTypesOnlySinkGroup, *globalObject, principal,
     68        compliantStringHolder, aRv);
     69    if (aRv.Failed()) {
     70      return nullptr;
     71    }
     72  } else {
     73    compliantString = aScriptURL.IsUSVString()
     74                          ? &aScriptURL.GetAsUSVString()
     75                          : &aScriptURL.GetAsTrustedScriptURL().mData;
     76  }
     77  MOZ_ASSERT(compliantString);
     78 
     79  RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
     80      cx, *compliantString, false /* aIsChromeWorker */, WorkerKindDedicated,
     81      aOptions.mCredentials, aOptions.mType, aOptions.mName, VoidCString(),
     82      nullptr /*aLoadInfo */, aRv);
     83  if (NS_WARN_IF(aRv.Failed())) {
     84    return nullptr;
     85  }
     86 
     87  RefPtr<Worker> worker = new Worker(globalObject, workerPrivate.forget());
     88  return worker.forget();
     89 }
     90 
     91 Worker::Worker(nsIGlobalObject* aGlobalObject,
     92               already_AddRefed<WorkerPrivate> aWorkerPrivate)
     93    : DOMEventTargetHelper(aGlobalObject),
     94      mWorkerPrivate(std::move(aWorkerPrivate)) {
     95  MOZ_ASSERT(mWorkerPrivate);
     96  mWorkerPrivate->SetParentEventTargetRef(this);
     97 }
     98 
     99 Worker::~Worker() { Terminate(); }
    100 
    101 JSObject* Worker::WrapObject(JSContext* aCx,
    102                             JS::Handle<JSObject*> aGivenProto) {
    103  JS::Rooted<JSObject*> wrapper(aCx,
    104                                Worker_Binding::Wrap(aCx, this, aGivenProto));
    105  if (wrapper) {
    106    // Most DOM objects don't assume they have a reflector. If they don't have
    107    // one and need one, they create it. But in workers code, we assume that the
    108    // reflector is always present.  In order to guarantee that it's always
    109    // present, we have to preserve it. Otherwise the GC will happily collect it
    110    // as needed.
    111    MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
    112  }
    113 
    114  return wrapper;
    115 }
    116 
    117 bool Worker::IsEligibleForMessaging() {
    118  NS_ASSERT_OWNINGTHREAD(Worker);
    119 
    120  return mWorkerPrivate && mWorkerPrivate->ParentStatusProtected() <= Running;
    121 }
    122 
    123 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
    124                         const Sequence<JSObject*>& aTransferable,
    125                         ErrorResult& aRv) {
    126  NS_ASSERT_OWNINGTHREAD(Worker);
    127 
    128  if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
    129    return;
    130  }
    131  RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
    132  (void)workerPrivate;
    133 
    134  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
    135 
    136  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
    137                                                          &transferable);
    138  if (NS_WARN_IF(aRv.Failed())) {
    139    return;
    140  }
    141 
    142  NS_ConvertUTF16toUTF8 nameOrScriptURL(
    143      mWorkerPrivate->WorkerName().IsEmpty()
    144          ? Substring(
    145                mWorkerPrivate->ScriptURL(), 0,
    146                std::min(size_t(1024), mWorkerPrivate->ScriptURL().Length()))
    147          : Substring(
    148                mWorkerPrivate->WorkerName(), 0,
    149                std::min(size_t(1024), mWorkerPrivate->WorkerName().Length())));
    150  AUTO_PROFILER_MARKER_TEXT("Worker.postMessage", DOM, {}, nameOrScriptURL);
    151  uint32_t flags = uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS);
    152  if (mWorkerPrivate->IsChromeWorker()) {
    153    flags |= uint32_t(js::ProfilingStackFrame::Flags::NONSENSITIVE);
    154  }
    155  mozilla::AutoProfilerLabel PROFILER_RAII(
    156      "Worker.postMessage", nameOrScriptURL.get(),
    157      JS::ProfilingCategoryPair::DOM, flags);
    158 
    159  RefPtr<MessageEventRunnable> runnable =
    160      new MessageEventRunnable(mWorkerPrivate);
    161 
    162  JS::CloneDataPolicy clonePolicy;
    163  // DedicatedWorkers are always part of the same agent cluster.
    164  clonePolicy.allowIntraClusterClonableSharedObjects();
    165 
    166  if (NS_IsMainThread()) {
    167    nsGlobalWindowInner* win = nsContentUtils::IncumbentInnerWindow();
    168    if (win && win->IsSharedMemoryAllowed()) {
    169      clonePolicy.allowSharedMemoryObjects();
    170    }
    171  } else {
    172    WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
    173    if (worker && worker->IsSharedMemoryAllowed()) {
    174      clonePolicy.allowSharedMemoryObjects();
    175    }
    176  }
    177 
    178  runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
    179 
    180  if (!mWorkerPrivate || mWorkerPrivate->ParentStatusProtected() > Running) {
    181    return;
    182  }
    183 
    184  if (NS_WARN_IF(aRv.Failed())) {
    185    return;
    186  }
    187 
    188  // The worker could have closed between the time we entered this function and
    189  // checked ParentStatusProtected and now, which could cause the dispatch to
    190  // fail.
    191  (void)NS_WARN_IF(!runnable->Dispatch(mWorkerPrivate));
    192 }
    193 
    194 void Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
    195                         const StructuredSerializeOptions& aOptions,
    196                         ErrorResult& aRv) {
    197  PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
    198 }
    199 
    200 void Worker::PostEventWithOptions(JSContext* aCx,
    201                                  JS::Handle<JS::Value> aOptions,
    202                                  const Sequence<JSObject*>& aTransferable,
    203                                  EventWithOptionsRunnable* aRunnable,
    204                                  ErrorResult& aRv) {
    205  NS_ASSERT_OWNINGTHREAD(Worker);
    206 
    207  if (NS_WARN_IF(!mWorkerPrivate ||
    208                 mWorkerPrivate->ParentStatusProtected() > Running)) {
    209    return;
    210  }
    211  RefPtr<WorkerPrivate> workerPrivate = mWorkerPrivate;
    212  (void)workerPrivate;
    213 
    214  aRunnable->InitOptions(aCx, aOptions, aTransferable, aRv);
    215 
    216  if (NS_WARN_IF(!mWorkerPrivate ||
    217                 mWorkerPrivate->ParentStatusProtected() > Running)) {
    218    return;
    219  }
    220 
    221  if (NS_WARN_IF(aRv.Failed())) {
    222    return;
    223  }
    224 
    225  (void)NS_WARN_IF(!aRunnable->Dispatch(mWorkerPrivate));
    226 }
    227 
    228 void Worker::Terminate() {
    229  NS_ASSERT_OWNINGTHREAD(Worker);
    230 
    231  if (mWorkerPrivate) {
    232    mWorkerPrivate->Cancel();
    233    mWorkerPrivate = nullptr;
    234  }
    235 }
    236 
    237 NS_IMPL_CYCLE_COLLECTION_CLASS(Worker)
    238 
    239 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
    240  if (tmp->mWorkerPrivate) {
    241    tmp->mWorkerPrivate->Traverse(cb);
    242  }
    243 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    244 
    245 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
    246  tmp->Terminate();
    247  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    248 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    249 
    250 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
    251 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    252 
    253 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker)
    254 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    255 
    256 NS_IMPL_ADDREF_INHERITED(Worker, DOMEventTargetHelper)
    257 NS_IMPL_RELEASE_INHERITED(Worker, DOMEventTargetHelper)
    258 
    259 }  // namespace mozilla::dom