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 "base/memory/scoped_ptr.h"
6 #include "base/message_loop/message_loop_proxy.h"
7 #include "base/values.h"
8 #include "content/child/indexed_db/indexed_db_dispatcher.h"
9 #include "content/child/indexed_db/webidbcursor_impl.h"
10 #include "content/child/thread_safe_sender.h"
11 #include "content/common/indexed_db/indexed_db_key.h"
12 #include "content/common/indexed_db/indexed_db_key_range.h"
13 #include "content/common/indexed_db/indexed_db_messages.h"
14 #include "ipc/ipc_sync_message_filter.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/WebKit/public/platform/WebBlobInfo.h"
17 #include "third_party/WebKit/public/platform/WebData.h"
18 #include "third_party/WebKit/public/platform/WebIDBCallbacks.h"
19
20 using blink::WebBlobInfo;
21 using blink::WebData;
22 using blink::WebIDBCallbacks;
23 using blink::WebIDBCursor;
24 using blink::WebIDBDatabase;
25 using blink::WebIDBDatabaseError;
26 using blink::WebIDBKey;
27 using blink::WebVector;
28
29 namespace content {
30 namespace {
31
32 class MockCallbacks : public WebIDBCallbacks {
33 public:
MockCallbacks()34 MockCallbacks() : error_seen_(false) {}
35
onError(const WebIDBDatabaseError &)36 virtual void onError(const WebIDBDatabaseError&) { error_seen_ = true; }
37
error_seen() const38 bool error_seen() const { return error_seen_; }
39
40 private:
41 bool error_seen_;
42
43 DISALLOW_COPY_AND_ASSIGN(MockCallbacks);
44 };
45
46 class MockDispatcher : public IndexedDBDispatcher {
47 public:
MockDispatcher(ThreadSafeSender * sender)48 explicit MockDispatcher(ThreadSafeSender* sender)
49 : IndexedDBDispatcher(sender) {}
50
Send(IPC::Message * msg)51 virtual bool Send(IPC::Message* msg) OVERRIDE {
52 delete msg;
53 return true;
54 }
55
56 private:
57 DISALLOW_COPY_AND_ASSIGN(MockDispatcher);
58 };
59
60 } // namespace
61
62 class IndexedDBDispatcherTest : public testing::Test {
63 public:
IndexedDBDispatcherTest()64 IndexedDBDispatcherTest()
65 : message_loop_proxy_(base::MessageLoopProxy::current()),
66 sync_message_filter_(new IPC::SyncMessageFilter(NULL)),
67 thread_safe_sender_(new ThreadSafeSender(message_loop_proxy_.get(),
68 sync_message_filter_.get())) {}
69
70 protected:
71 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
72 scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_;
73 scoped_refptr<ThreadSafeSender> thread_safe_sender_;
74
75 private:
76 DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcherTest);
77 };
78
TEST_F(IndexedDBDispatcherTest,ValueSizeTest)79 TEST_F(IndexedDBDispatcherTest, ValueSizeTest) {
80 const std::vector<char> data(kMaxIDBValueSizeInBytes + 1);
81 const WebData value(&data.front(), data.size());
82 const WebVector<WebBlobInfo> web_blob_info;
83 const int32 ipc_dummy_id = -1;
84 const int64 transaction_id = 1;
85 const int64 object_store_id = 2;
86
87 MockCallbacks callbacks;
88 IndexedDBDispatcher dispatcher(thread_safe_sender_.get());
89 IndexedDBKey key(0, blink::WebIDBKeyTypeNumber);
90 dispatcher.RequestIDBDatabasePut(ipc_dummy_id,
91 transaction_id,
92 object_store_id,
93 value,
94 web_blob_info,
95 key,
96 WebIDBDatabase::AddOrUpdate,
97 &callbacks,
98 WebVector<long long>(),
99 WebVector<WebVector<WebIDBKey> >());
100
101 EXPECT_TRUE(callbacks.error_seen());
102 }
103
TEST_F(IndexedDBDispatcherTest,KeyAndValueSizeTest)104 TEST_F(IndexedDBDispatcherTest, KeyAndValueSizeTest) {
105 const size_t kKeySize = 1024 * 1024;
106
107 const std::vector<char> data(kMaxIDBValueSizeInBytes - kKeySize);
108 const WebData value(&data.front(), data.size());
109 const WebVector<WebBlobInfo> web_blob_info;
110 const IndexedDBKey key(
111 base::string16(kKeySize / sizeof(base::string16::value_type), 'x'));
112
113 const int32 ipc_dummy_id = -1;
114 const int64 transaction_id = 1;
115 const int64 object_store_id = 2;
116
117 MockCallbacks callbacks;
118 IndexedDBDispatcher dispatcher(thread_safe_sender_.get());
119 dispatcher.RequestIDBDatabasePut(ipc_dummy_id,
120 transaction_id,
121 object_store_id,
122 value,
123 web_blob_info,
124 key,
125 WebIDBDatabase::AddOrUpdate,
126 &callbacks,
127 WebVector<long long>(),
128 WebVector<WebVector<WebIDBKey> >());
129
130 EXPECT_TRUE(callbacks.error_seen());
131 }
132
133 namespace {
134
135 class CursorCallbacks : public WebIDBCallbacks {
136 public:
CursorCallbacks(scoped_ptr<WebIDBCursor> * cursor)137 explicit CursorCallbacks(scoped_ptr<WebIDBCursor>* cursor)
138 : cursor_(cursor) {}
139
onSuccess(const WebData &,const WebVector<WebBlobInfo> &)140 virtual void onSuccess(const WebData&,
141 const WebVector<WebBlobInfo>&) OVERRIDE {}
onSuccess(WebIDBCursor * cursor,const WebIDBKey & key,const WebIDBKey & primaryKey,const WebData & value,const WebVector<WebBlobInfo> &)142 virtual void onSuccess(WebIDBCursor* cursor,
143 const WebIDBKey& key,
144 const WebIDBKey& primaryKey,
145 const WebData& value,
146 const WebVector<WebBlobInfo>&) OVERRIDE {
147 cursor_->reset(cursor);
148 }
149
150 private:
151 scoped_ptr<WebIDBCursor>* cursor_;
152
153 DISALLOW_COPY_AND_ASSIGN(CursorCallbacks);
154 };
155
156 } // namespace
157
TEST_F(IndexedDBDispatcherTest,CursorTransactionId)158 TEST_F(IndexedDBDispatcherTest, CursorTransactionId) {
159 const int32 ipc_database_id = -1;
160 const int64 transaction_id = 1234;
161 const int64 object_store_id = 2;
162 const int32 index_id = 3;
163 const WebIDBCursor::Direction direction = WebIDBCursor::Next;
164 const bool key_only = false;
165
166 MockDispatcher dispatcher(thread_safe_sender_.get());
167
168 // First case: successful cursor open.
169 {
170 scoped_ptr<WebIDBCursor> cursor;
171 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size());
172
173 // Make a cursor request. This should record the transaction id.
174 dispatcher.RequestIDBDatabaseOpenCursor(ipc_database_id,
175 transaction_id,
176 object_store_id,
177 index_id,
178 IndexedDBKeyRange(),
179 direction,
180 key_only,
181 blink::WebIDBDatabase::NormalTask,
182 new CursorCallbacks(&cursor));
183
184 // Verify that the transaction id was captured.
185 EXPECT_EQ(1UL, dispatcher.cursor_transaction_ids_.size());
186 EXPECT_FALSE(cursor.get());
187
188 int32 ipc_callbacks_id = dispatcher.cursor_transaction_ids_.begin()->first;
189
190 IndexedDBMsg_CallbacksSuccessIDBCursor_Params params;
191 params.ipc_thread_id = dispatcher.CurrentWorkerId();
192 params.ipc_callbacks_id = ipc_callbacks_id;
193
194 // Now simululate the cursor response.
195 params.ipc_cursor_id = WebIDBCursorImpl::kInvalidCursorId;
196 dispatcher.OnSuccessOpenCursor(params);
197
198 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size());
199
200 EXPECT_TRUE(cursor.get());
201
202 WebIDBCursorImpl* impl = static_cast<WebIDBCursorImpl*>(cursor.get());
203
204 // This is the primary expectation of this test: the transaction id was
205 // applied to the cursor.
206 EXPECT_EQ(transaction_id, impl->transaction_id());
207 }
208
209 // Second case: null cursor (no data in range)
210 {
211 scoped_ptr<WebIDBCursor> cursor;
212 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size());
213
214 // Make a cursor request. This should record the transaction id.
215 dispatcher.RequestIDBDatabaseOpenCursor(ipc_database_id,
216 transaction_id,
217 object_store_id,
218 index_id,
219 IndexedDBKeyRange(),
220 direction,
221 key_only,
222 blink::WebIDBDatabase::NormalTask,
223 new CursorCallbacks(&cursor));
224
225 // Verify that the transaction id was captured.
226 EXPECT_EQ(1UL, dispatcher.cursor_transaction_ids_.size());
227 EXPECT_FALSE(cursor.get());
228
229 int32 ipc_callbacks_id = dispatcher.cursor_transaction_ids_.begin()->first;
230
231 // Now simululate a "null cursor" response.
232 IndexedDBMsg_CallbacksSuccessValue_Params params;
233 params.ipc_thread_id = dispatcher.CurrentWorkerId();
234 params.ipc_callbacks_id = ipc_callbacks_id;
235 dispatcher.OnSuccessValue(params);
236
237 // Ensure the map result was deleted.
238 EXPECT_EQ(0UL, dispatcher.cursor_transaction_ids_.size());
239 EXPECT_FALSE(cursor.get());
240 }
241 }
242
243 namespace {
244
245 class MockCursor : public WebIDBCursorImpl {
246 public:
MockCursor(int32 ipc_cursor_id,int64 transaction_id,ThreadSafeSender * thread_safe_sender)247 MockCursor(int32 ipc_cursor_id,
248 int64 transaction_id,
249 ThreadSafeSender* thread_safe_sender)
250 : WebIDBCursorImpl(ipc_cursor_id, transaction_id, thread_safe_sender),
251 reset_count_(0) {}
252
253 // This method is virtual so it can be overridden in unit tests.
ResetPrefetchCache()254 virtual void ResetPrefetchCache() OVERRIDE { ++reset_count_; }
255
reset_count() const256 int reset_count() const { return reset_count_; }
257
258 private:
259 int reset_count_;
260
261 DISALLOW_COPY_AND_ASSIGN(MockCursor);
262 };
263
264 } // namespace
265
TEST_F(IndexedDBDispatcherTest,CursorReset)266 TEST_F(IndexedDBDispatcherTest, CursorReset) {
267 scoped_ptr<WebIDBCursor> cursor;
268 MockDispatcher dispatcher(thread_safe_sender_.get());
269
270 const int32 ipc_database_id = 0;
271 const int32 object_store_id = 0;
272 const int32 index_id = 0;
273 const bool key_only = false;
274 const int cursor1_ipc_id = 1;
275 const int cursor2_ipc_id = 2;
276 const int other_cursor_ipc_id = 2;
277 const int cursor1_transaction_id = 1;
278 const int cursor2_transaction_id = 2;
279 const int other_transaction_id = 3;
280
281 scoped_ptr<MockCursor> cursor1(
282 new MockCursor(WebIDBCursorImpl::kInvalidCursorId,
283 cursor1_transaction_id,
284 thread_safe_sender_.get()));
285
286 scoped_ptr<MockCursor> cursor2(
287 new MockCursor(WebIDBCursorImpl::kInvalidCursorId,
288 cursor2_transaction_id,
289 thread_safe_sender_.get()));
290
291 dispatcher.cursors_[cursor1_ipc_id] = cursor1.get();
292 dispatcher.cursors_[cursor2_ipc_id] = cursor2.get();
293
294 EXPECT_EQ(0, cursor1->reset_count());
295 EXPECT_EQ(0, cursor2->reset_count());
296
297 // Other transaction:
298 dispatcher.RequestIDBDatabaseGet(ipc_database_id,
299 other_transaction_id,
300 object_store_id,
301 index_id,
302 IndexedDBKeyRange(),
303 key_only,
304 new MockCallbacks());
305
306 EXPECT_EQ(0, cursor1->reset_count());
307 EXPECT_EQ(0, cursor2->reset_count());
308
309 // Same transaction:
310 dispatcher.RequestIDBDatabaseGet(ipc_database_id,
311 cursor1_transaction_id,
312 object_store_id,
313 index_id,
314 IndexedDBKeyRange(),
315 key_only,
316 new MockCallbacks());
317
318 EXPECT_EQ(1, cursor1->reset_count());
319 EXPECT_EQ(0, cursor2->reset_count());
320
321 // Same transaction and same cursor:
322 dispatcher.RequestIDBCursorContinue(IndexedDBKey(),
323 IndexedDBKey(),
324 new MockCallbacks(),
325 cursor1_ipc_id,
326 cursor1_transaction_id);
327
328 EXPECT_EQ(1, cursor1->reset_count());
329 EXPECT_EQ(0, cursor2->reset_count());
330
331 // Same transaction and different cursor:
332 dispatcher.RequestIDBCursorContinue(IndexedDBKey(),
333 IndexedDBKey(),
334 new MockCallbacks(),
335 other_cursor_ipc_id,
336 cursor1_transaction_id);
337
338 EXPECT_EQ(2, cursor1->reset_count());
339 EXPECT_EQ(0, cursor2->reset_count());
340
341 cursor1.reset();
342 cursor2.reset();
343 }
344
345 } // namespace content
346