michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 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: #include "nsMemory.h" michael@0: #include "nsString.h" michael@0: michael@0: #include "jsapi.h" michael@0: michael@0: #include "mozStoragePrivateHelpers.h" michael@0: #include "mozStorageStatementParams.h" michael@0: #include "mozIStorageStatement.h" michael@0: michael@0: namespace mozilla { michael@0: namespace storage { michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// StatementParams michael@0: michael@0: StatementParams::StatementParams(mozIStorageStatement *aStatement) : michael@0: mStatement(aStatement) michael@0: { michael@0: NS_ASSERTION(mStatement != nullptr, "mStatement is null"); michael@0: (void)mStatement->GetParameterCount(&mParamCount); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: StatementParams, michael@0: mozIStorageStatementParams, michael@0: nsIXPCScriptable michael@0: ) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// nsIXPCScriptable michael@0: michael@0: #define XPC_MAP_CLASSNAME StatementParams michael@0: #define XPC_MAP_QUOTED_CLASSNAME "StatementParams" michael@0: #define XPC_MAP_WANT_SETPROPERTY michael@0: #define XPC_MAP_WANT_NEWENUMERATE michael@0: #define XPC_MAP_WANT_NEWRESOLVE michael@0: #define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE michael@0: #include "xpc_map_end.h" michael@0: michael@0: NS_IMETHODIMP michael@0: StatementParams::SetProperty(nsIXPConnectWrappedNative *aWrapper, michael@0: JSContext *aCtx, michael@0: JSObject *aScopeObj, michael@0: jsid aId, michael@0: JS::Value *_vp, michael@0: bool *_retval) michael@0: { michael@0: NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: if (JSID_IS_INT(aId)) { michael@0: int idx = JSID_TO_INT(aId); michael@0: michael@0: nsCOMPtr variant(convertJSValToVariant(aCtx, *_vp)); michael@0: NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED); michael@0: nsresult rv = mStatement->BindByIndex(idx, variant); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else if (JSID_IS_STRING(aId)) { michael@0: JSString *str = JSID_TO_STRING(aId); michael@0: size_t length; michael@0: const jschar *chars = JS_GetStringCharsAndLength(aCtx, str, &length); michael@0: NS_ENSURE_TRUE(chars, NS_ERROR_UNEXPECTED); michael@0: NS_ConvertUTF16toUTF8 name(chars, length); michael@0: michael@0: // check to see if there's a parameter with this name michael@0: nsCOMPtr variant(convertJSValToVariant(aCtx, *_vp)); michael@0: NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED); michael@0: nsresult rv = mStatement->BindByName(name, variant); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: StatementParams::NewEnumerate(nsIXPConnectWrappedNative *aWrapper, michael@0: JSContext *aCtx, michael@0: JSObject *aScopeObj, michael@0: uint32_t aEnumOp, michael@0: jsval *_statep, michael@0: jsid *_idp, michael@0: bool *_retval) michael@0: { michael@0: NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: switch (aEnumOp) { michael@0: case JSENUMERATE_INIT: michael@0: case JSENUMERATE_INIT_ALL: michael@0: { michael@0: // Start our internal index at zero. michael@0: *_statep = JSVAL_ZERO; michael@0: michael@0: // And set our length, if needed. michael@0: if (_idp) michael@0: *_idp = INT_TO_JSID(mParamCount); michael@0: michael@0: break; michael@0: } michael@0: case JSENUMERATE_NEXT: michael@0: { michael@0: NS_ASSERTION(*_statep != JSVAL_NULL, "Internal state is null!"); michael@0: michael@0: // Make sure we are in range first. michael@0: uint32_t index = static_cast(JSVAL_TO_INT(*_statep)); michael@0: if (index >= mParamCount) { michael@0: *_statep = JSVAL_NULL; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Get the name of our parameter. michael@0: nsAutoCString name; michael@0: nsresult rv = mStatement->GetParameterName(index, name); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // But drop the first character, which is going to be a ':'. michael@0: JS::RootedString jsname(aCtx, ::JS_NewStringCopyN(aCtx, &(name.get()[1]), michael@0: name.Length() - 1)); michael@0: NS_ENSURE_TRUE(jsname, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // Set our name. michael@0: JS::Rooted id(aCtx); michael@0: if (!::JS_StringToId(aCtx, jsname, &id)) { michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: *_idp = id; michael@0: michael@0: // And increment our index. michael@0: *_statep = INT_TO_JSVAL(++index); michael@0: michael@0: break; michael@0: } michael@0: case JSENUMERATE_DESTROY: michael@0: { michael@0: // Clear our state. michael@0: *_statep = JSVAL_NULL; michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: StatementParams::NewResolve(nsIXPConnectWrappedNative *aWrapper, michael@0: JSContext *aCtx, michael@0: JSObject *aScopeObj, michael@0: jsid aId, michael@0: JSObject **_objp, michael@0: bool *_retval) michael@0: { michael@0: NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); michael@0: // We do not throw at any point after this unless our index is out of range michael@0: // because we want to allow the prototype chain to be checked for the michael@0: // property. michael@0: michael@0: JS::RootedObject scope(aCtx, aScopeObj); michael@0: JS::RootedId id(aCtx, aId); michael@0: bool resolved = false; michael@0: bool ok = true; michael@0: if (JSID_IS_INT(id)) { michael@0: uint32_t idx = JSID_TO_INT(id); michael@0: michael@0: // Ensure that our index is within range. We do not care about the michael@0: // prototype chain being checked here. michael@0: if (idx >= mParamCount) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: ok = ::JS_DefineElement(aCtx, scope, idx, JSVAL_VOID, nullptr, michael@0: nullptr, JSPROP_ENUMERATE); michael@0: resolved = true; michael@0: } michael@0: else if (JSID_IS_STRING(id)) { michael@0: JSString *str = JSID_TO_STRING(id); michael@0: size_t nameLength; michael@0: const jschar *nameChars = JS_GetStringCharsAndLength(aCtx, str, &nameLength); michael@0: NS_ENSURE_TRUE(nameChars, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Check to see if there's a parameter with this name, and if not, let michael@0: // the rest of the prototype chain be checked. michael@0: NS_ConvertUTF16toUTF8 name(nameChars, nameLength); michael@0: uint32_t idx; michael@0: nsresult rv = mStatement->GetParameterIndex(name, &idx); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ok = ::JS_DefinePropertyById(aCtx, scope, id, JSVAL_VOID, nullptr, michael@0: nullptr, JSPROP_ENUMERATE); michael@0: resolved = true; michael@0: } michael@0: } michael@0: michael@0: *_retval = ok; michael@0: *_objp = resolved && ok ? scope.get() : nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace storage michael@0: } // namespace mozilla