dom/indexedDB/KeyPath.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/indexedDB/KeyPath.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,573 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "KeyPath.h"
    1.11 +#include "IDBObjectStore.h"
    1.12 +#include "Key.h"
    1.13 +#include "ReportInternalError.h"
    1.14 +
    1.15 +#include "nsCharSeparatedTokenizer.h"
    1.16 +#include "nsJSUtils.h"
    1.17 +#include "xpcpublic.h"
    1.18 +
    1.19 +#include "mozilla/dom/BindingDeclarations.h"
    1.20 +
    1.21 +USING_INDEXEDDB_NAMESPACE
    1.22 +
    1.23 +namespace {
    1.24 +
    1.25 +inline
    1.26 +bool
    1.27 +IgnoreWhitespace(char16_t c)
    1.28 +{
    1.29 +  return false;
    1.30 +}
    1.31 +
    1.32 +typedef nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> KeyPathTokenizer;
    1.33 +
    1.34 +bool
    1.35 +IsValidKeyPathString(JSContext* aCx, const nsAString& aKeyPath)
    1.36 +{
    1.37 +  NS_ASSERTION(!aKeyPath.IsVoid(), "What?");
    1.38 +
    1.39 +  KeyPathTokenizer tokenizer(aKeyPath, '.');
    1.40 +
    1.41 +  while (tokenizer.hasMoreTokens()) {
    1.42 +    nsString token(tokenizer.nextToken());
    1.43 +
    1.44 +    if (!token.Length()) {
    1.45 +      return false;
    1.46 +    }
    1.47 +
    1.48 +    JS::Rooted<JS::Value> stringVal(aCx);
    1.49 +    if (!xpc::StringToJsval(aCx, token, &stringVal)) {
    1.50 +      return false;
    1.51 +    }
    1.52 +
    1.53 +    NS_ASSERTION(stringVal.toString(), "This should never happen");
    1.54 +    JS::Rooted<JSString*> str(aCx, stringVal.toString());
    1.55 +
    1.56 +    bool isIdentifier = false;
    1.57 +    if (!JS_IsIdentifier(aCx, str, &isIdentifier) || !isIdentifier) {
    1.58 +      return false;
    1.59 +    }
    1.60 +  }
    1.61 +
    1.62 +  // If the very last character was a '.', the tokenizer won't give us an empty
    1.63 +  // token, but the keyPath is still invalid.
    1.64 +  if (!aKeyPath.IsEmpty() &&
    1.65 +      aKeyPath.CharAt(aKeyPath.Length() - 1) == '.') {
    1.66 +    return false;
    1.67 +  }
    1.68 +
    1.69 +  return true;
    1.70 +}
    1.71 +
    1.72 +enum KeyExtractionOptions {
    1.73 +  DoNotCreateProperties,
    1.74 +  CreateProperties
    1.75 +};
    1.76 +
    1.77 +nsresult
    1.78 +GetJSValFromKeyPathString(JSContext* aCx,
    1.79 +                          const JS::Value& aValue,
    1.80 +                          const nsAString& aKeyPathString,
    1.81 +                          JS::Value* aKeyJSVal,
    1.82 +                          KeyExtractionOptions aOptions,
    1.83 +                          KeyPath::ExtractOrCreateKeyCallback aCallback,
    1.84 +                          void* aClosure)
    1.85 +{
    1.86 +  NS_ASSERTION(aCx, "Null pointer!");
    1.87 +  NS_ASSERTION(IsValidKeyPathString(aCx, aKeyPathString),
    1.88 +               "This will explode!");
    1.89 +  NS_ASSERTION(!(aCallback || aClosure) || aOptions == CreateProperties,
    1.90 +               "This is not allowed!");
    1.91 +  NS_ASSERTION(aOptions != CreateProperties || aCallback,
    1.92 +               "If properties are created, there must be a callback!");
    1.93 +
    1.94 +  nsresult rv = NS_OK;
    1.95 +  *aKeyJSVal = aValue;
    1.96 +
    1.97 +  KeyPathTokenizer tokenizer(aKeyPathString, '.');
    1.98 +
    1.99 +  nsString targetObjectPropName;
   1.100 +  JS::Rooted<JSObject*> targetObject(aCx, nullptr);
   1.101 +  JS::Rooted<JSObject*> obj(aCx,
   1.102 +    JSVAL_IS_PRIMITIVE(aValue) ? nullptr : JSVAL_TO_OBJECT(aValue));
   1.103 +
   1.104 +  while (tokenizer.hasMoreTokens()) {
   1.105 +    const nsDependentSubstring& token = tokenizer.nextToken();
   1.106 +
   1.107 +    NS_ASSERTION(!token.IsEmpty(), "Should be a valid keypath");
   1.108 +
   1.109 +    const jschar* keyPathChars = token.BeginReading();
   1.110 +    const size_t keyPathLen = token.Length();
   1.111 +
   1.112 +    bool hasProp;
   1.113 +    if (!targetObject) {
   1.114 +      // We're still walking the chain of existing objects
   1.115 +      if (!obj) {
   1.116 +        return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.117 +      }
   1.118 +
   1.119 +      bool ok = JS_HasUCProperty(aCx, obj, keyPathChars, keyPathLen,
   1.120 +                                 &hasProp);
   1.121 +      IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   1.122 +
   1.123 +      if (hasProp) {
   1.124 +        // Get if the property exists...
   1.125 +        JS::Rooted<JS::Value> intermediate(aCx);
   1.126 +        bool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen, &intermediate);
   1.127 +        IDB_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   1.128 +
   1.129 +        // Treat explicitly undefined as an error.
   1.130 +        if (intermediate == JSVAL_VOID) {
   1.131 +          return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.132 +        }
   1.133 +        if (tokenizer.hasMoreTokens()) {
   1.134 +          // ...and walk to it if there are more steps...
   1.135 +          if (JSVAL_IS_PRIMITIVE(intermediate)) {
   1.136 +            return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.137 +          }
   1.138 +          obj = JSVAL_TO_OBJECT(intermediate);
   1.139 +        }
   1.140 +        else {
   1.141 +          // ...otherwise use it as key
   1.142 +          *aKeyJSVal = intermediate;
   1.143 +        }
   1.144 +      }
   1.145 +      else {
   1.146 +        // If the property doesn't exist, fall into below path of starting
   1.147 +        // to define properties, if allowed.
   1.148 +        if (aOptions == DoNotCreateProperties) {
   1.149 +          return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.150 +        }
   1.151 +
   1.152 +        targetObject = obj;
   1.153 +        targetObjectPropName = token;
   1.154 +      }
   1.155 +    }
   1.156 +
   1.157 +    if (targetObject) {
   1.158 +      // We have started inserting new objects or are about to just insert
   1.159 +      // the first one.
   1.160 +
   1.161 +      *aKeyJSVal = JSVAL_VOID;
   1.162 +
   1.163 +      if (tokenizer.hasMoreTokens()) {
   1.164 +        // If we're not at the end, we need to add a dummy object to the
   1.165 +        // chain.
   1.166 +        JS::Rooted<JSObject*> dummy(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(),
   1.167 +                                                      JS::NullPtr()));
   1.168 +        if (!dummy) {
   1.169 +          IDB_REPORT_INTERNAL_ERR();
   1.170 +          rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.171 +          break;
   1.172 +        }
   1.173 +
   1.174 +        if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
   1.175 +                                 token.Length(),
   1.176 +                                 OBJECT_TO_JSVAL(dummy), nullptr, nullptr,
   1.177 +                                 JSPROP_ENUMERATE)) {
   1.178 +          IDB_REPORT_INTERNAL_ERR();
   1.179 +          rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.180 +          break;
   1.181 +        }
   1.182 +
   1.183 +        obj = dummy;
   1.184 +      }
   1.185 +      else {
   1.186 +        JS::Rooted<JSObject*> dummy(aCx, JS_NewObject(aCx, &IDBObjectStore::sDummyPropJSClass,
   1.187 +                                                      JS::NullPtr(), JS::NullPtr()));
   1.188 +        if (!dummy) {
   1.189 +          IDB_REPORT_INTERNAL_ERR();
   1.190 +          rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.191 +          break;
   1.192 +        }
   1.193 +
   1.194 +        if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
   1.195 +                                 token.Length(), OBJECT_TO_JSVAL(dummy),
   1.196 +                                 nullptr, nullptr, JSPROP_ENUMERATE)) {
   1.197 +          IDB_REPORT_INTERNAL_ERR();
   1.198 +          rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.199 +          break;
   1.200 +        }
   1.201 +
   1.202 +        obj = dummy;
   1.203 +      }
   1.204 +    }
   1.205 +  }
   1.206 +
   1.207 +  // We guard on rv being a success because we need to run the property
   1.208 +  // deletion code below even if we should not be running the callback.
   1.209 +  if (NS_SUCCEEDED(rv) && aCallback) {
   1.210 +    rv = (*aCallback)(aCx, aClosure);
   1.211 +  }
   1.212 +
   1.213 +  if (targetObject) {
   1.214 +    // If this fails, we lose, and the web page sees a magical property
   1.215 +    // appear on the object :-(
   1.216 +    bool succeeded;
   1.217 +    if (!JS_DeleteUCProperty2(aCx, targetObject,
   1.218 +                              targetObjectPropName.get(),
   1.219 +                              targetObjectPropName.Length(),
   1.220 +                              &succeeded)) {
   1.221 +      IDB_REPORT_INTERNAL_ERR();
   1.222 +      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.223 +    }
   1.224 +    IDB_ENSURE_TRUE(succeeded, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   1.225 +  }
   1.226 +
   1.227 +  NS_ENSURE_SUCCESS(rv, rv);
   1.228 +  return rv;
   1.229 +}
   1.230 +
   1.231 +} // anonymous namespace
   1.232 +
   1.233 +// static
   1.234 +nsresult
   1.235 +KeyPath::Parse(JSContext* aCx, const nsAString& aString, KeyPath* aKeyPath)
   1.236 +{
   1.237 +  KeyPath keyPath(0);
   1.238 +  keyPath.SetType(STRING);
   1.239 +
   1.240 +  if (!keyPath.AppendStringWithValidation(aCx, aString)) {
   1.241 +    return NS_ERROR_FAILURE;
   1.242 +  }
   1.243 +
   1.244 +  *aKeyPath = keyPath;
   1.245 +  return NS_OK;
   1.246 +}
   1.247 +
   1.248 +//static
   1.249 +nsresult
   1.250 +KeyPath::Parse(JSContext* aCx, const mozilla::dom::Sequence<nsString>& aStrings,
   1.251 +               KeyPath* aKeyPath)
   1.252 +{
   1.253 +  KeyPath keyPath(0);
   1.254 +  keyPath.SetType(ARRAY);
   1.255 +
   1.256 +  for (uint32_t i = 0; i < aStrings.Length(); ++i) {
   1.257 +    if (!keyPath.AppendStringWithValidation(aCx, aStrings[i])) {
   1.258 +      return NS_ERROR_FAILURE;
   1.259 +    }
   1.260 +  }
   1.261 +
   1.262 +  *aKeyPath = keyPath;
   1.263 +  return NS_OK;
   1.264 +}
   1.265 +
   1.266 +// static
   1.267 +nsresult
   1.268 +KeyPath::Parse(JSContext* aCx, const JS::Value& aValue_, KeyPath* aKeyPath)
   1.269 +{
   1.270 +  JS::Rooted<JS::Value> aValue(aCx, aValue_);
   1.271 +  KeyPath keyPath(0);
   1.272 +
   1.273 +  aKeyPath->SetType(NONEXISTENT);
   1.274 +
   1.275 +  // See if this is a JS array.
   1.276 +  if (JS_IsArrayObject(aCx, aValue)) {
   1.277 +
   1.278 +    JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(aValue));
   1.279 +
   1.280 +    uint32_t length;
   1.281 +    if (!JS_GetArrayLength(aCx, obj, &length)) {
   1.282 +      return NS_ERROR_FAILURE;
   1.283 +    }
   1.284 +
   1.285 +    if (!length) {
   1.286 +      return NS_ERROR_FAILURE;
   1.287 +    }
   1.288 +
   1.289 +    keyPath.SetType(ARRAY);
   1.290 +
   1.291 +    for (uint32_t index = 0; index < length; index++) {
   1.292 +      JS::Rooted<JS::Value> val(aCx);
   1.293 +      JSString* jsstr;
   1.294 +      nsDependentJSString str;
   1.295 +      if (!JS_GetElement(aCx, obj, index, &val) ||
   1.296 +          !(jsstr = JS::ToString(aCx, val)) ||
   1.297 +          !str.init(aCx, jsstr)) {
   1.298 +        return NS_ERROR_FAILURE;
   1.299 +      }
   1.300 +
   1.301 +      if (!keyPath.AppendStringWithValidation(aCx, str)) {
   1.302 +        return NS_ERROR_FAILURE;
   1.303 +      }
   1.304 +    }
   1.305 +  }
   1.306 +  // Otherwise convert it to a string.
   1.307 +  else if (!JSVAL_IS_NULL(aValue) && !JSVAL_IS_VOID(aValue)) {
   1.308 +    JSString* jsstr;
   1.309 +    nsDependentJSString str;
   1.310 +    if (!(jsstr = JS::ToString(aCx, aValue)) ||
   1.311 +        !str.init(aCx, jsstr)) {
   1.312 +      return NS_ERROR_FAILURE;
   1.313 +    }
   1.314 +
   1.315 +    keyPath.SetType(STRING);
   1.316 +
   1.317 +    if (!keyPath.AppendStringWithValidation(aCx, str)) {
   1.318 +      return NS_ERROR_FAILURE;
   1.319 +    }
   1.320 +  }
   1.321 +
   1.322 +  *aKeyPath = keyPath;
   1.323 +  return NS_OK;
   1.324 +}
   1.325 +
   1.326 +void
   1.327 +KeyPath::SetType(KeyPathType aType)
   1.328 +{
   1.329 +  mType = aType;
   1.330 +  mStrings.Clear();
   1.331 +}
   1.332 +
   1.333 +bool
   1.334 +KeyPath::AppendStringWithValidation(JSContext* aCx, const nsAString& aString)
   1.335 +{
   1.336 +  if (!IsValidKeyPathString(aCx, aString)) {
   1.337 +    return false;
   1.338 +  }
   1.339 +
   1.340 +  if (IsString()) {
   1.341 +    NS_ASSERTION(mStrings.Length() == 0, "Too many strings!");
   1.342 +    mStrings.AppendElement(aString);
   1.343 +    return true;
   1.344 +  }
   1.345 +
   1.346 +  if (IsArray()) {
   1.347 +    mStrings.AppendElement(aString);
   1.348 +    return true;
   1.349 +  }
   1.350 +
   1.351 +  NS_NOTREACHED("What?!");
   1.352 +  return false;
   1.353 +}
   1.354 +
   1.355 +nsresult
   1.356 +KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const
   1.357 +{
   1.358 +  uint32_t len = mStrings.Length();
   1.359 +  JS::Rooted<JS::Value> value(aCx);
   1.360 +
   1.361 +  aKey.Unset();
   1.362 +
   1.363 +  for (uint32_t i = 0; i < len; ++i) {
   1.364 +    nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[i],
   1.365 +                                            value.address(),
   1.366 +                                            DoNotCreateProperties, nullptr,
   1.367 +                                            nullptr);
   1.368 +    if (NS_FAILED(rv)) {
   1.369 +      return rv;
   1.370 +    }
   1.371 +
   1.372 +    if (NS_FAILED(aKey.AppendItem(aCx, IsArray() && i == 0, value))) {
   1.373 +      NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset");
   1.374 +      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.375 +    }
   1.376 +  }
   1.377 +
   1.378 +  aKey.FinishArray();
   1.379 +
   1.380 +  return NS_OK;
   1.381 +}
   1.382 +
   1.383 +nsresult
   1.384 +KeyPath::ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue,
   1.385 +                           JS::Value* aOutVal) const
   1.386 +{
   1.387 +  NS_ASSERTION(IsValid(), "This doesn't make sense!");
   1.388 +
   1.389 +  if (IsString()) {
   1.390 +    return GetJSValFromKeyPathString(aCx, aValue, mStrings[0], aOutVal,
   1.391 +                                     DoNotCreateProperties, nullptr, nullptr);
   1.392 +  }
   1.393 +
   1.394 +  const uint32_t len = mStrings.Length();
   1.395 +  JS::Rooted<JSObject*> arrayObj(aCx, JS_NewArrayObject(aCx, len));
   1.396 +  if (!arrayObj) {
   1.397 +    return NS_ERROR_OUT_OF_MEMORY;
   1.398 +  }
   1.399 +
   1.400 +  JS::Rooted<JS::Value> value(aCx);
   1.401 +  for (uint32_t i = 0; i < len; ++i) {
   1.402 +    nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[i],
   1.403 +                                            value.address(),
   1.404 +                                            DoNotCreateProperties, nullptr,
   1.405 +                                            nullptr);
   1.406 +    if (NS_FAILED(rv)) {
   1.407 +      return rv;
   1.408 +    }
   1.409 +
   1.410 +    if (!JS_SetElement(aCx, arrayObj, i, value)) {
   1.411 +      IDB_REPORT_INTERNAL_ERR();
   1.412 +      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.413 +    }
   1.414 +  }
   1.415 +
   1.416 +  *aOutVal = OBJECT_TO_JSVAL(arrayObj);
   1.417 +  return NS_OK;
   1.418 +}
   1.419 +
   1.420 +nsresult
   1.421 +KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue,
   1.422 +                            Key& aKey, ExtractOrCreateKeyCallback aCallback,
   1.423 +                            void* aClosure) const
   1.424 +{
   1.425 +  NS_ASSERTION(IsString(), "This doesn't make sense!");
   1.426 +
   1.427 +  JS::Rooted<JS::Value> value(aCx);
   1.428 +
   1.429 +  aKey.Unset();
   1.430 +
   1.431 +  nsresult rv = GetJSValFromKeyPathString(aCx, aValue, mStrings[0],
   1.432 +                                          value.address(),
   1.433 +                                          CreateProperties, aCallback,
   1.434 +                                          aClosure);
   1.435 +  if (NS_FAILED(rv)) {
   1.436 +    return rv;
   1.437 +  }
   1.438 +
   1.439 +  if (NS_FAILED(aKey.AppendItem(aCx, false, value))) {
   1.440 +    NS_ASSERTION(aKey.IsUnset(), "Should be unset");
   1.441 +    return JSVAL_IS_VOID(value) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   1.442 +  }
   1.443 +
   1.444 +  aKey.FinishArray();
   1.445 +
   1.446 +  return NS_OK;
   1.447 +}
   1.448 +
   1.449 +void
   1.450 +KeyPath::SerializeToString(nsAString& aString) const
   1.451 +{
   1.452 +  NS_ASSERTION(IsValid(), "Check to see if I'm valid first!");
   1.453 +
   1.454 +  if (IsString()) {
   1.455 +    aString = mStrings[0];
   1.456 +    return;
   1.457 +  }
   1.458 +
   1.459 +  if (IsArray()) {
   1.460 +    // We use a comma in the beginning to indicate that it's an array of
   1.461 +    // key paths. This is to be able to tell a string-keypath from an
   1.462 +    // array-keypath which contains only one item.
   1.463 +    // It also makes serializing easier :-)
   1.464 +    uint32_t len = mStrings.Length();
   1.465 +    for (uint32_t i = 0; i < len; ++i) {
   1.466 +      aString.Append(NS_LITERAL_STRING(",") + mStrings[i]);
   1.467 +    }
   1.468 +
   1.469 +    return;
   1.470 +  }
   1.471 +
   1.472 +  NS_NOTREACHED("What?");
   1.473 +}
   1.474 +
   1.475 +// static
   1.476 +KeyPath
   1.477 +KeyPath::DeserializeFromString(const nsAString& aString)
   1.478 +{
   1.479 +  KeyPath keyPath(0);
   1.480 +
   1.481 +  if (!aString.IsEmpty() && aString.First() == ',') {
   1.482 +    keyPath.SetType(ARRAY);
   1.483 +
   1.484 +    // We use a comma in the beginning to indicate that it's an array of
   1.485 +    // key paths. This is to be able to tell a string-keypath from an
   1.486 +    // array-keypath which contains only one item.
   1.487 +    nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(aString, ',');
   1.488 +    tokenizer.nextToken();
   1.489 +    while (tokenizer.hasMoreTokens()) {
   1.490 +      keyPath.mStrings.AppendElement(tokenizer.nextToken());
   1.491 +    }
   1.492 +
   1.493 +    return keyPath;
   1.494 +  }
   1.495 +
   1.496 +  keyPath.SetType(STRING);
   1.497 +  keyPath.mStrings.AppendElement(aString);
   1.498 +
   1.499 +  return keyPath;
   1.500 +}
   1.501 +
   1.502 +nsresult
   1.503 +KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const
   1.504 +{
   1.505 +  if (IsArray()) {
   1.506 +    uint32_t len = mStrings.Length();
   1.507 +    JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len));
   1.508 +    if (!array) {
   1.509 +      IDB_WARNING("Failed to make array!");
   1.510 +      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.511 +    }
   1.512 +
   1.513 +    for (uint32_t i = 0; i < len; ++i) {
   1.514 +      JS::Rooted<JS::Value> val(aCx);
   1.515 +      nsString tmp(mStrings[i]);
   1.516 +      if (!xpc::StringToJsval(aCx, tmp, &val)) {
   1.517 +        IDB_REPORT_INTERNAL_ERR();
   1.518 +        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.519 +      }
   1.520 +
   1.521 +      if (!JS_SetElement(aCx, array, i, val)) {
   1.522 +        IDB_REPORT_INTERNAL_ERR();
   1.523 +        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.524 +      }
   1.525 +    }
   1.526 +
   1.527 +    aValue.setObject(*array);
   1.528 +    return NS_OK;
   1.529 +  }
   1.530 +
   1.531 +  if (IsString()) {
   1.532 +    nsString tmp(mStrings[0]);
   1.533 +    if (!xpc::StringToJsval(aCx, tmp, aValue)) {
   1.534 +      IDB_REPORT_INTERNAL_ERR();
   1.535 +      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   1.536 +    }
   1.537 +    return NS_OK;
   1.538 +  }
   1.539 +
   1.540 +  aValue.setNull();
   1.541 +  return NS_OK;
   1.542 +}
   1.543 +
   1.544 +nsresult
   1.545 +KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const
   1.546 +{
   1.547 +  JS::Rooted<JS::Value> value(aCx);
   1.548 +  nsresult rv = ToJSVal(aCx, &value);
   1.549 +  if (NS_SUCCEEDED(rv)) {
   1.550 +    aValue = value;
   1.551 +  }
   1.552 +  return rv;
   1.553 +}
   1.554 +
   1.555 +bool
   1.556 +KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const
   1.557 +{
   1.558 +  // Any keypath that passed validation is allowed for non-autoIncrement
   1.559 +  // objectStores.
   1.560 +  if (!aAutoIncrement) {
   1.561 +    return true;
   1.562 +  }
   1.563 +
   1.564 +  // Array keypaths are not allowed for autoIncrement objectStores.
   1.565 +  if (IsArray()) {
   1.566 +    return false;
   1.567 +  }
   1.568 +
   1.569 +  // Neither are empty strings.
   1.570 +  if (IsEmpty()) {
   1.571 +    return false;
   1.572 +  }
   1.573 +
   1.574 +  // Everything else is ok.
   1.575 +  return true;
   1.576 +}

mercurial