Bumping manifests a=b2g-bump
[gecko.git] / dom / bindings / Exceptions.cpp
blob9886328b98e22b8eb61d4b0dd117221d47db1a22
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/Exceptions.h"
8 #include "js/GCAPI.h"
9 #include "jsapi.h"
10 #include "jsprf.h"
11 #include "mozilla/CycleCollectedJSRuntime.h"
12 #include "mozilla/dom/BindingUtils.h"
13 #include "mozilla/dom/DOMException.h"
14 #include "mozilla/dom/ScriptSettings.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsThreadUtils.h"
18 #include "XPCWrapper.h"
19 #include "WorkerPrivate.h"
20 #include "nsContentUtils.h"
22 namespace mozilla {
23 namespace dom {
25 bool
26 ThrowExceptionObject(JSContext* aCx, nsIException* aException)
28 // See if we really have an Exception.
29 nsCOMPtr<Exception> exception = do_QueryInterface(aException);
30 if (exception) {
31 return ThrowExceptionObject(aCx, exception);
34 // We only have an nsIException (probably an XPCWrappedJS). Fall back on old
35 // wrapping.
36 MOZ_ASSERT(NS_IsMainThread());
38 JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
39 if (!glob) {
40 // XXXbz Can this really be null here?
41 return false;
44 JS::Rooted<JS::Value> val(aCx);
45 if (!WrapObject(aCx, aException, &NS_GET_IID(nsIException), &val)) {
46 return false;
49 JS_SetPendingException(aCx, val);
51 return true;
54 bool
55 ThrowExceptionObject(JSContext* aCx, Exception* aException)
57 JS::Rooted<JS::Value> thrown(aCx);
59 // If we stored the original thrown JS value in the exception
60 // (see XPCConvert::ConstructException) and we are in a web context
61 // (i.e., not chrome), rethrow the original value. This only applies to JS
62 // implemented components so we only need to check for this on the main
63 // thread.
64 if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() &&
65 aException->StealJSVal(thrown.address())) {
66 if (!JS_WrapValue(aCx, &thrown)) {
67 return false;
69 JS_SetPendingException(aCx, thrown);
70 return true;
73 JS::Rooted<JSObject*> glob(aCx, JS::CurrentGlobalOrNull(aCx));
74 if (!glob) {
75 // XXXbz Can this actually be null here?
76 return false;
79 if (!GetOrCreateDOMReflector(aCx, aException, &thrown)) {
80 return false;
83 JS_SetPendingException(aCx, thrown);
84 return true;
87 bool
88 Throw(JSContext* aCx, nsresult aRv, const char* aMessage)
90 if (JS_IsExceptionPending(aCx)) {
91 // Don't clobber the existing exception.
92 return false;
95 CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
96 nsCOMPtr<nsIException> existingException = runtime->GetPendingException();
97 if (existingException) {
98 nsresult nr;
99 if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
100 aRv == nr) {
101 // Reuse the existing exception.
103 // Clear pending exception
104 runtime->SetPendingException(nullptr);
106 if (!ThrowExceptionObject(aCx, existingException)) {
107 // If we weren't able to throw an exception we're
108 // most likely out of memory
109 JS_ReportOutOfMemory(aCx);
111 return false;
115 nsRefPtr<Exception> finalException = CreateException(aCx, aRv, aMessage);
117 MOZ_ASSERT(finalException);
118 if (!ThrowExceptionObject(aCx, finalException)) {
119 // If we weren't able to throw an exception we're
120 // most likely out of memory
121 JS_ReportOutOfMemory(aCx);
124 return false;
127 void
128 ThrowAndReport(nsPIDOMWindow* aWindow, nsresult aRv, const char* aMessage)
130 AutoJSAPI jsapi;
131 if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
132 return;
135 Throw(jsapi.cx(), aRv, aMessage);
136 (void) JS_ReportPendingException(jsapi.cx());
139 already_AddRefed<Exception>
140 CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
142 // Do we use DOM exceptions for this error code?
143 switch (NS_ERROR_GET_MODULE(aRv)) {
144 case NS_ERROR_MODULE_DOM:
145 case NS_ERROR_MODULE_SVG:
146 case NS_ERROR_MODULE_DOM_XPATH:
147 case NS_ERROR_MODULE_DOM_INDEXEDDB:
148 case NS_ERROR_MODULE_DOM_FILEHANDLE:
149 case NS_ERROR_MODULE_DOM_BLUETOOTH:
150 return DOMException::Create(aRv);
151 default:
152 break;
155 // If not, use the default.
156 // aMessage can be null, so we can't use nsDependentCString on it.
157 nsRefPtr<Exception> exception =
158 new Exception(nsCString(aMessage), aRv,
159 EmptyCString(), nullptr, nullptr);
160 return exception.forget();
163 already_AddRefed<nsIStackFrame>
164 GetCurrentJSStack()
166 // is there a current context available?
167 JSContext* cx = nullptr;
169 if (NS_IsMainThread()) {
170 MOZ_ASSERT(nsContentUtils::XPConnect());
171 cx = nsContentUtils::GetCurrentJSContext();
172 } else {
173 cx = workers::GetCurrentThreadJSContext();
176 if (!cx) {
177 return nullptr;
180 nsCOMPtr<nsIStackFrame> stack = exceptions::CreateStack(cx);
181 if (!stack) {
182 return nullptr;
185 // Note that CreateStack only returns JS frames, so we're done here.
186 return stack.forget();
189 namespace exceptions {
191 class StackFrame : public nsIStackFrame
193 public:
194 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
195 NS_DECL_CYCLE_COLLECTION_CLASS(StackFrame)
196 NS_DECL_NSISTACKFRAME
198 StackFrame(uint32_t aLanguage,
199 const char* aFilename,
200 const char* aFunctionName,
201 int32_t aLineNumber,
202 nsIStackFrame* aCaller);
204 StackFrame()
205 : mLineno(0)
206 , mColNo(0)
207 , mLanguage(nsIProgrammingLanguage::UNKNOWN)
211 static already_AddRefed<nsIStackFrame>
212 CreateStackFrameLocation(uint32_t aLanguage,
213 const char* aFilename,
214 const char* aFunctionName,
215 int32_t aLineNumber,
216 nsIStackFrame* aCaller);
217 protected:
218 virtual ~StackFrame();
220 virtual bool IsJSFrame() const
222 return false;
225 virtual nsresult GetLineno(int32_t* aLineNo)
227 *aLineNo = mLineno;
228 return NS_OK;
231 virtual nsresult GetColNo(int32_t* aColNo)
233 *aColNo = mColNo;
234 return NS_OK;
237 nsCOMPtr<nsIStackFrame> mCaller;
238 nsString mFilename;
239 nsString mFunname;
240 int32_t mLineno;
241 int32_t mColNo;
242 uint32_t mLanguage;
245 StackFrame::StackFrame(uint32_t aLanguage,
246 const char* aFilename,
247 const char* aFunctionName,
248 int32_t aLineNumber,
249 nsIStackFrame* aCaller)
250 : mCaller(aCaller)
251 , mLineno(aLineNumber)
252 , mLanguage(aLanguage)
254 CopyUTF8toUTF16(aFilename, mFilename);
255 CopyUTF8toUTF16(aFunctionName, mFunname);
258 StackFrame::~StackFrame()
262 NS_IMPL_CYCLE_COLLECTION(StackFrame, mCaller)
263 NS_IMPL_CYCLE_COLLECTING_ADDREF(StackFrame)
264 NS_IMPL_CYCLE_COLLECTING_RELEASE(StackFrame)
266 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StackFrame)
267 NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
268 NS_INTERFACE_MAP_ENTRY(nsISupports)
269 NS_INTERFACE_MAP_END
271 class JSStackFrame : public StackFrame
273 public:
274 NS_DECL_ISUPPORTS_INHERITED
275 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSStackFrame,
276 StackFrame)
278 // aStack must not be null.
279 explicit JSStackFrame(JS::Handle<JSObject*> aStack);
281 static already_AddRefed<nsIStackFrame>
282 CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
284 NS_IMETHOD GetLanguageName(nsACString& aLanguageName) MOZ_OVERRIDE;
285 NS_IMETHOD GetFilename(nsAString& aFilename) MOZ_OVERRIDE;
286 NS_IMETHOD GetName(nsAString& aFunction) MOZ_OVERRIDE;
287 NS_IMETHOD GetCaller(nsIStackFrame** aCaller) MOZ_OVERRIDE;
288 NS_IMETHOD GetFormattedStack(nsAString& aStack) MOZ_OVERRIDE;
290 protected:
291 virtual bool IsJSFrame() const MOZ_OVERRIDE {
292 return true;
295 virtual nsresult GetLineno(int32_t* aLineNo) MOZ_OVERRIDE;
296 virtual nsresult GetColNo(int32_t* aColNo) MOZ_OVERRIDE;
298 private:
299 virtual ~JSStackFrame();
301 JS::Heap<JSObject*> mStack;
302 nsString mFormattedStack;
304 bool mFilenameInitialized;
305 bool mFunnameInitialized;
306 bool mLinenoInitialized;
307 bool mColNoInitialized;
308 bool mCallerInitialized;
309 bool mFormattedStackInitialized;
312 JSStackFrame::JSStackFrame(JS::Handle<JSObject*> aStack)
313 : mStack(aStack)
314 , mFilenameInitialized(false)
315 , mFunnameInitialized(false)
316 , mLinenoInitialized(false)
317 , mColNoInitialized(false)
318 , mCallerInitialized(false)
319 , mFormattedStackInitialized(false)
321 MOZ_ASSERT(mStack);
323 mozilla::HoldJSObjects(this);
324 mLineno = 0;
325 mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
328 JSStackFrame::~JSStackFrame()
330 mozilla::DropJSObjects(this);
333 NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame)
334 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(JSStackFrame, StackFrame)
335 tmp->mStack = nullptr;
336 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(JSStackFrame, StackFrame)
338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
339 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
340 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSStackFrame, StackFrame)
341 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
342 NS_IMPL_CYCLE_COLLECTION_TRACE_END
344 NS_IMPL_ADDREF_INHERITED(JSStackFrame, StackFrame)
345 NS_IMPL_RELEASE_INHERITED(JSStackFrame, StackFrame)
347 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(JSStackFrame)
348 NS_INTERFACE_MAP_END_INHERITING(StackFrame)
350 /* readonly attribute uint32_t language; */
351 NS_IMETHODIMP StackFrame::GetLanguage(uint32_t* aLanguage)
353 *aLanguage = mLanguage;
354 return NS_OK;
357 /* readonly attribute string languageName; */
358 NS_IMETHODIMP StackFrame::GetLanguageName(nsACString& aLanguageName)
360 aLanguageName.AssignLiteral("C++");
361 return NS_OK;
364 NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
366 aLanguageName.AssignLiteral("JavaScript");
367 return NS_OK;
370 /* readonly attribute AString filename; */
371 NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
373 // We can get called after unlink; in that case we can't do much
374 // about producing a useful value.
375 if (!mFilenameInitialized && mStack) {
376 ThreadsafeAutoJSContext cx;
377 JS::Rooted<JSObject*> stack(cx, mStack);
378 JS::ExposeObjectToActiveJS(mStack);
379 JSAutoCompartment ac(cx, stack);
380 JS::Rooted<JS::Value> filenameVal(cx);
381 if (!JS_GetProperty(cx, stack, "source", &filenameVal) ||
382 !filenameVal.isString()) {
383 return NS_ERROR_UNEXPECTED;
385 nsAutoJSString str;
386 if (!str.init(cx, filenameVal.toString())) {
387 return NS_ERROR_OUT_OF_MEMORY;
389 mFilename = str;
390 mFilenameInitialized = true;
393 return StackFrame::GetFilename(aFilename);
396 NS_IMETHODIMP StackFrame::GetFilename(nsAString& aFilename)
398 // The filename must be set to null if empty.
399 if (mFilename.IsEmpty()) {
400 aFilename.SetIsVoid(true);
401 } else {
402 aFilename.Assign(mFilename);
405 return NS_OK;
408 /* readonly attribute AString name; */
409 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
411 // We can get called after unlink; in that case we can't do much
412 // about producing a useful value.
413 if (!mFunnameInitialized && mStack) {
414 ThreadsafeAutoJSContext cx;
415 JS::Rooted<JSObject*> stack(cx, mStack);
416 JS::ExposeObjectToActiveJS(mStack);
417 JSAutoCompartment ac(cx, stack);
418 JS::Rooted<JS::Value> nameVal(cx);
419 // functionDisplayName can be null
420 if (!JS_GetProperty(cx, stack, "functionDisplayName", &nameVal) ||
421 (!nameVal.isString() && !nameVal.isNull())) {
422 return NS_ERROR_UNEXPECTED;
424 if (nameVal.isString()) {
425 nsAutoJSString str;
426 if (!str.init(cx, nameVal.toString())) {
427 return NS_ERROR_OUT_OF_MEMORY;
429 mFunname = str;
431 mFunnameInitialized = true;
434 return StackFrame::GetName(aFunction);
437 NS_IMETHODIMP StackFrame::GetName(nsAString& aFunction)
439 // The function name must be set to null if empty.
440 if (mFunname.IsEmpty()) {
441 aFunction.SetIsVoid(true);
442 } else {
443 aFunction.Assign(mFunname);
446 return NS_OK;
449 // virtual
450 nsresult
451 JSStackFrame::GetLineno(int32_t* aLineNo)
453 // We can get called after unlink; in that case we can't do much
454 // about producing a useful value.
455 if (!mLinenoInitialized && mStack) {
456 ThreadsafeAutoJSContext cx;
457 JS::Rooted<JSObject*> stack(cx, mStack);
458 JS::ExposeObjectToActiveJS(mStack);
459 JSAutoCompartment ac(cx, stack);
460 JS::Rooted<JS::Value> lineVal(cx);
461 if (!JS_GetProperty(cx, stack, "line", &lineVal) ||
462 !lineVal.isNumber()) {
463 return NS_ERROR_UNEXPECTED;
465 mLineno = lineVal.toNumber();
466 mLinenoInitialized = true;
469 return StackFrame::GetLineno(aLineNo);
472 /* readonly attribute int32_t lineNumber; */
473 NS_IMETHODIMP StackFrame::GetLineNumber(int32_t* aLineNumber)
475 return GetLineno(aLineNumber);
478 // virtual
479 nsresult
480 JSStackFrame::GetColNo(int32_t* aColNo)
482 // We can get called after unlink; in that case we can't do much
483 // about producing a useful value.
484 if (!mColNoInitialized && mStack) {
485 ThreadsafeAutoJSContext cx;
486 JS::Rooted<JSObject*> stack(cx, mStack);
487 JS::ExposeObjectToActiveJS(mStack);
488 JSAutoCompartment ac(cx, stack);
489 JS::Rooted<JS::Value> colVal(cx);
490 if (!JS_GetProperty(cx, stack, "column", &colVal) ||
491 !colVal.isNumber()) {
492 return NS_ERROR_UNEXPECTED;
494 mColNo = colVal.toNumber();
495 mColNoInitialized = true;
498 return StackFrame::GetColNo(aColNo);
501 /* readonly attribute int32_t columnNumber; */
502 NS_IMETHODIMP StackFrame::GetColumnNumber(int32_t* aColumnNumber)
504 return GetColNo(aColumnNumber);
507 /* readonly attribute AUTF8String sourceLine; */
508 NS_IMETHODIMP StackFrame::GetSourceLine(nsACString& aSourceLine)
510 aSourceLine.Truncate();
511 return NS_OK;
514 /* readonly attribute nsIStackFrame caller; */
515 NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller)
517 // We can get called after unlink; in that case we can't do much
518 // about producing a useful value.
519 if (!mCallerInitialized && mStack) {
520 ThreadsafeAutoJSContext cx;
521 JS::Rooted<JSObject*> stack(cx, mStack);
522 JS::ExposeObjectToActiveJS(mStack);
523 JSAutoCompartment ac(cx, stack);
524 JS::Rooted<JS::Value> callerVal(cx);
525 if (!JS_GetProperty(cx, stack, "parent", &callerVal) ||
526 !callerVal.isObjectOrNull()) {
527 return NS_ERROR_UNEXPECTED;
530 if (callerVal.isObject()) {
531 JS::Rooted<JSObject*> caller(cx, &callerVal.toObject());
532 mCaller = new JSStackFrame(caller);
533 } else {
534 // Do we really need this dummy frame? If so, we should document why... I
535 // guess for symmetry with the "nothing on the stack" case, which returns
536 // a single dummy frame?
537 mCaller = new StackFrame();
539 mCallerInitialized = true;
541 return StackFrame::GetCaller(aCaller);
544 NS_IMETHODIMP StackFrame::GetCaller(nsIStackFrame** aCaller)
546 NS_IF_ADDREF(*aCaller = mCaller);
547 return NS_OK;
550 NS_IMETHODIMP JSStackFrame::GetFormattedStack(nsAString& aStack)
552 // We can get called after unlink; in that case we can't do much
553 // about producing a useful value.
554 if (!mFormattedStackInitialized && mStack) {
555 ThreadsafeAutoJSContext cx;
556 JS::Rooted<JS::Value> stack(cx, JS::ObjectValue(*mStack));
557 JS::ExposeObjectToActiveJS(mStack);
558 JSAutoCompartment ac(cx, mStack);
559 JS::Rooted<JSString*> formattedStack(cx, JS::ToString(cx, stack));
560 if (!formattedStack) {
561 return NS_ERROR_UNEXPECTED;
563 nsAutoJSString str;
564 if (!str.init(cx, formattedStack)) {
565 return NS_ERROR_OUT_OF_MEMORY;
567 mFormattedStack = str;
568 mFormattedStackInitialized = true;
571 aStack = mFormattedStack;
572 return NS_OK;
575 NS_IMETHODIMP StackFrame::GetFormattedStack(nsAString& aStack)
577 aStack.Truncate();
578 return NS_OK;
581 /* AUTF8String toString (); */
582 NS_IMETHODIMP StackFrame::ToString(nsACString& _retval)
584 _retval.Truncate();
586 const char* frametype = IsJSFrame() ? "JS" : "native";
588 nsString filename;
589 nsresult rv = GetFilename(filename);
590 NS_ENSURE_SUCCESS(rv, rv);
592 if (filename.IsEmpty()) {
593 filename.AssignLiteral("<unknown filename>");
596 nsString funname;
597 rv = GetName(funname);
598 NS_ENSURE_SUCCESS(rv, rv);
600 if (funname.IsEmpty()) {
601 funname.AssignLiteral("<TOP_LEVEL>");
604 int32_t lineno;
605 rv = GetLineno(&lineno);
606 NS_ENSURE_SUCCESS(rv, rv);
608 static const char format[] = "%s frame :: %s :: %s :: line %d";
609 _retval.AppendPrintf(format, frametype,
610 NS_ConvertUTF16toUTF8(filename).get(),
611 NS_ConvertUTF16toUTF8(funname).get(),
612 lineno);
613 return NS_OK;
616 /* static */ already_AddRefed<nsIStackFrame>
617 JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
619 static const unsigned MAX_FRAMES = 100;
620 if (aMaxDepth < 0) {
621 aMaxDepth = MAX_FRAMES;
624 JS::Rooted<JSObject*> stack(aCx);
625 if (!JS::CaptureCurrentStack(aCx, &stack, aMaxDepth)) {
626 return nullptr;
629 nsCOMPtr<nsIStackFrame> first;
630 if (!stack) {
631 first = new StackFrame();
632 } else {
633 first = new JSStackFrame(stack);
635 return first.forget();
638 /* static */ already_AddRefed<nsIStackFrame>
639 StackFrame::CreateStackFrameLocation(uint32_t aLanguage,
640 const char* aFilename,
641 const char* aFunctionName,
642 int32_t aLineNumber,
643 nsIStackFrame* aCaller)
645 nsRefPtr<StackFrame> self =
646 new StackFrame(aLanguage, aFilename, aFunctionName, aLineNumber, aCaller);
647 return self.forget();
650 already_AddRefed<nsIStackFrame>
651 CreateStack(JSContext* aCx, int32_t aMaxDepth)
653 return JSStackFrame::CreateStack(aCx, aMaxDepth);
656 already_AddRefed<nsIStackFrame>
657 CreateStackFrameLocation(uint32_t aLanguage,
658 const char* aFilename,
659 const char* aFunctionName,
660 int32_t aLineNumber,
661 nsIStackFrame* aCaller)
663 return StackFrame::CreateStackFrameLocation(aLanguage, aFilename,
664 aFunctionName, aLineNumber,
665 aCaller);
668 } // namespace exceptions
669 } // namespace dom
670 } // namespace mozilla