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
23namespace content {
24
25class IndexedDBTest : public testing::Test {
26 public:
27  const GURL kNormalOrigin;
28  const GURL kSessionOnlyOrigin;
29
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:
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
54TEST_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
84TEST_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
118class ForceCloseDBCallbacks : public IndexedDBCallbacks {
119 public:
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
126  virtual void OnSuccess() OVERRIDE {}
127  virtual void OnSuccess(const std::vector<base::string16>&) OVERRIDE {}
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
134  IndexedDBConnection* connection() { return connection_.get(); }
135
136 protected:
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
146TEST_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
216TEST_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
241TEST_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