diff --git a/storage/src/mozStorageStatementParams.cpp b/storage/src/mozStorageStatementParams.cpp index 8bf8258a8dfd..e87f2f47f713 100644 --- a/storage/src/mozStorageStatementParams.cpp +++ b/storage/src/mozStorageStatementParams.cpp @@ -80,6 +80,7 @@ mozStorageStatementParams::GetScriptableFlags(PRUint32 *aScriptableFlags) { *aScriptableFlags = nsIXPCScriptable::WANT_SETPROPERTY | + nsIXPCScriptable::WANT_NEWENUMERATE | nsIXPCScriptable::WANT_NEWRESOLVE | nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE; return NS_OK; @@ -180,7 +181,62 @@ NS_IMETHODIMP mozStorageStatementParams::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, PRUint32 enum_op, jsval * statep, jsid *idp, PRBool *_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); + + switch (enum_op) { + case JSENUMERATE_INIT: + { + // Start our internal index at zero. + *statep = JSVAL_ZERO; + + // And set our length, if needed. + if (idp) + *idp = INT_TO_JSVAL(mParamCount); + + break; + } + case JSENUMERATE_NEXT: + { + NS_ASSERTION(*statep != JSVAL_NULL, "Internal state is null!"); + + // Make sure we are in range first. + PRUint32 index = static_cast(JSVAL_TO_INT(*statep)); + if (index >= mParamCount) { + *statep = JSVAL_NULL; + return NS_OK; + } + + // Get the name of our parameter. + nsCAutoString name; + nsresult rv = mStatement->GetParameterName(index, name); + NS_ENSURE_SUCCESS(rv, rv); + + // But drop the first character, which is going to be a ':'. + JSString *jsname = JS_NewStringCopyN(cx, &(name.get()[1]), + name.Length() - 1); + NS_ENSURE_TRUE(jsname, NS_ERROR_OUT_OF_MEMORY); + + // Set our name. + if (!JS_ValueToId(cx, STRING_TO_JSVAL(jsname), idp)) { + *_retval = PR_FALSE; + return NS_OK; + } + + // And increment our index. + *statep = INT_TO_JSVAL(++index); + + break; + } + case JSENUMERATE_DESTROY: + { + // Clear our state. + *statep = JSVAL_NULL; + + break; + } + } + + return NS_OK; } /* PRBool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in JSVal id, in PRUint32 flags, out JSObjectPtr objp); */ @@ -189,11 +245,18 @@ mozStorageStatementParams::NewResolve(nsIXPConnectWrappedNative *wrapper, JSCont JSObject * obj, jsval id, PRUint32 flags, JSObject * *objp, PRBool *_retval) { NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED); + // We do not throw at any point after this unless our index is out of range + // because we want to allow the prototype chain to be checked for the + // property. PRUint32 idx; if (JSVAL_IS_INT(id)) { idx = JSVAL_TO_INT(id); + + // Ensure that our index is within range. + if (idx >= mParamCount) + return NS_ERROR_INVALID_ARG; } else if (JSVAL_IS_STRING(id)) { JSString *str = JSVAL_TO_STRING(id); @@ -203,23 +266,21 @@ mozStorageStatementParams::NewResolve(nsIXPConnectWrappedNative *wrapper, JSCont nsCAutoString name(":"); name.Append(NS_ConvertUTF16toUTF8(nameChars, nameLength)); - // check to see if there's a parameter with this name + // Check to see if there's a parameter with this name, and if not, let + // the rest of the prototype chain be checked. nsresult rv = mStatement->GetParameterIndex(name, &idx); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) + return NS_OK; - PRBool success = JS_DefineUCProperty(cx, obj, nameChars, nameLength, - JSVAL_VOID, nsnull, nsnull, 0); - NS_ENSURE_TRUE(success, NS_ERROR_FAILURE); + *_retval = JS_DefineUCProperty(cx, obj, nameChars, nameLength, + JSVAL_VOID, nsnull, nsnull, 0); + NS_ENSURE_TRUE(*_retval, NS_OK); } else { // We do not handle other types. - return NS_ERROR_UNEXPECTED; + return NS_OK; } - // Ensure that our index is within range. - if (idx >= mParamCount) - return NS_ERROR_INVALID_ARG; - *_retval = ::JS_DefineElement(cx, obj, idx, JSVAL_VOID, nsnull, nsnull, 0); if (*_retval) *objp = obj; diff --git a/storage/test/unit/test_js_helpers_enumerate.js b/storage/test/unit/test_js_helpers_enumerate.js new file mode 100644 index 000000000000..88664d5eade7 --- /dev/null +++ b/storage/test/unit/test_js_helpers_enumerate.js @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 et + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Storage Test Code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * This file tests that the JS language helpers have the ability to enumerate + * their properties. + */ + +//////////////////////////////////////////////////////////////////////////////// +//// Test Functions + +function test_params_enumerate() +{ + let stmt = getOpenedDatabase().createStatement( + "SELECT * FROM test WHERE id IN (:a, :b, :c)" + ); + + // Make sure they are right. + let expected = ["a", "b", "c"]; + let index = 0; + for (let name in stmt.params) + do_check_eq(name, expected[index++]); +} + + +//////////////////////////////////////////////////////////////////////////////// +//// Test Runner + +let tests = [ + test_params_enumerate, +]; +function run_test() +{ + cleanup(); + + // Create our database. + getOpenedDatabase().executeSimpleSQL( + "CREATE TABLE test (" + + "id INTEGER PRIMARY KEY " + + ")" + ); + + // Run the tests. + tests.forEach(function(test) test()); +}