/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "xpctest_private.h" #include "xpctest_interfaces.h" #include "mozilla/Casting.h" #include "js/Value.h" #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" #include "nsIURI.h" using namespace mozilla; NS_IMPL_ISUPPORTS(nsXPCTestParams, nsIXPCTestParams) #define GENERIC_METHOD_IMPL \ { \ *_retval = *b; \ *b = a; \ return NS_OK; \ } #define STRING_METHOD_IMPL \ { \ _retval.Assign(b); \ b.Assign(a); \ return NS_OK; \ } #define SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP) \ { \ _retval = std::move(b); \ b = a.Clone(); \ for (uint32_t i = 0; i < b.Length(); ++i) TAKE_OWNERSHIP(b[i]); \ return NS_OK; \ } #define TAKE_OWNERSHIP_NOOP(val) \ {} #define TAKE_OWNERSHIP_INTERFACE(val) \ { static_cast(val)->AddRef(); } #define TAKE_OWNERSHIP_STRING(val) \ { \ nsDependentCString vprime(val); \ val = ToNewCString(vprime); \ } #define TAKE_OWNERSHIP_WSTRING(val) \ { \ nsDependentString vprime(val); \ val = ToNewUnicode(vprime); \ } // Macro for our buffer-oriented types: // 'type' is the type of element that the buffer contains. // 'padding' is an offset added to length, allowing us to handle // null-terminated strings. // 'TAKE_OWNERSHIP' is one of the macros above. #define BUFFER_METHOD_IMPL(type, padding, TAKE_OWNERSHIP) \ { \ uint32_t elemSize = sizeof(type); \ \ /* Copy b into rv. */ \ *rvLength = *bLength; \ *rv = static_cast(moz_xmalloc(elemSize * (*bLength + padding))); \ memcpy(*rv, *b, elemSize*(*bLength + padding)); \ \ /* Copy a into b. */ \ *bLength = aLength; \ free(*b); \ *b = static_cast(moz_xmalloc(elemSize * (aLength + padding))); \ memcpy(*b, a, elemSize*(aLength + padding)); \ \ /* We need to take ownership of the data we got from a, \ since the caller owns it. */ \ for (unsigned i = 0; i < *bLength + padding; ++i) TAKE_OWNERSHIP((*b)[i]); \ \ return NS_OK; \ } NS_IMETHODIMP nsXPCTestParams::TestBoolean(bool a, bool* b, bool* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestOctet(uint8_t a, uint8_t* b, uint8_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestShort(int16_t a, int16_t* b, int16_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestLong(int32_t a, int32_t* b, int32_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestLongLong(int64_t a, int64_t* b, int64_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestUnsignedShort(uint16_t a, uint16_t* b, uint16_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestUnsignedLong(uint32_t a, uint32_t* b, uint32_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestUnsignedLongLong(uint64_t a, uint64_t* b, uint64_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestFloat(float a, float* b, float* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestDouble(double a, float* b, double* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestChar(char a, char* b, char* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestString(const char* a, char** b, char** _retval) { nsDependentCString aprime(a); nsDependentCString bprime(*b); *_retval = ToNewCString(bprime); *b = ToNewCString(aprime); // XPCOM ownership rules dictate that overwritten inout params must be // callee-freed. See https://developer.mozilla.org/en/XPIDL free(const_cast(bprime.get())); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestWchar(char16_t a, char16_t* b, char16_t* _retval) { GENERIC_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestWstring(const char16_t* a, char16_t** b, char16_t** _retval) { nsDependentString aprime(a); nsDependentString bprime(*b); *_retval = ToNewUnicode(bprime); *b = ToNewUnicode(aprime); // XPCOM ownership rules dictate that overwritten inout params must be // callee-freed. See https://developer.mozilla.org/en/XPIDL free((void*)bprime.get()); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestAString(const nsAString& a, nsAString& b, nsAString& _retval) { STRING_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestAUTF8String(const nsACString& a, nsACString& b, nsACString& _retval) { STRING_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestACString(const nsACString& a, nsACString& b, nsACString& _retval) { STRING_METHOD_IMPL; } NS_IMETHODIMP nsXPCTestParams::TestJsval(JS::Handle a, JS::MutableHandle b, JS::MutableHandle _retval) { _retval.set(b); b.set(a); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestShortArray(uint32_t aLength, int16_t* a, uint32_t* bLength, int16_t** b, uint32_t* rvLength, int16_t** rv) { BUFFER_METHOD_IMPL(int16_t, 0, TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(uint32_t aLength, double* a, uint32_t* bLength, double** b, uint32_t* rvLength, double** rv) { BUFFER_METHOD_IMPL(double, 0, TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestByteArrayOptionalLength(uint8_t* a, uint32_t aLength, uint32_t* rv) { *rv = aLength; return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestStringArray(uint32_t aLength, const char** a, uint32_t* bLength, char*** b, uint32_t* rvLength, char*** rv) { BUFFER_METHOD_IMPL(char*, 0, TAKE_OWNERSHIP_STRING); } NS_IMETHODIMP nsXPCTestParams::TestWstringArray( uint32_t aLength, const char16_t** a, uint32_t* bLength, char16_t*** b, uint32_t* rvLength, char16_t*** rv) { BUFFER_METHOD_IMPL(char16_t*, 0, TAKE_OWNERSHIP_WSTRING); } NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray( uint32_t aLength, nsIXPCTestInterfaceA** a, uint32_t* bLength, nsIXPCTestInterfaceA*** b, uint32_t* rvLength, nsIXPCTestInterfaceA*** rv) { BUFFER_METHOD_IMPL(nsIXPCTestInterfaceA*, 0, TAKE_OWNERSHIP_INTERFACE); } NS_IMETHODIMP nsXPCTestParams::TestJsvalArray(uint32_t aLength, JS::Value* a, uint32_t* bLength, JS::Value** b, uint32_t* rvLength, JS::Value** rv) { BUFFER_METHOD_IMPL(JS::Value, 0, TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char* a, uint32_t* bLength, char** b, uint32_t* rvLength, char** rv) { BUFFER_METHOD_IMPL(char, 1, TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestSizedWstring(uint32_t aLength, const char16_t* a, uint32_t* bLength, char16_t** b, uint32_t* rvLength, char16_t** rv) { BUFFER_METHOD_IMPL(char16_t, 1, TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestInterfaceIs(const nsIID* aIID, void* a, nsIID** bIID, void** b, nsIID** rvIID, void** rv) { // // Getting the buffers and ownership right here can be a little tricky. // // The interface pointers are heap-allocated, and b has been AddRef'd // by XPConnect for the duration of the call. If we snatch it away from b // and leave no trace, XPConnect won't Release it. Since we also need to // return an already-AddRef'd pointer in rv, we don't need to do anything // special here. *rv = *b; // rvIID is out-only, so nobody allocated an IID buffer for us. Do that now, // and store b's IID in the new buffer. *rvIID = static_cast(moz_xmalloc(sizeof(nsID))); **rvIID = **bIID; // Copy the interface pointer from a to b. Since a is in-only, XPConnect will // release it upon completion of the call. AddRef it for b. *b = a; static_cast(*b)->AddRef(); // We already had a buffer allocated for b's IID, so we can re-use it. **bIID = *aIID; return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestInterfaceIsArray( uint32_t aLength, const nsIID* aIID, void** a, uint32_t* bLength, nsIID** bIID, void*** b, uint32_t* rvLength, nsIID** rvIID, void*** rv) { // Transfer the IIDs. See the comments in TestInterfaceIs (above) for an // explanation of what we're doing. *rvIID = static_cast(moz_xmalloc(sizeof(nsID))); **rvIID = **bIID; **bIID = *aIID; // The macro is agnostic to the actual interface types, so we can re-use code // here. // // Do this second, since the macro returns. BUFFER_METHOD_IMPL(void*, 0, TAKE_OWNERSHIP_INTERFACE); } NS_IMETHODIMP nsXPCTestParams::TestOutAString(nsAString& o) { o.AssignLiteral("out"); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char** a, uint32_t length, nsACString& out) { out.Truncate(); for (uint32_t i = 0; i < length; ++i) { out.Append(a[i]); } return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestShortSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestDoubleSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestInterfaceSequence( const nsTArray>& a, nsTArray>& b, nsTArray>& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestAStringSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestACStringSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestJsvalSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) { SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); } NS_IMETHODIMP nsXPCTestParams::TestSequenceSequence(const nsTArray>& a, nsTArray>& b, nsTArray>& _retval) { _retval = std::move(b); for (const auto& element : a) { b.AppendElement(element.Clone()); } return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestInterfaceIsSequence(const nsIID* aIID, const nsTArray& a, nsIID** bIID, nsTArray& b, nsIID** rvIID, nsTArray& _retval) { // Shuffle around our nsIIDs *rvIID = (*bIID)->Clone(); *bIID = aIID->Clone(); // Perform the generic sequence shuffle. SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_INTERFACE); } NS_IMETHODIMP nsXPCTestParams::TestOptionalSequence(const nsTArray& aInArr, nsTArray& aReturnArr) { aReturnArr = aInArr.Clone(); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::TestOmittedOptionalOut(nsIXPCTestParams* aJSObj, nsIURI** aOut) { MOZ_ASSERT(!(*aOut), "Unexpected value received"); // Call the js component, to check XPConnect won't crash when passing nullptr // as the optional out parameter, and that the out object is built regardless. nsresult rv; // Invoke it directly passing nullptr. rv = aJSObj->TestOmittedOptionalOut(nullptr, nullptr); NS_ENSURE_SUCCESS(rv, rv); // Also invoke it with a ref pointer. nsCOMPtr someURI; rv = aJSObj->TestOmittedOptionalOut(nullptr, getter_AddRefs(someURI)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString spec; rv = someURI->GetSpec(spec); if (!spec.EqualsLiteral("http://example.com/")) { return NS_ERROR_UNEXPECTED; } someURI.forget(aOut); return NS_OK; } NS_IMETHODIMP nsXPCTestParams::GetTestNaN(double* aResult) { *aResult = BitwiseCast((uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) + 1); return NS_OK; }