Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: third_party/WebKit/Source/modules/indexeddb/IDBTransactionTest.cpp

Issue 2822453003: Wrap large IndexedDB values into Blobs before writing to LevelDB. (Closed)
Patch Set: Addressed last round of feedback. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 18 matching lines...) Expand all
29 */ 29 */
30 30
31 #include "modules/indexeddb/IDBTransaction.h" 31 #include "modules/indexeddb/IDBTransaction.h"
32 32
33 #include <memory> 33 #include <memory>
34 34
35 #include "bindings/core/v8/V8BindingForTesting.h" 35 #include "bindings/core/v8/V8BindingForTesting.h"
36 #include "core/dom/DOMException.h" 36 #include "core/dom/DOMException.h"
37 #include "core/dom/Document.h" 37 #include "core/dom/Document.h"
38 #include "core/dom/ExceptionCode.h" 38 #include "core/dom/ExceptionCode.h"
39 #include "core/events/EventQueue.h"
39 #include "modules/indexeddb/IDBDatabase.h" 40 #include "modules/indexeddb/IDBDatabase.h"
40 #include "modules/indexeddb/IDBDatabaseCallbacks.h" 41 #include "modules/indexeddb/IDBDatabaseCallbacks.h"
42 #include "modules/indexeddb/IDBValue.h"
43 #include "modules/indexeddb/IDBValueWrapping.h"
41 #include "modules/indexeddb/MockWebIDBDatabase.h" 44 #include "modules/indexeddb/MockWebIDBDatabase.h"
42 #include "platform/SharedBuffer.h" 45 #include "platform/SharedBuffer.h"
46 #include "platform/wtf/PtrUtil.h"
47 #include "platform/wtf/RefPtr.h"
48 #include "platform/wtf/Vector.h"
49 #include "public/platform/Platform.h"
50 #include "public/platform/WebURLLoaderMockFactory.h"
51 #include "public/platform/WebURLResponse.h"
43 #include "testing/gtest/include/gtest/gtest.h" 52 #include "testing/gtest/include/gtest/gtest.h"
44 #include "v8/include/v8.h" 53 #include "v8/include/v8.h"
45 54
46 namespace blink { 55 namespace blink {
47 namespace { 56 namespace {
48 57
49 void DeactivateNewTransactions(v8::Isolate* isolate) { 58 void DeactivateNewTransactions(v8::Isolate* isolate) {
50 V8PerIsolateData::From(isolate)->RunEndOfScopeTasks(); 59 V8PerIsolateData::From(isolate)->RunEndOfScopeTasks();
51 } 60 }
52 61
53 class FakeIDBDatabaseCallbacks final : public IDBDatabaseCallbacks { 62 class FakeIDBDatabaseCallbacks final : public IDBDatabaseCallbacks {
54 public: 63 public:
55 static FakeIDBDatabaseCallbacks* Create() { 64 static FakeIDBDatabaseCallbacks* Create() {
56 return new FakeIDBDatabaseCallbacks(); 65 return new FakeIDBDatabaseCallbacks();
57 } 66 }
58 void OnVersionChange(int64_t old_version, int64_t new_version) override {} 67 void OnVersionChange(int64_t old_version, int64_t new_version) override {}
59 void OnForcedClose() override {} 68 void OnForcedClose() override {}
60 void OnAbort(int64_t transaction_id, DOMException* error) override {} 69 void OnAbort(int64_t transaction_id, DOMException* error) override {}
61 void OnComplete(int64_t transaction_id) override {} 70 void OnComplete(int64_t transaction_id) override {}
62 71
63 private: 72 private:
64 FakeIDBDatabaseCallbacks() {} 73 FakeIDBDatabaseCallbacks() {}
65 }; 74 };
66 75
67 TEST(IDBTransactionTest, EnsureLifetime) { 76 class IDBTransactionTest : public ::testing::Test {
77 protected:
78 void SetUp() override {
79 url_loader_mock_factory_ = Platform::Current()->GetURLLoaderMockFactory();
80 WebURLResponse response;
81 response.SetURL(KURL(KURL(), "blob:"));
82 url_loader_mock_factory_->RegisterURLProtocol(WebString("blob"), response,
83 "");
84 }
85
86 void TearDown() override {
87 url_loader_mock_factory_->UnregisterAllURLsAndClearMemoryCache();
88 }
89
90 void BuildTransaction(V8TestingScope& scope,
91 std::unique_ptr<MockWebIDBDatabase> backend) {
92 db_ = IDBDatabase::Create(scope.GetExecutionContext(), std::move(backend),
93 FakeIDBDatabaseCallbacks::Create(),
94 scope.GetIsolate());
95
96 HashSet<String> transaction_scope = {"store"};
97 transaction_ = IDBTransaction::CreateNonVersionChange(
98 scope.GetScriptState(), kTransactionId, transaction_scope,
99 kWebIDBTransactionModeReadOnly, db_.Get());
100 }
101
102 WebURLLoaderMockFactory* url_loader_mock_factory_;
103 Persistent<IDBDatabase> db_;
104 Persistent<IDBTransaction> transaction_;
105
106 static constexpr int64_t kTransactionId = 1234;
107 };
108
109 // The created value is an array of true. If create_wrapped_value is true, the
110 // IDBValue's byte array will be wrapped in a Blob, otherwise it will not be.
111 RefPtr<IDBValue> CreateIDBValue(v8::Isolate* isolate,
112 bool create_wrapped_value) {
113 size_t element_count = create_wrapped_value ? 16 : 2;
114 v8::Local<v8::Array> v8_array = v8::Array::New(isolate, element_count);
115 for (size_t i = 0; i < element_count; ++i)
116 v8_array->Set(i, v8::True(isolate));
117
118 NonThrowableExceptionState non_throwable_exception_state;
119 IDBValueWrapper wrapper(isolate, v8_array,
120 SerializedScriptValue::SerializeOptions::kSerialize,
121 non_throwable_exception_state);
122 wrapper.WrapIfBiggerThan(create_wrapped_value ? 0 : 1024 * element_count);
123
124 std::unique_ptr<Vector<RefPtr<BlobDataHandle>>> blob_data_handles =
125 WTF::MakeUnique<Vector<RefPtr<BlobDataHandle>>>();
126 wrapper.ExtractBlobDataHandles(blob_data_handles.get());
127 Vector<WebBlobInfo>& blob_infos = wrapper.WrappedBlobInfo();
128 RefPtr<SharedBuffer> wrapped_marker_buffer = wrapper.ExtractWireBytes();
129
130 RefPtr<IDBValue> idb_value =
131 IDBValue::Create(wrapped_marker_buffer, std::move(blob_data_handles),
132 WTF::MakeUnique<Vector<WebBlobInfo>>(blob_infos));
133
134 DCHECK_EQ(create_wrapped_value,
135 IDBValueUnwrapper::IsWrapped(idb_value.Get()));
136 return idb_value;
137 }
138
139 TEST_F(IDBTransactionTest, ContextDestroyedEarlyDeath) {
68 V8TestingScope scope; 140 V8TestingScope scope;
69 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create(); 141 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
70 EXPECT_CALL(*backend, Close()).Times(1); 142 EXPECT_CALL(*backend, Close()).Times(1);
71 Persistent<IDBDatabase> db = IDBDatabase::Create( 143 BuildTransaction(scope, std::move(backend));
72 scope.GetExecutionContext(), std::move(backend),
73 FakeIDBDatabaseCallbacks::Create(), scope.GetIsolate());
74 144
75 const int64_t kTransactionId = 1234;
76 HashSet<String> transaction_scope = HashSet<String>();
77 transaction_scope.insert("test-store-name");
78 Persistent<IDBTransaction> transaction =
79 IDBTransaction::CreateNonVersionChange(
80 scope.GetScriptState(), kTransactionId, transaction_scope,
81 kWebIDBTransactionModeReadOnly, db.Get());
82 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions; 145 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
83 live_transactions.insert(transaction); 146 live_transactions.insert(transaction_);
84 147
85 ThreadState::Current()->CollectAllGarbage(); 148 ThreadState::Current()->CollectAllGarbage();
86 EXPECT_EQ(1u, live_transactions.size()); 149 EXPECT_EQ(1u, live_transactions.size());
87 150
88 Persistent<IDBRequest> request = IDBRequest::Create( 151 Persistent<IDBRequest> request = IDBRequest::Create(
89 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction.Get()); 152 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction_.Get());
90 DeactivateNewTransactions(scope.GetIsolate()); 153 DeactivateNewTransactions(scope.GetIsolate());
91 154
92 request.Clear(); // The transaction is holding onto the request. 155 request.Clear(); // The transaction is holding onto the request.
93 ThreadState::Current()->CollectAllGarbage(); 156 ThreadState::Current()->CollectAllGarbage();
94 EXPECT_EQ(1u, live_transactions.size()); 157 EXPECT_EQ(1u, live_transactions.size());
95 158
96 // This will generate an Abort() call to the back end which is dropped by the 159 // This will generate an Abort() call to the back end which is dropped by the
97 // fake proxy, so an explicit OnAbort call is made. 160 // fake proxy, so an explicit OnAbort call is made.
98 scope.GetExecutionContext()->NotifyContextDestroyed(); 161 scope.GetExecutionContext()->NotifyContextDestroyed();
99 transaction->OnAbort(DOMException::Create(kAbortError, "Aborted")); 162 transaction_->OnAbort(DOMException::Create(kAbortError, "Aborted"));
100 transaction.Clear(); 163 transaction_.Clear();
101 164
102 ThreadState::Current()->CollectAllGarbage(); 165 ThreadState::Current()->CollectAllGarbage();
103 EXPECT_EQ(0u, live_transactions.size()); 166 EXPECT_EQ(0U, live_transactions.size());
104 } 167 }
105 168
106 TEST(IDBTransactionTest, TransactionFinish) { 169 TEST_F(IDBTransactionTest, ContextDestroyedAfterDone) {
107 V8TestingScope scope; 170 V8TestingScope scope;
108 const int64_t kTransactionId = 1234; 171 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
172 EXPECT_CALL(*backend, Close()).Times(1);
173 BuildTransaction(scope, std::move(backend));
109 174
175 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
176 live_transactions.insert(transaction_);
177
178 ThreadState::Current()->CollectAllGarbage();
179 EXPECT_EQ(1U, live_transactions.size());
180
181 Persistent<IDBRequest> request = IDBRequest::Create(
182 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction_.Get());
183 DeactivateNewTransactions(scope.GetIsolate());
184
185 // This response should result in an event being enqueued immediately.
186 request->HandleResponse(CreateIDBValue(scope.GetIsolate(), false));
187
188 request.Clear(); // The transaction is holding onto the request.
189 ThreadState::Current()->CollectAllGarbage();
190 EXPECT_EQ(1U, live_transactions.size());
191
192 // This will generate an Abort() call to the back end which is dropped by the
193 // fake proxy, so an explicit OnAbort call is made.
194 scope.GetExecutionContext()->NotifyContextDestroyed();
195 transaction_->OnAbort(DOMException::Create(kAbortError, "Aborted"));
196 transaction_.Clear();
197
198 // The request completed, so it has enqueued a success event. Discard the
199 // event, so that the transaction can go away.
200 EXPECT_EQ(1U, live_transactions.size());
201 scope.GetExecutionContext()->GetEventQueue()->Close();
202
203 ThreadState::Current()->CollectAllGarbage();
204 EXPECT_EQ(0U, live_transactions.size());
205 }
206
207 TEST_F(IDBTransactionTest, ContextDestroyedWithQueuedResult) {
208 V8TestingScope scope;
209 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
210 EXPECT_CALL(*backend, Close()).Times(1);
211 BuildTransaction(scope, std::move(backend));
212
213 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
214 live_transactions.insert(transaction_);
215
216 ThreadState::Current()->CollectAllGarbage();
217 EXPECT_EQ(1U, live_transactions.size());
218
219 Persistent<IDBRequest> request = IDBRequest::Create(
220 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction_.Get());
221 DeactivateNewTransactions(scope.GetIsolate());
222
223 request->HandleResponse(CreateIDBValue(scope.GetIsolate(), true));
224
225 request.Clear(); // The transaction is holding onto the request.
226 ThreadState::Current()->CollectAllGarbage();
227 EXPECT_EQ(1U, live_transactions.size());
228
229 // This will generate an Abort() call to the back end which is dropped by the
230 // fake proxy, so an explicit OnAbort call is made.
231 scope.GetExecutionContext()->NotifyContextDestroyed();
232 transaction_->OnAbort(DOMException::Create(kAbortError, "Aborted"));
233 transaction_.Clear();
234
235 url_loader_mock_factory_->ServeAsynchronousRequests();
236
237 ThreadState::Current()->CollectAllGarbage();
238 EXPECT_EQ(0U, live_transactions.size());
239 }
240
241 TEST_F(IDBTransactionTest, ContextDestroyedWithTwoQueuedResults) {
242 V8TestingScope scope;
243 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
244 EXPECT_CALL(*backend, Close()).Times(1);
245 BuildTransaction(scope, std::move(backend));
246
247 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
248 live_transactions.insert(transaction_);
249
250 ThreadState::Current()->CollectAllGarbage();
251 EXPECT_EQ(1U, live_transactions.size());
252
253 Persistent<IDBRequest> request1 = IDBRequest::Create(
254 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction_.Get());
255 Persistent<IDBRequest> request2 = IDBRequest::Create(
256 scope.GetScriptState(), IDBAny::CreateUndefined(), transaction_.Get());
257 DeactivateNewTransactions(scope.GetIsolate());
258
259 request1->HandleResponse(CreateIDBValue(scope.GetIsolate(), true));
260 request2->HandleResponse(CreateIDBValue(scope.GetIsolate(), true));
261
262 request1.Clear(); // The transaction is holding onto the requests.
263 request2.Clear();
264 ThreadState::Current()->CollectAllGarbage();
265 EXPECT_EQ(1U, live_transactions.size());
266
267 // This will generate an Abort() call to the back end which is dropped by the
268 // fake proxy, so an explicit OnAbort call is made.
269 scope.GetExecutionContext()->NotifyContextDestroyed();
270 transaction_->OnAbort(DOMException::Create(kAbortError, "Aborted"));
271 transaction_.Clear();
272
273 url_loader_mock_factory_->ServeAsynchronousRequests();
274
275 ThreadState::Current()->CollectAllGarbage();
276 EXPECT_EQ(0U, live_transactions.size());
277 }
278
279 TEST_F(IDBTransactionTest, TransactionFinish) {
280 V8TestingScope scope;
110 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create(); 281 std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
111 EXPECT_CALL(*backend, Commit(kTransactionId)).Times(1); 282 EXPECT_CALL(*backend, Commit(kTransactionId)).Times(1);
112 EXPECT_CALL(*backend, Close()).Times(1); 283 EXPECT_CALL(*backend, Close()).Times(1);
113 Persistent<IDBDatabase> db = IDBDatabase::Create( 284 BuildTransaction(scope, std::move(backend));
114 scope.GetExecutionContext(), std::move(backend),
115 FakeIDBDatabaseCallbacks::Create(), scope.GetIsolate());
116 285
117 HashSet<String> transaction_scope = HashSet<String>();
118 transaction_scope.insert("test-store-name");
119 Persistent<IDBTransaction> transaction =
120 IDBTransaction::CreateNonVersionChange(
121 scope.GetScriptState(), kTransactionId, transaction_scope,
122 kWebIDBTransactionModeReadOnly, db.Get());
123 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions; 286 PersistentHeapHashSet<WeakMember<IDBTransaction>> live_transactions;
124 live_transactions.insert(transaction); 287 live_transactions.insert(transaction_);
125 288
126 ThreadState::Current()->CollectAllGarbage(); 289 ThreadState::Current()->CollectAllGarbage();
127 EXPECT_EQ(1u, live_transactions.size()); 290 EXPECT_EQ(1U, live_transactions.size());
128 291
129 DeactivateNewTransactions(scope.GetIsolate()); 292 DeactivateNewTransactions(scope.GetIsolate());
130 293
131 ThreadState::Current()->CollectAllGarbage(); 294 ThreadState::Current()->CollectAllGarbage();
132 EXPECT_EQ(1u, live_transactions.size()); 295 EXPECT_EQ(1U, live_transactions.size());
133 296
134 transaction.Clear(); 297 transaction_.Clear();
135 298
136 ThreadState::Current()->CollectAllGarbage(); 299 ThreadState::Current()->CollectAllGarbage();
137 EXPECT_EQ(1u, live_transactions.size()); 300 EXPECT_EQ(1U, live_transactions.size());
138 301
139 // Stop the context, so events don't get queued (which would keep the 302 // Stop the context, so events don't get queued (which would keep the
140 // transaction alive). 303 // transaction alive).
141 scope.GetExecutionContext()->NotifyContextDestroyed(); 304 scope.GetExecutionContext()->NotifyContextDestroyed();
142 305
143 // Fire an abort to make sure this doesn't free the transaction during use. 306 // Fire an abort to make sure this doesn't free the transaction during use.
144 // The test will not fail if it is, but ASAN would notice the error. 307 // The test will not fail if it is, but ASAN would notice the error.
145 db->OnAbort(kTransactionId, DOMException::Create(kAbortError, "Aborted")); 308 db_->OnAbort(kTransactionId, DOMException::Create(kAbortError, "Aborted"));
146 309
147 // OnAbort() should have cleared the transaction's reference to the database. 310 // OnAbort() should have cleared the transaction's reference to the database.
148 ThreadState::Current()->CollectAllGarbage(); 311 ThreadState::Current()->CollectAllGarbage();
149 EXPECT_EQ(0u, live_transactions.size()); 312 EXPECT_EQ(0U, live_transactions.size());
150 } 313 }
151 314
152 } // namespace 315 } // namespace
153 } // namespace blink 316 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp ('k') | third_party/WebKit/Source/modules/indexeddb/IDBValue.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698