dom/indexedDB/KeyPath.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial