indexed_db_database.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_database.h"
6
7#include <math.h>
8#include <vector>
9
10#include "base/auto_reset.h"
11#include "base/logging.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/strings/utf_string_conversions.h"
15#include "content/browser/indexed_db/indexed_db_backing_store.h"
16#include "content/browser/indexed_db/indexed_db_cursor.h"
17#include "content/browser/indexed_db/indexed_db_factory.h"
18#include "content/browser/indexed_db/indexed_db_index_writer.h"
19#include "content/browser/indexed_db/indexed_db_tracing.h"
20#include "content/browser/indexed_db/indexed_db_transaction.h"
21#include "content/common/indexed_db/indexed_db_key_path.h"
22#include "content/common/indexed_db/indexed_db_key_range.h"
23#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
24
25using base::Int64ToString16;
26using WebKit::WebIDBKey;
27
28namespace content {
29
30class CreateObjectStoreOperation : public IndexedDBTransaction::Operation {
31 public:
32  CreateObjectStoreOperation(
33      scoped_refptr<IndexedDBBackingStore> backing_store,
34      const IndexedDBObjectStoreMetadata& object_store_metadata)
35      : backing_store_(backing_store),
36        object_store_metadata_(object_store_metadata) {}
37  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
38
39 private:
40  const scoped_refptr<IndexedDBBackingStore> backing_store_;
41  const IndexedDBObjectStoreMetadata object_store_metadata_;
42};
43
44class DeleteObjectStoreOperation : public IndexedDBTransaction::Operation {
45 public:
46  DeleteObjectStoreOperation(
47      scoped_refptr<IndexedDBBackingStore> backing_store,
48      const IndexedDBObjectStoreMetadata& object_store_metadata)
49      : backing_store_(backing_store),
50        object_store_metadata_(object_store_metadata) {}
51  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
52
53 private:
54  const scoped_refptr<IndexedDBBackingStore> backing_store_;
55  const IndexedDBObjectStoreMetadata object_store_metadata_;
56};
57
58class IndexedDBDatabase::VersionChangeOperation
59    : public IndexedDBTransaction::Operation {
60 public:
61  VersionChangeOperation(
62      scoped_refptr<IndexedDBDatabase> database,
63      int64 transaction_id,
64      int64 version,
65      scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
66      scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks)
67      : database_(database),
68        transaction_id_(transaction_id),
69        version_(version),
70        callbacks_(callbacks),
71        database_callbacks_(database_callbacks) {}
72  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
73
74 private:
75  scoped_refptr<IndexedDBDatabase> database_;
76  int64 transaction_id_;
77  int64 version_;
78  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
79  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
80};
81
82class CreateObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
83 public:
84  CreateObjectStoreAbortOperation(scoped_refptr<IndexedDBDatabase> database,
85                                  int64 object_store_id)
86      : database_(database), object_store_id_(object_store_id) {}
87  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
88
89 private:
90  const scoped_refptr<IndexedDBDatabase> database_;
91  const int64 object_store_id_;
92};
93
94class DeleteObjectStoreAbortOperation : public IndexedDBTransaction::Operation {
95 public:
96  DeleteObjectStoreAbortOperation(
97      scoped_refptr<IndexedDBDatabase> database,
98      const IndexedDBObjectStoreMetadata& object_store_metadata)
99      : database_(database), object_store_metadata_(object_store_metadata) {}
100  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
101
102 private:
103  scoped_refptr<IndexedDBDatabase> database_;
104  IndexedDBObjectStoreMetadata object_store_metadata_;
105};
106
107class IndexedDBDatabase::VersionChangeAbortOperation
108    : public IndexedDBTransaction::Operation {
109 public:
110  VersionChangeAbortOperation(scoped_refptr<IndexedDBDatabase> database,
111                              const string16& previous_version,
112                              int64 previous_int_version)
113      : database_(database),
114        previous_version_(previous_version),
115        previous_int_version_(previous_int_version) {}
116  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
117
118 private:
119  scoped_refptr<IndexedDBDatabase> database_;
120  string16 previous_version_;
121  int64 previous_int_version_;
122};
123
124class CreateIndexOperation : public IndexedDBTransaction::Operation {
125 public:
126  CreateIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
127                       int64 object_store_id,
128                       const IndexedDBIndexMetadata& index_metadata)
129      : backing_store_(backing_store),
130        object_store_id_(object_store_id),
131        index_metadata_(index_metadata) {}
132  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
133
134 private:
135  const scoped_refptr<IndexedDBBackingStore> backing_store_;
136  const int64 object_store_id_;
137  const IndexedDBIndexMetadata index_metadata_;
138};
139
140class DeleteIndexOperation : public IndexedDBTransaction::Operation {
141 public:
142  DeleteIndexOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
143                       int64 object_store_id,
144                       const IndexedDBIndexMetadata& index_metadata)
145      : backing_store_(backing_store),
146        object_store_id_(object_store_id),
147        index_metadata_(index_metadata) {}
148  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
149
150 private:
151  const scoped_refptr<IndexedDBBackingStore> backing_store_;
152  const int64 object_store_id_;
153  const IndexedDBIndexMetadata index_metadata_;
154};
155
156class CreateIndexAbortOperation : public IndexedDBTransaction::Operation {
157 public:
158  CreateIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database,
159                            int64 object_store_id,
160                            int64 index_id)
161      : database_(database),
162        object_store_id_(object_store_id),
163        index_id_(index_id) {}
164  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
165
166 private:
167  const scoped_refptr<IndexedDBDatabase> database_;
168  const int64 object_store_id_;
169  const int64 index_id_;
170};
171
172class DeleteIndexAbortOperation : public IndexedDBTransaction::Operation {
173 public:
174  DeleteIndexAbortOperation(scoped_refptr<IndexedDBDatabase> database,
175                            int64 object_store_id,
176                            const IndexedDBIndexMetadata& index_metadata)
177      : database_(database),
178        object_store_id_(object_store_id),
179        index_metadata_(index_metadata) {}
180  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
181
182 private:
183  const scoped_refptr<IndexedDBDatabase> database_;
184  const int64 object_store_id_;
185  const IndexedDBIndexMetadata index_metadata_;
186};
187
188class GetOperation : public IndexedDBTransaction::Operation {
189 public:
190  GetOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
191               const IndexedDBDatabaseMetadata& metadata,
192               int64 object_store_id,
193               int64 index_id,
194               scoped_ptr<IndexedDBKeyRange> key_range,
195               indexed_db::CursorType cursor_type,
196               scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
197      : backing_store_(backing_store),
198        database_id_(metadata.id),
199        object_store_id_(object_store_id),
200        index_id_(index_id),
201        key_path_(metadata.object_stores.find(object_store_id)
202                      ->second.key_path),
203        auto_increment_(metadata.object_stores.find(object_store_id)
204                            ->second.auto_increment),
205        key_range_(key_range.Pass()),
206        cursor_type_(cursor_type),
207        callbacks_(callbacks) {
208    DCHECK(metadata.object_stores.find(object_store_id) !=
209           metadata.object_stores.end());
210    DCHECK(metadata.object_stores.find(object_store_id)->second.id ==
211           object_store_id);
212  }
213  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
214
215 private:
216  const scoped_refptr<IndexedDBBackingStore> backing_store_;
217  const int64 database_id_;
218  const int64 object_store_id_;
219  const int64 index_id_;
220  const IndexedDBKeyPath key_path_;
221  const bool auto_increment_;
222  const scoped_ptr<IndexedDBKeyRange> key_range_;
223  const indexed_db::CursorType cursor_type_;
224  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
225};
226
227class PutOperation : public IndexedDBTransaction::Operation {
228 public:
229  PutOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
230               int64 database_id,
231               const IndexedDBObjectStoreMetadata& object_store,
232               std::vector<char>* value,
233               scoped_ptr<IndexedDBKey> key,
234               IndexedDBDatabase::PutMode put_mode,
235               scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
236               const std::vector<int64>& index_ids,
237               const std::vector<IndexedDBDatabase::IndexKeys>& index_keys)
238      : backing_store_(backing_store),
239        database_id_(database_id),
240        object_store_(object_store),
241        key_(key.Pass()),
242        put_mode_(put_mode),
243        callbacks_(callbacks),
244        index_ids_(index_ids),
245        index_keys_(index_keys) {
246    value_.swap(*value);
247  }
248  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
249
250 private:
251  const scoped_refptr<IndexedDBBackingStore> backing_store_;
252  const int64 database_id_;
253  const IndexedDBObjectStoreMetadata object_store_;
254  std::vector<char> value_;
255  scoped_ptr<IndexedDBKey> key_;
256  const IndexedDBDatabase::PutMode put_mode_;
257  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
258  const std::vector<int64> index_ids_;
259  const std::vector<IndexedDBDatabase::IndexKeys> index_keys_;
260};
261
262class SetIndexesReadyOperation : public IndexedDBTransaction::Operation {
263 public:
264  explicit SetIndexesReadyOperation(size_t index_count)
265      : index_count_(index_count) {}
266  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
267
268 private:
269  const size_t index_count_;
270};
271
272class OpenCursorOperation : public IndexedDBTransaction::Operation {
273 public:
274  OpenCursorOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
275                      int64 database_id,
276                      int64 object_store_id,
277                      int64 index_id,
278                      scoped_ptr<IndexedDBKeyRange> key_range,
279                      indexed_db::CursorDirection direction,
280                      indexed_db::CursorType cursor_type,
281                      IndexedDBDatabase::TaskType task_type,
282                      scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
283      : backing_store_(backing_store),
284        database_id_(database_id),
285        object_store_id_(object_store_id),
286        index_id_(index_id),
287        key_range_(key_range.Pass()),
288        direction_(direction),
289        cursor_type_(cursor_type),
290        task_type_(task_type),
291        callbacks_(callbacks) {}
292  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
293
294 private:
295  const scoped_refptr<IndexedDBBackingStore> backing_store_;
296  const int64 database_id_;
297  const int64 object_store_id_;
298  const int64 index_id_;
299  const scoped_ptr<IndexedDBKeyRange> key_range_;
300  const indexed_db::CursorDirection direction_;
301  const indexed_db::CursorType cursor_type_;
302  const IndexedDBDatabase::TaskType task_type_;
303  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
304};
305
306class CountOperation : public IndexedDBTransaction::Operation {
307 public:
308  CountOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
309                 int64 database_id,
310                 int64 object_store_id,
311                 int64 index_id,
312                 scoped_ptr<IndexedDBKeyRange> key_range,
313                 scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
314      : backing_store_(backing_store),
315        database_id_(database_id),
316        object_store_id_(object_store_id),
317        index_id_(index_id),
318        key_range_(key_range.Pass()),
319        callbacks_(callbacks) {}
320  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
321
322 private:
323  const scoped_refptr<IndexedDBBackingStore> backing_store_;
324  const int64 database_id_;
325  const int64 object_store_id_;
326  const int64 index_id_;
327  const scoped_ptr<IndexedDBKeyRange> key_range_;
328  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
329};
330
331class DeleteRangeOperation : public IndexedDBTransaction::Operation {
332 public:
333  DeleteRangeOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
334                       int64 database_id,
335                       int64 object_store_id,
336                       scoped_ptr<IndexedDBKeyRange> key_range,
337                       scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
338      : backing_store_(backing_store),
339        database_id_(database_id),
340        object_store_id_(object_store_id),
341        key_range_(key_range.Pass()),
342        callbacks_(callbacks) {}
343  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
344
345 private:
346  const scoped_refptr<IndexedDBBackingStore> backing_store_;
347  const int64 database_id_;
348  const int64 object_store_id_;
349  const scoped_ptr<IndexedDBKeyRange> key_range_;
350  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
351};
352
353class ClearOperation : public IndexedDBTransaction::Operation {
354 public:
355  ClearOperation(scoped_refptr<IndexedDBBackingStore> backing_store,
356                 int64 database_id,
357                 int64 object_store_id,
358                 scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
359      : backing_store_(backing_store),
360        database_id_(database_id),
361        object_store_id_(object_store_id),
362        callbacks_(callbacks) {}
363  virtual void Perform(IndexedDBTransaction* transaction) OVERRIDE;
364
365 private:
366  const scoped_refptr<IndexedDBBackingStore> backing_store_;
367  const int64 database_id_;
368  const int64 object_store_id_;
369  const scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
370};
371
372class IndexedDBDatabase::PendingOpenCall {
373 public:
374  PendingOpenCall(
375      scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
376      scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
377      int64 transaction_id,
378      int64 version)
379      : callbacks_(callbacks),
380        database_callbacks_(database_callbacks),
381        version_(version),
382        transaction_id_(transaction_id) {}
383  scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
384  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> DatabaseCallbacks() {
385    return database_callbacks_;
386  }
387  int64 Version() { return version_; }
388  int64 TransactionId() const { return transaction_id_; }
389
390 private:
391  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
392  scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks_;
393  int64 version_;
394  const int64 transaction_id_;
395};
396
397class IndexedDBDatabase::PendingDeleteCall {
398 public:
399  explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacksWrapper> callbacks)
400      : callbacks_(callbacks) {}
401  scoped_refptr<IndexedDBCallbacksWrapper> Callbacks() { return callbacks_; }
402
403 private:
404  scoped_refptr<IndexedDBCallbacksWrapper> callbacks_;
405};
406
407scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
408    const string16& name,
409    IndexedDBBackingStore* database,
410    IndexedDBFactory* factory,
411    const string16& unique_identifier) {
412  scoped_refptr<IndexedDBDatabase> backend =
413      new IndexedDBDatabase(name, database, factory, unique_identifier);
414  if (!backend->OpenInternal())
415    return 0;
416  return backend;
417}
418
419namespace {
420const base::string16::value_type kNoStringVersion[] = {0};
421}
422
423IndexedDBDatabase::IndexedDBDatabase(const string16& name,
424                                     IndexedDBBackingStore* backing_store,
425                                     IndexedDBFactory* factory,
426                                     const string16& unique_identifier)
427    : backing_store_(backing_store),
428      metadata_(name,
429                kInvalidId,
430                kNoStringVersion,
431                IndexedDBDatabaseMetadata::NO_INT_VERSION,
432                kInvalidId),
433      identifier_(unique_identifier),
434      factory_(factory),
435      running_version_change_transaction_(NULL),
436      closing_connection_(false) {
437  DCHECK(!metadata_.name.empty());
438}
439
440void IndexedDBDatabase::AddObjectStore(
441    const IndexedDBObjectStoreMetadata& object_store,
442    int64 new_max_object_store_id) {
443  DCHECK(metadata_.object_stores.find(object_store.id) ==
444         metadata_.object_stores.end());
445  if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
446    DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
447    metadata_.max_object_store_id = new_max_object_store_id;
448  }
449  metadata_.object_stores[object_store.id] = object_store;
450}
451
452void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
453  DCHECK(metadata_.object_stores.find(object_store_id) !=
454         metadata_.object_stores.end());
455  metadata_.object_stores.erase(object_store_id);
456}
457
458void IndexedDBDatabase::AddIndex(int64 object_store_id,
459                                 const IndexedDBIndexMetadata& index,
460                                 int64 new_max_index_id) {
461  DCHECK(metadata_.object_stores.find(object_store_id) !=
462         metadata_.object_stores.end());
463  IndexedDBObjectStoreMetadata object_store =
464      metadata_.object_stores[object_store_id];
465
466  DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
467  object_store.indexes[index.id] = index;
468  if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
469    DCHECK_LT(object_store.max_index_id, new_max_index_id);
470    object_store.max_index_id = new_max_index_id;
471  }
472  metadata_.object_stores[object_store_id] = object_store;
473}
474
475void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
476  DCHECK(metadata_.object_stores.find(object_store_id) !=
477         metadata_.object_stores.end());
478  IndexedDBObjectStoreMetadata object_store =
479      metadata_.object_stores[object_store_id];
480
481  DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
482  object_store.indexes.erase(index_id);
483  metadata_.object_stores[object_store_id] = object_store;
484}
485
486bool IndexedDBDatabase::OpenInternal() {
487  bool success = false;
488  bool ok = backing_store_->GetIDBDatabaseMetaData(
489      metadata_.name, &metadata_, &success);
490  DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
491                                                  << " id_ = " << metadata_.id;
492  if (!ok)
493    return false;
494  if (success)
495    return backing_store_->GetObjectStores(metadata_.id,
496                                           &metadata_.object_stores);
497
498  return backing_store_->CreateIDBDatabaseMetaData(
499      metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
500}
501
502IndexedDBDatabase::~IndexedDBDatabase() {
503  DCHECK(transactions_.empty());
504  DCHECK(pending_open_calls_.empty());
505  DCHECK(pending_delete_calls_.empty());
506}
507
508scoped_refptr<IndexedDBBackingStore> IndexedDBDatabase::BackingStore() const {
509  return backing_store_;
510}
511
512void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
513                                          int64 object_store_id,
514                                          const string16& name,
515                                          const IndexedDBKeyPath& key_path,
516                                          bool auto_increment) {
517  IDB_TRACE("IndexedDBDatabase::create_object_store");
518  TransactionMap::const_iterator trans_iterator =
519      transactions_.find(transaction_id);
520  if (trans_iterator == transactions_.end())
521    return;
522  IndexedDBTransaction* transaction = trans_iterator->second;
523  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
524
525  DCHECK(metadata_.object_stores.find(object_store_id) ==
526         metadata_.object_stores.end());
527  IndexedDBObjectStoreMetadata object_store_metadata(
528      name,
529      object_store_id,
530      key_path,
531      auto_increment,
532      IndexedDBDatabase::kMinimumIndexId);
533
534  transaction->ScheduleTask(
535      new CreateObjectStoreOperation(backing_store_, object_store_metadata),
536      new CreateObjectStoreAbortOperation(this, object_store_id));
537
538  AddObjectStore(object_store_metadata, object_store_id);
539}
540
541void CreateObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
542  IDB_TRACE("CreateObjectStoreOperation");
543  if (!backing_store_->CreateObjectStore(
544          transaction->BackingStoreTransaction(),
545          transaction->database()->id(),
546          object_store_metadata_.id,
547          object_store_metadata_.name,
548          object_store_metadata_.key_path,
549          object_store_metadata_.auto_increment)) {
550    transaction->Abort(IndexedDBDatabaseError(
551        WebKit::WebIDBDatabaseExceptionUnknownError,
552        ASCIIToUTF16("Internal error creating object store '") +
553            object_store_metadata_.name + ASCIIToUTF16("'.")));
554    return;
555  }
556}
557
558void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
559                                          int64 object_store_id) {
560  IDB_TRACE("IndexedDBDatabase::delete_object_store");
561  TransactionMap::const_iterator trans_iterator =
562      transactions_.find(transaction_id);
563  if (trans_iterator == transactions_.end())
564    return;
565  IndexedDBTransaction* transaction = trans_iterator->second;
566  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
567
568  DCHECK(metadata_.object_stores.find(object_store_id) !=
569         metadata_.object_stores.end());
570  const IndexedDBObjectStoreMetadata& object_store_metadata =
571      metadata_.object_stores[object_store_id];
572
573  transaction->ScheduleTask(
574      new DeleteObjectStoreOperation(backing_store_, object_store_metadata),
575      new DeleteObjectStoreAbortOperation(this, object_store_metadata));
576  RemoveObjectStore(object_store_id);
577}
578
579void IndexedDBDatabase::CreateIndex(int64 transaction_id,
580                                    int64 object_store_id,
581                                    int64 index_id,
582                                    const string16& name,
583                                    const IndexedDBKeyPath& key_path,
584                                    bool unique,
585                                    bool multi_entry) {
586  IDB_TRACE("IndexedDBDatabase::create_index");
587  TransactionMap::const_iterator trans_iterator =
588      transactions_.find(transaction_id);
589  if (trans_iterator == transactions_.end())
590    return;
591  IndexedDBTransaction* transaction = trans_iterator->second;
592  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
593
594  DCHECK(metadata_.object_stores.find(object_store_id) !=
595         metadata_.object_stores.end());
596  const IndexedDBObjectStoreMetadata object_store =
597      metadata_.object_stores[object_store_id];
598
599  DCHECK(object_store.indexes.find(index_id) == object_store.indexes.end());
600  const IndexedDBIndexMetadata index_metadata(
601      name, index_id, key_path, unique, multi_entry);
602
603  transaction->ScheduleTask(
604      new CreateIndexOperation(backing_store_, object_store_id, index_metadata),
605      new CreateIndexAbortOperation(this, object_store_id, index_id));
606
607  AddIndex(object_store_id, index_metadata, index_id);
608}
609
610void CreateIndexOperation::Perform(IndexedDBTransaction* transaction) {
611  IDB_TRACE("CreateIndexOperation");
612  if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
613                                   transaction->database()->id(),
614                                   object_store_id_,
615                                   index_metadata_.id,
616                                   index_metadata_.name,
617                                   index_metadata_.key_path,
618                                   index_metadata_.unique,
619                                   index_metadata_.multi_entry)) {
620    string16 error_string = ASCIIToUTF16("Internal error creating index '") +
621                            index_metadata_.name + ASCIIToUTF16("'.");
622    transaction->Abort(IndexedDBDatabaseError(
623        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
624    return;
625  }
626}
627
628void CreateIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
629  IDB_TRACE("CreateIndexAbortOperation");
630  DCHECK(!transaction);
631  database_->RemoveIndex(object_store_id_, index_id_);
632}
633
634void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
635                                    int64 object_store_id,
636                                    int64 index_id) {
637  IDB_TRACE("IndexedDBDatabase::delete_index");
638  TransactionMap::const_iterator trans_iterator =
639      transactions_.find(transaction_id);
640  if (trans_iterator == transactions_.end())
641    return;
642  IndexedDBTransaction* transaction = trans_iterator->second;
643  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
644
645  DCHECK(metadata_.object_stores.find(object_store_id) !=
646         metadata_.object_stores.end());
647  IndexedDBObjectStoreMetadata object_store =
648      metadata_.object_stores[object_store_id];
649
650  DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
651  const IndexedDBIndexMetadata& index_metadata = object_store.indexes[index_id];
652
653  transaction->ScheduleTask(
654      new DeleteIndexOperation(backing_store_, object_store_id, index_metadata),
655      new DeleteIndexAbortOperation(this, object_store_id, index_metadata));
656
657  RemoveIndex(object_store_id, index_id);
658}
659
660void DeleteIndexOperation::Perform(IndexedDBTransaction* transaction) {
661  IDB_TRACE("DeleteIndexOperation");
662  bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
663                                        transaction->database()->id(),
664                                        object_store_id_,
665                                        index_metadata_.id);
666  if (!ok) {
667    string16 error_string = ASCIIToUTF16("Internal error deleting index '") +
668                            index_metadata_.name + ASCIIToUTF16("'.");
669    transaction->Abort(IndexedDBDatabaseError(
670        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
671  }
672}
673
674void DeleteIndexAbortOperation::Perform(IndexedDBTransaction* transaction) {
675  IDB_TRACE("DeleteIndexAbortOperation");
676  DCHECK(!transaction);
677  database_->AddIndex(
678      object_store_id_, index_metadata_, IndexedDBIndexMetadata::kInvalidId);
679}
680
681void IndexedDBDatabase::Commit(int64 transaction_id) {
682  // The frontend suggests that we commit, but we may have previously initiated
683  // an abort, and so have disposed of the transaction. on_abort has already
684  // been dispatched to the frontend, so it will find out about that
685  // asynchronously.
686  if (transactions_.find(transaction_id) != transactions_.end())
687    transactions_[transaction_id]->Commit();
688}
689
690void IndexedDBDatabase::Abort(int64 transaction_id) {
691  // If the transaction is unknown, then it has already been aborted by the
692  // backend before this call so it is safe to ignore it.
693  if (transactions_.find(transaction_id) != transactions_.end())
694    transactions_[transaction_id]->Abort();
695}
696
697void IndexedDBDatabase::Abort(int64 transaction_id,
698                              const IndexedDBDatabaseError& error) {
699  // If the transaction is unknown, then it has already been aborted by the
700  // backend before this call so it is safe to ignore it.
701  if (transactions_.find(transaction_id) != transactions_.end())
702    transactions_[transaction_id]->Abort(error);
703}
704
705void IndexedDBDatabase::Get(
706    int64 transaction_id,
707    int64 object_store_id,
708    int64 index_id,
709    scoped_ptr<IndexedDBKeyRange> key_range,
710    bool key_only,
711    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
712  IDB_TRACE("IndexedDBDatabase::get");
713  TransactionMap::const_iterator trans_iterator =
714      transactions_.find(transaction_id);
715  if (trans_iterator == transactions_.end())
716    return;
717  IndexedDBTransaction* transaction = trans_iterator->second;
718
719  transaction->ScheduleTask(new GetOperation(
720      backing_store_,
721      metadata_,
722      object_store_id,
723      index_id,
724      key_range.Pass(),
725      key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
726      callbacks));
727}
728
729void GetOperation::Perform(IndexedDBTransaction* transaction) {
730  IDB_TRACE("GetOperation");
731
732  const IndexedDBKey* key;
733
734  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
735  if (key_range_->IsOnlyKey()) {
736    key = &key_range_->lower();
737  } else {
738    if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
739      DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
740      // ObjectStore Retrieval Operation
741      backing_store_cursor = backing_store_->OpenObjectStoreCursor(
742          transaction->BackingStoreTransaction(),
743          database_id_,
744          object_store_id_,
745          *key_range_,
746          indexed_db::CURSOR_NEXT);
747    } else if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
748      // Index Value Retrieval Operation
749      backing_store_cursor = backing_store_->OpenIndexKeyCursor(
750          transaction->BackingStoreTransaction(),
751          database_id_,
752          object_store_id_,
753          index_id_,
754          *key_range_,
755          indexed_db::CURSOR_NEXT);
756    } else {
757      // Index Referenced Value Retrieval Operation
758      backing_store_cursor = backing_store_->OpenIndexCursor(
759          transaction->BackingStoreTransaction(),
760          database_id_,
761          object_store_id_,
762          index_id_,
763          *key_range_,
764          indexed_db::CURSOR_NEXT);
765    }
766
767    if (!backing_store_cursor) {
768      callbacks_->OnSuccess();
769      return;
770    }
771
772    key = &backing_store_cursor->key();
773  }
774
775  scoped_ptr<IndexedDBKey> primary_key;
776  bool ok;
777  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
778    // Object Store Retrieval Operation
779    std::vector<char> value;
780    ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
781                                   database_id_,
782                                   object_store_id_,
783                                   *key,
784                                   &value);
785    if (!ok) {
786      callbacks_->OnError(
787          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
788                                 "Internal error in get_record."));
789      return;
790    }
791
792    if (value.empty()) {
793      callbacks_->OnSuccess();
794      return;
795    }
796
797    if (auto_increment_ && !key_path_.IsNull()) {
798      callbacks_->OnSuccess(&value, *key, key_path_);
799      return;
800    }
801
802    callbacks_->OnSuccess(&value);
803    return;
804  }
805
806  // From here we are dealing only with indexes.
807  ok = backing_store_->GetPrimaryKeyViaIndex(
808      transaction->BackingStoreTransaction(),
809      database_id_,
810      object_store_id_,
811      index_id_,
812      *key,
813      &primary_key);
814  if (!ok) {
815    callbacks_->OnError(
816        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
817                               "Internal error in get_primary_key_via_index."));
818    return;
819  }
820  if (!primary_key) {
821    callbacks_->OnSuccess();
822    return;
823  }
824  if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
825    // Index Value Retrieval Operation
826    callbacks_->OnSuccess(*primary_key);
827    return;
828  }
829
830  // Index Referenced Value Retrieval Operation
831  std::vector<char> value;
832  ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
833                                 database_id_,
834                                 object_store_id_,
835                                 *primary_key,
836                                 &value);
837  if (!ok) {
838    callbacks_->OnError(
839        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
840                               "Internal error in get_record."));
841    return;
842  }
843
844  if (value.empty()) {
845    callbacks_->OnSuccess();
846    return;
847  }
848  if (auto_increment_ && !key_path_.IsNull()) {
849    callbacks_->OnSuccess(&value, *primary_key, key_path_);
850    return;
851  }
852  callbacks_->OnSuccess(&value);
853}
854
855static scoped_ptr<IndexedDBKey> GenerateKey(
856    scoped_refptr<IndexedDBBackingStore> backing_store,
857    scoped_refptr<IndexedDBTransaction> transaction,
858    int64 database_id,
859    int64 object_store_id) {
860  const int64 max_generator_value =
861      9007199254740992LL;  // Maximum integer storable as ECMAScript number.
862  int64 current_number;
863  bool ok = backing_store->GetKeyGeneratorCurrentNumber(
864      transaction->BackingStoreTransaction(),
865      database_id,
866      object_store_id,
867      &current_number);
868  if (!ok) {
869    LOG(ERROR) << "Failed to get_key_generator_current_number";
870    return make_scoped_ptr(new IndexedDBKey());
871  }
872  if (current_number < 0 || current_number > max_generator_value)
873    return make_scoped_ptr(new IndexedDBKey());
874
875  return make_scoped_ptr(
876      new IndexedDBKey(current_number, WebIDBKey::NumberType));
877}
878
879static bool UpdateKeyGenerator(
880    scoped_refptr<IndexedDBBackingStore> backing_store,
881    scoped_refptr<IndexedDBTransaction> transaction,
882    int64 database_id,
883    int64 object_store_id,
884    const IndexedDBKey* key,
885    bool check_current) {
886  DCHECK(key);
887  DCHECK_EQ(WebIDBKey::NumberType, key->type());
888  return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
889      transaction->BackingStoreTransaction(),
890      database_id,
891      object_store_id,
892      static_cast<int64>(floor(key->number())) + 1,
893      check_current);
894}
895
896void IndexedDBDatabase::Put(int64 transaction_id,
897                            int64 object_store_id,
898                            std::vector<char>* value,
899                            scoped_ptr<IndexedDBKey> key,
900                            PutMode put_mode,
901                            scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
902                            const std::vector<int64>& index_ids,
903                            const std::vector<IndexKeys>& index_keys) {
904  IDB_TRACE("IndexedDBDatabase::put");
905  TransactionMap::const_iterator trans_iterator =
906      transactions_.find(transaction_id);
907  if (trans_iterator == transactions_.end())
908    return;
909  IndexedDBTransaction* transaction = trans_iterator->second;
910  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
911
912  const IndexedDBObjectStoreMetadata object_store_metadata =
913      metadata_.object_stores[object_store_id];
914
915  DCHECK(key);
916  DCHECK(object_store_metadata.auto_increment || key->IsValid());
917  transaction->ScheduleTask(new PutOperation(backing_store_,
918                                             id(),
919                                             object_store_metadata,
920                                             value,
921                                             key.Pass(),
922                                             put_mode,
923                                             callbacks,
924                                             index_ids,
925                                             index_keys));
926}
927
928void PutOperation::Perform(IndexedDBTransaction* transaction) {
929  IDB_TRACE("PutOperation");
930  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
931  DCHECK_EQ(index_ids_.size(), index_keys_.size());
932  bool key_was_generated = false;
933
934  scoped_ptr<IndexedDBKey> key;
935  if (put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
936      object_store_.auto_increment && !key_->IsValid()) {
937    scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
938        backing_store_, transaction, database_id_, object_store_.id);
939    key_was_generated = true;
940    if (!auto_inc_key->IsValid()) {
941      callbacks_->OnError(
942          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
943                                 "Maximum key generator value reached."));
944      return;
945    }
946    key = auto_inc_key.Pass();
947  } else {
948    key = key_.Pass();
949  }
950
951  DCHECK(key->IsValid());
952
953  IndexedDBBackingStore::RecordIdentifier record_identifier;
954  if (put_mode_ == IndexedDBDatabase::ADD_ONLY) {
955    bool found = false;
956    bool ok = backing_store_->KeyExistsInObjectStore(
957        transaction->BackingStoreTransaction(),
958        database_id_,
959        object_store_.id,
960        *key.get(),
961        &record_identifier,
962        &found);
963    if (!ok) {
964      callbacks_->OnError(
965          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
966                                 "Internal error checking key existence."));
967      return;
968    }
969    if (found) {
970      callbacks_->OnError(
971          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
972                                 "Key already exists in the object store."));
973      return;
974    }
975  }
976
977  ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
978  string16 error_message;
979  bool obeys_constraints = false;
980  bool backing_store_success =
981      IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
982                                                 backing_store_.get(),
983                                                 database_id_,
984                                                 object_store_,
985                                                 *key,
986                                                 key_was_generated,
987                                                 index_ids_,
988                                                 index_keys_,
989                                                 &index_writers,
990                                                 &error_message,
991                                                 &obeys_constraints);
992  if (!backing_store_success) {
993    callbacks_->OnError(IndexedDBDatabaseError(
994        WebKit::WebIDBDatabaseExceptionUnknownError,
995        "Internal error: backing store error updating index keys."));
996    return;
997  }
998  if (!obeys_constraints) {
999    callbacks_->OnError(IndexedDBDatabaseError(
1000        WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
1001    return;
1002  }
1003
1004  // Before this point, don't do any mutation. After this point, rollback the
1005  // transaction in case of error.
1006  backing_store_success =
1007      backing_store_->PutRecord(transaction->BackingStoreTransaction(),
1008                                database_id_,
1009                                object_store_.id,
1010                                *key.get(),
1011                                value_,
1012                                &record_identifier);
1013  if (!backing_store_success) {
1014    callbacks_->OnError(IndexedDBDatabaseError(
1015        WebKit::WebIDBDatabaseExceptionUnknownError,
1016        "Internal error: backing store error performing put/add."));
1017    return;
1018  }
1019
1020  for (size_t i = 0; i < index_writers.size(); ++i) {
1021    IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
1022    index_writer->WriteIndexKeys(record_identifier,
1023                                 backing_store_,
1024                                 transaction->BackingStoreTransaction(),
1025                                 database_id_,
1026                                 object_store_.id);
1027  }
1028
1029  if (object_store_.auto_increment &&
1030      put_mode_ != IndexedDBDatabase::CURSOR_UPDATE &&
1031      key->type() == WebIDBKey::NumberType) {
1032    bool ok = UpdateKeyGenerator(backing_store_,
1033                                 transaction,
1034                                 database_id_,
1035                                 object_store_.id,
1036                                 key.get(),
1037                                 !key_was_generated);
1038    if (!ok) {
1039      callbacks_->OnError(
1040          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1041                                 "Internal error updating key generator."));
1042      return;
1043    }
1044  }
1045
1046  callbacks_->OnSuccess(*key);
1047}
1048
1049void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
1050                                     int64 object_store_id,
1051                                     scoped_ptr<IndexedDBKey> primary_key,
1052                                     const std::vector<int64>& index_ids,
1053                                     const std::vector<IndexKeys>& index_keys) {
1054  IDB_TRACE("IndexedDBDatabase::set_index_keys");
1055  TransactionMap::const_iterator trans_iterator =
1056      transactions_.find(transaction_id);
1057  if (trans_iterator == transactions_.end())
1058    return;
1059  IndexedDBTransaction* transaction = trans_iterator->second;
1060  DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
1061
1062  scoped_refptr<IndexedDBBackingStore> store = BackingStore();
1063  // TODO(jsbell): This method could be asynchronous, but we need to
1064  // evaluate if it's worth the extra complexity.
1065  IndexedDBBackingStore::RecordIdentifier record_identifier;
1066  bool found = false;
1067  bool ok =
1068      store->KeyExistsInObjectStore(transaction->BackingStoreTransaction(),
1069                                    metadata_.id,
1070                                    object_store_id,
1071                                    *primary_key,
1072                                    &record_identifier,
1073                                    &found);
1074  if (!ok) {
1075    transaction->Abort(
1076        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1077                               "Internal error setting index keys."));
1078    return;
1079  }
1080  if (!found) {
1081    transaction->Abort(IndexedDBDatabaseError(
1082        WebKit::WebIDBDatabaseExceptionUnknownError,
1083        "Internal error setting index keys for object store."));
1084    return;
1085  }
1086
1087  ScopedVector<IndexedDBObjectStoreImpl::IndexWriter> index_writers;
1088  string16 error_message;
1089  bool obeys_constraints = false;
1090  DCHECK(metadata_.object_stores.find(object_store_id) !=
1091         metadata_.object_stores.end());
1092  const IndexedDBObjectStoreMetadata& object_store_metadata =
1093      metadata_.object_stores[object_store_id];
1094  bool backing_store_success =
1095      IndexedDBObjectStoreImpl::MakeIndexWriters(transaction,
1096                                                 store.get(),
1097                                                 id(),
1098                                                 object_store_metadata,
1099                                                 *primary_key,
1100                                                 false,
1101                                                 index_ids,
1102                                                 index_keys,
1103                                                 &index_writers,
1104                                                 &error_message,
1105                                                 &obeys_constraints);
1106  if (!backing_store_success) {
1107    transaction->Abort(IndexedDBDatabaseError(
1108        WebKit::WebIDBDatabaseExceptionUnknownError,
1109        "Internal error: backing store error updating index keys."));
1110    return;
1111  }
1112  if (!obeys_constraints) {
1113    transaction->Abort(IndexedDBDatabaseError(
1114        WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
1115    return;
1116  }
1117
1118  for (size_t i = 0; i < index_writers.size(); ++i) {
1119    IndexedDBObjectStoreImpl::IndexWriter* index_writer = index_writers[i];
1120    index_writer->WriteIndexKeys(record_identifier,
1121                                 store.get(),
1122                                 transaction->BackingStoreTransaction(),
1123                                 id(),
1124                                 object_store_id);
1125  }
1126}
1127
1128void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
1129                                        int64,
1130                                        const std::vector<int64>& index_ids) {
1131  IDB_TRACE("IndexedDBObjectStoreImpl::set_indexes_ready");
1132
1133  TransactionMap::const_iterator trans_iterator =
1134      transactions_.find(transaction_id);
1135  if (trans_iterator == transactions_.end())
1136    return;
1137  IndexedDBTransaction* transaction = trans_iterator->second;
1138
1139  transaction->ScheduleTask(IndexedDBDatabase::PREEMPTIVE_TASK,
1140                            new SetIndexesReadyOperation(index_ids.size()));
1141}
1142
1143void SetIndexesReadyOperation::Perform(IndexedDBTransaction* transaction) {
1144  IDB_TRACE("SetIndexesReadyOperation");
1145  for (size_t i = 0; i < index_count_; ++i)
1146    transaction->DidCompletePreemptiveEvent();
1147}
1148
1149void IndexedDBDatabase::OpenCursor(
1150    int64 transaction_id,
1151    int64 object_store_id,
1152    int64 index_id,
1153    scoped_ptr<IndexedDBKeyRange> key_range,
1154    indexed_db::CursorDirection direction,
1155    bool key_only,
1156    TaskType task_type,
1157    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1158  IDB_TRACE("IndexedDBDatabase::open_cursor");
1159  TransactionMap::const_iterator trans_iterator =
1160      transactions_.find(transaction_id);
1161  if (trans_iterator == transactions_.end())
1162    return;
1163  IndexedDBTransaction* transaction = trans_iterator->second;
1164
1165  transaction->ScheduleTask(new OpenCursorOperation(
1166      backing_store_,
1167      id(),
1168      object_store_id,
1169      index_id,
1170      key_range.Pass(),
1171      direction,
1172      key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
1173      task_type,
1174      callbacks));
1175}
1176
1177void OpenCursorOperation::Perform(IndexedDBTransaction* transaction) {
1178  IDB_TRACE("OpenCursorOperation");
1179
1180  // The frontend has begun indexing, so this pauses the transaction
1181  // until the indexing is complete. This can't happen any earlier
1182  // because we don't want to switch to early mode in case multiple
1183  // indexes are being created in a row, with Put()'s in between.
1184  if (task_type_ == IndexedDBDatabase::PREEMPTIVE_TASK)
1185    transaction->AddPreemptiveEvent();
1186
1187  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1188  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
1189    DCHECK_NE(cursor_type_, indexed_db::CURSOR_KEY_ONLY);
1190    backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1191        transaction->BackingStoreTransaction(),
1192        database_id_,
1193        object_store_id_,
1194        *key_range_,
1195        direction_);
1196  } else {
1197    DCHECK_EQ(task_type_, IndexedDBDatabase::NORMAL_TASK);
1198    if (cursor_type_ == indexed_db::CURSOR_KEY_ONLY) {
1199      backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1200          transaction->BackingStoreTransaction(),
1201          database_id_,
1202          object_store_id_,
1203          index_id_,
1204          *key_range_,
1205          direction_);
1206    } else {
1207      backing_store_cursor = backing_store_->OpenIndexCursor(
1208          transaction->BackingStoreTransaction(),
1209          database_id_,
1210          object_store_id_,
1211          index_id_,
1212          *key_range_,
1213          direction_);
1214    }
1215  }
1216
1217  if (!backing_store_cursor) {
1218    callbacks_->OnSuccess(static_cast<std::vector<char>*>(NULL));
1219    return;
1220  }
1221
1222  IndexedDBDatabase::TaskType task_type(
1223      static_cast<IndexedDBDatabase::TaskType>(task_type_));
1224  scoped_refptr<IndexedDBCursor> cursor = IndexedDBCursor::Create(
1225      backing_store_cursor.Pass(), cursor_type_, task_type, transaction);
1226  callbacks_->OnSuccess(
1227      cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1228}
1229
1230void IndexedDBDatabase::Count(
1231    int64 transaction_id,
1232    int64 object_store_id,
1233    int64 index_id,
1234    scoped_ptr<IndexedDBKeyRange> key_range,
1235    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1236  IDB_TRACE("IndexedDBDatabase::count");
1237  TransactionMap::const_iterator trans_iterator =
1238      transactions_.find(transaction_id);
1239  if (trans_iterator == transactions_.end())
1240    return;
1241  IndexedDBTransaction* transaction = trans_iterator->second;
1242
1243  DCHECK(metadata_.object_stores.find(object_store_id) !=
1244         metadata_.object_stores.end());
1245  transaction->ScheduleTask(new CountOperation(backing_store_,
1246                                               id(),
1247                                               object_store_id,
1248                                               index_id,
1249                                               key_range.Pass(),
1250                                               callbacks));
1251}
1252
1253void CountOperation::Perform(IndexedDBTransaction* transaction) {
1254  IDB_TRACE("CountOperation");
1255  uint32 count = 0;
1256  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1257
1258  if (index_id_ == IndexedDBIndexMetadata::kInvalidId) {
1259    backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1260        transaction->BackingStoreTransaction(),
1261        database_id_,
1262        object_store_id_,
1263        *key_range_,
1264        indexed_db::CURSOR_NEXT);
1265  } else {
1266    backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1267        transaction->BackingStoreTransaction(),
1268        database_id_,
1269        object_store_id_,
1270        index_id_,
1271        *key_range_,
1272        indexed_db::CURSOR_NEXT);
1273  }
1274  if (!backing_store_cursor) {
1275    callbacks_->OnSuccess(count);
1276    return;
1277  }
1278
1279  do {
1280    ++count;
1281  } while (backing_store_cursor->ContinueFunction(0));
1282
1283  callbacks_->OnSuccess(count);
1284}
1285
1286void IndexedDBDatabase::DeleteRange(
1287    int64 transaction_id,
1288    int64 object_store_id,
1289    scoped_ptr<IndexedDBKeyRange> key_range,
1290    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1291  IDB_TRACE("IndexedDBDatabase::delete_range");
1292  TransactionMap::const_iterator trans_iterator =
1293      transactions_.find(transaction_id);
1294  if (trans_iterator == transactions_.end())
1295    return;
1296  IndexedDBTransaction* transaction = trans_iterator->second;
1297
1298  transaction->ScheduleTask(new DeleteRangeOperation(
1299      backing_store_, id(), object_store_id, key_range.Pass(), callbacks));
1300}
1301
1302void DeleteRangeOperation::Perform(IndexedDBTransaction* transaction) {
1303  IDB_TRACE("DeleteRangeOperation");
1304  scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1305      backing_store_->OpenObjectStoreCursor(
1306          transaction->BackingStoreTransaction(),
1307          database_id_,
1308          object_store_id_,
1309          *key_range_,
1310          indexed_db::CURSOR_NEXT);
1311  if (backing_store_cursor) {
1312    do {
1313      if (!backing_store_->DeleteRecord(
1314              transaction->BackingStoreTransaction(),
1315              database_id_,
1316              object_store_id_,
1317              backing_store_cursor->record_identifier())) {
1318        callbacks_->OnError(
1319            IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1320                                   "Internal error deleting data in range"));
1321        return;
1322      }
1323    } while (backing_store_cursor->ContinueFunction(0));
1324  }
1325
1326  callbacks_->OnSuccess();
1327}
1328
1329void IndexedDBDatabase::Clear(
1330    int64 transaction_id,
1331    int64 object_store_id,
1332    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1333  IDB_TRACE("IndexedDBDatabase::clear");
1334  TransactionMap::const_iterator trans_iterator =
1335      transactions_.find(transaction_id);
1336  if (trans_iterator == transactions_.end())
1337    return;
1338  IndexedDBTransaction* transaction = trans_iterator->second;
1339  DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1340
1341  transaction->ScheduleTask(
1342      new ClearOperation(backing_store_, id(), object_store_id, callbacks));
1343}
1344
1345void ClearOperation::Perform(IndexedDBTransaction* transaction) {
1346  IDB_TRACE("ObjectStoreClearOperation");
1347  if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(),
1348                                        database_id_,
1349                                        object_store_id_)) {
1350    callbacks_->OnError(
1351        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1352                               "Internal error clearing object store"));
1353    return;
1354  }
1355  callbacks_->OnSuccess();
1356}
1357
1358void DeleteObjectStoreOperation::Perform(IndexedDBTransaction* transaction) {
1359  IDB_TRACE("DeleteObjectStoreOperation");
1360  bool ok =
1361      backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1362                                        transaction->database()->id(),
1363                                        object_store_metadata_.id);
1364  if (!ok) {
1365    string16 error_string =
1366        ASCIIToUTF16("Internal error deleting object store '") +
1367        object_store_metadata_.name + ASCIIToUTF16("'.");
1368    transaction->Abort(IndexedDBDatabaseError(
1369        WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
1370  }
1371}
1372
1373void IndexedDBDatabase::VersionChangeOperation::Perform(
1374    IndexedDBTransaction* transaction) {
1375  IDB_TRACE("VersionChangeOperation");
1376  int64 database_id = database_->id();
1377  int64 old_version = database_->metadata_.int_version;
1378  DCHECK_GT(version_, old_version);
1379  database_->metadata_.int_version = version_;
1380  if (!database_->backing_store_->UpdateIDBDatabaseIntVersion(
1381          transaction->BackingStoreTransaction(),
1382          database_id,
1383          database_->metadata_.int_version)) {
1384    IndexedDBDatabaseError error(
1385        WebKit::WebIDBDatabaseExceptionUnknownError,
1386        ASCIIToUTF16("Internal error writing data to stable storage when "
1387                     "updating version."));
1388    callbacks_->OnError(error);
1389    transaction->Abort(error);
1390    return;
1391  }
1392  DCHECK(!database_->pending_second_half_open_);
1393  database_->pending_second_half_open_.reset(new PendingOpenCall(
1394      callbacks_, database_callbacks_, transaction_id_, version_));
1395  callbacks_->OnUpgradeNeeded(old_version, database_, database_->metadata());
1396}
1397
1398void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) {
1399
1400  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1401    DCHECK(!running_version_change_transaction_);
1402    running_version_change_transaction_ = transaction;
1403  }
1404}
1405
1406void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction) {
1407
1408  DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1409  DCHECK_EQ(transactions_[transaction->id()], transaction);
1410  transactions_.erase(transaction->id());
1411  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1412    DCHECK_EQ(transaction, running_version_change_transaction_);
1413    running_version_change_transaction_ = NULL;
1414  }
1415}
1416
1417void IndexedDBDatabase::TransactionFinishedAndAbortFired(
1418    IndexedDBTransaction* transaction) {
1419  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1420    if (pending_second_half_open_) {
1421      pending_second_half_open_->Callbacks()->OnError(
1422          IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1423                                 "Version change transaction was aborted in "
1424                                 "upgradeneeded event handler."));
1425      pending_second_half_open_.reset();
1426    }
1427    ProcessPendingCalls();
1428  }
1429}
1430
1431void IndexedDBDatabase::TransactionFinishedAndCompleteFired(
1432    IndexedDBTransaction* transaction) {
1433  if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1434    DCHECK(pending_second_half_open_);
1435    if (pending_second_half_open_) {
1436      DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
1437      DCHECK(metadata_.id != kInvalidId);
1438      pending_second_half_open_->Callbacks()->OnSuccess(this, this->metadata());
1439      pending_second_half_open_.reset();
1440    }
1441    ProcessPendingCalls();
1442  }
1443}
1444
1445size_t IndexedDBDatabase::ConnectionCount() const {
1446  // This does not include pending open calls, as those should not block version
1447  // changes and deletes.
1448  return database_callbacks_set_.size();
1449}
1450
1451void IndexedDBDatabase::ProcessPendingCalls() {
1452  if (pending_second_half_open_) {
1453    DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
1454    DCHECK(metadata_.id != kInvalidId);
1455    scoped_ptr<PendingOpenCall> pending_call = pending_second_half_open_.Pass();
1456    pending_call->Callbacks()->OnSuccess(this, this->metadata());
1457    // Fall through when complete, as pending opens may be unblocked.
1458  }
1459
1460  if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1461    DCHECK(pending_run_version_change_transaction_call_->Version() >
1462           metadata_.int_version);
1463    scoped_ptr<PendingOpenCall> pending_call =
1464        pending_run_version_change_transaction_call_.Pass();
1465    RunVersionChangeTransactionFinal(pending_call->Callbacks(),
1466                                     pending_call->DatabaseCallbacks(),
1467                                     pending_call->TransactionId(),
1468                                     pending_call->Version());
1469    DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
1470    // Fall through would be a no-op, since transaction must complete
1471    // asynchronously.
1472    DCHECK(IsDeleteDatabaseBlocked());
1473    DCHECK(IsOpenConnectionBlocked());
1474    return;
1475  }
1476
1477  if (!IsDeleteDatabaseBlocked()) {
1478    PendingDeleteCallList pending_delete_calls;
1479    pending_delete_calls_.swap(pending_delete_calls);
1480    while (!pending_delete_calls.empty()) {
1481      // Only the first delete call will delete the database, but each must fire
1482      // callbacks.
1483      scoped_ptr<PendingDeleteCall> pending_delete_call(
1484          pending_delete_calls.front());
1485      pending_delete_calls.pop_front();
1486      DeleteDatabaseFinal(pending_delete_call->Callbacks());
1487    }
1488    // delete_database_final should never re-queue calls.
1489    DCHECK(pending_delete_calls_.empty());
1490    // Fall through when complete, as pending opens may be unblocked.
1491  }
1492
1493  if (!IsOpenConnectionBlocked()) {
1494    PendingOpenCallList pending_open_calls;
1495    pending_open_calls_.swap(pending_open_calls);
1496    while (!pending_open_calls.empty()) {
1497      scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
1498      pending_open_calls.pop_front();
1499      OpenConnection(pending_open_call->Callbacks(),
1500                     pending_open_call->DatabaseCallbacks(),
1501                     pending_open_call->TransactionId(),
1502                     pending_open_call->Version());
1503    }
1504  }
1505}
1506
1507void IndexedDBDatabase::CreateTransaction(
1508    int64 transaction_id,
1509    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks,
1510    const std::vector<int64>& object_store_ids,
1511    uint16 mode) {
1512
1513  DCHECK(database_callbacks_set_.has(callbacks));
1514
1515  scoped_refptr<IndexedDBTransaction> transaction =
1516      IndexedDBTransaction::Create(
1517          transaction_id,
1518          callbacks,
1519          object_store_ids,
1520          static_cast<indexed_db::TransactionMode>(mode),
1521          this);
1522  DCHECK(transactions_.find(transaction_id) == transactions_.end());
1523  transactions_[transaction_id] = transaction;
1524}
1525
1526bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1527  return !pending_delete_calls_.empty() ||
1528         running_version_change_transaction_ ||
1529         pending_run_version_change_transaction_call_;
1530}
1531
1532void IndexedDBDatabase::OpenConnection(
1533    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1534    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1535    int64 transaction_id,
1536    int64 version) {
1537  DCHECK(backing_store_.get());
1538
1539  // TODO(jsbell): Should have a priority queue so that higher version
1540  // requests are processed first. http://crbug.com/225850
1541  if (IsOpenConnectionBlocked()) {
1542    pending_open_calls_.push_back(new PendingOpenCall(
1543        callbacks, database_callbacks, transaction_id, version));
1544    return;
1545  }
1546
1547  if (metadata_.id == kInvalidId) {
1548    // The database was deleted then immediately re-opened; OpenInternal()
1549    // recreates it in the backing store.
1550    if (OpenInternal()) {
1551      DCHECK_EQ(metadata_.int_version,
1552                IndexedDBDatabaseMetadata::NO_INT_VERSION);
1553    } else {
1554      string16 message;
1555      if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1556        message = ASCIIToUTF16(
1557            "Internal error opening database with no version specified.");
1558      else
1559        message =
1560            ASCIIToUTF16("Internal error opening database with version ") +
1561            Int64ToString16(version);
1562      callbacks->OnError(IndexedDBDatabaseError(
1563          WebKit::WebIDBDatabaseExceptionUnknownError, message));
1564      return;
1565    }
1566  }
1567
1568  // We infer that the database didn't exist from its lack of either type of
1569  // version.
1570  bool is_new_database =
1571      metadata_.version == kNoStringVersion &&
1572      metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1573
1574  if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1575    // For unit tests only - skip upgrade steps. Calling from script with
1576    // DEFAULT_INT_VERSION throws exception.
1577    // TODO(jsbell): Assert that we're executing a unit test.
1578    DCHECK(is_new_database);
1579    database_callbacks_set_.insert(database_callbacks);
1580    callbacks->OnSuccess(this, this->metadata());
1581    return;
1582  }
1583
1584  if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1585    if (!is_new_database) {
1586      database_callbacks_set_.insert(database_callbacks);
1587      callbacks->OnSuccess(this, this->metadata());
1588      return;
1589    }
1590    // Spec says: If no version is specified and no database exists, set
1591    // database version to 1.
1592    version = 1;
1593  }
1594
1595  if (version > metadata_.int_version) {
1596    database_callbacks_set_.insert(database_callbacks);
1597    RunVersionChangeTransaction(
1598        callbacks, database_callbacks, transaction_id, version);
1599    return;
1600  }
1601  if (version < metadata_.int_version) {
1602    callbacks->OnError(IndexedDBDatabaseError(
1603        WebKit::WebIDBDatabaseExceptionVersionError,
1604        ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
1605            ASCIIToUTF16(") is less than the existing version (") +
1606            Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1607    return;
1608  }
1609  DCHECK_EQ(version, metadata_.int_version);
1610  database_callbacks_set_.insert(database_callbacks);
1611  callbacks->OnSuccess(this, this->metadata());
1612}
1613
1614void IndexedDBDatabase::RunVersionChangeTransaction(
1615    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1616    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1617    int64 transaction_id,
1618    int64 requested_version) {
1619
1620  DCHECK(callbacks);
1621  DCHECK(database_callbacks_set_.has(database_callbacks));
1622  if (ConnectionCount() > 1) {
1623    // Front end ensures the event is not fired at connections that have
1624    // close_pending set.
1625    for (DatabaseCallbacksSet::const_iterator it =
1626             database_callbacks_set_.begin();
1627         it != database_callbacks_set_.end();
1628         ++it) {
1629      if (*it != database_callbacks.get())
1630        (*it)->OnVersionChange(metadata_.int_version, requested_version);
1631    }
1632    // TODO(jsbell): Remove the call to on_blocked and instead wait
1633    // until the frontend tells us that all the "versionchange" events
1634    // have been delivered.  http://crbug.com/100123
1635    callbacks->OnBlocked(metadata_.int_version);
1636
1637    DCHECK(!pending_run_version_change_transaction_call_);
1638    pending_run_version_change_transaction_call_.reset(new PendingOpenCall(
1639        callbacks, database_callbacks, transaction_id, requested_version));
1640    return;
1641  }
1642  RunVersionChangeTransactionFinal(
1643      callbacks, database_callbacks, transaction_id, requested_version);
1644}
1645
1646void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1647    scoped_refptr<IndexedDBCallbacksWrapper> callbacks,
1648    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> database_callbacks,
1649    int64 transaction_id,
1650    int64 requested_version) {
1651
1652  std::vector<int64> object_store_ids;
1653  CreateTransaction(transaction_id,
1654                    database_callbacks,
1655                    object_store_ids,
1656                    indexed_db::TRANSACTION_VERSION_CHANGE);
1657  scoped_refptr<IndexedDBTransaction> transaction =
1658      transactions_[transaction_id];
1659
1660  transaction->ScheduleTask(
1661      new VersionChangeOperation(this,
1662                                 transaction_id,
1663                                 requested_version,
1664                                 callbacks,
1665                                 database_callbacks),
1666      new VersionChangeAbortOperation(
1667          this, metadata_.version, metadata_.int_version));
1668
1669  DCHECK(!pending_second_half_open_);
1670}
1671
1672void IndexedDBDatabase::DeleteDatabase(
1673    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1674
1675  if (IsDeleteDatabaseBlocked()) {
1676    for (DatabaseCallbacksSet::const_iterator it =
1677             database_callbacks_set_.begin();
1678         it != database_callbacks_set_.end();
1679         ++it) {
1680      // Front end ensures the event is not fired at connections that have
1681      // close_pending set.
1682      (*it)->OnVersionChange(metadata_.int_version,
1683                             IndexedDBDatabaseMetadata::NO_INT_VERSION);
1684    }
1685    // TODO(jsbell): Only fire on_blocked if there are open
1686    // connections after the VersionChangeEvents are received, not
1687    // just set up to fire.  http://crbug.com/100123
1688    callbacks->OnBlocked(metadata_.int_version);
1689    pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1690    return;
1691  }
1692  DeleteDatabaseFinal(callbacks);
1693}
1694
1695bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1696  return !!ConnectionCount();
1697}
1698
1699void IndexedDBDatabase::DeleteDatabaseFinal(
1700    scoped_refptr<IndexedDBCallbacksWrapper> callbacks) {
1701  DCHECK(!IsDeleteDatabaseBlocked());
1702  DCHECK(backing_store_);
1703  if (!backing_store_->DeleteDatabase(metadata_.name)) {
1704    callbacks->OnError(
1705        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1706                               "Internal error deleting database."));
1707    return;
1708  }
1709  metadata_.version = kNoStringVersion;
1710  metadata_.id = kInvalidId;
1711  metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1712  metadata_.object_stores.clear();
1713  callbacks->OnSuccess();
1714}
1715
1716void IndexedDBDatabase::Close(
1717    scoped_refptr<IndexedDBDatabaseCallbacksWrapper> callbacks) {
1718  DCHECK(callbacks);
1719  DCHECK(database_callbacks_set_.has(callbacks));
1720
1721  // Close outstanding transactions from the closing connection. This
1722  // can not happen if the close is requested by the connection itself
1723  // as the front-end defers the close until all transactions are
1724  // complete, so something unusual has happened e.g. unexpected
1725  // process termination.
1726  {
1727    TransactionMap transactions(transactions_);
1728    for (TransactionMap::const_iterator it = transactions.begin(),
1729                                        end = transactions.end();
1730         it != end;
1731         ++it) {
1732      if (it->second->connection() == callbacks)
1733        it->second->Abort(
1734            IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1735                                   "Connection is closing."));
1736    }
1737  }
1738
1739  database_callbacks_set_.erase(callbacks);
1740  if (pending_second_half_open_ &&
1741      pending_second_half_open_->DatabaseCallbacks() == callbacks) {
1742    pending_second_half_open_->Callbacks()->OnError(
1743        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1744                               "The connection was closed."));
1745    pending_second_half_open_.reset();
1746  }
1747
1748  // process_pending_calls allows the inspector to process a pending open call
1749  // and call close, reentering IndexedDBDatabase::close. Then the
1750  // backend would be removed both by the inspector closing its connection, and
1751  // by the connection that first called close.
1752  // To avoid that situation, don't proceed in case of reentrancy.
1753  if (closing_connection_)
1754    return;
1755  base::AutoReset<bool> ClosingConnection(&closing_connection_, true);
1756  ProcessPendingCalls();
1757
1758  // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1759  if (!ConnectionCount() && !pending_open_calls_.size() &&
1760      !pending_delete_calls_.size()) {
1761    DCHECK(transactions_.empty());
1762
1763    backing_store_ = NULL;
1764
1765    // This check should only be false in unit tests.
1766    // TODO(jsbell): Assert factory_ || we're executing a unit test.
1767    if (factory_)
1768      factory_->RemoveIDBDatabaseBackend(identifier_);
1769  }
1770}
1771
1772void CreateObjectStoreAbortOperation::Perform(
1773    IndexedDBTransaction* transaction) {
1774  IDB_TRACE("CreateObjectStoreAbortOperation");
1775  DCHECK(!transaction);
1776  database_->RemoveObjectStore(object_store_id_);
1777}
1778
1779void DeleteObjectStoreAbortOperation::Perform(
1780    IndexedDBTransaction* transaction) {
1781  IDB_TRACE("DeleteObjectStoreAbortOperation");
1782  DCHECK(!transaction);
1783  database_->AddObjectStore(object_store_metadata_,
1784                            IndexedDBObjectStoreMetadata::kInvalidId);
1785}
1786
1787void IndexedDBDatabase::VersionChangeAbortOperation::Perform(
1788    IndexedDBTransaction* transaction) {
1789  IDB_TRACE("VersionChangeAbortOperation");
1790  DCHECK(!transaction);
1791  database_->metadata_.version = previous_version_;
1792  database_->metadata_.int_version = previous_int_version_;
1793}
1794
1795}  // namespace content
1796