/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC // Exceptions, and arbitrary values that were associated with a stack by the // JS engine when they were thrown, as specified by exceptionStack. // // Note that the returned stackObj and stackGlobal are _not_ wrapped into the // compartment of exceptionValue. void FindExceptionStackForConsoleReport(
nsPIDOMWindowInner* win, JS::Handle<JS::Value> exceptionValue,
JS::Handle<JSObject*> exceptionStack, JS::MutableHandle<JSObject*> stackObj,
JS::MutableHandle<JSObject*> stackGlobal) {
stackObj.set(nullptr);
stackGlobal.set(nullptr);
if (!exceptionValue.isObject()) { // Use the stack provided by the JS engine, if available. This will not be // a wrapper. if (exceptionStack) {
stackObj.set(exceptionStack);
stackGlobal.set(JS::GetNonCCWObjectGlobal(exceptionStack));
} return;
}
if (win && win->AsGlobal()->IsDying()) { // Pretend like we have no stack, so we don't end up keeping the global // alive via the stack. return;
}
JS::RootingContext* rcx = RootingCx();
JS::Rooted<JSObject*> exceptionObject(rcx, &exceptionValue.toObject()); if (JSObject* excStack = JS::ExceptionStackOrNull(exceptionObject)) { // At this point we know exceptionObject is a possibly-wrapped // js::ErrorObject that has excStack as stack. excStack might also be a CCW, // but excStack must be same-compartment with the unwrapped ErrorObject. // Return the ErrorObject's global as stackGlobal. This matches what we do // in the ErrorObject's |.stack| getter and ensures stackObj and stackGlobal // are same-compartment.
JSObject* unwrappedException = js::UncheckedUnwrap(exceptionObject);
stackObj.set(excStack);
stackGlobal.set(JS::GetNonCCWObjectGlobal(unwrappedException)); return;
}
// It is not a JS Exception, try DOM Exception.
RefPtr<Exception> exception;
UNWRAP_OBJECT(DOMException, exceptionObject, exception); if (!exception) { // Not a DOM Exception, try XPC Exception.
UNWRAP_OBJECT(Exception, exceptionObject, exception); if (!exception) { // As above, use the stack provided by the JS engine, if available. if (exceptionStack) {
stackObj.set(exceptionStack);
stackGlobal.set(JS::GetNonCCWObjectGlobal(exceptionStack));
} return;
}
}
// A utility function for script languages to call. Although it looks small, // the use of nsIDocShell and nsPresContext triggers a huge number of // dependencies that most languages would not otherwise need. // XXXmarkh - This function is mis-placed! bool NS_HandleScriptError(nsIScriptGlobalObject* aScriptGlobal, const ErrorEventInit& aErrorEventInit,
nsEventStatus* aStatus) { bool called = false;
nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobal));
nsIDocShell* docShell = win ? win->GetDocShell() : nullptr; if (docShell) {
RefPtr<nsPresContext> presContext = docShell->GetPresContext();
if (errorDepth < 2) { // Dispatch() must be synchronous for the recursion block // (errorDepth) to work.
RefPtr<ErrorEvent> event = ErrorEvent::Constructor(
nsGlobalWindowInner::Cast(win), u"error"_ns, aErrorEventInit);
event->SetTrusted(true);
// MOZ_KnownLive due to bug 1506441
EventDispatcher::DispatchDOMEvent(
MOZ_KnownLive(nsGlobalWindowInner::Cast(win)), nullptr, event,
presContext, aStatus);
called = true;
}
--errorDepth;
} return called;
}
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
nsEventStatus status = nsEventStatus_eIgnore;
nsCOMPtr<nsPIDOMWindowInner> win = mWindow;
MOZ_ASSERT(win);
MOZ_ASSERT(NS_IsMainThread()); // First, notify the DOM that we have a script error, but only if // our window is still the current inner.
JS::RootingContext* rootingCx = RootingCx(); if (win->IsCurrentInnerWindow() && win->GetDocShell() &&
!sHandlingScriptError) {
AutoRestore<bool> recursionGuard(sHandlingScriptError);
sHandlingScriptError = true;
#ifdef DEBUG // A couple of useful functions to call when you're debugging.
nsGlobalWindowInner* JSObject2Win(JSObject* obj) { return xpc::WindowOrNull(obj);
}
nsIPrincipal* prin = win->GetPrincipal(); if (!prin) {
printf("Window doesn't have principals.\n"); return;
} if (prin->IsSystemPrincipal()) {
printf("No URI, it's the system principal.\n"); return;
}
nsCString spec;
prin->GetAsciiSpec(spec);
printf("%s\n", spec.get());
}
const char16_t* cp;
char16_t c; for (cp = name; *cp != '\0'; ++cp) {
c = *cp; if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) returnfalse;
}
returntrue;
} #endif
nsIScriptGlobalObject* nsJSContext::GetGlobalObject() { // Note: this could probably be simplified somewhat more; see bug 974327 // comments 1 and 3. if (!mWindowProxy) { return nullptr;
}
if (!aArgs) return NS_OK;
uint32_t argCount; // This general purpose function may need to convert an arg array // (window.arguments, event-handler args) and a generic property.
nsCOMPtr<nsIArray> argsArray(do_QueryInterface(aArgs));
if (argsArray) {
rv = argsArray->GetLength(&argCount);
NS_ENSURE_SUCCESS(rv, rv); if (argCount == 0) return NS_OK;
} else {
argCount = 1; // the nsISupports which is not an array
}
// Use the caller's auto guards to release and unroot. if (!aArgsOut.resize(argCount)) { return NS_ERROR_OUT_OF_MEMORY;
}
if (argsArray) { for (uint32_t argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
nsCOMPtr<nsISupports> arg;
JS::MutableHandle<JS::Value> thisVal = aArgsOut[argCtr];
argsArray->QueryElementAt(argCtr, NS_GET_IID(nsISupports),
getter_AddRefs(arg)); if (!arg) {
thisVal.setNull(); continue;
}
nsCOMPtr<nsIVariant> variant(do_QueryInterface(arg)); if (variant != nullptr) {
rv = xpc->VariantToJS(aCx, aScope, variant, thisVal);
} else { // And finally, support the nsISupportsPrimitives supplied // by the AppShell. It generally will pass only strings, but // as we have code for handling all, we may as well use it.
rv = AddSupportsPrimitiveTojsvals(aCx, arg, thisVal.address()); if (rv == NS_ERROR_NO_INTERFACE) { // something else - probably an event object or similar - // just wrap it. #ifdef DEBUG // but first, check its not another nsISupportsPrimitive, as // these are now deprecated for use with script contexts.
nsCOMPtr<nsISupportsPrimitive> prim(do_QueryInterface(arg));
NS_ASSERTION(prim == nullptr, "Don't pass nsISupportsPrimitives - use nsIVariant!"); #endif
JSAutoRealm ar(aCx, aScope);
rv = nsContentUtils::WrapNative(aCx, arg, thisVal);
}
}
}
} else {
nsCOMPtr<nsIVariant> variant = do_QueryInterface(aArgs); if (variant) {
rv = xpc->VariantToJS(aCx, aScope, variant, aArgsOut[0]);
} else {
NS_ERROR("Not an array, not an interface?");
rv = NS_ERROR_UNEXPECTED;
}
} return rv;
}
// This really should go into xpconnect somewhere...
nsresult nsJSContext::AddSupportsPrimitiveTojsvals(JSContext* aCx,
nsISupports* aArg,
JS::Value* aArgv) {
MOZ_ASSERT(aArg, "Empty arg");
nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg)); if (!argPrimitive) return NS_ERROR_NO_INTERFACE;
uint16_t type;
argPrimitive->GetType(&type);
switch (type) { case nsISupportsPrimitive::TYPE_CSTRING: {
nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
break;
} case nsISupportsPrimitive::TYPE_STRING: {
nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
nsAutoString data;
p->GetData(data);
// cast is probably safe since wchar_t and char16_t are expected // to be equivalent; both unsigned 16-bit entities
JSString* str = ::JS_NewUCStringCopyN(aCx, data.get(), data.Length());
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
aArgv->setString(str); break;
} case nsISupportsPrimitive::TYPE_PRBOOL: {
nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
bool data;
p->GetData(&data);
aArgv->setBoolean(data);
break;
} case nsISupportsPrimitive::TYPE_PRUINT8: {
nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
uint8_t data;
p->GetData(&data);
aArgv->setInt32(data);
break;
} case nsISupportsPrimitive::TYPE_PRUINT16: {
nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
uint16_t data;
p->GetData(&data);
aArgv->setInt32(data);
break;
} case nsISupportsPrimitive::TYPE_PRUINT32: {
nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
uint32_t data;
p->GetData(&data);
aArgv->setInt32(data);
break;
} case nsISupportsPrimitive::TYPE_CHAR: {
nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
// We use danger::GetJSContext() since AutoJSAPI will assert if the current // thread's context is null (such as during shutdown).
JSContext* cx = danger::GetJSContext();
if (!nsContentUtils::XPConnect() || !cx) { return;
}
if (sScheduler->InIncrementalGC() && wantIncremental) { // We're in the middle of incremental GC. Do another slice.
JS::PrepareForIncrementalGC(cx);
JS::IncrementalGCSlice(cx, aReason, aBudget); return;
}
if (!wantIncremental || aReason == JS::GCReason::FULL_GC_TIMER) {
sScheduler->SetNeedsFullGC();
}
if (sScheduler->NeedsFullGC()) {
JS::PrepareForFullGC(cx);
}
if (wantIncremental) { // Incremental GC slices will be triggered by the GC Runner. If one doesn't // already exist, create it in the GC_SLICE_END callback for the first // slice being executed here.
JS::StartIncrementalGC(cx, options, aReason, aBudget);
} else {
JS::NonIncrementalGC(cx, options, aReason);
}
}
if (sScheduler->InIncrementalGC()) {
AutoJSAPI jsapi;
jsapi.Init();
// We're in the middle of an incremental GC, so finish it.
JS::PrepareForIncrementalGC(jsapi.cx());
JS::FinishIncrementalGC(jsapi.cx(), JS::GCReason::CC_FORCED);
}
}
// Before we begin the cycle collection, make sure there is no active GC.
TimeStamp afterGCTime; if (sScheduler->InIncrementalGC()) {
FinishAnyIncrementalGC();
afterGCTime = TimeStamp::Now();
}
if (!sScheduler->IsCollectingCycles()) {
sCCStats->PrepareForCycleCollection(beginTime);
sScheduler->NoteCCBegin();
}
// Run forgetSkippable synchronously to reduce the size of the CC graph. This // is particularly useful if we recently finished a GC. if (sScheduler->IsEarlyForgetSkippable()) { while (sScheduler->IsEarlyForgetSkippable()) {
FireForgetSkippable(false, TimeStamp());
}
sCCStats->AfterSyncForgetSkippable(startTime);
}
// Update timing information for the current slice before we log it, if // we previously called PrepareForCycleCollectionSlice(). During shutdown // CCs, this won't happen.
sCCStats->AfterCycleCollectionSlice();
// Update global state to indicate we have just run a cycle collection.
sCCStats->Clear();
// If we need a GC after this CC (typically because lots of GCed objects or // zones have been collected in the CC), schedule it.
if (sScheduler->NeedsGCAfterCC()) {
MOZ_ASSERT(
TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay()) > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
TimeDuration delay; if (sScheduler->PreferFasterCollection()) { // If we collected lots of objects, trigger the next GC sooner so that // GC can cut JS-to-native edges and native objects can be then deleted.
delay = TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay_interslice());
} else {
delay = TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay()) -
std::min(ccNowDuration, kMaxICCDuration);
}
if (!aDeadline) {
mCurrentCollectionHasSeenNonIdle = true;
} elseif (mPreferFasterCollection) { // We found some idle time, try to utilize that a bit more given that // we're in a mode where idle time is rare.
aDeadline = aDeadline + TimeDuration::FromMilliseconds(5.0);
}
bool didDoWork = false;
// The CC/GC scheduler (sScheduler) decides what action(s) to take during // this invocation of the CC runner. // // This may be zero, one, or multiple actions. (Zero is when CC is blocked by // incremental GC, or when the scheduler determined that a CC is no longer // needed.) Loop until the scheduler finishes this invocation by returning // `Yield` in step.mYield.
CCRunnerStep step; do {
step = sScheduler->AdvanceCCRunner(aDeadline, TimeStamp::Now(),
nsCycleCollector_suspectedCount()); switch (step.mAction) { case CCRunnerAction::None: break;
case CCRunnerAction::MinorGC:
JS::MaybeRunNurseryCollection(CycleCollectedJSRuntime::Get()->Runtime(),
step.mParam.mReason);
sScheduler->NoteMinorGCEnd(); break;
case CCRunnerAction::ForgetSkippable: // 'Forget skippable' only, then end this invocation.
FireForgetSkippable(bool(step.mParam.mRemoveChildless), aDeadline); break;
case CCRunnerAction::CleanupContentUnbinder: // Clear content unbinder before the first actual CC slice.
Element::ClearContentUnbinder(); break;
case CCRunnerAction::CleanupDeferred: // and if time still permits, perform deferred deletions.
nsCycleCollector_doDeferredDeletion(); break;
case CCRunnerAction::CycleCollect: // Cycle collection slice.
nsJSContext::RunCycleCollectorSlice(step.mParam.mCCReason, aDeadline); break;
case CCRunnerAction::StopRunning: // End this CC, either because we have run a cycle collection slice, or // because a CC is no longer needed.
sScheduler->KillCCRunner(); break;
}
if (step.mAction != CCRunnerAction::None) {
didDoWork = true;
}
} while (step.mYield == CCRunnerYield::Continue);
PresShell* presShell = rootDocument->GetPresShell(); if (!presShell) { return;
}
nsViewManager* vm = presShell->GetViewManager(); if (!vm) { return;
}
if (!sScheduler->IsUserActive() &&
(sScheduler->InIncrementalGC() || sScheduler->IsCollectingCycles())) {
Maybe<TimeStamp> next = nsRefreshDriver::GetNextTickHint(); if (next.isSome()) { // Try to not delay the next RefreshDriver tick, so give a reasonable // deadline for collectors.
sScheduler->RunNextCollectorTimer(aReason, next.value());
}
}
// In order to improve performance on the next // page, run a minor GC. The 16ms limit ensures // it isn't called all the time if there are for // example multiple iframes loading at the same // time.
JS::RunNurseryCollection(
CycleCollectedJSRuntime::Get()->Runtime(),
JS::GCReason::PREPARE_FOR_PAGELOAD,
mozilla::TimeDuration::FromMilliseconds(16));
}),
EventQueuePriority::Idle);
}
// Bug 1772638: For now, only do eager minor GCs. Eager major GCs regress some // benchmarks. Hopefully that will be worked out and this will check for // whether an eager major GC is needed.
}
void nsJSContext::DoLowMemoryGC() { if (sShuttingDown) { return;
}
nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
nsJSContext::ShrinkingGC);
nsJSContext::CycleCollectNow(CCReason::MEM_PRESSURE); if (sScheduler->NeedsGCAfterCC()) {
nsJSContext::GarbageCollectNow(JS::GCReason::MEM_PRESSURE,
nsJSContext::ShrinkingGC);
}
}
// static void nsJSContext::LowMemoryGC() {
RefPtr<CCGCScheduler::MayGCPromise> mbPromise =
CCGCScheduler::MayGCNow(JS::GCReason::MEM_PRESSURE); if (!mbPromise) { // Normally when the promise is null it means that IPC failed, that probably // means that something bad happened, don't bother with the GC. return;
}
mbPromise->Then(
GetMainThreadSerialEventTarget(), __func__,
[](bool aIgnored) { DoLowMemoryGC(); },
[](mozilla::ipc::ResponseRejectReason r) {});
}
staticvoid DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescription& aDesc) {
NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
static TimeStamp sCurrentGCStartTime;
switch (aProgress) { case JS::GC_CYCLE_BEGIN: { // Prevent cycle collections and shrinking during incremental GC.
sScheduler->NoteGCBegin(aDesc.reason_);
sCurrentGCStartTime = TimeStamp::Now(); break;
}
case JS::GC_CYCLE_END: {
TimeDuration delta = GetCollectionTimeDelta();
if (sScheduler->IsCCNeeded(TimeStamp::Now(),
nsCycleCollector_suspectedCount()) !=
CCReason::NO_REASON) { #ifdefined(MOZ_MEMORY) // We're likely to free the dirty pages after CC.
freeDirty = false; #endif
nsCycleCollector_dispatchDeferredDeletion();
}
case JS::GC_SLICE_END:
sScheduler->NoteGCSliceEnd(aDesc.lastSliceStart(aCx),
aDesc.lastSliceEnd(aCx));
if (sShuttingDown) {
sScheduler->KillGCRunner();
} else { // If incremental GC wasn't triggered by GCTimerFired, we may not have a // runner to ensure all the slices are handled. So, create the runner // here.
sScheduler->EnsureOrResetGCRunner();
}
if (sScheduler->IsCCNeeded(TimeStamp::Now(),
nsCycleCollector_suspectedCount()) !=
CCReason::NO_REASON) {
nsCycleCollector_dispatchDeferredDeletion();
}
void mozilla::dom::StartupJSEnvironment() { // initialize all our statics, so that we can restart XPCOM
sIsInitialized = false;
sShuttingDown = false;
sCCStats = CycleCollectorStats::Get();
}
// This callback may execute either on the main thread or a random JS-internal // helper thread. This callback can be called during shutdown so we cannot // simply NS_DispatchToMainThread. Failure during shutdown is expected and // properly handled by the JS engine.
nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadSerialEventTarget(); if (!mainTarget) { returnfalse;
}
RefPtr<JSDispatchableRunnable> r = new JSDispatchableRunnable(aDispatchable);
MOZ_ALWAYS_SUCCEEDS(mainTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL)); returntrue;
}
// Set these global xpconnect options...
Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB, "javascript.options.mem.max",
(void*)JSGC_MAX_BYTES);
Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback, "javascript.options.mem.nursery.min_kb",
(void*)JSGC_MIN_NURSERY_BYTES);
Preferences::RegisterCallbackAndCall(SetMemoryNurseryPrefChangedCallback, "javascript.options.mem.nursery.max_kb",
(void*)JSGC_MAX_NURSERY_BYTES);
NS_IMETHODIMP AsyncErrorReporter::Run() {
AutoJSAPI jsapi; // We're only using this context to deserialize a stack to report to the // console, so the scope we use doesn't matter. Stack frame filtering happens // based on the principal encoded into the frame and the caller compartment, // not the compartment of the frame object, and the console reporting code // will not be using our context, and therefore will not care what compartment // it has entered.
DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
MOZ_ASSERT(ok, "Problem with system global?");
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> stack(cx);
JS::Rooted<JSObject*> stackGlobal(cx); if (mStackHolder) {
stack = mStackHolder->ReadStack(cx); if (stack) {
stackGlobal = JS::CurrentGlobalOrNull(cx);
}
}
JS::Rooted<Maybe<JS::Value>> exception(cx, Nothing()); if (mHasException) {
MOZ_ASSERT(NS_IsMainThread());
exception = Some(mException); // Remove our reference to the exception.
mException.setUndefined();
mHasException = false;
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.