Coverage Report

Created: 2018-09-25 14:53

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