mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1070842 - Introduce an API on AutoJSAPI to allow Gecko to handle exceptions without meddling from the JS engine. r=bz
This commit is contained in:
parent
a56064b5df
commit
a16cec5899
@ -234,9 +234,53 @@ FindJSContext(nsIGlobalObject* aGlobalObject)
|
||||
|
||||
AutoJSAPI::AutoJSAPI()
|
||||
: mCx(nullptr)
|
||||
, mOwnErrorReporting(false)
|
||||
, mOldDontReportUncaught(false)
|
||||
{
|
||||
}
|
||||
|
||||
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(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
|
||||
{
|
||||
@ -253,11 +297,19 @@ AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
|
||||
} else {
|
||||
mAutoNullableCompartment.emplace(mCx, aGlobal);
|
||||
}
|
||||
|
||||
if (aIsMainThread) {
|
||||
JSRuntime* rt = JS_GetRuntime(aCx);
|
||||
mOldErrorReporter.emplace(JS_GetErrorReporter(rt));
|
||||
JS_SetErrorReporter(rt, xpc::SystemErrorReporter);
|
||||
}
|
||||
}
|
||||
|
||||
AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
|
||||
bool aIsMainThread,
|
||||
JSContext* aCx)
|
||||
: mOwnErrorReporting(false)
|
||||
, mOldDontReportUncaught(false)
|
||||
{
|
||||
MOZ_ASSERT(aGlobalObject);
|
||||
MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
|
||||
@ -352,6 +404,49 @@ AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow)
|
||||
return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow));
|
||||
}
|
||||
|
||||
// Even with dontReportUncaught, the JS engine still sends warning reports
|
||||
// to the JSErrorReporter as soon as they are generated. These go directly to
|
||||
// the console, so we can handle them easily here.
|
||||
//
|
||||
// Eventually, SpiderMonkey will have a special-purpose callback for warnings only.
|
||||
void
|
||||
WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep)
|
||||
{
|
||||
MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
|
||||
nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
|
||||
const char* category = nsContentUtils::IsCallerChrome() ? "chrome javascript"
|
||||
: "content javascript";
|
||||
nsPIDOMWindow* win = xpc::WindowGlobalOrNull(JS::CurrentGlobalOrNull(aCx));
|
||||
xpcReport->Init(aRep, aMessage, category, win ? win->WindowID() : 0);
|
||||
xpcReport->LogToConsole();
|
||||
}
|
||||
|
||||
void
|
||||
AutoJSAPI::TakeOwnershipOfErrorReporting()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Can't own error reporting off-main-thread yet");
|
||||
MOZ_ASSERT(!mOwnErrorReporting);
|
||||
mOwnErrorReporting = true;
|
||||
|
||||
JSRuntime *rt = JS_GetRuntime(cx());
|
||||
mOldDontReportUncaught = JS::ContextOptionsRef(cx()).dontReportUncaught();
|
||||
JS::ContextOptionsRef(cx()).setDontReportUncaught(true);
|
||||
JS_SetErrorReporter(rt, WarningOnlyErrorReporter);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
||||
bool aIsMainThread,
|
||||
JSContext* aCx)
|
||||
|
@ -203,6 +203,8 @@ public:
|
||||
// accessing the JSContext through cx().
|
||||
AutoJSAPI();
|
||||
|
||||
~AutoJSAPI();
|
||||
|
||||
// This uses the SafeJSContext (or worker equivalent), and enters a null
|
||||
// compartment, so that the consumer is forced to select a compartment to
|
||||
// enter before manipulating objects.
|
||||
@ -253,6 +255,31 @@ public:
|
||||
|
||||
bool CxPusherIsStackTop() const { return mCxPusher->IsStackTop(); }
|
||||
|
||||
// We're moving towards a world where the AutoJSAPI always handles
|
||||
// exceptions that bubble up from the JS engine. In order to make this
|
||||
// process incremental, we allow consumers to opt-in to the new behavior
|
||||
// while keeping the old behavior as the default.
|
||||
void TakeOwnershipOfErrorReporting();
|
||||
bool OwnsErrorReporting() { return mOwnErrorReporting; }
|
||||
|
||||
bool HasException() const {
|
||||
MOZ_ASSERT(CxPusherIsStackTop());
|
||||
return JS_IsExceptionPending(cx());
|
||||
};
|
||||
|
||||
// Transfers ownership of the current exception from the JS engine to the
|
||||
// caller. 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 StealException(JS::MutableHandle<JS::Value> aVal);
|
||||
|
||||
void ClearException() {
|
||||
MOZ_ASSERT(CxPusherIsStackTop());
|
||||
JS_ClearPendingException(cx());
|
||||
}
|
||||
|
||||
protected:
|
||||
// Protected constructor, allowing subclasses to specify a particular cx to
|
||||
// be used. This constructor initialises the AutoJSAPI, so Init must NOT be
|
||||
@ -266,6 +293,11 @@ private:
|
||||
mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
|
||||
JSContext *mCx;
|
||||
|
||||
// Track state between the old and new error reporting modes.
|
||||
bool mOwnErrorReporting;
|
||||
bool mOldDontReportUncaught;
|
||||
Maybe<JSErrorReporter> mOldErrorReporter;
|
||||
|
||||
void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user