indexed_db_cursor.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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_cursor.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "content/browser/indexed_db/indexed_db_callbacks.h"
12#include "content/browser/indexed_db/indexed_db_database_error.h"
13#include "content/browser/indexed_db/indexed_db_tracing.h"
14#include "content/browser/indexed_db/indexed_db_transaction.h"
15#include "content/browser/indexed_db/indexed_db_value.h"
16
17namespace content {
18
19IndexedDBCursor::IndexedDBCursor(
20    scoped_ptr<IndexedDBBackingStore::Cursor> cursor,
21    indexed_db::CursorType cursor_type,
22    IndexedDBDatabase::TaskType task_type,
23    IndexedDBTransaction* transaction)
24    : task_type_(task_type),
25      cursor_type_(cursor_type),
26      transaction_(transaction),
27      cursor_(cursor.Pass()),
28      closed_(false) {
29  transaction_->RegisterOpenCursor(this);
30}
31
32IndexedDBCursor::~IndexedDBCursor() {
33  transaction_->UnregisterOpenCursor(this);
34}
35
36void IndexedDBCursor::Continue(scoped_ptr<IndexedDBKey> key,
37                               scoped_ptr<IndexedDBKey> primary_key,
38                               scoped_refptr<IndexedDBCallbacks> callbacks) {
39  IDB_TRACE("IndexedDBCursor::Continue");
40
41  transaction_->ScheduleTask(
42      task_type_,
43      base::Bind(&IndexedDBCursor::CursorIterationOperation,
44                 this,
45                 base::Passed(&key),
46                 base::Passed(&primary_key),
47                 callbacks));
48}
49
50void IndexedDBCursor::Advance(uint32 count,
51                              scoped_refptr<IndexedDBCallbacks> callbacks) {
52  IDB_TRACE("IndexedDBCursor::Advance");
53
54  transaction_->ScheduleTask(
55      task_type_,
56      base::Bind(
57          &IndexedDBCursor::CursorAdvanceOperation, this, count, callbacks));
58}
59
60void IndexedDBCursor::CursorAdvanceOperation(
61    uint32 count,
62    scoped_refptr<IndexedDBCallbacks> callbacks,
63    IndexedDBTransaction* /*transaction*/) {
64  IDB_TRACE("IndexedDBCursor::CursorAdvanceOperation");
65  leveldb::Status s;
66  // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
67  //                 properly fail, caller will not know why, and any corruption
68  //                 will be ignored.
69  if (!cursor_ || !cursor_->Advance(count, &s)) {
70    cursor_.reset();
71    callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
72    return;
73  }
74
75  callbacks->OnSuccess(key(), primary_key(), Value());
76}
77
78void IndexedDBCursor::CursorIterationOperation(
79    scoped_ptr<IndexedDBKey> key,
80    scoped_ptr<IndexedDBKey> primary_key,
81    scoped_refptr<IndexedDBCallbacks> callbacks,
82    IndexedDBTransaction* /*transaction*/) {
83  IDB_TRACE("IndexedDBCursor::CursorIterationOperation");
84  leveldb::Status s;
85  // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
86  //                 properly fail, caller will not know why, and any corruption
87  //                 will be ignored.
88  if (!cursor_ || !cursor_->Continue(key.get(),
89                                     primary_key.get(),
90                                     IndexedDBBackingStore::Cursor::SEEK,
91                                     &s) || !s.ok()) {
92    cursor_.reset();
93    callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
94    return;
95  }
96
97  callbacks->OnSuccess(this->key(), this->primary_key(), Value());
98}
99
100void IndexedDBCursor::PrefetchContinue(
101    int number_to_fetch,
102    scoped_refptr<IndexedDBCallbacks> callbacks) {
103  IDB_TRACE("IndexedDBCursor::PrefetchContinue");
104
105  transaction_->ScheduleTask(
106      task_type_,
107      base::Bind(&IndexedDBCursor::CursorPrefetchIterationOperation,
108                 this,
109                 number_to_fetch,
110                 callbacks));
111}
112
113void IndexedDBCursor::CursorPrefetchIterationOperation(
114    int number_to_fetch,
115    scoped_refptr<IndexedDBCallbacks> callbacks,
116    IndexedDBTransaction* /*transaction*/) {
117  IDB_TRACE("IndexedDBCursor::CursorPrefetchIterationOperation");
118
119  std::vector<IndexedDBKey> found_keys;
120  std::vector<IndexedDBKey> found_primary_keys;
121  std::vector<IndexedDBValue> found_values;
122
123  saved_cursor_.reset();
124  const size_t max_size_estimate = 10 * 1024 * 1024;
125  size_t size_estimate = 0;
126  leveldb::Status s;
127
128  // TODO(cmumford): Handle this error (crbug.com/363397). Although this will
129  //                 properly fail, caller will not know why, and any corruption
130  //                 will be ignored.
131  for (int i = 0; i < number_to_fetch; ++i) {
132    if (!cursor_ || !cursor_->Continue(&s)) {
133      cursor_.reset();
134      break;
135    }
136
137    if (i == 0) {
138      // First prefetched result is always used, so that's the position
139      // a cursor should be reset to if the prefetch is invalidated.
140      saved_cursor_.reset(cursor_->Clone());
141    }
142
143    found_keys.push_back(cursor_->key());
144    found_primary_keys.push_back(cursor_->primary_key());
145
146    switch (cursor_type_) {
147      case indexed_db::CURSOR_KEY_ONLY:
148        found_values.push_back(IndexedDBValue());
149        break;
150      case indexed_db::CURSOR_KEY_AND_VALUE: {
151        IndexedDBValue value;
152        value.swap(*cursor_->value());
153        size_estimate += value.SizeEstimate();
154        found_values.push_back(value);
155        break;
156      }
157      default:
158        NOTREACHED();
159    }
160    size_estimate += cursor_->key().size_estimate();
161    size_estimate += cursor_->primary_key().size_estimate();
162
163    if (size_estimate > max_size_estimate)
164      break;
165  }
166
167  if (!found_keys.size()) {
168    callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
169    return;
170  }
171
172  callbacks->OnSuccessWithPrefetch(
173      found_keys, found_primary_keys, found_values);
174}
175
176leveldb::Status IndexedDBCursor::PrefetchReset(int used_prefetches,
177                                               int /* unused_prefetches */) {
178  IDB_TRACE("IndexedDBCursor::PrefetchReset");
179  cursor_.swap(saved_cursor_);
180  saved_cursor_.reset();
181  leveldb::Status s;
182
183  if (closed_)
184    return s;
185  if (cursor_) {
186    // First prefetched result is always used.
187    DCHECK_GT(used_prefetches, 0);
188    for (int i = 0; i < used_prefetches - 1; ++i) {
189      bool ok = cursor_->Continue(&s);
190      DCHECK(ok);
191    }
192  }
193
194  return s;
195}
196
197void IndexedDBCursor::Close() {
198  IDB_TRACE("IndexedDBCursor::Close");
199  closed_ = true;
200  cursor_.reset();
201  saved_cursor_.reset();
202}
203
204}  // namespace content
205