/src/mozilla-central/dom/indexedDB/KeyPath.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
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 "KeyPath.h" |
8 | | #include "IDBObjectStore.h" |
9 | | #include "Key.h" |
10 | | #include "ReportInternalError.h" |
11 | | |
12 | | #include "nsCharSeparatedTokenizer.h" |
13 | | #include "nsJSUtils.h" |
14 | | #include "nsPrintfCString.h" |
15 | | #include "xpcpublic.h" |
16 | | |
17 | | #include "mozilla/dom/BindingDeclarations.h" |
18 | | #include "mozilla/dom/IDBObjectStoreBinding.h" |
19 | | |
20 | | namespace mozilla { |
21 | | namespace dom { |
22 | | namespace indexedDB { |
23 | | |
24 | | namespace { |
25 | | |
26 | | inline |
27 | | bool |
28 | | IgnoreWhitespace(char16_t c) |
29 | 0 | { |
30 | 0 | return false; |
31 | 0 | } |
32 | | |
33 | | typedef nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> KeyPathTokenizer; |
34 | | |
35 | | bool |
36 | | IsValidKeyPathString(const nsAString& aKeyPath) |
37 | 0 | { |
38 | 0 | NS_ASSERTION(!aKeyPath.IsVoid(), "What?"); |
39 | 0 |
|
40 | 0 | KeyPathTokenizer tokenizer(aKeyPath, '.'); |
41 | 0 |
|
42 | 0 | while (tokenizer.hasMoreTokens()) { |
43 | 0 | nsString token(tokenizer.nextToken()); |
44 | 0 |
|
45 | 0 | if (!token.Length()) { |
46 | 0 | return false; |
47 | 0 | } |
48 | 0 | |
49 | 0 | if (!JS_IsIdentifier(token.get(), token.Length())) { |
50 | 0 | return false; |
51 | 0 | } |
52 | 0 | } |
53 | 0 |
|
54 | 0 | // If the very last character was a '.', the tokenizer won't give us an empty |
55 | 0 | // token, but the keyPath is still invalid. |
56 | 0 | if (!aKeyPath.IsEmpty() && |
57 | 0 | aKeyPath.CharAt(aKeyPath.Length() - 1) == '.') { |
58 | 0 | return false; |
59 | 0 | } |
60 | 0 | |
61 | 0 | return true; |
62 | 0 | } |
63 | | |
64 | | enum KeyExtractionOptions { |
65 | | DoNotCreateProperties, |
66 | | CreateProperties |
67 | | }; |
68 | | |
69 | | nsresult |
70 | | GetJSValFromKeyPathString(JSContext* aCx, |
71 | | const JS::Value& aValue, |
72 | | const nsAString& aKeyPathString, |
73 | | JS::Value* aKeyJSVal, |
74 | | KeyExtractionOptions aOptions, |
75 | | KeyPath::ExtractOrCreateKeyCallback aCallback, |
76 | | void* aClosure) |
77 | 0 | { |
78 | 0 | NS_ASSERTION(aCx, "Null pointer!"); |
79 | 0 | NS_ASSERTION(IsValidKeyPathString(aKeyPathString), |
80 | 0 | "This will explode!"); |
81 | 0 | NS_ASSERTION(!(aCallback || aClosure) || aOptions == CreateProperties, |
82 | 0 | "This is not allowed!"); |
83 | 0 | NS_ASSERTION(aOptions != CreateProperties || aCallback, |
84 | 0 | "If properties are created, there must be a callback!"); |
85 | 0 |
|
86 | 0 | nsresult rv = NS_OK; |
87 | 0 | *aKeyJSVal = aValue; |
88 | 0 |
|
89 | 0 | KeyPathTokenizer tokenizer(aKeyPathString, '.'); |
90 | 0 |
|
91 | 0 | nsString targetObjectPropName; |
92 | 0 | JS::Rooted<JSObject*> targetObject(aCx, nullptr); |
93 | 0 | JS::Rooted<JS::Value> currentVal(aCx, aValue); |
94 | 0 | JS::Rooted<JSObject*> obj(aCx); |
95 | 0 |
|
96 | 0 | while (tokenizer.hasMoreTokens()) { |
97 | 0 | const nsDependentSubstring& token = tokenizer.nextToken(); |
98 | 0 |
|
99 | 0 | NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath"); |
100 | 0 |
|
101 | 0 | const char16_t* keyPathChars = token.BeginReading(); |
102 | 0 | const size_t keyPathLen = token.Length(); |
103 | 0 |
|
104 | 0 | bool hasProp; |
105 | 0 | if (!targetObject) { |
106 | 0 | // We're still walking the chain of existing objects |
107 | 0 | // http://w3c.github.io/IndexedDB/#evaluate-a-key-path-on-a-value |
108 | 0 | // step 4 substep 1: check for .length on a String value. |
109 | 0 | if (currentVal.isString() && !tokenizer.hasMoreTokens() && |
110 | 0 | token.EqualsLiteral("length")) { |
111 | 0 | aKeyJSVal->setNumber(double(JS_GetStringLength(currentVal.toString()))); |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | |
115 | 0 | if (!currentVal.isObject()) { |
116 | 0 | return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
117 | 0 | } |
118 | 0 | obj = ¤tVal.toObject(); |
119 | 0 |
|
120 | 0 | bool ok = JS_HasUCProperty(aCx, obj, keyPathChars, keyPathLen, |
121 | 0 | &hasProp); |
122 | 0 | IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
123 | 0 |
|
124 | 0 | if (hasProp) { |
125 | 0 | // Get if the property exists... |
126 | 0 | JS::Rooted<JS::Value> intermediate(aCx); |
127 | 0 | bool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen, &intermediate); |
128 | 0 | IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
129 | 0 |
|
130 | 0 | // Treat explicitly undefined as an error. |
131 | 0 | if (intermediate.isUndefined()) { |
132 | 0 | return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
133 | 0 | } |
134 | 0 | if (tokenizer.hasMoreTokens()) { |
135 | 0 | // ...and walk to it if there are more steps... |
136 | 0 | currentVal = intermediate; |
137 | 0 | } |
138 | 0 | else { |
139 | 0 | // ...otherwise use it as key |
140 | 0 | *aKeyJSVal = intermediate; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | else { |
144 | 0 | // If the property doesn't exist, fall into below path of starting |
145 | 0 | // to define properties, if allowed. |
146 | 0 | if (aOptions == DoNotCreateProperties) { |
147 | 0 | return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
148 | 0 | } |
149 | 0 | |
150 | 0 | targetObject = obj; |
151 | 0 | targetObjectPropName = token; |
152 | 0 | } |
153 | 0 | } |
154 | 0 |
|
155 | 0 | if (targetObject) { |
156 | 0 | // We have started inserting new objects or are about to just insert |
157 | 0 | // the first one. |
158 | 0 |
|
159 | 0 | aKeyJSVal->setUndefined(); |
160 | 0 |
|
161 | 0 | if (tokenizer.hasMoreTokens()) { |
162 | 0 | // If we're not at the end, we need to add a dummy object to the |
163 | 0 | // chain. |
164 | 0 | JS::Rooted<JSObject*> dummy(aCx, JS_NewPlainObject(aCx)); |
165 | 0 | if (!dummy) { |
166 | 0 | IDB_REPORT_INTERNAL_ERR(); |
167 | 0 | rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
168 | 0 | break; |
169 | 0 | } |
170 | 0 |
|
171 | 0 | if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(), |
172 | 0 | token.Length(), dummy, JSPROP_ENUMERATE)) { |
173 | 0 | IDB_REPORT_INTERNAL_ERR(); |
174 | 0 | rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
175 | 0 | break; |
176 | 0 | } |
177 | 0 |
|
178 | 0 | obj = dummy; |
179 | 0 | } |
180 | 0 | else { |
181 | 0 | JS::Rooted<JSObject*> dummy(aCx, |
182 | 0 | JS_NewObject(aCx, IDBObjectStore::DummyPropClass())); |
183 | 0 | if (!dummy) { |
184 | 0 | IDB_REPORT_INTERNAL_ERR(); |
185 | 0 | rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
186 | 0 | break; |
187 | 0 | } |
188 | 0 |
|
189 | 0 | if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(), |
190 | 0 | token.Length(), dummy, JSPROP_ENUMERATE)) { |
191 | 0 | IDB_REPORT_INTERNAL_ERR(); |
192 | 0 | rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
193 | 0 | break; |
194 | 0 | } |
195 | 0 |
|
196 | 0 | obj = dummy; |
197 | 0 | } |
198 | 0 | } |
199 | 0 | } |
200 | 0 |
|
201 | 0 | // We guard on rv being a success because we need to run the property |
202 | 0 | // deletion code below even if we should not be running the callback. |
203 | 0 | if (NS_SUCCEEDED(rv) && aCallback) { |
204 | 0 | rv = (*aCallback)(aCx, aClosure); |
205 | 0 | } |
206 | 0 |
|
207 | 0 | if (targetObject) { |
208 | 0 | // If this fails, we lose, and the web page sees a magical property |
209 | 0 | // appear on the object :-( |
210 | 0 | JS::ObjectOpResult succeeded; |
211 | 0 | if (!JS_DeleteUCProperty(aCx, targetObject, |
212 | 0 | targetObjectPropName.get(), |
213 | 0 | targetObjectPropName.Length(), |
214 | 0 | succeeded)) { |
215 | 0 | IDB_REPORT_INTERNAL_ERR(); |
216 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
217 | 0 | } |
218 | 0 | IDB_ENSURE_TRUE(succeeded, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
219 | 0 | } |
220 | 0 |
|
221 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
222 | 0 | return rv; |
223 | 0 | } |
224 | | |
225 | | } // namespace |
226 | | |
227 | | // static |
228 | | nsresult |
229 | | KeyPath::Parse(const nsAString& aString, KeyPath* aKeyPath) |
230 | 0 | { |
231 | 0 | KeyPath keyPath(0); |
232 | 0 | keyPath.SetType(STRING); |
233 | 0 |
|
234 | 0 | if (!keyPath.AppendStringWithValidation(aString)) { |
235 | 0 | return NS_ERROR_FAILURE; |
236 | 0 | } |
237 | 0 | |
238 | 0 | *aKeyPath = keyPath; |
239 | 0 | return NS_OK; |
240 | 0 | } |
241 | | |
242 | | //static |
243 | | nsresult |
244 | | KeyPath::Parse(const Sequence<nsString>& aStrings, KeyPath* aKeyPath) |
245 | 0 | { |
246 | 0 | KeyPath keyPath(0); |
247 | 0 | keyPath.SetType(ARRAY); |
248 | 0 |
|
249 | 0 | for (uint32_t i = 0; i < aStrings.Length(); ++i) { |
250 | 0 | if (!keyPath.AppendStringWithValidation(aStrings[i])) { |
251 | 0 | return NS_ERROR_FAILURE; |
252 | 0 | } |
253 | 0 | } |
254 | 0 |
|
255 | 0 | *aKeyPath = keyPath; |
256 | 0 | return NS_OK; |
257 | 0 | } |
258 | | |
259 | | // static |
260 | | nsresult |
261 | | KeyPath::Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath) |
262 | 0 | { |
263 | 0 | KeyPath keyPath(0); |
264 | 0 |
|
265 | 0 | aKeyPath->SetType(NONEXISTENT); |
266 | 0 |
|
267 | 0 | if (aValue.IsNull()) { |
268 | 0 | *aKeyPath = keyPath; |
269 | 0 | return NS_OK; |
270 | 0 | } |
271 | 0 | |
272 | 0 | if (aValue.Value().IsString()) { |
273 | 0 | return Parse(aValue.Value().GetAsString(), aKeyPath); |
274 | 0 | } |
275 | 0 | |
276 | 0 | MOZ_ASSERT(aValue.Value().IsStringSequence()); |
277 | 0 |
|
278 | 0 | const Sequence<nsString>& seq = aValue.Value().GetAsStringSequence(); |
279 | 0 | if (seq.Length() == 0) { |
280 | 0 | return NS_ERROR_FAILURE; |
281 | 0 | } |
282 | 0 | return Parse(seq, aKeyPath); |
283 | 0 | } |
284 | | |
285 | | void |
286 | | KeyPath::SetType(KeyPathType aType) |
287 | 0 | { |
288 | 0 | mType = aType; |
289 | 0 | mStrings.Clear(); |
290 | 0 | } |
291 | | |
292 | | bool |
293 | | KeyPath::AppendStringWithValidation(const nsAString& aString) |
294 | 0 | { |
295 | 0 | if (!IsValidKeyPathString(aString)) { |
296 | 0 | return false; |
297 | 0 | } |
298 | 0 | |
299 | 0 | if (IsString()) { |
300 | 0 | NS_ASSERTION(mStrings.Length() == 0, "Too many strings!"); |
301 | 0 | mStrings.AppendElement(aString); |
302 | 0 | return true; |
303 | 0 | } |
304 | 0 |
|
305 | 0 | if (IsArray()) { |
306 | 0 | mStrings.AppendElement(aString); |
307 | 0 | return true; |
308 | 0 | } |
309 | 0 | |
310 | 0 | MOZ_ASSERT_UNREACHABLE("What?!"); |
311 | 0 | return false; |
312 | 0 | } |
313 | | |
314 | | nsresult |
315 | | KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const |
316 | 0 | { |
317 | 0 | uint32_t len = mStrings.Length(); |
318 | 0 | JS::Rooted<JS::Value> value(aCx); |
319 | 0 |
|
320 | 0 | aKey.Unset(); |
321 | 0 |
|
322 | 0 | for (uint32_t i = 0; i < len; ++i) { |
323 | 0 | nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[i], |
324 | 0 | value.address(), |
325 | 0 | DoNotCreateProperties, nullptr, |
326 | 0 | nullptr); |
327 | 0 | if (NS_FAILED(rv)) { |
328 | 0 | return rv; |
329 | 0 | } |
330 | 0 | |
331 | 0 | if (NS_FAILED(aKey.AppendItem(aCx, IsArray() && i == 0, value))) { |
332 | 0 | NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset"); |
333 | 0 | return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
334 | 0 | } |
335 | 0 | } |
336 | 0 |
|
337 | 0 | aKey.FinishArray(); |
338 | 0 |
|
339 | 0 | return NS_OK; |
340 | 0 | } |
341 | | |
342 | | nsresult |
343 | | KeyPath::ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue, |
344 | | JS::Value* aOutVal) const |
345 | 0 | { |
346 | 0 | NS_ASSERTION(IsValid(), "This doesn't make sense!"); |
347 | 0 |
|
348 | 0 | if (IsString()) { |
349 | 0 | return GetJSValFromKeyPathString(aCx, aValue, mStrings[0], aOutVal, |
350 | 0 | DoNotCreateProperties, nullptr, nullptr); |
351 | 0 | } |
352 | 0 | |
353 | 0 | const uint32_t len = mStrings.Length(); |
354 | 0 | JS::Rooted<JSObject*> arrayObj(aCx, JS_NewArrayObject(aCx, len)); |
355 | 0 | if (!arrayObj) { |
356 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
357 | 0 | } |
358 | 0 | |
359 | 0 | JS::Rooted<JS::Value> value(aCx); |
360 | 0 | for (uint32_t i = 0; i < len; ++i) { |
361 | 0 | nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[i], |
362 | 0 | value.address(), |
363 | 0 | DoNotCreateProperties, nullptr, |
364 | 0 | nullptr); |
365 | 0 | if (NS_FAILED(rv)) { |
366 | 0 | return rv; |
367 | 0 | } |
368 | 0 | |
369 | 0 | if (!JS_DefineElement(aCx, arrayObj, i, value, JSPROP_ENUMERATE)) { |
370 | 0 | IDB_REPORT_INTERNAL_ERR(); |
371 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
372 | 0 | } |
373 | 0 | } |
374 | 0 |
|
375 | 0 | aOutVal->setObject(*arrayObj); |
376 | 0 | return NS_OK; |
377 | 0 | } |
378 | | |
379 | | nsresult |
380 | | KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, |
381 | | Key& aKey, ExtractOrCreateKeyCallback aCallback, |
382 | | void* aClosure) const |
383 | 0 | { |
384 | 0 | NS_ASSERTION(IsString(), "This doesn't make sense!"); |
385 | 0 |
|
386 | 0 | JS::Rooted<JS::Value> value(aCx); |
387 | 0 |
|
388 | 0 | aKey.Unset(); |
389 | 0 |
|
390 | 0 | nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[0], |
391 | 0 | value.address(), |
392 | 0 | CreateProperties, aCallback, |
393 | 0 | aClosure); |
394 | 0 | if (NS_FAILED(rv)) { |
395 | 0 | return rv; |
396 | 0 | } |
397 | 0 | |
398 | 0 | if (NS_FAILED(aKey.AppendItem(aCx, false, value))) { |
399 | 0 | NS_ASSERTION(aKey.IsUnset(), "Should be unset"); |
400 | 0 | return value.isUndefined() ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR; |
401 | 0 | } |
402 | 0 |
|
403 | 0 | aKey.FinishArray(); |
404 | 0 |
|
405 | 0 | return NS_OK; |
406 | 0 | } |
407 | | |
408 | | void |
409 | | KeyPath::SerializeToString(nsAString& aString) const |
410 | 0 | { |
411 | 0 | NS_ASSERTION(IsValid(), "Check to see if I'm valid first!"); |
412 | 0 |
|
413 | 0 | if (IsString()) { |
414 | 0 | aString = mStrings[0]; |
415 | 0 | return; |
416 | 0 | } |
417 | 0 | |
418 | 0 | if (IsArray()) { |
419 | 0 | // We use a comma in the beginning to indicate that it's an array of |
420 | 0 | // key paths. This is to be able to tell a string-keypath from an |
421 | 0 | // array-keypath which contains only one item. |
422 | 0 | // It also makes serializing easier :-) |
423 | 0 | uint32_t len = mStrings.Length(); |
424 | 0 | for (uint32_t i = 0; i < len; ++i) { |
425 | 0 | aString.Append(','); |
426 | 0 | aString.Append(mStrings[i]); |
427 | 0 | } |
428 | 0 |
|
429 | 0 | return; |
430 | 0 | } |
431 | 0 |
|
432 | 0 | MOZ_ASSERT_UNREACHABLE("What?"); |
433 | 0 | } |
434 | | |
435 | | // static |
436 | | KeyPath |
437 | | KeyPath::DeserializeFromString(const nsAString& aString) |
438 | 0 | { |
439 | 0 | KeyPath keyPath(0); |
440 | 0 |
|
441 | 0 | if (!aString.IsEmpty() && aString.First() == ',') { |
442 | 0 | keyPath.SetType(ARRAY); |
443 | 0 |
|
444 | 0 | // We use a comma in the beginning to indicate that it's an array of |
445 | 0 | // key paths. This is to be able to tell a string-keypath from an |
446 | 0 | // array-keypath which contains only one item. |
447 | 0 | nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(aString, ','); |
448 | 0 | tokenizer.nextToken(); |
449 | 0 | while (tokenizer.hasMoreTokens()) { |
450 | 0 | keyPath.mStrings.AppendElement(tokenizer.nextToken()); |
451 | 0 | } |
452 | 0 |
|
453 | 0 | return keyPath; |
454 | 0 | } |
455 | 0 |
|
456 | 0 | keyPath.SetType(STRING); |
457 | 0 | keyPath.mStrings.AppendElement(aString); |
458 | 0 |
|
459 | 0 | return keyPath; |
460 | 0 | } |
461 | | |
462 | | nsresult |
463 | | KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const |
464 | 0 | { |
465 | 0 | if (IsArray()) { |
466 | 0 | uint32_t len = mStrings.Length(); |
467 | 0 | JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len)); |
468 | 0 | if (!array) { |
469 | 0 | IDB_WARNING("Failed to make array!"); |
470 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
471 | 0 | } |
472 | 0 |
|
473 | 0 | for (uint32_t i = 0; i < len; ++i) { |
474 | 0 | JS::Rooted<JS::Value> val(aCx); |
475 | 0 | nsString tmp(mStrings[i]); |
476 | 0 | if (!xpc::StringToJsval(aCx, tmp, &val)) { |
477 | 0 | IDB_REPORT_INTERNAL_ERR(); |
478 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
479 | 0 | } |
480 | 0 |
|
481 | 0 | if (!JS_DefineElement(aCx, array, i, val, JSPROP_ENUMERATE)) { |
482 | 0 | IDB_REPORT_INTERNAL_ERR(); |
483 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
484 | 0 | } |
485 | 0 | } |
486 | 0 |
|
487 | 0 | aValue.setObject(*array); |
488 | 0 | return NS_OK; |
489 | 0 | } |
490 | 0 | |
491 | 0 | if (IsString()) { |
492 | 0 | nsString tmp(mStrings[0]); |
493 | 0 | if (!xpc::StringToJsval(aCx, tmp, aValue)) { |
494 | 0 | IDB_REPORT_INTERNAL_ERR(); |
495 | 0 | return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
496 | 0 | } |
497 | 0 | return NS_OK; |
498 | 0 | } |
499 | 0 | |
500 | 0 | aValue.setNull(); |
501 | 0 | return NS_OK; |
502 | 0 | } |
503 | | |
504 | | nsresult |
505 | | KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const |
506 | 0 | { |
507 | 0 | JS::Rooted<JS::Value> value(aCx); |
508 | 0 | nsresult rv = ToJSVal(aCx, &value); |
509 | 0 | if (NS_SUCCEEDED(rv)) { |
510 | 0 | aValue = value; |
511 | 0 | } |
512 | 0 | return rv; |
513 | 0 | } |
514 | | |
515 | | bool |
516 | | KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const |
517 | 0 | { |
518 | 0 | // Any keypath that passed validation is allowed for non-autoIncrement |
519 | 0 | // objectStores. |
520 | 0 | if (!aAutoIncrement) { |
521 | 0 | return true; |
522 | 0 | } |
523 | 0 | |
524 | 0 | // Array keypaths are not allowed for autoIncrement objectStores. |
525 | 0 | if (IsArray()) { |
526 | 0 | return false; |
527 | 0 | } |
528 | 0 | |
529 | 0 | // Neither are empty strings. |
530 | 0 | if (IsEmpty()) { |
531 | 0 | return false; |
532 | 0 | } |
533 | 0 | |
534 | 0 | // Everything else is ok. |
535 | 0 | return true; |
536 | 0 | } |
537 | | |
538 | | } // namespace indexedDB |
539 | | } // namespace dom |
540 | | } // namespace mozilla |