michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* nsIVariant implementation for xpconnect. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: #include "jsfriendapi.h" michael@0: #include "jsprf.h" michael@0: #include "jswrapper.h" michael@0: michael@0: using namespace JS; michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_CLASSINFO(XPCVariant, nullptr, 0, XPCVARIANT_CID) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCVariant) michael@0: NS_INTERFACE_MAP_ENTRY(XPCVariant) michael@0: NS_INTERFACE_MAP_ENTRY(nsIVariant) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_IMPL_QUERY_CLASSINFO(XPCVariant) michael@0: NS_INTERFACE_MAP_END michael@0: NS_IMPL_CI_INTERFACE_GETTER(XPCVariant, XPCVariant, nsIVariant) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant) michael@0: michael@0: XPCVariant::XPCVariant(JSContext* cx, jsval aJSVal) michael@0: : mJSVal(aJSVal), mCCGeneration(0) michael@0: { michael@0: nsVariant::Initialize(&mData); michael@0: if (!mJSVal.isPrimitive()) { michael@0: // XXXbholley - The innerization here was from bug 638026. Blake says michael@0: // the basic problem was that we were storing the C++ inner but the JS michael@0: // outer, which meant that, after navigation, the JS inner could be michael@0: // collected, which would cause us to try to recreate the JS inner at michael@0: // some later point after teardown, which would crash. This is shouldn't michael@0: // be a problem anymore because SetParentToWindow will do the right michael@0: // thing, but I'm saving the cleanup here for another day. Blake thinks michael@0: // that we should just not store the WN if we're creating a variant for michael@0: // an outer window. michael@0: JS::RootedObject obj(cx, &mJSVal.toObject()); michael@0: obj = JS_ObjectToInnerObject(cx, obj); michael@0: mJSVal = JS::ObjectValue(*obj); michael@0: michael@0: JSObject *unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); michael@0: mReturnRawObject = !(unwrapped && IS_WN_REFLECTOR(unwrapped)); michael@0: } else michael@0: mReturnRawObject = false; michael@0: } michael@0: michael@0: XPCTraceableVariant::~XPCTraceableVariant() michael@0: { michael@0: jsval val = GetJSValPreserveColor(); michael@0: michael@0: MOZ_ASSERT(JSVAL_IS_GCTHING(val), "Must be traceable or unlinked"); michael@0: michael@0: // If val is JSVAL_STRING, we don't need to clean anything up; simply michael@0: // removing the string from the root set is good. michael@0: if (!JSVAL_IS_STRING(val)) michael@0: nsVariant::Cleanup(&mData); michael@0: michael@0: if (!JSVAL_IS_NULL(val)) michael@0: RemoveFromRootSet(); michael@0: } michael@0: michael@0: void XPCTraceableVariant::TraceJS(JSTracer* trc) michael@0: { michael@0: MOZ_ASSERT(mJSVal.isMarkable()); michael@0: trc->setTracingDetails(GetTraceName, this, 0); michael@0: JS_CallHeapValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal"); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: XPCTraceableVariant::GetTraceName(JSTracer* trc, char *buf, size_t bufsize) michael@0: { michael@0: JS_snprintf(buf, bufsize, "XPCVariant[0x%p].mJSVal", trc->debugPrintArg()); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant) michael@0: JS::Value val = tmp->GetJSValPreserveColor(); michael@0: if (val.isObjectOrNull()) { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal"); michael@0: cb.NoteJSChild(JSVAL_TO_OBJECT(val)); michael@0: } michael@0: michael@0: nsVariant::Traverse(tmp->mData, cb); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCVariant) michael@0: JS::Value val = tmp->GetJSValPreserveColor(); michael@0: michael@0: // We're sharing val's buffer, clear the pointer to it so Cleanup() won't michael@0: // try to delete it michael@0: if (val.isString()) michael@0: tmp->mData.u.wstr.mWStringValue = nullptr; michael@0: nsVariant::Cleanup(&tmp->mData); michael@0: michael@0: if (val.isMarkable()) { michael@0: XPCTraceableVariant *v = static_cast(tmp); michael@0: v->RemoveFromRootSet(); michael@0: } michael@0: tmp->mJSVal = JS::NullValue(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: // static michael@0: already_AddRefed michael@0: XPCVariant::newVariant(JSContext* cx, jsval aJSVal) michael@0: { michael@0: nsRefPtr variant; michael@0: michael@0: if (!aJSVal.isMarkable()) michael@0: variant = new XPCVariant(cx, aJSVal); michael@0: else michael@0: variant = new XPCTraceableVariant(cx, aJSVal); michael@0: michael@0: if (!variant->InitializeData(cx)) michael@0: return nullptr; michael@0: michael@0: return variant.forget(); michael@0: } michael@0: michael@0: // Helper class to give us a namespace for the table based code below. michael@0: class XPCArrayHomogenizer michael@0: { michael@0: private: michael@0: enum Type michael@0: { michael@0: tNull = 0 , // null value michael@0: tInt , // Integer michael@0: tDbl , // Double michael@0: tBool , // Boolean michael@0: tStr , // String michael@0: tID , // ID michael@0: tArr , // Array michael@0: tISup , // nsISupports (really just a plain JSObject) michael@0: tUnk , // Unknown. Used only for initial state. michael@0: michael@0: tTypeCount , // Just a count for table dimensioning. michael@0: michael@0: tVar , // nsVariant - last ditch if no other common type found. michael@0: tErr // No valid state or type has this value. michael@0: }; michael@0: michael@0: // Table has tUnk as a state (column) but not as a type (row). michael@0: static const Type StateTable[tTypeCount][tTypeCount-1]; michael@0: michael@0: public: michael@0: static bool GetTypeForArray(JSContext* cx, HandleObject array, michael@0: uint32_t length, michael@0: nsXPTType* resultType, nsID* resultID); michael@0: }; michael@0: michael@0: michael@0: // Current state is the column down the side. michael@0: // Current type is the row along the top. michael@0: // New state is in the box at the intersection. michael@0: michael@0: const XPCArrayHomogenizer::Type michael@0: XPCArrayHomogenizer::StateTable[tTypeCount][tTypeCount-1] = { michael@0: /* tNull,tInt ,tDbl ,tBool,tStr ,tID ,tArr ,tISup */ michael@0: /* tNull */{tNull,tVar ,tVar ,tVar ,tStr ,tID ,tVar ,tISup }, michael@0: /* tInt */{tVar ,tInt ,tDbl ,tVar ,tVar ,tVar ,tVar ,tVar }, michael@0: /* tDbl */{tVar ,tDbl ,tDbl ,tVar ,tVar ,tVar ,tVar ,tVar }, michael@0: /* tBool */{tVar ,tVar ,tVar ,tBool,tVar ,tVar ,tVar ,tVar }, michael@0: /* tStr */{tStr ,tVar ,tVar ,tVar ,tStr ,tVar ,tVar ,tVar }, michael@0: /* tID */{tID ,tVar ,tVar ,tVar ,tVar ,tID ,tVar ,tVar }, michael@0: /* tArr */{tErr ,tErr ,tErr ,tErr ,tErr ,tErr ,tErr ,tErr }, michael@0: /* tISup */{tISup,tVar ,tVar ,tVar ,tVar ,tVar ,tVar ,tISup }, michael@0: /* tUnk */{tNull,tInt ,tDbl ,tBool,tStr ,tID ,tVar ,tISup }}; michael@0: michael@0: // static michael@0: bool michael@0: XPCArrayHomogenizer::GetTypeForArray(JSContext* cx, HandleObject array, michael@0: uint32_t length, michael@0: nsXPTType* resultType, nsID* resultID) michael@0: { michael@0: Type state = tUnk; michael@0: Type type; michael@0: michael@0: RootedValue val(cx); michael@0: RootedObject jsobj(cx); michael@0: for (uint32_t i = 0; i < length; i++) { michael@0: if (!JS_GetElement(cx, array, i, &val)) michael@0: return false; michael@0: michael@0: if (val.isInt32()) { michael@0: type = tInt; michael@0: } else if (val.isDouble()) { michael@0: type = tDbl; michael@0: } else if (val.isBoolean()) { michael@0: type = tBool; michael@0: } else if (val.isUndefined()) { michael@0: state = tVar; michael@0: break; michael@0: } else if (val.isNull()) { michael@0: type = tNull; michael@0: } else if (val.isString()) { michael@0: type = tStr; michael@0: } else { michael@0: MOZ_ASSERT(val.isObject(), "invalid type of jsval!"); michael@0: jsobj = &val.toObject(); michael@0: if (JS_IsArrayObject(cx, jsobj)) michael@0: type = tArr; michael@0: else if (xpc_JSObjectIsID(cx, jsobj)) michael@0: type = tID; michael@0: else michael@0: type = tISup; michael@0: } michael@0: michael@0: MOZ_ASSERT(state != tErr, "bad state table!"); michael@0: MOZ_ASSERT(type != tErr, "bad type!"); michael@0: MOZ_ASSERT(type != tVar, "bad type!"); michael@0: MOZ_ASSERT(type != tUnk, "bad type!"); michael@0: michael@0: state = StateTable[state][type]; michael@0: michael@0: MOZ_ASSERT(state != tErr, "bad state table!"); michael@0: MOZ_ASSERT(state != tUnk, "bad state table!"); michael@0: michael@0: if (state == tVar) michael@0: break; michael@0: } michael@0: michael@0: switch (state) { michael@0: case tInt : michael@0: *resultType = nsXPTType((uint8_t)TD_INT32); michael@0: break; michael@0: case tDbl : michael@0: *resultType = nsXPTType((uint8_t)TD_DOUBLE); michael@0: break; michael@0: case tBool: michael@0: *resultType = nsXPTType((uint8_t)TD_BOOL); michael@0: break; michael@0: case tStr : michael@0: *resultType = nsXPTType((uint8_t)TD_PWSTRING); michael@0: break; michael@0: case tID : michael@0: *resultType = nsXPTType((uint8_t)TD_PNSIID); michael@0: break; michael@0: case tISup: michael@0: *resultType = nsXPTType((uint8_t)TD_INTERFACE_IS_TYPE); michael@0: *resultID = NS_GET_IID(nsISupports); michael@0: break; michael@0: case tNull: michael@0: // FALL THROUGH michael@0: case tVar : michael@0: *resultType = nsXPTType((uint8_t)TD_INTERFACE_IS_TYPE); michael@0: *resultID = NS_GET_IID(nsIVariant); michael@0: break; michael@0: case tArr : michael@0: // FALL THROUGH michael@0: case tUnk : michael@0: // FALL THROUGH michael@0: case tErr : michael@0: // FALL THROUGH michael@0: default: michael@0: NS_ERROR("bad state"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool XPCVariant::InitializeData(JSContext* cx) michael@0: { michael@0: JS_CHECK_RECURSION(cx, return false); michael@0: michael@0: RootedValue val(cx, GetJSVal()); michael@0: michael@0: if (val.isInt32()) michael@0: return NS_SUCCEEDED(nsVariant::SetFromInt32(&mData, val.toInt32())); michael@0: if (val.isDouble()) michael@0: return NS_SUCCEEDED(nsVariant::SetFromDouble(&mData, val.toDouble())); michael@0: if (val.isBoolean()) michael@0: return NS_SUCCEEDED(nsVariant::SetFromBool(&mData, val.toBoolean())); michael@0: if (val.isUndefined()) michael@0: return NS_SUCCEEDED(nsVariant::SetToVoid(&mData)); michael@0: if (val.isNull()) michael@0: return NS_SUCCEEDED(nsVariant::SetToEmpty(&mData)); michael@0: if (val.isString()) { michael@0: JSString* str = val.toString(); michael@0: if (!str) michael@0: return false; michael@0: michael@0: // Don't use nsVariant::SetFromWStringWithSize, because that will copy michael@0: // the data. Just handle this ourselves. Note that it's ok to not michael@0: // copy because we added mJSVal as a GC root. michael@0: MOZ_ASSERT(mData.mType == nsIDataType::VTYPE_EMPTY, michael@0: "Why do we already have data?"); michael@0: michael@0: // Despite the fact that the variant holds the length, there are michael@0: // implicit assumptions that mWStringValue[mWStringLength] == 0 michael@0: size_t length; michael@0: const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length); michael@0: if (!chars) michael@0: return false; michael@0: michael@0: mData.u.wstr.mWStringValue = const_cast(chars); michael@0: // Use C-style cast, because reinterpret cast from size_t to michael@0: // uint32_t is not valid on some platforms. michael@0: mData.u.wstr.mWStringLength = (uint32_t)length; michael@0: mData.mType = nsIDataType::VTYPE_WSTRING_SIZE_IS; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // leaving only JSObject... michael@0: MOZ_ASSERT(val.isObject(), "invalid type of jsval!"); michael@0: michael@0: RootedObject jsobj(cx, &val.toObject()); michael@0: michael@0: // Let's see if it is a xpcJSID. michael@0: michael@0: const nsID* id = xpc_JSObjectToID(cx, jsobj); michael@0: if (id) michael@0: return NS_SUCCEEDED(nsVariant::SetFromID(&mData, *id)); michael@0: michael@0: // Let's see if it is a js array object. michael@0: michael@0: uint32_t len; michael@0: michael@0: if (JS_IsArrayObject(cx, jsobj) && JS_GetArrayLength(cx, jsobj, &len)) { michael@0: if (!len) { michael@0: // Zero length array michael@0: nsVariant::SetToEmptyArray(&mData); michael@0: return true; michael@0: } michael@0: michael@0: nsXPTType type; michael@0: nsID id; michael@0: michael@0: if (!XPCArrayHomogenizer::GetTypeForArray(cx, jsobj, len, &type, &id)) michael@0: return false; michael@0: michael@0: if (!XPCConvert::JSArray2Native(&mData.u.array.mArrayValue, michael@0: val, len, type, &id, nullptr)) michael@0: return false; michael@0: michael@0: mData.mType = nsIDataType::VTYPE_ARRAY; michael@0: if (type.IsInterfacePointer()) michael@0: mData.u.array.mArrayInterfaceID = id; michael@0: mData.u.array.mArrayCount = len; michael@0: mData.u.array.mArrayType = type.TagPart(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // XXX This could be smarter and pick some more interesting iface. michael@0: michael@0: nsXPConnect* xpc = nsXPConnect::XPConnect(); michael@0: nsCOMPtr wrapper; michael@0: const nsIID& iid = NS_GET_IID(nsISupports); michael@0: michael@0: return NS_SUCCEEDED(xpc->WrapJS(cx, jsobj, michael@0: iid, getter_AddRefs(wrapper))) && michael@0: NS_SUCCEEDED(nsVariant::SetFromInterface(&mData, iid, wrapper)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XPCVariant::GetAsJSVal(MutableHandleValue result) michael@0: { michael@0: result.set(GetJSVal()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: XPCVariant::VariantDataToJS(nsIVariant* variant, michael@0: nsresult* pErr, MutableHandleValue pJSVal) michael@0: { michael@0: // Get the type early because we might need to spoof it below. michael@0: uint16_t type; michael@0: if (NS_FAILED(variant->GetDataType(&type))) michael@0: return false; michael@0: michael@0: AutoJSContext cx; michael@0: RootedValue realVal(cx); michael@0: nsresult rv = variant->GetAsJSVal(&realVal); michael@0: michael@0: if (NS_SUCCEEDED(rv) && michael@0: (JSVAL_IS_PRIMITIVE(realVal) || michael@0: type == nsIDataType::VTYPE_ARRAY || michael@0: type == nsIDataType::VTYPE_EMPTY_ARRAY || michael@0: type == nsIDataType::VTYPE_ID)) { michael@0: if (!JS_WrapValue(cx, &realVal)) michael@0: return false; michael@0: pJSVal.set(realVal); michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr xpcvariant = do_QueryInterface(variant); michael@0: if (xpcvariant && xpcvariant->mReturnRawObject) { michael@0: MOZ_ASSERT(type == nsIDataType::VTYPE_INTERFACE || michael@0: type == nsIDataType::VTYPE_INTERFACE_IS, michael@0: "Weird variant"); michael@0: michael@0: if (!JS_WrapValue(cx, &realVal)) michael@0: return false; michael@0: pJSVal.set(realVal); michael@0: return true; michael@0: } michael@0: michael@0: // else, it's an object and we really need to double wrap it if we've michael@0: // already decided that its 'natural' type is as some sort of interface. michael@0: michael@0: // We just fall through to the code below and let it do what it does. michael@0: michael@0: // The nsIVariant is not a XPCVariant (or we act like it isn't). michael@0: // So we extract the data and do the Right Thing. michael@0: michael@0: // We ASSUME that the variant implementation can do these conversions... michael@0: michael@0: nsID iid; michael@0: michael@0: switch (type) { michael@0: case nsIDataType::VTYPE_INT8: michael@0: case nsIDataType::VTYPE_INT16: michael@0: case nsIDataType::VTYPE_INT32: michael@0: case nsIDataType::VTYPE_INT64: michael@0: case nsIDataType::VTYPE_UINT8: michael@0: case nsIDataType::VTYPE_UINT16: michael@0: case nsIDataType::VTYPE_UINT32: michael@0: case nsIDataType::VTYPE_UINT64: michael@0: case nsIDataType::VTYPE_FLOAT: michael@0: case nsIDataType::VTYPE_DOUBLE: michael@0: { michael@0: double d; michael@0: if (NS_FAILED(variant->GetAsDouble(&d))) michael@0: return false; michael@0: pJSVal.setNumber(d); michael@0: return true; michael@0: } michael@0: case nsIDataType::VTYPE_BOOL: michael@0: { michael@0: bool b; michael@0: if (NS_FAILED(variant->GetAsBool(&b))) michael@0: return false; michael@0: pJSVal.setBoolean(b); michael@0: return true; michael@0: } michael@0: case nsIDataType::VTYPE_CHAR: michael@0: { michael@0: char c; michael@0: if (NS_FAILED(variant->GetAsChar(&c))) michael@0: return false; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&c, TD_CHAR, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_WCHAR: michael@0: { michael@0: char16_t wc; michael@0: if (NS_FAILED(variant->GetAsWChar(&wc))) michael@0: return false; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, TD_WCHAR, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_ID: michael@0: { michael@0: if (NS_FAILED(variant->GetAsID(&iid))) michael@0: return false; michael@0: nsID *v = &iid; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, TD_PNSIID, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_ASTRING: michael@0: { michael@0: nsAutoString astring; michael@0: if (NS_FAILED(variant->GetAsAString(astring))) michael@0: return false; michael@0: nsAutoString *v = &astring; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, TD_ASTRING, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_DOMSTRING: michael@0: { michael@0: nsAutoString astring; michael@0: if (NS_FAILED(variant->GetAsAString(astring))) michael@0: return false; michael@0: nsAutoString *v = &astring; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, michael@0: TD_DOMSTRING, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_CSTRING: michael@0: { michael@0: nsAutoCString cString; michael@0: if (NS_FAILED(variant->GetAsACString(cString))) michael@0: return false; michael@0: nsAutoCString *v = &cString; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, michael@0: TD_CSTRING, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_UTF8STRING: michael@0: { michael@0: nsUTF8String utf8String; michael@0: if (NS_FAILED(variant->GetAsAUTF8String(utf8String))) michael@0: return false; michael@0: nsUTF8String *v = &utf8String; michael@0: return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, michael@0: TD_UTF8STRING, &iid, pErr); michael@0: } michael@0: case nsIDataType::VTYPE_CHAR_STR: michael@0: { michael@0: char *pc; michael@0: if (NS_FAILED(variant->GetAsString(&pc))) michael@0: return false; michael@0: bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pc, michael@0: TD_PSTRING, &iid, pErr); michael@0: nsMemory::Free(pc); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_STRING_SIZE_IS: michael@0: { michael@0: char *pc; michael@0: uint32_t size; michael@0: if (NS_FAILED(variant->GetAsStringWithSize(&size, &pc))) michael@0: return false; michael@0: bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pc, michael@0: TD_PSTRING_SIZE_IS, size, pErr); michael@0: nsMemory::Free(pc); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_WCHAR_STR: michael@0: { michael@0: char16_t *pwc; michael@0: if (NS_FAILED(variant->GetAsWString(&pwc))) michael@0: return false; michael@0: bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pwc, michael@0: TD_PSTRING, &iid, pErr); michael@0: nsMemory::Free(pwc); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_WSTRING_SIZE_IS: michael@0: { michael@0: char16_t *pwc; michael@0: uint32_t size; michael@0: if (NS_FAILED(variant->GetAsWStringWithSize(&size, &pwc))) michael@0: return false; michael@0: bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pwc, michael@0: TD_PWSTRING_SIZE_IS, size, pErr); michael@0: nsMemory::Free(pwc); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_INTERFACE: michael@0: case nsIDataType::VTYPE_INTERFACE_IS: michael@0: { michael@0: nsISupports *pi; michael@0: nsID* piid; michael@0: if (NS_FAILED(variant->GetAsInterface(&piid, (void **)&pi))) michael@0: return false; michael@0: michael@0: iid = *piid; michael@0: nsMemory::Free((char*)piid); michael@0: michael@0: bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pi, michael@0: TD_INTERFACE_IS_TYPE, &iid, pErr); michael@0: if (pi) michael@0: pi->Release(); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_ARRAY: michael@0: { michael@0: nsDiscriminatedUnion du; michael@0: nsVariant::Initialize(&du); michael@0: nsresult rv; michael@0: michael@0: rv = variant->GetAsArray(&du.u.array.mArrayType, michael@0: &du.u.array.mArrayInterfaceID, michael@0: &du.u.array.mArrayCount, michael@0: &du.u.array.mArrayValue); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: // must exit via VARIANT_DONE from here on... michael@0: du.mType = nsIDataType::VTYPE_ARRAY; michael@0: bool success = false; michael@0: michael@0: nsXPTType conversionType; michael@0: uint16_t elementType = du.u.array.mArrayType; michael@0: const nsID* pid = nullptr; michael@0: michael@0: switch (elementType) { michael@0: case nsIDataType::VTYPE_INT8: michael@0: case nsIDataType::VTYPE_INT16: michael@0: case nsIDataType::VTYPE_INT32: michael@0: case nsIDataType::VTYPE_INT64: michael@0: case nsIDataType::VTYPE_UINT8: michael@0: case nsIDataType::VTYPE_UINT16: michael@0: case nsIDataType::VTYPE_UINT32: michael@0: case nsIDataType::VTYPE_UINT64: michael@0: case nsIDataType::VTYPE_FLOAT: michael@0: case nsIDataType::VTYPE_DOUBLE: michael@0: case nsIDataType::VTYPE_BOOL: michael@0: case nsIDataType::VTYPE_CHAR: michael@0: case nsIDataType::VTYPE_WCHAR: michael@0: conversionType = nsXPTType((uint8_t)elementType); michael@0: break; michael@0: michael@0: case nsIDataType::VTYPE_ID: michael@0: case nsIDataType::VTYPE_CHAR_STR: michael@0: case nsIDataType::VTYPE_WCHAR_STR: michael@0: conversionType = nsXPTType((uint8_t)elementType); michael@0: break; michael@0: michael@0: case nsIDataType::VTYPE_INTERFACE: michael@0: pid = &NS_GET_IID(nsISupports); michael@0: conversionType = nsXPTType((uint8_t)elementType); michael@0: break; michael@0: michael@0: case nsIDataType::VTYPE_INTERFACE_IS: michael@0: pid = &du.u.array.mArrayInterfaceID; michael@0: conversionType = nsXPTType((uint8_t)elementType); michael@0: break; michael@0: michael@0: // The rest are illegal. michael@0: case nsIDataType::VTYPE_VOID: michael@0: case nsIDataType::VTYPE_ASTRING: michael@0: case nsIDataType::VTYPE_DOMSTRING: michael@0: case nsIDataType::VTYPE_CSTRING: michael@0: case nsIDataType::VTYPE_UTF8STRING: michael@0: case nsIDataType::VTYPE_WSTRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_STRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_ARRAY: michael@0: case nsIDataType::VTYPE_EMPTY_ARRAY: michael@0: case nsIDataType::VTYPE_EMPTY: michael@0: default: michael@0: NS_ERROR("bad type in array!"); michael@0: goto VARIANT_DONE; michael@0: } michael@0: michael@0: success = michael@0: XPCConvert::NativeArray2JS(pJSVal, michael@0: (const void**)&du.u.array.mArrayValue, michael@0: conversionType, pid, michael@0: du.u.array.mArrayCount, pErr); michael@0: michael@0: VARIANT_DONE: michael@0: nsVariant::Cleanup(&du); michael@0: return success; michael@0: } michael@0: case nsIDataType::VTYPE_EMPTY_ARRAY: michael@0: { michael@0: JSObject* array = JS_NewArrayObject(cx, 0); michael@0: if (!array) michael@0: return false; michael@0: pJSVal.setObject(*array); michael@0: return true; michael@0: } michael@0: case nsIDataType::VTYPE_VOID: michael@0: pJSVal.setUndefined(); michael@0: return true; michael@0: case nsIDataType::VTYPE_EMPTY: michael@0: pJSVal.setNull(); michael@0: return true; michael@0: default: michael@0: NS_ERROR("bad type in variant!"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: // XXX These default implementations need to be improved to allow for michael@0: // some more interesting conversions. michael@0: michael@0: michael@0: /* readonly attribute uint16_t dataType; */ michael@0: NS_IMETHODIMP XPCVariant::GetDataType(uint16_t *aDataType) michael@0: { michael@0: *aDataType = mData.mType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* uint8_t getAsInt8 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsInt8(uint8_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToInt8(mData, _retval); michael@0: } michael@0: michael@0: /* int16_t getAsInt16 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsInt16(int16_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToInt16(mData, _retval); michael@0: } michael@0: michael@0: /* int32_t getAsInt32 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsInt32(int32_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToInt32(mData, _retval); michael@0: } michael@0: michael@0: /* int64_t getAsInt64 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsInt64(int64_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToInt64(mData, _retval); michael@0: } michael@0: michael@0: /* uint8_t getAsUint8 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsUint8(uint8_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToUint8(mData, _retval); michael@0: } michael@0: michael@0: /* uint16_t getAsUint16 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsUint16(uint16_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToUint16(mData, _retval); michael@0: } michael@0: michael@0: /* uint32_t getAsUint32 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsUint32(uint32_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToUint32(mData, _retval); michael@0: } michael@0: michael@0: /* uint64_t getAsUint64 (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsUint64(uint64_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToUint64(mData, _retval); michael@0: } michael@0: michael@0: /* float getAsFloat (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsFloat(float *_retval) michael@0: { michael@0: return nsVariant::ConvertToFloat(mData, _retval); michael@0: } michael@0: michael@0: /* double getAsDouble (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsDouble(double *_retval) michael@0: { michael@0: return nsVariant::ConvertToDouble(mData, _retval); michael@0: } michael@0: michael@0: /* bool getAsBool (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsBool(bool *_retval) michael@0: { michael@0: return nsVariant::ConvertToBool(mData, _retval); michael@0: } michael@0: michael@0: /* char getAsChar (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsChar(char *_retval) michael@0: { michael@0: return nsVariant::ConvertToChar(mData, _retval); michael@0: } michael@0: michael@0: /* wchar getAsWChar (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsWChar(char16_t *_retval) michael@0: { michael@0: return nsVariant::ConvertToWChar(mData, _retval); michael@0: } michael@0: michael@0: /* [notxpcom] nsresult getAsID (out nsID retval); */ michael@0: NS_IMETHODIMP_(nsresult) XPCVariant::GetAsID(nsID *retval) michael@0: { michael@0: return nsVariant::ConvertToID(mData, retval); michael@0: } michael@0: michael@0: /* AString getAsAString (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsAString(nsAString & _retval) michael@0: { michael@0: return nsVariant::ConvertToAString(mData, _retval); michael@0: } michael@0: michael@0: /* DOMString getAsDOMString (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsDOMString(nsAString & _retval) michael@0: { michael@0: // A DOMString maps to an AString internally, so we can re-use michael@0: // ConvertToAString here. michael@0: return nsVariant::ConvertToAString(mData, _retval); michael@0: } michael@0: michael@0: /* ACString getAsACString (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsACString(nsACString & _retval) michael@0: { michael@0: return nsVariant::ConvertToACString(mData, _retval); michael@0: } michael@0: michael@0: /* AUTF8String getAsAUTF8String (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsAUTF8String(nsAUTF8String & _retval) michael@0: { michael@0: return nsVariant::ConvertToAUTF8String(mData, _retval); michael@0: } michael@0: michael@0: /* string getAsString (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsString(char **_retval) michael@0: { michael@0: return nsVariant::ConvertToString(mData, _retval); michael@0: } michael@0: michael@0: /* wstring getAsWString (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsWString(char16_t **_retval) michael@0: { michael@0: return nsVariant::ConvertToWString(mData, _retval); michael@0: } michael@0: michael@0: /* nsISupports getAsISupports (); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsISupports(nsISupports **_retval) michael@0: { michael@0: return nsVariant::ConvertToISupports(mData, _retval); michael@0: } michael@0: michael@0: /* void getAsInterface (out nsIIDPtr iid, [iid_is (iid), retval] out nsQIResult iface); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsInterface(nsIID * *iid, void * *iface) michael@0: { michael@0: return nsVariant::ConvertToInterface(mData, iid, iface); michael@0: } michael@0: michael@0: michael@0: /* [notxpcom] nsresult getAsArray (out uint16_t type, out nsIID iid, out uint32_t count, out voidPtr ptr); */ michael@0: NS_IMETHODIMP_(nsresult) XPCVariant::GetAsArray(uint16_t *type, nsIID *iid, uint32_t *count, void * *ptr) michael@0: { michael@0: return nsVariant::ConvertToArray(mData, type, iid, count, ptr); michael@0: } michael@0: michael@0: /* void getAsStringWithSize (out uint32_t size, [size_is (size), retval] out string str); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsStringWithSize(uint32_t *size, char **str) michael@0: { michael@0: return nsVariant::ConvertToStringWithSize(mData, size, str); michael@0: } michael@0: michael@0: /* void getAsWStringWithSize (out uint32_t size, [size_is (size), retval] out wstring str); */ michael@0: NS_IMETHODIMP XPCVariant::GetAsWStringWithSize(uint32_t *size, char16_t **str) michael@0: { michael@0: return nsVariant::ConvertToWStringWithSize(mData, size, str); michael@0: } michael@0: michael@0: