mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
window.onerror catches JSON.parse error in Promise fulfillment
This commit is contained in:
@@ -173,7 +173,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Exception)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@@ -184,7 +183,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
tmp->mThrownJSVal.setNull();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@@ -244,7 +242,7 @@ Exception::Exception(const nsACString& aMessage,
|
||||
}
|
||||
}
|
||||
|
||||
Initialize(aMessage, aResult, aName, location, aData, nullptr);
|
||||
Initialize(aMessage, aResult, aName, location, aData);
|
||||
}
|
||||
|
||||
Exception::Exception()
|
||||
@@ -403,18 +401,6 @@ Exception::GetData(nsISupports** aData)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute nsIException inner; */
|
||||
NS_IMETHODIMP
|
||||
Exception::GetInner(nsIException** aException)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aException);
|
||||
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsCOMPtr<nsIException> inner = mInner;
|
||||
inner.forget(aException);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* AUTF8String toString (); */
|
||||
NS_IMETHODIMP
|
||||
Exception::ToString(nsACString& _retval)
|
||||
@@ -463,7 +449,7 @@ Exception::ToString(nsACString& _retval)
|
||||
NS_IMETHODIMP
|
||||
Exception::Initialize(const nsACString& aMessage, nsresult aResult,
|
||||
const nsACString& aName, nsIStackFrame *aLocation,
|
||||
nsISupports *aData, nsIException *aInner)
|
||||
nsISupports *aData)
|
||||
{
|
||||
NS_ENSURE_FALSE(mInitialized, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
@@ -483,7 +469,6 @@ Exception::Initialize(const nsACString& aMessage, nsresult aResult,
|
||||
}
|
||||
|
||||
mData = aData;
|
||||
mInner = aInner;
|
||||
|
||||
mInitialized = true;
|
||||
return NS_OK;
|
||||
@@ -552,13 +537,6 @@ Exception::GetLocation() const
|
||||
return location.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
Exception::GetInner() const
|
||||
{
|
||||
nsCOMPtr<nsIException> inner = mInner;
|
||||
return inner.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
Exception::GetData() const
|
||||
{
|
||||
@@ -632,25 +610,6 @@ DOMException::ToString(nsACString& aReturn)
|
||||
|
||||
nsAutoCString location;
|
||||
|
||||
if (mInner) {
|
||||
nsString filename;
|
||||
mInner->GetFilename(filename);
|
||||
|
||||
if (!filename.IsEmpty()) {
|
||||
uint32_t line_nr = 0;
|
||||
|
||||
mInner->GetLineNumber(&line_nr);
|
||||
|
||||
char *temp = PR_smprintf("%s Line: %d",
|
||||
NS_ConvertUTF16toUTF8(filename).get(),
|
||||
line_nr);
|
||||
if (temp) {
|
||||
location.Assign(temp);
|
||||
PR_smprintf_free(temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (location.IsEmpty()) {
|
||||
location = defaultLocation;
|
||||
}
|
||||
|
||||
@@ -83,8 +83,6 @@ public:
|
||||
|
||||
already_AddRefed<nsIStackFrame> GetLocation() const;
|
||||
|
||||
already_AddRefed<nsISupports> GetInner() const;
|
||||
|
||||
already_AddRefed<nsISupports> GetData() const;
|
||||
|
||||
void GetStack(nsAString& aStack, ErrorResult& aRv) const;
|
||||
@@ -110,7 +108,6 @@ protected:
|
||||
nsCOMPtr<nsISupports> mData;
|
||||
nsString mFilename;
|
||||
int mLineNumber;
|
||||
nsCOMPtr<nsIException> mInner;
|
||||
bool mInitialized;
|
||||
|
||||
bool mHoldingJSVal;
|
||||
|
||||
+60
-37
@@ -312,36 +312,15 @@ AutoJSAPI::~AutoJSAPI()
|
||||
{
|
||||
if (mOwnErrorReporting) {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()");
|
||||
|
||||
ReportException();
|
||||
|
||||
// We need to do this _after_ processing the existing exception, because the
|
||||
// JS engine can throw while doing that, and uses this bit to determine what
|
||||
// to do in that case: squelch the exception if the bit is set, otherwise
|
||||
// call the error reporter. Calling WarningOnlyErrorReporter with a
|
||||
// non-warning will assert, so we need to make sure we do the former.
|
||||
JS::ContextOptionsRef(cx()).setAutoJSAPIOwnsErrorReporting(mOldAutoJSAPIOwnsErrorReporting);
|
||||
|
||||
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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mOldErrorReporter.isSome()) {
|
||||
@@ -502,17 +481,61 @@ AutoJSAPI::TakeOwnershipOfErrorReporting()
|
||||
JS_SetErrorReporter(rt, WarningOnlyErrorReporter);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
|
||||
{
|
||||
MOZ_ASSERT_IF(mIsMainThread, CxPusherIsStackTop());
|
||||
MOZ_ASSERT(HasException());
|
||||
MOZ_ASSERT(js::GetContextCompartment(cx()));
|
||||
if (!JS_GetPendingException(cx(), aVal)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
|
||||
{
|
||||
MOZ_ASSERT(CxPusherIsStackTop());
|
||||
MOZ_ASSERT(HasException());
|
||||
MOZ_ASSERT(js::GetContextCompartment(cx()));
|
||||
if (!JS_GetPendingException(cx(), aVal)) {
|
||||
return false;
|
||||
}
|
||||
JS_ClearPendingException(cx());
|
||||
return true;
|
||||
if (!PeekException(aVal)) {
|
||||
return false;
|
||||
}
|
||||
JS_ClearPendingException(cx());
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||
|
||||
@@ -273,6 +273,9 @@ public:
|
||||
// while keeping the old behavior as the default.
|
||||
void TakeOwnershipOfErrorReporting();
|
||||
bool OwnsErrorReporting() { return mOwnErrorReporting; }
|
||||
// If HasException, report it. Otherwise, a no-op. This must be
|
||||
// called only if OwnsErrorReporting().
|
||||
void ReportException();
|
||||
|
||||
bool HasException() const {
|
||||
MOZ_ASSERT(CxPusherIsStackTop());
|
||||
@@ -287,6 +290,14 @@ public:
|
||||
// into the current compartment.
|
||||
bool StealException(JS::MutableHandle<JS::Value> aVal);
|
||||
|
||||
// Peek the current exception from the JS engine, without stealing it.
|
||||
// Callers must ensure that HasException() is true, and that cx() is in a
|
||||
// non-null compartment.
|
||||
//
|
||||
// Note that this fails if and only if we OOM while wrapping the exception
|
||||
// into the current compartment.
|
||||
bool PeekException(JS::MutableHandle<JS::Value> aVal);
|
||||
|
||||
void ClearException() {
|
||||
MOZ_ASSERT(CxPusherIsStackTop());
|
||||
JS_ClearPendingException(cx());
|
||||
|
||||
@@ -12413,6 +12413,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
||||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialization-steps
|
||||
AutoEntryScript entryScript(this, true, aScx->GetNativeContext());
|
||||
entryScript.TakeOwnershipOfErrorReporting();
|
||||
JS::CompileOptions options(entryScript.cx());
|
||||
options.setFileAndLine(filename, lineNo)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
|
||||
+6
-59
@@ -98,41 +98,6 @@ nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext)
|
||||
return innerWindowID;
|
||||
}
|
||||
|
||||
void
|
||||
nsJSUtils::ReportPendingException(JSContext *aContext)
|
||||
{
|
||||
if (JS_IsExceptionPending(aContext)) {
|
||||
bool saved = JS_SaveFrameChain(aContext);
|
||||
{
|
||||
// JS_SaveFrameChain set the compartment of aContext to null, so we need
|
||||
// to enter a compartment. The question is, which one? We don't want to
|
||||
// enter the original compartment of aContext (or the compartment of the
|
||||
// current exception on aContext, for that matter) because when we
|
||||
// JS_ReportPendingException the JS engine can try to duck-type the
|
||||
// exception and produce a JSErrorReport. It will then pass that
|
||||
// JSErrorReport to the error reporter on aContext, which might expose
|
||||
// information from it to script via onerror handlers. So it's very
|
||||
// important that the duck typing happen in the same compartment as the
|
||||
// onerror handler. In practice, that's the compartment of the window (or
|
||||
// otherwise default global) of aContext, so use that here.
|
||||
nsIScriptContext* scx = GetScriptContextFromJSContext(aContext);
|
||||
JS::Rooted<JSObject*> scope(aContext);
|
||||
scope = scx ? scx->GetWindowProxy() : nullptr;
|
||||
if (!scope) {
|
||||
// The SafeJSContext has no default object associated with it.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aContext == nsContentUtils::GetSafeJSContext());
|
||||
scope = xpc::UnprivilegedJunkScope(); // Usage approved by bholley
|
||||
}
|
||||
JSAutoCompartment ac(aContext, scope);
|
||||
JS_ReportPendingException(aContext);
|
||||
}
|
||||
if (saved) {
|
||||
JS_RestoreFrameChain(aContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
JS::AutoObjectVector& aScopeChain,
|
||||
@@ -199,12 +164,11 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
PROFILER_LABEL("nsJSUtils", "EvaluateString",
|
||||
js::ProfileEntry::Category::JS);
|
||||
|
||||
MOZ_ASSERT(JS::ContextOptionsRef(aCx).autoJSAPIOwnsErrorReporting(),
|
||||
"Caller must own error reporting");
|
||||
MOZ_ASSERT_IF(aCompileOptions.versionSet,
|
||||
aCompileOptions.version != JSVERSION_UNKNOWN);
|
||||
MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, !aCompileOptions.noScriptRval);
|
||||
MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, !aCompileOptions.noScriptRval);
|
||||
// Note that the above assert means that if aCompileOptions.noScriptRval then
|
||||
// also aEvaluateOptions.reportUncaught.
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(aSrcBuf.get());
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
|
||||
@@ -225,13 +189,6 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_TRUE(ssm->ScriptAllowed(aEvaluationGlobal), NS_OK);
|
||||
|
||||
mozilla::Maybe<AutoDontReportUncaught> dontReport;
|
||||
if (!aEvaluateOptions.reportUncaught) {
|
||||
// We need to prevent AutoLastFrameCheck from reporting and clearing
|
||||
// any pending exceptions.
|
||||
dontReport.emplace(aCx);
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
// Scope the JSAutoCompartment so that we can later wrap the return value
|
||||
// into the caller's cx.
|
||||
@@ -275,24 +232,14 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
if (aEvaluateOptions.reportUncaught) {
|
||||
ReportPendingException(aCx);
|
||||
if (!aCompileOptions.noScriptRval) {
|
||||
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);
|
||||
MOZ_ASSERT(!aCompileOptions.noScriptRval); // we asserted this on entry
|
||||
aRetValue.set(exn);
|
||||
JS_ClearPendingException(aCx);
|
||||
rv = NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
|
||||
if (!aCompileOptions.noScriptRval) {
|
||||
aRetValue.setUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the return value into whatever compartment aCx was in.
|
||||
if (!aCompileOptions.noScriptRval) {
|
||||
if (ok && !aCompileOptions.noScriptRval) {
|
||||
if (!JS_WrapValue(aCx, aRetValue)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
+3
-15
@@ -52,13 +52,6 @@ public:
|
||||
*/
|
||||
static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);
|
||||
|
||||
/**
|
||||
* Report a pending exception on aContext, if any. Note that this
|
||||
* can be called when the context has a JS stack. If that's the
|
||||
* case, the stack will be set aside before reporting the exception.
|
||||
*/
|
||||
static void ReportPendingException(JSContext *aContext);
|
||||
|
||||
static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
|
||||
JS::AutoObjectVector& aScopeChain,
|
||||
JS::CompileOptions& aOptions,
|
||||
@@ -70,12 +63,10 @@ public:
|
||||
|
||||
struct MOZ_STACK_CLASS EvaluateOptions {
|
||||
bool coerceToString;
|
||||
bool reportUncaught;
|
||||
JS::AutoObjectVector scopeChain;
|
||||
|
||||
explicit EvaluateOptions(JSContext* cx)
|
||||
: coerceToString(false)
|
||||
, reportUncaught(true)
|
||||
, scopeChain(cx)
|
||||
{}
|
||||
|
||||
@@ -83,16 +74,13 @@ public:
|
||||
coerceToString = aCoerce;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EvaluateOptions& setReportUncaught(bool aReport) {
|
||||
reportUncaught = aReport;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// aEvaluationGlobal is the global to evaluate in. The return value
|
||||
// will then be wrapped back into the compartment aCx is in when
|
||||
// this function is called.
|
||||
// this function is called. For all the EvaluateString overloads,
|
||||
// the JSContext must come from an AutoJSAPI that has had
|
||||
// TakeOwnershipOfErrorReporting() called on it.
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
|
||||
@@ -1138,6 +1138,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
|
||||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
|
||||
AutoEntryScript entryScript(globalObject, true, context->GetNativeContext());
|
||||
entryScript.TakeOwnershipOfErrorReporting();
|
||||
JS::Rooted<JSObject*> global(entryScript.cx(),
|
||||
globalObject->GetGlobalJSObject());
|
||||
|
||||
|
||||
@@ -180,12 +180,8 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
||||
// And now we're ready to go.
|
||||
mCx = cx;
|
||||
|
||||
// Make sure the JS engine doesn't report exceptions we want to re-throw
|
||||
if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
|
||||
mExceptionHandling == eRethrowExceptions) {
|
||||
mSavedJSContextOptions = JS::ContextOptionsRef(cx);
|
||||
JS::ContextOptionsRef(cx).setDontReportUncaught(true);
|
||||
}
|
||||
// Make sure the JS engine doesn't report exceptions we want to re-throw.
|
||||
mAutoEntryScript->TakeOwnershipOfErrorReporting();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -251,18 +247,18 @@ CallbackObject::CallSetup::~CallSetup()
|
||||
// Now, if we have a JSContext, report any pending errors on it, unless we
|
||||
// were told to re-throw them.
|
||||
if (mCx) {
|
||||
bool needToDealWithException = JS_IsExceptionPending(mCx);
|
||||
bool needToDealWithException = mAutoEntryScript->HasException();
|
||||
if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
|
||||
mExceptionHandling == eRethrowExceptions) {
|
||||
// Restore the old context options
|
||||
JS::ContextOptionsRef(mCx) = mSavedJSContextOptions;
|
||||
mErrorResult.MightThrowJSException();
|
||||
MOZ_ASSERT(mAutoEntryScript->OwnsErrorReporting());
|
||||
if (needToDealWithException) {
|
||||
JS::Rooted<JS::Value> exn(mCx);
|
||||
if (JS_GetPendingException(mCx, &exn) &&
|
||||
if (mAutoEntryScript->PeekException(&exn) &&
|
||||
ShouldRethrowException(exn)) {
|
||||
mAutoEntryScript->ClearException();
|
||||
MOZ_ASSERT(!mAutoEntryScript->HasException());
|
||||
mErrorResult.ThrowJSException(mCx, exn);
|
||||
JS_ClearPendingException(mCx);
|
||||
needToDealWithException = false;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +266,7 @@ CallbackObject::CallSetup::~CallSetup()
|
||||
|
||||
if (needToDealWithException) {
|
||||
// Either we're supposed to report our exceptions, or we're supposed to
|
||||
// re-throw them but we failed to JS_GetPendingException. Either way,
|
||||
// re-throw them but we failed to get the exception value. Either way,
|
||||
// just report the pending exception, if any.
|
||||
//
|
||||
// We don't use nsJSUtils::ReportPendingException here because all it
|
||||
@@ -281,7 +277,9 @@ CallbackObject::CallSetup::~CallSetup()
|
||||
// screw up our compartment, which is exactly what we do not want.
|
||||
//
|
||||
// XXXbz FIXME: bug 979525 means we don't always JS_SaveFrameChain here,
|
||||
// so we need to go ahead and do that.
|
||||
// so we need to go ahead and do that. This is also the reason we don't
|
||||
// just rely on ~AutoJSAPI reporting the exception for us. I think if we
|
||||
// didn't need to JS_SaveFrameChain here, we could just rely on that.
|
||||
JS::Rooted<JSObject*> oldGlobal(mCx, JS::CurrentGlobalOrNull(mCx));
|
||||
MOZ_ASSERT(oldGlobal, "How can we not have a global here??");
|
||||
bool saved = JS_SaveFrameChain(mCx);
|
||||
@@ -292,7 +290,10 @@ CallbackObject::CallSetup::~CallSetup()
|
||||
MOZ_ASSERT(!JS::DescribeScriptedCaller(mCx),
|
||||
"Our comment above about JS_SaveFrameChain having been "
|
||||
"called is a lie?");
|
||||
JS_ReportPendingException(mCx);
|
||||
// Note that we don't JS_ReportPendingException here because we want to
|
||||
// go through our AutoEntryScript's reporting mechanism instead, since
|
||||
// it currently owns error reporting.
|
||||
mAutoEntryScript->ReportException();
|
||||
}
|
||||
if (saved) {
|
||||
JS_RestoreFrameChain(mCx);
|
||||
|
||||
@@ -218,7 +218,6 @@ protected:
|
||||
// we should re-throw them.
|
||||
ErrorResult& mErrorResult;
|
||||
const ExceptionHandling mExceptionHandling;
|
||||
JS::ContextOptions mSavedJSContextOptions;
|
||||
const bool mIsMainThread;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -59,3 +59,4 @@ skip-if = debug == false
|
||||
skip-if = debug == false
|
||||
[test_worker_UnwrapArg.html]
|
||||
[test_crossOriginWindowSymbolAccess.html]
|
||||
[test_callback_exceptions.html]
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for ...</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
promise_test(function(t) {
|
||||
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, JSON.parse);
|
||||
return promise_rejects(t, new SyntaxError,
|
||||
Promise.resolve().then(iterator.nextNode.bind(iterator)));
|
||||
}, "Trying to use JSON.parse as filter should throw a catchable SyntaxError exception even when the filter is invoked async");
|
||||
|
||||
promise_test(function(t) {
|
||||
return promise_rejects(t, new SyntaxError, Promise.resolve('{').then(JSON.parse));
|
||||
}, "Trying to use JSON.parse as a promise callback should allow the next promise to handle the resulting exception.");
|
||||
</script>
|
||||
@@ -249,6 +249,10 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
|
||||
AutoEntryScript entryScript(innerGlobal, true,
|
||||
scriptContext->GetNativeContext());
|
||||
// We want to make sure we report any exceptions that happen before we
|
||||
// return, since whatever happens inside our execution shouldn't affect any
|
||||
// other scripts that might happen to be running.
|
||||
entryScript.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = entryScript.cx();
|
||||
JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
|
||||
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
|
||||
@@ -276,20 +280,13 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
||||
rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
|
||||
globalJSObject, options, evalOptions, &v);
|
||||
|
||||
// If there's an error on cx as a result of that call, report
|
||||
// it now -- either we're just running under the event loop,
|
||||
// so we shouldn't propagate JS exceptions out of here, or we
|
||||
// can't be sure that our caller is JS (and if it's not we'll
|
||||
// lose the error), or it might be JS that then proceeds to
|
||||
// cause an error of its own (which will also make us lose
|
||||
// this error).
|
||||
::JS_ReportPendingException(cx);
|
||||
|
||||
if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
|
||||
return NS_ERROR_MALFORMED_URI;
|
||||
} else if (v.isUndefined()) {
|
||||
return NS_ERROR_DOM_RETVAL_UNDEFINED;
|
||||
} else {
|
||||
MOZ_ASSERT(rv != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW,
|
||||
"How did we get a non-undefined return value?");
|
||||
nsAutoJSString result;
|
||||
if (!result.init(cx, v)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
@@ -1508,6 +1508,7 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
|
||||
}
|
||||
|
||||
dom::AutoEntryScript aes(win);
|
||||
aes.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
|
||||
|
||||
@@ -52,8 +52,6 @@ interface ExceptionMembers
|
||||
// this was only ever usefully available to chrome JS.
|
||||
[ChromeOnly]
|
||||
readonly attribute StackFrame? location;
|
||||
// An inner exception that triggered this, if available.
|
||||
readonly attribute nsISupports? inner;
|
||||
|
||||
// Arbitary data for the implementation.
|
||||
readonly attribute nsISupports? data;
|
||||
|
||||
@@ -405,6 +405,7 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
|
||||
// We are going to run script via EvaluateString, so we need a script entry
|
||||
// point, but as this is XBL related it does not appear in the HTML spec.
|
||||
AutoEntryScript entryScript(globalObject, true);
|
||||
entryScript.TakeOwnershipOfErrorReporting();
|
||||
JSContext* cx = entryScript.cx();
|
||||
|
||||
NS_ASSERTION(!::JS_IsExceptionPending(cx),
|
||||
@@ -440,6 +441,12 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW) {
|
||||
// Report the exception now, before we try using the JSContext for
|
||||
// the JS_DefineUCProperty call.
|
||||
entryScript.ReportException();
|
||||
}
|
||||
|
||||
// Now, enter the node's compartment, wrap the eval result, and define it on
|
||||
// the bound node.
|
||||
JSAutoCompartment ac2(cx, aBoundNode);
|
||||
|
||||
@@ -229,6 +229,14 @@ ReportError(JSContext* cx, const char* message, JSErrorReport* reportp,
|
||||
if (js_ErrorToException(cx, message, reportp, callback, userRef)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The AutoJSAPI error reporter only allows warnings to be reported so
|
||||
* just ignore this error rather than try to report it.
|
||||
*/
|
||||
if (cx->options().autoJSAPIOwnsErrorReporting() && !JSREPORT_IS_WARNING(reportp->flags))
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -607,6 +607,12 @@ js_ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp,
|
||||
static bool
|
||||
IsDuckTypedErrorObject(JSContext* cx, HandleObject exnObject, const char** filename_strp)
|
||||
{
|
||||
/*
|
||||
* This function is called from ErrorReport::init and so should not generate
|
||||
* any new exceptions.
|
||||
*/
|
||||
AutoClearPendingException acpe(cx);
|
||||
|
||||
bool found;
|
||||
if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found)
|
||||
return false;
|
||||
|
||||
@@ -116,4 +116,18 @@ ExnTypeFromProtoKey(JSProtoKey key)
|
||||
return type;
|
||||
}
|
||||
|
||||
class AutoClearPendingException
|
||||
{
|
||||
JSContext *cx;
|
||||
|
||||
public:
|
||||
explicit AutoClearPendingException(JSContext *cxArg)
|
||||
: cx(cxArg)
|
||||
{ }
|
||||
|
||||
~AutoClearPendingException() {
|
||||
cx->clearPendingException();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* jsexn_h */
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIException.idl"
|
||||
|
||||
[scriptable, uuid(dd250248-2586-4ec1-a68f-8d14ef452517)]
|
||||
[scriptable, uuid(875e6645-e762-4da6-9ec8-bf19ab0050df)]
|
||||
interface nsIXPCException : nsIException
|
||||
{
|
||||
// inherits methods from nsIException
|
||||
@@ -16,8 +16,7 @@ interface nsIXPCException : nsIException
|
||||
in nsresult aResult,
|
||||
in AUTF8String aName,
|
||||
in nsIStackFrame aLocation,
|
||||
in nsISupports aData,
|
||||
in nsIException aInner);
|
||||
in nsISupports aData);
|
||||
};
|
||||
|
||||
/* this goes into the C++ header verbatim. */
|
||||
|
||||
@@ -529,6 +529,12 @@
|
||||
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
||||
*/
|
||||
ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
|
||||
|
||||
/*
|
||||
* A success code that indicates that evaluating a string of JS went
|
||||
* just fine except it threw an exception.
|
||||
*/
|
||||
ERROR(NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW, SUCCESS(2)),
|
||||
#undef MODULE
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
[ptr] native JSContext(JSContext);
|
||||
|
||||
[scriptable, uuid(4ed5cd87-401a-425a-8d8d-c28fbc1e88b2)]
|
||||
[scriptable, uuid(4371b5bf-6845-487f-8d9d-3f1e4a9badd2)]
|
||||
interface nsIStackFrame : nsISupports
|
||||
{
|
||||
// see nsIProgrammingLanguage for list of language consts
|
||||
@@ -69,8 +69,6 @@ interface nsIException : nsISupports
|
||||
|
||||
// A stack trace, if available.
|
||||
readonly attribute nsIStackFrame location;
|
||||
// An inner exception that triggered this, if available.
|
||||
readonly attribute nsIException inner;
|
||||
|
||||
// Arbitary data for the implementation.
|
||||
readonly attribute nsISupports data;
|
||||
|
||||
Reference in New Issue
Block a user