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/files/file.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/thread_test_helper.h"
16 #include "content/browser/browser_main_loop.h"
17 #include "content/browser/indexed_db/indexed_db_class_factory.h"
18 #include "content/browser/indexed_db/indexed_db_context_impl.h"
19 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "content/public/test/content_browser_test.h"
30 #include "content/public/test/content_browser_test_utils.h"
31 #include "content/shell/browser/shell.h"
32 #include "net/base/escape.h"
33 #include "net/base/net_errors.h"
34 #include "net/test/embedded_test_server/embedded_test_server.h"
35 #include "net/test/embedded_test_server/http_request.h"
36 #include "net/test/embedded_test_server/http_response.h"
37 #include "storage/browser/blob/blob_storage_context.h"
38 #include "storage/browser/database/database_util.h"
39 #include "storage/browser/quota/quota_manager.h"
41 using base::ASCIIToUTF16
;
42 using storage::QuotaManager
;
43 using storage::DatabaseUtil
;
47 // This browser test is aimed towards exercising the IndexedDB bindings and
48 // the actual implementation that lives in the browser side.
49 class IndexedDBBrowserTest
: public ContentBrowserTest
{
51 IndexedDBBrowserTest() : disk_usage_(-1) {}
53 void SetUp() override
{
54 GetTestClassFactory()->Reset();
55 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory
);
56 ContentBrowserTest::SetUp();
59 void TearDown() override
{
60 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL
);
61 ContentBrowserTest::TearDown();
64 void FailOperation(FailClass failure_class
,
65 FailMethod failure_method
,
66 int fail_on_instance_num
,
67 int fail_on_call_num
) {
68 GetTestClassFactory()->FailOperation(
69 failure_class
, failure_method
, fail_on_instance_num
, fail_on_call_num
);
72 void SimpleTest(const GURL
& test_url
, bool incognito
= false) {
73 // The test page will perform tests on IndexedDB, then navigate to either
74 // a #pass or #fail ref.
75 Shell
* the_browser
= incognito
? CreateOffTheRecordBrowser() : shell();
77 VLOG(0) << "Navigating to URL and blocking.";
78 NavigateToURLBlockUntilNavigationsComplete(the_browser
, test_url
, 2);
79 VLOG(0) << "Navigation done.";
81 the_browser
->web_contents()->GetLastCommittedURL().ref();
82 if (result
!= "pass") {
83 std::string js_result
;
84 ASSERT_TRUE(ExecuteScriptAndExtractString(
85 the_browser
->web_contents(),
86 "window.domAutomationController.send(getLog())",
88 FAIL() << "Failed: " << js_result
;
92 void NavigateAndWaitForTitle(Shell
* shell
,
95 const char* expected_string
) {
96 GURL url
= GetTestUrl("indexeddb", filename
);
98 url
= GURL(url
.spec() + hash
);
100 base::string16
expected_title16(ASCIIToUTF16(expected_string
));
101 TitleWatcher
title_watcher(shell
->web_contents(), expected_title16
);
102 NavigateToURL(shell
, url
);
103 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
106 IndexedDBContextImpl
* GetContext() {
107 StoragePartition
* partition
=
108 BrowserContext::GetDefaultStoragePartition(
109 shell()->web_contents()->GetBrowserContext());
110 return static_cast<IndexedDBContextImpl
*>(partition
->GetIndexedDBContext());
113 void SetQuota(int quota_kilobytes
) {
114 const int kTemporaryStorageQuotaSize
=
115 quota_kilobytes
* 1024 * QuotaManager::kPerHostTemporaryPortion
;
116 SetTempQuota(kTemporaryStorageQuotaSize
,
117 BrowserContext::GetDefaultStoragePartition(
118 shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
121 static void SetTempQuota(int64 bytes
, scoped_refptr
<QuotaManager
> qm
) {
122 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
123 BrowserThread::PostTask(
124 BrowserThread::IO
, FROM_HERE
,
125 base::Bind(&IndexedDBBrowserTest::SetTempQuota
, bytes
, qm
));
128 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
129 qm
->SetTemporaryGlobalOverrideQuota(bytes
, storage::QuotaCallback());
130 // Don't return until the quota has been set.
131 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
132 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB
)));
133 ASSERT_TRUE(helper
->Run());
136 virtual int64
RequestDiskUsage() {
137 PostTaskAndReplyWithResult(
138 GetContext()->TaskRunner(),
140 base::Bind(&IndexedDBContext::GetOriginDiskUsage
,
143 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage
, this));
144 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
145 BrowserMainLoop::GetInstance()->indexed_db_thread()->
146 message_loop_proxy()));
147 EXPECT_TRUE(helper
->Run());
148 // Wait for DidGetDiskUsage to be called.
149 base::MessageLoop::current()->RunUntilIdle();
153 virtual int RequestBlobFileCount() {
154 PostTaskAndReplyWithResult(
155 GetContext()->TaskRunner(), FROM_HERE
,
156 base::Bind(&IndexedDBContextImpl::GetOriginBlobFileCount
, GetContext(),
158 base::Bind(&IndexedDBBrowserTest::DidGetBlobFileCount
, this));
159 scoped_refptr
<base::ThreadTestHelper
> helper(
160 new base::ThreadTestHelper(BrowserMainLoop::GetInstance()
161 ->indexed_db_thread()
162 ->message_loop_proxy()));
163 EXPECT_TRUE(helper
->Run());
164 // Wait for DidGetBlobFileCount to be called.
165 base::MessageLoop::current()->RunUntilIdle();
166 return blob_file_count_
;
170 static MockBrowserTestIndexedDBClassFactory
* GetTestClassFactory() {
171 static ::base::LazyInstance
<MockBrowserTestIndexedDBClassFactory
>::Leaky
172 s_factory
= LAZY_INSTANCE_INITIALIZER
;
173 return s_factory
.Pointer();
176 static IndexedDBClassFactory
* GetIDBClassFactory() {
177 return GetTestClassFactory();
180 virtual void DidGetDiskUsage(int64 bytes
) {
184 virtual void DidGetBlobFileCount(int count
) { blob_file_count_
= count
; }
187 int blob_file_count_
= 0;
189 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest
);
192 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorTest
) {
193 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
196 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorTestIncognito
) {
197 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
198 true /* incognito */);
201 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CursorPrefetch
) {
202 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
205 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, IndexTest
) {
206 SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
209 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, KeyPathTest
) {
210 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
213 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, TransactionGetTest
) {
214 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
217 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, KeyTypesTest
) {
218 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
221 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, ObjectStoreTest
) {
222 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
225 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DatabaseTest
) {
226 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
229 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, TransactionTest
) {
230 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
233 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CallbackAccounting
) {
234 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
237 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DoesntHangTest
) {
238 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html"));
239 CrashTab(shell()->web_contents());
240 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html"));
243 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug84933Test
) {
244 const GURL url
= GetTestUrl("indexeddb", "bug_84933.html");
246 // Just navigate to the URL. Test will crash if it fails.
247 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
250 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug106883Test
) {
251 const GURL url
= GetTestUrl("indexeddb", "bug_106883.html");
253 // Just navigate to the URL. Test will crash if it fails.
254 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
257 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, Bug109187Test
) {
258 const GURL url
= GetTestUrl("indexeddb", "bug_109187.html");
260 // Just navigate to the URL. Test will crash if it fails.
261 NavigateToURLBlockUntilNavigationsComplete(shell(), url
, 1);
264 class IndexedDBBrowserTestWithLowQuota
: public IndexedDBBrowserTest
{
266 IndexedDBBrowserTestWithLowQuota() {}
268 void SetUpOnMainThread() override
{
269 const int kInitialQuotaKilobytes
= 5000;
270 SetQuota(kInitialQuotaKilobytes
);
274 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota
);
277 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota
, QuotaTest
) {
278 SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
281 class IndexedDBBrowserTestWithGCExposed
: public IndexedDBBrowserTest
{
283 IndexedDBBrowserTestWithGCExposed() {}
285 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
286 command_line
->AppendSwitchASCII(switches::kJavaScriptFlags
, "--expose-gc");
290 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed
);
293 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed
,
294 DatabaseCallbacksTest
) {
295 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
298 static void CopyLevelDBToProfile(Shell
* shell
,
299 scoped_refptr
<IndexedDBContextImpl
> context
,
300 const std::string
& test_directory
) {
301 DCHECK(context
->TaskRunner()->RunsTasksOnCurrentThread());
302 base::FilePath
leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
303 base::FilePath test_data_dir
=
304 GetTestFilePath("indexeddb", test_directory
.c_str()).Append(leveldb_dir
);
305 base::FilePath dest
= context
->data_path().Append(leveldb_dir
);
306 // If we don't create the destination directory first, the contents of the
307 // leveldb directory are copied directly into profile/IndexedDB instead of
308 // profile/IndexedDB/file__0.xxx/
309 ASSERT_TRUE(base::CreateDirectory(dest
));
310 const bool kRecursive
= true;
311 ASSERT_TRUE(base::CopyDirectory(test_data_dir
,
312 context
->data_path(),
316 class IndexedDBBrowserTestWithPreexistingLevelDB
: public IndexedDBBrowserTest
{
318 IndexedDBBrowserTestWithPreexistingLevelDB() {}
319 void SetUpOnMainThread() override
{
320 scoped_refptr
<IndexedDBContextImpl
> context
= GetContext();
321 context
->TaskRunner()->PostTask(
324 &CopyLevelDBToProfile
, shell(), context
, EnclosingLevelDBDir()));
325 scoped_refptr
<base::ThreadTestHelper
> helper(new base::ThreadTestHelper(
326 BrowserMainLoop::GetInstance()->indexed_db_thread()->
327 message_loop_proxy()));
328 ASSERT_TRUE(helper
->Run());
331 virtual std::string
EnclosingLevelDBDir() = 0;
334 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB
);
337 class IndexedDBBrowserTestWithVersion0Schema
: public
338 IndexedDBBrowserTestWithPreexistingLevelDB
{
339 std::string
EnclosingLevelDBDir() override
{ return "migration_from_0"; }
342 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema
, MigrationTest
) {
343 SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
346 class IndexedDBBrowserTestWithVersion123456Schema
: public
347 IndexedDBBrowserTestWithPreexistingLevelDB
{
348 std::string
EnclosingLevelDBDir() override
{ return "schema_version_123456"; }
351 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema
,
353 int64 original_size
= RequestDiskUsage();
354 EXPECT_GT(original_size
, 0);
355 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
356 int64 new_size
= RequestDiskUsage();
357 EXPECT_GT(new_size
, 0);
358 EXPECT_NE(original_size
, new_size
);
361 class IndexedDBBrowserTestWithVersion987654SSVData
: public
362 IndexedDBBrowserTestWithPreexistingLevelDB
{
363 std::string
EnclosingLevelDBDir() override
{ return "ssv_version_987654"; }
366 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData
,
368 int64 original_size
= RequestDiskUsage();
369 EXPECT_GT(original_size
, 0);
370 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
371 int64 new_size
= RequestDiskUsage();
372 EXPECT_GT(new_size
, 0);
373 EXPECT_NE(original_size
, new_size
);
376 class IndexedDBBrowserTestWithCorruptLevelDB
: public
377 IndexedDBBrowserTestWithPreexistingLevelDB
{
378 std::string
EnclosingLevelDBDir() override
{ return "corrupt_leveldb"; }
381 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB
,
383 int64 original_size
= RequestDiskUsage();
384 EXPECT_GT(original_size
, 0);
385 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
386 int64 new_size
= RequestDiskUsage();
387 EXPECT_GT(new_size
, 0);
388 EXPECT_NE(original_size
, new_size
);
391 class IndexedDBBrowserTestWithMissingSSTFile
: public
392 IndexedDBBrowserTestWithPreexistingLevelDB
{
393 std::string
EnclosingLevelDBDir() override
{ return "missing_sst"; }
396 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile
,
398 int64 original_size
= RequestDiskUsage();
399 EXPECT_GT(original_size
, 0);
400 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html"));
401 int64 new_size
= RequestDiskUsage();
402 EXPECT_GT(new_size
, 0);
403 EXPECT_NE(original_size
, new_size
);
406 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, LevelDBLogFileTest
) {
407 // Any page that opens an IndexedDB will work here.
408 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
409 base::FilePath
leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
410 base::FilePath
log_file(FILE_PATH_LITERAL("LOG"));
411 base::FilePath log_file_path
=
412 GetContext()->data_path().Append(leveldb_dir
).Append(log_file
);
414 EXPECT_TRUE(base::GetFileSize(log_file_path
, &size
));
418 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, CanDeleteWhenOverQuotaTest
) {
419 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html"));
420 int64 size
= RequestDiskUsage();
421 const int kQuotaKilobytes
= 2;
422 EXPECT_GT(size
, kQuotaKilobytes
* 1024);
423 SetQuota(kQuotaKilobytes
);
424 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
427 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, EmptyBlob
) {
428 // First delete all IDB's for the test origin
429 GetContext()->TaskRunner()->PostTask(
430 FROM_HERE
, base::Bind(&IndexedDBContextImpl::DeleteForOrigin
,
431 GetContext(), GURL("file:///")));
432 EXPECT_EQ(0, RequestBlobFileCount()); // Start with no blob files.
433 const GURL test_url
= GetTestUrl("indexeddb", "empty_blob.html");
434 // For some reason Android's futimes fails (EPERM) in this test. Do not assert
435 // file times on Android, but do so on other platforms. crbug.com/467247
436 // TODO(cmumford): Figure out why this is the case and fix if possible.
437 #if defined(OS_ANDROID)
438 SimpleTest(GURL(test_url
.spec() + "#ignoreTimes"));
440 SimpleTest(GURL(test_url
.spec()));
442 // Test stores one blob and one file to disk, so expect two files.
443 EXPECT_EQ(2, RequestBlobFileCount());
446 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed
, BlobDidAck
) {
447 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack.html"));
448 // Wait for idle so that the blob ack has time to be received/processed by
449 // the browser process.
450 base::MessageLoop::current()->RunUntilIdle();
451 content::ChromeBlobStorageContext
* blob_context
=
452 ChromeBlobStorageContext::GetFor(
453 shell()->web_contents()->GetBrowserContext());
454 EXPECT_EQ(0UL, blob_context
->context()->blob_count());
457 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed
, BlobDidAckPrefetch
) {
458 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack_prefetch.html"));
459 // Wait for idle so that the blob ack has time to be received/processed by
460 // the browser process.
461 base::MessageLoop::current()->RunUntilIdle();
462 content::ChromeBlobStorageContext
* blob_context
=
463 ChromeBlobStorageContext::GetFor(
464 shell()->web_contents()->GetBrowserContext());
465 EXPECT_EQ(0UL, blob_context
->context()->blob_count());
468 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, BlobsCountAgainstQuota
) {
469 SimpleTest(GetTestUrl("indexeddb", "blobs_use_quota.html"));
472 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DeleteForOriginDeletesBlobs
) {
473 SimpleTest(GetTestUrl("indexeddb", "write_20mb_blob.html"));
474 int64 size
= RequestDiskUsage();
475 // This assertion assumes that we do not compress blobs.
476 EXPECT_GT(size
, 20 << 20 /* 20 MB */);
477 GetContext()->TaskRunner()->PostTask(
478 FROM_HERE
, base::Bind(&IndexedDBContextImpl::DeleteForOrigin
,
479 GetContext(), GURL("file:///")));
480 scoped_refptr
<base::ThreadTestHelper
> helper(
481 new base::ThreadTestHelper(BrowserMainLoop::GetInstance()
482 ->indexed_db_thread()
483 ->message_loop_proxy()));
484 ASSERT_TRUE(helper
->Run());
485 EXPECT_EQ(0, RequestDiskUsage());
488 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, DiskFullOnCommit
) {
489 // Ignore several preceding transactions:
490 // * The test calls deleteDatabase() which opens the backing store:
491 // #1: IndexedDBBackingStore::OpenBackingStore
492 // => IndexedDBBackingStore::SetUpMetadata
493 // #2: IndexedDBBackingStore::OpenBackingStore
494 // => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
495 // * Then deletes the database:
496 // #3: IndexedDBFactoryImpl::DeleteDatabase
497 // => IndexedDBDatabase::Create
498 // => IndexedDBBackingStore::CreateIDBDatabaseMetaData
499 // #4: IndexedDBFactoryImpl::DeleteDatabase
500 // => IndexedDBDatabase::DeleteDatabase
501 // => IndexedDBBackingStore::DeleteDatabase
502 // => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
503 // * The test calls open(), to create a new database:
504 // #5: IndexedDBFactoryImpl::Open
505 // => IndexedDBDatabase::Create
506 // => IndexedDBBackingStore::CreateIDBDatabaseMetaData
507 // #6: IndexedDBTransaction::Commit - initial "versionchange" transaction
508 // * Once the connection is opened, the test runs:
509 // #7: IndexedDBTransaction::Commit - the test's "readwrite" transaction)
510 const int instance_num
= 7;
511 const int call_num
= 1;
512 FailOperation(FAIL_CLASS_LEVELDB_TRANSACTION
, FAIL_METHOD_COMMIT_DISK_FULL
,
513 instance_num
, call_num
);
514 SimpleTest(GetTestUrl("indexeddb", "disk_full_on_commit.html"));
519 static void CompactIndexedDBBackingStore(
520 scoped_refptr
<IndexedDBContextImpl
> context
,
521 const GURL
& origin_url
) {
522 IndexedDBFactory
* factory
= context
->GetIDBFactory();
524 std::pair
<IndexedDBFactory::OriginDBMapIterator
,
525 IndexedDBFactory::OriginDBMapIterator
> range
=
526 factory
->GetOpenDatabasesForOrigin(origin_url
);
528 if (range
.first
== range
.second
) // If no open db's for this origin
531 // Compact the first db's backing store since all the db's are in the same
533 IndexedDBDatabase
* db
= range
.first
->second
;
534 IndexedDBBackingStore
* backing_store
= db
->backing_store();
535 backing_store
->Compact();
538 static void CorruptIndexedDBDatabase(
539 IndexedDBContextImpl
* context
,
540 const GURL
& origin_url
,
541 base::WaitableEvent
* signal_when_finished
) {
543 CompactIndexedDBBackingStore(context
, origin_url
);
547 const bool recursive
= false;
548 for (const base::FilePath
& idb_data_path
:
549 context
->GetStoragePaths(origin_url
)) {
550 base::FileEnumerator
enumerator(
551 idb_data_path
, recursive
, base::FileEnumerator::FILES
);
552 for (base::FilePath idb_file
= enumerator
.Next(); !idb_file
.empty();
553 idb_file
= enumerator
.Next()) {
555 GetFileSize(idb_file
, &size
);
557 if (idb_file
.Extension() == FILE_PATH_LITERAL(".ldb")) {
560 idb_file
, base::File::FLAG_WRITE
| base::File::FLAG_OPEN_TRUNCATED
);
561 if (file
.IsValid()) {
562 // Was opened truncated, expand back to the original
563 // file size and fill with zeros (corrupting the file).
564 file
.SetLength(size
);
570 VLOG(0) << "There were " << num_files
<< " in " << idb_data_path
.value()
571 << " with " << num_errors
<< " errors";
574 signal_when_finished
->Signal();
577 const std::string s_corrupt_db_test_prefix
= "/corrupt/test/";
579 static scoped_ptr
<net::test_server::HttpResponse
> CorruptDBRequestHandler(
580 IndexedDBContextImpl
* context
,
581 const GURL
& origin_url
,
582 const std::string
& path
,
583 IndexedDBBrowserTest
* test
,
584 const net::test_server::HttpRequest
& request
) {
585 std::string request_path
;
586 if (path
.find(s_corrupt_db_test_prefix
) != std::string::npos
)
587 request_path
= request
.relative_url
.substr(s_corrupt_db_test_prefix
.size());
589 return scoped_ptr
<net::test_server::HttpResponse
>();
591 // Remove the query string if present.
592 std::string request_query
;
593 size_t query_pos
= request_path
.find('?');
594 if (query_pos
!= std::string::npos
) {
595 request_query
= request_path
.substr(query_pos
+ 1);
596 request_path
= request_path
.substr(0, query_pos
);
599 if (request_path
== "corruptdb" && !request_query
.empty()) {
600 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query
;
601 base::WaitableEvent
signal_when_finished(false, false);
602 context
->TaskRunner()->PostTask(FROM_HERE
,
603 base::Bind(&CorruptIndexedDBDatabase
,
604 base::ConstRef(context
),
606 &signal_when_finished
));
607 signal_when_finished
.Wait();
609 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
610 new net::test_server::BasicHttpResponse
);
611 http_response
->set_code(net::HTTP_OK
);
612 return http_response
.Pass();
613 } else if (request_path
== "fail" && !request_query
.empty()) {
614 FailClass failure_class
= FAIL_CLASS_NOTHING
;
615 FailMethod failure_method
= FAIL_METHOD_NOTHING
;
616 int instance_num
= 1;
618 std::string fail_class
;
619 std::string fail_method
;
621 url::Component
query(0, request_query
.length()), key_pos
, value_pos
;
622 while (url::ExtractQueryKeyValue(
623 request_query
.c_str(), &query
, &key_pos
, &value_pos
)) {
624 std::string
escaped_key(request_query
.substr(key_pos
.begin
, key_pos
.len
));
625 std::string
escaped_value(
626 request_query
.substr(value_pos
.begin
, value_pos
.len
));
628 std::string key
= net::UnescapeURLComponent(
630 net::UnescapeRule::NORMAL
| net::UnescapeRule::SPACES
|
631 net::UnescapeRule::URL_SPECIAL_CHARS
);
633 std::string value
= net::UnescapeURLComponent(
635 net::UnescapeRule::NORMAL
| net::UnescapeRule::SPACES
|
636 net::UnescapeRule::URL_SPECIAL_CHARS
);
640 else if (key
== "class")
642 else if (key
== "instNum")
643 instance_num
= atoi(value
.c_str());
644 else if (key
== "callNum")
645 call_num
= atoi(value
.c_str());
647 NOTREACHED() << "Unknown param: \"" << key
<< "\"";
650 if (fail_class
== "LevelDBTransaction") {
651 failure_class
= FAIL_CLASS_LEVELDB_TRANSACTION
;
652 if (fail_method
== "Get")
653 failure_method
= FAIL_METHOD_GET
;
654 else if (fail_method
== "Commit")
655 failure_method
= FAIL_METHOD_COMMIT
;
657 NOTREACHED() << "Unknown method: \"" << fail_method
<< "\"";
658 } else if (fail_class
== "LevelDBIterator") {
659 failure_class
= FAIL_CLASS_LEVELDB_ITERATOR
;
660 if (fail_method
== "Seek")
661 failure_method
= FAIL_METHOD_SEEK
;
663 NOTREACHED() << "Unknown method: \"" << fail_method
<< "\"";
665 NOTREACHED() << "Unknown class: \"" << fail_class
<< "\"";
668 DCHECK_GE(instance_num
, 1);
669 DCHECK_GE(call_num
, 1);
671 test
->FailOperation(failure_class
, failure_method
, instance_num
, call_num
);
673 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
674 new net::test_server::BasicHttpResponse
);
675 http_response
->set_code(net::HTTP_OK
);
676 return http_response
.Pass();
679 // A request for a test resource
680 base::FilePath resource_path
=
681 content::GetTestFilePath("indexeddb", request_path
.c_str());
682 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
683 new net::test_server::BasicHttpResponse
);
684 http_response
->set_code(net::HTTP_OK
);
685 std::string file_contents
;
686 if (!base::ReadFileToString(resource_path
, &file_contents
))
687 return scoped_ptr
<net::test_server::HttpResponse
>();
688 http_response
->set_content(file_contents
);
689 return http_response
.Pass();
694 class IndexedDBBrowserCorruptionTest
695 : public IndexedDBBrowserTest
,
696 public ::testing::WithParamInterface
<const char*> {};
698 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest
,
699 OperationOnCorruptedOpenDatabase
) {
700 ASSERT_TRUE(embedded_test_server()->Started() ||
701 embedded_test_server()->InitializeAndWaitUntilReady());
702 const GURL
& origin_url
= embedded_test_server()->base_url();
703 embedded_test_server()->RegisterRequestHandler(
704 base::Bind(&CorruptDBRequestHandler
,
705 base::Unretained(GetContext()),
707 s_corrupt_db_test_prefix
,
710 std::string test_file
= s_corrupt_db_test_prefix
+
711 "corrupted_open_db_detection.html#" + GetParam();
712 SimpleTest(embedded_test_server()->GetURL(test_file
));
714 test_file
= s_corrupt_db_test_prefix
+ "corrupted_open_db_recovery.html";
715 SimpleTest(embedded_test_server()->GetURL(test_file
));
718 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation
,
719 IndexedDBBrowserCorruptionTest
,
720 ::testing::Values("failGetBlobJournal",
722 "failWebkitGetDatabaseNames",
724 "failTransactionCommit",
725 "clearObjectStore"));
727 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
,
728 DeleteCompactsBackingStore
) {
729 const GURL test_url
= GetTestUrl("indexeddb", "delete_compact.html");
730 SimpleTest(GURL(test_url
.spec() + "#fill"));
731 int64 after_filling
= RequestDiskUsage();
732 EXPECT_GT(after_filling
, 0);
734 SimpleTest(GURL(test_url
.spec() + "#purge"));
735 int64 after_deleting
= RequestDiskUsage();
736 EXPECT_LT(after_deleting
, after_filling
);
738 // The above tests verify basic assertions - that filling writes data and
739 // deleting reduces the amount stored.
741 // The below tests make assumptions about implementation specifics, such as
742 // data compression, compaction efficiency, and the maximum amount of
743 // metadata and log data remains after a deletion. It is possible that
744 // changes to the implementation may require these constants to be tweaked.
746 const int kTestFillBytes
= 1024 * 1024 * 5; // 5MB
747 EXPECT_GT(after_filling
, kTestFillBytes
);
749 const int kTestCompactBytes
= 1024 * 10; // 10kB
750 EXPECT_LT(after_deleting
, kTestCompactBytes
);
753 // Complex multi-step (converted from pyauto) tests begin here.
755 // Verify null key path persists after restarting browser.
756 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, PRE_NullKeyPathPersistence
) {
757 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
761 // Verify null key path persists after restarting browser.
762 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, NullKeyPathPersistence
) {
763 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
764 "pass - second run");
767 // Verify that a VERSION_CHANGE transaction is rolled back after a
768 // renderer/browser crash
769 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
,
770 PRE_PRE_VersionChangeCrashResilience
) {
771 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
772 "pass - part1 - complete");
775 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, PRE_VersionChangeCrashResilience
) {
776 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
777 "pass - part2 - crash me");
778 // If we actually crash here then googletest will not run the next step
779 // (VersionChangeCrashResilience) as an optimization. googletest's
780 // ASSERT_DEATH/EXIT fails to work properly (on Windows) due to how we
781 // implement the PRE_* test mechanism.
785 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, VersionChangeCrashResilience
) {
786 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
787 "pass - part3 - rolled back");
791 // Disable this test for ASAN on Android because it takes too long to run.
792 #if defined(ANDROID) && defined(ADDRESS_SANITIZER)
793 #define MAYBE_ConnectionsClosedOnTabClose DISABLED_ConnectionsClosedOnTabClose
795 #define MAYBE_ConnectionsClosedOnTabClose ConnectionsClosedOnTabClose
797 // Verify that open DB connections are closed when a tab is destroyed.
798 IN_PROC_BROWSER_TEST_F(
799 IndexedDBBrowserTest
, MAYBE_ConnectionsClosedOnTabClose
) {
800 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
801 "setVersion(2) complete");
803 // Start on a different URL to force a new renderer process.
804 Shell
* new_shell
= CreateBrowser();
805 NavigateToURL(new_shell
, GURL(url::kAboutBlankURL
));
806 NavigateAndWaitForTitle(new_shell
, "version_change_blocked.html", "#tab2",
807 "setVersion(3) blocked");
809 base::string16
expected_title16(ASCIIToUTF16("setVersion(3) complete"));
810 TitleWatcher
title_watcher(new_shell
->web_contents(), expected_title16
);
812 shell()->web_contents()->GetRenderProcessHost()->Shutdown(0, true);
815 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
818 // Verify that a "close" event is fired at database connections when
819 // the backing store is deleted.
820 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest
, ForceCloseEventTest
) {
821 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL
,
824 GetContext()->TaskRunner()->PostTask(
826 base::Bind(&IndexedDBContextImpl::DeleteForOrigin
,
830 base::string16
expected_title16(ASCIIToUTF16("connection closed"));
831 TitleWatcher
title_watcher(shell()->web_contents(), expected_title16
);
832 title_watcher
.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
833 EXPECT_EQ(expected_title16
, title_watcher
.WaitAndGetTitle());
836 class IndexedDBBrowserTestSingleProcess
: public IndexedDBBrowserTest
{
838 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
839 command_line
->AppendSwitch(switches::kSingleProcess
);
843 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess
,
844 RenderThreadShutdownTest
) {
845 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
848 } // namespace content