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