Bug 1062077 - Introduce xpc::ErrorReport. r=bz

I've got it! Let's just wrap the JS thing in an XPCOM thing that duplicates
its fields with a different ownership model! We totally need another one of
those, right? And we could even stick it in XPConnect! </sarcasm>

In seriousness - the code to own, format, and display error reports is
currently spread between the JS engine, JSErrorReporters, an async Runnable
abstraction, and elsewhere. We need to condense it somewhere to start chipping
away at this mess.
This commit is contained in:
Bobby Holley 2014-09-08 16:30:10 -07:00
parent 1174c125e2
commit 35d62e8c00
5 changed files with 139 additions and 106 deletions

View File

@ -355,46 +355,6 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
namespace mozilla {
namespace dom {
AsyncErrorReporter::AsyncErrorReporter(JSRuntime* aRuntime,
JSErrorReport* aErrorReport,
const char* aFallbackMessage,
bool aIsChromeError,
nsPIDOMWindow* aWindow)
: mSourceLine(static_cast<const char16_t*>(aErrorReport->uclinebuf))
, mLineNumber(aErrorReport->lineno)
, mColumn(aErrorReport->column)
, mFlags(aErrorReport->flags)
{
if (!aErrorReport->filename) {
mFileName.SetIsVoid(true);
} else {
mFileName.AssignWithConversion(aErrorReport->filename);
}
const char16_t* m = static_cast<const char16_t*>(aErrorReport->ucmessage);
if (m) {
JSFlatString* name = js::GetErrorTypeName(aRuntime, aErrorReport->exnType);
if (name) {
AssignJSFlatString(mErrorMsg, name);
mErrorMsg.AppendLiteral(": ");
}
mErrorMsg.Append(m);
}
if (mErrorMsg.IsEmpty() && aFallbackMessage) {
mErrorMsg.AssignWithConversion(aFallbackMessage);
}
mCategory = aIsChromeError ? NS_LITERAL_CSTRING("chrome javascript") :
NS_LITERAL_CSTRING("content javascript");
mInnerWindowID = 0;
if (aWindow) {
MOZ_ASSERT(aWindow->IsInnerWindow());
mInnerWindowID = aWindow->WindowID();
}
}
void
AsyncErrorReporter::ReportError()
{
@ -404,10 +364,15 @@ AsyncErrorReporter::ReportError()
return;
}
nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName,
mSourceLine, mLineNumber,
mColumn, mFlags, mCategory,
mInnerWindowID);
uint64_t windowID = mReport->mWindow ? mReport->mWindow->WindowID() : 0;
nsresult rv = errorObject->InitWithWindowID(mReport->mErrorMsg,
mReport->mFileName,
mReport->mSourceLine,
mReport->mLineNumber,
mReport->mColumn,
mReport->mFlags,
mReport->Category(),
windowID);
if (NS_FAILED(rv)) {
return;
}
@ -429,34 +394,27 @@ class ScriptErrorEvent : public AsyncErrorReporter
{
public:
ScriptErrorEvent(JSRuntime* aRuntime,
JSErrorReport* aErrorReport,
const char* aFallbackMessage,
xpc::ErrorReport* aReport,
nsIPrincipal* aScriptOriginPrincipal,
nsIPrincipal* aGlobalPrincipal,
nsPIDOMWindow* aWindow,
JS::Handle<JS::Value> aError,
bool aDispatchEvent)
// Pass an empty category, then compute ours
: AsyncErrorReporter(aRuntime, aErrorReport, aFallbackMessage,
nsContentUtils::IsSystemPrincipal(aGlobalPrincipal),
aWindow)
: AsyncErrorReporter(aRuntime, aReport)
, mOriginPrincipal(aScriptOriginPrincipal)
, mDispatchEvent(aDispatchEvent)
, mError(aRuntime, aError)
, mWindow(aWindow)
{
MOZ_ASSERT_IF(mWindow, mWindow->IsInnerWindow());
}
{}
NS_IMETHOD Run()
{
nsEventStatus status = nsEventStatus_eIgnore;
nsPIDOMWindow* win = mReport->mWindow;
// First, notify the DOM that we have a script error, but only if
// our window is still the current inner, if we're associated with a window.
if (mDispatchEvent && (!mWindow || mWindow->IsCurrentInnerWindow())) {
nsIDocShell* docShell = mWindow ? mWindow->GetDocShell() : nullptr;
if (mDispatchEvent && (!win || win->IsCurrentInnerWindow())) {
nsIDocShell* docShell = win ? win->GetDocShell() : nullptr;
if (docShell &&
!JSREPORT_IS_WARNING(mFlags) &&
!JSREPORT_IS_WARNING(mReport->mFlags) &&
!sHandlingScriptError) {
AutoRestore<bool> recursionGuard(sHandlingScriptError);
sHandlingScriptError = true;
@ -467,10 +425,10 @@ public:
ThreadsafeAutoJSContext cx;
RootedDictionary<ErrorEventInit> init(cx);
init.mCancelable = true;
init.mFilename = mFileName;
init.mFilename = mReport->mFileName;
init.mBubbles = true;
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(mWindow));
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(win));
NS_ENSURE_STATE(sop);
nsIPrincipal* p = sop->GetPrincipal();
NS_ENSURE_STATE(p);
@ -485,9 +443,9 @@ public:
NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
if (sameOrigin) {
init.mMessage = mErrorMsg;
init.mLineno = mLineNumber;
init.mColno = mColumn;
init.mMessage = mReport->mErrorMsg;
init.mLineno = mReport->mLineNumber;
init.mColno = mReport->mColumn;
init.mError = mError;
} else {
NS_WARNING("Not same origin error!");
@ -496,11 +454,11 @@ public:
}
nsRefPtr<ErrorEvent> event =
ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(mWindow.get()),
ErrorEvent::Constructor(static_cast<nsGlobalWindow*>(win),
NS_LITERAL_STRING("error"), init);
event->SetTrusted(true);
EventDispatcher::DispatchDOMEvent(mWindow, nullptr, event, presContext,
EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
&status);
}
}
@ -516,7 +474,6 @@ private:
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
bool mDispatchEvent;
JS::PersistentRootedValue mError;
nsCOMPtr<nsPIDOMWindow> mWindow;
static bool sHandlingScriptError;
};
@ -547,20 +504,13 @@ NS_ScriptErrorReporter(JSContext *cx,
}
}
if (globalObject) {
nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
xpcReport->Init(report, message, globalObject);
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
MOZ_ASSERT_IF(win, win->IsInnerWindow());
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
do_QueryInterface(globalObject);
NS_ASSERTION(scriptPrincipal, "Global objects must implement "
"nsIScriptObjectPrincipal");
nsContentUtils::AddScriptRunner(
new ScriptErrorEvent(JS_GetRuntime(cx),
report,
message,
xpcReport,
nsJSPrincipals::get(report->originPrincipals),
scriptPrincipal->GetPrincipal(),
win,
exception,
/* We do not try to report Out Of Memory via a dom
* event because the dom event handler would

View File

@ -14,7 +14,9 @@
#include "nsIXPConnect.h"
#include "nsIArray.h"
#include "mozilla/Attributes.h"
#include "nsPIDOMWindow.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"
class nsICycleCollectorListener;
class nsIXPConnectJSObjectHolder;
@ -189,11 +191,9 @@ class AsyncErrorReporter : public nsRunnable
{
public:
// aWindow may be null if this error report is not associated with a window
AsyncErrorReporter(JSRuntime* aRuntime,
JSErrorReport* aErrorReport,
const char* aFallbackMessage,
bool aIsChromeError, // To determine category
nsPIDOMWindow* aWindow);
AsyncErrorReporter(JSRuntime* aRuntime, xpc::ErrorReport* aReport)
: mReport(aReport)
{}
NS_IMETHOD Run()
{
@ -205,14 +205,7 @@ protected:
// Do the actual error reporting
void ReportError();
nsString mErrorMsg;
nsString mFileName;
nsString mSourceLine;
nsCString mCategory;
uint32_t mLineNumber;
uint32_t mColumn;
uint32_t mFlags;
uint64_t mInnerWindowID;
nsRefPtr<xpc::ErrorReport> mReport;
};
} // namespace dom

View File

@ -1005,19 +1005,13 @@ Promise::MaybeReportRejected()
return;
}
// Remains null in case of worker.
nsCOMPtr<nsPIDOMWindow> win;
bool isChromeError = false;
nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
if (MOZ_LIKELY(NS_IsMainThread())) {
nsIPrincipal* principal;
win = xpc::WindowGlobalOrNull(obj);
principal = nsContentUtils::ObjectPrincipal(obj);
isChromeError = nsContentUtils::IsSystemPrincipal(principal);
nsIGlobalObject* global = xpc::GetNativeForGlobal(js::GetGlobalForObjectCrossCompartment(obj));
xpcReport->Init(report.report(), report.message(), global);
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
isChromeError = worker->IsChromeWorker();
xpcReport->InitOnWorkerThread(report.report(), report.message(),
GetCurrentThreadWorkerPrivate()->IsChromeWorker());
}
// Now post an event to do the real reporting async
@ -1025,11 +1019,7 @@ Promise::MaybeReportRejected()
// AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
// will leak. See Bug 958684.
nsRefPtr<AsyncErrorReporter> r =
new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(),
report.report(),
report.message(),
isChromeError,
win);
new AsyncErrorReporter(CycleCollectedJSRuntime::Get()->Runtime(), xpcReport);
NS_DispatchToMainThread(r);
}

View File

@ -237,6 +237,65 @@ xpc::SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
}
void
xpc::ErrorReport::Init(JSErrorReport *aReport,
const char *aFallbackMessage,
nsIGlobalObject *aGlobal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aGlobal);
mGlobal = aGlobal;
mWindow = do_QueryInterface(mGlobal);
MOZ_ASSERT_IF(mWindow, mWindow->IsInnerWindow());
nsIPrincipal *prin = mGlobal->PrincipalOrNull();
mIsChrome = nsContentUtils::IsSystemPrincipal(prin);
InitInternal(aReport, aFallbackMessage);
}
void
xpc::ErrorReport::InitOnWorkerThread(JSErrorReport *aReport,
const char *aFallbackMessage,
bool aIsChrome)
{
MOZ_ASSERT(!NS_IsMainThread());
mIsChrome = aIsChrome;
InitInternal(aReport, aFallbackMessage);
}
void
xpc::ErrorReport::InitInternal(JSErrorReport *aReport,
const char *aFallbackMessage)
{
const char16_t* m = static_cast<const char16_t*>(aReport->ucmessage);
if (m) {
JSFlatString* name = js::GetErrorTypeName(CycleCollectedJSRuntime::Get()->Runtime(), aReport->exnType);
if (name) {
AssignJSFlatString(mErrorMsg, name);
mErrorMsg.AppendLiteral(": ");
}
mErrorMsg.Append(m);
}
if (mErrorMsg.IsEmpty() && aFallbackMessage) {
mErrorMsg.AssignWithConversion(aFallbackMessage);
}
if (!aReport->filename) {
mFileName.SetIsVoid(true);
} else {
mFileName.AssignWithConversion(aReport->filename);
}
mSourceLine = static_cast<const char16_t*>(aReport->uclinebuf);
mLineNumber = aReport->lineno;
mColumn = aReport->column;
mFlags = aReport->flags;
}
/***************************************************************************/

View File

@ -15,6 +15,8 @@
#include "nsISupports.h"
#include "nsIURI.h"
#include "nsIPrincipal.h"
#include "nsIGlobalObject.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
@ -26,7 +28,6 @@
class nsGlobalWindow;
class nsIPrincipal;
class nsScriptNameSpaceManager;
class nsIGlobalObject;
class nsIMemoryReporterCallback;
#ifndef BAD_TLS_INDEX
@ -490,6 +491,46 @@ SetAddonInterposition(const nsACString &addonId, nsIAddonInterposition *interpos
bool
ExtraWarningsForSystemJS();
class ErrorReport {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport);
ErrorReport() : mIsChrome(false)
, mLineNumber(0)
, mColumn(0)
, mFlags(0)
{}
void Init(JSErrorReport *aReport, const char *aFallbackMessage,
nsIGlobalObject *aGlobal);
void InitOnWorkerThread(JSErrorReport *aReport, const char *aFallbackMessage,
bool aIsChrome);
private:
void InitInternal(JSErrorReport *aReport, const char *aFallbackMessage);
bool mIsChrome;
public:
const nsCString Category() {
return mIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
: NS_LITERAL_CSTRING("content javascript");
}
nsString mErrorMsg;
nsString mFileName;
nsString mSourceLine;
uint32_t mLineNumber;
uint32_t mColumn;
uint32_t mFlags;
// These are both null for ErrorReports initialized on a worker thread.
nsCOMPtr<nsIGlobalObject> mGlobal;
nsCOMPtr<nsPIDOMWindow> mWindow;
private:
~ErrorReport() {}
};
} // namespace xpc
namespace mozilla {