Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / workers / WorkerDebugger.cpp
blob3110a5a6e17454b904c51368385f4f7f2cff58fc
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/. */
7 #include "WorkerDebugger.h"
9 #include "mozilla/dom/MessageEvent.h"
10 #include "mozilla/dom/MessageEventBinding.h"
11 #include "mozilla/PerformanceUtils.h"
12 #include "nsProxyRelease.h"
13 #include "nsQueryObject.h"
14 #include "nsThreadUtils.h"
15 #include "ScriptLoader.h"
16 #include "WorkerCommon.h"
17 #include "WorkerError.h"
18 #include "WorkerPrivate.h"
19 #include "WorkerRunnable.h"
20 #include "WorkerScope.h"
21 #if defined(XP_WIN)
22 # include <processthreadsapi.h> // for GetCurrentProcessId()
23 #else
24 # include <unistd.h> // for getpid()
25 #endif // defined(XP_WIN)
27 namespace mozilla {
28 namespace dom {
30 namespace {
32 class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
33 nsString mMessage;
35 public:
36 DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
37 const nsAString& aMessage)
38 : WorkerDebuggerRunnable(aWorkerPrivate), mMessage(aMessage) {}
40 private:
41 virtual bool WorkerRun(JSContext* aCx,
42 WorkerPrivate* aWorkerPrivate) override {
43 WorkerDebuggerGlobalScope* globalScope =
44 aWorkerPrivate->DebuggerGlobalScope();
45 MOZ_ASSERT(globalScope);
47 JS::Rooted<JSString*> message(
48 aCx, JS_NewUCStringCopyN(aCx, mMessage.get(), mMessage.Length()));
49 if (!message) {
50 return false;
52 JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
54 RefPtr<MessageEvent> event =
55 new MessageEvent(globalScope, nullptr, nullptr);
56 event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
57 CanBubble::eNo, Cancelable::eYes, data,
58 EmptyString(), EmptyString(), nullptr,
59 Sequence<OwningNonNull<MessagePort>>());
60 event->SetTrusted(true);
62 globalScope->DispatchEvent(*event);
63 return true;
67 class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable {
68 nsString mScriptURL;
70 public:
71 CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate,
72 const nsAString& aScriptURL)
73 : WorkerDebuggerRunnable(aWorkerPrivate), mScriptURL(aScriptURL) {}
75 private:
76 virtual bool WorkerRun(JSContext* aCx,
77 WorkerPrivate* aWorkerPrivate) override {
78 aWorkerPrivate->AssertIsOnWorkerThread();
80 WorkerDebuggerGlobalScope* globalScope =
81 aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
82 if (!globalScope) {
83 NS_WARNING("Failed to make global!");
84 return false;
87 if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
88 return false;
91 if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
92 return false;
95 // Initialize performance state which might be used on the main thread, as
96 // in CompileScriptRunnable. This runnable might execute first.
97 aWorkerPrivate->EnsurePerformanceStorage();
98 aWorkerPrivate->EnsurePerformanceCounter();
100 JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
102 ErrorResult rv;
103 JSAutoRealm ar(aCx, global);
104 workerinternals::LoadMainScript(aWorkerPrivate, nullptr, mScriptURL,
105 DebuggerScript, rv);
106 rv.WouldReportJSException();
107 // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
108 // return false and don't SetWorkerScriptExecutedSuccessfully() in that
109 // case, but don't throw anything on aCx. The idea is to not dispatch error
110 // events if our load is canceled with that error code.
111 if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
112 rv.SuppressException();
113 return false;
115 // Make sure to propagate exceptions from rv onto aCx, so that they will get
116 // reported after we return. We do this for all failures on rv, because now
117 // we're using rv to track all the state we care about.
118 if (rv.MaybeSetPendingException(aCx)) {
119 return false;
122 return true;
126 } // namespace
128 class WorkerDebugger::PostDebuggerMessageRunnable final : public Runnable {
129 WorkerDebugger* mDebugger;
130 nsString mMessage;
132 public:
133 PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
134 const nsAString& aMessage)
135 : mozilla::Runnable("PostDebuggerMessageRunnable"),
136 mDebugger(aDebugger),
137 mMessage(aMessage) {}
139 private:
140 ~PostDebuggerMessageRunnable() {}
142 NS_IMETHOD
143 Run() override {
144 mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
146 return NS_OK;
150 class WorkerDebugger::ReportDebuggerErrorRunnable final : public Runnable {
151 WorkerDebugger* mDebugger;
152 nsString mFilename;
153 uint32_t mLineno;
154 nsString mMessage;
156 public:
157 ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
158 const nsAString& aFilename, uint32_t aLineno,
159 const nsAString& aMessage)
160 : Runnable("ReportDebuggerErrorRunnable"),
161 mDebugger(aDebugger),
162 mFilename(aFilename),
163 mLineno(aLineno),
164 mMessage(aMessage) {}
166 private:
167 ~ReportDebuggerErrorRunnable() {}
169 NS_IMETHOD
170 Run() override {
171 mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
173 return NS_OK;
177 WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
178 : mWorkerPrivate(aWorkerPrivate), mIsInitialized(false) {
179 AssertIsOnMainThread();
182 WorkerDebugger::~WorkerDebugger() {
183 MOZ_ASSERT(!mWorkerPrivate);
185 if (!NS_IsMainThread()) {
186 for (size_t index = 0; index < mListeners.Length(); ++index) {
187 NS_ReleaseOnMainThreadSystemGroup("WorkerDebugger::mListeners",
188 mListeners[index].forget());
193 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
195 NS_IMETHODIMP
196 WorkerDebugger::GetIsClosed(bool* aResult) {
197 AssertIsOnMainThread();
199 *aResult = !mWorkerPrivate;
200 return NS_OK;
203 NS_IMETHODIMP
204 WorkerDebugger::GetIsChrome(bool* aResult) {
205 AssertIsOnMainThread();
207 if (!mWorkerPrivate) {
208 return NS_ERROR_UNEXPECTED;
211 *aResult = mWorkerPrivate->IsChromeWorker();
212 return NS_OK;
215 NS_IMETHODIMP
216 WorkerDebugger::GetIsInitialized(bool* aResult) {
217 AssertIsOnMainThread();
219 if (!mWorkerPrivate) {
220 return NS_ERROR_UNEXPECTED;
223 *aResult = mIsInitialized;
224 return NS_OK;
227 NS_IMETHODIMP
228 WorkerDebugger::GetParent(nsIWorkerDebugger** aResult) {
229 AssertIsOnMainThread();
231 if (!mWorkerPrivate) {
232 return NS_ERROR_UNEXPECTED;
235 WorkerPrivate* parent = mWorkerPrivate->GetParent();
236 if (!parent) {
237 *aResult = nullptr;
238 return NS_OK;
241 MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
243 nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
244 debugger.forget(aResult);
245 return NS_OK;
248 NS_IMETHODIMP
249 WorkerDebugger::GetType(uint32_t* aResult) {
250 AssertIsOnMainThread();
252 if (!mWorkerPrivate) {
253 return NS_ERROR_UNEXPECTED;
256 *aResult = mWorkerPrivate->Type();
257 return NS_OK;
260 NS_IMETHODIMP
261 WorkerDebugger::GetUrl(nsAString& aResult) {
262 AssertIsOnMainThread();
264 if (!mWorkerPrivate) {
265 return NS_ERROR_UNEXPECTED;
268 aResult = mWorkerPrivate->ScriptURL();
269 return NS_OK;
272 NS_IMETHODIMP
273 WorkerDebugger::GetWindow(mozIDOMWindow** aResult) {
274 AssertIsOnMainThread();
276 if (!mWorkerPrivate) {
277 return NS_ERROR_UNEXPECTED;
280 if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) {
281 *aResult = nullptr;
282 return NS_OK;
285 nsCOMPtr<nsPIDOMWindowInner> window = mWorkerPrivate->GetWindow();
286 window.forget(aResult);
287 return NS_OK;
290 NS_IMETHODIMP
291 WorkerDebugger::GetPrincipal(nsIPrincipal** aResult) {
292 AssertIsOnMainThread();
293 MOZ_ASSERT(aResult);
295 if (!mWorkerPrivate) {
296 return NS_ERROR_UNEXPECTED;
299 nsCOMPtr<nsIPrincipal> prin = mWorkerPrivate->GetPrincipal();
300 prin.forget(aResult);
302 return NS_OK;
305 NS_IMETHODIMP
306 WorkerDebugger::GetServiceWorkerID(uint32_t* aResult) {
307 AssertIsOnMainThread();
308 MOZ_ASSERT(aResult);
310 if (!mWorkerPrivate || !mWorkerPrivate->IsServiceWorker()) {
311 return NS_ERROR_UNEXPECTED;
314 *aResult = mWorkerPrivate->ServiceWorkerID();
315 return NS_OK;
318 NS_IMETHODIMP
319 WorkerDebugger::GetId(nsAString& aResult) {
320 AssertIsOnMainThread();
322 if (!mWorkerPrivate) {
323 return NS_ERROR_UNEXPECTED;
326 aResult = mWorkerPrivate->Id();
327 return NS_OK;
330 NS_IMETHODIMP
331 WorkerDebugger::Initialize(const nsAString& aURL) {
332 AssertIsOnMainThread();
334 if (!mWorkerPrivate) {
335 return NS_ERROR_UNEXPECTED;
338 if (!mIsInitialized) {
339 RefPtr<CompileDebuggerScriptRunnable> runnable =
340 new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL);
341 if (!runnable->Dispatch()) {
342 return NS_ERROR_FAILURE;
345 mIsInitialized = true;
348 return NS_OK;
351 NS_IMETHODIMP
352 WorkerDebugger::PostMessageMoz(const nsAString& aMessage) {
353 AssertIsOnMainThread();
355 if (!mWorkerPrivate || !mIsInitialized) {
356 return NS_ERROR_UNEXPECTED;
359 RefPtr<DebuggerMessageEventRunnable> runnable =
360 new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
361 if (!runnable->Dispatch()) {
362 return NS_ERROR_FAILURE;
365 return NS_OK;
368 NS_IMETHODIMP
369 WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener) {
370 AssertIsOnMainThread();
372 if (mListeners.Contains(aListener)) {
373 return NS_ERROR_INVALID_ARG;
376 mListeners.AppendElement(aListener);
377 return NS_OK;
380 NS_IMETHODIMP
381 WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener) {
382 AssertIsOnMainThread();
384 if (!mListeners.Contains(aListener)) {
385 return NS_ERROR_INVALID_ARG;
388 mListeners.RemoveElement(aListener);
389 return NS_OK;
392 NS_IMETHODIMP
393 WorkerDebugger::SetDebuggerReady(bool aReady) {
394 return mWorkerPrivate->SetIsDebuggerReady(aReady);
397 void WorkerDebugger::Close() {
398 MOZ_ASSERT(mWorkerPrivate);
399 mWorkerPrivate = nullptr;
401 nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
402 for (size_t index = 0; index < listeners.Length(); ++index) {
403 listeners[index]->OnClose();
407 void WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage) {
408 mWorkerPrivate->AssertIsOnWorkerThread();
410 RefPtr<PostDebuggerMessageRunnable> runnable =
411 new PostDebuggerMessageRunnable(this, aMessage);
412 if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
413 runnable.forget()))) {
414 NS_WARNING("Failed to post message to debugger on main thread!");
418 void WorkerDebugger::PostMessageToDebuggerOnMainThread(
419 const nsAString& aMessage) {
420 AssertIsOnMainThread();
422 nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
423 for (size_t index = 0; index < listeners.Length(); ++index) {
424 listeners[index]->OnMessage(aMessage);
428 void WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
429 uint32_t aLineno,
430 const nsAString& aMessage) {
431 mWorkerPrivate->AssertIsOnWorkerThread();
433 RefPtr<ReportDebuggerErrorRunnable> runnable =
434 new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
435 if (NS_FAILED(mWorkerPrivate->DispatchToMainThreadForMessaging(
436 runnable.forget()))) {
437 NS_WARNING("Failed to report error to debugger on main thread!");
441 void WorkerDebugger::ReportErrorToDebuggerOnMainThread(
442 const nsAString& aFilename, uint32_t aLineno, const nsAString& aMessage) {
443 AssertIsOnMainThread();
445 nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
446 for (size_t index = 0; index < listeners.Length(); ++index) {
447 listeners[index]->OnError(aFilename, aLineno, aMessage);
450 // We need a JSContext to be able to read any stack associated with the error.
451 // This will not run any scripts.
452 AutoJSAPI jsapi;
453 DebugOnly<bool> ok = jsapi.Init(xpc::UnprivilegedJunkScope());
454 MOZ_ASSERT(ok, "UnprivilegedJunkScope should exist");
456 WorkerErrorReport report;
457 report.mMessage = aMessage;
458 report.mFilename = aFilename;
459 WorkerErrorReport::LogErrorToConsole(jsapi.cx(), report, 0);
462 RefPtr<PerformanceInfoPromise> WorkerDebugger::ReportPerformanceInfo() {
463 AssertIsOnMainThread();
464 nsCOMPtr<nsPIDOMWindowOuter> top;
465 RefPtr<WorkerDebugger> self = this;
467 #if defined(XP_WIN)
468 uint32_t pid = GetCurrentProcessId();
469 #else
470 uint32_t pid = getpid();
471 #endif
472 bool isTopLevel = false;
473 uint64_t windowID = mWorkerPrivate->WindowID();
474 PerformanceMemoryInfo memoryInfo;
476 // Walk up to our containing page and its window
477 WorkerPrivate* wp = mWorkerPrivate;
478 while (wp->GetParent()) {
479 wp = wp->GetParent();
481 nsPIDOMWindowInner* win = wp->GetWindow();
482 if (win) {
483 nsPIDOMWindowOuter* outer = win->GetOuterWindow();
484 if (outer) {
485 top = outer->GetTop();
486 if (top) {
487 windowID = top->WindowID();
488 isTopLevel = outer->IsTopLevelWindow();
493 // getting the worker URL
494 RefPtr<nsIURI> scriptURI = mWorkerPrivate->GetResolvedScriptURI();
495 if (NS_WARN_IF(!scriptURI)) {
496 // This can happen at shutdown, let's stop here.
497 return PerformanceInfoPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
499 nsCString url = scriptURI->GetSpecOrDefault();
501 // Workers only produce metrics for a single category -
502 // DispatchCategory::Worker. We still return an array of CategoryDispatch so
503 // the PerformanceInfo struct is common to all performance counters throughout
504 // Firefox.
505 FallibleTArray<CategoryDispatch> items;
506 uint64_t duration = 0;
507 uint16_t count = 0;
508 uint64_t perfId = 0;
510 RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
511 if (perf) {
512 perfId = perf->GetID();
513 count = perf->GetTotalDispatchCount();
514 duration = perf->GetExecutionDuration();
515 CategoryDispatch item =
516 CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
517 if (!items.AppendElement(item, fallible)) {
518 NS_ERROR("Could not complete the operation");
522 if (!isTopLevel) {
523 return PerformanceInfoPromise::CreateAndResolve(
524 PerformanceInfo(url, pid, windowID, duration, perfId, true, isTopLevel,
525 memoryInfo, items),
526 __func__);
529 // We need to keep a ref on workerPrivate, passed to the promise,
530 // to make sure it's still aloive when collecting the info.
531 RefPtr<WorkerPrivate> workerRef = mWorkerPrivate;
532 RefPtr<AbstractThread> mainThread =
533 SystemGroup::AbstractMainThreadFor(TaskCategory::Performance);
535 return CollectMemoryInfo(top, mainThread)
536 ->Then(
537 mainThread, __func__,
538 [workerRef, url, pid, perfId, windowID, duration, isTopLevel,
539 items](const PerformanceMemoryInfo& aMemoryInfo) {
540 return PerformanceInfoPromise::CreateAndResolve(
541 PerformanceInfo(url, pid, windowID, duration, perfId, true,
542 isTopLevel, aMemoryInfo, items),
543 __func__);
545 [workerRef]() {
546 return PerformanceInfoPromise::CreateAndReject(NS_ERROR_FAILURE,
547 __func__);
551 } // namespace dom
552 } // namespace mozilla