tor-browser

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

WorkerDebugger.cpp (13931B)


      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 "WorkerDebugger.h"
      8 
      9 #include "ScriptLoader.h"
     10 #include "WorkerCommon.h"
     11 #include "WorkerError.h"
     12 #include "WorkerRunnable.h"
     13 #include "mozilla/AbstractThread.h"
     14 #include "mozilla/Encoding.h"
     15 #include "mozilla/dom/BrowsingContext.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/dom/MessageEvent.h"
     18 #include "mozilla/dom/MessageEventBinding.h"
     19 #include "mozilla/dom/RemoteWorkerChild.h"
     20 #include "mozilla/dom/WindowContext.h"
     21 #include "nsProxyRelease.h"
     22 #include "nsQueryObject.h"
     23 #include "nsThreadUtils.h"
     24 
     25 #if defined(XP_WIN)
     26 #  include <processthreadsapi.h>  // for GetCurrentProcessId()
     27 #else
     28 #  include <unistd.h>  // for getpid()
     29 #endif                 // defined(XP_WIN)
     30 
     31 namespace mozilla::dom {
     32 
     33 namespace {
     34 
     35 class DebuggerMessageEventRunnable final : public WorkerDebuggerRunnable {
     36  nsString mMessage;
     37 
     38 public:
     39  DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
     40                               const nsAString& aMessage)
     41      : WorkerDebuggerRunnable("DebuggerMessageEventRunnable"),
     42        mMessage(aMessage) {}
     43 
     44 private:
     45  virtual bool WorkerRun(JSContext* aCx,
     46                         WorkerPrivate* aWorkerPrivate) override {
     47    WorkerDebuggerGlobalScope* globalScope =
     48        aWorkerPrivate->DebuggerGlobalScope();
     49    MOZ_ASSERT(globalScope);
     50 
     51    JS::Rooted<JSString*> message(
     52        aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), mMessage.Length()));
     53    if (!message) {
     54      return false;
     55    }
     56    JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
     57 
     58    RefPtr<MessageEvent> event =
     59        new MessageEvent(globalScope, nullptr, nullptr);
     60    event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
     61                            Cancelable::eYes, data, u""_ns, u""_ns, nullptr,
     62                            Sequence<OwningNonNull<MessagePort>>());
     63    event->SetTrusted(true);
     64 
     65    globalScope->DispatchEvent(*event);
     66    return true;
     67  }
     68 };
     69 
     70 class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable {
     71  nsString mScriptURL;
     72  const mozilla::Encoding* mDocumentEncoding;
     73 
     74 public:
     75  CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate,
     76                                const nsAString& aScriptURL,
     77                                const mozilla::Encoding* aDocumentEncoding)
     78      : WorkerDebuggerRunnable("CompileDebuggerScriptRunnable"),
     79        mScriptURL(aScriptURL),
     80        mDocumentEncoding(aDocumentEncoding) {}
     81 
     82 private:
     83  virtual bool WorkerRun(JSContext* aCx,
     84                         WorkerPrivate* aWorkerPrivate) override {
     85    aWorkerPrivate->AssertIsOnWorkerThread();
     86 
     87    WorkerDebuggerGlobalScope* globalScope =
     88        aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
     89    if (!globalScope) {
     90      NS_WARNING("Failed to make global!");
     91      return false;
     92    }
     93 
     94    if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
     95      return false;
     96    }
     97 
     98    JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
     99 
    100    ErrorResult rv;
    101    JSAutoRealm ar(aCx, global);
    102    workerinternals::LoadMainScript(aWorkerPrivate, nullptr, mScriptURL,
    103                                    DebuggerScript, rv, mDocumentEncoding);
    104    rv.WouldReportJSException();
    105    // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
    106    // return false and don't SetWorkerScriptExecutedSuccessfully() in that
    107    // case, but don't throw anything on aCx.  The idea is to not dispatch error
    108    // events if our load is canceled with that error code.
    109    if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
    110      rv.SuppressException();
    111      return false;
    112    }
    113    // Make sure to propagate exceptions from rv onto aCx, so that they will get
    114    // reported after we return.  We do this for all failures on rv, because now
    115    // we're using rv to track all the state we care about.
    116    if (rv.MaybeSetPendingException(aCx)) {
    117      return false;
    118    }
    119 
    120    return true;
    121  }
    122 };
    123 
    124 }  // namespace
    125 
    126 class WorkerDebugger::PostDebuggerMessageRunnable final : public Runnable {
    127  WorkerDebugger* mDebugger;
    128  nsString mMessage;
    129 
    130 public:
    131  PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
    132                              const nsAString& aMessage)
    133      : mozilla::Runnable("PostDebuggerMessageRunnable"),
    134        mDebugger(aDebugger),
    135        mMessage(aMessage) {}
    136 
    137 private:
    138  ~PostDebuggerMessageRunnable() = default;
    139 
    140  NS_IMETHOD
    141  Run() override {
    142    mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
    143 
    144    return NS_OK;
    145  }
    146 };
    147 
    148 class WorkerDebugger::ReportDebuggerErrorRunnable final : public Runnable {
    149  WorkerDebugger* mDebugger;
    150  nsCString mFilename;
    151  uint32_t mLineno;
    152  nsString mMessage;
    153 
    154 public:
    155  ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
    156                              const nsACString& aFilename, uint32_t aLineno,
    157                              const nsAString& aMessage)
    158      : Runnable("ReportDebuggerErrorRunnable"),
    159        mDebugger(aDebugger),
    160        mFilename(aFilename),
    161        mLineno(aLineno),
    162        mMessage(aMessage) {}
    163 
    164 private:
    165  ~ReportDebuggerErrorRunnable() = default;
    166 
    167  NS_IMETHOD
    168  Run() override {
    169    mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
    170 
    171    return NS_OK;
    172  }
    173 };
    174 
    175 WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
    176    : mWorkerPrivate(aWorkerPrivate), mIsInitialized(false) {
    177  AssertIsOnMainThread();
    178 }
    179 
    180 WorkerDebugger::~WorkerDebugger() {
    181  MOZ_ASSERT(!mWorkerPrivate);
    182 
    183  if (!NS_IsMainThread()) {
    184    for (auto& listener : mListeners) {
    185      NS_ReleaseOnMainThread("WorkerDebugger::mListeners", listener.forget());
    186    }
    187  }
    188 }
    189 
    190 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
    191 
    192 NS_IMETHODIMP
    193 WorkerDebugger::GetIsClosed(bool* aResult) {
    194  AssertIsOnMainThread();
    195 
    196  *aResult = !mWorkerPrivate || mWorkerPrivate->IsDead();
    197  return NS_OK;
    198 }
    199 
    200 NS_IMETHODIMP
    201 WorkerDebugger::GetIsChrome(bool* aResult) {
    202  AssertIsOnMainThread();
    203 
    204  if (!mWorkerPrivate) {
    205    return NS_ERROR_UNEXPECTED;
    206  }
    207 
    208  *aResult = mWorkerPrivate->IsChromeWorker();
    209  return NS_OK;
    210 }
    211 
    212 NS_IMETHODIMP
    213 WorkerDebugger::GetIsRemote(bool* aResult) {
    214  AssertIsOnMainThread();
    215 
    216  *aResult = false;
    217  return NS_OK;
    218 }
    219 
    220 NS_IMETHODIMP
    221 WorkerDebugger::GetIsInitialized(bool* aResult) {
    222  AssertIsOnMainThread();
    223 
    224  if (!mWorkerPrivate) {
    225    return NS_ERROR_UNEXPECTED;
    226  }
    227 
    228  *aResult = mIsInitialized;
    229  return NS_OK;
    230 }
    231 
    232 NS_IMETHODIMP
    233 WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) {
    234  AssertIsOnMainThread();
    235 
    236  if (!mWorkerPrivate) {
    237    return NS_ERROR_UNEXPECTED;
    238  }
    239 
    240  WorkerPrivate* parent = mWorkerPrivate->GetParent();
    241  if (!parent) {
    242    *aResult = nullptr;
    243    return NS_OK;
    244  }
    245 
    246  MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
    247 
    248  nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
    249  debugger.forget(aResult);
    250  return NS_OK;
    251 }
    252 
    253 NS_IMETHODIMP
    254 WorkerDebugger::GetType(uint32_t* aResult) {
    255  AssertIsOnMainThread();
    256 
    257  if (!mWorkerPrivate) {
    258    return NS_ERROR_UNEXPECTED;
    259  }
    260 
    261  *aResult = mWorkerPrivate->Kind();
    262  return NS_OK;
    263 }
    264 
    265 NS_IMETHODIMP
    266 WorkerDebugger::GetUrl(nsAString& aResult) {
    267  AssertIsOnMainThread();
    268 
    269  if (!mWorkerPrivate) {
    270    return NS_ERROR_UNEXPECTED;
    271  }
    272 
    273  aResult = mWorkerPrivate->ScriptURL();
    274  return NS_OK;
    275 }
    276 
    277 NS_IMETHODIMP
    278 WorkerDebugger::GetWindow(mozIDOMWindow** aResult) {
    279  AssertIsOnMainThread();
    280 
    281  if (!mWorkerPrivate) {
    282    return NS_ERROR_UNEXPECTED;
    283  }
    284 
    285  nsCOMPtr<nsPIDOMWindowInner> window = DedicatedWorkerWindow();
    286  window.forget(aResult);
    287  return NS_OK;
    288 }
    289 
    290 NS_IMETHODIMP
    291 WorkerDebugger::GetWindowIDs(nsTArray<uint64_t>& aResult) {
    292  AssertIsOnMainThread();
    293 
    294  if (!mWorkerPrivate) {
    295    return NS_ERROR_UNEXPECTED;
    296  }
    297 
    298  if (mWorkerPrivate->IsDedicatedWorker()) {
    299    if (const auto window = DedicatedWorkerWindow()) {
    300      aResult.AppendElement(window->WindowID());
    301    }
    302  } else if (mWorkerPrivate->IsSharedWorker()) {
    303    const RemoteWorkerChild* const controller =
    304        mWorkerPrivate->GetRemoteWorkerController();
    305    MOZ_ASSERT(controller);
    306    aResult = controller->WindowIDs().Clone();
    307  }
    308 
    309  return NS_OK;
    310 }
    311 
    312 nsCOMPtr<nsPIDOMWindowInner> WorkerDebugger::DedicatedWorkerWindow() {
    313  MOZ_ASSERT(mWorkerPrivate);
    314 
    315  WorkerPrivate* worker = mWorkerPrivate;
    316  while (worker->GetParent()) {
    317    worker = worker->GetParent();
    318  }
    319 
    320  if (!worker->IsDedicatedWorker()) {
    321    return nullptr;
    322  }
    323 
    324  return worker->GetWindow();
    325 }
    326 
    327 NS_IMETHODIMP
    328 WorkerDebugger::GetPrincipal(nsIPrincipal** aResult) {
    329  AssertIsOnMainThread();
    330  MOZ_ASSERT(aResult);
    331 
    332  if (!mWorkerPrivate) {
    333    return NS_ERROR_UNEXPECTED;
    334  }
    335 
    336  nsCOMPtr<nsIPrincipal> prin = mWorkerPrivate->GetPrincipal();
    337  prin.forget(aResult);
    338 
    339  return NS_OK;
    340 }
    341 
    342 NS_IMETHODIMP
    343 WorkerDebugger::GetServiceWorkerID(uint32_t* aResult) {
    344  AssertIsOnMainThread();
    345  MOZ_ASSERT(aResult);
    346 
    347  if (!mWorkerPrivate || !mWorkerPrivate->IsServiceWorker()) {
    348    return NS_ERROR_UNEXPECTED;
    349  }
    350 
    351  *aResult = mWorkerPrivate->ServiceWorkerID();
    352  return NS_OK;
    353 }
    354 
    355 NS_IMETHODIMP
    356 WorkerDebugger::GetId(nsAString& aResult) {
    357  AssertIsOnMainThread();
    358 
    359  if (!mWorkerPrivate) {
    360    return NS_ERROR_UNEXPECTED;
    361  }
    362 
    363  aResult = mWorkerPrivate->Id();
    364  return NS_OK;
    365 }
    366 
    367 NS_IMETHODIMP
    368 WorkerDebugger::GetName(nsAString& aResult) {
    369  AssertIsOnMainThread();
    370 
    371  if (!mWorkerPrivate) {
    372    return NS_ERROR_UNEXPECTED;
    373  }
    374 
    375  aResult = mWorkerPrivate->WorkerName();
    376  return NS_OK;
    377 }
    378 
    379 NS_IMETHODIMP
    380 WorkerDebugger::Initialize(const nsAString& aURL) {
    381  AssertIsOnMainThread();
    382 
    383  if (!mWorkerPrivate) {
    384    return NS_ERROR_UNEXPECTED;
    385  }
    386 
    387  // This should be non-null for dedicated workers and null for Shared and
    388  // Service workers. All Encoding values are static and will live as long
    389  // as the process and the convention is to therefore use raw pointers.
    390  const mozilla::Encoding* aDocumentEncoding =
    391      NS_IsMainThread() && !mWorkerPrivate->GetParent() &&
    392              mWorkerPrivate->GetDocument()
    393          ? mWorkerPrivate->GetDocument()->GetDocumentCharacterSet().get()
    394          : nullptr;
    395 
    396  if (!mIsInitialized) {
    397    RefPtr<CompileDebuggerScriptRunnable> runnable =
    398        new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL,
    399                                          aDocumentEncoding);
    400    if (!runnable->Dispatch(mWorkerPrivate)) {
    401      return NS_ERROR_FAILURE;
    402    }
    403 
    404    mIsInitialized = true;
    405  }
    406 
    407  return NS_OK;
    408 }
    409 
    410 NS_IMETHODIMP
    411 WorkerDebugger::PostMessageMoz(const nsAString& aMessage) {
    412  AssertIsOnMainThread();
    413 
    414  if (!mWorkerPrivate || !mIsInitialized) {
    415    return NS_ERROR_UNEXPECTED;
    416  }
    417 
    418  RefPtr<DebuggerMessageEventRunnable> runnable =
    419      new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
    420  if (!runnable->Dispatch(mWorkerPrivate)) {
    421    return NS_ERROR_FAILURE;
    422  }
    423 
    424  return NS_OK;
    425 }
    426 
    427 NS_IMETHODIMP
    428 WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener) {
    429  AssertIsOnMainThread();
    430 
    431  if (mListeners.Contains(aListener)) {
    432    return NS_ERROR_INVALID_ARG;
    433  }
    434 
    435  mListeners.AppendElement(aListener);
    436  return NS_OK;
    437 }
    438 
    439 NS_IMETHODIMP
    440 WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
    441  AssertIsOnMainThread();
    442 
    443  if (!mListeners.Contains(aListener)) {
    444    return NS_ERROR_INVALID_ARG;
    445  }
    446 
    447  mListeners.RemoveElement(aListener);
    448  return NS_OK;
    449 }
    450 
    451 NS_IMETHODIMP
    452 WorkerDebugger::SetDebuggerReady(bool aReady) {
    453  return mWorkerPrivate->SetIsDebuggerReady(aReady);
    454 }
    455 
    456 void WorkerDebugger::Close() {
    457  MOZ_ASSERT(mWorkerPrivate);
    458  mWorkerPrivate = nullptr;
    459 
    460  for (const auto& listener : mListeners.Clone()) {
    461    listener->OnClose();
    462  }
    463 }
    464 
    465 void WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) {
    466  mWorkerPrivate->AssertIsOnWorkerThread();
    467 
    468  RefPtr<PostDebuggerMessageRunnable> runnable =
    469      new PostDebuggerMessageRunnable(this, aMessage);
    470  if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
    471          runnable.forget()))) {
    472    NS_WARNING("Failed to post message to debugger on main thread!");
    473  }
    474 }
    475 
    476 void WorkerDebugger::PostMessageToDebuggerOnMainThread(
    477    const nsAString& aMessage) {
    478  AssertIsOnMainThread();
    479 
    480  for (const auto& listener : mListeners.Clone()) {
    481    listener->OnMessage(aMessage);
    482  }
    483 }
    484 
    485 void WorkerDebugger::ReportErrorToDebugger(const nsACString& aFilename,
    486                                           uint32_t aLineno,
    487                                           const nsAString& aMessage) {
    488  mWorkerPrivate->AssertIsOnWorkerThread();
    489 
    490  RefPtr<ReportDebuggerErrorRunnable> runnable =
    491      new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
    492  if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
    493          runnable.forget()))) {
    494    NS_WARNING("Failed to report error to debugger on main thread!");
    495  }
    496 }
    497 
    498 void WorkerDebugger::ReportErrorToDebuggerOnMainThread(
    499    const nsACString& aFilename, uint32_t aLineno, const nsAString& aMessage) {
    500  AssertIsOnMainThread();
    501 
    502  for (const auto& listener : mListeners.Clone()) {
    503    listener->OnError(aFilename, aLineno, aMessage);
    504  }
    505 
    506  AutoJSAPI jsapi;
    507  // We're only using this context to deserialize a stack to report to the
    508  // console, so the scope we use doesn't matter. Stack frame filtering happens
    509  // based on the principal encoded into the frame and the caller compartment,
    510  // not the compartment of the frame object, and the console reporting code
    511  // will not be using our context, and therefore will not care what compartment
    512  // it has entered.
    513  DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
    514  MOZ_ASSERT(ok, "PrivilegedJunkScope should exist");
    515 
    516  WorkerErrorReport report;
    517  report.mMessage = aMessage;
    518  report.mFilename = aFilename;
    519  WorkerErrorReport::LogErrorToConsole(jsapi.cx(), report, 0);
    520 }
    521 
    522 }  // namespace mozilla::dom