JSBool WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) { // Slim wrappers don't expect to be wrapped, so morph them to fat wrappers // if we're about to wrap one. JSObject *innerObj = JSVAL_TO_OBJECT(v); if (IS_SLIM_WRAPPER(innerObj) && !MorphSlimWrapper(cx, innerObj)) { return ThrowException(NS_ERROR_FAILURE, cx); } JSObject *wrapperObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SOWClass), NULL, parent); if (!wrapperObj) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(wrapperObj); js::AutoObjectRooter tvr(cx, wrapperObj); if (!JS_SetReservedSlot(cx, wrapperObj, sWrappedObjSlot, v) || !JS_SetReservedSlot(cx, wrapperObj, sFlagsSlot, JSVAL_ZERO)) { return JS_FALSE; } return JS_TRUE; }
JSBool js_json_stringify(JSContext *cx, uintN argc, jsval *vp) { jsval *argv = vp + 2; JSObject *replacer = NULL; jsval space = JSVAL_NULL; JSAutoTempValueRooter tvr(cx, replacer); JSAutoTempValueRooter tvr2(cx, 1, &space); // Must throw an Error if there isn't a first arg if (!JS_ConvertArguments(cx, argc, argv, "v / o v", vp, &replacer, &space)) return JS_FALSE; JSCharBuffer cb(cx); if (!js_Stringify(cx, vp, replacer, space, cb)) return JS_FALSE; // XXX This can never happen to nsJSON.cpp, but the JSON object // needs to support returning undefined. So this is a little awkward // for the API, because we want to support streaming writers. if (!cb.empty()) { JSString *str = js_NewStringFromCharBuffer(cx, cb); if (!str) return JS_FALSE; *vp = STRING_TO_JSVAL(str); } else { *vp = JSVAL_VOID; } return JS_TRUE; }
static JSObject * XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { ThrowException(NS_ERROR_INVALID_ARG, cx); return nsnull; } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { ThrowException(NS_ERROR_FAILURE, cx); return nsnull; } JSObject *wrapperIter = JS_NewObject(cx, &sXPC_COW_JSClass.base, nsnull, JS_GetGlobalForObject(cx, obj)); if (!wrapperIter) { return nsnull; } JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); // Initialize our COW. jsval v = OBJECT_TO_JSVAL(wrappedObj); if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot, v) || !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot, JSVAL_ZERO)) { return nsnull; } return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, keysonly); }
static JSObject * GeneratePropertyOp(JSContext *cx, JSObject *obj, jsval idval, uintN argc, const char *name, JSPropertyOp pop) { // The JS engine provides two reserved slots on function objects for // XPConnect to use. Use them to stick the necessary info here. JSFunction *fun = JS_NewFunction(cx, reinterpret_cast<JSNative>(PropertyOpForwarder), argc, JSFUN_FAST_NATIVE, obj, name); if(!fun) return JS_FALSE; JSObject *funobj = JS_GetFunctionObject(fun); JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(funobj)); // Unfortunately, we cannot guarantee that JSPropertyOp is aligned. Use a // second object to work around this. JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, nsnull, funobj); if(!ptrobj) return JS_FALSE; JSPropertyOp *popp = new JSPropertyOp; if(!popp) return JS_FALSE; *popp = pop; JS_SetPrivate(cx, ptrobj, popp); JS_SetReservedSlot(cx, funobj, 0, OBJECT_TO_JSVAL(ptrobj)); JS_SetReservedSlot(cx, funobj, 1, idval); return funobj; }
JSBool XPC_COW_WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) { JSObject *wrapperObj = JS_NewObjectWithGivenProto(cx, &sXPC_COW_JSClass.base, NULL, parent); if (!wrapperObj) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(wrapperObj); jsval exposedProps = JSVAL_VOID; JSAutoTempValueRooter tvr(cx, 1, &exposedProps); if (!GetExposedProperties(cx, JSVAL_TO_OBJECT(v), &exposedProps)) { return JS_FALSE; } if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, v) || !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, wrapperObj, sExposedPropsSlot, exposedProps)) { return JS_FALSE; } return JS_TRUE; }
// Because of the drastically different ways that same- and cross-origin XOWs // work, we have to call JS_ClearScope when a XOW changes from being same- // origin to cross-origin. Normally, there are defined places in Gecko where // this happens and they notify us. However, UniversalXPConnect causes the // same transition without any notifications. We could try to detect when this // happens, but doing so would require calling JS_ClearScope from random // hooks, which is bad. // // The compromise is the UXPCObject. When resolving a property on a XOW as // same-origin because of UniversalXPConnect, we actually resolve it on the // UXPCObject (which is just a XOW for the same object). This causes the JS // engine to do all of its work on another object, not polluting the main // object. However, if the get results in calling a setter, the engine still // uses the regular object as 'this', ensuring that the UXPCObject doesn't // leak to script. static JSObject * GetUXPCObject(JSContext *cx, JSObject *obj) { NS_ASSERTION(STOBJ_GET_CLASS(obj) == &sXPC_XOW_JSClass.base, "wrong object"); jsval v; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &v)) { return nsnull; } if (HAS_FLAGS(v, FLAG_IS_UXPC_OBJECT)) { return obj; } if (!JS_GetReservedSlot(cx, obj, sUXPCObjectSlot, &v)) { return nsnull; } if (JSVAL_IS_OBJECT(v)) { return JSVAL_TO_OBJECT(v); } JSObject *uxpco = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, STOBJ_GET_PARENT(obj)); if (!uxpco) { return nsnull; } JSAutoTempValueRooter tvr(cx, uxpco); jsval wrappedObj, parentScope; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sWrappedObjSlot, &wrappedObj) || !JS_GetReservedSlot(cx, obj, XPC_XOW_ScopeSlot, &parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, uxpco, XPCWrapper::sWrappedObjSlot, wrappedObj) || !JS_SetReservedSlot(cx, uxpco, XPCWrapper::sFlagsSlot, INT_TO_JSVAL(FLAG_IS_UXPC_OBJECT)) || !JS_SetReservedSlot(cx, uxpco, XPC_XOW_ScopeSlot, parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, obj, sUXPCObjectSlot, OBJECT_TO_JSVAL(uxpco))) { return nsnull; } return uxpco; }
static JSObject * XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { obj = FindSafeObject(obj); NS_ASSERTION(obj != nsnull, "FindSafeObject() returned null in class hook!"); JSObject *unsafeObj = GetUnsafeObject(obj); if (!unsafeObj) { ThrowException(NS_ERROR_INVALID_ARG, cx); return nsnull; } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return nsnull; } JSObject *tmp = XPCWrapper::UnwrapGeneric(cx, &sXPC_XOW_JSClass, unsafeObj); if (tmp) { unsafeObj = tmp; // Repeat the CanCallerAccess check because the XOW is parented to our // scope's global object which makes the above CanCallerAccess call lie. if (!CanCallerAccess(cx, unsafeObj)) { // CanCallerAccess() already threw for us. return nsnull; } } // Create our dummy SJOW. JSObject *wrapperIter = ::JS_NewObjectWithGivenProto(cx, &sXPC_SJOW_JSClass.base, nsnull, unsafeObj); if (!wrapperIter) { return nsnull; } if (!::JS_SetReservedSlot(cx, wrapperIter, XPC_SJOW_SLOT_IS_RESOLVING, JSVAL_ZERO)) { return nsnull; } JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); // Initialize the wrapper. return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, unsafeObj, keysonly); }
static JSObject * XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) { JSObject *wrappedObj = GetWrappedObject(cx, obj); if (!wrappedObj) { ThrowException(NS_ERROR_INVALID_ARG, cx); return nsnull; } XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { ThrowException(NS_ERROR_FAILURE, cx); return nsnull; } nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull); if (NS_FAILED(rv)) { if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { // Can't create iterators for foreign objects. ThrowException(rv, cx); return nsnull; } ThrowException(NS_ERROR_FAILURE, cx); return nsnull; } JSObject *wrapperIter = JS_NewObject(cx, &sXPC_XOW_JSClass.base, nsnull, JS_GetGlobalForObject(cx, obj)); if (!wrapperIter) { return nsnull; } JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); // Initialize our XOW. jsval v = OBJECT_TO_JSVAL(wrappedObj); if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot, v) || !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, wrapperIter, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(nsnull))) { return nsnull; } return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, keysonly); }
nsresult mozilla::dom::bluetooth::StringArrayToJSArray(JSContext* aCx, JSObject* aGlobal, const nsTArray<nsString>& aSourceArray, JSObject** aResultArray) { NS_ASSERTION(aCx, "Null context!"); NS_ASSERTION(aGlobal, "Null global!"); JSAutoRequest ar(aCx); JSAutoEnterCompartment ac; if (!ac.enter(aCx, aGlobal)) { NS_WARNING("Failed to enter compartment!"); return NS_ERROR_FAILURE; } JSObject* arrayObj; if (aSourceArray.IsEmpty()) { arrayObj = JS_NewArrayObject(aCx, 0, nullptr); } else { uint32_t valLength = aSourceArray.Length(); mozilla::ScopedDeleteArray<jsval> valArray(new jsval[valLength]); JS::AutoArrayRooter tvr(aCx, valLength, valArray); for (PRUint32 index = 0; index < valLength; index++) { JSString* s = JS_NewUCStringCopyN(aCx, aSourceArray[index].BeginReading(), aSourceArray[index].Length()); if(!s) { NS_WARNING("Memory allocation error!"); return NS_ERROR_OUT_OF_MEMORY; } valArray[index] = STRING_TO_JSVAL(s); } arrayObj = JS_NewArrayObject(aCx, valLength, valArray); } if (!arrayObj) { return NS_ERROR_OUT_OF_MEMORY; } // XXX This is not what Jonas wants. He wants it to be live. // Followup at bug 717414 if (!JS_FreezeObject(aCx, arrayObj)) { return NS_ERROR_FAILURE; } *aResultArray = arrayObj; return NS_OK; }
static JSBool JA(JSContext *cx, jsval *vp, StringifyContext *scx) { JSObject *obj = JSVAL_TO_OBJECT(*vp); if (!scx->cb.append('[')) return JS_FALSE; jsuint length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; jsval outputValue = JSVAL_NULL; JSAutoTempValueRooter tvr(cx, 1, &outputValue); jsid id; jsuint i; for (i = 0; i < length; i++) { id = INT_TO_JSID(i); if (!obj->getProperty(cx, id, &outputValue)) return JS_FALSE; if (!Str(cx, id, obj, scx, &outputValue)) return JS_FALSE; if (outputValue == JSVAL_VOID) { if (!js_AppendLiteral(scx->cb, "null")) return JS_FALSE; } if (i < length - 1) { if (!scx->cb.append(',')) return JS_FALSE; if (!WriteIndent(cx, scx, scx->depth)) return JS_FALSE; } } if (length != 0 && !WriteIndent(cx, scx, scx->depth - 1)) return JS_FALSE; return scx->cb.append(']'); }
static JSBool ReifyPropertyOps(JSContext *cx, JSObject *obj, jsval idval, jsid interned_id, const char *name, JSPropertyOp getter, JSPropertyOp setter, JSObject **getterobjp, JSObject **setterobjp) { // Generate both getter and setter and stash them in the prototype. jsval roots[2] = { JSVAL_NULL, JSVAL_NULL }; JSAutoTempValueRooter tvr(cx, 2, roots); uintN attrs = JSPROP_SHARED; JSObject *getterobj; if(getter) { getterobj = GeneratePropertyOp(cx, obj, idval, 0, name, getter); if(!getterobj) return JS_FALSE; roots[0] = OBJECT_TO_JSVAL(getterobj); attrs |= JSPROP_GETTER; } else getterobj = nsnull; JSObject *setterobj; if (setter) { setterobj = GeneratePropertyOp(cx, obj, idval, 1, name, setter); if(!setterobj) return JS_FALSE; roots[1] = OBJECT_TO_JSVAL(setterobj); attrs |= JSPROP_SETTER; } else setterobj = nsnull; if(getterobjp) *getterobjp = getterobj; if(setterobjp) *setterobjp = setterobj; return JS_DefinePropertyById(cx, obj, interned_id, JSVAL_VOID, JS_DATA_TO_FUNC_PTR(JSPropertyOp, getterobj), JS_DATA_TO_FUNC_PTR(JSPropertyOp, setterobj), attrs); }
static JSBool PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value) { JSAutoTempValueRooter tvr(cx, 1, &value); JSBool ok; if (OBJ_IS_ARRAY(cx, parent)) { jsuint len; ok = JS_GetArrayLength(cx, parent, &len); if (ok) ok = JS_SetElement(cx, parent, len, &value); } else { ok = JS_DefineUCProperty(cx, parent, JS_GetStringChars(jp->objectKey), JS_GetStringLength(jp->objectKey), value, NULL, NULL, JSPROP_ENUMERATE); } return ok; }
static JSBool PushObject(JSContext *cx, JSONParser *jp, JSObject *obj) { jsuint len; if (!js_GetLengthProperty(cx, jp->objectStack, &len)) return JS_FALSE; if (len >= JSON_MAX_DEPTH) return JS_FALSE; // decoding error jsval v = OBJECT_TO_JSVAL(obj); JSAutoTempValueRooter tvr(cx, v); // Check if this is the root object if (len == 0) { *jp->rootVal = v; // This property must be enumerable to keep the array dense if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(0), *jp->rootVal, NULL, NULL, JSPROP_ENUMERATE, NULL)) { return JS_FALSE; } return JS_TRUE; } jsval p; if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &p)) return JS_FALSE; JS_ASSERT(JSVAL_IS_OBJECT(p)); JSObject *parent = JSVAL_TO_OBJECT(p); if (!PushValue(cx, jp, parent, OBJECT_TO_JSVAL(obj))) return JS_FALSE; // This property must be enumerable to keep the array dense if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len), v, NULL, NULL, JSPROP_ENUMERATE, NULL)) { return JS_FALSE; } return JS_TRUE; }
JSBool js_json_parse(JSContext *cx, uintN argc, jsval *vp) { JSString *s = NULL; jsval *argv = vp + 2; jsval reviver = JSVAL_NULL; JSAutoTempValueRooter tvr(cx, 1, &reviver); if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver)) return JS_FALSE; JSONParser *jp = js_BeginJSONParse(cx, vp); JSBool ok = jp != NULL; if (ok) { const jschar *chars; size_t length; s->getCharsAndLength(chars, length); ok = js_ConsumeJSONText(cx, jp, chars, length); ok &= js_FinishJSONParse(cx, jp, reviver); } return ok; }
static JSBool PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value) { JSAutoTempValueRooter tvr(cx, 1, &value); JSBool ok; if (OBJ_IS_ARRAY(cx, parent)) { jsuint len; ok = js_GetLengthProperty(cx, parent, &len); if (ok) { ok = OBJ_DEFINE_PROPERTY(cx, parent, INT_TO_JSID(len), value, NULL, NULL, JSPROP_ENUMERATE, NULL); } } else { ok = JS_DefineUCProperty(cx, parent, jp->objectKey->base, STRING_BUFFER_OFFSET(jp->objectKey), value, NULL, NULL, JSPROP_ENUMERATE); js_FinishStringBuffer(jp->objectKey); js_InitStringBuffer(jp->objectKey); } return ok; }
static JSBool js_json_stringify(JSContext *cx, JSObject* pThis, uintN argc, jsval *vp, jsval* rval) { JSObject *obj; jsval *argv = vp; // Must throw an Error if there isn't a first arg if (!JS_ConvertArguments(cx, argc, argv, "o", &obj)) return JS_FALSE; // Only use objects and arrays as the root for now jsval v = OBJECT_TO_JSVAL(obj); JSBool ok = js_TryJSON(cx, &v); JSType type; if (!(ok && !JSVAL_IS_PRIMITIVE(v) && (type = JS_TypeOfValue(cx, v)) != JSTYPE_FUNCTION && type != JSTYPE_XML)) { JS_ReportError(cx, "Invalid argument."); return JS_FALSE; } JSString *s = JS_NewStringCopyN(cx, "", 0); if (!s) ok = JS_FALSE; if (ok) { jsval sv = STRING_TO_JSVAL(s); StringifyClosure sc; sc.cx = cx; sc.s = &sv; JSAutoTempValueRooter tvr(cx, 1, sc.s); ok = js_Stringify(cx, &v, NULL, &WriteCallback, &sc, 0); *rval = *sc.s; } return ok; }
static void TestArgFormatter(JSContext* jscontext, JSObject* glob, nsIXPConnect* xpc) { JSBool ok = JS_TRUE; const char* a_in = "some string"; nsCOMPtr<nsITestXPCFoo> b_in = new nsTestXPCFoo(); nsCOMPtr<nsIWritableVariant> c_in = do_CreateInstance("@mozilla.org/variant;1"); static NS_NAMED_LITERAL_STRING(d_in, "foo bar"); const char* e_in = "another meaningless chunck of text"; JSBool a_match; nsCOMPtr<nsISupports> b_out; nsCOMPtr<nsIVariant> c_out; nsAutoString d_out; JSBool e_match; nsCOMPtr<nsITestXPCFoo> specified; PRInt32 val; printf("ArgumentFormatter test: "); if(!b_in || !c_in || NS_FAILED(c_in->SetAsInt32(5))) { printf(" failed to construct test objects -- FAILED!\n"); return; } do { JSAutoRequest ar(jscontext); // Prepare an array of arguments for JS_ConvertArguments jsval argv[5]; js::AutoArrayRooter tvr(jscontext, JS_ARRAY_LENGTH(argv), argv); if (!PushArguments(jscontext, 5, argv, "s %ip %iv %is s", a_in, &NS_GET_IID(nsITestXPCFoo2), b_in.get(), c_in.get(), static_cast<const nsAString*>(&d_in), e_in)) { printf(" could not convert from native to JS -- FAILED!\n"); return; } JSString *a_out, *e_out; ok = JS_ConvertArguments(jscontext, 5, argv, "S %ip %iv %is S", &a_out, static_cast<nsISupports**>(getter_AddRefs(b_out)), static_cast<nsIVariant**>(getter_AddRefs(c_out)), static_cast<nsAString*>(&d_out), &e_out); TAF_CHECK(ok, " could not convert from JS to native -- FAILED!\n"); TAF_CHECK(b_out, " JS to native for %%ip returned NULL -- FAILED!\n"); specified = do_QueryInterface(b_out); TAF_CHECK(specified, " could not QI value JS to native returned -- FAILED!\n"); ok = specified.get() == b_in.get(); TAF_CHECK(ok, " JS to native returned wrong value -- FAILED!\n"); TAF_CHECK(c_out, " JS to native for %%iv returned NULL -- FAILED!\n"); TAF_CHECK(NS_SUCCEEDED(c_out->GetAsInt32(&val)) && val == 5, " JS to native for %%iv holds wrong value -- FAILED!\n"); TAF_CHECK(d_in.Equals(d_out), " JS to native for %%is returned the wrong value -- FAILED!\n"); TAF_CHECK(JS_StringEqualsAscii(jscontext, a_out, a_in, &a_match), " oom -- FAILED!\n"); TAF_CHECK(JS_StringEqualsAscii(jscontext, e_out, e_in, &e_match), " oom -- FAILED!\n"); } while (0); if (!ok) return; if(a_match && e_match) printf("passed\n"); else printf(" conversion OK, but surrounding was mangled -- FAILED!\n"); }
JSBool js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, JSONWriteCallback callback, void *data, uint32 depth) { if (depth > JSON_MAX_DEPTH) return JS_FALSE; /* encoding error */ JSBool ok = JS_TRUE; JSObject *obj = JSVAL_TO_OBJECT(*vp); JSBool isArray = JS_IsArrayObject(cx, obj); jschar output = jschar(isArray ? '[' : '{'); if (!callback(&output, 1, data)) return JS_FALSE; JSObject *iterObj = NULL; jsint i = 0; jsuint length = 0; if (isArray) { if (!JS_GetArrayLength(cx, obj, &length)) return JS_FALSE; } else { if (!js_ValueToIterator(cx, JSITER_ENUMERATE, vp)) return JS_FALSE; iterObj = JSVAL_TO_OBJECT(*vp); } jsval outputValue = JSVAL_VOID; JSAutoTempValueRooter tvr(cx, 1, &outputValue); jsval key; JSBool memberWritten = JS_FALSE; do { outputValue = JSVAL_VOID; if (isArray) { if ((jsuint)i >= length) break; ok = JS_GetElement(cx, obj, i++, &outputValue); } else { ok = js_CallIteratorNext(cx, iterObj, &key); if (!ok) break; if (key == JSVAL_HOLE) break; JSString *ks; if (JSVAL_IS_STRING(key)) { ks = JSVAL_TO_STRING(key); } else { ks = JS_ValueToString(cx, key); if (!ks) { ok = JS_FALSE; break; } } ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks), JS_GetStringLength(ks), &outputValue); } if (!ok) break; // if this is an array, holes are transmitted as null if (isArray && outputValue == JSVAL_VOID) { outputValue = JSVAL_NULL; } else if (JSVAL_IS_OBJECT(outputValue)) { ok = js_TryJSON(cx, &outputValue); if (!ok) break; } // elide undefined values if (outputValue == JSVAL_VOID) continue; // output a comma unless this is the first member to write if (memberWritten) { output = jschar(','); ok = callback(&output, 1, data); if (!ok) break; } memberWritten = JS_TRUE; JSType type = JS_TypeOfValue(cx, outputValue); // Can't encode these types, so drop them if (type == JSTYPE_FUNCTION || type == JSTYPE_XML) break; // Be careful below, this string is weakly rooted. JSString *s; // If this isn't an array, we need to output a key if (!isArray) { s = JS_ValueToString(cx, key); if (!s) { ok = JS_FALSE; break; } ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s)); if (!ok) break; output = jschar(':'); ok = callback(&output, 1, data); if (!ok) break; } if (!JSVAL_IS_PRIMITIVE(outputValue)) { // recurse ok = js_Stringify(cx, &outputValue, replacer, callback, data, depth + 1); } else { JSString *outputString; s = JS_ValueToString(cx, outputValue); if (!s) { ok = JS_FALSE; break; } if (type == JSTYPE_STRING) { ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s)); if (!ok) break; continue; } if (type == JSTYPE_NUMBER) { if (JSVAL_IS_DOUBLE(outputValue)) { jsdouble d = *JSVAL_TO_DOUBLE(outputValue); if (!JSDOUBLE_IS_FINITE(d)) outputString = JS_NewStringCopyN(cx, "null", 4); else outputString = s; } else { outputString = s; } } else if (type == JSTYPE_BOOLEAN) { outputString = s; } else if (JSVAL_IS_NULL(outputValue)) { outputString = JS_NewStringCopyN(cx, "null", 4); } else { ok = JS_FALSE; // encoding error break; } ok = callback(JS_GetStringChars(outputString), JS_GetStringLength(outputString), data); } } while (ok); if (iterObj) { // Always close the iterator, but make sure not to stomp on OK js_CloseIteratorState(cx, iterObj); // encoding error or propagate? FIXME: Bug 408838. } if (!ok) { JS_ReportError(cx, "Error during JSON encoding."); return JS_FALSE; } output = jschar(isArray ? ']' : '}'); ok = callback(&output, 1, data); return ok; }
static JSBool JO(JSContext *cx, jsval *vp, StringifyContext *scx) { JSObject *obj = JSVAL_TO_OBJECT(*vp); if (!scx->cb.append('{')) return JS_FALSE; jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; JSAutoTempValueRooter tvr(cx, 3, vec); jsval& key = vec[0]; jsval& outputValue = vec[1]; JSObject *iterObj = NULL; jsval *keySource = vp; bool usingWhitelist = false; // if the replacer is an array, we use the keys from it if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) { usingWhitelist = true; vec[2] = OBJECT_TO_JSVAL(scx->replacer); keySource = &vec[2]; } if (!js_ValueToIterator(cx, JSITER_ENUMERATE, keySource)) return JS_FALSE; iterObj = JSVAL_TO_OBJECT(*keySource); JSBool memberWritten = JS_FALSE; bool ok = false; while (true) { outputValue = JSVAL_VOID; if (!js_CallIteratorNext(cx, iterObj, &key)) goto error_break; if (key == JSVAL_HOLE) break; jsuint index = 0; if (usingWhitelist) { // skip non-index properties if (!js_IdIsIndex(key, &index)) continue; jsval newKey; if (!scx->replacer->getProperty(cx, key, &newKey)) goto error_break; key = newKey; } JSString *ks; if (JSVAL_IS_STRING(key)) { ks = JSVAL_TO_STRING(key); } else { ks = js_ValueToString(cx, key); if (!ks) goto error_break; } JSAutoTempValueRooter keyStringRoot(cx, ks); // Don't include prototype properties, since this operation is // supposed to be implemented as if by ES3.1 Object.keys() jsid id; jsval v = JS_FALSE; if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) || !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) { goto error_break; } if (v != JSVAL_TRUE) continue; if (!JS_GetPropertyById(cx, obj, id, &outputValue)) goto error_break; if (JSVAL_IS_OBJECT(outputValue) && !js_TryJSON(cx, &outputValue)) goto error_break; // call this here, so we don't write out keys if the replacer function // wants to elide the value. if (!CallReplacerFunction(cx, id, obj, scx, &outputValue)) goto error_break; JSType type = JS_TypeOfValue(cx, outputValue); // elide undefined values and functions and XML if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML) continue; // output a comma unless this is the first member to write if (memberWritten && !scx->cb.append(',')) goto error_break; memberWritten = JS_TRUE; if (!WriteIndent(cx, scx, scx->depth)) goto error_break; // Be careful below, this string is weakly rooted JSString *s = js_ValueToString(cx, key); if (!s) goto error_break; const jschar *chars; size_t length; s->getCharsAndLength(chars, length); if (!write_string(cx, scx->cb, chars, length) || !scx->cb.append(':') || !Str(cx, id, obj, scx, &outputValue, false)) { goto error_break; } } ok = true; error_break: if (iterObj) { // Always close the iterator, but make sure not to stomp on OK JS_ASSERT(OBJECT_TO_JSVAL(iterObj) == *keySource); ok &= js_CloseIterator(cx, *keySource); } if (!ok) return JS_FALSE; if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1)) return JS_FALSE; return scx->cb.append('}'); }
static JSBool CVE_2012_0478_firefox4_0_1_nsIDOMWebGLRenderingContext_TexImage2D(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; nsresult rv; nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) return JS_FALSE; if (argc < 6 || argc == 7 || argc == 8) return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); jsval *argv = JS_ARGV(cx, vp); // arguments common to all cases GET_UINT32_ARG(argv0, 0); GET_INT32_ARG(argv1, 1); if (argc > 5 && !JSVAL_IS_PRIMITIVE(argv[5])) { // implement the variants taking a DOMElement as argv[5] GET_UINT32_ARG(argv2, 2); GET_UINT32_ARG(argv3, 3); GET_UINT32_ARG(argv4, 4); nsIDOMElement *elt; xpc_qsSelfRef eltRef; rv = xpc_qsUnwrapArg<nsIDOMElement>(cx, argv[5], &elt, &eltRef.ptr, &argv[5]); if (NS_FAILED(rv)) return JS_FALSE; rv = self->TexImage2D_dom(argv0, argv1, argv2, argv3, argv4, elt); if (NS_FAILED(rv)) { // failed to interprete argv[5] as a DOMElement, now try to interprete it as ImageData JSObject *argv5 = JSVAL_TO_OBJECT(argv[5]); jsval js_width, js_height, js_data; JS_GetProperty(cx, argv5, "width", &js_width); JS_GetProperty(cx, argv5, "height", &js_height); JS_GetProperty(cx, argv5, "data", &js_data); if (js_width == JSVAL_VOID || js_height == JSVAL_VOID || js_data == JSVAL_VOID) { xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 5); return JS_FALSE; } int32 int_width, int_height; JSObject *obj_data = JSVAL_TO_OBJECT(js_data); if (!JS_ValueToECMAInt32(cx, js_width, &int_width) || !JS_ValueToECMAInt32(cx, js_height, &int_height)) { return JS_FALSE; } if (!js_IsTypedArray(obj_data)) { xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 5); return JS_FALSE; } rv = self->TexImage2D_imageData(argv0, argv1, argv2, int_width, int_height, 0, argv3, argv4, js::TypedArray::fromJSObject(obj_data)); } } else if (argc > 8 && JSVAL_IS_OBJECT(argv[8])) // here, we allow null ! { // implement the variants taking a buffer/array as argv[8] GET_UINT32_ARG(argv2, 2); GET_INT32_ARG(argv3, 3); GET_INT32_ARG(argv4, 4); GET_INT32_ARG(argv5, 5); GET_UINT32_ARG(argv6, 6); GET_UINT32_ARG(argv7, 7); JSObject *argv8 = JSVAL_TO_OBJECT(argv[8]); // then try to grab either a js::ArrayBuffer, js::TypedArray, or null if (argv8 == nsnull) { rv = self->TexImage2D_buf(argv0, argv1, argv2, argv3, argv4, argv5, argv6, argv7, nsnull); } else if (js_IsArrayBuffer(argv8)) { rv = self->TexImage2D_buf(argv0, argv1, argv2, argv3, argv4, argv5, argv6, argv7, js::ArrayBuffer::fromJSObject(argv8)); } else if (js_IsTypedArray(argv8)) { rv = self->TexImage2D_array(argv0, argv1, argv2, argv3, argv4, argv5, argv6, argv7, js::TypedArray::fromJSObject(argv8)); } else { xpc_qsThrowBadArg(cx, NS_ERROR_FAILURE, vp, 8); return JS_FALSE; } } else { xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); return JS_FALSE; } if (NS_FAILED(rv)) return xpc_qsThrowMethodFailed(cx, rv, vp); *vp = JSVAL_VOID; return JS_TRUE; }
JSObject * js_InitExceptionClasses(JSContext *cx, JSObject *obj) { jsval roots[3]; JSObject *obj_proto, *error_proto; jsval empty; /* * If lazy class initialization occurs for any Error subclass, then all * classes are initialized, starting with Error. To avoid reentry and * redundant initialization, we must not pass a null proto parameter to * js_NewObject below, when called for the Error superclass. We need to * ensure that Object.prototype is the proto of Error.prototype. * * See the equivalent code to ensure that parent_proto is non-null when * JS_InitClass calls js_NewObject, in jsapi.c. */ if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), &obj_proto)) { return NULL; } memset(roots, 0, sizeof(roots)); JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots); #ifdef __GNUC__ error_proto = NULL; /* quell GCC overwarning */ #endif /* Initialize the prototypes first. */ for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) { JSObject *proto; JSProtoKey protoKey; JSAtom *atom; JSFunction *fun; /* Make the prototype for the current constructor name. */ proto = js_NewObject(cx, &js_ErrorClass, (i != JSEXN_ERR) ? error_proto : obj_proto, obj); if (!proto) return NULL; if (i == JSEXN_ERR) { error_proto = proto; roots[0] = OBJECT_TO_JSVAL(proto); } else { // We cannot share the root for error_proto and other prototypes // as error_proto must be rooted until the function returns. roots[1] = OBJECT_TO_JSVAL(proto); } /* So exn_finalize knows whether to destroy private data. */ proto->setPrivate(NULL); /* Make a constructor function for the current name. */ protoKey = GetExceptionProtoKey(i); atom = cx->runtime->atomState.classAtoms[protoKey]; fun = js_DefineFunction(cx, obj, atom, Exception, 3, 0); if (!fun) return NULL; roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun)); /* Make this constructor make objects of class Exception. */ FUN_CLASP(fun) = &js_ErrorClass; /* Make the prototype and constructor links. */ if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto, JSPROP_READONLY | JSPROP_PERMANENT)) { return NULL; } /* Add the name property to the prototype. */ if (!JS_DefineProperty(cx, proto, js_name_str, ATOM_KEY(atom), NULL, NULL, JSPROP_ENUMERATE)) { return NULL; } /* Finally, stash the constructor for later uses. */ if (!js_SetClassObject(cx, obj, protoKey, FUN_OBJECT(fun))) return NULL; } /* * Set default values and add methods. We do it only for Error.prototype * as the rest of exceptions delegate to it. */ empty = STRING_TO_JSVAL(cx->runtime->emptyString); if (!JS_DefineProperty(cx, error_proto, js_message_str, empty, NULL, NULL, JSPROP_ENUMERATE) || !JS_DefineProperty(cx, error_proto, js_fileName_str, empty, NULL, NULL, JSPROP_ENUMERATE) || !JS_DefineProperty(cx, error_proto, js_lineNumber_str, JSVAL_ZERO, NULL, NULL, JSPROP_ENUMERATE) || !JS_DefineFunctions(cx, error_proto, exception_methods)) { return NULL; } return error_proto; }