mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changesets c674dc13ef85 and 0b3a14bbfd81 (bug 995295) for mochitest-2 crashes.
CLOSED TREE
This commit is contained in:
parent
019398a53c
commit
4fef62b59a
@ -7,8 +7,6 @@
|
||||
#include "mozilla/dom/ConsoleBinding.h"
|
||||
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDocument.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
@ -18,7 +16,6 @@
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
#include "nsIConsoleAPIStorage.h"
|
||||
#include "nsIDOMWindowUtils.h"
|
||||
@ -153,9 +150,10 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
Initialize(Console::MethodName aName,
|
||||
Initialize(JSContext* aCx, Console::MethodName aName,
|
||||
const nsAString& aString, const Sequence<JS::Value>& aArguments)
|
||||
{
|
||||
mGlobal = JS::CurrentGlobalOrNull(aCx);
|
||||
mMethodName = aName;
|
||||
mMethodString = aString;
|
||||
|
||||
@ -164,6 +162,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
JS::Heap<JSObject*> mGlobal;
|
||||
|
||||
Console::MethodName mMethodName;
|
||||
bool mPrivate;
|
||||
int64_t mTimeStamp;
|
||||
@ -171,16 +171,7 @@ public:
|
||||
|
||||
nsString mMethodString;
|
||||
nsTArray<JS::Heap<JS::Value>> mArguments;
|
||||
|
||||
// Stack management is complicated, because we want to do it as
|
||||
// lazily as possible. Therefore, we have the following behavior:
|
||||
// 1) mTopStackFrame is initialized whenever we have any JS on the stack
|
||||
// 2) mReifiedStack is initialized if we're created in a worker.
|
||||
// 3) mStack is set (possibly to null if there is no JS on the stack) if
|
||||
// we're created on main thread.
|
||||
Maybe<ConsoleStackEntry> mTopStackFrame;
|
||||
Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
|
||||
nsCOMPtr<nsIStackFrame> mStack;
|
||||
Sequence<ConsoleStackEntry> mStack;
|
||||
};
|
||||
|
||||
// This class is used to clear any exception at the end of this method.
|
||||
@ -216,11 +207,13 @@ public:
|
||||
}
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
Dispatch()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!PreDispatch(aCx)) {
|
||||
JSContext* cx = mWorkerPrivate->GetJSContext();
|
||||
|
||||
if (!PreDispatch(cx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -228,7 +221,7 @@ public:
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
||||
JS_ReportError(aCx,
|
||||
JS_ReportError(cx,
|
||||
"Failed to dispatch to main thread for the Console API!");
|
||||
return false;
|
||||
}
|
||||
@ -282,6 +275,7 @@ private:
|
||||
PreDispatch(JSContext* aCx) MOZ_OVERRIDE
|
||||
{
|
||||
ClearException ce(aCx);
|
||||
JSAutoCompartment ac(aCx, mCallData->mGlobal);
|
||||
|
||||
JS::Rooted<JSObject*> arguments(aCx,
|
||||
JS_NewArrayObject(aCx, mCallData->mArguments.Length()));
|
||||
@ -303,6 +297,7 @@ private:
|
||||
}
|
||||
|
||||
mCallData->mArguments.Clear();
|
||||
mCallData->mGlobal = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -357,6 +352,7 @@ private:
|
||||
|
||||
MOZ_ASSERT(mCallData->mArguments.Length() == length);
|
||||
|
||||
mCallData->mGlobal = JS::CurrentGlobalOrNull(cx);
|
||||
console->AppendCallData(mCallData.forget());
|
||||
}
|
||||
|
||||
@ -506,6 +502,10 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
|
||||
|
||||
for (ConsoleCallData* data = tmp->mQueuedCalls.getFirst(); data != nullptr;
|
||||
data = data->getNext()) {
|
||||
if (data->mGlobal) {
|
||||
aCallbacks.Trace(&data->mGlobal, "data->mGlobal", aClosure);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < data->mArguments.Length(); ++i) {
|
||||
aCallbacks.Trace(&data->mArguments[i], "data->mArguments[i]", aClosure);
|
||||
}
|
||||
@ -672,7 +672,7 @@ Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
|
||||
// Here we are in a worker thread.
|
||||
nsRefPtr<ConsoleProfileRunnable> runnable =
|
||||
new ConsoleProfileRunnable(aAction, aData);
|
||||
runnable->Dispatch(aCx);
|
||||
runnable->Dispatch();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -733,58 +733,6 @@ Console::__noSuchMethod__()
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
static
|
||||
nsresult
|
||||
StackFrameToStackEntry(nsIStackFrame* aStackFrame,
|
||||
ConsoleStackEntry& aStackEntry,
|
||||
uint32_t aLanguage)
|
||||
{
|
||||
MOZ_ASSERT(aStackFrame);
|
||||
|
||||
nsresult rv = aStackFrame->GetFilename(aStackEntry.mFilename);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t lineNumber;
|
||||
rv = aStackFrame->GetLineNumber(&lineNumber);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aStackEntry.mLineNumber = lineNumber;
|
||||
|
||||
rv = aStackFrame->GetName(aStackEntry.mFunctionName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aStackEntry.mLanguage = aLanguage;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static
|
||||
nsresult
|
||||
ReifyStack(nsIStackFrame* aStack, nsTArray<ConsoleStackEntry>& aRefiedStack)
|
||||
{
|
||||
nsCOMPtr<nsIStackFrame> stack(aStack);
|
||||
|
||||
while (stack) {
|
||||
uint32_t language;
|
||||
nsresult rv = stack->GetLanguage(&language);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (language == nsIProgrammingLanguage::JAVASCRIPT ||
|
||||
language == nsIProgrammingLanguage::JAVASCRIPT2) {
|
||||
ConsoleStackEntry& data = *aRefiedStack.AppendElement();
|
||||
rv = StackFrameToStackEntry(stack, data, language);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStackFrame> caller;
|
||||
rv = stack->GetCaller(getter_AddRefs(caller));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
stack.swap(caller);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Queue a call to a console method. See the CALL_DELAY constant.
|
||||
void
|
||||
Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
@ -824,7 +772,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
ConsoleCallData* callData = new ConsoleCallData();
|
||||
mQueuedCalls.insertBack(callData);
|
||||
|
||||
callData->Initialize(aMethodName, aMethodString, aData);
|
||||
callData->Initialize(aCx, aMethodName, aMethodString, aData);
|
||||
RAII raii(mQueuedCalls);
|
||||
|
||||
if (mWindow) {
|
||||
@ -849,7 +797,8 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk up to the first JS stack frame and save it if we find it.
|
||||
// nsIStackFrame is not thread-safe so we take what we need and we store in
|
||||
// an array of ConsoleStackEntry objects.
|
||||
do {
|
||||
uint32_t language;
|
||||
nsresult rv = stack->GetLanguage(&language);
|
||||
@ -860,16 +809,30 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
|
||||
if (language == nsIProgrammingLanguage::JAVASCRIPT ||
|
||||
language == nsIProgrammingLanguage::JAVASCRIPT2) {
|
||||
callData->mTopStackFrame.construct();
|
||||
nsresult rv = StackFrameToStackEntry(stack,
|
||||
callData->mTopStackFrame.ref(),
|
||||
language);
|
||||
ConsoleStackEntry& data = *callData->mStack.AppendElement();
|
||||
|
||||
rv = stack->GetFilename(data.mFilename);
|
||||
if (NS_FAILED(rv)) {
|
||||
Throw(aCx, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
int32_t lineNumber;
|
||||
rv = stack->GetLineNumber(&lineNumber);
|
||||
if (NS_FAILED(rv)) {
|
||||
Throw(aCx, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
data.mLineNumber = lineNumber;
|
||||
|
||||
rv = stack->GetName(data.mFunctionName);
|
||||
if (NS_FAILED(rv)) {
|
||||
Throw(aCx, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
data.mLanguage = language;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStackFrame> caller;
|
||||
@ -882,19 +845,6 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
stack.swap(caller);
|
||||
} while (stack);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
callData->mStack = stack;
|
||||
} else {
|
||||
// nsIStackFrame is not threadsafe, so we need to snapshot it now,
|
||||
// before we post our runnable to the main thread.
|
||||
callData->mReifiedStack.construct();
|
||||
nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Throw(aCx, rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Monotonic timer for 'time' and 'timeEnd'
|
||||
if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd) && mWindow) {
|
||||
nsGlobalWindow *win = static_cast<nsGlobalWindow*>(mWindow.get());
|
||||
@ -921,7 +871,7 @@ Console::Method(JSContext* aCx, MethodName aMethodName,
|
||||
|
||||
nsRefPtr<ConsoleCallDataRunnable> runnable =
|
||||
new ConsoleCallDataRunnable(callData);
|
||||
runnable->Dispatch(aCx);
|
||||
runnable->Dispatch();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -968,77 +918,21 @@ Console::Notify(nsITimer *timer)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We store information to lazily compute the stack in the reserved slots of
|
||||
// LazyStackGetter. The first slot always stores a JS object: it's either the
|
||||
// JS wrapper of the nsIStackFrame or the actual reified stack representation.
|
||||
// The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
|
||||
// reified the stack yet, or an UndefinedValue() otherwise.
|
||||
enum {
|
||||
SLOT_STACKOBJ,
|
||||
SLOT_RAW_STACK
|
||||
};
|
||||
|
||||
bool
|
||||
LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
JS::Rooted<JSObject*> callee(aCx, &args.callee());
|
||||
|
||||
JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
|
||||
if (v.isUndefined()) {
|
||||
// Already reified.
|
||||
args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
|
||||
nsTArray<ConsoleStackEntry> reifiedStack;
|
||||
nsresult rv = ReifyStack(stack, reifiedStack);
|
||||
if (NS_FAILED(rv)) {
|
||||
Throw(aCx, rv);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> stackVal(aCx);
|
||||
if (!ToJSValue(aCx, reifiedStack, &stackVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(stackVal.isObject());
|
||||
|
||||
js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
|
||||
js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
|
||||
|
||||
args.rval().set(stackVal);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Console::ProcessCallData(ConsoleCallData* aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ConsoleStackEntry frame;
|
||||
if (!aData->mTopStackFrame.empty()) {
|
||||
frame = aData->mTopStackFrame.ref();
|
||||
if (!aData->mStack.IsEmpty()) {
|
||||
frame = aData->mStack[0];
|
||||
}
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
ClearException ce(cx);
|
||||
RootedDictionary<ConsoleEvent> event(cx);
|
||||
|
||||
// We want to create a console event object and pass it to our
|
||||
// nsIConsoleAPIStorage implementation. We want to define some accessor
|
||||
// properties on this object, and those will need to keep an nsIStackFrame
|
||||
// alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
|
||||
// further, passing untrusted objects to system code is likely to run afoul of
|
||||
// Object Xrays. So we want to wrap in a system-principal scope here. But
|
||||
// which one? We could cheat and try to get the underlying JSObject* of
|
||||
// mStorage, but that's a bit fragile. Instead, we just use the junk scope,
|
||||
// with explicit permission from the XPConnect module owner. If you're
|
||||
// tempted to do that anywhere else, talk to said module owner first.
|
||||
JSAutoCompartment ac(cx, xpc::GetJunkScope());
|
||||
JSAutoCompartment ac(cx, aData->mGlobal);
|
||||
|
||||
event.mID.Construct();
|
||||
event.mInnerID.Construct();
|
||||
@ -1077,9 +971,14 @@ Console::ProcessCallData(ConsoleCallData* aData)
|
||||
ArgumentsToValueList(aData->mArguments, event.mArguments.Value());
|
||||
}
|
||||
|
||||
if (aData->mMethodName == MethodGroup ||
|
||||
aData->mMethodName == MethodGroupCollapsed ||
|
||||
aData->mMethodName == MethodGroupEnd) {
|
||||
if (ShouldIncludeStackrace(aData->mMethodName)) {
|
||||
event.mStacktrace.Construct();
|
||||
event.mStacktrace.Value().SwapElements(aData->mStack);
|
||||
}
|
||||
|
||||
else if (aData->mMethodName == MethodGroup ||
|
||||
aData->mMethodName == MethodGroupCollapsed ||
|
||||
aData->mMethodName == MethodGroupEnd) {
|
||||
ComposeGroupName(cx, aData->mArguments, event.mGroupName);
|
||||
}
|
||||
|
||||
@ -1108,51 +1007,6 @@ Console::ProcessCallData(ConsoleCallData* aData)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldIncludeStackrace(aData->mMethodName)) {
|
||||
// Now define the "stacktrace" property on eventObj. There are two cases
|
||||
// here. Either we came from a worker and have a reified stack, or we want
|
||||
// to define a getter that will lazily reify the stack.
|
||||
if (!aData->mReifiedStack.empty()) {
|
||||
JS::Rooted<JS::Value> stacktrace(cx);
|
||||
if (!ToJSValue(cx, aData->mReifiedStack.ref(), &stacktrace) ||
|
||||
!JS_DefineProperty(cx, eventObj, "stacktrace", stacktrace,
|
||||
JSPROP_ENUMERATE)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
JSFunction* fun = js::NewFunctionWithReserved(cx, LazyStackGetter, 0, 0,
|
||||
eventObj, "stacktrace");
|
||||
if (!fun) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
|
||||
|
||||
// We want to store our stack in the function and have it stay alive. But
|
||||
// we also need sane access to the C++ nsIStackFrame. So store both a JS
|
||||
// wrapper and the raw pointer: the former will keep the latter alive.
|
||||
JS::Rooted<JS::Value> stackVal(cx);
|
||||
nsresult rv = nsContentUtils::WrapNative(cx, aData->mStack,
|
||||
&stackVal);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
|
||||
js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
|
||||
JS::PrivateValue(aData->mStack.get()));
|
||||
|
||||
if (!JS_DefineProperty(cx, eventObj, "stacktrace",
|
||||
JS::UndefinedHandleValue,
|
||||
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
|
||||
JSPROP_SETTER,
|
||||
JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
|
||||
nullptr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mStorage) {
|
||||
mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
|
||||
}
|
||||
|
@ -47,12 +47,7 @@ dictionary ConsoleEvent {
|
||||
sequence<any> styles;
|
||||
|
||||
boolean private = false;
|
||||
// stacktrace is handled via a getter in some cases so we can construct it
|
||||
// lazily. Note that we're not making this whole thing an interface because
|
||||
// consumers expect to see own properties on it, which would mean making the
|
||||
// props unforgeable, which means lots of JSFunction allocations. Maybe we
|
||||
// should fix those consumers, of course....
|
||||
// sequence<ConsoleStackEntry> stacktrace;
|
||||
sequence<ConsoleStackEntry> stacktrace;
|
||||
DOMString groupName = "";
|
||||
any timer = null;
|
||||
any counter = null;
|
||||
|
Loading…
Reference in New Issue
Block a user