forked from mirrors/gecko-dev
		
	- Fixes a data race where the member variable is being written to by EnsurePerformanceCounter on the worker thread while being read on a separate thread (via Worker.postMessage). - Apply some pointer guildelines to the member variable getters. - Constify some things that should be const. Differential Revision: https://phabricator.services.mozilla.com/D82475
		
			
				
	
	
		
			544 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			544 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "WorkerDebugger.h"
 | 
						|
 | 
						|
#include "mozilla/dom/MessageEvent.h"
 | 
						|
#include "mozilla/dom/MessageEventBinding.h"
 | 
						|
#include "mozilla/AbstractThread.h"
 | 
						|
#include "mozilla/PerformanceUtils.h"
 | 
						|
#include "nsProxyRelease.h"
 | 
						|
#include "nsQueryObject.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
#include "ScriptLoader.h"
 | 
						|
#include "WorkerCommon.h"
 | 
						|
#include "WorkerError.h"
 | 
						|
#include "WorkerPrivate.h"
 | 
						|
#include "WorkerRunnable.h"
 | 
						|
#include "WorkerScope.h"
 | 
						|
#if defined(XP_WIN)
 | 
						|
#  include <processthreadsapi.h>  // for GetCurrentProcessId()
 | 
						|
#else
 | 
						|
#  include <unistd.h>  // for getpid()
 | 
						|
#endif                 // defined(XP_WIN)
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace dom {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
 | 
						|
  nsString mMessage;
 | 
						|
 | 
						|
 public:
 | 
						|
  DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
 | 
						|
                               const nsAString& aMessage)
 | 
						|
      : WorkerDebuggerRunnable(aWorkerPrivate), mMessage(aMessage) {}
 | 
						|
 | 
						|
 private:
 | 
						|
  virtual bool WorkerRun(JSContext* aCx,
 | 
						|
                         WorkerPrivate* aWorkerPrivate) override {
 | 
						|
    WorkerDebuggerGlobalScope* globalScope =
 | 
						|
        aWorkerPrivate->DebuggerGlobalScope();
 | 
						|
    MOZ_ASSERT(globalScope);
 | 
						|
 | 
						|
    JS::Rooted<JSString*> message(
 | 
						|
        aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), mMessage.Length()));
 | 
						|
    if (!message) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
 | 
						|
 | 
						|
    RefPtr<MessageEvent> event =
 | 
						|
        new MessageEvent(globalScope, nullptr, nullptr);
 | 
						|
    event->InitMessageEvent(nullptr, u"message"_ns, CanBubble::eNo,
 | 
						|
                            Cancelable::eYes, data, EmptyString(),
 | 
						|
                            EmptyString(), nullptr,
 | 
						|
                            Sequence<OwningNonNull<MessagePort>>());
 | 
						|
    event->SetTrusted(true);
 | 
						|
 | 
						|
    globalScope->DispatchEvent(*event);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable {
 | 
						|
  nsString mScriptURL;
 | 
						|
 | 
						|
 public:
 | 
						|
  CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate,
 | 
						|
                                const nsAString& aScriptURL)
 | 
						|
      : WorkerDebuggerRunnable(aWorkerPrivate), mScriptURL(aScriptURL) {}
 | 
						|
 | 
						|
 private:
 | 
						|
  virtual bool WorkerRun(JSContext* aCx,
 | 
						|
                         WorkerPrivate* aWorkerPrivate) override {
 | 
						|
    aWorkerPrivate->AssertIsOnWorkerThread();
 | 
						|
 | 
						|
    WorkerDebuggerGlobalScope* globalScope =
 | 
						|
        aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
 | 
						|
    if (!globalScope) {
 | 
						|
      NS_WARNING("Failed to make global!");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
 | 
						|
 | 
						|
    ErrorResult rv;
 | 
						|
    JSAutoRealm ar(aCx, global);
 | 
						|
    workerinternals::LoadMainScript(aWorkerPrivate, nullptr, mScriptURL,
 | 
						|
                                    DebuggerScript, rv);
 | 
						|
    rv.WouldReportJSException();
 | 
						|
    // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
 | 
						|
    // return false and don't SetWorkerScriptExecutedSuccessfully() in that
 | 
						|
    // case, but don't throw anything on aCx.  The idea is to not dispatch error
 | 
						|
    // events if our load is canceled with that error code.
 | 
						|
    if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
 | 
						|
      rv.SuppressException();
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    // Make sure to propagate exceptions from rv onto aCx, so that they will get
 | 
						|
    // reported after we return.  We do this for all failures on rv, because now
 | 
						|
    // we're using rv to track all the state we care about.
 | 
						|
    if (rv.MaybeSetPendingException(aCx)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
class WorkerDebugger::PostDebuggerMessageRunnable final : public Runnable {
 | 
						|
  WorkerDebugger* mDebugger;
 | 
						|
  nsString mMessage;
 | 
						|
 | 
						|
 public:
 | 
						|
  PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
 | 
						|
                              const nsAString& aMessage)
 | 
						|
      : mozilla::Runnable("PostDebuggerMessageRunnable"),
 | 
						|
        mDebugger(aDebugger),
 | 
						|
        mMessage(aMessage) {}
 | 
						|
 | 
						|
 private:
 | 
						|
  ~PostDebuggerMessageRunnable() = default;
 | 
						|
 | 
						|
  NS_IMETHOD
 | 
						|
  Run() override {
 | 
						|
    mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class WorkerDebugger::ReportDebuggerErrorRunnable final : public Runnable {
 | 
						|
  WorkerDebugger* mDebugger;
 | 
						|
  nsString mFilename;
 | 
						|
  uint32_t mLineno;
 | 
						|
  nsString mMessage;
 | 
						|
 | 
						|
 public:
 | 
						|
  ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
 | 
						|
                              const nsAString& aFilename, uint32_t aLineno,
 | 
						|
                              const nsAString& aMessage)
 | 
						|
      : Runnable("ReportDebuggerErrorRunnable"),
 | 
						|
        mDebugger(aDebugger),
 | 
						|
        mFilename(aFilename),
 | 
						|
        mLineno(aLineno),
 | 
						|
        mMessage(aMessage) {}
 | 
						|
 | 
						|
 private:
 | 
						|
  ~ReportDebuggerErrorRunnable() = default;
 | 
						|
 | 
						|
  NS_IMETHOD
 | 
						|
  Run() override {
 | 
						|
    mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
 | 
						|
    : mWorkerPrivate(aWorkerPrivate), mIsInitialized(false) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
}
 | 
						|
 | 
						|
WorkerDebugger::~WorkerDebugger() {
 | 
						|
  MOZ_ASSERT(!mWorkerPrivate);
 | 
						|
 | 
						|
  if (!NS_IsMainThread()) {
 | 
						|
    for (auto& listener : mListeners) {
 | 
						|
      NS_ReleaseOnMainThread("WorkerDebugger::mListeners", listener.forget());
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetIsClosed(bool* aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  *aResult = !mWorkerPrivate;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetIsChrome(bool* aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *aResult = mWorkerPrivate->IsChromeWorker();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetIsInitialized(bool* aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *aResult = mIsInitialized;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  WorkerPrivate* parent = mWorkerPrivate->GetParent();
 | 
						|
  if (!parent) {
 | 
						|
    *aResult = nullptr;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
 | 
						|
 | 
						|
  nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
 | 
						|
  debugger.forget(aResult);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetType(uint32_t* aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *aResult = mWorkerPrivate->Type();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetUrl(nsAString& aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  aResult = mWorkerPrivate->ScriptURL();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetWindow(mozIDOMWindow** aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  WorkerPrivate* worker = mWorkerPrivate;
 | 
						|
  while (worker->GetParent()) {
 | 
						|
    worker = worker->GetParent();
 | 
						|
  }
 | 
						|
 | 
						|
  if (!worker->IsDedicatedWorker()) {
 | 
						|
    *aResult = nullptr;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsPIDOMWindowInner> window = worker->GetWindow();
 | 
						|
  window.forget(aResult);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetPrincipal(nsIPrincipal** aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
  MOZ_ASSERT(aResult);
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIPrincipal> prin = mWorkerPrivate->GetPrincipal();
 | 
						|
  prin.forget(aResult);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetServiceWorkerID(uint32_t* aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
  MOZ_ASSERT(aResult);
 | 
						|
 | 
						|
  if (!mWorkerPrivate || !mWorkerPrivate->IsServiceWorker()) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *aResult = mWorkerPrivate->ServiceWorkerID();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::GetId(nsAString& aResult) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  aResult = mWorkerPrivate->Id();
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::Initialize(const nsAString& aURL) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!mIsInitialized) {
 | 
						|
    RefPtr<CompileDebuggerScriptRunnable> runnable =
 | 
						|
        new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL);
 | 
						|
    if (!runnable->Dispatch()) {
 | 
						|
      return NS_ERROR_FAILURE;
 | 
						|
    }
 | 
						|
 | 
						|
    mIsInitialized = true;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::PostMessageMoz(const nsAString& aMessage) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mWorkerPrivate || !mIsInitialized) {
 | 
						|
    return NS_ERROR_UNEXPECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<DebuggerMessageEventRunnable> runnable =
 | 
						|
      new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
 | 
						|
  if (!runnable->Dispatch()) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (mListeners.Contains(aListener)) {
 | 
						|
    return NS_ERROR_INVALID_ARG;
 | 
						|
  }
 | 
						|
 | 
						|
  mListeners.AppendElement(aListener);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  if (!mListeners.Contains(aListener)) {
 | 
						|
    return NS_ERROR_INVALID_ARG;
 | 
						|
  }
 | 
						|
 | 
						|
  mListeners.RemoveElement(aListener);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
WorkerDebugger::SetDebuggerReady(bool aReady) {
 | 
						|
  return mWorkerPrivate->SetIsDebuggerReady(aReady);
 | 
						|
}
 | 
						|
 | 
						|
void WorkerDebugger::Close() {
 | 
						|
  MOZ_ASSERT(mWorkerPrivate);
 | 
						|
  mWorkerPrivate = nullptr;
 | 
						|
 | 
						|
  for (const auto& listener : mListeners.Clone()) {
 | 
						|
    listener->OnClose();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) {
 | 
						|
  mWorkerPrivate->AssertIsOnWorkerThread();
 | 
						|
 | 
						|
  RefPtr<PostDebuggerMessageRunnable> runnable =
 | 
						|
      new PostDebuggerMessageRunnable(this, aMessage);
 | 
						|
  if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
 | 
						|
          runnable.forget()))) {
 | 
						|
    NS_WARNING("Failed to post message to debugger on main thread!");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void WorkerDebugger::PostMessageToDebuggerOnMainThread(
 | 
						|
    const nsAString& aMessage) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  for (const auto& listener : mListeners.Clone()) {
 | 
						|
    listener->OnMessage(aMessage);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
 | 
						|
                                           uint32_t aLineno,
 | 
						|
                                           const nsAString& aMessage) {
 | 
						|
  mWorkerPrivate->AssertIsOnWorkerThread();
 | 
						|
 | 
						|
  RefPtr<ReportDebuggerErrorRunnable> runnable =
 | 
						|
      new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
 | 
						|
  if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
 | 
						|
          runnable.forget()))) {
 | 
						|
    NS_WARNING("Failed to report error to debugger on main thread!");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void WorkerDebugger::ReportErrorToDebuggerOnMainThread(
 | 
						|
    const nsAString& aFilename, uint32_t aLineno, const nsAString& aMessage) {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
 | 
						|
  for (const auto& listener : mListeners.Clone()) {
 | 
						|
    listener->OnError(aFilename, aLineno, aMessage);
 | 
						|
  }
 | 
						|
 | 
						|
  AutoJSAPI jsapi;
 | 
						|
  // We're only using this context to deserialize a stack to report to the
 | 
						|
  // console, so the scope we use doesn't matter. Stack frame filtering happens
 | 
						|
  // based on the principal encoded into the frame and the caller compartment,
 | 
						|
  // not the compartment of the frame object, and the console reporting code
 | 
						|
  // will not be using our context, and therefore will not care what compartment
 | 
						|
  // it has entered.
 | 
						|
  DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
 | 
						|
  MOZ_ASSERT(ok, "PrivilegedJunkScope should exist");
 | 
						|
 | 
						|
  WorkerErrorReport report;
 | 
						|
  report.mMessage = aMessage;
 | 
						|
  report.mFilename = aFilename;
 | 
						|
  WorkerErrorReport::LogErrorToConsole(jsapi.cx(), report, 0);
 | 
						|
}
 | 
						|
 | 
						|
RefPtr<PerformanceInfoPromise> WorkerDebugger::ReportPerformanceInfo() {
 | 
						|
  AssertIsOnMainThread();
 | 
						|
  nsCOMPtr<nsPIDOMWindowOuter> top;
 | 
						|
  RefPtr<WorkerDebugger> self = this;
 | 
						|
 | 
						|
#if defined(XP_WIN)
 | 
						|
  uint32_t pid = GetCurrentProcessId();
 | 
						|
#else
 | 
						|
  uint32_t pid = getpid();
 | 
						|
#endif
 | 
						|
  bool isTopLevel = false;
 | 
						|
  uint64_t windowID = mWorkerPrivate->WindowID();
 | 
						|
  PerformanceMemoryInfo memoryInfo;
 | 
						|
 | 
						|
  // Walk up to our containing page and its window
 | 
						|
  WorkerPrivate* wp = mWorkerPrivate;
 | 
						|
  while (wp->GetParent()) {
 | 
						|
    wp = wp->GetParent();
 | 
						|
  }
 | 
						|
  nsPIDOMWindowInner* win = wp->GetWindow();
 | 
						|
  if (win) {
 | 
						|
    nsPIDOMWindowOuter* outer = win->GetOuterWindow();
 | 
						|
    if (outer) {
 | 
						|
      top = outer->GetInProcessTop();
 | 
						|
      if (top) {
 | 
						|
        windowID = top->WindowID();
 | 
						|
        isTopLevel = outer->GetBrowsingContext()->IsTop();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // getting the worker URL
 | 
						|
  RefPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
 | 
						|
  if (NS_WARN_IF(!scriptURI)) {
 | 
						|
    // This can happen at shutdown, let's stop here.
 | 
						|
    return PerformanceInfoPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
 | 
						|
  }
 | 
						|
  nsCString url = scriptURI->GetSpecOrDefault();
 | 
						|
 | 
						|
  const auto& perf = mWorkerPrivate->PerformanceCounterRef();
 | 
						|
  uint64_t perfId = perf.GetID();
 | 
						|
  uint16_t count = perf.GetTotalDispatchCount();
 | 
						|
  uint64_t duration = perf.GetExecutionDuration();
 | 
						|
 | 
						|
  // Workers only produce metrics for a single category -
 | 
						|
  // DispatchCategory::Worker. We still return an array of CategoryDispatch so
 | 
						|
  // the PerformanceInfo struct is common to all performance counters throughout
 | 
						|
  // Firefox.
 | 
						|
  FallibleTArray<CategoryDispatch> items;
 | 
						|
 | 
						|
  CategoryDispatch item =
 | 
						|
      CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
 | 
						|
  if (!items.AppendElement(item, fallible)) {
 | 
						|
    NS_ERROR("Could not complete the operation");
 | 
						|
  }
 | 
						|
 | 
						|
  if (!isTopLevel) {
 | 
						|
    return PerformanceInfoPromise::CreateAndResolve(
 | 
						|
        PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel,
 | 
						|
                        memoryInfo, items),
 | 
						|
        __func__);
 | 
						|
  }
 | 
						|
 | 
						|
  // We need to keep a ref on workerPrivate, passed to the promise,
 | 
						|
  // to make sure it's still aloive when collecting the info.
 | 
						|
  RefPtr<WorkerPrivate> workerRef = mWorkerPrivate;
 | 
						|
  RefPtr<AbstractThread> mainThread = AbstractThread::MainThread();
 | 
						|
 | 
						|
  return CollectMemoryInfo(top, mainThread)
 | 
						|
      ->Then(
 | 
						|
          mainThread, __func__,
 | 
						|
          [workerRef, url, pid, perfId, windowID, duration, isTopLevel,
 | 
						|
           items = std::move(items)](const PerformanceMemoryInfo& aMemoryInfo) {
 | 
						|
            return PerformanceInfoPromise::CreateAndResolve(
 | 
						|
                PerformanceInfo(url, pid, windowID, duration, perfId, true,
 | 
						|
                                isTopLevel, aMemoryInfo, items),
 | 
						|
                __func__);
 | 
						|
          },
 | 
						|
          [workerRef]() {
 | 
						|
            return PerformanceInfoPromise::CreateAndReject(NS_ERROR_FAILURE,
 | 
						|
                                                           __func__);
 | 
						|
          });
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace dom
 | 
						|
}  // namespace mozilla
 |