1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Indexed Database.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Ben Turner <bent.mozilla@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "base/basictypes.h"
41 :
42 : #include "IDBFactory.h"
43 :
44 : #include "nsILocalFile.h"
45 : #include "nsIScriptContext.h"
46 :
47 : #include "mozilla/storage.h"
48 : #include "mozilla/dom/ContentChild.h"
49 : #include "nsAppDirectoryServiceDefs.h"
50 : #include "nsComponentManagerUtils.h"
51 : #include "nsIScriptSecurityManager.h"
52 : #include "nsCharSeparatedTokenizer.h"
53 : #include "nsContentUtils.h"
54 : #include "nsDirectoryServiceUtils.h"
55 : #include "nsDOMClassInfoID.h"
56 : #include "nsIPrincipal.h"
57 : #include "nsHashKeys.h"
58 : #include "nsPIDOMWindow.h"
59 : #include "nsServiceManagerUtils.h"
60 : #include "nsThreadUtils.h"
61 : #include "nsXPCOMCID.h"
62 : #include "nsXULAppAPI.h"
63 :
64 : #include "AsyncConnectionHelper.h"
65 : #include "CheckPermissionsHelper.h"
66 : #include "DatabaseInfo.h"
67 : #include "IDBDatabase.h"
68 : #include "IDBEvents.h"
69 : #include "IDBKeyRange.h"
70 : #include "IndexedDatabaseManager.h"
71 : #include "Key.h"
72 :
73 : using namespace mozilla;
74 :
75 : USING_INDEXEDDB_NAMESPACE
76 :
77 : namespace {
78 :
79 : GeckoProcessType gAllowedProcessType = GeckoProcessType_Invalid;
80 :
81 : struct ObjectStoreInfoMap
82 23 : {
83 23 : ObjectStoreInfoMap()
84 23 : : id(LL_MININT), info(nsnull) { }
85 :
86 : PRInt64 id;
87 : ObjectStoreInfo* info;
88 : };
89 :
90 : } // anonymous namespace
91 :
92 50 : IDBFactory::IDBFactory()
93 50 : : mOwningObject(nsnull)
94 : {
95 50 : IDBFactory::NoteUsedByProcessType(XRE_GetProcessType());
96 50 : }
97 :
98 50 : IDBFactory::~IDBFactory()
99 : {
100 50 : }
101 :
102 : // static
103 : already_AddRefed<nsIIDBFactory>
104 0 : IDBFactory::Create(nsPIDOMWindow* aWindow)
105 : {
106 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
107 :
108 0 : NS_ENSURE_TRUE(aWindow, nsnull);
109 :
110 0 : if (aWindow->IsOuterWindow()) {
111 0 : aWindow = aWindow->GetCurrentInnerWindow();
112 0 : NS_ENSURE_TRUE(aWindow, nsnull);
113 : }
114 :
115 0 : nsRefPtr<IDBFactory> factory = new IDBFactory();
116 0 : factory->mWindow = aWindow;
117 0 : return factory.forget();
118 : }
119 :
120 : // static
121 : already_AddRefed<nsIIDBFactory>
122 50 : IDBFactory::Create(JSContext* aCx,
123 : JSObject* aOwningObject)
124 : {
125 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
126 50 : NS_ASSERTION(aCx, "Null context!");
127 50 : NS_ASSERTION(aOwningObject, "Null object!");
128 50 : NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
129 : "Not a global object!");
130 :
131 100 : nsRefPtr<IDBFactory> factory = new IDBFactory();
132 50 : factory->mOwningObject = aOwningObject;
133 50 : return factory.forget();
134 : }
135 :
136 : // static
137 : already_AddRefed<mozIStorageConnection>
138 529 : IDBFactory::GetConnection(const nsAString& aDatabaseFilePath)
139 : {
140 529 : NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")),
141 : "Bad file path!");
142 :
143 1058 : nsCOMPtr<nsILocalFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
144 529 : NS_ENSURE_TRUE(dbFile, nsnull);
145 :
146 529 : nsresult rv = dbFile->InitWithPath(aDatabaseFilePath);
147 529 : NS_ENSURE_SUCCESS(rv, nsnull);
148 :
149 : bool exists;
150 529 : rv = dbFile->Exists(&exists);
151 529 : NS_ENSURE_SUCCESS(rv, nsnull);
152 529 : NS_ENSURE_TRUE(exists, nsnull);
153 :
154 : nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
155 1058 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
156 529 : NS_ENSURE_TRUE(ss, nsnull);
157 :
158 1058 : nsCOMPtr<mozIStorageConnection> connection;
159 1058 : rv = ss->OpenDatabaseWithVFS(dbFile, NS_LITERAL_CSTRING("quota"),
160 1058 : getter_AddRefs(connection));
161 529 : NS_ENSURE_SUCCESS(rv, nsnull);
162 :
163 : // Turn on foreign key constraints!
164 1058 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
165 : "PRAGMA foreign_keys = ON;"
166 529 : ));
167 529 : NS_ENSURE_SUCCESS(rv, nsnull);
168 :
169 529 : return connection.forget();
170 : }
171 :
172 : // static
173 : void
174 50 : IDBFactory::NoteUsedByProcessType(GeckoProcessType aProcessType)
175 : {
176 50 : if (gAllowedProcessType == GeckoProcessType_Invalid) {
177 50 : gAllowedProcessType = aProcessType;
178 0 : } else if (aProcessType != gAllowedProcessType) {
179 0 : NS_RUNTIMEABORT("More than one process type is accessing IndexedDB!");
180 : }
181 50 : }
182 :
183 : // static
184 : nsresult
185 84 : IDBFactory::GetDirectory(nsIFile** aDirectory)
186 : {
187 : nsresult rv;
188 84 : if (XRE_GetProcessType() == GeckoProcessType_Default) {
189 84 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aDirectory);
190 84 : NS_ENSURE_SUCCESS(rv, rv);
191 84 : rv = (*aDirectory)->Append(NS_LITERAL_STRING("indexedDB"));
192 84 : NS_ENSURE_SUCCESS(rv, rv);
193 : } else {
194 : nsCOMPtr<nsILocalFile> localDirectory =
195 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
196 0 : rv = localDirectory->InitWithPath(
197 0 : ContentChild::GetSingleton()->GetIndexedDBPath());
198 0 : NS_ENSURE_SUCCESS(rv, rv);
199 0 : localDirectory.forget((nsILocalFile**)aDirectory);
200 : }
201 84 : return NS_OK;
202 : }
203 :
204 : // static
205 : nsresult
206 84 : IDBFactory::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
207 : nsIFile** aDirectory)
208 : {
209 168 : nsCOMPtr<nsIFile> directory;
210 84 : nsresult rv = GetDirectory(getter_AddRefs(directory));
211 84 : NS_ENSURE_SUCCESS(rv, rv);
212 :
213 168 : NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin);
214 84 : originSanitized.ReplaceChar(":/", '+');
215 :
216 84 : rv = directory->Append(originSanitized);
217 84 : NS_ENSURE_SUCCESS(rv, rv);
218 :
219 84 : directory.forget(aDirectory);
220 84 : return NS_OK;
221 : }
222 :
223 : inline
224 : bool
225 0 : IgnoreWhitespace(PRUnichar c)
226 : {
227 0 : return false;
228 : }
229 :
230 : // static
231 : nsresult
232 76 : IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
233 : nsIAtom* aDatabaseId,
234 : PRUint64* aVersion,
235 : ObjectStoreInfoArray& aObjectStores)
236 : {
237 76 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
238 76 : NS_ASSERTION(aConnection, "Null pointer!");
239 :
240 76 : aObjectStores.Clear();
241 :
242 : // Load object store names and ids.
243 152 : nsCOMPtr<mozIStorageStatement> stmt;
244 76 : nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
245 : "SELECT name, id, key_path, auto_increment "
246 : "FROM object_store"
247 152 : ), getter_AddRefs(stmt));
248 76 : NS_ENSURE_SUCCESS(rv, rv);
249 :
250 152 : nsAutoTArray<ObjectStoreInfoMap, 20> infoMap;
251 :
252 : bool hasResult;
253 175 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
254 : nsRefPtr<ObjectStoreInfo>* element =
255 23 : aObjectStores.AppendElement(new ObjectStoreInfo());
256 :
257 23 : ObjectStoreInfo* info = element->get();
258 :
259 23 : rv = stmt->GetString(0, info->name);
260 23 : NS_ENSURE_SUCCESS(rv, rv);
261 :
262 23 : info->id = stmt->AsInt64(1);
263 :
264 : PRInt32 columnType;
265 23 : nsresult rv = stmt->GetTypeOfIndex(2, &columnType);
266 23 : NS_ENSURE_SUCCESS(rv, rv);
267 23 : if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
268 10 : info->keyPath.SetIsVoid(true);
269 : }
270 : else {
271 13 : NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT,
272 : "Should be a string");
273 26 : nsString keyPath;
274 13 : rv = stmt->GetString(2, keyPath);
275 13 : NS_ENSURE_SUCCESS(rv, rv);
276 :
277 13 : if (!keyPath.IsEmpty() && keyPath.First() == ',') {
278 : // We use a comma in the beginning to indicate that it's an array of
279 : // key paths. This is to be able to tell a string-keypath from an
280 : // array-keypath which contains only one item.
281 : nsCharSeparatedTokenizerTemplate<IgnoreWhitespace>
282 0 : tokenizer(keyPath, ',');
283 0 : tokenizer.nextToken();
284 0 : while (tokenizer.hasMoreTokens()) {
285 0 : info->keyPathArray.AppendElement(tokenizer.nextToken());
286 : }
287 0 : NS_ASSERTION(!info->keyPathArray.IsEmpty(),
288 : "Should have at least one keypath");
289 : }
290 : else {
291 13 : info->keyPath = keyPath;
292 : }
293 :
294 : }
295 :
296 23 : info->nextAutoIncrementId = stmt->AsInt64(3);
297 23 : info->comittedAutoIncrementId = info->nextAutoIncrementId;
298 :
299 23 : ObjectStoreInfoMap* mapEntry = infoMap.AppendElement();
300 23 : NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY);
301 :
302 23 : mapEntry->id = info->id;
303 23 : mapEntry->info = info;
304 : }
305 76 : NS_ENSURE_SUCCESS(rv, rv);
306 :
307 : // Load index information
308 76 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
309 : "SELECT object_store_id, id, name, key_path, unique_index, multientry "
310 : "FROM object_store_index"
311 152 : ), getter_AddRefs(stmt));
312 76 : NS_ENSURE_SUCCESS(rv, rv);
313 :
314 170 : while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
315 18 : PRInt64 objectStoreId = stmt->AsInt64(0);
316 :
317 18 : ObjectStoreInfo* objectStoreInfo = nsnull;
318 28 : for (PRUint32 index = 0; index < infoMap.Length(); index++) {
319 28 : if (infoMap[index].id == objectStoreId) {
320 18 : objectStoreInfo = infoMap[index].info;
321 18 : break;
322 : }
323 : }
324 :
325 18 : if (!objectStoreInfo) {
326 0 : NS_ERROR("Index for nonexistant object store!");
327 0 : return NS_ERROR_UNEXPECTED;
328 : }
329 :
330 18 : IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement();
331 18 : NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY);
332 :
333 18 : indexInfo->id = stmt->AsInt64(1);
334 :
335 18 : rv = stmt->GetString(2, indexInfo->name);
336 18 : NS_ENSURE_SUCCESS(rv, rv);
337 :
338 36 : nsString keyPath;
339 18 : rv = stmt->GetString(3, keyPath);
340 18 : NS_ENSURE_SUCCESS(rv, rv);
341 18 : if (!keyPath.IsEmpty() && keyPath.First() == ',') {
342 : // We use a comma in the beginning to indicate that it's an array of
343 : // key paths. This is to be able to tell a string-keypath from an
344 : // array-keypath which contains only one item.
345 : nsCharSeparatedTokenizerTemplate<IgnoreWhitespace>
346 0 : tokenizer(keyPath, ',');
347 0 : tokenizer.nextToken();
348 0 : while (tokenizer.hasMoreTokens()) {
349 0 : indexInfo->keyPathArray.AppendElement(tokenizer.nextToken());
350 : }
351 0 : NS_ASSERTION(!indexInfo->keyPathArray.IsEmpty(),
352 : "Should have at least one keypath");
353 : }
354 : else {
355 18 : indexInfo->keyPath = keyPath;
356 : }
357 :
358 18 : indexInfo->unique = !!stmt->AsInt32(4);
359 36 : indexInfo->multiEntry = !!stmt->AsInt32(5);
360 : }
361 76 : NS_ENSURE_SUCCESS(rv, rv);
362 :
363 : // Load version information.
364 76 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
365 : "SELECT version "
366 : "FROM database"
367 152 : ), getter_AddRefs(stmt));
368 76 : NS_ENSURE_SUCCESS(rv, rv);
369 :
370 76 : rv = stmt->ExecuteStep(&hasResult);
371 76 : NS_ENSURE_SUCCESS(rv, rv);
372 :
373 76 : if (!hasResult) {
374 0 : NS_ERROR("Database has no version!");
375 0 : return NS_ERROR_UNEXPECTED;
376 : }
377 :
378 76 : PRInt64 version = 0;
379 76 : rv = stmt->GetInt64(0, &version);
380 :
381 76 : *aVersion = NS_MAX<PRInt64>(version, 0);
382 :
383 76 : return rv;
384 : }
385 :
386 : // static
387 : nsresult
388 54 : IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
389 : PRUint64 aVersion,
390 : ObjectStoreInfoArray& aObjectStores)
391 : {
392 54 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
393 54 : NS_ASSERTION(aDatabaseInfo, "Null pointer!");
394 :
395 108 : ObjectStoreInfoArray objectStores;
396 54 : objectStores.SwapElements(aObjectStores);
397 :
398 : #ifdef DEBUG
399 : {
400 108 : nsTArray<nsString> existingNames;
401 54 : aDatabaseInfo->GetObjectStoreNames(existingNames);
402 54 : NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo");
403 : }
404 : #endif
405 :
406 54 : aDatabaseInfo->version = aVersion;
407 :
408 54 : for (PRUint32 index = 0; index < objectStores.Length(); index++) {
409 0 : nsRefPtr<ObjectStoreInfo>& info = objectStores[index];
410 :
411 0 : if (!aDatabaseInfo->PutObjectStore(info)) {
412 0 : NS_WARNING("Out of memory!");
413 0 : return NS_ERROR_OUT_OF_MEMORY;
414 : }
415 : }
416 :
417 54 : return NS_OK;
418 : }
419 :
420 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory)
421 :
422 3090 : NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory)
423 3140 : NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory)
424 :
425 9208 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory)
426 6130 : NS_INTERFACE_MAP_ENTRY(nsISupports)
427 6080 : NS_INTERFACE_MAP_ENTRY(nsIIDBFactory)
428 3090 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFactory)
429 3040 : NS_INTERFACE_MAP_END
430 :
431 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory)
432 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
433 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow)
434 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
435 :
436 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
437 0 : if (tmp->mOwningObject) {
438 0 : tmp->mOwningObject = nsnull;
439 : }
440 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow)
441 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
442 :
443 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
444 0 : if (tmp->mOwningObject) {
445 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mOwningObject,
446 : "mOwningObject")
447 : }
448 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
449 :
450 : DOMCI_DATA(IDBFactory, IDBFactory)
451 :
452 : nsresult
453 76 : IDBFactory::OpenCommon(const nsAString& aName,
454 : PRInt64 aVersion,
455 : bool aDeleting,
456 : nsIIDBOpenDBRequest** _retval)
457 : {
458 76 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
459 76 : NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
460 :
461 76 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
462 : // Force ContentChild to cache the path from the parent, so that
463 : // we do not end up in a side thread that asks for the path (which
464 : // would make ContentChild try to send a message in a thread other
465 : // than the main one).
466 0 : ContentChild::GetSingleton()->GetIndexedDBPath();
467 : }
468 :
469 152 : nsCOMPtr<nsPIDOMWindow> window;
470 152 : nsCOMPtr<nsIScriptGlobalObject> sgo;
471 76 : JSObject* scriptOwner = nsnull;
472 :
473 76 : if (mWindow) {
474 0 : window = mWindow;
475 : }
476 : else {
477 76 : scriptOwner = mOwningObject;
478 : }
479 :
480 152 : nsCString origin;
481 : nsresult rv =
482 76 : IndexedDatabaseManager::GetASCIIOriginFromWindow(window, origin);
483 76 : NS_ENSURE_SUCCESS(rv, rv);
484 :
485 : nsRefPtr<IDBOpenDBRequest> request =
486 152 : IDBOpenDBRequest::Create(window, scriptOwner);
487 76 : NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
488 :
489 : nsRefPtr<OpenDatabaseHelper> openHelper =
490 228 : new OpenDatabaseHelper(request, aName, origin, aVersion, aDeleting);
491 :
492 76 : rv = openHelper->Init();
493 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
494 :
495 : nsRefPtr<CheckPermissionsHelper> permissionHelper =
496 228 : new CheckPermissionsHelper(openHelper, window, origin, aDeleting);
497 :
498 152 : nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
499 76 : NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
500 :
501 76 : rv = mgr->WaitForOpenAllowed(origin, openHelper->Id(), permissionHelper);
502 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
503 :
504 76 : request.forget(_retval);
505 76 : return NS_OK;
506 : }
507 :
508 : NS_IMETHODIMP
509 76 : IDBFactory::Open(const nsAString& aName,
510 : PRInt64 aVersion,
511 : PRUint8 aArgc,
512 : nsIIDBOpenDBRequest** _retval)
513 : {
514 76 : if (aVersion < 1 && aArgc) {
515 0 : return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
516 : }
517 :
518 76 : return OpenCommon(aName, aVersion, false, _retval);
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : IDBFactory::DeleteDatabase(const nsAString& aName,
523 : nsIIDBOpenDBRequest** _retval)
524 : {
525 0 : return OpenCommon(aName, 0, true, _retval);
526 : }
527 :
528 : NS_IMETHODIMP
529 2864 : IDBFactory::Cmp(const jsval& aFirst,
530 : const jsval& aSecond,
531 : JSContext* aCx,
532 : PRInt16* _retval)
533 : {
534 5728 : Key first, second;
535 2864 : nsresult rv = first.SetFromJSVal(aCx, aFirst);
536 2864 : NS_ENSURE_SUCCESS(rv, rv);
537 :
538 2846 : rv = second.SetFromJSVal(aCx, aSecond);
539 2846 : NS_ENSURE_SUCCESS(rv, rv);
540 :
541 2828 : if (first.IsUnset() || second.IsUnset()) {
542 4 : return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
543 : }
544 :
545 2824 : *_retval = Key::CompareKeys(first, second);
546 2824 : return NS_OK;
547 4392 : }
|