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.
6 #include "base/command_line.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/test/thread_test_helper.h"
13 #include "content/browser/browser_main_loop.h"
14 #include "content/browser/indexed_db/indexed_db_context_impl.h"
15 #include "content/browser/web_contents/web_contents_impl.h"
16 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/storage_partition.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/public/test/browser_test_utils.h"
24 #include "content/public/test/content_browser_test.h"
25 #include "content/public/test/content_browser_test_utils.h"
26 #include "content/shell/browser/shell.h"
27 #include "webkit/browser/database/database_util.h"
28 #include "webkit/browser/quota/quota_manager.h"
30 using base::ASCIIToUTF16
;
31 using quota::QuotaManager
;
32 using webkit_database::DatabaseUtil
;
36 // This browser test is aimed towards exercising the IndexedDB bindings and
37 // the actual implementation that lives in the browser side.
38 class IndexedDBBrowserTest
: public ContentBrowserTest
{
40 IndexedDBBrowserTest() : disk_usage_(-1) {}
42 void SimpleTest(const GURL
& test_url
, bool incognito
= false) {
43 // The test page will perform tests on IndexedDB, then navigate to either
44 // a #pass or #fail ref.
45 Shell
* the_browser
= incognito
? CreateOffTheRecordBrowser() : shell();
47 VLOG(0) << "Navigating to URL and blocking.";
48 NavigateToURLBlockUntilNavigationsComplete(the_browser
, test_url
, 2);
49 VLOG(0) << "Navigation done.";
51 the_browser
->web_contents()->GetLastCommittedURL().ref();
52 if (result
!= "pass") {
53 std::string js_result
;
54 ASSERT_TRUE(ExecuteScriptAndExtractString(
55 the_browser
->web_contents(),
56 "window.domAutomationController.send(getLog())",
58 FAIL() << "Failed: " << js_result
;
62 void NavigateAndWaitForTitle(Shell
* shell
,
65 const char* expected_string
) {
66 GURL url
= GetTestUrl("indexeddb", filename
);
68 url
= GURL(url
.spec() + hash
);
70 base::string16
expected_title16(ASCIIToUTF16(expected_string
));
71 TitleWatcher
title_watcher(shell
->web_contents(), expected_title16
);
72 NavigateToURL(shell
, url
);
73 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
76 IndexedDBContextImpl
* GetContext() {
77 StoragePartition
* partition
=
78 BrowserContext::GetDefaultStoragePartition(
79 shell()->web_contents()->GetBrowserContext());
80 return static_cast<IndexedDBContextImpl
*>(partition
->GetIndexedDBContext());
83 void SetQuota(int quotaKilobytes
) {
84 const int kTemporaryStorageQuotaSize
= quotaKilobytes
85 * 1024 * QuotaManager::kPerHostTemporaryPortion
;
86 SetTempQuota(kTemporaryStorageQuotaSize
,
87 BrowserContext::GetDefaultStoragePartition(
88 shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
91 static void SetTempQuota(int64 bytes
, scoped_refptr
<QuotaManager
> qm
) {
92 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
93 BrowserThread::PostTask(
94 BrowserThread::IO
, FROM_HERE
,
95 base::Bind(&IndexedDBBrowserTest::SetTempQuota
, bytes
, qm
));
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
99 qm
->SetTemporaryGlobalOverrideQuota(bytes
, quota::QuotaCallback());
100 // Don't return until the quota has been set.
101 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
102 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB
)));
103 ASSERT_TRUE(helper
->Run());
106 virtual int64
RequestDiskUsage() {
107 PostTaskAndReplyWithResult(
108 GetContext()->TaskRunner(),
110 base::Bind(&IndexedDBContext::GetOriginDiskUsage
,
113 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage
, this));
114 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
115 BrowserMainLoop::GetInstance()->indexed_db_thread()->
116 message_loop_proxy()));
117 EXPECT_TRUE(helper
->Run());
118 // Wait for DidGetDiskUsage to be called.
119 base::MessageLoop::current()->RunUntilIdle();
123 virtual void DidGetDiskUsage(int64 bytes
) {
131 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorTest
) {
132 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
135 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorTestIncognito
) {
136 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
137 true /* incognito */);
140 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorPrefetch
) {
141 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
144 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, IndexTest
) {
145 SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
148 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, KeyPathTest
) {
149 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
152 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, TransactionGetTest
) {
153 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
156 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, KeyTypesTest
) {
157 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
160 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, ObjectStoreTest
) {
161 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
164 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DatabaseTest
) {
165 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
168 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, TransactionTest
) {
169 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
172 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CallbackAccounting
) {
173 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
176 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DoesntHangTest
) {
177 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html"));
178 CrashTab(shell()->web_contents());
179 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html"));
182 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug84933Test
) {
183 const GURL url
= GetTestUrl("indexeddb", "bug_84933.html");
185 // Just navigate to the URL. Test will crash if it fails.
186 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
189 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug106883Test
) {
190 const GURL url
= GetTestUrl("indexeddb", "bug_106883.html");
192 // Just navigate to the URL. Test will crash if it fails.
193 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
196 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug109187Test
) {
197 const GURL url
= GetTestUrl("indexeddb", "bug_109187.html");
199 // Just navigate to the URL. Test will crash if it fails.
200 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
203 class IndexedDBBrowserTestWithLowQuota
: public IndexedDBBrowserTest
{
205 virtual void SetUpOnMainThread() OVERRIDE
{
206 const int kInitialQuotaKilobytes
= 5000;
207 SetQuota(kInitialQuotaKilobytes
);
211 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota
, QuotaTest
) {
212 SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
215 class IndexedDBBrowserTestWithGCExposed
: public IndexedDBBrowserTest
{
217 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
218 command_line
->AppendSwitchASCII(switches::kJavaScriptFlags
, "--expose-gc");
222 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed
,
223 DatabaseCallbacksTest
) {
224 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
227 static void CopyLevelDBToProfile(Shell
* shell
,
228 scoped_refptr
<IndexedDBContextImpl
> context
,
229 const std::string
& test_directory
) {
230 DCHECK(context
->TaskRunner()->RunsTasksOnCurrentThread());
231 base::FilePath
leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
232 base::FilePath test_data_dir
=
233 GetTestFilePath("indexeddb", test_directory
.c_str()).Append(leveldb_dir
);
234 base::FilePath dest
= context
->data_path().Append(leveldb_dir
);
235 // If we don't create the destination directory first, the contents of the
236 // leveldb directory are copied directly into profile/IndexedDB instead of
237 // profile/IndexedDB/file__0.xxx/
238 ASSERT_TRUE(base::CreateDirectory(dest
));
239 const bool kRecursive
= true;
240 ASSERT_TRUE(base::CopyDirectory(test_data_dir
,
241 context
->data_path(),
245 class IndexedDBBrowserTestWithPreexistingLevelDB
: public IndexedDBBrowserTest
{
247 virtual void SetUpOnMainThread() OVERRIDE
{
248 scoped_refptr
<IndexedDBContextImpl
> context
= GetContext();
249 context
->TaskRunner()->PostTask(
252 &CopyLevelDBToProfile
, shell(), context
, EnclosingLevelDBDir()));
253 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
254 BrowserMainLoop::GetInstance()->indexed_db_thread()->
255 message_loop_proxy()));
256 ASSERT_TRUE(helper
->Run());
259 virtual std::string
EnclosingLevelDBDir() = 0;
263 class IndexedDBBrowserTestWithVersion0Schema
: public
264 IndexedDBBrowserTestWithPreexistingLevelDB
{
265 virtual std::string
EnclosingLevelDBDir() OVERRIDE
{
266 return "migration_from_0";
270 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema
, MigrationTest
) {
271 SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
274 class IndexedDBBrowserTestWithVersion123456Schema
: public
275 IndexedDBBrowserTestWithPreexistingLevelDB
{
276 virtual std::string
EnclosingLevelDBDir() OVERRIDE
{
277 return "schema_version_123456";
281 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema
,
283 int64 original_size
= RequestDiskUsage();
284 EXPECT_GT(original_size
, 0);
285 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
286 int64 new_size
= RequestDiskUsage();
287 EXPECT_NE(original_size
, new_size
);
290 class IndexedDBBrowserTestWithVersion987654SSVData
: public
291 IndexedDBBrowserTestWithPreexistingLevelDB
{
292 virtual std::string
EnclosingLevelDBDir() OVERRIDE
{
293 return "ssv_version_987654";
297 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData
,
299 int64 original_size
= RequestDiskUsage();
300 EXPECT_GT(original_size
, 0);
301 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
302 int64 new_size
= RequestDiskUsage();
303 EXPECT_NE(original_size
, new_size
);
306 class IndexedDBBrowserTestWithCorruptLevelDB
: public
307 IndexedDBBrowserTestWithPreexistingLevelDB
{
308 virtual std::string
EnclosingLevelDBDir() OVERRIDE
{
309 return "corrupt_leveldb";
313 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB
,
315 int64 original_size
= RequestDiskUsage();
316 EXPECT_GT(original_size
, 0);
317 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
318 int64 new_size
= RequestDiskUsage();
319 EXPECT_NE(original_size
, new_size
);
322 class IndexedDBBrowserTestWithMissingSSTFile
: public
323 IndexedDBBrowserTestWithPreexistingLevelDB
{
324 virtual std::string
EnclosingLevelDBDir() OVERRIDE
{
325 return "missing_sst";
329 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile
,
331 int64 original_size
= RequestDiskUsage();
332 EXPECT_GT(original_size
, 0);
333 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html"));
334 int64 new_size
= RequestDiskUsage();
335 EXPECT_NE(original_size
, new_size
);
338 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, LevelDBLogFileTest
) {
339 // Any page that opens an IndexedDB will work here.
340 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
341 base::FilePath
leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
342 base::FilePath
log_file(FILE_PATH_LITERAL("LOG"));
343 base::FilePath log_file_path
=
344 GetContext()->data_path().Append(leveldb_dir
).Append(log_file
);
346 EXPECT_TRUE(base::GetFileSize(log_file_path
, &size
));
350 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CanDeleteWhenOverQuotaTest
) {
351 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html"));
352 int64 size
= RequestDiskUsage();
353 const int kQuotaKilobytes
= 2;
354 EXPECT_GT(size
, kQuotaKilobytes
* 1024);
355 SetQuota(kQuotaKilobytes
);
356 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
359 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DeleteCompactsBackingStore
) {
360 const GURL test_url
= GetTestUrl("indexeddb", "delete_compact.html");
361 SimpleTest(GURL(test_url
.spec() + "#fill"));
362 int64 after_filling
= RequestDiskUsage();
363 EXPECT_GT(after_filling
, 0);
365 SimpleTest(GURL(test_url
.spec() + "#purge"));
366 int64 after_deleting
= RequestDiskUsage();
367 EXPECT_LT(after_deleting
, after_filling
);
369 // The above tests verify basic assertions - that filling writes data and
370 // deleting reduces the amount stored.
372 // The below tests make assumptions about implementation specifics, such as
373 // data compression, compaction efficiency, and the maximum amount of
374 // metadata and log data remains after a deletion. It is possible that
375 // changes to the implementation may require these constants to be tweaked.
377 const int kTestFillBytes
= 1024 * 1024 * 5; // 5MB
378 EXPECT_GT(after_filling
, kTestFillBytes
);
380 const int kTestCompactBytes
= 1024 * 1024 * 1; // 1MB
381 EXPECT_LT(after_deleting
, kTestCompactBytes
);
384 // Complex multi-step (converted from pyauto) tests begin here.
386 // Verify null key path persists after restarting browser.
387 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, PRE_NullKeyPathPersistence
) {
388 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
392 // Verify null key path persists after restarting browser.
393 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, NullKeyPathPersistence
) {
394 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
395 "pass - second run");
398 // Verify that a VERSION_CHANGE transaction is rolled back after a
399 // renderer/browser crash
400 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
,
401 PRE_PRE_VersionChangeCrashResilience
) {
402 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
403 "pass - part1 - complete");
406 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, PRE_VersionChangeCrashResilience
) {
407 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
408 "pass - part2 - crash me");
409 NavigateToURL(shell(), GURL(kChromeUIBrowserCrashHost
));
412 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, VersionChangeCrashResilience
) {
413 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
414 "pass - part3 - rolled back");
417 // Verify that open DB connections are closed when a tab is destroyed.
418 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, ConnectionsClosedOnTabClose
) {
419 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
420 "setVersion(2) complete");
422 // Start on a different URL to force a new renderer process.
423 Shell
* new_shell
= CreateBrowser();
424 NavigateToURL(new_shell
, GURL(kAboutBlankURL
));
425 NavigateAndWaitForTitle(new_shell
, "version_change_blocked.html", "#tab2",
426 "setVersion(3) blocked");
428 base::string16
expected_title16(ASCIIToUTF16("setVersion(3) complete"));
429 TitleWatcher
title_watcher(new_shell
->web_contents(), expected_title16
);
432 shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true);
435 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
438 // Verify that a "close" event is fired at database connections when
439 // the backing store is deleted.
440 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, ForceCloseEventTest
) {
441 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL
,
444 GetContext()->TaskRunner()->PostTask(
446 base::Bind(&IndexedDBContextImpl::DeleteForOrigin
,
450 base::string16
expected_title16(ASCIIToUTF16("connection closed"));
451 TitleWatcher
title_watcher(shell()->web_contents(), expected_title16
);
452 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
455 class IndexedDBBrowserTestSingleProcess
: public IndexedDBBrowserTest
{
457 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
458 command_line
->AppendSwitch(switches::kSingleProcess
);
462 // Crashing on Android due to kSingleProcess flag: http://crbug.com/342525
463 #if defined(OS_ANDROID)
464 #define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest
466 #define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest
468 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess
,
469 MAYBE_RenderThreadShutdownTest
) {
470 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
473 } // namespace content