Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/script/ScriptSettings.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 "mozilla/dom/ScriptSettings.h"
8
#include "mozilla/ThreadLocal.h"
9
#include "mozilla/Assertions.h"
10
#include "mozilla/CycleCollectedJSContext.h"
11
#include "mozilla/dom/WorkerPrivate.h"
12
13
#include "jsapi.h"
14
#include "js/StableStringChars.h"
15
#include "xpcpublic.h"
16
#include "nsIGlobalObject.h"
17
#include "nsIDocShell.h"
18
#include "nsIScriptGlobalObject.h"
19
#include "nsIScriptContext.h"
20
#include "nsContentUtils.h"
21
#include "nsGlobalWindow.h"
22
#include "nsPIDOMWindow.h"
23
#include "nsTArray.h"
24
#include "nsJSUtils.h"
25
#include "nsDOMJSUtils.h"
26
27
namespace mozilla {
28
namespace dom {
29
30
static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
31
static bool sScriptSettingsTLSInitialized;
32
33
class ScriptSettingsStack
34
{
35
public:
36
61.7M
  static ScriptSettingsStackEntry* Top() {
37
61.7M
    return sScriptSettingsTLS.get();
38
61.7M
  }
39
40
  static void Push(ScriptSettingsStackEntry* aEntry)
41
1.62M
  {
42
1.62M
    MOZ_ASSERT(!aEntry->mOlder);
43
1.62M
    // Whenever JSAPI use is disabled, the next stack entry pushed must
44
1.62M
    // not be an AutoIncumbentScript.
45
1.62M
    MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(),
46
1.62M
                  !aEntry->IsIncumbentScript());
47
1.62M
    // Whenever the top entry is not an incumbent canidate, the next stack entry
48
1.62M
    // pushed must not be an AutoIncumbentScript.
49
1.62M
    MOZ_ASSERT_IF(Top() && !Top()->IsIncumbentCandidate(),
50
1.62M
                  !aEntry->IsIncumbentScript());
51
1.62M
52
1.62M
    aEntry->mOlder = Top();
53
1.62M
    sScriptSettingsTLS.set(aEntry);
54
1.62M
  }
55
56
  static void Pop(ScriptSettingsStackEntry* aEntry)
57
1.62M
  {
58
1.62M
    MOZ_ASSERT(aEntry == Top());
59
1.62M
    sScriptSettingsTLS.set(aEntry->mOlder);
60
1.62M
  }
61
62
  static nsIGlobalObject* IncumbentGlobal()
63
0
  {
64
0
    ScriptSettingsStackEntry* entry = Top();
65
0
    while (entry) {
66
0
      if (entry->IsIncumbentCandidate()) {
67
0
        return entry->mGlobalObject;
68
0
      }
69
0
      entry = entry->mOlder;
70
0
    }
71
0
    return nullptr;
72
0
  }
73
74
  static ScriptSettingsStackEntry* EntryPoint()
75
0
  {
76
0
    ScriptSettingsStackEntry* entry = Top();
77
0
    while (entry) {
78
0
      if (entry->IsEntryCandidate()) {
79
0
        return entry;
80
0
      }
81
0
      entry = entry->mOlder;
82
0
    }
83
0
    return nullptr;
84
0
  }
85
86
  static nsIGlobalObject* EntryGlobal()
87
0
  {
88
0
    ScriptSettingsStackEntry* entry = EntryPoint();
89
0
    if (!entry) {
90
0
      return nullptr;
91
0
    }
92
0
    return entry->mGlobalObject;
93
0
  }
94
95
#ifdef DEBUG
96
  static ScriptSettingsStackEntry* TopNonIncumbentScript()
97
  {
98
    ScriptSettingsStackEntry* entry = Top();
99
    while (entry) {
100
      if (!entry->IsIncumbentScript()) {
101
        return entry;
102
      }
103
      entry = entry->mOlder;
104
    }
105
    return nullptr;
106
  }
107
#endif // DEBUG
108
109
};
110
111
static unsigned long gRunToCompletionListeners = 0;
112
113
void
114
UseEntryScriptProfiling()
115
0
{
116
0
  MOZ_ASSERT(NS_IsMainThread());
117
0
  ++gRunToCompletionListeners;
118
0
}
119
120
void
121
UnuseEntryScriptProfiling()
122
0
{
123
0
  MOZ_ASSERT(NS_IsMainThread());
124
0
  MOZ_ASSERT(gRunToCompletionListeners > 0);
125
0
  --gRunToCompletionListeners;
126
0
}
127
128
void
129
InitScriptSettings()
130
3
{
131
3
  bool success = sScriptSettingsTLS.init();
132
3
  if (!success) {
133
0
    MOZ_CRASH();
134
0
  }
135
3
136
3
  sScriptSettingsTLS.set(nullptr);
137
3
  sScriptSettingsTLSInitialized = true;
138
3
}
139
140
void
141
DestroyScriptSettings()
142
0
{
143
0
  MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr);
144
0
}
145
146
bool
147
ScriptSettingsInitialized()
148
0
{
149
0
  return sScriptSettingsTLSInitialized;
150
0
}
151
152
ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject* aGlobal,
153
                                                   Type aType)
154
  : mGlobalObject(aGlobal)
155
  , mType(aType)
156
  , mOlder(nullptr)
157
56.8M
{
158
56.8M
  MOZ_ASSERT_IF(IsIncumbentCandidate() && !NoJSAPI(), mGlobalObject);
159
56.8M
  MOZ_ASSERT(!mGlobalObject || mGlobalObject->GetGlobalJSObject(),
160
56.8M
             "Must have an actual JS global for the duration on the stack");
161
56.8M
  MOZ_ASSERT(!mGlobalObject ||
162
56.8M
             JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
163
56.8M
             "No outer windows allowed");
164
56.8M
}
165
166
ScriptSettingsStackEntry::~ScriptSettingsStackEntry()
167
56.8M
{
168
56.8M
  // We must have an actual JS global for the entire time this is on the stack.
169
56.8M
  MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
170
56.8M
}
171
172
// If the entry or incumbent global ends up being something that the subject
173
// principal doesn't subsume, we don't want to use it. This never happens on
174
// the web, but can happen with asymmetric privilege relationships (i.e.
175
// ExpandedPrincipal and System Principal).
176
//
177
// The most correct thing to use instead would be the topmost global on the
178
// callstack whose principal is subsumed by the subject principal. But that's
179
// hard to compute, so we just substitute the global of the current
180
// compartment. In practice, this is fine.
181
//
182
// Note that in particular things like:
183
//
184
// |SpecialPowers.wrap(crossOriginWindow).eval(open())|
185
//
186
// trigger this case. Although both the entry global and the current global
187
// have normal principals, the use of Gecko-specific System-Principaled JS
188
// puts the code from two different origins on the callstack at once, which
189
// doesn't happen normally on the web.
190
static nsIGlobalObject*
191
ClampToSubject(nsIGlobalObject* aGlobalOrNull)
192
0
{
193
0
  if (!aGlobalOrNull || !NS_IsMainThread()) {
194
0
    return aGlobalOrNull;
195
0
  }
196
0
197
0
  nsIPrincipal* globalPrin = aGlobalOrNull->PrincipalOrNull();
198
0
  NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal());
199
0
  if (!nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->SubsumesConsideringDomain(globalPrin)) {
200
0
    return GetCurrentGlobal();
201
0
  }
202
0
203
0
  return aGlobalOrNull;
204
0
}
205
206
nsIGlobalObject*
207
GetEntryGlobal()
208
0
{
209
0
  return ClampToSubject(ScriptSettingsStack::EntryGlobal());
210
0
}
211
212
nsIDocument*
213
GetEntryDocument()
214
0
{
215
0
  nsIGlobalObject* global = GetEntryGlobal();
216
0
  nsCOMPtr<nsPIDOMWindowInner> entryWin = do_QueryInterface(global);
217
0
218
0
  return entryWin ? entryWin->GetExtantDoc() : nullptr;
219
0
}
220
221
nsIGlobalObject*
222
GetIncumbentGlobal()
223
0
{
224
0
  // We need the current JSContext in order to check the JS for
225
0
  // scripted frames that may have appeared since anyone last
226
0
  // manipulated the stack. If it's null, that means that there
227
0
  // must be no entry global on the stack, and therefore no incumbent
228
0
  // global either.
229
0
  JSContext* cx = nsContentUtils::GetCurrentJSContext();
230
0
  if (!cx) {
231
0
    MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
232
0
    return nullptr;
233
0
  }
234
0
235
0
  // See what the JS engine has to say. If we've got a scripted caller
236
0
  // override in place, the JS engine will lie to us and pretend that
237
0
  // there's nothing on the JS stack, which will cause us to check the
238
0
  // incumbent script stack below.
239
0
  if (JSObject* global = JS::GetScriptedCallerGlobal(cx)) {
240
0
    return ClampToSubject(xpc::NativeGlobal(global));
241
0
  }
242
0
243
0
  // Ok, nothing from the JS engine. Let's use whatever's on the
244
0
  // explicit stack.
245
0
  return ClampToSubject(ScriptSettingsStack::IncumbentGlobal());
246
0
}
247
248
nsIGlobalObject*
249
GetCurrentGlobal()
250
0
{
251
0
  JSContext* cx = nsContentUtils::GetCurrentJSContext();
252
0
  if (!cx) {
253
0
    return nullptr;
254
0
  }
255
0
256
0
  JSObject* global = JS::CurrentGlobalOrNull(cx);
257
0
  if (!global) {
258
0
    return nullptr;
259
0
  }
260
0
261
0
  return xpc::NativeGlobal(global);
262
0
}
263
264
nsIPrincipal*
265
GetWebIDLCallerPrincipal()
266
0
{
267
0
  MOZ_ASSERT(NS_IsMainThread());
268
0
  ScriptSettingsStackEntry* entry = ScriptSettingsStack::EntryPoint();
269
0
270
0
  // If we have an entry point that is not NoJSAPI, we know it must be an
271
0
  // AutoEntryScript.
272
0
  if (!entry || entry->NoJSAPI()) {
273
0
    return nullptr;
274
0
  }
275
0
  AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry);
276
0
277
0
  return aes->mWebIDLCallerPrincipal;
278
0
}
279
280
bool
281
IsJSAPIActive()
282
60.0M
{
283
60.0M
  ScriptSettingsStackEntry* topEntry = ScriptSettingsStack::Top();
284
60.0M
  return topEntry && !topEntry->NoJSAPI();
285
60.0M
}
286
287
namespace danger {
288
JSContext*
289
GetJSContext()
290
61.7M
{
291
61.7M
  return CycleCollectedJSContext::Get()->Context();
292
61.7M
}
293
} // namespace danger
294
295
JS::RootingContext*
296
RootingCx()
297
3.24M
{
298
3.24M
  return CycleCollectedJSContext::Get()->RootingCx();
299
3.24M
}
300
301
AutoJSAPI::AutoJSAPI()
302
  : ScriptSettingsStackEntry(nullptr, eJSAPI)
303
  , mCx(nullptr)
304
  , mIsMainThread(false) // For lack of anything better
305
55.2M
{
306
55.2M
}
307
308
AutoJSAPI::~AutoJSAPI()
309
56.8M
{
310
56.8M
  if (!mCx) {
311
55.2M
    // No need to do anything here: we never managed to Init, so can't have an
312
55.2M
    // exception on our (nonexistent) JSContext.  We also don't need to restore
313
55.2M
    // any state on it.  Finally, we never made it to pushing outselves onto the
314
55.2M
    // ScriptSettingsStack, so shouldn't pop.
315
55.2M
    MOZ_ASSERT(ScriptSettingsStack::Top() != this);
316
55.2M
    return;
317
55.2M
  }
318
1.62M
319
1.62M
  ReportException();
320
1.62M
321
1.62M
  if (mOldWarningReporter.isSome()) {
322
1.62M
    JS::SetWarningReporter(cx(), mOldWarningReporter.value());
323
1.62M
  }
324
1.62M
325
1.62M
  ScriptSettingsStack::Pop(this);
326
1.62M
}
327
328
void
329
WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep);
330
331
void
332
AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
333
                        JSContext* aCx, bool aIsMainThread)
334
1.62M
{
335
1.62M
  MOZ_ASSERT(aCx);
336
1.62M
  MOZ_ASSERT(aCx == danger::GetJSContext());
337
1.62M
  MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
338
1.62M
  MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal));
339
1.62M
  MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal);
340
#ifdef DEBUG
341
  bool haveException = JS_IsExceptionPending(aCx);
342
#endif // DEBUG
343
344
1.62M
  mCx = aCx;
345
1.62M
  mIsMainThread = aIsMainThread;
346
1.62M
  mGlobalObject = aGlobalObject;
347
1.62M
  if (aGlobal) {
348
1.62M
    JS::ExposeObjectToActiveJS(aGlobal);
349
1.62M
  }
350
1.62M
  mAutoNullableRealm.emplace(mCx, aGlobal);
351
1.62M
352
1.62M
  ScriptSettingsStack::Push(this);
353
1.62M
354
1.62M
  mOldWarningReporter.emplace(JS::GetWarningReporter(aCx));
355
1.62M
356
1.62M
  JS::SetWarningReporter(aCx, WarningOnlyErrorReporter);
357
1.62M
358
#ifdef DEBUG
359
  if (haveException) {
360
    JS::Rooted<JS::Value> exn(aCx);
361
    JS_GetPendingException(aCx, &exn);
362
363
    JS_ClearPendingException(aCx);
364
    if (exn.isObject()) {
365
      JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject());
366
367
      // Make sure we can actually read things from it.  This UncheckedUwrap is
368
      // safe because we're only getting data for a debug printf.  In
369
      // particular, we do not expose this data to anyone, which is very
370
      // important; otherwise it could be a cross-origin information leak.
371
      exnObj = js::UncheckedUnwrap(exnObj);
372
      JSAutoRealm ar(aCx, exnObj);
373
374
      nsAutoJSString stack, filename, name, message;
375
      int32_t line;
376
377
      JS::Rooted<JS::Value> tmp(aCx);
378
      if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) {
379
        JS_ClearPendingException(aCx);
380
      }
381
      if (tmp.isUndefined()) {
382
        if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) {
383
          JS_ClearPendingException(aCx);
384
        }
385
      }
386
387
      if (!filename.init(aCx, tmp)) {
388
        JS_ClearPendingException(aCx);
389
      }
390
391
      if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) ||
392
          !stack.init(aCx, tmp)) {
393
        JS_ClearPendingException(aCx);
394
      }
395
396
      if (!JS_GetProperty(aCx, exnObj, "name", &tmp) ||
397
          !name.init(aCx, tmp)) {
398
        JS_ClearPendingException(aCx);
399
      }
400
401
      if (!JS_GetProperty(aCx, exnObj, "message", &tmp) ||
402
          !message.init(aCx, tmp)) {
403
        JS_ClearPendingException(aCx);
404
      }
405
406
      if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) ||
407
          !JS::ToInt32(aCx, tmp, &line)) {
408
        JS_ClearPendingException(aCx);
409
        line = 0;
410
      }
411
412
      printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n",
413
                    NS_ConvertUTF16toUTF8(name).get(),
414
                    NS_ConvertUTF16toUTF8(message).get(),
415
                    NS_ConvertUTF16toUTF8(filename).get(), line,
416
                    NS_ConvertUTF16toUTF8(stack).get());
417
    } else {
418
      // It's a primitive... not much we can do other than stringify it.
419
      nsAutoJSString exnStr;
420
      if (!exnStr.init(aCx, exn)) {
421
        JS_ClearPendingException(aCx);
422
      }
423
424
      printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n",
425
                    NS_ConvertUTF16toUTF8(exnStr).get());
426
    }
427
    MOZ_ASSERT(false, "We had an exception; we should not have");
428
  }
429
#endif // DEBUG
430
}
431
432
AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
433
                     bool aIsMainThread,
434
                     Type aType)
435
  : ScriptSettingsStackEntry(aGlobalObject, aType)
436
  , mIsMainThread(aIsMainThread)
437
1.62M
{
438
1.62M
  MOZ_ASSERT(aGlobalObject);
439
1.62M
  MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
440
1.62M
  MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
441
1.62M
442
1.62M
  InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(),
443
1.62M
               danger::GetJSContext(), aIsMainThread);
444
1.62M
}
445
446
void
447
AutoJSAPI::Init()
448
88
{
449
88
  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
450
88
451
88
  InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr,
452
88
               danger::GetJSContext(), NS_IsMainThread());
453
88
}
454
455
bool
456
AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx)
457
0
{
458
0
  MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
459
0
  MOZ_ASSERT(aCx);
460
0
461
0
  if (NS_WARN_IF(!aGlobalObject)) {
462
0
    return false;
463
0
  }
464
0
465
0
  JSObject* global = aGlobalObject->GetGlobalJSObject();
466
0
  if (NS_WARN_IF(!global)) {
467
0
    return false;
468
0
  }
469
0
470
0
  InitInternal(aGlobalObject, global, aCx, NS_IsMainThread());
471
0
  return true;
472
0
}
473
474
bool
475
AutoJSAPI::Init(nsIGlobalObject* aGlobalObject)
476
0
{
477
0
  return Init(aGlobalObject, danger::GetJSContext());
478
0
}
479
480
bool
481
AutoJSAPI::Init(JSObject* aObject)
482
0
{
483
0
  MOZ_ASSERT(!js::IsCrossCompartmentWrapper(aObject));
484
0
  return Init(xpc::NativeGlobal(aObject));
485
0
}
486
487
bool
488
AutoJSAPI::Init(nsPIDOMWindowInner* aWindow, JSContext* aCx)
489
0
{
490
0
  return Init(nsGlobalWindowInner::Cast(aWindow), aCx);
491
0
}
492
493
bool
494
AutoJSAPI::Init(nsPIDOMWindowInner* aWindow)
495
0
{
496
0
  return Init(nsGlobalWindowInner::Cast(aWindow));
497
0
}
498
499
bool
500
AutoJSAPI::Init(nsGlobalWindowInner* aWindow, JSContext* aCx)
501
0
{
502
0
  return Init(static_cast<nsIGlobalObject*>(aWindow), aCx);
503
0
}
504
505
bool
506
AutoJSAPI::Init(nsGlobalWindowInner* aWindow)
507
0
{
508
0
  return Init(static_cast<nsIGlobalObject*>(aWindow));
509
0
}
510
511
// Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
512
// reports to the JSErrorReporter as soon as they are generated. These go
513
// directly to the console, so we can handle them easily here.
514
//
515
// Eventually, SpiderMonkey will have a special-purpose callback for warnings
516
// only.
517
void
518
WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep)
519
0
{
520
0
  MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
521
0
  if (!NS_IsMainThread()) {
522
0
    // Reporting a warning on workers is a bit complicated because we have to
523
0
    // climb our parent chain until we get to the main thread.  So go ahead and
524
0
    // just go through the worker ReportError codepath here.
525
0
    //
526
0
    // That said, it feels like we should be able to short-circuit things a bit
527
0
    // here by posting an appropriate runnable to the main thread directly...
528
0
    // Worth looking into sometime.
529
0
    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
530
0
    MOZ_ASSERT(worker);
531
0
532
0
    worker->ReportError(aCx, JS::ConstUTF8CharsZ(), aRep);
533
0
    return;
534
0
  }
535
0
536
0
  RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
537
0
  nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(aCx);
538
0
  xpcReport->Init(aRep, nullptr, nsContentUtils::IsSystemCaller(aCx),
539
0
                  win ? win->AsInner()->WindowID() : 0);
540
0
  xpcReport->LogToConsole();
541
0
}
542
543
void
544
AutoJSAPI::ReportException()
545
1.62M
{
546
1.62M
  if (!HasException()) {
547
1.62M
    return;
548
1.62M
  }
549
0
550
0
  // AutoJSAPI uses a JSAutoNullableRealm, and may be in a null realm
551
0
  // when the destructor is called. However, the JS engine requires us
552
0
  // to be in a realm when we fetch the pending exception. In this case,
553
0
  // we enter the privileged junk scope and don't dispatch any error events.
554
0
  JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx()));
555
0
  if (!errorGlobal) {
556
0
    if (mIsMainThread) {
557
0
      errorGlobal = xpc::PrivilegedJunkScope();
558
0
    } else {
559
0
      errorGlobal = GetCurrentThreadWorkerGlobal();
560
0
    }
561
0
  }
562
0
  MOZ_ASSERT(JS_IsGlobalObject(errorGlobal));
563
0
  JSAutoRealm ar(cx(), errorGlobal);
564
0
  JS::Rooted<JS::Value> exn(cx());
565
0
  js::ErrorReport jsReport(cx());
566
0
  if (StealException(&exn) &&
567
0
      jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) {
568
0
    if (mIsMainThread) {
569
0
      RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
570
0
571
0
      RefPtr<nsGlobalWindowInner> win = xpc::WindowOrNull(errorGlobal);
572
0
      nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr;
573
0
      bool isChrome = nsContentUtils::IsSystemPrincipal(
574
0
        nsContentUtils::ObjectPrincipal(errorGlobal));
575
0
      xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
576
0
                      isChrome,
577
0
                      inner ? inner->WindowID() : 0);
578
0
      if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
579
0
        JS::RootingContext* rcx = JS::RootingContext::get(cx());
580
0
        DispatchScriptErrorEvent(inner, rcx, xpcReport, exn);
581
0
      } else {
582
0
        JS::Rooted<JSObject*> stack(cx());
583
0
        JS::Rooted<JSObject*> stackGlobal(cx());
584
0
        xpc::FindExceptionStackForConsoleReport(inner, exn, &stack, &stackGlobal);
585
0
        xpcReport->LogToConsoleWithStack(stack, stackGlobal);
586
0
      }
587
0
    } else {
588
0
      // On a worker, we just use the worker error reporting mechanism and don't
589
0
      // bother with xpc::ErrorReport.  This will ensure that all the right
590
0
      // events (which are a lot more complicated than in the window case) get
591
0
      // fired.
592
0
      WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
593
0
      MOZ_ASSERT(worker);
594
0
      MOZ_ASSERT(worker->GetJSContext() == cx());
595
0
      // Before invoking ReportError, put the exception back on the context,
596
0
      // because it may want to put it in its error events and has no other way
597
0
      // to get hold of it.  After we invoke ReportError, clear the exception on
598
0
      // cx(), just in case ReportError didn't.
599
0
      JS_SetPendingException(cx(), exn);
600
0
      worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report());
601
0
      ClearException();
602
0
    }
603
0
  } else {
604
0
    NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
605
0
    ClearException();
606
0
  }
607
0
}
608
609
bool
610
AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
611
0
{
612
0
  MOZ_ASSERT_IF(mIsMainThread, IsStackTop());
613
0
  MOZ_ASSERT(HasException());
614
0
  MOZ_ASSERT(js::GetContextRealm(cx()));
615
0
  if (!JS_GetPendingException(cx(), aVal)) {
616
0
    return false;
617
0
  }
618
0
  return true;
619
0
}
620
621
bool
622
AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
623
0
{
624
0
  if (!PeekException(aVal)) {
625
0
    return false;
626
0
  }
627
0
  JS_ClearPendingException(cx());
628
0
  return true;
629
0
}
630
631
#ifdef DEBUG
632
bool
633
AutoJSAPI::IsStackTop() const
634
{
635
  return ScriptSettingsStack::TopNonIncumbentScript() == this;
636
}
637
#endif // DEBUG
638
639
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
640
                                 const char* aReason,
641
                                 bool aIsMainThread)
642
  : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript)
643
  , mWebIDLCallerPrincipal(nullptr)
644
  // This relies on us having a cx() because the AutoJSAPI constructor already
645
  // ran.
646
  , mCallerOverride(cx())
647
#ifdef MOZ_GECKO_PROFILER
648
  , mAutoProfilerLabel("AutoEntryScript", aReason, __LINE__,
649
                       js::ProfilingStackFrame::Category::JS)
650
#endif
651
1.62M
{
652
1.62M
  MOZ_ASSERT(aGlobalObject);
653
1.62M
654
1.62M
  if (aIsMainThread) {
655
1.62M
    if (gRunToCompletionListeners > 0) {
656
0
      mDocShellEntryMonitor.emplace(cx(), aReason);
657
0
    }
658
1.62M
    mScriptActivity.emplace(true);
659
1.62M
  }
660
1.62M
}
661
662
AutoEntryScript::AutoEntryScript(JSObject* aObject,
663
                                 const char* aReason,
664
                                 bool aIsMainThread)
665
  : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread)
666
8
{
667
8
  // xpc::NativeGlobal uses JS::GetNonCCWObjectGlobal, which asserts that
668
8
  // aObject is not a CCW.
669
8
}
670
671
AutoEntryScript::~AutoEntryScript()
672
1.62M
{
673
1.62M
}
674
675
AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
676
                                                            const char* aReason)
677
  : JS::dbg::AutoEntryMonitor(aCx)
678
  , mReason(aReason)
679
0
{
680
0
}
681
682
void
683
AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction,
684
                                             JSScript* aScript, JS::Handle<JS::Value> aAsyncStack,
685
                                             const char* aAsyncCause)
686
0
{
687
0
  JS::Rooted<JSFunction*> rootedFunction(aCx);
688
0
  if (aFunction) {
689
0
    rootedFunction = aFunction;
690
0
  }
691
0
  JS::Rooted<JSScript*> rootedScript(aCx);
692
0
  if (aScript) {
693
0
    rootedScript = aScript;
694
0
  }
695
0
696
0
  nsCOMPtr<nsPIDOMWindowInner> window = xpc::CurrentWindowOrNull(aCx);
697
0
  if (!window || !window->GetDocShell() ||
698
0
      !window->GetDocShell()->GetRecordProfileTimelineMarkers()) {
699
0
    return;
700
0
  }
701
0
702
0
  nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
703
0
  nsString filename;
704
0
  uint32_t lineNumber = 0;
705
0
706
0
  JS::AutoStableStringChars functionName(aCx);
707
0
  if (rootedFunction) {
708
0
    JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction));
709
0
    if (displayId) {
710
0
      if (!functionName.initTwoByte(aCx, displayId)) {
711
0
        JS_ClearPendingException(aCx);
712
0
        return;
713
0
      }
714
0
    }
715
0
  }
716
0
717
0
  if (!rootedScript) {
718
0
    rootedScript = JS_GetFunctionScript(aCx, rootedFunction);
719
0
  }
720
0
  if (rootedScript) {
721
0
    filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript));
722
0
    lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript);
723
0
  }
724
0
725
0
  if (!filename.IsEmpty() || functionName.isTwoByte()) {
726
0
    const char16_t* functionNameChars = functionName.isTwoByte() ?
727
0
      functionName.twoByteChars() : nullptr;
728
0
729
0
    docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason,
730
0
                                                               functionNameChars,
731
0
                                                               filename.BeginReading(),
732
0
                                                               lineNumber, aAsyncStack,
733
0
                                                               aAsyncCause);
734
0
  }
735
0
}
736
737
void
738
AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx)
739
0
{
740
0
  nsCOMPtr<nsPIDOMWindowInner> window = xpc::CurrentWindowOrNull(aCx);
741
0
  // Not really worth checking GetRecordProfileTimelineMarkers here.
742
0
  if (window && window->GetDocShell()) {
743
0
    nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
744
0
    docShellForJSRunToCompletion->NotifyJSRunToCompletionStop();
745
0
  }
746
0
}
747
748
AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
749
  : ScriptSettingsStackEntry(aGlobalObject, eIncumbentScript)
750
  , mCallerOverride(nsContentUtils::GetCurrentJSContext())
751
0
{
752
0
  ScriptSettingsStack::Push(this);
753
0
}
754
755
AutoIncumbentScript::~AutoIncumbentScript()
756
0
{
757
0
  ScriptSettingsStack::Pop(this);
758
0
}
759
760
AutoNoJSAPI::AutoNoJSAPI()
761
  : ScriptSettingsStackEntry(nullptr, eNoJSAPI)
762
0
{
763
0
  ScriptSettingsStack::Push(this);
764
0
}
765
766
AutoNoJSAPI::~AutoNoJSAPI()
767
0
{
768
0
  ScriptSettingsStack::Pop(this);
769
0
}
770
771
} // namespace dom
772
773
AutoJSContext::AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
774
  : mCx(nullptr)
775
55.2M
{
776
55.2M
  JS::AutoSuppressGCAnalysis nogc;
777
55.2M
  MOZ_ASSERT(!mCx, "mCx should not be initialized!");
778
55.2M
  MOZ_ASSERT(NS_IsMainThread());
779
55.2M
780
55.2M
  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
781
55.2M
782
55.2M
  if (dom::IsJSAPIActive()) {
783
55.2M
    mCx = dom::danger::GetJSContext();
784
55.2M
  } else {
785
0
    mJSAPI.Init();
786
0
    mCx = mJSAPI.cx();
787
0
  }
788
55.2M
}
789
790
AutoJSContext::operator JSContext*() const
791
81.1M
{
792
81.1M
  return mCx;
793
81.1M
}
794
795
AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
796
  : AutoJSAPI()
797
0
{
798
0
  MOZ_ASSERT(NS_IsMainThread());
799
0
800
0
  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
801
0
802
0
  DebugOnly<bool> ok = Init(xpc::UnprivilegedJunkScope());
803
0
  MOZ_ASSERT(ok,
804
0
             "This is quite odd.  We should have crashed in the "
805
0
             "xpc::NativeGlobal() call if xpc::UnprivilegedJunkScope() "
806
0
             "returned null, and inited correctly otherwise!");
807
0
}
808
809
AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
810
  : mIsMainThread(NS_IsMainThread())
811
0
{
812
0
  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
813
0
  if (mIsMainThread) {
814
0
    mScriptActivity.emplace(true);
815
0
  }
816
0
}
817
818
void
819
AutoSlowOperation::CheckForInterrupt()
820
0
{
821
0
  // For now we support only main thread!
822
0
  if (mIsMainThread) {
823
0
    // JS_CheckForInterrupt expects us to be in a realm.
824
0
    AutoJSAPI jsapi;
825
0
    if (jsapi.Init(xpc::UnprivilegedJunkScope())) {
826
0
      JS_CheckForInterrupt(jsapi.cx());
827
0
    }
828
0
  }
829
0
}
830
831
} // namespace mozilla