1 // Copyright (c) 2012 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/file_util.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/test/test_simple_task_runner.h"
8 #include "base/threading/thread.h"
9 #include "content/browser/browser_thread_impl.h"
10 #include "content/browser/indexed_db/indexed_db_connection.h"
11 #include "content/browser/indexed_db/indexed_db_context_impl.h"
12 #include "content/browser/indexed_db/mock_indexed_db_callbacks.h"
13 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
14 #include "content/public/browser/storage_partition.h"
15 #include "content/public/common/url_constants.h"
16 #include "content/public/test/mock_special_storage_policy.h"
17 #include "content/public/test/test_browser_context.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "webkit/browser/quota/quota_manager.h"
20 #include "webkit/browser/quota/special_storage_policy.h"
21 #include "webkit/common/database/database_identifier.h"
22
23 namespace content {
24
25 class IndexedDBTest : public testing::Test {
26 public:
27 const GURL kNormalOrigin;
28 const GURL kSessionOnlyOrigin;
29
IndexedDBTest()30 IndexedDBTest()
31 : kNormalOrigin("http://normal/"),
32 kSessionOnlyOrigin("http://session-only/"),
33 task_runner_(new base::TestSimpleTaskRunner),
34 special_storage_policy_(new MockSpecialStoragePolicy),
35 file_thread_(BrowserThread::FILE_USER_BLOCKING, &message_loop_),
36 io_thread_(BrowserThread::IO, &message_loop_) {
37 special_storage_policy_->AddSessionOnly(kSessionOnlyOrigin);
38 }
39
40 protected:
FlushIndexedDBTaskRunner()41 void FlushIndexedDBTaskRunner() { task_runner_->RunUntilIdle(); }
42
43 base::MessageLoopForIO message_loop_;
44 scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
45 scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
46
47 private:
48 BrowserThreadImpl file_thread_;
49 BrowserThreadImpl io_thread_;
50
51 DISALLOW_COPY_AND_ASSIGN(IndexedDBTest);
52 };
53
TEST_F(IndexedDBTest,ClearSessionOnlyDatabases)54 TEST_F(IndexedDBTest, ClearSessionOnlyDatabases) {
55 base::ScopedTempDir temp_dir;
56 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
57
58 base::FilePath normal_path;
59 base::FilePath session_only_path;
60
61 // Create the scope which will ensure we run the destructor of the context
62 // which should trigger the clean up.
63 {
64 scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
65 temp_dir.path(), special_storage_policy_, NULL, task_runner_);
66
67 normal_path = idb_context->GetFilePathForTesting(
68 webkit_database::GetIdentifierFromOrigin(kNormalOrigin));
69 session_only_path = idb_context->GetFilePathForTesting(
70 webkit_database::GetIdentifierFromOrigin(kSessionOnlyOrigin));
71 ASSERT_TRUE(base::CreateDirectory(normal_path));
72 ASSERT_TRUE(base::CreateDirectory(session_only_path));
73 FlushIndexedDBTaskRunner();
74 message_loop_.RunUntilIdle();
75 }
76
77 FlushIndexedDBTaskRunner();
78 message_loop_.RunUntilIdle();
79
80 EXPECT_TRUE(base::DirectoryExists(normal_path));
81 EXPECT_FALSE(base::DirectoryExists(session_only_path));
82 }
83
TEST_F(IndexedDBTest,SetForceKeepSessionState)84 TEST_F(IndexedDBTest, SetForceKeepSessionState) {
85 base::ScopedTempDir temp_dir;
86 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
87
88 base::FilePath normal_path;
89 base::FilePath session_only_path;
90
91 // Create the scope which will ensure we run the destructor of the context.
92 {
93 // Create some indexedDB paths.
94 // With the levelDB backend, these are directories.
95 scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
96 temp_dir.path(), special_storage_policy_, NULL, task_runner_);
97
98 // Save session state. This should bypass the destruction-time deletion.
99 idb_context->SetForceKeepSessionState();
100
101 normal_path = idb_context->GetFilePathForTesting(
102 webkit_database::GetIdentifierFromOrigin(kNormalOrigin));
103 session_only_path = idb_context->GetFilePathForTesting(
104 webkit_database::GetIdentifierFromOrigin(kSessionOnlyOrigin));
105 ASSERT_TRUE(base::CreateDirectory(normal_path));
106 ASSERT_TRUE(base::CreateDirectory(session_only_path));
107 message_loop_.RunUntilIdle();
108 }
109
110 // Make sure we wait until the destructor has run.
111 message_loop_.RunUntilIdle();
112
113 // No data was cleared because of SetForceKeepSessionState.
114 EXPECT_TRUE(base::DirectoryExists(normal_path));
115 EXPECT_TRUE(base::DirectoryExists(session_only_path));
116 }
117
118 class ForceCloseDBCallbacks : public IndexedDBCallbacks {
119 public:
ForceCloseDBCallbacks(scoped_refptr<IndexedDBContextImpl> idb_context,const GURL & origin_url)120 ForceCloseDBCallbacks(scoped_refptr<IndexedDBContextImpl> idb_context,
121 const GURL& origin_url)
122 : IndexedDBCallbacks(NULL, 0, 0),
123 idb_context_(idb_context),
124 origin_url_(origin_url) {}
125
OnSuccess()126 virtual void OnSuccess() OVERRIDE {}
OnSuccess(const std::vector<base::string16> &)127 virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE {}
OnSuccess(scoped_ptr<IndexedDBConnection> connection,const IndexedDBDatabaseMetadata & metadata)128 virtual void OnSuccess(scoped_ptr<IndexedDBConnection> connection,
129 const IndexedDBDatabaseMetadata& metadata) OVERRIDE {
130 connection_ = connection.Pass();
131 idb_context_->ConnectionOpened(origin_url_, connection_.get());
132 }
133
connection()134 IndexedDBConnection* connection() { return connection_.get(); }
135
136 protected:
~ForceCloseDBCallbacks()137 virtual ~ForceCloseDBCallbacks() {}
138
139 private:
140 scoped_refptr<IndexedDBContextImpl> idb_context_;
141 GURL origin_url_;
142 scoped_ptr<IndexedDBConnection> connection_;
143 DISALLOW_COPY_AND_ASSIGN(ForceCloseDBCallbacks);
144 };
145
TEST_F(IndexedDBTest,ForceCloseOpenDatabasesOnDelete)146 TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
147 base::ScopedTempDir temp_dir;
148 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
149
150 scoped_refptr<MockIndexedDBDatabaseCallbacks> open_db_callbacks(
151 new MockIndexedDBDatabaseCallbacks());
152 scoped_refptr<MockIndexedDBDatabaseCallbacks> closed_db_callbacks(
153 new MockIndexedDBDatabaseCallbacks());
154
155 base::FilePath test_path;
156
157 // Create the scope which will ensure we run the destructor of the context.
158 {
159 TestBrowserContext browser_context;
160
161 const GURL kTestOrigin("http://test/");
162
163 scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
164 temp_dir.path(), special_storage_policy_, NULL, task_runner_);
165
166 scoped_refptr<ForceCloseDBCallbacks> open_callbacks =
167 new ForceCloseDBCallbacks(idb_context, kTestOrigin);
168
169 scoped_refptr<ForceCloseDBCallbacks> closed_callbacks =
170 new ForceCloseDBCallbacks(idb_context, kTestOrigin);
171
172 IndexedDBFactory* factory = idb_context->GetIDBFactory();
173
174 test_path = idb_context->GetFilePathForTesting(
175 webkit_database::GetIdentifierFromOrigin(kTestOrigin));
176
177 IndexedDBPendingConnection open_connection(open_callbacks,
178 open_db_callbacks,
179 0 /* child_process_id */,
180 0 /* host_transaction_id */,
181 0 /* version */);
182 factory->Open(base::ASCIIToUTF16("opendb"),
183 open_connection,
184 NULL /* request_context */,
185 kTestOrigin,
186 idb_context->data_path());
187 IndexedDBPendingConnection closed_connection(closed_callbacks,
188 closed_db_callbacks,
189 0 /* child_process_id */,
190 0 /* host_transaction_id */,
191 0 /* version */);
192 factory->Open(base::ASCIIToUTF16("closeddb"),
193 closed_connection,
194 NULL /* request_context */,
195 kTestOrigin,
196 idb_context->data_path());
197
198 closed_callbacks->connection()->Close();
199
200 idb_context->TaskRunner()->PostTask(
201 FROM_HERE,
202 base::Bind(
203 &IndexedDBContextImpl::DeleteForOrigin, idb_context, kTestOrigin));
204 FlushIndexedDBTaskRunner();
205 message_loop_.RunUntilIdle();
206 }
207
208 // Make sure we wait until the destructor has run.
209 message_loop_.RunUntilIdle();
210
211 EXPECT_TRUE(open_db_callbacks->forced_close_called());
212 EXPECT_FALSE(closed_db_callbacks->forced_close_called());
213 EXPECT_FALSE(base::DirectoryExists(test_path));
214 }
215
TEST_F(IndexedDBTest,DeleteFailsIfDirectoryLocked)216 TEST_F(IndexedDBTest, DeleteFailsIfDirectoryLocked) {
217 base::ScopedTempDir temp_dir;
218 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
219 const GURL kTestOrigin("http://test/");
220
221 scoped_refptr<IndexedDBContextImpl> idb_context = new IndexedDBContextImpl(
222 temp_dir.path(), special_storage_policy_, NULL, task_runner_);
223
224 base::FilePath test_path = idb_context->GetFilePathForTesting(
225 webkit_database::GetIdentifierFromOrigin(kTestOrigin));
226 ASSERT_TRUE(base::CreateDirectory(test_path));
227
228 scoped_ptr<LevelDBLock> lock =
229 LevelDBDatabase::LockForTesting(test_path);
230 ASSERT_TRUE(lock);
231
232 idb_context->TaskRunner()->PostTask(
233 FROM_HERE,
234 base::Bind(
235 &IndexedDBContextImpl::DeleteForOrigin, idb_context, kTestOrigin));
236 FlushIndexedDBTaskRunner();
237
238 EXPECT_TRUE(base::DirectoryExists(test_path));
239 }
240
TEST_F(IndexedDBTest,ForceCloseOpenDatabasesOnCommitFailure)241 TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
242 const GURL kTestOrigin("http://test/");
243
244 base::ScopedTempDir temp_dir;
245 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
246
247 scoped_refptr<IndexedDBContextImpl> context = new IndexedDBContextImpl(
248 temp_dir.path(), special_storage_policy_, NULL, task_runner_);
249
250 scoped_refptr<IndexedDBFactory> factory = context->GetIDBFactory();
251
252 scoped_refptr<MockIndexedDBCallbacks> callbacks(new MockIndexedDBCallbacks());
253 scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks(
254 new MockIndexedDBDatabaseCallbacks());
255 const int64 transaction_id = 1;
256 IndexedDBPendingConnection connection(
257 callbacks,
258 db_callbacks,
259 0 /* child_process_id */,
260 transaction_id,
261 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
262 factory->Open(base::ASCIIToUTF16("db"),
263 connection,
264 NULL /* request_context */,
265 kTestOrigin,
266 temp_dir.path());
267
268 EXPECT_TRUE(callbacks->connection());
269
270 // ConnectionOpened() is usually called by the dispatcher.
271 context->ConnectionOpened(kTestOrigin, callbacks->connection());
272
273 EXPECT_TRUE(factory->IsBackingStoreOpen(kTestOrigin));
274
275 // Simulate the write failure.
276 callbacks->connection()->database()->TransactionCommitFailed();
277
278 EXPECT_TRUE(db_callbacks->forced_close_called());
279 EXPECT_FALSE(factory->IsBackingStoreOpen(kTestOrigin));
280 }
281
282 } // namespace content
283