Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/bindings/Exceptions.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/Exceptions.h"
8
9
#include "js/RootingAPI.h"
10
#include "js/TypeDecls.h"
11
#include "jsapi.h"
12
#include "js/SavedFrameAPI.h"
13
#include "mozilla/CycleCollectedJSContext.h"
14
#include "mozilla/dom/BindingUtils.h"
15
#include "mozilla/dom/DOMException.h"
16
#include "mozilla/dom/ScriptSettings.h"
17
#include "nsPIDOMWindow.h"
18
#include "nsServiceManagerUtils.h"
19
#include "nsThreadUtils.h"
20
#include "XPCWrapper.h"
21
#include "WorkerPrivate.h"
22
#include "nsContentUtils.h"
23
24
namespace mozilla {
25
namespace dom {
26
27
// Throw the given exception value if it's safe.  If it's not safe, then
28
// synthesize and throw a new exception value for NS_ERROR_UNEXPECTED.  The
29
// incoming value must be in the compartment of aCx.  This function guarantees
30
// that an exception is pending on aCx when it returns.
31
static void
32
ThrowExceptionValueIfSafe(JSContext* aCx, JS::Handle<JS::Value> exnVal,
33
                          Exception* aOriginalException)
34
0
{
35
0
  MOZ_ASSERT(aOriginalException);
36
0
37
0
  if (!exnVal.isObject()) {
38
0
    JS_SetPendingException(aCx, exnVal);
39
0
    return;
40
0
  }
41
0
42
0
  JS::Rooted<JSObject*> exnObj(aCx, &exnVal.toObject());
43
0
  MOZ_ASSERT(js::IsObjectInContextCompartment(exnObj, aCx),
44
0
             "exnObj needs to be in the right compartment for the "
45
0
             "CheckedUnwrap thing to make sense");
46
0
47
0
  if (js::CheckedUnwrap(exnObj)) {
48
0
    // This is an object we're allowed to work with, so just go ahead and throw
49
0
    // it.
50
0
    JS_SetPendingException(aCx, exnVal);
51
0
    return;
52
0
  }
53
0
54
0
  // We could probably Throw(aCx, NS_ERROR_UNEXPECTED) here, and it would do the
55
0
  // right thing due to there not being an existing exception on the runtime at
56
0
  // this point, but it's clearer to explicitly do the thing we want done.  This
57
0
  // is also why we don't just call ThrowExceptionObject on the Exception we
58
0
  // create: it would do the right thing, but that fact is not obvious.
59
0
  RefPtr<Exception> syntheticException =
60
0
    CreateException(NS_ERROR_UNEXPECTED);
61
0
  JS::Rooted<JS::Value> syntheticVal(aCx);
62
0
  if (!GetOrCreateDOMReflector(aCx, syntheticException, &syntheticVal)) {
63
0
    return;
64
0
  }
65
0
  MOZ_ASSERT(syntheticVal.isObject() &&
66
0
             !js::IsWrapper(&syntheticVal.toObject()),
67
0
             "Must have a reflector here, not a wrapper");
68
0
  JS_SetPendingException(aCx, syntheticVal);
69
0
}
70
71
void
72
ThrowExceptionObject(JSContext* aCx, Exception* aException)
73
0
{
74
0
  JS::Rooted<JS::Value> thrown(aCx);
75
0
76
0
  // If we stored the original thrown JS value in the exception
77
0
  // (see XPCConvert::ConstructException) and we are in a web context
78
0
  // (i.e., not chrome), rethrow the original value. This only applies to JS
79
0
  // implemented components so we only need to check for this on the main
80
0
  // thread.
81
0
  if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() &&
82
0
      aException->StealJSVal(thrown.address())) {
83
0
    // Now check for the case when thrown is a number which matches
84
0
    // aException->GetResult().  This would indicate that what actually got
85
0
    // thrown was an nsresult value.  In that situation, we should go back
86
0
    // through dom::Throw with that nsresult value, because it will make sure to
87
0
    // create the right sort of Exception or DOMException, with the right
88
0
    // global.
89
0
    if (thrown.isNumber()) {
90
0
      nsresult exceptionResult = aException->GetResult();
91
0
      if (double(exceptionResult) == thrown.toNumber()) {
92
0
        Throw(aCx, exceptionResult);
93
0
        return;
94
0
      }
95
0
    }
96
0
    if (!JS_WrapValue(aCx, &thrown)) {
97
0
      return;
98
0
    }
99
0
    ThrowExceptionValueIfSafe(aCx, thrown, aException);
100
0
    return;
101
0
  }
102
0
103
0
  if (!GetOrCreateDOMReflector(aCx, aException, &thrown)) {
104
0
    return;
105
0
  }
106
0
107
0
  ThrowExceptionValueIfSafe(aCx, thrown, aException);
108
0
}
109
110
bool
111
Throw(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
112
0
{
113
0
  if (aRv == NS_ERROR_UNCATCHABLE_EXCEPTION) {
114
0
    // Nuke any existing exception on aCx, to make sure we're uncatchable.
115
0
    JS_ClearPendingException(aCx);
116
0
    return false;
117
0
  }
118
0
119
0
  if (JS_IsExceptionPending(aCx)) {
120
0
    // Don't clobber the existing exception.
121
0
    return false;
122
0
  }
123
0
124
0
  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
125
0
  RefPtr<Exception> existingException = context->GetPendingException();
126
0
  // Make sure to clear the pending exception now.  Either we're going to reuse
127
0
  // it (and we already grabbed it), or we plan to throw something else and this
128
0
  // pending exception is no longer relevant.
129
0
  context->SetPendingException(nullptr);
130
0
131
0
  // Ignore the pending exception if we have a non-default message passed in.
132
0
  if (aMessage.IsEmpty() && existingException) {
133
0
    if (aRv == existingException->GetResult()) {
134
0
      // Reuse the existing exception.
135
0
      ThrowExceptionObject(aCx, existingException);
136
0
      return false;
137
0
    }
138
0
  }
139
0
140
0
  RefPtr<Exception> finalException = CreateException(aRv, aMessage);
141
0
  MOZ_ASSERT(finalException);
142
0
143
0
  ThrowExceptionObject(aCx, finalException);
144
0
  return false;
145
0
}
146
147
void
148
ThrowAndReport(nsPIDOMWindowInner* aWindow, nsresult aRv)
149
0
{
150
0
  MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
151
0
             "Doesn't make sense to report uncatchable exceptions!");
152
0
  AutoJSAPI jsapi;
153
0
  if (NS_WARN_IF(!jsapi.Init(aWindow))) {
154
0
    return;
155
0
  }
156
0
157
0
  Throw(jsapi.cx(), aRv);
158
0
}
159
160
already_AddRefed<Exception>
161
CreateException(nsresult aRv, const nsACString& aMessage)
162
0
{
163
0
  // Do we use DOM exceptions for this error code?
164
0
  switch (NS_ERROR_GET_MODULE(aRv)) {
165
0
  case NS_ERROR_MODULE_DOM:
166
0
  case NS_ERROR_MODULE_SVG:
167
0
  case NS_ERROR_MODULE_DOM_XPATH:
168
0
  case NS_ERROR_MODULE_DOM_INDEXEDDB:
169
0
  case NS_ERROR_MODULE_DOM_FILEHANDLE:
170
0
  case NS_ERROR_MODULE_DOM_ANIM:
171
0
  case NS_ERROR_MODULE_DOM_PUSH:
172
0
  case NS_ERROR_MODULE_DOM_MEDIA:
173
0
    if (aMessage.IsEmpty()) {
174
0
      return DOMException::Create(aRv);
175
0
    }
176
0
    return DOMException::Create(aRv, aMessage);
177
0
  default:
178
0
    break;
179
0
  }
180
0
181
0
  // If not, use the default.
182
0
  RefPtr<Exception> exception =
183
0
    new Exception(aMessage, aRv, EmptyCString(), nullptr, nullptr);
184
0
  return exception.forget();
185
0
}
186
187
already_AddRefed<nsIStackFrame>
188
GetCurrentJSStack(int32_t aMaxDepth)
189
0
{
190
0
  // is there a current context available?
191
0
  JSContext* cx = nsContentUtils::GetCurrentJSContext();
192
0
193
0
  if (!cx || !js::GetContextRealm(cx)) {
194
0
    return nullptr;
195
0
  }
196
0
197
0
  static const unsigned MAX_FRAMES = 100;
198
0
  if (aMaxDepth < 0) {
199
0
    aMaxDepth = MAX_FRAMES;
200
0
  }
201
0
202
0
  JS::StackCapture captureMode = aMaxDepth == 0
203
0
    ? JS::StackCapture(JS::AllFrames())
204
0
    : JS::StackCapture(JS::MaxFrames(aMaxDepth));
205
0
206
0
  return dom::exceptions::CreateStack(cx, std::move(captureMode));
207
0
}
208
209
namespace exceptions {
210
211
class JSStackFrame : public nsIStackFrame
212
{
213
public:
214
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
215
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(JSStackFrame)
216
  NS_DECL_NSISTACKFRAME
217
218
  // aStack must not be null.
219
  explicit JSStackFrame(JS::Handle<JSObject*> aStack);
220
221
private:
222
  virtual ~JSStackFrame();
223
224
  JS::Heap<JSObject*> mStack;
225
  nsString mFormattedStack;
226
227
  nsCOMPtr<nsIStackFrame> mCaller;
228
  nsCOMPtr<nsIStackFrame> mAsyncCaller;
229
  nsString mFilename;
230
  nsString mFunname;
231
  nsString mAsyncCause;
232
  int32_t mLineno;
233
  int32_t mColNo;
234
235
  bool mFilenameInitialized;
236
  bool mFunnameInitialized;
237
  bool mLinenoInitialized;
238
  bool mColNoInitialized;
239
  bool mAsyncCauseInitialized;
240
  bool mAsyncCallerInitialized;
241
  bool mCallerInitialized;
242
  bool mFormattedStackInitialized;
243
};
244
245
JSStackFrame::JSStackFrame(JS::Handle<JSObject*> aStack)
246
  : mStack(aStack)
247
  , mLineno(0)
248
  , mColNo(0)
249
  , mFilenameInitialized(false)
250
  , mFunnameInitialized(false)
251
  , mLinenoInitialized(false)
252
  , mColNoInitialized(false)
253
  , mAsyncCauseInitialized(false)
254
  , mAsyncCallerInitialized(false)
255
  , mCallerInitialized(false)
256
  , mFormattedStackInitialized(false)
257
0
{
258
0
  MOZ_ASSERT(mStack);
259
0
  MOZ_ASSERT(JS::IsUnwrappedSavedFrame(mStack));
260
0
261
0
  mozilla::HoldJSObjects(this);
262
0
}
263
264
JSStackFrame::~JSStackFrame()
265
0
{
266
0
  mozilla::DropJSObjects(this);
267
0
}
268
269
NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame)
270
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSStackFrame)
271
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCaller)
272
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAsyncCaller)
273
0
  tmp->mStack = nullptr;
274
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
275
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSStackFrame)
276
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCaller)
277
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAsyncCaller)
278
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
279
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSStackFrame)
280
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
281
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
282
283
NS_IMPL_CYCLE_COLLECTING_ADDREF(JSStackFrame)
284
NS_IMPL_CYCLE_COLLECTING_RELEASE(JSStackFrame)
285
286
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSStackFrame)
287
0
  NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
288
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
289
0
NS_INTERFACE_MAP_END
290
291
// Helper method to determine the JSPrincipals* to pass to JS SavedFrame APIs.
292
//
293
// @argument aStack the stack we're working with; must be non-null.
294
// @argument [out] aCanCache whether we can use cached JSStackFrame values.
295
static JSPrincipals*
296
GetPrincipalsForStackGetter(JSContext* aCx, JS::Handle<JSObject*> aStack,
297
                            bool* aCanCache)
298
0
{
299
0
  MOZ_ASSERT(JS::IsUnwrappedSavedFrame(aStack));
300
0
301
0
  JSPrincipals* currentPrincipals =
302
0
    JS::GetRealmPrincipals(js::GetContextRealm(aCx));
303
0
  JSPrincipals* stackPrincipals =
304
0
    JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aStack));
305
0
306
0
  // Fast path for when the principals are equal. This check is also necessary
307
0
  // for workers: no nsIPrincipal there so we can't use the code below.
308
0
  if (currentPrincipals == stackPrincipals) {
309
0
    *aCanCache = true;
310
0
    return stackPrincipals;
311
0
  }
312
0
313
0
  MOZ_ASSERT(NS_IsMainThread());
314
0
315
0
  if (nsJSPrincipals::get(currentPrincipals)->Subsumes(
316
0
        nsJSPrincipals::get(stackPrincipals))) {
317
0
    // The current principals subsume the stack's principals. In this case use
318
0
    // the stack's principals: the idea is that this way devtools code that's
319
0
    // asking an exception object for a stack to display will end up with the
320
0
    // stack the web developer would see via doing .stack in a web page, with
321
0
    // Firefox implementation details excluded.
322
0
323
0
    // Because we use the stack's principals and don't rely on the current
324
0
    // context realm, we can use cached values.
325
0
    *aCanCache = true;
326
0
    return stackPrincipals;
327
0
  }
328
0
329
0
  // The stack was captured in more-privileged code, so use the less privileged
330
0
  // principals. Don't use cached values because we don't want these values to
331
0
  // depend on the current realm/principals.
332
0
  *aCanCache = false;
333
0
  return currentPrincipals;
334
0
}
335
336
// Helper method to get the value of a stack property, if it's not already
337
// cached.  This will make sure we skip the cache if the property value depends
338
// on the (current) context's realm/principals.
339
//
340
// @argument aStack the stack we're working with; must be non-null.
341
// @argument aPropGetter the getter function to call.
342
// @argument aIsCached whether we've cached this property's value before.
343
//
344
// @argument [out] aCanCache whether the value can get cached.
345
// @argument [out] aUseCachedValue if true, just use the cached value.
346
// @argument [out] aValue the value we got from the stack.
347
template<typename ReturnType, typename GetterOutParamType>
348
static void
349
GetValueIfNotCached(JSContext* aCx, const JS::Heap<JSObject*>& aStack,
350
                    JS::SavedFrameResult (*aPropGetter)(JSContext*,
351
                                                        JSPrincipals*,
352
                                                        JS::Handle<JSObject*>,
353
                                                        GetterOutParamType,
354
                                                        JS::SavedFrameSelfHosted),
355
                    bool aIsCached, bool* aCanCache, bool* aUseCachedValue,
356
                    ReturnType aValue)
357
0
{
358
0
  MOZ_ASSERT(aStack);
359
0
  MOZ_ASSERT(JS::IsUnwrappedSavedFrame(aStack));
360
0
361
0
  JS::Rooted<JSObject*> stack(aCx, aStack);
362
0
363
0
  JSPrincipals* principals = GetPrincipalsForStackGetter(aCx, stack, aCanCache);
364
0
  if (*aCanCache && aIsCached) {
365
0
    *aUseCachedValue = true;
366
0
    return;
367
0
  }
368
0
369
0
  *aUseCachedValue = false;
370
0
371
0
  aPropGetter(aCx, principals, stack, aValue,
372
0
              JS::SavedFrameSelfHosted::Exclude);
373
0
}
Unexecuted instantiation: Unified_cpp_dom_bindings0.cpp:void mozilla::dom::exceptions::GetValueIfNotCached<JS::Rooted<JSString*>*, JS::MutableHandle<JSString*> >(JSContext*, JS::Heap<JSObject*> const&, JS::SavedFrameResult (*)(JSContext*, JSPrincipals*, JS::Handle<JSObject*>, JS::MutableHandle<JSString*>, JS::SavedFrameSelfHosted), bool, bool*, bool*, JS::Rooted<JSString*>*)
Unexecuted instantiation: Unified_cpp_dom_bindings0.cpp:void mozilla::dom::exceptions::GetValueIfNotCached<unsigned int*, unsigned int*>(JSContext*, JS::Heap<JSObject*> const&, JS::SavedFrameResult (*)(JSContext*, JSPrincipals*, JS::Handle<JSObject*>, unsigned int*, JS::SavedFrameSelfHosted), bool, bool*, bool*, unsigned int*)
Unexecuted instantiation: Unified_cpp_dom_bindings0.cpp:void mozilla::dom::exceptions::GetValueIfNotCached<JS::Rooted<JSObject*>*, JS::MutableHandle<JSObject*> >(JSContext*, JS::Heap<JSObject*> const&, JS::SavedFrameResult (*)(JSContext*, JSPrincipals*, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>, JS::SavedFrameSelfHosted), bool, bool*, bool*, JS::Rooted<JSObject*>*)
374
375
NS_IMETHODIMP JSStackFrame::GetFilenameXPCOM(JSContext* aCx, nsAString& aFilename)
376
0
{
377
0
  GetFilename(aCx, aFilename);
378
0
  return NS_OK;
379
0
}
380
381
void
382
JSStackFrame::GetFilename(JSContext* aCx, nsAString& aFilename)
383
0
{
384
0
  if (!mStack) {
385
0
    aFilename.Truncate();
386
0
    return;
387
0
  }
388
0
389
0
  JS::Rooted<JSString*> filename(aCx);
390
0
  bool canCache = false, useCachedValue = false;
391
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameSource,
392
0
                      mFilenameInitialized,
393
0
                      &canCache, &useCachedValue, &filename);
394
0
  if (useCachedValue) {
395
0
    aFilename = mFilename;
396
0
    return;
397
0
  }
398
0
399
0
  nsAutoJSString str;
400
0
  if (!str.init(aCx, filename)) {
401
0
    JS_ClearPendingException(aCx);
402
0
    aFilename.Truncate();
403
0
    return;
404
0
  }
405
0
  aFilename = str;
406
0
407
0
  if (canCache) {
408
0
    mFilename = str;
409
0
    mFilenameInitialized = true;
410
0
  }
411
0
}
412
413
NS_IMETHODIMP
414
JSStackFrame::GetNameXPCOM(JSContext* aCx, nsAString& aFunction)
415
0
{
416
0
  GetName(aCx, aFunction);
417
0
  return NS_OK;
418
0
}
419
420
void
421
JSStackFrame::GetName(JSContext* aCx, nsAString& aFunction)
422
0
{
423
0
  if (!mStack) {
424
0
    aFunction.Truncate();
425
0
    return;
426
0
  }
427
0
428
0
  JS::Rooted<JSString*> name(aCx);
429
0
  bool canCache = false, useCachedValue = false;
430
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameFunctionDisplayName,
431
0
                      mFunnameInitialized, &canCache, &useCachedValue,
432
0
                      &name);
433
0
434
0
  if (useCachedValue) {
435
0
    aFunction = mFunname;
436
0
    return;
437
0
  }
438
0
439
0
  if (name) {
440
0
    nsAutoJSString str;
441
0
    if (!str.init(aCx, name)) {
442
0
      JS_ClearPendingException(aCx);
443
0
      aFunction.Truncate();
444
0
      return;
445
0
    }
446
0
    aFunction = str;
447
0
  } else {
448
0
    aFunction.SetIsVoid(true);
449
0
  }
450
0
451
0
  if (canCache) {
452
0
    mFunname = aFunction;
453
0
    mFunnameInitialized = true;
454
0
  }
455
0
}
456
457
int32_t
458
JSStackFrame::GetLineNumber(JSContext* aCx)
459
0
{
460
0
  if (!mStack) {
461
0
    return 0;
462
0
  }
463
0
464
0
  uint32_t line;
465
0
  bool canCache = false, useCachedValue = false;
466
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameLine, mLinenoInitialized,
467
0
                      &canCache, &useCachedValue, &line);
468
0
469
0
  if (useCachedValue) {
470
0
    return mLineno;
471
0
  }
472
0
473
0
  if (canCache) {
474
0
    mLineno = line;
475
0
    mLinenoInitialized = true;
476
0
  }
477
0
478
0
  return line;
479
0
}
480
481
NS_IMETHODIMP
482
JSStackFrame::GetLineNumberXPCOM(JSContext* aCx, int32_t* aLineNumber)
483
0
{
484
0
  *aLineNumber = GetLineNumber(aCx);
485
0
  return NS_OK;
486
0
}
487
488
int32_t
489
JSStackFrame::GetColumnNumber(JSContext* aCx)
490
0
{
491
0
  if (!mStack) {
492
0
    return 0;
493
0
  }
494
0
495
0
  uint32_t col;
496
0
  bool canCache = false, useCachedValue = false;
497
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameColumn, mColNoInitialized,
498
0
                      &canCache, &useCachedValue, &col);
499
0
500
0
  if (useCachedValue) {
501
0
    return mColNo;
502
0
  }
503
0
504
0
  if (canCache) {
505
0
    mColNo = col;
506
0
    mColNoInitialized = true;
507
0
  }
508
0
509
0
  return col;
510
0
}
511
512
NS_IMETHODIMP
513
JSStackFrame::GetColumnNumberXPCOM(JSContext* aCx,
514
                                   int32_t* aColumnNumber)
515
0
{
516
0
  *aColumnNumber = GetColumnNumber(aCx);
517
0
  return NS_OK;
518
0
}
519
520
NS_IMETHODIMP JSStackFrame::GetSourceLine(nsACString& aSourceLine)
521
0
{
522
0
  aSourceLine.Truncate();
523
0
  return NS_OK;
524
0
}
525
526
NS_IMETHODIMP
527
JSStackFrame::GetAsyncCauseXPCOM(JSContext* aCx,
528
                                 nsAString& aAsyncCause)
529
0
{
530
0
  GetAsyncCause(aCx, aAsyncCause);
531
0
  return NS_OK;
532
0
}
533
534
void
535
JSStackFrame::GetAsyncCause(JSContext* aCx,
536
                            nsAString& aAsyncCause)
537
0
{
538
0
  if (!mStack) {
539
0
    aAsyncCause.Truncate();
540
0
    return;
541
0
  }
542
0
543
0
  JS::Rooted<JSString*> asyncCause(aCx);
544
0
  bool canCache = false, useCachedValue = false;
545
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameAsyncCause,
546
0
                      mAsyncCauseInitialized, &canCache, &useCachedValue,
547
0
                      &asyncCause);
548
0
549
0
  if (useCachedValue) {
550
0
    aAsyncCause = mAsyncCause;
551
0
    return;
552
0
  }
553
0
554
0
  if (asyncCause) {
555
0
    nsAutoJSString str;
556
0
    if (!str.init(aCx, asyncCause)) {
557
0
      JS_ClearPendingException(aCx);
558
0
      aAsyncCause.Truncate();
559
0
      return;
560
0
    }
561
0
    aAsyncCause = str;
562
0
  } else {
563
0
    aAsyncCause.SetIsVoid(true);
564
0
  }
565
0
566
0
  if (canCache) {
567
0
    mAsyncCause = aAsyncCause;
568
0
    mAsyncCauseInitialized = true;
569
0
  }
570
0
}
571
572
NS_IMETHODIMP
573
JSStackFrame::GetAsyncCallerXPCOM(JSContext* aCx,
574
                                  nsIStackFrame** aAsyncCaller)
575
0
{
576
0
  *aAsyncCaller = GetAsyncCaller(aCx).take();
577
0
  return NS_OK;
578
0
}
579
580
already_AddRefed<nsIStackFrame>
581
JSStackFrame::GetAsyncCaller(JSContext* aCx)
582
0
{
583
0
  if (!mStack) {
584
0
    return nullptr;
585
0
  }
586
0
587
0
  JS::Rooted<JSObject*> asyncCallerObj(aCx);
588
0
  bool canCache = false, useCachedValue = false;
589
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameAsyncParent,
590
0
                      mAsyncCallerInitialized, &canCache, &useCachedValue,
591
0
                      &asyncCallerObj);
592
0
593
0
  if (useCachedValue) {
594
0
    nsCOMPtr<nsIStackFrame> asyncCaller = mAsyncCaller;
595
0
    return asyncCaller.forget();
596
0
  }
597
0
598
0
  nsCOMPtr<nsIStackFrame> asyncCaller =
599
0
    asyncCallerObj ? new JSStackFrame(asyncCallerObj) : nullptr;
600
0
601
0
  if (canCache) {
602
0
    mAsyncCaller = asyncCaller;
603
0
    mAsyncCallerInitialized = true;
604
0
  }
605
0
606
0
  return asyncCaller.forget();
607
0
}
608
609
NS_IMETHODIMP
610
JSStackFrame::GetCallerXPCOM(JSContext* aCx, nsIStackFrame** aCaller)
611
0
{
612
0
  *aCaller = GetCaller(aCx).take();
613
0
  return NS_OK;
614
0
}
615
616
already_AddRefed<nsIStackFrame>
617
JSStackFrame::GetCaller(JSContext* aCx)
618
0
{
619
0
  if (!mStack) {
620
0
    return nullptr;
621
0
  }
622
0
623
0
  JS::Rooted<JSObject*> callerObj(aCx);
624
0
  bool canCache = false, useCachedValue = false;
625
0
  GetValueIfNotCached(aCx, mStack, JS::GetSavedFrameParent, mCallerInitialized,
626
0
                      &canCache, &useCachedValue, &callerObj);
627
0
628
0
  if (useCachedValue) {
629
0
    nsCOMPtr<nsIStackFrame> caller = mCaller;
630
0
    return caller.forget();
631
0
  }
632
0
633
0
  nsCOMPtr<nsIStackFrame> caller =
634
0
    callerObj ? new JSStackFrame(callerObj) : nullptr;
635
0
636
0
  if (canCache) {
637
0
    mCaller = caller;
638
0
    mCallerInitialized = true;
639
0
  }
640
0
641
0
  return caller.forget();
642
0
}
643
644
NS_IMETHODIMP
645
JSStackFrame::GetFormattedStackXPCOM(JSContext* aCx, nsAString& aStack)
646
0
{
647
0
  GetFormattedStack(aCx, aStack);
648
0
  return NS_OK;
649
0
}
650
651
void
652
JSStackFrame::GetFormattedStack(JSContext* aCx, nsAString& aStack)
653
0
{
654
0
  if (!mStack) {
655
0
    aStack.Truncate();
656
0
    return;
657
0
  }
658
0
659
0
  // Sadly we can't use GetValueIfNotCached here, because our getter
660
0
  // returns bool, not JS::SavedFrameResult.  Maybe it's possible to
661
0
  // make the templates more complicated to deal, but in the meantime
662
0
  // let's just inline GetValueIfNotCached here.
663
0
664
0
  JS::Rooted<JSObject*> stack(aCx, mStack);
665
0
666
0
  bool canCache;
667
0
  JSPrincipals* principals = GetPrincipalsForStackGetter(aCx, stack, &canCache);
668
0
  if (canCache && mFormattedStackInitialized) {
669
0
    aStack = mFormattedStack;
670
0
    return;
671
0
  }
672
0
673
0
  JS::Rooted<JSString*> formattedStack(aCx);
674
0
  if (!JS::BuildStackString(aCx, principals, stack, &formattedStack)) {
675
0
    JS_ClearPendingException(aCx);
676
0
    aStack.Truncate();
677
0
    return;
678
0
  }
679
0
680
0
  nsAutoJSString str;
681
0
  if (!str.init(aCx, formattedStack)) {
682
0
    JS_ClearPendingException(aCx);
683
0
    aStack.Truncate();
684
0
    return;
685
0
  }
686
0
687
0
  aStack = str;
688
0
689
0
  if (canCache) {
690
0
    mFormattedStack = str;
691
0
    mFormattedStackInitialized = true;
692
0
  }
693
0
}
694
695
NS_IMETHODIMP JSStackFrame::GetNativeSavedFrame(JS::MutableHandle<JS::Value> aSavedFrame)
696
0
{
697
0
  aSavedFrame.setObjectOrNull(mStack);
698
0
  return NS_OK;
699
0
}
700
701
NS_IMETHODIMP
702
JSStackFrame::ToStringXPCOM(JSContext* aCx, nsACString& _retval)
703
0
{
704
0
  ToString(aCx, _retval);
705
0
  return NS_OK;
706
0
}
707
708
void
709
JSStackFrame::ToString(JSContext* aCx, nsACString& _retval)
710
0
{
711
0
  _retval.Truncate();
712
0
713
0
  nsString filename;
714
0
  GetFilename(aCx, filename);
715
0
716
0
  if (filename.IsEmpty()) {
717
0
    filename.AssignLiteral("<unknown filename>");
718
0
  }
719
0
720
0
  nsString funname;
721
0
  GetName(aCx, funname);
722
0
723
0
  if (funname.IsEmpty()) {
724
0
    funname.AssignLiteral("<TOP_LEVEL>");
725
0
  }
726
0
727
0
  int32_t lineno = GetLineNumber(aCx);
728
0
729
0
  static const char format[] = "JS frame :: %s :: %s :: line %d";
730
0
  _retval.AppendPrintf(format,
731
0
                       NS_ConvertUTF16toUTF8(filename).get(),
732
0
                       NS_ConvertUTF16toUTF8(funname).get(),
733
0
                       lineno);
734
0
}
735
736
already_AddRefed<nsIStackFrame>
737
CreateStack(JSContext* aCx, JS::StackCapture&& aCaptureMode)
738
0
{
739
0
  JS::Rooted<JSObject*> stack(aCx);
740
0
  if (!JS::CaptureCurrentStack(aCx, &stack, std::move(aCaptureMode))) {
741
0
    return nullptr;
742
0
  }
743
0
744
0
  if (!stack) {
745
0
    return nullptr;
746
0
  }
747
0
748
0
  nsCOMPtr<nsIStackFrame> frame = new JSStackFrame(stack);
749
0
  return frame.forget();
750
0
}
751
752
} // namespace exceptions
753
} // namespace dom
754
} // namespace mozilla