1// Copyright 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/child/indexed_db/webidbcursor_impl.h"
6
7#include <vector>
8
9#include "content/child/indexed_db/indexed_db_dispatcher.h"
10#include "content/child/indexed_db/indexed_db_key_builders.h"
11#include "content/child/thread_safe_sender.h"
12#include "content/common/indexed_db/indexed_db_messages.h"
13
14using blink::WebData;
15using blink::WebIDBCallbacks;
16using blink::WebIDBKey;
17
18namespace content {
19
20WebIDBCursorImpl::WebIDBCursorImpl(int32 ipc_cursor_id,
21                                   int64 transaction_id,
22                                   ThreadSafeSender* thread_safe_sender)
23    : ipc_cursor_id_(ipc_cursor_id),
24      transaction_id_(transaction_id),
25      continue_count_(0),
26      used_prefetches_(0),
27      pending_onsuccess_callbacks_(0),
28      prefetch_amount_(kMinPrefetchAmount),
29      thread_safe_sender_(thread_safe_sender) {}
30
31WebIDBCursorImpl::~WebIDBCursorImpl() {
32  // It's not possible for there to be pending callbacks that address this
33  // object since inside WebKit, they hold a reference to the object which owns
34  // this object. But, if that ever changed, then we'd need to invalidate
35  // any such pointers.
36
37  if (ipc_cursor_id_ != kInvalidCursorId) {
38    // Invalid ID used in tests to avoid really sending this message.
39    thread_safe_sender_->Send(
40        new IndexedDBHostMsg_CursorDestroyed(ipc_cursor_id_));
41  }
42  IndexedDBDispatcher* dispatcher =
43      IndexedDBDispatcher::ThreadSpecificInstance(thread_safe_sender_.get());
44  dispatcher->CursorDestroyed(ipc_cursor_id_);
45}
46
47void WebIDBCursorImpl::advance(unsigned long count,
48                               WebIDBCallbacks* callbacks_ptr) {
49  IndexedDBDispatcher* dispatcher =
50      IndexedDBDispatcher::ThreadSpecificInstance(thread_safe_sender_.get());
51  scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
52  if (count <= prefetch_keys_.size()) {
53    CachedAdvance(count, callbacks.get());
54    return;
55  }
56  ResetPrefetchCache();
57  dispatcher->RequestIDBCursorAdvance(
58      count, callbacks.release(), ipc_cursor_id_, transaction_id_);
59}
60
61void WebIDBCursorImpl::continueFunction(const WebIDBKey& key,
62                                        WebIDBCallbacks* callbacks_ptr) {
63  continueFunction(key, WebIDBKey::createNull(), callbacks_ptr);
64}
65
66void WebIDBCursorImpl::continueFunction(const WebIDBKey& key,
67                                        const WebIDBKey& primary_key,
68                                        WebIDBCallbacks* callbacks_ptr) {
69  IndexedDBDispatcher* dispatcher =
70      IndexedDBDispatcher::ThreadSpecificInstance(thread_safe_sender_.get());
71  scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr);
72
73  if (key.keyType() == blink::WebIDBKeyTypeNull &&
74      primary_key.keyType() == blink::WebIDBKeyTypeNull) {
75    // No key(s), so this would qualify for a prefetch.
76    ++continue_count_;
77
78    if (!prefetch_keys_.empty()) {
79      // We have a prefetch cache, so serve the result from that.
80      CachedContinue(callbacks.get());
81      return;
82    }
83
84    if (continue_count_ > kPrefetchContinueThreshold) {
85      // Request pre-fetch.
86      ++pending_onsuccess_callbacks_;
87      dispatcher->RequestIDBCursorPrefetch(
88          prefetch_amount_, callbacks.release(), ipc_cursor_id_);
89
90      // Increase prefetch_amount_ exponentially.
91      prefetch_amount_ *= 2;
92      if (prefetch_amount_ > kMaxPrefetchAmount)
93        prefetch_amount_ = kMaxPrefetchAmount;
94
95      return;
96    }
97  } else {
98    // Key argument supplied. We couldn't prefetch this.
99    ResetPrefetchCache();
100  }
101
102  dispatcher->RequestIDBCursorContinue(IndexedDBKeyBuilder::Build(key),
103                                       IndexedDBKeyBuilder::Build(primary_key),
104                                       callbacks.release(),
105                                       ipc_cursor_id_,
106                                       transaction_id_);
107}
108
109void WebIDBCursorImpl::postSuccessHandlerCallback() {
110  pending_onsuccess_callbacks_--;
111
112  // If the onsuccess callback called continue()/advance() on the cursor
113  // again, and that request was served by the prefetch cache, then
114  // pending_onsuccess_callbacks_ would be incremented. If not, it means the
115  // callback did something else, or nothing at all, in which case we need to
116  // reset the cache.
117
118  if (pending_onsuccess_callbacks_ == 0)
119    ResetPrefetchCache();
120}
121
122void WebIDBCursorImpl::SetPrefetchData(
123    const std::vector<IndexedDBKey>& keys,
124    const std::vector<IndexedDBKey>& primary_keys,
125    const std::vector<WebData>& values,
126    const std::vector<blink::WebVector<blink::WebBlobInfo> >& blob_info) {
127  prefetch_keys_.assign(keys.begin(), keys.end());
128  prefetch_primary_keys_.assign(primary_keys.begin(), primary_keys.end());
129  prefetch_values_.assign(values.begin(), values.end());
130  prefetch_blob_info_.assign(blob_info.begin(), blob_info.end());
131
132  used_prefetches_ = 0;
133  pending_onsuccess_callbacks_ = 0;
134}
135
136void WebIDBCursorImpl::CachedAdvance(unsigned long count,
137                                     WebIDBCallbacks* callbacks) {
138  DCHECK_GE(prefetch_keys_.size(), count);
139  DCHECK_EQ(prefetch_primary_keys_.size(), prefetch_keys_.size());
140  DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
141  DCHECK_EQ(prefetch_blob_info_.size(), prefetch_keys_.size());
142
143  while (count > 1) {
144    prefetch_keys_.pop_front();
145    prefetch_primary_keys_.pop_front();
146    prefetch_values_.pop_front();
147    prefetch_blob_info_.pop_front();
148    ++used_prefetches_;
149    --count;
150  }
151
152  CachedContinue(callbacks);
153}
154
155void WebIDBCursorImpl::CachedContinue(WebIDBCallbacks* callbacks) {
156  DCHECK_GT(prefetch_keys_.size(), 0ul);
157  DCHECK_EQ(prefetch_primary_keys_.size(), prefetch_keys_.size());
158  DCHECK_EQ(prefetch_values_.size(), prefetch_keys_.size());
159  DCHECK_EQ(prefetch_blob_info_.size(), prefetch_keys_.size());
160
161  IndexedDBKey key = prefetch_keys_.front();
162  IndexedDBKey primary_key = prefetch_primary_keys_.front();
163  WebData value = prefetch_values_.front();
164  blink::WebVector<blink::WebBlobInfo> blob_info = prefetch_blob_info_.front();
165
166  prefetch_keys_.pop_front();
167  prefetch_primary_keys_.pop_front();
168  prefetch_values_.pop_front();
169  prefetch_blob_info_.pop_front();
170  ++used_prefetches_;
171
172  ++pending_onsuccess_callbacks_;
173
174  if (!continue_count_) {
175    // The cache was invalidated by a call to ResetPrefetchCache()
176    // after the RequestIDBCursorPrefetch() was made. Now that the
177    // initiating continue() call has been satisfied, discard
178    // the rest of the cache.
179    ResetPrefetchCache();
180  }
181
182  callbacks->onSuccess(WebIDBKeyBuilder::Build(key),
183                       WebIDBKeyBuilder::Build(primary_key),
184                       value,
185                       blob_info);
186}
187
188void WebIDBCursorImpl::ResetPrefetchCache() {
189  continue_count_ = 0;
190  prefetch_amount_ = kMinPrefetchAmount;
191
192  if (!prefetch_keys_.size()) {
193    // No prefetch cache, so no need to reset the cursor in the back-end.
194    return;
195  }
196
197  IndexedDBDispatcher* dispatcher =
198      IndexedDBDispatcher::ThreadSpecificInstance(thread_safe_sender_.get());
199  dispatcher->RequestIDBCursorPrefetchReset(
200      used_prefetches_, prefetch_keys_.size(), ipc_cursor_id_);
201  prefetch_keys_.clear();
202  prefetch_primary_keys_.clear();
203  prefetch_values_.clear();
204  prefetch_blob_info_.clear();
205
206  pending_onsuccess_callbacks_ = 0;
207}
208
209}  // namespace content
210