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