mozStorageStatementJSHelper.cpp (7876B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsIXPConnect.h" 8 #include "mozStorageStatement.h" 9 #include "mozStorageService.h" 10 11 #include "nsString.h" 12 #include "nsServiceManagerUtils.h" 13 14 #include "mozStorageStatementJSHelper.h" 15 16 #include "mozStorageStatementRow.h" 17 #include "mozStorageStatementParams.h" 18 19 #include "jsapi.h" 20 #include "js/PropertyAndElement.h" // JS_DefineFunction, JS_DefineProperty, JS_DefinePropertyById 21 #include "js/Value.h" 22 23 #include "xpc_make_class.h" 24 25 namespace mozilla { 26 namespace storage { 27 28 //////////////////////////////////////////////////////////////////////////////// 29 //// Global Functions 30 31 static bool stepFunc(JSContext* aCtx, uint32_t argc, JS::Value* _vp) { 32 JS::CallArgs args = CallArgsFromVp(argc, _vp); 33 34 nsCOMPtr<nsIXPConnect> xpc(nsIXPConnect::XPConnect()); 35 nsCOMPtr<nsIXPConnectWrappedNative> wrapper; 36 37 if (!args.thisv().isObject()) { 38 ::JS_ReportErrorASCII(aCtx, "mozIStorageStatement::step() requires object"); 39 return false; 40 } 41 42 JS::Rooted<JSObject*> obj(aCtx, &args.thisv().toObject()); 43 nsresult rv = 44 xpc->GetWrappedNativeOfJSObject(aCtx, obj, getter_AddRefs(wrapper)); 45 if (NS_FAILED(rv)) { 46 ::JS_ReportErrorASCII( 47 aCtx, "mozIStorageStatement::step() could not obtain native statement"); 48 return false; 49 } 50 51 #ifdef DEBUG 52 { 53 nsCOMPtr<mozIStorageStatement> isStatement( 54 do_QueryInterface(wrapper->Native())); 55 NS_ASSERTION(isStatement, "How is this not a statement?!"); 56 } 57 #endif 58 59 Statement* stmt = static_cast<Statement*>( 60 static_cast<mozIStorageStatement*>(wrapper->Native())); 61 62 bool hasMore = false; 63 rv = stmt->ExecuteStep(&hasMore); 64 if (NS_SUCCEEDED(rv) && !hasMore) { 65 args.rval().setBoolean(false); 66 (void)stmt->Reset(); 67 return true; 68 } 69 70 if (NS_FAILED(rv)) { 71 ::JS_ReportErrorASCII(aCtx, 72 "mozIStorageStatement::step() returned an error"); 73 return false; 74 } 75 76 args.rval().setBoolean(hasMore); 77 return true; 78 } 79 80 //////////////////////////////////////////////////////////////////////////////// 81 //// StatementJSHelper 82 83 nsresult StatementJSHelper::getRow(Statement* aStatement, JSContext* aCtx, 84 JSObject* aScopeObj, JS::Value* _row) { 85 MOZ_ASSERT(NS_IsMainThread()); 86 87 #ifdef DEBUG 88 int32_t state; 89 (void)aStatement->GetState(&state); 90 NS_ASSERTION(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING, 91 "Invalid state to get the row object - all calls will fail!"); 92 #endif 93 94 JS::Rooted<JSObject*> scope(aCtx, aScopeObj); 95 96 if (!aStatement->mStatementRowHolder) { 97 dom::GlobalObject global(aCtx, scope); 98 if (global.Failed()) { 99 return NS_ERROR_UNEXPECTED; 100 } 101 102 nsCOMPtr<nsPIDOMWindowInner> window = 103 do_QueryInterface(global.GetAsSupports()); 104 105 RefPtr<StatementRow> row(new StatementRow(window, aStatement)); 106 NS_ENSURE_TRUE(row, NS_ERROR_OUT_OF_MEMORY); 107 108 RefPtr<StatementRowHolder> rowHolder = new StatementRowHolder(row); 109 NS_ENSURE_TRUE(rowHolder, NS_ERROR_OUT_OF_MEMORY); 110 111 aStatement->mStatementRowHolder = 112 new nsMainThreadPtrHolder<StatementRowHolder>( 113 "Statement::mStatementRowHolder", rowHolder); 114 } 115 116 RefPtr<StatementRow> row(aStatement->mStatementRowHolder->Get()); 117 JSObject* obj = row->WrapObject(aCtx, nullptr); 118 if (!obj) { 119 return NS_ERROR_UNEXPECTED; 120 } 121 122 _row->setObject(*obj); 123 return NS_OK; 124 } 125 126 nsresult StatementJSHelper::getParams(Statement* aStatement, JSContext* aCtx, 127 JSObject* aScopeObj, JS::Value* _params) { 128 MOZ_ASSERT(NS_IsMainThread()); 129 130 #ifdef DEBUG 131 int32_t state; 132 (void)aStatement->GetState(&state); 133 NS_ASSERTION(state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_READY, 134 "Invalid state to get the params object - all calls will fail!"); 135 #endif 136 137 JS::Rooted<JSObject*> scope(aCtx, aScopeObj); 138 139 if (!aStatement->mStatementParamsHolder) { 140 dom::GlobalObject global(aCtx, scope); 141 if (global.Failed()) { 142 return NS_ERROR_UNEXPECTED; 143 } 144 145 nsCOMPtr<nsPIDOMWindowInner> window = 146 do_QueryInterface(global.GetAsSupports()); 147 148 RefPtr<StatementParams> params(new StatementParams(window, aStatement)); 149 NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY); 150 151 RefPtr<StatementParamsHolder> paramsHolder = 152 new StatementParamsHolder(params); 153 NS_ENSURE_TRUE(paramsHolder, NS_ERROR_OUT_OF_MEMORY); 154 155 aStatement->mStatementParamsHolder = 156 new nsMainThreadPtrHolder<StatementParamsHolder>( 157 "Statement::mStatementParamsHolder", paramsHolder); 158 } 159 160 RefPtr<StatementParams> params(aStatement->mStatementParamsHolder->Get()); 161 JSObject* obj = params->WrapObject(aCtx, nullptr); 162 if (!obj) { 163 return NS_ERROR_UNEXPECTED; 164 } 165 166 _params->setObject(*obj); 167 return NS_OK; 168 } 169 170 NS_IMETHODIMP_(MozExternalRefCountType) StatementJSHelper::AddRef() { 171 return 2; 172 } 173 NS_IMETHODIMP_(MozExternalRefCountType) StatementJSHelper::Release() { 174 return 1; 175 } 176 NS_INTERFACE_MAP_BEGIN(StatementJSHelper) 177 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) 178 NS_INTERFACE_MAP_ENTRY(nsISupports) 179 NS_INTERFACE_MAP_END 180 181 //////////////////////////////////////////////////////////////////////////////// 182 //// nsIXPCScriptable 183 184 #define XPC_MAP_CLASSNAME StatementJSHelper 185 #define XPC_MAP_QUOTED_CLASSNAME "StatementJSHelper" 186 #define XPC_MAP_FLAGS \ 187 (XPC_SCRIPTABLE_WANT_RESOLVE | XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) 188 #include "xpc_map_end.h" 189 190 NS_IMETHODIMP 191 StatementJSHelper::Resolve(nsIXPConnectWrappedNative* aWrapper, JSContext* aCtx, 192 JSObject* aScopeObj, jsid aId, bool* aResolvedp, 193 bool* _retval) { 194 if (!aId.isString()) return NS_OK; 195 196 JS::Rooted<JSObject*> scope(aCtx, aScopeObj); 197 JS::Rooted<jsid> id(aCtx, aId); 198 199 #ifdef DEBUG 200 { 201 nsCOMPtr<mozIStorageStatement> isStatement( 202 do_QueryInterface(aWrapper->Native())); 203 NS_ASSERTION(isStatement, "How is this not a statement?!"); 204 } 205 #endif 206 207 Statement* stmt = static_cast<Statement*>( 208 static_cast<mozIStorageStatement*>(aWrapper->Native())); 209 210 JSLinearString* str = id.toLinearString(); 211 if (::JS_LinearStringEqualsLiteral(str, "step")) { 212 *_retval = ::JS_DefineFunction(aCtx, scope, "step", stepFunc, 0, 213 JSPROP_RESOLVING) != nullptr; 214 *aResolvedp = true; 215 return NS_OK; 216 } 217 218 JS::Rooted<JS::Value> val(aCtx); 219 220 if (::JS_LinearStringEqualsLiteral(str, "row")) { 221 nsresult rv = getRow(stmt, aCtx, scope, val.address()); 222 NS_ENSURE_SUCCESS(rv, rv); 223 *_retval = ::JS_DefinePropertyById(aCtx, scope, id, val, JSPROP_RESOLVING); 224 *aResolvedp = true; 225 return NS_OK; 226 } 227 228 if (::JS_LinearStringEqualsLiteral(str, "params")) { 229 nsresult rv = getParams(stmt, aCtx, scope, val.address()); 230 NS_ENSURE_SUCCESS(rv, rv); 231 *_retval = ::JS_DefinePropertyById(aCtx, scope, id, val, JSPROP_RESOLVING); 232 *aResolvedp = true; 233 return NS_OK; 234 } 235 236 return NS_OK; 237 } 238 239 NS_IMPL_ISUPPORTS0(StatementParamsHolder); 240 241 StatementParamsHolder::~StatementParamsHolder() { 242 MOZ_ASSERT(NS_IsMainThread()); 243 // We are considered dead at this point, so any wrappers for row or params 244 // need to lose their reference to the statement. 245 mParams->mStatement = nullptr; 246 } 247 248 NS_IMPL_ISUPPORTS0(StatementRowHolder); 249 250 StatementRowHolder::~StatementRowHolder() { 251 MOZ_ASSERT(NS_IsMainThread()); 252 // We are considered dead at this point, so any wrappers for row or params 253 // need to lose their reference to the statement. 254 mRow->mStatement = nullptr; 255 } 256 257 } // namespace storage 258 } // namespace mozilla