// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(*vp)); MOZ_ASSERT(!js::IsInnerObject(obj)); if (js::IsObjectInContextCompartment(obj, cx)) { *vp = OBJECT_TO_JSVAL(obj); return true; } // Even though waivers have no effect on access by scopes that don't subsume // the underlying object, good defense-in-depth dictates that we should avoid // handing out waivers to callers that can't use them. The transitive waiving // machinery unconditionally calls WaiveXrayAndWrap on return values from // waived functions, even though the return value might be not be same-origin // with the function. So if we find ourselves trying to create a waiver for // |cx|, we should check whether the caller has any business with waivers // to things in |obj|'s compartment. JSCompartment *target = js::GetContextCompartment(cx); JSCompartment *origin = js::GetObjectCompartment(obj); obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = JSVAL_TO_OBJECT(*vp); obj = WaiveXray(cx, obj); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
void FileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, ErrorResult& aRv) { JS::Rooted<JS::Value> result(aCx); if (mDataFormat == FILE_AS_ARRAYBUFFER) { if (mReadyState == DONE && mResultArrayBuffer) { result.setObject(*mResultArrayBuffer); } else { result.setNull(); } if (!JS_WrapValue(aCx, &result)) { aRv.Throw(NS_ERROR_FAILURE); return; } aResult.set(result); return; } nsString tmpResult = mResult; if (!xpc::StringToJsval(aCx, tmpResult, aResult)) { aRv.Throw(NS_ERROR_FAILURE); return; } }
JSString* jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) { JSContext* cx = jsdc->dumbContext; JSExceptionState* exceptionState; JSCrossCompartmentCall *call = NULL; jsval stringval; JSString *string; JSBool needWrap; JSObject *scopeObj; if(jsdval->string) return jsdval->string; /* Reuse the string without copying or re-rooting it */ if(JSVAL_IS_STRING(jsdval->val)) { jsdval->string = JSVAL_TO_STRING(jsdval->val); return jsdval->string; } JS_BeginRequest(cx); /* Objects call JS_ValueToString in their own compartment. */ scopeObj = JSVAL_IS_OBJECT(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; call = JS_EnterCrossCompartmentCall(cx, scopeObj); if(!call) { JS_EndRequest(cx); return NULL; } exceptionState = JS_SaveExceptionState(cx); string = JS_ValueToString(cx, jsdval->val); JS_RestoreExceptionState(cx, exceptionState); JS_LeaveCrossCompartmentCall(call); call = NULL; if(string) { stringval = STRING_TO_JSVAL(string); call = JS_EnterCrossCompartmentCall(cx, jsdc->glob); } if(!string || !call || !JS_WrapValue(cx, &stringval)) { if(call) JS_LeaveCrossCompartmentCall(call); JS_EndRequest(cx); return NULL; } jsdval->string = JSVAL_TO_STRING(stringval); if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString")) jsdval->string = NULL; JS_LeaveCrossCompartmentCall(call); JS_EndRequest(cx); return jsdval->string; }
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = JSVAL_TO_OBJECT(*vp)->unwrap(); obj = GetCurrentOuter(cx, obj); if (obj->compartment() == cx->compartment) { *vp = OBJECT_TO_JSVAL(obj); return true; } obj = WaiveXray(cx, obj); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = js::UnwrapObject(JSVAL_TO_OBJECT(*vp)); obj = GetCurrentOuter(cx, obj); if (js::IsObjectInContextCompartment(obj, cx)) { *vp = OBJECT_TO_JSVAL(obj); return true; } obj = WaiveXray(cx, obj); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
JSString* jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) { JSContext* cx = jsdc->dumbContext; JSExceptionState* exceptionState; JSCompartment* oldCompartment = NULL; jsval stringval; JS::RootedString string(cx); JS::RootedObject scopeObj(cx); if(jsdval->string) return jsdval->string; /* Reuse the string without copying or re-rooting it */ if(JSVAL_IS_STRING(jsdval->val)) { jsdval->string = JSVAL_TO_STRING(jsdval->val); return jsdval->string; } JS_BeginRequest(cx); /* Objects call JS_ValueToString in their own compartment. */ scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; oldCompartment = JS_EnterCompartment(cx, scopeObj); exceptionState = JS_SaveExceptionState(cx); string = JS_ValueToString(cx, jsdval->val); JS_RestoreExceptionState(cx, exceptionState); JS_LeaveCompartment(cx, oldCompartment); oldCompartment = NULL; if(string) { stringval = STRING_TO_JSVAL(string); oldCompartment = JS_EnterCompartment(cx, jsdc->glob); } if(!string || !JS_WrapValue(cx, &stringval)) { if(oldCompartment) JS_LeaveCompartment(cx, oldCompartment); JS_EndRequest(cx); return NULL; } jsdval->string = JSVAL_TO_STRING(stringval); if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString")) jsdval->string = NULL; JS_LeaveCompartment(cx, oldCompartment); JS_EndRequest(cx); return jsdval->string; }
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp) { if (vp.isPrimitive()) return JS_WrapValue(cx, vp); RootedObject obj(cx, &vp.toObject()); if (!WaiveXrayAndWrap(cx, &obj)) return false; vp.setObject(*obj); return true; }
static bool XrayWrapperConstructor(JSContext *cx, unsigned argc, jsval *vp) { if (argc == 0) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } JS::RootedValue v(cx, JS_ARGV(cx, vp)[0]); if (!v.isObject()) { JS_SET_RVAL(cx, vp, v); return true; } *vp = JS::ObjectValue(*js::UncheckedUnwrap(&v.toObject())); return JS_WrapValue(cx, vp); }
static bool XrayWrapperConstructor(JSContext* cx, unsigned argc, Value* vp) { JS::CallArgs args = CallArgsFromVp(argc, vp); if (args.length() == 0) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } if (!args[0].isObject()) { args.rval().set(args[0]); return true; } args.rval().setObject(*js::UncheckedUnwrap(&args[0].toObject())); return JS_WrapValue(cx, args.rval()); }
JSBool JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp) { if (argc != 2) { JS_ReportError(cx, "evalInSandbox takes two arguments"); return JS_FALSE; } jsval* argv = JS_ARGV(cx, vp); JSObject* obj; if (!JSVAL_IS_OBJECT(argv[0]) || !(obj = JSVAL_TO_OBJECT(argv[0]))) { JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); JS_ASSERT(JS_FALSE); return JS_FALSE; } // Unwrap, and switch compartments obj = js::UnwrapObject(obj); JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return JS_FALSE; if (&sGlobalClass != JS_GetClass(cx, obj) || obj == JS_GetGlobalObject(cx)) { JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); JS_ASSERT(JS_FALSE); return JS_FALSE; } if (!JS_WrapValue(cx, &argv[1])) return JS_FALSE; JSString* str = JS_ValueToString(cx, argv[1]); if (!str) return JS_FALSE; size_t length; const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); if (!chars) return JS_FALSE; js::AutoValueRooter ignored(cx); return JS_EvaluateUCScript(cx, obj, chars, length, "", 1, ignored.jsval_addr()); }
// static JSBool XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e) { JSBool success = JS_FALSE; if(e) { nsCOMPtr<nsIXPCException> xpcEx; jsval thrown; nsXPConnect* xpc; // If we stored the original thrown JS value in the exception // (see XPCConvert::ConstructException) and we are in a web // context (i.e., not chrome), rethrow the original value. if(!IsCallerChrome(cx) && (xpcEx = do_QueryInterface(e)) && NS_SUCCEEDED(xpcEx->StealJSVal(&thrown))) { if (!JS_WrapValue(cx, &thrown)) return JS_FALSE; JS_SetPendingException(cx, thrown); success = JS_TRUE; } else if((xpc = nsXPConnect::GetXPConnect())) { JSObject* glob = JS_GetScopeChain(cx); if(!glob) return JS_FALSE; glob = JS_GetGlobalForObject(cx, glob); nsCOMPtr<nsIXPConnectJSObjectHolder> holder; nsresult rv = xpc->WrapNative(cx, glob, e, NS_GET_IID(nsIException), getter_AddRefs(holder)); if(NS_SUCCEEDED(rv) && holder) { JSObject* obj; if(NS_SUCCEEDED(holder->GetJSObject(&obj))) { JS_SetPendingException(cx, OBJECT_TO_JSVAL(obj)); success = JS_TRUE; } } } } return success; }
/* * Create a new JSD value referring to a jsval. Copy string values into the * JSD compartment. Leave all other GCTHINGs in their native compartments * and access them through cross-compartment calls. */ JSDValue* jsd_NewValue(JSDContext* jsdc, jsval val) { JSDValue* jsdval; JSCrossCompartmentCall *call = NULL; if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue)))) return NULL; if(JSVAL_IS_GCTHING(val)) { JSBool ok; JS_BeginRequest(jsdc->dumbContext); call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob); if(!call) { JS_EndRequest(jsdc->dumbContext); free(jsdval); return NULL; } ok = JS_AddNamedValueRoot(jsdc->dumbContext, &jsdval->val, "JSDValue"); if(ok && JSVAL_IS_STRING(val)) { if(!JS_WrapValue(jsdc->dumbContext, &val)) { ok = JS_FALSE; } } JS_LeaveCrossCompartmentCall(call); JS_EndRequest(jsdc->dumbContext); if(!ok) { free(jsdval); return NULL; } } jsdval->val = val; jsdval->nref = 1; JS_INIT_CLIST(&jsdval->props); return jsdval; }
static JSBool XrayWrapperConstructor(JSContext *cx, unsigned argc, jsval *vp) { if (argc == 0) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } if (JSVAL_IS_PRIMITIVE(vp[2])) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } JSObject *obj = JSVAL_TO_OBJECT(vp[2]); if (!js::IsWrapper(obj)) { *vp = OBJECT_TO_JSVAL(obj); return true; } obj = js::UnwrapObject(obj); *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
static JSBool XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp) { if (argc == 0) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } if (JSVAL_IS_PRIMITIVE(vp[2])) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } JSObject *obj = JSVAL_TO_OBJECT(vp[2]); if (!obj->isWrapper()) { *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } obj = obj->unwrap(); *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
void ThrowExceptionObject(JSContext* aCx, Exception* aException) { JS::Rooted<JS::Value> thrown(aCx); // If we stored the original thrown JS value in the exception // (see XPCConvert::ConstructException) and we are in a web context // (i.e., not chrome), rethrow the original value. This only applies to JS // implemented components so we only need to check for this on the main // thread. if (NS_IsMainThread() && !nsContentUtils::IsCallerChrome() && aException->StealJSVal(thrown.address())) { // Now check for the case when thrown is a number which matches // aException->GetResult(). This would indicate that what actually got // thrown was an nsresult value. In that situation, we should go back // through dom::Throw with that nsresult value, because it will make sure to // create the right sort of Exception or DOMException, with the right // global. if (thrown.isNumber()) { nsresult exceptionResult; if (NS_SUCCEEDED(aException->GetResult(&exceptionResult)) && double(exceptionResult) == thrown.toNumber()) { Throw(aCx, exceptionResult); return; } } if (!JS_WrapValue(aCx, &thrown)) { return; } ThrowExceptionValueIfSafe(aCx, thrown, aException); return; } if (!GetOrCreateDOMReflector(aCx, aException, &thrown)) { return; } ThrowExceptionValueIfSafe(aCx, thrown, aException); }
/* * Create a new JSD value referring to a jsval. Copy string values into the * JSD compartment. Leave all other GCTHINGs in their native compartments * and access them through cross-compartment calls. */ JSDValue* jsd_NewValue(JSDContext* jsdc, jsval value) { JS::RootedValue val(jsdc->dumbContext, value); JSDValue* jsdval; JSCompartment* oldCompartment = NULL; if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue)))) return NULL; if(JSVAL_IS_GCTHING(val)) { JSBool ok; JS_BeginRequest(jsdc->dumbContext); oldCompartment = JS_EnterCompartment(jsdc->dumbContext, jsdc->glob); ok = JS_AddNamedValueRoot(jsdc->dumbContext, &jsdval->val, "JSDValue"); if(ok && JSVAL_IS_STRING(val)) { if(!JS_WrapValue(jsdc->dumbContext, val.address())) { ok = JS_FALSE; } } JS_LeaveCompartment(jsdc->dumbContext, oldCompartment); JS_EndRequest(jsdc->dumbContext); if(!ok) { free(jsdval); return NULL; } } jsdval->val = val; jsdval->nref = 1; JS_INIT_CLIST(&jsdval->props); return jsdval; }
nsresult InvokePromiseFuncCallback::Call(JSContext* aCx, JS::Handle<JS::Value> aValue) { // Run resolver's algorithm with value and the synchronous flag set. JS::ExposeObjectToActiveJS(mGlobal); JS::ExposeValueToActiveJS(aValue); JSAutoCompartment ac(aCx, mGlobal); JS::Rooted<JS::Value> value(aCx, aValue); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return NS_ERROR_FAILURE; } ErrorResult rv; JS::Rooted<JS::Value> ignored(aCx); mPromiseFunc->Call(value, &ignored, rv); // Useful exceptions already got reported. rv.SuppressException(); return NS_OK; }
JSBool JetpackChild::CreateSandbox(JSContext* cx, uintN argc, jsval* vp) { if (argc > 0) { JS_ReportError(cx, "createSandbox takes zero arguments"); return JS_FALSE; } JSObject* obj = JS_NewCompartmentAndGlobalObject(cx, const_cast<JSClass*>(&sGlobalClass), NULL); if (!obj) return JS_FALSE; jsval rval = OBJECT_TO_JSVAL(obj); if (!JS_WrapValue(cx, &rval)) return JS_FALSE; JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return JS_FALSE; JS_SET_RVAL(cx, vp, rval); return JS_InitStandardClasses(cx, obj); }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, const nsAString& aScript, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, EvaluateOptions& aEvaluateOptions, JS::Value* aRetValue) { PROFILER_LABEL("JS", "EvaluateString"); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aRetValue); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aRetValue); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers use null to indicate they don't care. if (aRetValue) { *aRetValue = JSVAL_VOID; } xpc_UnmarkGrayObject(aScopeObject); nsAutoMicroTask mt; JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject)); aCompileOptions.setPrincipals(p); bool ok = false; nsresult rv = nsContentUtils::GetSecurityManager()-> CanExecuteScripts(aCx, nsJSPrincipals::get(p), &ok); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(ok, NS_OK); // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::RootedObject rootedScope(aCx, aScopeObject); ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, PromiseFlatString(aScript).get(), aScript.Length(), aRetValue); if (ok && aEvaluateOptions.coerceToString && !aRetValue->isUndefined()) { JSString* str = JS_ValueToString(aCx, *aRetValue); ok = !!str; *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue(); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aRetValue) { *aRetValue = JS::UndefinedValue(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS_GetPendingException(aCx, aRetValue); JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aRetValue && !JS_WrapValue(aCx, aRetValue)) return NS_ERROR_OUT_OF_MEMORY; return rv; }
nsresult EventListenerManager::CompileEventHandlerInternal(Listener* aListener, const nsAString* aBody, Element* aElement) { MOZ_ASSERT(aListener->GetJSEventHandler()); MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?"); JSEventHandler* jsEventHandler = aListener->GetJSEventHandler(); MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(), "What is there to compile?"); nsresult result = NS_OK; nsCOMPtr<nsIDocument> doc; nsCOMPtr<nsIScriptGlobalObject> global = GetScriptGlobalAndDocument(getter_AddRefs(doc)); NS_ENSURE_STATE(global); // Activate JSAPI, and make sure that exceptions are reported on the right // Window. AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(global))) { return NS_ERROR_UNEXPECTED; } JSContext* cx = jsapi.cx(); nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom; nsIAtom* attrName = typeAtom; // Flag us as not a string so we don't keep trying to compile strings which // can't be compiled. aListener->mHandlerIsString = false; // mTarget may not be an Element if it's a window and we're // getting an inline event listener forwarded from <html:body> or // <html:frameset> or <xul:window> or the like. // XXX I don't like that we have to reference content from // here. The alternative is to store the event handler string on // the JSEventHandler itself, and that still doesn't address // the arg names issue. nsCOMPtr<Element> element = do_QueryInterface(mTarget); MOZ_ASSERT(element || aBody, "Where will we get our body?"); nsAutoString handlerBody; const nsAString* body = aBody; if (!aBody) { if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) { attrName = nsGkAtoms::onload; } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) { attrName = nsGkAtoms::onunload; } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) { attrName = nsGkAtoms::onresize; } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) { attrName = nsGkAtoms::onscroll; } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) { attrName = nsGkAtoms::onzoom; } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) { attrName = nsGkAtoms::onbegin; } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) { attrName = nsGkAtoms::onrepeat; } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) { attrName = nsGkAtoms::onend; } element->GetAttr(kNameSpaceID_None, attrName, handlerBody); body = &handlerBody; aElement = element; } aListener = nullptr; uint32_t lineNo = 0; nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener")); MOZ_ASSERT(body); MOZ_ASSERT(aElement); nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI(); if (uri) { uri->GetSpec(url); lineNo = 1; } nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget); uint32_t argCount; const char **argNames; nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), typeAtom, win, &argCount, &argNames); JSAddonId *addonId = MapURIToAddonID(uri); // Wrap the event target, so that we can use it as the scope for the event // handler. Note that mTarget is different from aElement in the <body> case, // where mTarget is a Window. // // The wrapScope doesn't really matter here, because the target will create // its reflector in the proper scope, and then we'll enter that compartment. JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject()); JS::Rooted<JS::Value> v(cx); { JSAutoCompartment ac(cx, wrapScope); nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v, /* aAllowWrapping = */ false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } if (addonId) { JS::Rooted<JSObject*> vObj(cx, &v.toObject()); JS::Rooted<JSObject*> addonScope(cx, xpc::GetAddonScope(cx, vObj, addonId)); if (!addonScope) { return NS_ERROR_FAILURE; } JSAutoCompartment ac(cx, addonScope); if (!JS_WrapValue(cx, &v)) { return NS_ERROR_FAILURE; } } JS::Rooted<JSObject*> target(cx, &v.toObject()); JSAutoCompartment ac(cx, target); nsDependentAtomString str(attrName); // Most of our names are short enough that we don't even have to malloc // the JS string stuff, so don't worry about playing games with // refcounting XPCOM stringbuffers. JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length())); NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY); // Get the reflector for |aElement|, so that we can pass to setElement. if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) { return NS_ERROR_FAILURE; } JS::CompileOptions options(cx); options.setIntroductionType("eventHandler") .setFileAndLine(url.get(), lineNo) .setVersion(JSVERSION_DEFAULT) .setElement(&v.toObject()) .setElementAttributeName(jsStr) .setDefineOnScope(false); JS::Rooted<JSObject*> handler(cx); result = nsJSUtils::CompileFunction(cx, target, options, nsAtomCString(typeAtom), argCount, argNames, *body, handler.address()); NS_ENSURE_SUCCESS(result, result); NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE); if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) { nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback = new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); jsEventHandler->SetHandler(handlerCallback); } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) { nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback = new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); jsEventHandler->SetHandler(handlerCallback); } else { nsRefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); jsEventHandler->SetHandler(handlerCallback); } return result; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, const nsAString& aScript, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, EvaluateOptions& aEvaluateOptions, JS::Value* aRetValue, void **aOffThreadToken) { PROFILER_LABEL("JS", "EvaluateString"); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aRetValue); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aRetValue); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers use null to indicate they don't care. if (aRetValue) { *aRetValue = JSVAL_VOID; } JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.construct(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JSScript *script = JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = false; } } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, PromiseFlatString(aScript).get(), aScript.Length(), aRetValue); } if (ok && aEvaluateOptions.coerceToString && !aRetValue->isUndefined()) { JS::Rooted<JS::Value> value(aCx, *aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue(); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aRetValue) { *aRetValue = JS::UndefinedValue(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); if (aRetValue) { *aRetValue = exn; } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aRetValue) { JS::Rooted<JS::Value> v(aCx, *aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } *aRetValue = v; } return rv; }
void WrapperPromiseCallback::Call(JSContext* aCx, JS::Handle<JS::Value> aValue) { JSAutoCompartment ac(aCx, mGlobal); JS::Rooted<JS::Value> value(aCx, aValue); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } ErrorResult rv; // If invoking callback threw an exception, run resolver's reject with the // thrown exception as argument and the synchronous flag set. JS::Rooted<JS::Value> retValue(aCx); mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions); rv.WouldReportJSException(); if (rv.Failed() && rv.IsJSException()) { JS::Rooted<JS::Value> value(aCx); rv.StealJSException(aCx, &value); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } mNextPromise->RejectInternal(aCx, value, Promise::SyncTask); return; } // If the return value is the same as the promise itself, throw TypeError. if (retValue.isObject()) { JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject()); Promise* returnedPromise; nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise); if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) { const char* fileName = nullptr; uint32_t lineNumber = 0; // Try to get some information about the callback to report a sane error, // but don't try too hard (only deals with scripted functions). JS::Rooted<JSObject*> unwrapped(aCx, js::CheckedUnwrap(mCallback->Callback())); if (unwrapped) { JSAutoCompartment ac(aCx, unwrapped); if (JS_ObjectIsFunction(aCx, unwrapped)) { JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped)); JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue)); MOZ_ASSERT(func); JSScript* script = JS_GetFunctionScript(aCx, func); if (script) { fileName = JS_GetScriptFilename(script); lineNumber = JS_GetScriptBaseLineNumber(aCx, script); } } } // We're back in aValue's compartment here. JS::Rooted<JSString*> stack(aCx, JS_GetEmptyString(JS_GetRuntime(aCx))); JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName)); if (!fn) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } JS::Rooted<JSString*> message(aCx, JS_NewStringCopyZ(aCx, "then() cannot return same Promise that it resolves.")); if (!message) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } JS::Rooted<JS::Value> typeError(aCx); if (!JS::CreateTypeError(aCx, stack, fn, lineNumber, 0, nullptr, message, &typeError)) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } mNextPromise->RejectInternal(aCx, typeError, Promise::SyncTask); return; } } // Otherwise, run resolver's resolve with value and the synchronous flag // set. if (!JS_WrapValue(aCx, &retValue)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } mNextPromise->ResolveInternal(aCx, retValue, Promise::SyncTask); }
nsresult WrapperPromiseCallback::Call(JSContext* aCx, JS::Handle<JS::Value> aValue) { JS::ExposeObjectToActiveJS(mGlobal); JS::ExposeValueToActiveJS(aValue); JSAutoCompartment ac(aCx, mGlobal); JS::Rooted<JS::Value> value(aCx, aValue); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return NS_ERROR_FAILURE; } ErrorResult rv; // PromiseReactionTask step 6 JS::Rooted<JS::Value> retValue(aCx); JSCompartment* compartment; if (mNextPromise) { compartment = mNextPromise->Compartment(); } else { MOZ_ASSERT(mNextPromiseObj); compartment = js::GetObjectCompartment(mNextPromiseObj); } mCallback->Call(value, &retValue, rv, "promise callback", CallbackObject::eRethrowExceptions, compartment); rv.WouldReportJSException(); // PromiseReactionTask step 7 if (rv.Failed()) { JS::Rooted<JS::Value> value(aCx); { // Scope for JSAutoCompartment // Convert the ErrorResult to a JS exception object that we can reject // ourselves with. This will be exactly the exception that would get // thrown from a binding method whose ErrorResult ended up with whatever // is on "rv" right now. Do this in the promise reflector compartment. Maybe<JSAutoCompartment> ac; if (mNextPromise) { ac.emplace(aCx, mNextPromise->GlobalJSObject()); } else { ac.emplace(aCx, mNextPromiseObj); } DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value); MOZ_ASSERT(conversionResult); } if (mNextPromise) { mNextPromise->RejectInternal(aCx, value); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult rejectRv; mRejectFunc->Call(value, &ignored, rejectRv); // This reported any JS exceptions; we just have a pointless exception on // there now. rejectRv.SuppressException(); } return NS_OK; } // If the return value is the same as the promise itself, throw TypeError. if (retValue.isObject()) { JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject()); valueObj = js::CheckedUnwrap(valueObj); JS::Rooted<JSObject*> nextPromiseObj(aCx); if (mNextPromise) { nextPromiseObj = mNextPromise->GetWrapper(); } else { MOZ_ASSERT(mNextPromiseObj); nextPromiseObj = mNextPromiseObj; } // XXXbz shouldn't this check be over in ResolveInternal anyway? if (valueObj == nextPromiseObj) { const char* fileName = nullptr; uint32_t lineNumber = 0; // Try to get some information about the callback to report a sane error, // but don't try too hard (only deals with scripted functions). JS::Rooted<JSObject*> unwrapped(aCx, js::CheckedUnwrap(mCallback->Callback())); if (unwrapped) { JSAutoCompartment ac(aCx, unwrapped); if (JS_ObjectIsFunction(aCx, unwrapped)) { JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped)); JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue)); MOZ_ASSERT(func); JSScript* script = JS_GetFunctionScript(aCx, func); if (script) { fileName = JS_GetScriptFilename(script); lineNumber = JS_GetScriptBaseLineNumber(aCx, script); } } } // We're back in aValue's compartment here. JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName)); if (!fn) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JSString*> message(aCx, JS_NewStringCopyZ(aCx, "then() cannot return same Promise that it resolves.")); if (!message) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JS::Value> typeError(aCx); if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0, nullptr, message, &typeError)) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } if (mNextPromise) { mNextPromise->RejectInternal(aCx, typeError); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult rejectRv; mRejectFunc->Call(typeError, &ignored, rejectRv); // This reported any JS exceptions; we just have a pointless exception // on there now. rejectRv.SuppressException(); } return NS_OK; } } // Otherwise, run resolver's resolve with value. if (!JS_WrapValue(aCx, &retValue)) { NS_WARNING("Failed to wrap value into the right compartment."); return NS_ERROR_FAILURE; } if (mNextPromise) { mNextPromise->ResolveInternal(aCx, retValue); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult resolveRv; mResolveFunc->Call(retValue, &ignored, resolveRv); // This reported any JS exceptions; we just have a pointless exception // on there now. resolveRv.SuppressException(); } return NS_OK; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, JS::SourceBufferHolder& aSrcBuf, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, const EvaluateOptions& aEvaluateOptions, JS::MutableHandle<JS::Value> aRetValue, void **aOffThreadToken) { PROFILER_LABEL("nsJSUtils", "EvaluateString", js::ProfileEntry::Category::JS); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aEvaluateOptions.needResult); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aEvaluateOptions.needResult); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aSrcBuf.get()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers can do this by calling the other overload of // EvaluateString() which calls this function with aEvaluateOptions.needResult // set to false. aRetValue.setUndefined(); JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.emplace(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JS::Rooted<JSScript*> script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { if (aEvaluateOptions.needResult) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = JS_ExecuteScript(aCx, rootedScope, script); } } else { ok = false; } } else { if (aEvaluateOptions.needResult) { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf, aRetValue); } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf); } } if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) { JS::Rooted<JS::Value> value(aCx, aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue()); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aEvaluateOptions.needResult) { 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); if (aEvaluateOptions.needResult) { aRetValue.set(exn); } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aEvaluateOptions.needResult) { JS::Rooted<JS::Value> v(aCx, aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } aRetValue.set(v); } return rv; }