void AutoJSAPI::ReportException() { MOZ_ASSERT(OwnsErrorReporting(), "This is not our exception to report!"); if (!HasException()) { return; } // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) errorGlobal = xpc::PrivilegedJunkScope(); JSAutoCompartment ac(cx(), errorGlobal); nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal); JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn)) { nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); xpcReport->Init(jsReport.report(), jsReport.message(), nsContentUtils::IsCallerChrome(), win ? win->WindowID() : 0); if (win) { DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn); } else { xpcReport->LogToConsole(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); } }
void AutoJSAPI::ReportException() { MOZ_ASSERT(OwnsErrorReporting(), "This is not our exception to report!"); if (!HasException()) { return; } // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) { if (mIsMainThread) { errorGlobal = xpc::PrivilegedJunkScope(); } else { errorGlobal = workers::GetCurrentThreadWorkerGlobal(); } } JSAutoCompartment ac(cx(), errorGlobal); JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn)) { if (mIsMainThread) { RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal); nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr; xpcReport->Init(jsReport.report(), jsReport.message(), nsContentUtils::IsCallerChrome(), inner ? inner->WindowID() : 0); if (inner) { DispatchScriptErrorEvent(inner, JS_GetRuntime(cx()), xpcReport, exn); } else { xpcReport->LogToConsole(); } } else { // On a worker, we just use the worker error reporting mechanism and don't // bother with xpc::ErrorReport. This will ensure that all the right // events (which are a lot more complicated than in the window case) get // fired. workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); MOZ_ASSERT(worker->GetJSContext() == cx()); // Before invoking ReportError, put the exception back on the context, // because it may want to put it in its error events and has no other way // to get hold of it. After we invoke ReportError, clear the exception on // cx(), just in case ReportError didn't. JS_SetPendingException(cx(), exn); worker->ReportError(cx(), jsReport.message(), jsReport.report()); ClearException(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); ClearException(); } }
void Promise::HandleException(JSContext* aCx) { JS::Rooted<JS::Value> exn(aCx); if (JS_GetPendingException(aCx, &exn)) { JS_ClearPendingException(aCx); // This is only called from MaybeSomething, so it's OK to MaybeReject here. MaybeReject(aCx, exn); } }
CallbackObject::CallSetup::~CallSetup() { // To get our nesting right we have to destroy our JSAutoCompartment first. // In particular, we want to do this before we try reporting any exceptions, // so we end up reporting them while in the compartment of our entry point, // not whatever cross-compartment wrappper mCallback might be. // Be careful: the JSAutoCompartment might not have been constructed at all! mAc.reset(); // Now, if we have a JSContext, report any pending errors on it, unless we // were told to re-throw them. if (mCx) { bool needToDealWithException = mAutoEntryScript->HasException(); if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) || mExceptionHandling == eRethrowExceptions) { mErrorResult.MightThrowJSException(); if (needToDealWithException) { JS::Rooted<JS::Value> exn(mCx); if (mAutoEntryScript->PeekException(&exn) && ShouldRethrowException(exn)) { mAutoEntryScript->ClearException(); MOZ_ASSERT(!mAutoEntryScript->HasException()); mErrorResult.ThrowJSException(mCx, exn); needToDealWithException = false; } } } if (needToDealWithException) { // Either we're supposed to report our exceptions, or we're supposed to // re-throw them but we failed to get the exception value. Either way, // we'll just report the pending exception, if any, once ~mAutoEntryScript // runs. Note that we've already run ~mAc, effectively, so we don't have // to worry about ordering here. if (mErrorResult.IsJSContextException()) { // XXXkhuey bug 1117269. When this is fixed, please consider fixing // ThrowExceptionValueIfSafe over in Exceptions.cpp in the same way. // IsJSContextException shouldn't be true anymore because we will report // the exception on the JSContext ... so throw something else. mErrorResult.ThrowWithCustomCleanup(NS_ERROR_UNEXPECTED); } } } mAutoIncumbentScript.reset(); mAutoEntryScript.reset(); // It is important that this is the last thing we do, after leaving the // compartment and undoing all our entry/incumbent script changes if (mIsMainThread) { nsContentUtils::LeaveMicroTask(); } }
~AutoPACErrorReporter() { if (!JS_IsExceptionPending(mCx)) { return; } JS::RootedValue exn(mCx); if (!JS_GetPendingException(mCx, &exn)) { return; } JS_ClearPendingException(mCx); js::ErrorReport report(mCx); if (!report.init(mCx, exn, js::ErrorReport::WithSideEffects)) { JS_ClearPendingException(mCx); return; } PACLogErrorOrWarning(NS_LITERAL_STRING("Error"), report.report()); }
AutoJSAPI::~AutoJSAPI() { if (mOwnErrorReporting) { MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()"); JS::ContextOptionsRef(cx()).setDontReportUncaught(mOldDontReportUncaught); if (HasException()) { // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) errorGlobal = xpc::PrivilegedJunkScope(); JSAutoCompartment ac(cx(), errorGlobal); nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal); const char *category = nsContentUtils::IsCallerChrome() ? "chrome javascript" : "content javascript"; JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn)) { nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); xpcReport->Init(jsReport.report(), jsReport.message(), category, win ? win->WindowID() : 0); if (win) { DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn); } else { xpcReport->LogToConsole(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); } } } if (mOldErrorReporter.isSome()) { JS_SetErrorReporter(JS_GetRuntime(cx()), mOldErrorReporter.value()); } }
void AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal, JSContext* aCx, bool aIsMainThread) { MOZ_ASSERT(aCx); MOZ_ASSERT(aCx == danger::GetJSContext()); MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal)); MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal); #ifdef DEBUG bool haveException = JS_IsExceptionPending(aCx); #endif // DEBUG mCx = aCx; mIsMainThread = aIsMainThread; mGlobalObject = aGlobalObject; if (aIsMainThread) { // We _could_ just unconditionally emplace mAutoRequest here. It's just not // needed on worker threads, and we're hoping to kill it on the main thread // too. mAutoRequest.emplace(mCx); } if (aGlobal) { JS::ExposeObjectToActiveJS(aGlobal); } mAutoNullableCompartment.emplace(mCx, aGlobal); ScriptSettingsStack::Push(this); mOldWarningReporter.emplace(JS::GetWarningReporter(aCx)); JS::SetWarningReporter(aCx, WarningOnlyErrorReporter); #ifdef DEBUG if (haveException) { JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); JS_ClearPendingException(aCx); if (exn.isObject()) { JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject()); nsAutoJSString stack, filename, name, message; int32_t line; JS::Rooted<JS::Value> tmp(aCx); if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) { JS_ClearPendingException(aCx); } if (tmp.isUndefined()) { if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) { JS_ClearPendingException(aCx); } } if (!filename.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) || !stack.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "name", &tmp) || !name.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "message", &tmp) || !message.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) || !JS::ToInt32(aCx, tmp, &line)) { JS_ClearPendingException(aCx); line = 0; } printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n", NS_ConvertUTF16toUTF8(name).get(), NS_ConvertUTF16toUTF8(message).get(), NS_ConvertUTF16toUTF8(filename).get(), line, NS_ConvertUTF16toUTF8(stack).get()); } else { // It's a primitive... not much we can do other than stringify it. nsAutoJSString exnStr; if (!exnStr.init(aCx, exn)) { JS_ClearPendingException(aCx); } printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n", NS_ConvertUTF16toUTF8(exnStr).get()); } MOZ_ASSERT(false, "We had an exception; we should not have"); } #endif // DEBUG }
already_AddRefed<Promise> Request::ConsumeBody(ConsumeType aType, ErrorResult& aRv) { nsRefPtr<Promise> promise = Promise::Create(mOwner, aRv); if (aRv.Failed()) { return nullptr; } if (BodyUsed()) { aRv.ThrowTypeError(MSG_REQUEST_BODY_CONSUMED_ERROR); return nullptr; } SetBodyUsed(); // While the spec says to do this asynchronously, all the body constructors // right now only accept bodies whose streams are backed by an in-memory // buffer that can be read without blocking. So I think this is fine. nsCOMPtr<nsIInputStream> stream; mRequest->GetBody(getter_AddRefs(stream)); if (!stream) { aRv = NS_NewByteInputStream(getter_AddRefs(stream), "", 0, NS_ASSIGNMENT_COPY); if (aRv.Failed()) { return nullptr; } } AutoJSAPI api; api.Init(mOwner); JSContext* cx = api.cx(); // We can make this assertion because for now we only support memory backed // structures for the body argument for a Request. MOZ_ASSERT(NS_InputStreamIsBuffered(stream)); nsCString buffer; uint64_t len; aRv = stream->Available(&len); if (aRv.Failed()) { return nullptr; } aRv = NS_ReadInputStreamToString(stream, buffer, len); if (aRv.Failed()) { return nullptr; } buffer.SetLength(len); switch (aType) { case CONSUME_ARRAYBUFFER: { JS::Rooted<JSObject*> arrayBuffer(cx); arrayBuffer = ArrayBuffer::Create(cx, buffer.Length(), reinterpret_cast<const uint8_t*>(buffer.get())); JS::Rooted<JS::Value> val(cx); val.setObjectOrNull(arrayBuffer); promise->MaybeResolve(cx, val); return promise.forget(); } case CONSUME_BLOB: { // XXXnsm it is actually possible to avoid these duplicate allocations // for the Blob case by having the Blob adopt the stream's memory // directly, but I've not added a special case for now. // // This is similar to nsContentUtils::CreateBlobBuffer, but also deals // with worker wrapping. uint32_t blobLen = buffer.Length(); void* blobData = moz_malloc(blobLen); nsRefPtr<File> blob; if (blobData) { memcpy(blobData, buffer.BeginReading(), blobLen); blob = File::CreateMemoryFile(GetParentObject(), blobData, blobLen, NS_ConvertUTF8toUTF16(mMimeType)); } else { aRv = NS_ERROR_OUT_OF_MEMORY; return nullptr; } promise->MaybeResolve(blob); return promise.forget(); } case CONSUME_JSON: { nsString decoded; aRv = DecodeUTF8(buffer, decoded); if (aRv.Failed()) { return nullptr; } JS::Rooted<JS::Value> json(cx); if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) { JS::Rooted<JS::Value> exn(cx); if (JS_GetPendingException(cx, &exn)) { JS_ClearPendingException(cx); promise->MaybeReject(cx, exn); } } promise->MaybeResolve(cx, json); return promise.forget(); } case CONSUME_TEXT: { nsString decoded; aRv = DecodeUTF8(buffer, decoded); if (aRv.Failed()) { return nullptr; } promise->MaybeResolve(decoded); return promise.forget(); } } NS_NOTREACHED("Unexpected consume body type"); // Silence warnings. return nullptr; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, JS::SourceBufferHolder& aSrcBuf, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, const EvaluateOptions& aEvaluateOptions, JS::MutableHandle<JS::Value> aRetValue, void **aOffThreadToken) { PROFILER_LABEL("nsJSUtils", "EvaluateString", js::ProfileEntry::Category::JS); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aEvaluateOptions.needResult); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aEvaluateOptions.needResult); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aSrcBuf.get()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers can do this by calling the other overload of // EvaluateString() which calls this function with aEvaluateOptions.needResult // set to false. aRetValue.setUndefined(); JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.emplace(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JS::Rooted<JSScript*> script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { if (aEvaluateOptions.needResult) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = JS_ExecuteScript(aCx, rootedScope, script); } } else { ok = false; } } else { if (aEvaluateOptions.needResult) { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf, aRetValue); } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf); } } if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) { JS::Rooted<JS::Value> value(aCx, aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue()); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aEvaluateOptions.needResult) { aRetValue.setUndefined(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); if (aEvaluateOptions.needResult) { aRetValue.set(exn); } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aEvaluateOptions.needResult) { JS::Rooted<JS::Value> v(aCx, aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } aRetValue.set(v); } return rv; }
void AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread) { MOZ_ASSERT(aCx); MOZ_ASSERT(aIsMainThread == NS_IsMainThread()); #ifdef DEBUG bool haveException = JS_IsExceptionPending(aCx); #endif // DEBUG mCx = aCx; mIsMainThread = aIsMainThread; if (aIsMainThread) { // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher // can GC, which is only possible because XPCJSContextStack::Push calls // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer // be necessary. JS::Rooted<JSObject*> global(JS_GetRuntime(aCx), aGlobal); mCxPusher.emplace(mCx); mAutoNullableCompartment.emplace(mCx, global); } else { mAutoNullableCompartment.emplace(mCx, aGlobal); } JSRuntime* rt = JS_GetRuntime(aCx); mOldErrorReporter.emplace(JS_GetErrorReporter(rt)); mOldAutoJSAPIOwnsErrorReporting = JS::ContextOptionsRef(aCx).autoJSAPIOwnsErrorReporting(); JS::ContextOptionsRef(aCx).setAutoJSAPIOwnsErrorReporting(true); JS_SetErrorReporter(rt, WarningOnlyErrorReporter); #ifdef DEBUG if (haveException) { JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); JS_ClearPendingException(aCx); if (exn.isObject()) { JS::Rooted<JSObject*> exnObj(aCx, &exn.toObject()); nsAutoJSString stack, filename, name, message; int32_t line; JS::Rooted<JS::Value> tmp(aCx); if (!JS_GetProperty(aCx, exnObj, "filename", &tmp)) { JS_ClearPendingException(aCx); } if (tmp.isUndefined()) { if (!JS_GetProperty(aCx, exnObj, "fileName", &tmp)) { JS_ClearPendingException(aCx); } } if (!filename.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "stack", &tmp) || !stack.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "name", &tmp) || !name.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "message", &tmp) || !message.init(aCx, tmp)) { JS_ClearPendingException(aCx); } if (!JS_GetProperty(aCx, exnObj, "lineNumber", &tmp) || !JS::ToInt32(aCx, tmp, &line)) { JS_ClearPendingException(aCx); line = 0; } printf_stderr("PREEXISTING EXCEPTION OBJECT: '%s: %s'\n%s:%d\n%s\n", NS_ConvertUTF16toUTF8(name).get(), NS_ConvertUTF16toUTF8(message).get(), NS_ConvertUTF16toUTF8(filename).get(), line, NS_ConvertUTF16toUTF8(stack).get()); } else { // It's a primitive... not much we can do other than stringify it. nsAutoJSString exnStr; if (!exnStr.init(aCx, exn)) { JS_ClearPendingException(aCx); } printf_stderr("PREEXISTING EXCEPTION PRIMITIVE: %s\n", NS_ConvertUTF16toUTF8(exnStr).get()); } MOZ_ASSERT(false, "We had an exception; we should not have"); } #endif // DEBUG }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, const nsAString& aScript, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, EvaluateOptions& aEvaluateOptions, JS::Value* aRetValue, void **aOffThreadToken) { PROFILER_LABEL("JS", "EvaluateString"); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aRetValue); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aRetValue); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers use null to indicate they don't care. if (aRetValue) { *aRetValue = JSVAL_VOID; } JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.construct(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JSScript *script = JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = false; } } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, PromiseFlatString(aScript).get(), aScript.Length(), aRetValue); } if (ok && aEvaluateOptions.coerceToString && !aRetValue->isUndefined()) { JS::Rooted<JS::Value> value(aCx, *aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue(); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aRetValue) { *aRetValue = JS::UndefinedValue(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); if (aRetValue) { *aRetValue = exn; } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aRetValue) { JS::Rooted<JS::Value> v(aCx, *aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } *aRetValue = v; } return rv; }
void AutoJSAPI::ReportException() { if (!HasException()) { return; } // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) { if (mIsMainThread) { errorGlobal = xpc::PrivilegedJunkScope(); } else { errorGlobal = workers::GetCurrentThreadWorkerGlobal(); } } JSAutoCompartment ac(cx(), errorGlobal); JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) { if (mIsMainThread) { RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal); if (!win) { // We run addons in a separate privileged compartment, but they still // expect to trigger the onerror handler of their associated DOM Window. win = xpc::AddonWindowOrNull(errorGlobal); } nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr; xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(), nsContentUtils::IsCallerChrome(), inner ? inner->WindowID() : 0); if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) { JS::RootingContext* rcx = JS::RootingContext::get(cx()); DispatchScriptErrorEvent(inner, rcx, xpcReport, exn); } else { JS::Rooted<JSObject*> stack(cx(), xpc::FindExceptionStackForConsoleReport(inner, exn)); xpcReport->LogToConsoleWithStack(stack); } } else { // On a worker, we just use the worker error reporting mechanism and don't // bother with xpc::ErrorReport. This will ensure that all the right // events (which are a lot more complicated than in the window case) get // fired. workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); MOZ_ASSERT(worker->GetJSContext() == cx()); // Before invoking ReportError, put the exception back on the context, // because it may want to put it in its error events and has no other way // to get hold of it. After we invoke ReportError, clear the exception on // cx(), just in case ReportError didn't. JS_SetPendingException(cx(), exn); worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report()); ClearException(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); ClearException(); } }