• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
6 
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
29 #include "content/common/indexed_db/indexed_db_key.h"
30 #include "content/common/indexed_db/indexed_db_key_path.h"
31 #include "content/common/indexed_db/indexed_db_key_range.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "net/url_request/url_request_context.h"
34 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
35 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
36 #include "third_party/leveldatabase/env_chromium.h"
37 #include "webkit/browser/blob/blob_data_handle.h"
38 #include "webkit/browser/fileapi/file_stream_writer.h"
39 #include "webkit/browser/fileapi/file_writer_delegate.h"
40 #include "webkit/browser/fileapi/local_file_stream_writer.h"
41 #include "webkit/common/database/database_identifier.h"
42 
43 using base::FilePath;
44 using base::StringPiece;
45 using fileapi::FileWriterDelegate;
46 
47 namespace content {
48 
49 namespace {
50 
GetBlobDirectoryName(const FilePath & pathBase,int64 database_id)51 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
52   return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
53 }
54 
GetBlobDirectoryNameForKey(const FilePath & pathBase,int64 database_id,int64 key)55 FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
56                                     int64 database_id,
57                                     int64 key) {
58   FilePath path = GetBlobDirectoryName(pathBase, database_id);
59   path = path.AppendASCII(base::StringPrintf(
60       "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
61   return path;
62 }
63 
GetBlobFileNameForKey(const FilePath & pathBase,int64 database_id,int64 key)64 FilePath GetBlobFileNameForKey(const FilePath& pathBase,
65                                int64 database_id,
66                                int64 key) {
67   FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
68   path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
69   return path;
70 }
71 
MakeIDBBlobDirectory(const FilePath & pathBase,int64 database_id,int64 key)72 bool MakeIDBBlobDirectory(const FilePath& pathBase,
73                           int64 database_id,
74                           int64 key) {
75   FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
76   return base::CreateDirectory(path);
77 }
78 
ComputeOriginIdentifier(const GURL & origin_url)79 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
80   return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
81 }
82 
ComputeFileName(const GURL & origin_url)83 static base::FilePath ComputeFileName(const GURL& origin_url) {
84   return base::FilePath()
85       .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
86       .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
87 }
88 
ComputeBlobPath(const GURL & origin_url)89 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
90   return base::FilePath()
91       .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
92       .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
93 }
94 
ComputeCorruptionFileName(const GURL & origin_url)95 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
96   return ComputeFileName(origin_url)
97       .Append(FILE_PATH_LITERAL("corruption_info.json"));
98 }
99 
100 }  // namespace
101 
102 static const int64 kKeyGeneratorInitialNumber =
103     1;  // From the IndexedDB specification.
104 
105 enum IndexedDBBackingStoreErrorSource {
106   // 0 - 2 are no longer used.
107   FIND_KEY_IN_INDEX = 3,
108   GET_IDBDATABASE_METADATA,
109   GET_INDEXES,
110   GET_KEY_GENERATOR_CURRENT_NUMBER,
111   GET_OBJECT_STORES,
112   GET_RECORD,
113   KEY_EXISTS_IN_OBJECT_STORE,
114   LOAD_CURRENT_ROW,
115   SET_UP_METADATA,
116   GET_PRIMARY_KEY_VIA_INDEX,
117   KEY_EXISTS_IN_INDEX,
118   VERSION_EXISTS,
119   DELETE_OBJECT_STORE,
120   SET_MAX_OBJECT_STORE_ID,
121   SET_MAX_INDEX_ID,
122   GET_NEW_DATABASE_ID,
123   GET_NEW_VERSION_NUMBER,
124   CREATE_IDBDATABASE_METADATA,
125   DELETE_DATABASE,
126   TRANSACTION_COMMIT_METHOD,  // TRANSACTION_COMMIT is a WinNT.h macro
127   GET_DATABASE_NAMES,
128   DELETE_INDEX,
129   CLEAR_OBJECT_STORE,
130   READ_BLOB_JOURNAL,
131   DECODE_BLOB_JOURNAL,
132   GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
133   GET_BLOB_INFO_FOR_RECORD,
134   INTERNAL_ERROR_MAX,
135 };
136 
RecordInternalError(const char * type,IndexedDBBackingStoreErrorSource location)137 static void RecordInternalError(const char* type,
138                                 IndexedDBBackingStoreErrorSource location) {
139   std::string name;
140   name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
141   base::Histogram::FactoryGet(name,
142                               1,
143                               INTERNAL_ERROR_MAX,
144                               INTERNAL_ERROR_MAX + 1,
145                               base::HistogramBase::kUmaTargetedHistogramFlag)
146       ->Add(location);
147 }
148 
149 // Use to signal conditions caused by data corruption.
150 // A macro is used instead of an inline function so that the assert and log
151 // report the line number.
152 #define REPORT_ERROR(type, location)                      \
153   do {                                                    \
154     LOG(ERROR) << "IndexedDB " type " Error: " #location; \
155     RecordInternalError(type, location);                  \
156   } while (0)
157 
158 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
159 #define INTERNAL_CONSISTENCY_ERROR(location) \
160   REPORT_ERROR("Consistency", location)
161 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
162 
163 // Use to signal conditions that usually indicate developer error, but
164 // could be caused by data corruption.  A macro is used instead of an
165 // inline function so that the assert and log report the line number.
166 // TODO(cmumford): Improve test coverage so that all error conditions are
167 // "tested" and then delete this macro.
168 #define REPORT_ERROR_UNTESTED(type, location)             \
169   do {                                                    \
170     LOG(ERROR) << "IndexedDB " type " Error: " #location; \
171     NOTREACHED();                                         \
172     RecordInternalError(type, location);                  \
173   } while (0)
174 
175 #define INTERNAL_READ_ERROR_UNTESTED(location) \
176   REPORT_ERROR_UNTESTED("Read", location)
177 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
178   REPORT_ERROR_UNTESTED("Consistency", location)
179 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
180   REPORT_ERROR_UNTESTED("Write", location)
181 
PutBool(LevelDBTransaction * transaction,const StringPiece & key,bool value)182 static void PutBool(LevelDBTransaction* transaction,
183                     const StringPiece& key,
184                     bool value) {
185   std::string buffer;
186   EncodeBool(value, &buffer);
187   transaction->Put(key, &buffer);
188 }
189 
190 // Was able to use LevelDB to read the data w/o error, but the data read was not
191 // in the expected format.
InternalInconsistencyStatus()192 static leveldb::Status InternalInconsistencyStatus() {
193   return leveldb::Status::Corruption("Internal inconsistency");
194 }
195 
InvalidDBKeyStatus()196 static leveldb::Status InvalidDBKeyStatus() {
197   return leveldb::Status::InvalidArgument("Invalid database key ID");
198 }
199 
IOErrorStatus()200 static leveldb::Status IOErrorStatus() {
201   return leveldb::Status::IOError("IO Error");
202 }
203 
204 template <typename DBOrTransaction>
GetInt(DBOrTransaction * db,const StringPiece & key,int64 * found_int,bool * found)205 static leveldb::Status GetInt(DBOrTransaction* db,
206                               const StringPiece& key,
207                               int64* found_int,
208                               bool* found) {
209   std::string result;
210   leveldb::Status s = db->Get(key, &result, found);
211   if (!s.ok())
212     return s;
213   if (!*found)
214     return leveldb::Status::OK();
215   StringPiece slice(result);
216   if (DecodeInt(&slice, found_int) && slice.empty())
217     return s;
218   return InternalInconsistencyStatus();
219 }
220 
PutInt(LevelDBTransaction * transaction,const StringPiece & key,int64 value)221 static void PutInt(LevelDBTransaction* transaction,
222                    const StringPiece& key,
223                    int64 value) {
224   DCHECK_GE(value, 0);
225   std::string buffer;
226   EncodeInt(value, &buffer);
227   transaction->Put(key, &buffer);
228 }
229 
230 template <typename DBOrTransaction>
GetVarInt(DBOrTransaction * db,const StringPiece & key,int64 * found_int,bool * found)231 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
232                                                     const StringPiece& key,
233                                                     int64* found_int,
234                                                     bool* found) {
235   std::string result;
236   leveldb::Status s = db->Get(key, &result, found);
237   if (!s.ok())
238     return s;
239   if (!*found)
240     return leveldb::Status::OK();
241   StringPiece slice(result);
242   if (DecodeVarInt(&slice, found_int) && slice.empty())
243     return s;
244   return InternalInconsistencyStatus();
245 }
246 
PutVarInt(LevelDBTransaction * transaction,const StringPiece & key,int64 value)247 static void PutVarInt(LevelDBTransaction* transaction,
248                       const StringPiece& key,
249                       int64 value) {
250   std::string buffer;
251   EncodeVarInt(value, &buffer);
252   transaction->Put(key, &buffer);
253 }
254 
255 template <typename DBOrTransaction>
GetString(DBOrTransaction * db,const StringPiece & key,base::string16 * found_string,bool * found)256 WARN_UNUSED_RESULT static leveldb::Status GetString(
257     DBOrTransaction* db,
258     const StringPiece& key,
259     base::string16* found_string,
260     bool* found) {
261   std::string result;
262   *found = false;
263   leveldb::Status s = db->Get(key, &result, found);
264   if (!s.ok())
265     return s;
266   if (!*found)
267     return leveldb::Status::OK();
268   StringPiece slice(result);
269   if (DecodeString(&slice, found_string) && slice.empty())
270     return s;
271   return InternalInconsistencyStatus();
272 }
273 
PutString(LevelDBTransaction * transaction,const StringPiece & key,const base::string16 & value)274 static void PutString(LevelDBTransaction* transaction,
275                       const StringPiece& key,
276                       const base::string16& value) {
277   std::string buffer;
278   EncodeString(value, &buffer);
279   transaction->Put(key, &buffer);
280 }
281 
PutIDBKeyPath(LevelDBTransaction * transaction,const StringPiece & key,const IndexedDBKeyPath & value)282 static void PutIDBKeyPath(LevelDBTransaction* transaction,
283                           const StringPiece& key,
284                           const IndexedDBKeyPath& value) {
285   std::string buffer;
286   EncodeIDBKeyPath(value, &buffer);
287   transaction->Put(key, &buffer);
288 }
289 
CompareKeys(const StringPiece & a,const StringPiece & b)290 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
291   return Compare(a, b, false /*index_keys*/);
292 }
293 
CompareIndexKeys(const StringPiece & a,const StringPiece & b)294 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
295   return Compare(a, b, true /*index_keys*/);
296 }
297 
Compare(const StringPiece & a,const StringPiece & b) const298 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
299                                                const StringPiece& b) const {
300   return content::Compare(a, b, false /*index_keys*/);
301 }
302 
Name() const303 const char* IndexedDBBackingStore::Comparator::Name() const {
304   return "idb_cmp1";
305 }
306 
307 // 0 - Initial version.
308 // 1 - Adds UserIntVersion to DatabaseMetaData.
309 // 2 - Adds DataVersion to to global metadata.
310 // 3 - Adds metadata needed for blob support.
311 static const int64 kLatestKnownSchemaVersion = 3;
IsSchemaKnown(LevelDBDatabase * db,bool * known)312 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
313   int64 db_schema_version = 0;
314   bool found = false;
315   leveldb::Status s =
316       GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
317   if (!s.ok())
318     return false;
319   if (!found) {
320     *known = true;
321     return true;
322   }
323   if (db_schema_version > kLatestKnownSchemaVersion) {
324     *known = false;
325     return true;
326   }
327 
328   const uint32 latest_known_data_version =
329       blink::kSerializedScriptValueVersion;
330   int64 db_data_version = 0;
331   s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
332   if (!s.ok())
333     return false;
334   if (!found) {
335     *known = true;
336     return true;
337   }
338 
339   if (db_data_version > latest_known_data_version) {
340     *known = false;
341     return true;
342   }
343 
344   *known = true;
345   return true;
346 }
347 
348 // TODO(ericu): Move this down into the member section of this file.  I'm
349 // leaving it here for this CL as it's easier to see the diffs in place.
SetUpMetadata()350 WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
351   const uint32 latest_known_data_version =
352       blink::kSerializedScriptValueVersion;
353   const std::string schema_version_key = SchemaVersionKey::Encode();
354   const std::string data_version_key = DataVersionKey::Encode();
355 
356   scoped_refptr<LevelDBTransaction> transaction =
357       IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
358 
359   int64 db_schema_version = 0;
360   int64 db_data_version = 0;
361   bool found = false;
362   leveldb::Status s =
363       GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
364   if (!s.ok()) {
365     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
366     return false;
367   }
368   if (!found) {
369     // Initialize new backing store.
370     db_schema_version = kLatestKnownSchemaVersion;
371     PutInt(transaction.get(), schema_version_key, db_schema_version);
372     db_data_version = latest_known_data_version;
373     PutInt(transaction.get(), data_version_key, db_data_version);
374     // If a blob directory already exists for this database, blow it away.  It's
375     // leftover from a partially-purged previous generation of data.
376     if (!base::DeleteFile(blob_path_, true)) {
377       INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
378       return false;
379     }
380   } else {
381     // Upgrade old backing store.
382     DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
383     if (db_schema_version < 1) {
384       db_schema_version = 1;
385       PutInt(transaction.get(), schema_version_key, db_schema_version);
386       const std::string start_key =
387           DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
388       const std::string stop_key =
389           DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
390       scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
391       for (s = it->Seek(start_key);
392            s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
393            s = it->Next()) {
394         int64 database_id = 0;
395         found = false;
396         s = GetInt(transaction.get(), it->Key(), &database_id, &found);
397         if (!s.ok()) {
398           INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
399           return false;
400         }
401         if (!found) {
402           INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
403           return false;
404         }
405         std::string int_version_key = DatabaseMetaDataKey::Encode(
406             database_id, DatabaseMetaDataKey::USER_INT_VERSION);
407         PutVarInt(transaction.get(),
408                   int_version_key,
409                   IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
410       }
411     }
412     if (s.ok() && db_schema_version < 2) {
413       db_schema_version = 2;
414       PutInt(transaction.get(), schema_version_key, db_schema_version);
415       db_data_version = blink::kSerializedScriptValueVersion;
416       PutInt(transaction.get(), data_version_key, db_data_version);
417     }
418     if (db_schema_version < 3) {
419       db_schema_version = 3;
420       if (!base::DeleteFile(blob_path_, true)) {
421         INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
422         return false;
423       }
424     }
425   }
426 
427   if (!s.ok()) {
428     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
429     return false;
430   }
431 
432   // All new values will be written using this serialization version.
433   found = false;
434   s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
435   if (!s.ok()) {
436     INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
437     return false;
438   }
439   if (!found) {
440     INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
441     return false;
442   }
443   if (db_data_version < latest_known_data_version) {
444     db_data_version = latest_known_data_version;
445     PutInt(transaction.get(), data_version_key, db_data_version);
446   }
447 
448   DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
449   DCHECK_EQ(db_data_version, latest_known_data_version);
450 
451   s = transaction->Commit();
452   if (!s.ok()) {
453     INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
454     return false;
455   }
456   return true;
457 }
458 
459 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,int64 database_id,int64 * max_object_store_id)460 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
461     DBOrTransaction* db,
462     int64 database_id,
463     int64* max_object_store_id) {
464   const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
465       database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
466   return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
467 }
468 
469 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,const std::string & max_object_store_id_key,int64 * max_object_store_id)470 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
471     DBOrTransaction* db,
472     const std::string& max_object_store_id_key,
473     int64* max_object_store_id) {
474   *max_object_store_id = -1;
475   bool found = false;
476   leveldb::Status s =
477       GetInt(db, max_object_store_id_key, max_object_store_id, &found);
478   if (!s.ok())
479     return s;
480   if (!found)
481     *max_object_store_id = 0;
482 
483   DCHECK_GE(*max_object_store_id, 0);
484   return s;
485 }
486 
487 class DefaultLevelDBFactory : public LevelDBFactory {
488  public:
DefaultLevelDBFactory()489   DefaultLevelDBFactory() {}
OpenLevelDB(const base::FilePath & file_name,const LevelDBComparator * comparator,scoped_ptr<LevelDBDatabase> * db,bool * is_disk_full)490   virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
491                                       const LevelDBComparator* comparator,
492                                       scoped_ptr<LevelDBDatabase>* db,
493                                       bool* is_disk_full) OVERRIDE {
494     return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
495   }
DestroyLevelDB(const base::FilePath & file_name)496   virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
497       OVERRIDE {
498     return LevelDBDatabase::Destroy(file_name);
499   }
500 
501  private:
502   DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
503 };
504 
GetBlobKeyGeneratorCurrentNumber(LevelDBTransaction * leveldb_transaction,int64 database_id,int64 * blob_key_generator_current_number)505 static bool GetBlobKeyGeneratorCurrentNumber(
506     LevelDBTransaction* leveldb_transaction,
507     int64 database_id,
508     int64* blob_key_generator_current_number) {
509   const std::string key_gen_key = DatabaseMetaDataKey::Encode(
510       database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
511 
512   // Default to initial number if not found.
513   int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
514   std::string data;
515 
516   bool found = false;
517   bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
518   if (!ok) {
519     INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
520     return false;
521   }
522   if (found) {
523     StringPiece slice(data);
524     if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
525         !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
526       INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
527       return false;
528     }
529   }
530   *blob_key_generator_current_number = cur_number;
531   return true;
532 }
533 
UpdateBlobKeyGeneratorCurrentNumber(LevelDBTransaction * leveldb_transaction,int64 database_id,int64 blob_key_generator_current_number)534 static bool UpdateBlobKeyGeneratorCurrentNumber(
535     LevelDBTransaction* leveldb_transaction,
536     int64 database_id,
537     int64 blob_key_generator_current_number) {
538 #ifndef NDEBUG
539   int64 old_number;
540   if (!GetBlobKeyGeneratorCurrentNumber(
541           leveldb_transaction, database_id, &old_number))
542     return false;
543   DCHECK_LT(old_number, blob_key_generator_current_number);
544 #endif
545   DCHECK(
546       DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
547   const std::string key = DatabaseMetaDataKey::Encode(
548       database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
549 
550   PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
551   return true;
552 }
553 
554 // TODO(ericu): Error recovery. If we persistently can't read the
555 // blob journal, the safe thing to do is to clear it and leak the blobs,
556 // though that may be costly. Still, database/directory deletion should always
557 // clean things up, and we can write an fsck that will do a full correction if
558 // need be.
559 template <typename T>
GetBlobJournal(const StringPiece & leveldb_key,T * leveldb_transaction,BlobJournalType * journal)560 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
561                                       T* leveldb_transaction,
562                                       BlobJournalType* journal) {
563   std::string data;
564   bool found = false;
565   leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
566   if (!s.ok()) {
567     INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL);
568     return s;
569   }
570   journal->clear();
571   if (!found || !data.size())
572     return leveldb::Status::OK();
573   StringPiece slice(data);
574   if (!DecodeBlobJournal(&slice, journal)) {
575     INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
576     s = InternalInconsistencyStatus();
577   }
578   return s;
579 }
580 
ClearBlobJournal(LevelDBTransaction * leveldb_transaction,const std::string & level_db_key)581 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
582                              const std::string& level_db_key) {
583   leveldb_transaction->Remove(level_db_key);
584 }
585 
UpdatePrimaryJournalWithBlobList(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)586 static void UpdatePrimaryJournalWithBlobList(
587     LevelDBTransaction* leveldb_transaction,
588     const BlobJournalType& journal) {
589   const std::string leveldb_key = BlobJournalKey::Encode();
590   std::string data;
591   EncodeBlobJournal(journal, &data);
592   leveldb_transaction->Put(leveldb_key, &data);
593 }
594 
UpdateLiveBlobJournalWithBlobList(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)595 static void UpdateLiveBlobJournalWithBlobList(
596     LevelDBTransaction* leveldb_transaction,
597     const BlobJournalType& journal) {
598   const std::string leveldb_key = LiveBlobJournalKey::Encode();
599   std::string data;
600   EncodeBlobJournal(journal, &data);
601   leveldb_transaction->Put(leveldb_key, &data);
602 }
603 
MergeBlobsIntoLiveBlobJournal(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)604 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
605     LevelDBTransaction* leveldb_transaction,
606     const BlobJournalType& journal) {
607   BlobJournalType old_journal;
608   const std::string key = LiveBlobJournalKey::Encode();
609   leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
610   if (!s.ok())
611     return s;
612 
613   old_journal.insert(old_journal.end(), journal.begin(), journal.end());
614 
615   UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
616   return leveldb::Status::OK();
617 }
618 
UpdateBlobJournalWithDatabase(LevelDBDirectTransaction * leveldb_transaction,int64 database_id)619 static void UpdateBlobJournalWithDatabase(
620     LevelDBDirectTransaction* leveldb_transaction,
621     int64 database_id) {
622   BlobJournalType journal;
623   journal.push_back(
624       std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
625   const std::string key = BlobJournalKey::Encode();
626   std::string data;
627   EncodeBlobJournal(journal, &data);
628   leveldb_transaction->Put(key, &data);
629 }
630 
MergeDatabaseIntoLiveBlobJournal(LevelDBDirectTransaction * leveldb_transaction,int64 database_id)631 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
632     LevelDBDirectTransaction* leveldb_transaction,
633     int64 database_id) {
634   BlobJournalType journal;
635   const std::string key = LiveBlobJournalKey::Encode();
636   leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
637   if (!s.ok())
638     return s;
639   journal.push_back(
640       std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
641   std::string data;
642   EncodeBlobJournal(journal, &data);
643   leveldb_transaction->Put(key, &data);
644   return leveldb::Status::OK();
645 }
646 
647 // Blob Data is encoded as a series of:
648 //   { is_file [bool], key [int64 as varInt],
649 //     type [string-with-length, may be empty],
650 //     (for Blobs only) size [int64 as varInt]
651 //     (for Files only) fileName [string-with-length]
652 //   }
653 // There is no length field; just read until you run out of data.
EncodeBlobData(const std::vector<IndexedDBBlobInfo * > & blob_info)654 static std::string EncodeBlobData(
655     const std::vector<IndexedDBBlobInfo*>& blob_info) {
656   std::string ret;
657   std::vector<IndexedDBBlobInfo*>::const_iterator iter;
658   for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
659     const IndexedDBBlobInfo& info = **iter;
660     EncodeBool(info.is_file(), &ret);
661     EncodeVarInt(info.key(), &ret);
662     EncodeStringWithLength(info.type(), &ret);
663     if (info.is_file())
664       EncodeStringWithLength(info.file_name(), &ret);
665     else
666       EncodeVarInt(info.size(), &ret);
667   }
668   return ret;
669 }
670 
DecodeBlobData(const std::string & data,std::vector<IndexedDBBlobInfo> * output)671 static bool DecodeBlobData(const std::string& data,
672                            std::vector<IndexedDBBlobInfo>* output) {
673   std::vector<IndexedDBBlobInfo> ret;
674   output->clear();
675   StringPiece slice(data);
676   while (!slice.empty()) {
677     bool is_file;
678     int64 key;
679     base::string16 type;
680     int64 size;
681     base::string16 file_name;
682 
683     if (!DecodeBool(&slice, &is_file))
684       return false;
685     if (!DecodeVarInt(&slice, &key) ||
686         !DatabaseMetaDataKey::IsValidBlobKey(key))
687       return false;
688     if (!DecodeStringWithLength(&slice, &type))
689       return false;
690     if (is_file) {
691       if (!DecodeStringWithLength(&slice, &file_name))
692         return false;
693       ret.push_back(IndexedDBBlobInfo(key, type, file_name));
694     } else {
695       if (!DecodeVarInt(&slice, &size) || size < 0)
696         return false;
697       ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
698     }
699   }
700   output->swap(ret);
701 
702   return true;
703 }
704 
IndexedDBBackingStore(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & blob_path,net::URLRequestContext * request_context,scoped_ptr<LevelDBDatabase> db,scoped_ptr<LevelDBComparator> comparator,base::TaskRunner * task_runner)705 IndexedDBBackingStore::IndexedDBBackingStore(
706     IndexedDBFactory* indexed_db_factory,
707     const GURL& origin_url,
708     const base::FilePath& blob_path,
709     net::URLRequestContext* request_context,
710     scoped_ptr<LevelDBDatabase> db,
711     scoped_ptr<LevelDBComparator> comparator,
712     base::TaskRunner* task_runner)
713     : indexed_db_factory_(indexed_db_factory),
714       origin_url_(origin_url),
715       blob_path_(blob_path),
716       origin_identifier_(ComputeOriginIdentifier(origin_url)),
717       request_context_(request_context),
718       task_runner_(task_runner),
719       db_(db.Pass()),
720       comparator_(comparator.Pass()),
721       active_blob_registry_(this) {
722 }
723 
~IndexedDBBackingStore()724 IndexedDBBackingStore::~IndexedDBBackingStore() {
725   if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
726     ChildProcessSecurityPolicyImpl* policy =
727         ChildProcessSecurityPolicyImpl::GetInstance();
728     std::set<int>::const_iterator iter;
729     for (iter = child_process_ids_granted_.begin();
730          iter != child_process_ids_granted_.end();
731          ++iter) {
732       policy->RevokeAllPermissionsForFile(*iter, blob_path_);
733     }
734   }
735   STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
736                                        incognito_blob_map_.end());
737   // db_'s destructor uses comparator_. The order of destruction is important.
738   db_.reset();
739   comparator_.reset();
740 }
741 
RecordIdentifier(const std::string & primary_key,int64 version)742 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
743     const std::string& primary_key,
744     int64 version)
745     : primary_key_(primary_key), version_(version) {
746   DCHECK(!primary_key.empty());
747 }
RecordIdentifier()748 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
749     : primary_key_(), version_(-1) {}
~RecordIdentifier()750 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
751 
CursorOptions()752 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
~CursorOptions()753 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
754 
755 enum IndexedDBBackingStoreOpenResult {
756   INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
757   INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
758   INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
759   INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
760   INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
761   INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
762   INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
763   INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
764   INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
765   INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
766   INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
767   INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
768   INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
769   INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
770   INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
771   INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
772   INDEXED_DB_BACKING_STORE_OPEN_MAX,
773 };
774 
775 // static
Open(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & path_base,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * disk_full,base::TaskRunner * task_runner,bool clean_journal)776 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
777     IndexedDBFactory* indexed_db_factory,
778     const GURL& origin_url,
779     const base::FilePath& path_base,
780     net::URLRequestContext* request_context,
781     blink::WebIDBDataLoss* data_loss,
782     std::string* data_loss_message,
783     bool* disk_full,
784     base::TaskRunner* task_runner,
785     bool clean_journal) {
786   *data_loss = blink::WebIDBDataLossNone;
787   DefaultLevelDBFactory leveldb_factory;
788   return IndexedDBBackingStore::Open(indexed_db_factory,
789                                      origin_url,
790                                      path_base,
791                                      request_context,
792                                      data_loss,
793                                      data_loss_message,
794                                      disk_full,
795                                      &leveldb_factory,
796                                      task_runner,
797                                      clean_journal);
798 }
799 
OriginToCustomHistogramSuffix(const GURL & origin_url)800 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
801   if (origin_url.host() == "docs.google.com")
802     return ".Docs";
803   return std::string();
804 }
805 
HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,const GURL & origin_url)806 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
807                                 const GURL& origin_url) {
808   UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
809                             result,
810                             INDEXED_DB_BACKING_STORE_OPEN_MAX);
811   const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
812   // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
813   // to generate a graph. So as not to alter the meaning of that graph,
814   // continue to collect all stats there (above) but also now collect docs stats
815   // separately (below).
816   if (!suffix.empty()) {
817     base::LinearHistogram::FactoryGet(
818         "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
819         1,
820         INDEXED_DB_BACKING_STORE_OPEN_MAX,
821         INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
822         base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
823   }
824 }
825 
IsPathTooLong(const base::FilePath & leveldb_dir)826 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
827   int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
828   if (limit == -1) {
829     DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
830     // In limited testing, ChromeOS returns 143, other OSes 255.
831 #if defined(OS_CHROMEOS)
832     limit = 143;
833 #else
834     limit = 255;
835 #endif
836   }
837   size_t component_length = leveldb_dir.BaseName().value().length();
838   if (component_length > static_cast<uint32_t>(limit)) {
839     DLOG(WARNING) << "Path component length (" << component_length
840                   << ") exceeds maximum (" << limit
841                   << ") allowed by this filesystem.";
842     const int min = 140;
843     const int max = 300;
844     const int num_buckets = 12;
845     UMA_HISTOGRAM_CUSTOM_COUNTS(
846         "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
847         component_length,
848         min,
849         max,
850         num_buckets);
851     return true;
852   }
853   return false;
854 }
855 
DestroyBackingStore(const base::FilePath & path_base,const GURL & origin_url)856 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
857     const base::FilePath& path_base,
858     const GURL& origin_url) {
859   const base::FilePath file_path =
860       path_base.Append(ComputeFileName(origin_url));
861   DefaultLevelDBFactory leveldb_factory;
862   return leveldb_factory.DestroyLevelDB(file_path);
863 }
864 
ReadCorruptionInfo(const base::FilePath & path_base,const GURL & origin_url,std::string & message)865 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
866                                                const GURL& origin_url,
867                                                std::string& message) {
868   const base::FilePath info_path =
869       path_base.Append(ComputeCorruptionFileName(origin_url));
870 
871   if (IsPathTooLong(info_path))
872     return false;
873 
874   const int64 max_json_len = 4096;
875   int64 file_size(0);
876   if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
877     return false;
878   if (!file_size) {
879     NOTREACHED();
880     return false;
881   }
882 
883   base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
884   bool success = false;
885   if (file.IsValid()) {
886     std::vector<char> bytes(file_size);
887     if (file_size == file.Read(0, &bytes[0], file_size)) {
888       std::string input_js(&bytes[0], file_size);
889       base::JSONReader reader;
890       scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
891       if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
892         base::DictionaryValue* dict_val =
893             static_cast<base::DictionaryValue*>(val.get());
894         success = dict_val->GetString("message", &message);
895       }
896     }
897     file.Close();
898   }
899 
900   base::DeleteFile(info_path, false);
901 
902   return success;
903 }
904 
RecordCorruptionInfo(const base::FilePath & path_base,const GURL & origin_url,const std::string & message)905 bool IndexedDBBackingStore::RecordCorruptionInfo(
906     const base::FilePath& path_base,
907     const GURL& origin_url,
908     const std::string& message) {
909   const base::FilePath info_path =
910       path_base.Append(ComputeCorruptionFileName(origin_url));
911   if (IsPathTooLong(info_path))
912     return false;
913 
914   base::DictionaryValue root_dict;
915   root_dict.SetString("message", message);
916   std::string output_js;
917   base::JSONWriter::Write(&root_dict, &output_js);
918 
919   base::File file(info_path,
920                   base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
921   if (!file.IsValid())
922     return false;
923   int written = file.Write(0, output_js.c_str(), output_js.length());
924   return size_t(written) == output_js.length();
925 }
926 
927 // static
Open(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & path_base,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * is_disk_full,LevelDBFactory * leveldb_factory,base::TaskRunner * task_runner,bool clean_journal)928 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
929     IndexedDBFactory* indexed_db_factory,
930     const GURL& origin_url,
931     const base::FilePath& path_base,
932     net::URLRequestContext* request_context,
933     blink::WebIDBDataLoss* data_loss,
934     std::string* data_loss_message,
935     bool* is_disk_full,
936     LevelDBFactory* leveldb_factory,
937     base::TaskRunner* task_runner,
938     bool clean_journal) {
939   IDB_TRACE("IndexedDBBackingStore::Open");
940   DCHECK(!path_base.empty());
941   *data_loss = blink::WebIDBDataLossNone;
942   *data_loss_message = "";
943   *is_disk_full = false;
944 
945   scoped_ptr<LevelDBComparator> comparator(new Comparator());
946 
947   if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
948     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
949                         origin_url);
950   }
951   if (!base::CreateDirectory(path_base)) {
952     LOG(ERROR) << "Unable to create IndexedDB database path "
953                << path_base.AsUTF8Unsafe();
954     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
955                         origin_url);
956     return scoped_refptr<IndexedDBBackingStore>();
957   }
958 
959   const base::FilePath file_path =
960       path_base.Append(ComputeFileName(origin_url));
961   const base::FilePath blob_path =
962       path_base.Append(ComputeBlobPath(origin_url));
963 
964   if (IsPathTooLong(file_path)) {
965     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
966                         origin_url);
967     return scoped_refptr<IndexedDBBackingStore>();
968   }
969 
970   scoped_ptr<LevelDBDatabase> db;
971   leveldb::Status status = leveldb_factory->OpenLevelDB(
972       file_path, comparator.get(), &db, is_disk_full);
973 
974   DCHECK(!db == !status.ok());
975   if (!status.ok()) {
976     if (leveldb_env::IndicatesDiskFull(status)) {
977       *is_disk_full = true;
978     } else if (leveldb_env::IsCorruption(status)) {
979       *data_loss = blink::WebIDBDataLossTotal;
980       *data_loss_message = leveldb_env::GetCorruptionMessage(status);
981     }
982   }
983 
984   bool is_schema_known = false;
985   if (db) {
986     std::string corruption_message;
987     if (ReadCorruptionInfo(path_base, origin_url, corruption_message)) {
988       LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
989                     "database.";
990       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
991                           origin_url);
992       db.reset();
993       *data_loss = blink::WebIDBDataLossTotal;
994       *data_loss_message =
995           "IndexedDB (database was corrupt): " + corruption_message;
996     } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
997       LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
998                     "failure to open";
999       HistogramOpenStatus(
1000           INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1001           origin_url);
1002       db.reset();
1003       *data_loss = blink::WebIDBDataLossTotal;
1004       *data_loss_message = "I/O error checking schema";
1005     } else if (!is_schema_known) {
1006       LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1007                     "as failure to open";
1008       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1009                           origin_url);
1010       db.reset();
1011       *data_loss = blink::WebIDBDataLossTotal;
1012       *data_loss_message = "Unknown schema";
1013     }
1014   }
1015 
1016   DCHECK(status.ok() || !is_schema_known || leveldb_env::IsIOError(status) ||
1017          leveldb_env::IsCorruption(status));
1018 
1019   if (db) {
1020     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1021   } else if (leveldb_env::IsIOError(status)) {
1022     LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1023                << status.ToString();
1024     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1025     return scoped_refptr<IndexedDBBackingStore>();
1026   } else {
1027     DCHECK(!is_schema_known || leveldb_env::IsCorruption(status));
1028     LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1029     status = leveldb_factory->DestroyLevelDB(file_path);
1030     if (!status.ok()) {
1031       LOG(ERROR) << "IndexedDB backing store cleanup failed";
1032       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1033                           origin_url);
1034       return scoped_refptr<IndexedDBBackingStore>();
1035     }
1036 
1037     LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1038     leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1039     if (!db) {
1040       LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1041       HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1042                           origin_url);
1043       return scoped_refptr<IndexedDBBackingStore>();
1044     }
1045     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1046                         origin_url);
1047   }
1048 
1049   if (!db) {
1050     NOTREACHED();
1051     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
1052                         origin_url);
1053     return scoped_refptr<IndexedDBBackingStore>();
1054   }
1055 
1056   scoped_refptr<IndexedDBBackingStore> backing_store =
1057       Create(indexed_db_factory,
1058              origin_url,
1059              blob_path,
1060              request_context,
1061              db.Pass(),
1062              comparator.Pass(),
1063              task_runner);
1064 
1065   if (clean_journal && backing_store &&
1066       !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1067     HistogramOpenStatus(
1068         INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1069     return scoped_refptr<IndexedDBBackingStore>();
1070   }
1071   return backing_store;
1072 }
1073 
1074 // static
OpenInMemory(const GURL & origin_url,base::TaskRunner * task_runner)1075 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1076     const GURL& origin_url,
1077     base::TaskRunner* task_runner) {
1078   DefaultLevelDBFactory leveldb_factory;
1079   return IndexedDBBackingStore::OpenInMemory(
1080       origin_url, &leveldb_factory, task_runner);
1081 }
1082 
1083 // static
OpenInMemory(const GURL & origin_url,LevelDBFactory * leveldb_factory,base::TaskRunner * task_runner)1084 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1085     const GURL& origin_url,
1086     LevelDBFactory* leveldb_factory,
1087     base::TaskRunner* task_runner) {
1088   IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1089 
1090   scoped_ptr<LevelDBComparator> comparator(new Comparator());
1091   scoped_ptr<LevelDBDatabase> db =
1092       LevelDBDatabase::OpenInMemory(comparator.get());
1093   if (!db) {
1094     LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1095     HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1096                         origin_url);
1097     return scoped_refptr<IndexedDBBackingStore>();
1098   }
1099   HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1100 
1101   return Create(NULL /* indexed_db_factory */,
1102                 origin_url,
1103                 base::FilePath(),
1104                 NULL /* request_context */,
1105                 db.Pass(),
1106                 comparator.Pass(),
1107                 task_runner);
1108 }
1109 
1110 // static
Create(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & blob_path,net::URLRequestContext * request_context,scoped_ptr<LevelDBDatabase> db,scoped_ptr<LevelDBComparator> comparator,base::TaskRunner * task_runner)1111 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1112     IndexedDBFactory* indexed_db_factory,
1113     const GURL& origin_url,
1114     const base::FilePath& blob_path,
1115     net::URLRequestContext* request_context,
1116     scoped_ptr<LevelDBDatabase> db,
1117     scoped_ptr<LevelDBComparator> comparator,
1118     base::TaskRunner* task_runner) {
1119   // TODO(jsbell): Handle comparator name changes.
1120   scoped_refptr<IndexedDBBackingStore> backing_store(
1121       new IndexedDBBackingStore(indexed_db_factory,
1122                                 origin_url,
1123                                 blob_path,
1124                                 request_context,
1125                                 db.Pass(),
1126                                 comparator.Pass(),
1127                                 task_runner));
1128   if (!backing_store->SetUpMetadata())
1129     return scoped_refptr<IndexedDBBackingStore>();
1130 
1131   return backing_store;
1132 }
1133 
GrantChildProcessPermissions(int child_process_id)1134 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1135   if (!child_process_ids_granted_.count(child_process_id)) {
1136     child_process_ids_granted_.insert(child_process_id);
1137     ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1138         child_process_id, blob_path_);
1139   }
1140 }
1141 
GetDatabaseNames(leveldb::Status * s)1142 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1143     leveldb::Status* s) {
1144   *s = leveldb::Status::OK();
1145   std::vector<base::string16> found_names;
1146   const std::string start_key =
1147       DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1148   const std::string stop_key =
1149       DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1150 
1151   DCHECK(found_names.empty());
1152 
1153   scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1154   for (*s = it->Seek(start_key);
1155        s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1156        *s = it->Next()) {
1157     StringPiece slice(it->Key());
1158     DatabaseNameKey database_name_key;
1159     if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1160         !slice.empty()) {
1161       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1162       continue;
1163     }
1164     found_names.push_back(database_name_key.database_name());
1165   }
1166 
1167   if (!s->ok())
1168     INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1169 
1170   return found_names;
1171 }
1172 
GetIDBDatabaseMetaData(const base::string16 & name,IndexedDBDatabaseMetadata * metadata,bool * found)1173 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1174     const base::string16& name,
1175     IndexedDBDatabaseMetadata* metadata,
1176     bool* found) {
1177   const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1178   *found = false;
1179 
1180   leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1181   if (!s.ok()) {
1182     INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1183     return s;
1184   }
1185   if (!*found)
1186     return leveldb::Status::OK();
1187 
1188   s = GetString(db_.get(),
1189                 DatabaseMetaDataKey::Encode(metadata->id,
1190                                             DatabaseMetaDataKey::USER_VERSION),
1191                 &metadata->version,
1192                 found);
1193   if (!s.ok()) {
1194     INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1195     return s;
1196   }
1197   if (!*found) {
1198     INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1199     return InternalInconsistencyStatus();
1200   }
1201 
1202   s = GetVarInt(db_.get(),
1203                 DatabaseMetaDataKey::Encode(
1204                     metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1205                 &metadata->int_version,
1206                 found);
1207   if (!s.ok()) {
1208     INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1209     return s;
1210   }
1211   if (!*found) {
1212     INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1213     return InternalInconsistencyStatus();
1214   }
1215 
1216   if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1217     metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1218 
1219   s = GetMaxObjectStoreId(
1220       db_.get(), metadata->id, &metadata->max_object_store_id);
1221   if (!s.ok()) {
1222     INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1223   }
1224 
1225   // We don't cache this, we just check it if it's there.
1226   int64 blob_key_generator_current_number =
1227       DatabaseMetaDataKey::kInvalidBlobKey;
1228 
1229   s = GetVarInt(
1230       db_.get(),
1231       DatabaseMetaDataKey::Encode(
1232           metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1233       &blob_key_generator_current_number,
1234       found);
1235   if (!s.ok()) {
1236     INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1237     return s;
1238   }
1239   if (!*found) {
1240     // This database predates blob support.
1241     *found = true;
1242   } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1243                  blob_key_generator_current_number)) {
1244     INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1245     return InternalInconsistencyStatus();
1246   }
1247 
1248   return s;
1249 }
1250 
GetNewDatabaseId(LevelDBTransaction * transaction,int64 * new_id)1251 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1252     LevelDBTransaction* transaction,
1253     int64* new_id) {
1254   *new_id = -1;
1255   int64 max_database_id = -1;
1256   bool found = false;
1257   leveldb::Status s =
1258       GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1259   if (!s.ok()) {
1260     INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1261     return s;
1262   }
1263   if (!found)
1264     max_database_id = 0;
1265 
1266   DCHECK_GE(max_database_id, 0);
1267 
1268   int64 database_id = max_database_id + 1;
1269   PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1270   *new_id = database_id;
1271   return leveldb::Status::OK();
1272 }
1273 
CreateIDBDatabaseMetaData(const base::string16 & name,const base::string16 & version,int64 int_version,int64 * row_id)1274 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1275     const base::string16& name,
1276     const base::string16& version,
1277     int64 int_version,
1278     int64* row_id) {
1279   scoped_refptr<LevelDBTransaction> transaction =
1280       IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1281 
1282   leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1283   if (!s.ok())
1284     return s;
1285   DCHECK_GE(*row_id, 0);
1286 
1287   if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1288     int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1289 
1290   PutInt(transaction.get(),
1291          DatabaseNameKey::Encode(origin_identifier_, name),
1292          *row_id);
1293   PutString(
1294       transaction.get(),
1295       DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1296       version);
1297   PutVarInt(transaction.get(),
1298             DatabaseMetaDataKey::Encode(*row_id,
1299                                         DatabaseMetaDataKey::USER_INT_VERSION),
1300             int_version);
1301   PutVarInt(
1302       transaction.get(),
1303       DatabaseMetaDataKey::Encode(
1304           *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1305       DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1306 
1307   s = transaction->Commit();
1308   if (!s.ok())
1309     INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1310   return s;
1311 }
1312 
UpdateIDBDatabaseIntVersion(IndexedDBBackingStore::Transaction * transaction,int64 row_id,int64 int_version)1313 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1314     IndexedDBBackingStore::Transaction* transaction,
1315     int64 row_id,
1316     int64 int_version) {
1317   if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1318     int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1319   DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1320   PutVarInt(transaction->transaction(),
1321             DatabaseMetaDataKey::Encode(row_id,
1322                                         DatabaseMetaDataKey::USER_INT_VERSION),
1323             int_version);
1324   return true;
1325 }
1326 
1327 // If you're deleting a range that contains user keys that have blob info, this
1328 // won't clean up the blobs.
DeleteRangeBasic(LevelDBTransaction * transaction,const std::string & begin,const std::string & end,bool upper_open)1329 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1330                                         const std::string& begin,
1331                                         const std::string& end,
1332                                         bool upper_open) {
1333   scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1334   leveldb::Status s;
1335   for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1336                                 (upper_open ? CompareKeys(it->Key(), end) < 0
1337                                             : CompareKeys(it->Key(), end) <= 0);
1338        s = it->Next())
1339     transaction->Remove(it->Key());
1340   return s;
1341 }
1342 
DeleteBlobsInRange(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const std::string & start_key,const std::string & end_key,bool upper_open)1343 static leveldb::Status DeleteBlobsInRange(
1344     IndexedDBBackingStore::Transaction* transaction,
1345     int64 database_id,
1346     int64 object_store_id,
1347     const std::string& start_key,
1348     const std::string& end_key,
1349     bool upper_open) {
1350   scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1351   leveldb::Status s = it->Seek(start_key);
1352   for (; s.ok() && it->IsValid() &&
1353              (upper_open ? CompareKeys(it->Key(), end_key) < 0
1354                          : CompareKeys(it->Key(), end_key) <= 0);
1355        s = it->Next()) {
1356     StringPiece key_piece(it->Key());
1357     std::string user_key =
1358         BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1359     if (!user_key.size()) {
1360       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1361       return InternalInconsistencyStatus();
1362     }
1363     transaction->PutBlobInfo(
1364         database_id, object_store_id, user_key, NULL, NULL);
1365   }
1366   return s;
1367 }
1368 
DeleteBlobsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1369 static leveldb::Status DeleteBlobsInObjectStore(
1370     IndexedDBBackingStore::Transaction* transaction,
1371     int64 database_id,
1372     int64 object_store_id) {
1373   std::string start_key, stop_key;
1374   start_key =
1375       BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1376   stop_key =
1377       BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1378   return DeleteBlobsInRange(
1379       transaction, database_id, object_store_id, start_key, stop_key, true);
1380 }
1381 
DeleteDatabase(const base::string16 & name)1382 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1383     const base::string16& name) {
1384   IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1385   scoped_ptr<LevelDBDirectTransaction> transaction =
1386       LevelDBDirectTransaction::Create(db_.get());
1387 
1388   leveldb::Status s;
1389   s = CleanUpBlobJournal(BlobJournalKey::Encode());
1390   if (!s.ok())
1391     return s;
1392 
1393   IndexedDBDatabaseMetadata metadata;
1394   bool success = false;
1395   s = GetIDBDatabaseMetaData(name, &metadata, &success);
1396   if (!s.ok())
1397     return s;
1398   if (!success)
1399     return leveldb::Status::OK();
1400 
1401   const std::string start_key = DatabaseMetaDataKey::Encode(
1402       metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1403   const std::string stop_key = DatabaseMetaDataKey::Encode(
1404       metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1405   scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1406   for (s = it->Seek(start_key);
1407        s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1408        s = it->Next())
1409     transaction->Remove(it->Key());
1410   if (!s.ok()) {
1411     INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1412     return s;
1413   }
1414 
1415   const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1416   transaction->Remove(key);
1417 
1418   bool need_cleanup = false;
1419   if (active_blob_registry()->MarkDeletedCheckIfUsed(
1420           metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1421     s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1422     if (!s.ok())
1423       return s;
1424   } else {
1425     UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1426     need_cleanup = true;
1427   }
1428 
1429   s = transaction->Commit();
1430   if (!s.ok()) {
1431     INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1432     return s;
1433   }
1434 
1435   if (need_cleanup)
1436     CleanUpBlobJournal(BlobJournalKey::Encode());
1437 
1438   db_->Compact(start_key, stop_key);
1439   return s;
1440 }
1441 
CheckObjectStoreAndMetaDataType(const LevelDBIterator * it,const std::string & stop_key,int64 object_store_id,int64 meta_data_type)1442 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1443                                             const std::string& stop_key,
1444                                             int64 object_store_id,
1445                                             int64 meta_data_type) {
1446   if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1447     return false;
1448 
1449   StringPiece slice(it->Key());
1450   ObjectStoreMetaDataKey meta_data_key;
1451   bool ok =
1452       ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1453   DCHECK(ok);
1454   if (meta_data_key.ObjectStoreId() != object_store_id)
1455     return false;
1456   if (meta_data_key.MetaDataType() != meta_data_type)
1457     return false;
1458   return ok;
1459 }
1460 
1461 // TODO(jsbell): This should do some error handling rather than
1462 // plowing ahead when bad data is encountered.
GetObjectStores(int64 database_id,IndexedDBDatabaseMetadata::ObjectStoreMap * object_stores)1463 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1464     int64 database_id,
1465     IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1466   IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1467   if (!KeyPrefix::IsValidDatabaseId(database_id))
1468     return InvalidDBKeyStatus();
1469   const std::string start_key =
1470       ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1471   const std::string stop_key =
1472       ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1473 
1474   DCHECK(object_stores->empty());
1475 
1476   scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1477   leveldb::Status s = it->Seek(start_key);
1478   while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1479     StringPiece slice(it->Key());
1480     ObjectStoreMetaDataKey meta_data_key;
1481     bool ok =
1482         ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1483     DCHECK(ok);
1484     if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1485       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1486       // Possible stale metadata, but don't fail the load.
1487       s = it->Next();
1488       if (!s.ok())
1489         break;
1490       continue;
1491     }
1492 
1493     int64 object_store_id = meta_data_key.ObjectStoreId();
1494 
1495     // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1496     // simplify.
1497     base::string16 object_store_name;
1498     {
1499       StringPiece slice(it->Value());
1500       if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1501         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1502     }
1503 
1504     s = it->Next();
1505     if (!s.ok())
1506       break;
1507     if (!CheckObjectStoreAndMetaDataType(it.get(),
1508                                          stop_key,
1509                                          object_store_id,
1510                                          ObjectStoreMetaDataKey::KEY_PATH)) {
1511       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1512       break;
1513     }
1514     IndexedDBKeyPath key_path;
1515     {
1516       StringPiece slice(it->Value());
1517       if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1518         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1519     }
1520 
1521     s = it->Next();
1522     if (!s.ok())
1523       break;
1524     if (!CheckObjectStoreAndMetaDataType(
1525              it.get(),
1526              stop_key,
1527              object_store_id,
1528              ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1529       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1530       break;
1531     }
1532     bool auto_increment;
1533     {
1534       StringPiece slice(it->Value());
1535       if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1536         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1537     }
1538 
1539     s = it->Next();  // Is evictable.
1540     if (!s.ok())
1541       break;
1542     if (!CheckObjectStoreAndMetaDataType(it.get(),
1543                                          stop_key,
1544                                          object_store_id,
1545                                          ObjectStoreMetaDataKey::EVICTABLE)) {
1546       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1547       break;
1548     }
1549 
1550     s = it->Next();  // Last version.
1551     if (!s.ok())
1552       break;
1553     if (!CheckObjectStoreAndMetaDataType(
1554              it.get(),
1555              stop_key,
1556              object_store_id,
1557              ObjectStoreMetaDataKey::LAST_VERSION)) {
1558       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1559       break;
1560     }
1561 
1562     s = it->Next();  // Maximum index id allocated.
1563     if (!s.ok())
1564       break;
1565     if (!CheckObjectStoreAndMetaDataType(
1566              it.get(),
1567              stop_key,
1568              object_store_id,
1569              ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1570       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1571       break;
1572     }
1573     int64 max_index_id;
1574     {
1575       StringPiece slice(it->Value());
1576       if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1577         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1578     }
1579 
1580     s = it->Next();  // [optional] has key path (is not null)
1581     if (!s.ok())
1582       break;
1583     if (CheckObjectStoreAndMetaDataType(it.get(),
1584                                         stop_key,
1585                                         object_store_id,
1586                                         ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1587       bool has_key_path;
1588       {
1589         StringPiece slice(it->Value());
1590         if (!DecodeBool(&slice, &has_key_path))
1591           INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1592       }
1593       // This check accounts for two layers of legacy coding:
1594       // (1) Initially, has_key_path was added to distinguish null vs. string.
1595       // (2) Later, null vs. string vs. array was stored in the key_path itself.
1596       // So this check is only relevant for string-type key_paths.
1597       if (!has_key_path &&
1598           (key_path.type() == blink::WebIDBKeyPathTypeString &&
1599            !key_path.string().empty())) {
1600         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1601         break;
1602       }
1603       if (!has_key_path)
1604         key_path = IndexedDBKeyPath();
1605       s = it->Next();
1606       if (!s.ok())
1607         break;
1608     }
1609 
1610     int64 key_generator_current_number = -1;
1611     if (CheckObjectStoreAndMetaDataType(
1612             it.get(),
1613             stop_key,
1614             object_store_id,
1615             ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1616       StringPiece slice(it->Value());
1617       if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1618         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1619 
1620       // TODO(jsbell): Return key_generator_current_number, cache in
1621       // object store, and write lazily to backing store.  For now,
1622       // just assert that if it was written it was valid.
1623       DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1624       s = it->Next();
1625       if (!s.ok())
1626         break;
1627     }
1628 
1629     IndexedDBObjectStoreMetadata metadata(object_store_name,
1630                                           object_store_id,
1631                                           key_path,
1632                                           auto_increment,
1633                                           max_index_id);
1634     s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1635     if (!s.ok())
1636       break;
1637     (*object_stores)[object_store_id] = metadata;
1638   }
1639 
1640   if (!s.ok())
1641     INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1642 
1643   return s;
1644 }
1645 
SetMaxObjectStoreId(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id)1646 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1647     LevelDBTransaction* transaction,
1648     int64 database_id,
1649     int64 object_store_id) {
1650   const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1651       database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1652   int64 max_object_store_id = -1;
1653   leveldb::Status s = GetMaxObjectStoreId(
1654       transaction, max_object_store_id_key, &max_object_store_id);
1655   if (!s.ok()) {
1656     INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1657     return s;
1658   }
1659 
1660   if (object_store_id <= max_object_store_id) {
1661     INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1662     return InternalInconsistencyStatus();
1663   }
1664   PutInt(transaction, max_object_store_id_key, object_store_id);
1665   return s;
1666 }
1667 
Compact()1668 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1669 
CreateObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool auto_increment)1670 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1671     IndexedDBBackingStore::Transaction* transaction,
1672     int64 database_id,
1673     int64 object_store_id,
1674     const base::string16& name,
1675     const IndexedDBKeyPath& key_path,
1676     bool auto_increment) {
1677   IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1678   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1679     return InvalidDBKeyStatus();
1680   LevelDBTransaction* leveldb_transaction = transaction->transaction();
1681   leveldb::Status s =
1682       SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1683   if (!s.ok())
1684     return s;
1685 
1686   const std::string name_key = ObjectStoreMetaDataKey::Encode(
1687       database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1688   const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1689       database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1690   const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1691       database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1692   const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1693       database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1694   const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1695       database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1696   const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1697       database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1698   const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1699       database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1700   const std::string key_generator_current_number_key =
1701       ObjectStoreMetaDataKey::Encode(
1702           database_id,
1703           object_store_id,
1704           ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1705   const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1706 
1707   PutString(leveldb_transaction, name_key, name);
1708   PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1709   PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1710   PutInt(leveldb_transaction, evictable_key, false);
1711   PutInt(leveldb_transaction, last_version_key, 1);
1712   PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1713   PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1714   PutInt(leveldb_transaction,
1715          key_generator_current_number_key,
1716          kKeyGeneratorInitialNumber);
1717   PutInt(leveldb_transaction, names_key, object_store_id);
1718   return s;
1719 }
1720 
DeleteObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1721 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1722     IndexedDBBackingStore::Transaction* transaction,
1723     int64 database_id,
1724     int64 object_store_id) {
1725   IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1726   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1727     return InvalidDBKeyStatus();
1728   LevelDBTransaction* leveldb_transaction = transaction->transaction();
1729 
1730   base::string16 object_store_name;
1731   bool found = false;
1732   leveldb::Status s =
1733       GetString(leveldb_transaction,
1734                 ObjectStoreMetaDataKey::Encode(
1735                     database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1736                 &object_store_name,
1737                 &found);
1738   if (!s.ok()) {
1739     INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1740     return s;
1741   }
1742   if (!found) {
1743     INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1744     return InternalInconsistencyStatus();
1745   }
1746 
1747   s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1748   if (!s.ok()) {
1749     INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1750     return s;
1751   }
1752 
1753   s = DeleteRangeBasic(
1754       leveldb_transaction,
1755       ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1756       ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1757       true);
1758 
1759   if (s.ok()) {
1760     leveldb_transaction->Remove(
1761         ObjectStoreNamesKey::Encode(database_id, object_store_name));
1762 
1763     s = DeleteRangeBasic(
1764         leveldb_transaction,
1765         IndexFreeListKey::Encode(database_id, object_store_id, 0),
1766         IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1767         true);
1768   }
1769 
1770   if (s.ok()) {
1771     s = DeleteRangeBasic(
1772         leveldb_transaction,
1773         IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1774         IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1775         true);
1776   }
1777 
1778   if (!s.ok()) {
1779     INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1780     return s;
1781   }
1782 
1783   return ClearObjectStore(transaction, database_id, object_store_id);
1784 }
1785 
GetRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,IndexedDBValue * record)1786 leveldb::Status IndexedDBBackingStore::GetRecord(
1787     IndexedDBBackingStore::Transaction* transaction,
1788     int64 database_id,
1789     int64 object_store_id,
1790     const IndexedDBKey& key,
1791     IndexedDBValue* record) {
1792   IDB_TRACE("IndexedDBBackingStore::GetRecord");
1793   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1794     return InvalidDBKeyStatus();
1795   LevelDBTransaction* leveldb_transaction = transaction->transaction();
1796 
1797   const std::string leveldb_key =
1798       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1799   std::string data;
1800 
1801   record->clear();
1802 
1803   bool found = false;
1804   leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1805   if (!s.ok()) {
1806     INTERNAL_READ_ERROR(GET_RECORD);
1807     return s;
1808   }
1809   if (!found)
1810     return s;
1811   if (data.empty()) {
1812     INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1813     return leveldb::Status::NotFound("Record contained no data");
1814   }
1815 
1816   int64 version;
1817   StringPiece slice(data);
1818   if (!DecodeVarInt(&slice, &version)) {
1819     INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1820     return InternalInconsistencyStatus();
1821   }
1822 
1823   record->bits = slice.as_string();
1824   return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1825 }
1826 
GetNewVersionNumber(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 * new_version_number)1827 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1828     LevelDBTransaction* transaction,
1829     int64 database_id,
1830     int64 object_store_id,
1831     int64* new_version_number) {
1832   const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1833       database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1834 
1835   *new_version_number = -1;
1836   int64 last_version = -1;
1837   bool found = false;
1838   leveldb::Status s =
1839       GetInt(transaction, last_version_key, &last_version, &found);
1840   if (!s.ok()) {
1841     INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1842     return s;
1843   }
1844   if (!found)
1845     last_version = 0;
1846 
1847   DCHECK_GE(last_version, 0);
1848 
1849   int64 version = last_version + 1;
1850   PutInt(transaction, last_version_key, version);
1851 
1852   // TODO(jsbell): Think about how we want to handle the overflow scenario.
1853   DCHECK(version > last_version);
1854 
1855   *new_version_number = version;
1856   return s;
1857 }
1858 
PutRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,IndexedDBValue & value,ScopedVector<webkit_blob::BlobDataHandle> * handles,RecordIdentifier * record_identifier)1859 leveldb::Status IndexedDBBackingStore::PutRecord(
1860     IndexedDBBackingStore::Transaction* transaction,
1861     int64 database_id,
1862     int64 object_store_id,
1863     const IndexedDBKey& key,
1864     IndexedDBValue& value,
1865     ScopedVector<webkit_blob::BlobDataHandle>* handles,
1866     RecordIdentifier* record_identifier) {
1867   IDB_TRACE("IndexedDBBackingStore::PutRecord");
1868   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1869     return InvalidDBKeyStatus();
1870   DCHECK(key.IsValid());
1871 
1872   LevelDBTransaction* leveldb_transaction = transaction->transaction();
1873   int64 version = -1;
1874   leveldb::Status s = GetNewVersionNumber(
1875       leveldb_transaction, database_id, object_store_id, &version);
1876   if (!s.ok())
1877     return s;
1878   DCHECK_GE(version, 0);
1879   const std::string object_store_data_key =
1880       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1881 
1882   std::string v;
1883   EncodeVarInt(version, &v);
1884   v.append(value.bits);
1885 
1886   leveldb_transaction->Put(object_store_data_key, &v);
1887   s = transaction->PutBlobInfoIfNeeded(database_id,
1888                                        object_store_id,
1889                                        object_store_data_key,
1890                                        &value.blob_info,
1891                                        handles);
1892   if (!s.ok())
1893     return s;
1894   DCHECK(!handles->size());
1895 
1896   const std::string exists_entry_key =
1897       ExistsEntryKey::Encode(database_id, object_store_id, key);
1898   std::string version_encoded;
1899   EncodeInt(version, &version_encoded);
1900   leveldb_transaction->Put(exists_entry_key, &version_encoded);
1901 
1902   std::string key_encoded;
1903   EncodeIDBKey(key, &key_encoded);
1904   record_identifier->Reset(key_encoded, version);
1905   return s;
1906 }
1907 
ClearObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1908 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1909     IndexedDBBackingStore::Transaction* transaction,
1910     int64 database_id,
1911     int64 object_store_id) {
1912   IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1913   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1914     return InvalidDBKeyStatus();
1915   const std::string start_key =
1916       KeyPrefix(database_id, object_store_id).Encode();
1917   const std::string stop_key =
1918       KeyPrefix(database_id, object_store_id + 1).Encode();
1919 
1920   leveldb::Status s =
1921       DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
1922   if (!s.ok()) {
1923     INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1924     return s;
1925   }
1926   return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1927 }
1928 
DeleteRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const RecordIdentifier & record_identifier)1929 leveldb::Status IndexedDBBackingStore::DeleteRecord(
1930     IndexedDBBackingStore::Transaction* transaction,
1931     int64 database_id,
1932     int64 object_store_id,
1933     const RecordIdentifier& record_identifier) {
1934   IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1935   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1936     return InvalidDBKeyStatus();
1937   LevelDBTransaction* leveldb_transaction = transaction->transaction();
1938 
1939   const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1940       database_id, object_store_id, record_identifier.primary_key());
1941   leveldb_transaction->Remove(object_store_data_key);
1942   leveldb::Status s = transaction->PutBlobInfoIfNeeded(
1943       database_id, object_store_id, object_store_data_key, NULL, NULL);
1944   if (!s.ok())
1945     return s;
1946 
1947   const std::string exists_entry_key = ExistsEntryKey::Encode(
1948       database_id, object_store_id, record_identifier.primary_key());
1949   leveldb_transaction->Remove(exists_entry_key);
1950   return leveldb::Status::OK();
1951 }
1952 
DeleteRange(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & key_range)1953 leveldb::Status IndexedDBBackingStore::DeleteRange(
1954     IndexedDBBackingStore::Transaction* transaction,
1955     int64 database_id,
1956     int64 object_store_id,
1957     const IndexedDBKeyRange& key_range) {
1958   leveldb::Status s;
1959   scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
1960       OpenObjectStoreCursor(transaction,
1961                             database_id,
1962                             object_store_id,
1963                             key_range,
1964                             indexed_db::CURSOR_NEXT,
1965                             &s);
1966   if (!s.ok())
1967     return s;
1968   if (!start_cursor)
1969     return leveldb::Status::OK();  // Empty range == delete success.
1970 
1971   scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
1972       OpenObjectStoreCursor(transaction,
1973                             database_id,
1974                             object_store_id,
1975                             key_range,
1976                             indexed_db::CURSOR_PREV,
1977                             &s);
1978 
1979   if (!s.ok())
1980     return s;
1981   if (!end_cursor)
1982     return leveldb::Status::OK();  // Empty range == delete success.
1983 
1984   BlobEntryKey start_blob_key, end_blob_key;
1985 
1986   std::string start_key = ObjectStoreDataKey::Encode(
1987       database_id, object_store_id, start_cursor->key());
1988   base::StringPiece start_key_piece(start_key);
1989   if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
1990     return InternalInconsistencyStatus();
1991   std::string stop_key = ObjectStoreDataKey::Encode(
1992       database_id, object_store_id, end_cursor->key());
1993   base::StringPiece stop_key_piece(stop_key);
1994   if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
1995     return InternalInconsistencyStatus();
1996 
1997   s = DeleteBlobsInRange(transaction,
1998                          database_id,
1999                          object_store_id,
2000                          start_blob_key.Encode(),
2001                          end_blob_key.Encode(),
2002                          false);
2003   if (!s.ok())
2004     return s;
2005   s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2006   if (!s.ok())
2007     return s;
2008   start_key =
2009       ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2010   stop_key =
2011       ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2012   return DeleteRangeBasic(
2013       transaction->transaction(), start_key, stop_key, false);
2014 }
2015 
GetKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 * key_generator_current_number)2016 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2017     IndexedDBBackingStore::Transaction* transaction,
2018     int64 database_id,
2019     int64 object_store_id,
2020     int64* key_generator_current_number) {
2021   if (!KeyPrefix::ValidIds(database_id, object_store_id))
2022     return InvalidDBKeyStatus();
2023   LevelDBTransaction* leveldb_transaction = transaction->transaction();
2024 
2025   const std::string key_generator_current_number_key =
2026       ObjectStoreMetaDataKey::Encode(
2027           database_id,
2028           object_store_id,
2029           ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2030 
2031   *key_generator_current_number = -1;
2032   std::string data;
2033 
2034   bool found = false;
2035   leveldb::Status s =
2036       leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2037   if (!s.ok()) {
2038     INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2039     return s;
2040   }
2041   if (found && !data.empty()) {
2042     StringPiece slice(data);
2043     if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2044       INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2045       return InternalInconsistencyStatus();
2046     }
2047     return s;
2048   }
2049 
2050   // Previously, the key generator state was not stored explicitly
2051   // but derived from the maximum numeric key present in existing
2052   // data. This violates the spec as the data may be cleared but the
2053   // key generator state must be preserved.
2054   // TODO(jsbell): Fix this for all stores on database open?
2055   const std::string start_key =
2056       ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2057   const std::string stop_key =
2058       ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2059 
2060   scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2061   int64 max_numeric_key = 0;
2062 
2063   for (s = it->Seek(start_key);
2064        s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2065        s = it->Next()) {
2066     StringPiece slice(it->Key());
2067     ObjectStoreDataKey data_key;
2068     if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2069       INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2070       return InternalInconsistencyStatus();
2071     }
2072     scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2073     if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2074       int64 n = static_cast<int64>(user_key->number());
2075       if (n > max_numeric_key)
2076         max_numeric_key = n;
2077     }
2078   }
2079 
2080   if (s.ok())
2081     *key_generator_current_number = max_numeric_key + 1;
2082   else
2083     INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2084 
2085   return s;
2086 }
2087 
MaybeUpdateKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 new_number,bool check_current)2088 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2089     IndexedDBBackingStore::Transaction* transaction,
2090     int64 database_id,
2091     int64 object_store_id,
2092     int64 new_number,
2093     bool check_current) {
2094   if (!KeyPrefix::ValidIds(database_id, object_store_id))
2095     return InvalidDBKeyStatus();
2096 
2097   if (check_current) {
2098     int64 current_number;
2099     leveldb::Status s = GetKeyGeneratorCurrentNumber(
2100         transaction, database_id, object_store_id, &current_number);
2101     if (!s.ok())
2102       return s;
2103     if (new_number <= current_number)
2104       return s;
2105   }
2106 
2107   const std::string key_generator_current_number_key =
2108       ObjectStoreMetaDataKey::Encode(
2109           database_id,
2110           object_store_id,
2111           ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2112   PutInt(
2113       transaction->transaction(), key_generator_current_number_key, new_number);
2114   return leveldb::Status::OK();
2115 }
2116 
KeyExistsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,RecordIdentifier * found_record_identifier,bool * found)2117 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2118     IndexedDBBackingStore::Transaction* transaction,
2119     int64 database_id,
2120     int64 object_store_id,
2121     const IndexedDBKey& key,
2122     RecordIdentifier* found_record_identifier,
2123     bool* found) {
2124   IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2125   if (!KeyPrefix::ValidIds(database_id, object_store_id))
2126     return InvalidDBKeyStatus();
2127   *found = false;
2128   const std::string leveldb_key =
2129       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2130   std::string data;
2131 
2132   leveldb::Status s =
2133       transaction->transaction()->Get(leveldb_key, &data, found);
2134   if (!s.ok()) {
2135     INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2136     return s;
2137   }
2138   if (!*found)
2139     return leveldb::Status::OK();
2140   if (!data.size()) {
2141     INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2142     return InternalInconsistencyStatus();
2143   }
2144 
2145   int64 version;
2146   StringPiece slice(data);
2147   if (!DecodeVarInt(&slice, &version))
2148     return InternalInconsistencyStatus();
2149 
2150   std::string encoded_key;
2151   EncodeIDBKey(key, &encoded_key);
2152   found_record_identifier->Reset(encoded_key, version);
2153   return s;
2154 }
2155 
2156 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2157     : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2158  public:
2159   typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2160       WriteDescriptorVec;
ChainedBlobWriterImpl(int64 database_id,IndexedDBBackingStore * backing_store,WriteDescriptorVec & blobs,scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)2161   ChainedBlobWriterImpl(
2162       int64 database_id,
2163       IndexedDBBackingStore* backing_store,
2164       WriteDescriptorVec& blobs,
2165       scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2166       : waiting_for_callback_(false),
2167         database_id_(database_id),
2168         backing_store_(backing_store),
2169         callback_(callback),
2170         aborted_(false) {
2171     blobs_.swap(blobs);
2172     iter_ = blobs_.begin();
2173     backing_store->task_runner()->PostTask(
2174         FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2175   }
2176 
set_delegate(scoped_ptr<FileWriterDelegate> delegate)2177   virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE {
2178     delegate_.reset(delegate.release());
2179   }
2180 
ReportWriteCompletion(bool succeeded,int64 bytes_written)2181   virtual void ReportWriteCompletion(bool succeeded,
2182                                      int64 bytes_written) OVERRIDE {
2183     DCHECK(waiting_for_callback_);
2184     DCHECK(!succeeded || bytes_written >= 0);
2185     waiting_for_callback_ = false;
2186     if (delegate_.get())  // Only present for Blob, not File.
2187       content::BrowserThread::DeleteSoon(
2188           content::BrowserThread::IO, FROM_HERE, delegate_.release());
2189     if (aborted_) {
2190       self_ref_ = NULL;
2191       return;
2192     }
2193     if (iter_->size() != -1 && iter_->size() != bytes_written)
2194       succeeded = false;
2195     if (succeeded) {
2196       ++iter_;
2197       WriteNextFile();
2198     } else {
2199       callback_->Run(false);
2200     }
2201   }
2202 
Abort()2203   virtual void Abort() OVERRIDE {
2204     if (!waiting_for_callback_)
2205       return;
2206     self_ref_ = this;
2207     aborted_ = true;
2208   }
2209 
2210  private:
~ChainedBlobWriterImpl()2211   virtual ~ChainedBlobWriterImpl() {}
2212 
WriteNextFile()2213   void WriteNextFile() {
2214     DCHECK(!waiting_for_callback_);
2215     DCHECK(!aborted_);
2216     if (iter_ == blobs_.end()) {
2217       DCHECK(!self_ref_);
2218       callback_->Run(true);
2219       return;
2220     } else {
2221       if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2222         callback_->Run(false);
2223         return;
2224       }
2225       waiting_for_callback_ = true;
2226     }
2227   }
2228 
2229   bool waiting_for_callback_;
2230   scoped_refptr<ChainedBlobWriterImpl> self_ref_;
2231   WriteDescriptorVec blobs_;
2232   WriteDescriptorVec::const_iterator iter_;
2233   int64 database_id_;
2234   IndexedDBBackingStore* backing_store_;
2235   scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2236   scoped_ptr<FileWriterDelegate> delegate_;
2237   bool aborted_;
2238 
2239   DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2240 };
2241 
2242 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2243                           public base::RefCounted<LocalWriteClosure> {
2244  public:
LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter * chained_blob_writer,base::TaskRunner * task_runner)2245   LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2246                         chained_blob_writer,
2247                     base::TaskRunner* task_runner)
2248       : chained_blob_writer_(chained_blob_writer),
2249         task_runner_(task_runner),
2250         bytes_written_(0) {}
2251 
Run(base::File::Error rv,int64 bytes,FileWriterDelegate::WriteProgressStatus write_status)2252   void Run(base::File::Error rv,
2253            int64 bytes,
2254            FileWriterDelegate::WriteProgressStatus write_status) {
2255     DCHECK_GE(bytes, 0);
2256     bytes_written_ += bytes;
2257     if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2258       return;  // We don't care about progress events.
2259     if (rv == base::File::FILE_OK) {
2260       DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2261     } else {
2262       DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2263              write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2264     }
2265     task_runner_->PostTask(
2266         FROM_HERE,
2267         base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
2268                    this,
2269                    write_status == FileWriterDelegate::SUCCESS_COMPLETED));
2270   }
2271 
writeBlobToFileOnIOThread(const FilePath & file_path,const GURL & blob_url,net::URLRequestContext * request_context)2272   void writeBlobToFileOnIOThread(const FilePath& file_path,
2273                                  const GURL& blob_url,
2274                                  net::URLRequestContext* request_context) {
2275     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
2276     scoped_ptr<fileapi::FileStreamWriter> writer(
2277         fileapi::FileStreamWriter::CreateForLocalFile(
2278             task_runner_, file_path, 0,
2279             fileapi::FileStreamWriter::CREATE_NEW_FILE));
2280     scoped_ptr<FileWriterDelegate> delegate(
2281         new FileWriterDelegate(writer.Pass(),
2282                                FileWriterDelegate::FLUSH_ON_COMPLETION));
2283 
2284     DCHECK(blob_url.is_valid());
2285     scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2286         blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
2287 
2288     delegate->Start(blob_request.Pass(),
2289                     base::Bind(&LocalWriteClosure::Run, this));
2290     chained_blob_writer_->set_delegate(delegate.Pass());
2291   }
2292 
2293  private:
~LocalWriteClosure()2294   virtual ~LocalWriteClosure() {}
2295   friend class base::RefCounted<LocalWriteClosure>;
2296 
callBlobCallbackOnIDBTaskRunner(bool succeeded)2297   void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
2298     DCHECK(task_runner_->RunsTasksOnCurrentThread());
2299     chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
2300   }
2301 
2302   IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
2303   base::TaskRunner* task_runner_;
2304   int64 bytes_written_;
2305 
2306   DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2307 };
2308 
WriteBlobFile(int64 database_id,const Transaction::WriteDescriptor & descriptor,Transaction::ChainedBlobWriter * chained_blob_writer)2309 bool IndexedDBBackingStore::WriteBlobFile(
2310     int64 database_id,
2311     const Transaction::WriteDescriptor& descriptor,
2312     Transaction::ChainedBlobWriter* chained_blob_writer) {
2313 
2314   if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2315     return false;
2316 
2317   FilePath path = GetBlobFileName(database_id, descriptor.key());
2318 
2319   if (descriptor.is_file()) {
2320     DCHECK(!descriptor.file_path().empty());
2321     if (!base::CopyFile(descriptor.file_path(), path))
2322       return false;
2323 
2324     base::File::Info info;
2325     if (base::GetFileInfo(descriptor.file_path(), &info)) {
2326       if (descriptor.size() != -1) {
2327         if (descriptor.size() != info.size)
2328           return false;
2329         // The round-trip can be lossy; round to nearest millisecond.
2330         int64 delta = (descriptor.last_modified() -
2331             info.last_modified).InMilliseconds();
2332         if (std::abs(delta) > 1)
2333           return false;
2334       }
2335       if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2336         // TODO(ericu): Complain quietly; timestamp's probably not vital.
2337       }
2338     } else {
2339       // TODO(ericu): Complain quietly; timestamp's probably not vital.
2340     }
2341 
2342     task_runner_->PostTask(
2343         FROM_HERE,
2344         base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2345                    chained_blob_writer,
2346                    true,
2347                    info.size));
2348   } else {
2349     DCHECK(descriptor.url().is_valid());
2350     scoped_refptr<LocalWriteClosure> write_closure(
2351         new LocalWriteClosure(chained_blob_writer, task_runner_));
2352     content::BrowserThread::PostTask(
2353         content::BrowserThread::IO,
2354         FROM_HERE,
2355         base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2356                    write_closure.get(),
2357                    path,
2358                    descriptor.url(),
2359                    request_context_));
2360   }
2361   return true;
2362 }
2363 
ReportBlobUnused(int64 database_id,int64 blob_key)2364 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2365                                              int64 blob_key) {
2366   DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2367   bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2368   DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2369   scoped_refptr<LevelDBTransaction> transaction =
2370       IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2371 
2372   std::string live_blob_key = LiveBlobJournalKey::Encode();
2373   BlobJournalType live_blob_journal;
2374   if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2375            .ok())
2376     return;
2377   DCHECK(live_blob_journal.size());
2378 
2379   std::string primary_key = BlobJournalKey::Encode();
2380   BlobJournalType primary_journal;
2381   if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2382     return;
2383 
2384   // There are several cases to handle.  If blob_key is kAllBlobsKey, we want to
2385   // remove all entries with database_id from the live_blob journal and add only
2386   // kAllBlobsKey to the primary journal.  Otherwise if IsValidBlobKey(blob_key)
2387   // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2388   // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2389   // to the primary.  Otherwise if IsValidBlobKey(blob_key) and we find a
2390   // matching (database_id, blob_key) tuple, we should move it to the primary
2391   // journal.
2392   BlobJournalType new_live_blob_journal;
2393   for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2394        journal_iter != live_blob_journal.end();
2395        ++journal_iter) {
2396     int64 current_database_id = journal_iter->first;
2397     int64 current_blob_key = journal_iter->second;
2398     bool current_all_blobs =
2399         current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2400     DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2401            current_all_blobs);
2402     if (current_database_id == database_id &&
2403         (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2404       if (!all_blobs) {
2405         primary_journal.push_back(
2406             std::make_pair(database_id, current_blob_key));
2407         if (current_all_blobs)
2408           new_live_blob_journal.push_back(*journal_iter);
2409         new_live_blob_journal.insert(new_live_blob_journal.end(),
2410                                      ++journal_iter,
2411                                      live_blob_journal.end());  // All the rest.
2412         break;
2413       }
2414     } else {
2415       new_live_blob_journal.push_back(*journal_iter);
2416     }
2417   }
2418   if (all_blobs) {
2419     primary_journal.push_back(
2420         std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2421   }
2422   UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2423   UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2424   transaction->Commit();
2425   // We could just do the deletions/cleaning here, but if there are a lot of
2426   // blobs about to be garbage collected, it'd be better to wait and do them all
2427   // at once.
2428   StartJournalCleaningTimer();
2429 }
2430 
2431 // The this reference is a raw pointer that's declared Unretained inside the
2432 // timer code, so this won't confuse IndexedDBFactory's check for
2433 // HasLastBackingStoreReference.  It's safe because if the backing store is
2434 // deleted, the timer will automatically be canceled on destruction.
StartJournalCleaningTimer()2435 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2436   journal_cleaning_timer_.Start(
2437       FROM_HERE,
2438       base::TimeDelta::FromSeconds(5),
2439       this,
2440       &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2441 }
2442 
2443 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
GetBlobFileName(int64 database_id,int64 key)2444 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2445   return GetBlobFileNameForKey(blob_path_, database_id, key);
2446 }
2447 
CheckIndexAndMetaDataKey(const LevelDBIterator * it,const std::string & stop_key,int64 index_id,unsigned char meta_data_type)2448 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2449                                      const std::string& stop_key,
2450                                      int64 index_id,
2451                                      unsigned char meta_data_type) {
2452   if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2453     return false;
2454 
2455   StringPiece slice(it->Key());
2456   IndexMetaDataKey meta_data_key;
2457   bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2458   DCHECK(ok);
2459   if (meta_data_key.IndexId() != index_id)
2460     return false;
2461   if (meta_data_key.meta_data_type() != meta_data_type)
2462     return false;
2463   return true;
2464 }
2465 
2466 // TODO(jsbell): This should do some error handling rather than plowing ahead
2467 // when bad data is encountered.
GetIndexes(int64 database_id,int64 object_store_id,IndexedDBObjectStoreMetadata::IndexMap * indexes)2468 leveldb::Status IndexedDBBackingStore::GetIndexes(
2469     int64 database_id,
2470     int64 object_store_id,
2471     IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2472   IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2473   if (!KeyPrefix::ValidIds(database_id, object_store_id))
2474     return InvalidDBKeyStatus();
2475   const std::string start_key =
2476       IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2477   const std::string stop_key =
2478       IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2479 
2480   DCHECK(indexes->empty());
2481 
2482   scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2483   leveldb::Status s = it->Seek(start_key);
2484   while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2485     StringPiece slice(it->Key());
2486     IndexMetaDataKey meta_data_key;
2487     bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2488     DCHECK(ok);
2489     if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2490       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2491       // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2492       // the load.
2493       s = it->Next();
2494       if (!s.ok())
2495         break;
2496       continue;
2497     }
2498 
2499     // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2500     // simplify.
2501     int64 index_id = meta_data_key.IndexId();
2502     base::string16 index_name;
2503     {
2504       StringPiece slice(it->Value());
2505       if (!DecodeString(&slice, &index_name) || !slice.empty())
2506         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2507     }
2508 
2509     s = it->Next();  // unique flag
2510     if (!s.ok())
2511       break;
2512     if (!CheckIndexAndMetaDataKey(
2513              it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2514       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2515       break;
2516     }
2517     bool index_unique;
2518     {
2519       StringPiece slice(it->Value());
2520       if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2521         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2522     }
2523 
2524     s = it->Next();  // key_path
2525     if (!s.ok())
2526       break;
2527     if (!CheckIndexAndMetaDataKey(
2528              it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2529       INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2530       break;
2531     }
2532     IndexedDBKeyPath key_path;
2533     {
2534       StringPiece slice(it->Value());
2535       if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2536         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2537     }
2538 
2539     s = it->Next();  // [optional] multi_entry flag
2540     if (!s.ok())
2541       break;
2542     bool index_multi_entry = false;
2543     if (CheckIndexAndMetaDataKey(
2544             it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2545       StringPiece slice(it->Value());
2546       if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2547         INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2548 
2549       s = it->Next();
2550       if (!s.ok())
2551         break;
2552     }
2553 
2554     (*indexes)[index_id] = IndexedDBIndexMetadata(
2555         index_name, index_id, key_path, index_unique, index_multi_entry);
2556   }
2557 
2558   if (!s.ok())
2559     INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2560 
2561   return s;
2562 }
2563 
RemoveBlobFile(int64 database_id,int64 key)2564 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2565   FilePath fileName = GetBlobFileName(database_id, key);
2566   return base::DeleteFile(fileName, false);
2567 }
2568 
RemoveBlobDirectory(int64 database_id)2569 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2570   FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2571   return base::DeleteFile(dirName, true);
2572 }
2573 
CleanUpBlobJournal(const std::string & level_db_key)2574 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2575     const std::string& level_db_key) {
2576   scoped_refptr<LevelDBTransaction> journal_transaction =
2577       IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2578   BlobJournalType journal;
2579   leveldb::Status s =
2580       GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2581   if (!s.ok())
2582     return s;
2583   if (!journal.size())
2584     return leveldb::Status::OK();
2585   BlobJournalType::iterator journal_iter;
2586   for (journal_iter = journal.begin(); journal_iter != journal.end();
2587        ++journal_iter) {
2588     int64 database_id = journal_iter->first;
2589     int64 blob_key = journal_iter->second;
2590     DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2591     if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2592       if (!RemoveBlobDirectory(database_id))
2593         return IOErrorStatus();
2594     } else {
2595       DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2596       if (!RemoveBlobFile(database_id, blob_key))
2597         return IOErrorStatus();
2598     }
2599   }
2600   ClearBlobJournal(journal_transaction.get(), level_db_key);
2601   return journal_transaction->Commit();
2602 }
2603 
GetBlobInfoForRecord(int64 database_id,const std::string & object_store_data_key,IndexedDBValue * value)2604 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2605     int64 database_id,
2606     const std::string& object_store_data_key,
2607     IndexedDBValue* value) {
2608   BlobChangeRecord* change_record = NULL;
2609   BlobChangeMap::const_iterator blob_iter =
2610       blob_change_map_.find(object_store_data_key);
2611   if (blob_iter != blob_change_map_.end()) {
2612     change_record = blob_iter->second;
2613   } else {
2614     blob_iter = incognito_blob_map_.find(object_store_data_key);
2615     if (blob_iter != incognito_blob_map_.end())
2616       change_record = blob_iter->second;
2617   }
2618   if (change_record) {
2619     // Either we haven't written the blob to disk yet or we're in incognito
2620     // mode, so we have to send back the one they sent us.  This change record
2621     // includes the original UUID.
2622     value->blob_info = change_record->blob_info();
2623     return leveldb::Status::OK();
2624   }
2625 
2626   BlobEntryKey blob_entry_key;
2627   StringPiece leveldb_key_piece(object_store_data_key);
2628   if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2629                                             &blob_entry_key)) {
2630     NOTREACHED();
2631     return InternalInconsistencyStatus();
2632   }
2633   scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
2634   std::string encoded_key = blob_entry_key.Encode();
2635   leveldb::Status s = it->Seek(encoded_key);
2636   if (!s.ok())
2637     return s;
2638   if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
2639     if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
2640       INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2641       return InternalInconsistencyStatus();
2642     }
2643     std::vector<IndexedDBBlobInfo>::iterator iter;
2644     for (iter = value->blob_info.begin(); iter != value->blob_info.end();
2645          ++iter) {
2646       iter->set_file_path(
2647           backing_store_->GetBlobFileName(database_id, iter->key()));
2648       iter->set_mark_used_callback(
2649           backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2650               database_id, iter->key()));
2651       iter->set_release_callback(
2652           backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2653               database_id, iter->key()));
2654       if (iter->is_file()) {
2655         base::File::Info info;
2656         if (base::GetFileInfo(iter->file_path(), &info)) {
2657           // This should always work, but it isn't fatal if it doesn't; it just
2658           // means a potential slow synchronous call from the renderer later.
2659           iter->set_last_modified(info.last_modified);
2660           iter->set_size(info.size);
2661         }
2662       }
2663     }
2664   }
2665   return leveldb::Status::OK();
2666 }
2667 
CleanPrimaryJournalIgnoreReturn()2668 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2669   CleanUpBlobJournal(BlobJournalKey::Encode());
2670 }
2671 
SetMaxIndexId(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 index_id)2672 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2673     LevelDBTransaction* transaction,
2674     int64 database_id,
2675     int64 object_store_id,
2676     int64 index_id) {
2677   int64 max_index_id = -1;
2678   const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2679       database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2680   bool found = false;
2681   leveldb::Status s =
2682       GetInt(transaction, max_index_id_key, &max_index_id, &found);
2683   if (!s.ok()) {
2684     INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2685     return s;
2686   }
2687   if (!found)
2688     max_index_id = kMinimumIndexId;
2689 
2690   if (index_id <= max_index_id) {
2691     INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2692     return InternalInconsistencyStatus();
2693   }
2694 
2695   PutInt(transaction, max_index_id_key, index_id);
2696   return s;
2697 }
2698 
CreateIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool is_unique,bool is_multi_entry)2699 leveldb::Status IndexedDBBackingStore::CreateIndex(
2700     IndexedDBBackingStore::Transaction* transaction,
2701     int64 database_id,
2702     int64 object_store_id,
2703     int64 index_id,
2704     const base::string16& name,
2705     const IndexedDBKeyPath& key_path,
2706     bool is_unique,
2707     bool is_multi_entry) {
2708   IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2709   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2710     return InvalidDBKeyStatus();
2711   LevelDBTransaction* leveldb_transaction = transaction->transaction();
2712   leveldb::Status s = SetMaxIndexId(
2713       leveldb_transaction, database_id, object_store_id, index_id);
2714 
2715   if (!s.ok())
2716     return s;
2717 
2718   const std::string name_key = IndexMetaDataKey::Encode(
2719       database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2720   const std::string unique_key = IndexMetaDataKey::Encode(
2721       database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2722   const std::string key_path_key = IndexMetaDataKey::Encode(
2723       database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2724   const std::string multi_entry_key = IndexMetaDataKey::Encode(
2725       database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2726 
2727   PutString(leveldb_transaction, name_key, name);
2728   PutBool(leveldb_transaction, unique_key, is_unique);
2729   PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2730   PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2731   return s;
2732 }
2733 
DeleteIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id)2734 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2735     IndexedDBBackingStore::Transaction* transaction,
2736     int64 database_id,
2737     int64 object_store_id,
2738     int64 index_id) {
2739   IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2740   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2741     return InvalidDBKeyStatus();
2742   LevelDBTransaction* leveldb_transaction = transaction->transaction();
2743 
2744   const std::string index_meta_data_start =
2745       IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2746   const std::string index_meta_data_end =
2747       IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2748   leveldb::Status s = DeleteRangeBasic(
2749       leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2750 
2751   if (s.ok()) {
2752     const std::string index_data_start =
2753         IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2754     const std::string index_data_end =
2755         IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2756     s = DeleteRangeBasic(
2757         leveldb_transaction, index_data_start, index_data_end, true);
2758   }
2759 
2760   if (!s.ok())
2761     INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2762 
2763   return s;
2764 }
2765 
PutIndexDataForRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,const RecordIdentifier & record_identifier)2766 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2767     IndexedDBBackingStore::Transaction* transaction,
2768     int64 database_id,
2769     int64 object_store_id,
2770     int64 index_id,
2771     const IndexedDBKey& key,
2772     const RecordIdentifier& record_identifier) {
2773   IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2774   DCHECK(key.IsValid());
2775   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2776     return InvalidDBKeyStatus();
2777 
2778   std::string encoded_key;
2779   EncodeIDBKey(key, &encoded_key);
2780 
2781   const std::string index_data_key =
2782       IndexDataKey::Encode(database_id,
2783                            object_store_id,
2784                            index_id,
2785                            encoded_key,
2786                            record_identifier.primary_key(),
2787                            0);
2788 
2789   std::string data;
2790   EncodeVarInt(record_identifier.version(), &data);
2791   data.append(record_identifier.primary_key());
2792 
2793   transaction->transaction()->Put(index_data_key, &data);
2794   return leveldb::Status::OK();
2795 }
2796 
FindGreatestKeyLessThanOrEqual(LevelDBTransaction * transaction,const std::string & target,std::string * found_key,leveldb::Status & s)2797 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2798                                            const std::string& target,
2799                                            std::string* found_key,
2800                                            leveldb::Status& s) {
2801   scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2802   s = it->Seek(target);
2803   if (!s.ok())
2804     return false;
2805 
2806   if (!it->IsValid()) {
2807     s = it->SeekToLast();
2808     if (!s.ok() || !it->IsValid())
2809       return false;
2810   }
2811 
2812   while (CompareIndexKeys(it->Key(), target) > 0) {
2813     s = it->Prev();
2814     if (!s.ok() || !it->IsValid())
2815       return false;
2816   }
2817 
2818   do {
2819     *found_key = it->Key().as_string();
2820 
2821     // There can be several index keys that compare equal. We want the last one.
2822     s = it->Next();
2823   } while (s.ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2824 
2825   return true;
2826 }
2827 
VersionExists(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 version,const std::string & encoded_primary_key,bool * exists)2828 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2829                                      int64 database_id,
2830                                      int64 object_store_id,
2831                                      int64 version,
2832                                      const std::string& encoded_primary_key,
2833                                      bool* exists) {
2834   const std::string key =
2835       ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2836   std::string data;
2837 
2838   leveldb::Status s = transaction->Get(key, &data, exists);
2839   if (!s.ok()) {
2840     INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2841     return s;
2842   }
2843   if (!*exists)
2844     return s;
2845 
2846   StringPiece slice(data);
2847   int64 decoded;
2848   if (!DecodeInt(&slice, &decoded) || !slice.empty())
2849     return InternalInconsistencyStatus();
2850   *exists = (decoded == version);
2851   return s;
2852 }
2853 
FindKeyInIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,std::string * found_encoded_primary_key,bool * found)2854 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2855     IndexedDBBackingStore::Transaction* transaction,
2856     int64 database_id,
2857     int64 object_store_id,
2858     int64 index_id,
2859     const IndexedDBKey& key,
2860     std::string* found_encoded_primary_key,
2861     bool* found) {
2862   IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2863   DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2864 
2865   DCHECK(found_encoded_primary_key->empty());
2866   *found = false;
2867 
2868   LevelDBTransaction* leveldb_transaction = transaction->transaction();
2869   const std::string leveldb_key =
2870       IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2871   scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2872   leveldb::Status s = it->Seek(leveldb_key);
2873   if (!s.ok()) {
2874     INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2875     return s;
2876   }
2877 
2878   for (;;) {
2879     if (!it->IsValid())
2880       return leveldb::Status::OK();
2881     if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2882       return leveldb::Status::OK();
2883 
2884     StringPiece slice(it->Value());
2885 
2886     int64 version;
2887     if (!DecodeVarInt(&slice, &version)) {
2888       INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2889       return InternalInconsistencyStatus();
2890     }
2891     *found_encoded_primary_key = slice.as_string();
2892 
2893     bool exists = false;
2894     s = VersionExists(leveldb_transaction,
2895                       database_id,
2896                       object_store_id,
2897                       version,
2898                       *found_encoded_primary_key,
2899                       &exists);
2900     if (!s.ok())
2901       return s;
2902     if (!exists) {
2903       // Delete stale index data entry and continue.
2904       leveldb_transaction->Remove(it->Key());
2905       s = it->Next();
2906       continue;
2907     }
2908     *found = true;
2909     return s;
2910   }
2911 }
2912 
GetPrimaryKeyViaIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,scoped_ptr<IndexedDBKey> * primary_key)2913 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2914     IndexedDBBackingStore::Transaction* transaction,
2915     int64 database_id,
2916     int64 object_store_id,
2917     int64 index_id,
2918     const IndexedDBKey& key,
2919     scoped_ptr<IndexedDBKey>* primary_key) {
2920   IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2921   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2922     return InvalidDBKeyStatus();
2923 
2924   bool found = false;
2925   std::string found_encoded_primary_key;
2926   leveldb::Status s = FindKeyInIndex(transaction,
2927                                      database_id,
2928                                      object_store_id,
2929                                      index_id,
2930                                      key,
2931                                      &found_encoded_primary_key,
2932                                      &found);
2933   if (!s.ok()) {
2934     INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2935     return s;
2936   }
2937   if (!found)
2938     return s;
2939   if (!found_encoded_primary_key.size()) {
2940     INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2941     return InvalidDBKeyStatus();
2942   }
2943 
2944   StringPiece slice(found_encoded_primary_key);
2945   if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2946     return s;
2947   else
2948     return InvalidDBKeyStatus();
2949 }
2950 
KeyExistsInIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & index_key,scoped_ptr<IndexedDBKey> * found_primary_key,bool * exists)2951 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
2952     IndexedDBBackingStore::Transaction* transaction,
2953     int64 database_id,
2954     int64 object_store_id,
2955     int64 index_id,
2956     const IndexedDBKey& index_key,
2957     scoped_ptr<IndexedDBKey>* found_primary_key,
2958     bool* exists) {
2959   IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2960   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2961     return InvalidDBKeyStatus();
2962 
2963   *exists = false;
2964   std::string found_encoded_primary_key;
2965   leveldb::Status s = FindKeyInIndex(transaction,
2966                                      database_id,
2967                                      object_store_id,
2968                                      index_id,
2969                                      index_key,
2970                                      &found_encoded_primary_key,
2971                                      exists);
2972   if (!s.ok()) {
2973     INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2974     return s;
2975   }
2976   if (!*exists)
2977     return leveldb::Status::OK();
2978   if (found_encoded_primary_key.empty()) {
2979     INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2980     return InvalidDBKeyStatus();
2981   }
2982 
2983   StringPiece slice(found_encoded_primary_key);
2984   if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
2985     return s;
2986   else
2987     return InvalidDBKeyStatus();
2988 }
2989 
Cursor(const IndexedDBBackingStore::Cursor * other)2990 IndexedDBBackingStore::Cursor::Cursor(
2991     const IndexedDBBackingStore::Cursor* other)
2992     : backing_store_(other->backing_store_),
2993       transaction_(other->transaction_),
2994       database_id_(other->database_id_),
2995       cursor_options_(other->cursor_options_),
2996       current_key_(new IndexedDBKey(*other->current_key_)) {
2997   if (other->iterator_) {
2998     iterator_ = transaction_->transaction()->CreateIterator();
2999 
3000     if (other->iterator_->IsValid()) {
3001       leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3002       // TODO(cmumford): Handle this error (crbug.com/363397)
3003       DCHECK(iterator_->IsValid());
3004     }
3005   }
3006 }
3007 
Cursor(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const CursorOptions & cursor_options)3008 IndexedDBBackingStore::Cursor::Cursor(
3009     scoped_refptr<IndexedDBBackingStore> backing_store,
3010     IndexedDBBackingStore::Transaction* transaction,
3011     int64 database_id,
3012     const CursorOptions& cursor_options)
3013     : backing_store_(backing_store),
3014       transaction_(transaction),
3015       database_id_(database_id),
3016       cursor_options_(cursor_options) {
3017 }
~Cursor()3018 IndexedDBBackingStore::Cursor::~Cursor() {}
3019 
FirstSeek(leveldb::Status * s)3020 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3021   iterator_ = transaction_->transaction()->CreateIterator();
3022   if (cursor_options_.forward)
3023     *s = iterator_->Seek(cursor_options_.low_key);
3024   else
3025     *s = iterator_->Seek(cursor_options_.high_key);
3026   if (!s->ok())
3027     return false;
3028 
3029   return Continue(0, READY, s);
3030 }
3031 
Advance(uint32 count,leveldb::Status * s)3032 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3033   *s = leveldb::Status::OK();
3034   while (count--) {
3035     if (!Continue(s))
3036       return false;
3037   }
3038   return true;
3039 }
3040 
Continue(const IndexedDBKey * key,const IndexedDBKey * primary_key,IteratorState next_state,leveldb::Status * s)3041 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3042                                              const IndexedDBKey* primary_key,
3043                                              IteratorState next_state,
3044                                              leveldb::Status* s) {
3045   DCHECK(!key || key->IsValid());
3046   DCHECK(!primary_key || primary_key->IsValid());
3047   *s = leveldb::Status::OK();
3048 
3049   // TODO(alecflett): avoid a copy here?
3050   IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3051 
3052   bool first_iteration = true;
3053 
3054   // When iterating with PrevNoDuplicate, spec requires that the
3055   // value we yield for each key is the first duplicate in forwards
3056   // order.
3057   IndexedDBKey last_duplicate_key;
3058 
3059   bool forward = cursor_options_.forward;
3060 
3061   for (;;) {
3062     if (next_state == SEEK) {
3063       // TODO(jsbell): Optimize seeking for reverse cursors as well.
3064       if (first_iteration && key && forward) {
3065         std::string leveldb_key;
3066         if (primary_key) {
3067           leveldb_key = EncodeKey(*key, *primary_key);
3068         } else {
3069           leveldb_key = EncodeKey(*key);
3070         }
3071         *s = iterator_->Seek(leveldb_key);
3072         first_iteration = false;
3073       } else if (forward) {
3074         *s = iterator_->Next();
3075       } else {
3076         *s = iterator_->Prev();
3077       }
3078       if (!s->ok())
3079         return false;
3080     } else {
3081       next_state = SEEK;  // for subsequent iterations
3082     }
3083 
3084     if (!iterator_->IsValid()) {
3085       if (!forward && last_duplicate_key.IsValid()) {
3086         // We need to walk forward because we hit the end of
3087         // the data.
3088         forward = true;
3089         continue;
3090       }
3091 
3092       return false;
3093     }
3094 
3095     if (IsPastBounds()) {
3096       if (!forward && last_duplicate_key.IsValid()) {
3097         // We need to walk forward because now we're beyond the
3098         // bounds defined by the cursor.
3099         forward = true;
3100         continue;
3101       }
3102 
3103       return false;
3104     }
3105 
3106     if (!HaveEnteredRange())
3107       continue;
3108 
3109     // The row may not load because there's a stale entry in the
3110     // index. This is not fatal.
3111     if (!LoadCurrentRow())
3112       continue;
3113 
3114     if (key) {
3115       if (forward) {
3116         if (primary_key && current_key_->Equals(*key) &&
3117             this->primary_key().IsLessThan(*primary_key))
3118           continue;
3119         if (current_key_->IsLessThan(*key))
3120           continue;
3121       } else {
3122         if (primary_key && key->Equals(*current_key_) &&
3123             primary_key->IsLessThan(this->primary_key()))
3124           continue;
3125         if (key->IsLessThan(*current_key_))
3126           continue;
3127       }
3128     }
3129 
3130     if (cursor_options_.unique) {
3131       if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3132         // We should never be able to walk forward all the way
3133         // to the previous key.
3134         DCHECK(!last_duplicate_key.IsValid());
3135         continue;
3136       }
3137 
3138       if (!forward) {
3139         if (!last_duplicate_key.IsValid()) {
3140           last_duplicate_key = *current_key_;
3141           continue;
3142         }
3143 
3144         // We need to walk forward because we hit the boundary
3145         // between key ranges.
3146         if (!last_duplicate_key.Equals(*current_key_)) {
3147           forward = true;
3148           continue;
3149         }
3150 
3151         continue;
3152       }
3153     }
3154     break;
3155   }
3156 
3157   DCHECK(!last_duplicate_key.IsValid() ||
3158          (forward && last_duplicate_key.Equals(*current_key_)));
3159   return true;
3160 }
3161 
HaveEnteredRange() const3162 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3163   if (cursor_options_.forward) {
3164     int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3165     if (cursor_options_.low_open) {
3166       return compare > 0;
3167     }
3168     return compare >= 0;
3169   }
3170   int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3171   if (cursor_options_.high_open) {
3172     return compare < 0;
3173   }
3174   return compare <= 0;
3175 }
3176 
IsPastBounds() const3177 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3178   if (cursor_options_.forward) {
3179     int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3180     if (cursor_options_.high_open) {
3181       return compare >= 0;
3182     }
3183     return compare > 0;
3184   }
3185   int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3186   if (cursor_options_.low_open) {
3187     return compare <= 0;
3188   }
3189   return compare < 0;
3190 }
3191 
primary_key() const3192 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3193   return *current_key_;
3194 }
3195 
3196 const IndexedDBBackingStore::RecordIdentifier&
record_identifier() const3197 IndexedDBBackingStore::Cursor::record_identifier() const {
3198   return record_identifier_;
3199 }
3200 
3201 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3202  public:
ObjectStoreKeyCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3203   ObjectStoreKeyCursorImpl(
3204       scoped_refptr<IndexedDBBackingStore> backing_store,
3205       IndexedDBBackingStore::Transaction* transaction,
3206       int64 database_id,
3207       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3208       : IndexedDBBackingStore::Cursor(backing_store,
3209                                       transaction,
3210                                       database_id,
3211                                       cursor_options) {}
3212 
Clone()3213   virtual Cursor* Clone() OVERRIDE {
3214     return new ObjectStoreKeyCursorImpl(this);
3215   }
3216 
3217   // IndexedDBBackingStore::Cursor
value()3218   virtual IndexedDBValue* value() OVERRIDE {
3219     NOTREACHED();
3220     return NULL;
3221   }
3222   virtual bool LoadCurrentRow() OVERRIDE;
3223 
3224  protected:
EncodeKey(const IndexedDBKey & key)3225   virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3226     return ObjectStoreDataKey::Encode(
3227         cursor_options_.database_id, cursor_options_.object_store_id, key);
3228   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3229   virtual std::string EncodeKey(const IndexedDBKey& key,
3230                                 const IndexedDBKey& primary_key) OVERRIDE {
3231     NOTREACHED();
3232     return std::string();
3233   }
3234 
3235  private:
ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl * other)3236   explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3237       : IndexedDBBackingStore::Cursor(other) {}
3238 
3239   DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3240 };
3241 
LoadCurrentRow()3242 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3243   StringPiece slice(iterator_->Key());
3244   ObjectStoreDataKey object_store_data_key;
3245   if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3246     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3247     return false;
3248   }
3249 
3250   current_key_ = object_store_data_key.user_key();
3251 
3252   int64 version;
3253   slice = StringPiece(iterator_->Value());
3254   if (!DecodeVarInt(&slice, &version)) {
3255     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3256     return false;
3257   }
3258 
3259   // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3260   std::string encoded_key;
3261   EncodeIDBKey(*current_key_, &encoded_key);
3262   record_identifier_.Reset(encoded_key, version);
3263 
3264   return true;
3265 }
3266 
3267 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3268  public:
ObjectStoreCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3269   ObjectStoreCursorImpl(
3270       scoped_refptr<IndexedDBBackingStore> backing_store,
3271       IndexedDBBackingStore::Transaction* transaction,
3272       int64 database_id,
3273       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3274       : IndexedDBBackingStore::Cursor(backing_store,
3275                                       transaction,
3276                                       database_id,
3277                                       cursor_options) {}
3278 
Clone()3279   virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
3280 
3281   // IndexedDBBackingStore::Cursor
value()3282   virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
3283   virtual bool LoadCurrentRow() OVERRIDE;
3284 
3285  protected:
EncodeKey(const IndexedDBKey & key)3286   virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3287     return ObjectStoreDataKey::Encode(
3288         cursor_options_.database_id, cursor_options_.object_store_id, key);
3289   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3290   virtual std::string EncodeKey(const IndexedDBKey& key,
3291                                 const IndexedDBKey& primary_key) OVERRIDE {
3292     NOTREACHED();
3293     return std::string();
3294   }
3295 
3296  private:
ObjectStoreCursorImpl(const ObjectStoreCursorImpl * other)3297   explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3298       : IndexedDBBackingStore::Cursor(other),
3299         current_value_(other->current_value_) {}
3300 
3301   IndexedDBValue current_value_;
3302 
3303   DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3304 };
3305 
LoadCurrentRow()3306 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3307   StringPiece key_slice(iterator_->Key());
3308   ObjectStoreDataKey object_store_data_key;
3309   if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3310     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3311     return false;
3312   }
3313 
3314   current_key_ = object_store_data_key.user_key();
3315 
3316   int64 version;
3317   StringPiece value_slice = StringPiece(iterator_->Value());
3318   if (!DecodeVarInt(&value_slice, &version)) {
3319     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3320     return false;
3321   }
3322 
3323   // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3324   std::string encoded_key;
3325   EncodeIDBKey(*current_key_, &encoded_key);
3326   record_identifier_.Reset(encoded_key, version);
3327 
3328   if (!transaction_->GetBlobInfoForRecord(database_id_,
3329                                           iterator_->Key().as_string(),
3330                                           &current_value_).ok()) {
3331     return false;
3332   }
3333   current_value_.bits = value_slice.as_string();
3334   return true;
3335 }
3336 
3337 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3338  public:
IndexKeyCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3339   IndexKeyCursorImpl(
3340       scoped_refptr<IndexedDBBackingStore> backing_store,
3341       IndexedDBBackingStore::Transaction* transaction,
3342       int64 database_id,
3343       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3344       : IndexedDBBackingStore::Cursor(backing_store,
3345                                       transaction,
3346                                       database_id,
3347                                       cursor_options) {}
3348 
Clone()3349   virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
3350 
3351   // IndexedDBBackingStore::Cursor
value()3352   virtual IndexedDBValue* value() OVERRIDE {
3353     NOTREACHED();
3354     return NULL;
3355   }
primary_key() const3356   virtual const IndexedDBKey& primary_key() const OVERRIDE {
3357     return *primary_key_;
3358   }
record_identifier() const3359   virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3360       const OVERRIDE {
3361     NOTREACHED();
3362     return record_identifier_;
3363   }
3364   virtual bool LoadCurrentRow() OVERRIDE;
3365 
3366  protected:
EncodeKey(const IndexedDBKey & key)3367   virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3368     return IndexDataKey::Encode(cursor_options_.database_id,
3369                                 cursor_options_.object_store_id,
3370                                 cursor_options_.index_id,
3371                                 key);
3372   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3373   virtual std::string EncodeKey(const IndexedDBKey& key,
3374                                 const IndexedDBKey& primary_key) OVERRIDE {
3375     return IndexDataKey::Encode(cursor_options_.database_id,
3376                                 cursor_options_.object_store_id,
3377                                 cursor_options_.index_id,
3378                                 key,
3379                                 primary_key);
3380   }
3381 
3382  private:
IndexKeyCursorImpl(const IndexKeyCursorImpl * other)3383   explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3384       : IndexedDBBackingStore::Cursor(other),
3385         primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3386 
3387   scoped_ptr<IndexedDBKey> primary_key_;
3388 
3389   DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3390 };
3391 
LoadCurrentRow()3392 bool IndexKeyCursorImpl::LoadCurrentRow() {
3393   StringPiece slice(iterator_->Key());
3394   IndexDataKey index_data_key;
3395   if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3396     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3397     return false;
3398   }
3399 
3400   current_key_ = index_data_key.user_key();
3401   DCHECK(current_key_);
3402 
3403   slice = StringPiece(iterator_->Value());
3404   int64 index_data_version;
3405   if (!DecodeVarInt(&slice, &index_data_version)) {
3406     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3407     return false;
3408   }
3409 
3410   if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3411     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3412     return false;
3413   }
3414 
3415   std::string primary_leveldb_key =
3416       ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3417                                  index_data_key.ObjectStoreId(),
3418                                  *primary_key_);
3419 
3420   std::string result;
3421   bool found = false;
3422   leveldb::Status s =
3423       transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3424   if (!s.ok()) {
3425     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3426     return false;
3427   }
3428   if (!found) {
3429     transaction_->transaction()->Remove(iterator_->Key());
3430     return false;
3431   }
3432   if (!result.size()) {
3433     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3434     return false;
3435   }
3436 
3437   int64 object_store_data_version;
3438   slice = StringPiece(result);
3439   if (!DecodeVarInt(&slice, &object_store_data_version)) {
3440     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3441     return false;
3442   }
3443 
3444   if (object_store_data_version != index_data_version) {
3445     transaction_->transaction()->Remove(iterator_->Key());
3446     return false;
3447   }
3448 
3449   return true;
3450 }
3451 
3452 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3453  public:
IndexCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3454   IndexCursorImpl(
3455       scoped_refptr<IndexedDBBackingStore> backing_store,
3456       IndexedDBBackingStore::Transaction* transaction,
3457       int64 database_id,
3458       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3459       : IndexedDBBackingStore::Cursor(backing_store,
3460                                       transaction,
3461                                       database_id,
3462                                       cursor_options) {}
3463 
Clone()3464   virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
3465 
3466   // IndexedDBBackingStore::Cursor
value()3467   virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
primary_key() const3468   virtual const IndexedDBKey& primary_key() const OVERRIDE {
3469     return *primary_key_;
3470   }
record_identifier() const3471   virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3472       const OVERRIDE {
3473     NOTREACHED();
3474     return record_identifier_;
3475   }
3476   virtual bool LoadCurrentRow() OVERRIDE;
3477 
3478  protected:
EncodeKey(const IndexedDBKey & key)3479   virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3480     return IndexDataKey::Encode(cursor_options_.database_id,
3481                                 cursor_options_.object_store_id,
3482                                 cursor_options_.index_id,
3483                                 key);
3484   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3485   virtual std::string EncodeKey(const IndexedDBKey& key,
3486                                 const IndexedDBKey& primary_key) OVERRIDE {
3487     return IndexDataKey::Encode(cursor_options_.database_id,
3488                                 cursor_options_.object_store_id,
3489                                 cursor_options_.index_id,
3490                                 key,
3491                                 primary_key);
3492   }
3493 
3494  private:
IndexCursorImpl(const IndexCursorImpl * other)3495   explicit IndexCursorImpl(const IndexCursorImpl* other)
3496       : IndexedDBBackingStore::Cursor(other),
3497         primary_key_(new IndexedDBKey(*other->primary_key_)),
3498         current_value_(other->current_value_),
3499         primary_leveldb_key_(other->primary_leveldb_key_) {}
3500 
3501   scoped_ptr<IndexedDBKey> primary_key_;
3502   IndexedDBValue current_value_;
3503   std::string primary_leveldb_key_;
3504 
3505   DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3506 };
3507 
LoadCurrentRow()3508 bool IndexCursorImpl::LoadCurrentRow() {
3509   StringPiece slice(iterator_->Key());
3510   IndexDataKey index_data_key;
3511   if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3512     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3513     return false;
3514   }
3515 
3516   current_key_ = index_data_key.user_key();
3517   DCHECK(current_key_);
3518 
3519   slice = StringPiece(iterator_->Value());
3520   int64 index_data_version;
3521   if (!DecodeVarInt(&slice, &index_data_version)) {
3522     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3523     return false;
3524   }
3525   if (!DecodeIDBKey(&slice, &primary_key_)) {
3526     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3527     return false;
3528   }
3529 
3530   DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3531   primary_leveldb_key_ =
3532       ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3533                                  index_data_key.ObjectStoreId(),
3534                                  *primary_key_);
3535 
3536   std::string result;
3537   bool found = false;
3538   leveldb::Status s =
3539       transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3540   if (!s.ok()) {
3541     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3542     return false;
3543   }
3544   if (!found) {
3545     transaction_->transaction()->Remove(iterator_->Key());
3546     return false;
3547   }
3548   if (!result.size()) {
3549     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3550     return false;
3551   }
3552 
3553   int64 object_store_data_version;
3554   slice = StringPiece(result);
3555   if (!DecodeVarInt(&slice, &object_store_data_version)) {
3556     INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3557     return false;
3558   }
3559 
3560   if (object_store_data_version != index_data_version) {
3561     transaction_->transaction()->Remove(iterator_->Key());
3562     return false;
3563   }
3564 
3565   current_value_.bits = slice.as_string();
3566   return transaction_->GetBlobInfoForRecord(database_id_,
3567                                             primary_leveldb_key_,
3568                                             &current_value_).ok();
3569 }
3570 
ObjectStoreCursorOptions(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options)3571 bool ObjectStoreCursorOptions(
3572     LevelDBTransaction* transaction,
3573     int64 database_id,
3574     int64 object_store_id,
3575     const IndexedDBKeyRange& range,
3576     indexed_db::CursorDirection direction,
3577     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3578   cursor_options->database_id = database_id;
3579   cursor_options->object_store_id = object_store_id;
3580 
3581   bool lower_bound = range.lower().IsValid();
3582   bool upper_bound = range.upper().IsValid();
3583   cursor_options->forward =
3584       (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3585        direction == indexed_db::CURSOR_NEXT);
3586   cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3587                             direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3588 
3589   if (!lower_bound) {
3590     cursor_options->low_key =
3591         ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3592     cursor_options->low_open = true;  // Not included.
3593   } else {
3594     cursor_options->low_key =
3595         ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3596     cursor_options->low_open = range.lowerOpen();
3597   }
3598 
3599   leveldb::Status s;
3600 
3601   if (!upper_bound) {
3602     cursor_options->high_key =
3603         ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3604 
3605     if (cursor_options->forward) {
3606       cursor_options->high_open = true;  // Not included.
3607     } else {
3608       // We need a key that exists.
3609       // TODO(cmumford): Handle this error (crbug.com/363397)
3610       if (!FindGreatestKeyLessThanOrEqual(transaction,
3611                                           cursor_options->high_key,
3612                                           &cursor_options->high_key,
3613                                           s))
3614         return false;
3615       cursor_options->high_open = false;
3616     }
3617   } else {
3618     cursor_options->high_key =
3619         ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3620     cursor_options->high_open = range.upperOpen();
3621 
3622     if (!cursor_options->forward) {
3623       // For reverse cursors, we need a key that exists.
3624       std::string found_high_key;
3625       // TODO(cmumford): Handle this error (crbug.com/363397)
3626       if (!FindGreatestKeyLessThanOrEqual(
3627               transaction, cursor_options->high_key, &found_high_key, s))
3628         return false;
3629 
3630       // If the target key should not be included, but we end up with a smaller
3631       // key, we should include that.
3632       if (cursor_options->high_open &&
3633           CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3634         cursor_options->high_open = false;
3635 
3636       cursor_options->high_key = found_high_key;
3637     }
3638   }
3639 
3640   return true;
3641 }
3642 
IndexCursorOptions(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options)3643 bool IndexCursorOptions(
3644     LevelDBTransaction* transaction,
3645     int64 database_id,
3646     int64 object_store_id,
3647     int64 index_id,
3648     const IndexedDBKeyRange& range,
3649     indexed_db::CursorDirection direction,
3650     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3651   DCHECK(transaction);
3652   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3653     return false;
3654 
3655   cursor_options->database_id = database_id;
3656   cursor_options->object_store_id = object_store_id;
3657   cursor_options->index_id = index_id;
3658 
3659   bool lower_bound = range.lower().IsValid();
3660   bool upper_bound = range.upper().IsValid();
3661   cursor_options->forward =
3662       (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3663        direction == indexed_db::CURSOR_NEXT);
3664   cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3665                             direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3666 
3667   if (!lower_bound) {
3668     cursor_options->low_key =
3669         IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3670     cursor_options->low_open = false;  // Included.
3671   } else {
3672     cursor_options->low_key = IndexDataKey::Encode(
3673         database_id, object_store_id, index_id, range.lower());
3674     cursor_options->low_open = range.lowerOpen();
3675   }
3676 
3677   leveldb::Status s;
3678 
3679   if (!upper_bound) {
3680     cursor_options->high_key =
3681         IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3682     cursor_options->high_open = false;  // Included.
3683 
3684     if (!cursor_options->forward) {  // We need a key that exists.
3685       if (!FindGreatestKeyLessThanOrEqual(transaction,
3686                                           cursor_options->high_key,
3687                                           &cursor_options->high_key,
3688                                           s))
3689         return false;
3690       cursor_options->high_open = false;
3691     }
3692   } else {
3693     cursor_options->high_key = IndexDataKey::Encode(
3694         database_id, object_store_id, index_id, range.upper());
3695     cursor_options->high_open = range.upperOpen();
3696 
3697     std::string found_high_key;
3698     // Seek to the *last* key in the set of non-unique keys
3699     // TODO(cmumford): Handle this error (crbug.com/363397)
3700     if (!FindGreatestKeyLessThanOrEqual(
3701             transaction, cursor_options->high_key, &found_high_key, s))
3702       return false;
3703 
3704     // If the target key should not be included, but we end up with a smaller
3705     // key, we should include that.
3706     if (cursor_options->high_open &&
3707         CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3708       cursor_options->high_open = false;
3709 
3710     cursor_options->high_key = found_high_key;
3711   }
3712 
3713   return true;
3714 }
3715 
3716 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3717 IndexedDBBackingStore::OpenObjectStoreCursor(
3718     IndexedDBBackingStore::Transaction* transaction,
3719     int64 database_id,
3720     int64 object_store_id,
3721     const IndexedDBKeyRange& range,
3722     indexed_db::CursorDirection direction,
3723     leveldb::Status* s) {
3724   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3725   *s = leveldb::Status::OK();
3726   LevelDBTransaction* leveldb_transaction = transaction->transaction();
3727   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3728   if (!ObjectStoreCursorOptions(leveldb_transaction,
3729                                 database_id,
3730                                 object_store_id,
3731                                 range,
3732                                 direction,
3733                                 &cursor_options))
3734     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3735   scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3736       this, transaction, database_id, cursor_options));
3737   if (!cursor->FirstSeek(s))
3738     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3739 
3740   return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3741 }
3742 
3743 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3744 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3745     IndexedDBBackingStore::Transaction* transaction,
3746     int64 database_id,
3747     int64 object_store_id,
3748     const IndexedDBKeyRange& range,
3749     indexed_db::CursorDirection direction,
3750     leveldb::Status* s) {
3751   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3752   *s = leveldb::Status::OK();
3753   LevelDBTransaction* leveldb_transaction = transaction->transaction();
3754   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3755   if (!ObjectStoreCursorOptions(leveldb_transaction,
3756                                 database_id,
3757                                 object_store_id,
3758                                 range,
3759                                 direction,
3760                                 &cursor_options))
3761     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3762   scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3763       this, transaction, database_id, cursor_options));
3764   if (!cursor->FirstSeek(s))
3765     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3766 
3767   return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3768 }
3769 
3770 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenIndexKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3771 IndexedDBBackingStore::OpenIndexKeyCursor(
3772     IndexedDBBackingStore::Transaction* transaction,
3773     int64 database_id,
3774     int64 object_store_id,
3775     int64 index_id,
3776     const IndexedDBKeyRange& range,
3777     indexed_db::CursorDirection direction,
3778     leveldb::Status* s) {
3779   IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3780   *s = leveldb::Status::OK();
3781   LevelDBTransaction* leveldb_transaction = transaction->transaction();
3782   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3783   if (!IndexCursorOptions(leveldb_transaction,
3784                           database_id,
3785                           object_store_id,
3786                           index_id,
3787                           range,
3788                           direction,
3789                           &cursor_options))
3790     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3791   scoped_ptr<IndexKeyCursorImpl> cursor(
3792       new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3793   if (!cursor->FirstSeek(s))
3794     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3795 
3796   return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3797 }
3798 
3799 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenIndexCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3800 IndexedDBBackingStore::OpenIndexCursor(
3801     IndexedDBBackingStore::Transaction* transaction,
3802     int64 database_id,
3803     int64 object_store_id,
3804     int64 index_id,
3805     const IndexedDBKeyRange& range,
3806     indexed_db::CursorDirection direction,
3807     leveldb::Status* s) {
3808   IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3809   LevelDBTransaction* leveldb_transaction = transaction->transaction();
3810   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3811   if (!IndexCursorOptions(leveldb_transaction,
3812                           database_id,
3813                           object_store_id,
3814                           index_id,
3815                           range,
3816                           direction,
3817                           &cursor_options))
3818     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3819   scoped_ptr<IndexCursorImpl> cursor(
3820       new IndexCursorImpl(this, transaction, database_id, cursor_options));
3821   if (!cursor->FirstSeek(s))
3822     return scoped_ptr<IndexedDBBackingStore::Cursor>();
3823 
3824   return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3825 }
3826 
Transaction(IndexedDBBackingStore * backing_store)3827 IndexedDBBackingStore::Transaction::Transaction(
3828     IndexedDBBackingStore* backing_store)
3829     : backing_store_(backing_store), database_id_(-1) {
3830 }
3831 
~Transaction()3832 IndexedDBBackingStore::Transaction::~Transaction() {
3833   STLDeleteContainerPairSecondPointers(
3834       blob_change_map_.begin(), blob_change_map_.end());
3835   STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3836                                        incognito_blob_map_.end());
3837 }
3838 
Begin()3839 void IndexedDBBackingStore::Transaction::Begin() {
3840   IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3841   DCHECK(!transaction_.get());
3842   transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3843       backing_store_->db_.get());
3844 
3845   // If incognito, this snapshots blobs just as the above transaction_
3846   // constructor snapshots the leveldb.
3847   BlobChangeMap::const_iterator iter;
3848   for (iter = backing_store_->incognito_blob_map_.begin();
3849        iter != backing_store_->incognito_blob_map_.end();
3850        ++iter)
3851     incognito_blob_map_[iter->first] = iter->second->Clone().release();
3852 }
3853 
getURLFromUUID(const string & uuid)3854 static GURL getURLFromUUID(const string& uuid) {
3855   return GURL("blob:uuid/" + uuid);
3856 }
3857 
HandleBlobPreTransaction(BlobEntryKeyValuePairVec * new_blob_entries,WriteDescriptorVec * new_files_to_write)3858 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3859     BlobEntryKeyValuePairVec* new_blob_entries,
3860     WriteDescriptorVec* new_files_to_write) {
3861   if (backing_store_->is_incognito())
3862     return leveldb::Status::OK();
3863 
3864   BlobChangeMap::iterator iter = blob_change_map_.begin();
3865   new_blob_entries->clear();
3866   new_files_to_write->clear();
3867   if (iter != blob_change_map_.end()) {
3868     // Create LevelDBTransaction for the name generator seed and add-journal.
3869     scoped_refptr<LevelDBTransaction> pre_transaction =
3870         IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3871             backing_store_->db_.get());
3872     BlobJournalType journal;
3873     for (; iter != blob_change_map_.end(); ++iter) {
3874       std::vector<IndexedDBBlobInfo>::iterator info_iter;
3875       std::vector<IndexedDBBlobInfo*> new_blob_keys;
3876       for (info_iter = iter->second->mutable_blob_info().begin();
3877            info_iter != iter->second->mutable_blob_info().end();
3878            ++info_iter) {
3879         int64 next_blob_key = -1;
3880         bool result = GetBlobKeyGeneratorCurrentNumber(
3881             pre_transaction.get(), database_id_, &next_blob_key);
3882         if (!result || next_blob_key < 0)
3883           return InternalInconsistencyStatus();
3884         BlobJournalEntryType journal_entry =
3885             std::make_pair(database_id_, next_blob_key);
3886         journal.push_back(journal_entry);
3887         if (info_iter->is_file()) {
3888           new_files_to_write->push_back(
3889               WriteDescriptor(info_iter->file_path(),
3890                               next_blob_key,
3891                               info_iter->size(),
3892                               info_iter->last_modified()));
3893         } else {
3894           new_files_to_write->push_back(
3895               WriteDescriptor(getURLFromUUID(info_iter->uuid()),
3896                               next_blob_key,
3897                               info_iter->size()));
3898         }
3899         info_iter->set_key(next_blob_key);
3900         new_blob_keys.push_back(&*info_iter);
3901         result = UpdateBlobKeyGeneratorCurrentNumber(
3902             pre_transaction.get(), database_id_, next_blob_key + 1);
3903         if (!result)
3904           return InternalInconsistencyStatus();
3905       }
3906       BlobEntryKey blob_entry_key;
3907       StringPiece key_piece(iter->second->key());
3908       if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3909         NOTREACHED();
3910         return InternalInconsistencyStatus();
3911       }
3912       new_blob_entries->push_back(
3913           std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
3914     }
3915     UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
3916     leveldb::Status s = pre_transaction->Commit();
3917     if (!s.ok())
3918       return InternalInconsistencyStatus();
3919   }
3920   return leveldb::Status::OK();
3921 }
3922 
CollectBlobFilesToRemove()3923 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3924   if (backing_store_->is_incognito())
3925     return true;
3926 
3927   BlobChangeMap::const_iterator iter = blob_change_map_.begin();
3928   // Look up all old files to remove as part of the transaction, store their
3929   // names in blobs_to_remove_, and remove their old blob data entries.
3930   if (iter != blob_change_map_.end()) {
3931     scoped_ptr<LevelDBIterator> db_iter = transaction_->CreateIterator();
3932     for (; iter != blob_change_map_.end(); ++iter) {
3933       BlobEntryKey blob_entry_key;
3934       StringPiece key_piece(iter->second->key());
3935       if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3936         NOTREACHED();
3937         INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3938         transaction_ = NULL;
3939         return false;
3940       }
3941       if (database_id_ < 0)
3942         database_id_ = blob_entry_key.database_id();
3943       else
3944         DCHECK_EQ(database_id_, blob_entry_key.database_id());
3945       std::string blob_entry_key_bytes = blob_entry_key.Encode();
3946       db_iter->Seek(blob_entry_key_bytes);
3947       if (db_iter->IsValid() &&
3948           !CompareKeys(db_iter->Key(), blob_entry_key_bytes)) {
3949         std::vector<IndexedDBBlobInfo> blob_info;
3950         if (!DecodeBlobData(db_iter->Value().as_string(), &blob_info)) {
3951           INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3952           transaction_ = NULL;
3953           return false;
3954         }
3955         std::vector<IndexedDBBlobInfo>::iterator blob_info_iter;
3956         for (blob_info_iter = blob_info.begin();
3957              blob_info_iter != blob_info.end();
3958              ++blob_info_iter)
3959           blobs_to_remove_.push_back(
3960               std::make_pair(database_id_, blob_info_iter->key()));
3961         transaction_->Remove(blob_entry_key_bytes);
3962       }
3963     }
3964   }
3965   return true;
3966 }
3967 
SortBlobsToRemove()3968 leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
3969   IndexedDBActiveBlobRegistry* registry =
3970       backing_store_->active_blob_registry();
3971   BlobJournalType::iterator iter;
3972   BlobJournalType primary_journal, live_blob_journal;
3973   for (iter = blobs_to_remove_.begin(); iter != blobs_to_remove_.end();
3974        ++iter) {
3975     if (registry->MarkDeletedCheckIfUsed(iter->first, iter->second))
3976       live_blob_journal.push_back(*iter);
3977     else
3978       primary_journal.push_back(*iter);
3979   }
3980   UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
3981   leveldb::Status s =
3982       MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
3983   if (!s.ok())
3984     return s;
3985   // To signal how many blobs need attention right now.
3986   blobs_to_remove_.swap(primary_journal);
3987   return leveldb::Status::OK();
3988 }
3989 
CommitPhaseOne(scoped_refptr<BlobWriteCallback> callback)3990 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
3991     scoped_refptr<BlobWriteCallback> callback) {
3992   IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
3993   DCHECK(transaction_);
3994   DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
3995 
3996   leveldb::Status s;
3997 
3998   s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
3999   if (!s.ok()) {
4000     INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4001     transaction_ = NULL;
4002     return s;
4003   }
4004 
4005   BlobEntryKeyValuePairVec new_blob_entries;
4006   WriteDescriptorVec new_files_to_write;
4007   s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4008   if (!s.ok()) {
4009     INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4010     transaction_ = NULL;
4011     return s;
4012   }
4013 
4014   DCHECK(!new_files_to_write.size() ||
4015          KeyPrefix::IsValidDatabaseId(database_id_));
4016   if (!CollectBlobFilesToRemove()) {
4017     INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4018     transaction_ = NULL;
4019     return InternalInconsistencyStatus();
4020   }
4021 
4022   if (new_files_to_write.size()) {
4023     // This kicks off the writes of the new blobs, if any.
4024     // This call will zero out new_blob_entries and new_files_to_write.
4025     WriteNewBlobs(new_blob_entries, new_files_to_write, callback);
4026     // Remove the add journal, if any; once the blobs are written, and we
4027     // commit, this will do the cleanup.
4028     ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
4029   } else {
4030     callback->Run(true);
4031   }
4032 
4033   return leveldb::Status::OK();
4034 }
4035 
CommitPhaseTwo()4036 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4037   IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4038   leveldb::Status s;
4039   if (blobs_to_remove_.size()) {
4040     s = SortBlobsToRemove();
4041     if (!s.ok()) {
4042       INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4043       transaction_ = NULL;
4044       return s;
4045     }
4046   }
4047 
4048   s = transaction_->Commit();
4049   transaction_ = NULL;
4050 
4051   if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
4052     BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4053     BlobChangeMap::iterator iter;
4054     for (iter = blob_change_map_.begin(); iter != blob_change_map_.end();
4055          ++iter) {
4056       BlobChangeMap::iterator target_record = target_map.find(iter->first);
4057       if (target_record != target_map.end()) {
4058         delete target_record->second;
4059         target_map.erase(target_record);
4060       }
4061       if (iter->second) {
4062         target_map[iter->first] = iter->second;
4063         iter->second = NULL;
4064       }
4065     }
4066   }
4067   if (!s.ok())
4068     INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4069   else if (blobs_to_remove_.size())
4070     s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4071 
4072   return s;
4073 }
4074 
4075 
4076 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4077     : public IndexedDBBackingStore::BlobWriteCallback {
4078  public:
BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction * transaction,scoped_refptr<BlobWriteCallback> callback)4079   BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4080                            scoped_refptr<BlobWriteCallback> callback)
4081       : transaction_(transaction), callback_(callback) {}
Run(bool succeeded)4082   virtual void Run(bool succeeded) OVERRIDE {
4083     callback_->Run(succeeded);
4084     if (succeeded)  // Else it's already been deleted during rollback.
4085       transaction_->chained_blob_writer_ = NULL;
4086   }
4087 
4088  private:
~BlobWriteCallbackWrapper()4089   virtual ~BlobWriteCallbackWrapper() {}
4090   friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4091 
4092   IndexedDBBackingStore::Transaction* transaction_;
4093   scoped_refptr<BlobWriteCallback> callback_;
4094 
4095   DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4096 };
4097 
WriteNewBlobs(BlobEntryKeyValuePairVec & new_blob_entries,WriteDescriptorVec & new_files_to_write,scoped_refptr<BlobWriteCallback> callback)4098 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4099     BlobEntryKeyValuePairVec& new_blob_entries,
4100     WriteDescriptorVec& new_files_to_write,
4101     scoped_refptr<BlobWriteCallback> callback) {
4102   DCHECK_GT(new_files_to_write.size(), 0UL);
4103   DCHECK_GT(database_id_, 0);
4104   BlobEntryKeyValuePairVec::iterator blob_entry_iter;
4105   for (blob_entry_iter = new_blob_entries.begin();
4106        blob_entry_iter != new_blob_entries.end();
4107        ++blob_entry_iter) {
4108     // Add the new blob-table entry for each blob to the main transaction, or
4109     // remove any entry that may exist if there's no new one.
4110     if (!blob_entry_iter->second.size())
4111       transaction_->Remove(blob_entry_iter->first.Encode());
4112     else
4113       transaction_->Put(blob_entry_iter->first.Encode(),
4114                         &blob_entry_iter->second);
4115   }
4116   // Creating the writer will start it going asynchronously.
4117   chained_blob_writer_ =
4118       new ChainedBlobWriterImpl(database_id_,
4119                                 backing_store_,
4120                                 new_files_to_write,
4121                                 new BlobWriteCallbackWrapper(this, callback));
4122 }
4123 
Rollback()4124 void IndexedDBBackingStore::Transaction::Rollback() {
4125   IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4126   DCHECK(transaction_.get());
4127   if (chained_blob_writer_) {
4128     chained_blob_writer_->Abort();
4129     chained_blob_writer_ = NULL;
4130   }
4131   transaction_->Rollback();
4132   transaction_ = NULL;
4133 }
4134 
BlobChangeRecord(const std::string & key,int64 object_store_id)4135 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4136     const std::string& key,
4137     int64 object_store_id)
4138     : key_(key), object_store_id_(object_store_id) {
4139 }
4140 
~BlobChangeRecord()4141 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4142 }
4143 
SetBlobInfo(std::vector<IndexedDBBlobInfo> * blob_info)4144 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4145     std::vector<IndexedDBBlobInfo>* blob_info) {
4146   blob_info_.clear();
4147   if (blob_info)
4148     blob_info_.swap(*blob_info);
4149 }
4150 
SetHandles(ScopedVector<webkit_blob::BlobDataHandle> * handles)4151 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4152     ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4153   handles_.clear();
4154   if (handles)
4155     handles_.swap(*handles);
4156 }
4157 
4158 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
Clone() const4159 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4160   scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4161       new BlobChangeRecord(key_, object_store_id_));
4162   record->blob_info_ = blob_info_;
4163 
4164   ScopedVector<webkit_blob::BlobDataHandle>::const_iterator iter;
4165   for (iter = handles_.begin(); iter != handles_.end(); ++iter)
4166     record->handles_.push_back(new webkit_blob::BlobDataHandle(**iter));
4167   return record.Pass();
4168 }
4169 
PutBlobInfoIfNeeded(int64 database_id,int64 object_store_id,const std::string & object_store_data_key,std::vector<IndexedDBBlobInfo> * blob_info,ScopedVector<webkit_blob::BlobDataHandle> * handles)4170 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4171     int64 database_id,
4172     int64 object_store_id,
4173     const std::string& object_store_data_key,
4174     std::vector<IndexedDBBlobInfo>* blob_info,
4175     ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4176   if (!blob_info || blob_info->empty()) {
4177     blob_change_map_.erase(object_store_data_key);
4178     incognito_blob_map_.erase(object_store_data_key);
4179 
4180     BlobEntryKey blob_entry_key;
4181     StringPiece leveldb_key_piece(object_store_data_key);
4182     if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4183                                               &blob_entry_key)) {
4184       NOTREACHED();
4185       return InternalInconsistencyStatus();
4186     }
4187     std::string value;
4188     bool found = false;
4189     leveldb::Status s =
4190         transaction()->Get(blob_entry_key.Encode(), &value, &found);
4191     if (!s.ok())
4192       return s;
4193     if (!found)
4194       return leveldb::Status::OK();
4195   }
4196   PutBlobInfo(
4197       database_id, object_store_id, object_store_data_key, blob_info, handles);
4198   return leveldb::Status::OK();
4199 }
4200 
4201 // This is storing an info, even if empty, even if the previous key had no blob
4202 // info that we know of.  It duplicates a bunch of information stored in the
4203 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4204 // changes to exists or index keys here.
PutBlobInfo(int64 database_id,int64 object_store_id,const std::string & object_store_data_key,std::vector<IndexedDBBlobInfo> * blob_info,ScopedVector<webkit_blob::BlobDataHandle> * handles)4205 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4206     int64 database_id,
4207     int64 object_store_id,
4208     const std::string& object_store_data_key,
4209     std::vector<IndexedDBBlobInfo>* blob_info,
4210     ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4211   DCHECK_GT(object_store_data_key.size(), 0UL);
4212   if (database_id_ < 0)
4213     database_id_ = database_id;
4214   DCHECK_EQ(database_id_, database_id);
4215 
4216   BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4217   BlobChangeRecord* record = NULL;
4218   if (it == blob_change_map_.end()) {
4219     record = new BlobChangeRecord(object_store_data_key, object_store_id);
4220     blob_change_map_[object_store_data_key] = record;
4221   } else {
4222     record = it->second;
4223   }
4224   DCHECK_EQ(record->object_store_id(), object_store_id);
4225   record->SetBlobInfo(blob_info);
4226   record->SetHandles(handles);
4227   DCHECK(!handles || !handles->size());
4228 }
4229 
WriteDescriptor(const GURL & url,int64_t key,int64_t size)4230 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4231     const GURL& url,
4232     int64_t key,
4233     int64_t size)
4234     : is_file_(false), url_(url), key_(key), size_(size) {
4235 }
4236 
WriteDescriptor(const FilePath & file_path,int64_t key,int64_t size,base::Time last_modified)4237 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4238     const FilePath& file_path,
4239     int64_t key,
4240     int64_t size,
4241     base::Time last_modified)
4242     : is_file_(true),
4243       file_path_(file_path),
4244       key_(key),
4245       size_(size),
4246       last_modified_(last_modified) {
4247 }
4248 
4249 }  // namespace content
4250