Roll src/third_party/WebKit c63b89c:29324ab (svn 202546:202547)
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_browsertest.cc
blobc8ff36f87bbbff2aab1f9bd5de2b0701323ead9c
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.
5 #include "base/bind.h"
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/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/thread_test_helper.h"
17 #include "content/browser/browser_main_loop.h"
18 #include "content/browser/indexed_db/indexed_db_class_factory.h"
19 #include "content/browser/indexed_db/indexed_db_context_impl.h"
20 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
21 #include "content/browser/web_contents/web_contents_impl.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/storage_partition.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/url_constants.h"
29 #include "content/public/test/browser_test_utils.h"
30 #include "content/public/test/content_browser_test.h"
31 #include "content/public/test/content_browser_test_utils.h"
32 #include "content/shell/browser/shell.h"
33 #include "net/base/escape.h"
34 #include "net/base/net_errors.h"
35 #include "net/test/embedded_test_server/embedded_test_server.h"
36 #include "net/test/embedded_test_server/http_request.h"
37 #include "net/test/embedded_test_server/http_response.h"
38 #include "storage/browser/blob/blob_storage_context.h"
39 #include "storage/browser/database/database_util.h"
40 #include "storage/browser/quota/quota_manager.h"
42 using base::ASCIIToUTF16;
43 using storage::QuotaManager;
44 using storage::DatabaseUtil;
46 namespace content {
48 // This browser test is aimed towards exercising the IndexedDB bindings and
49 // the actual implementation that lives in the browser side.
50 class IndexedDBBrowserTest : public ContentBrowserTest {
51 public:
52 IndexedDBBrowserTest() : disk_usage_(-1) {}
54 void SetUp() override {
55 GetTestClassFactory()->Reset();
56 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
57 ContentBrowserTest::SetUp();
60 void TearDown() override {
61 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL);
62 ContentBrowserTest::TearDown();
65 void FailOperation(FailClass failure_class,
66 FailMethod failure_method,
67 int fail_on_instance_num,
68 int fail_on_call_num) {
69 GetTestClassFactory()->FailOperation(
70 failure_class, failure_method, fail_on_instance_num, fail_on_call_num);
73 void SimpleTest(const GURL& test_url, bool incognito = false) {
74 // The test page will perform tests on IndexedDB, then navigate to either
75 // a #pass or #fail ref.
76 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
78 VLOG(0) << "Navigating to URL and blocking.";
79 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
80 VLOG(0) << "Navigation done.";
81 std::string result =
82 the_browser->web_contents()->GetLastCommittedURL().ref();
83 if (result != "pass") {
84 std::string js_result;
85 ASSERT_TRUE(ExecuteScriptAndExtractString(
86 the_browser->web_contents(),
87 "window.domAutomationController.send(getLog())",
88 &js_result));
89 FAIL() << "Failed: " << js_result;
93 void NavigateAndWaitForTitle(Shell* shell,
94 const char* filename,
95 const char* hash,
96 const char* expected_string) {
97 GURL url = GetTestUrl("indexeddb", filename);
98 if (hash)
99 url = GURL(url.spec() + hash);
101 base::string16 expected_title16(ASCIIToUTF16(expected_string));
102 TitleWatcher title_watcher(shell->web_contents(), expected_title16);
103 NavigateToURL(shell, url);
104 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
107 IndexedDBContextImpl* GetContext() {
108 StoragePartition* partition =
109 BrowserContext::GetDefaultStoragePartition(
110 shell()->web_contents()->GetBrowserContext());
111 return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext());
114 void SetQuota(int quota_kilobytes) {
115 const int kTemporaryStorageQuotaSize =
116 quota_kilobytes * 1024 * QuotaManager::kPerHostTemporaryPortion;
117 SetTempQuota(kTemporaryStorageQuotaSize,
118 BrowserContext::GetDefaultStoragePartition(
119 shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
122 static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) {
123 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
124 BrowserThread::PostTask(
125 BrowserThread::IO, FROM_HERE,
126 base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm));
127 return;
129 DCHECK_CURRENTLY_ON(BrowserThread::IO);
130 qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback());
131 // Don't return until the quota has been set.
132 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
133 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)));
134 ASSERT_TRUE(helper->Run());
137 virtual int64 RequestDiskUsage() {
138 PostTaskAndReplyWithResult(
139 GetContext()->TaskRunner(),
140 FROM_HERE,
141 base::Bind(&IndexedDBContext::GetOriginDiskUsage,
142 GetContext(),
143 GURL("file:///")),
144 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage, this));
145 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
146 BrowserMainLoop::GetInstance()->indexed_db_thread()->task_runner()));
147 EXPECT_TRUE(helper->Run());
148 // Wait for DidGetDiskUsage to be called.
149 base::MessageLoop::current()->RunUntilIdle();
150 return disk_usage_;
153 virtual int RequestBlobFileCount() {
154 PostTaskAndReplyWithResult(
155 GetContext()->TaskRunner(), FROM_HERE,
156 base::Bind(&IndexedDBContextImpl::GetOriginBlobFileCount, GetContext(),
157 GURL("file:///")),
158 base::Bind(&IndexedDBBrowserTest::DidGetBlobFileCount, this));
159 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
160 BrowserMainLoop::GetInstance()->indexed_db_thread()->task_runner()));
161 EXPECT_TRUE(helper->Run());
162 // Wait for DidGetBlobFileCount to be called.
163 base::MessageLoop::current()->RunUntilIdle();
164 return blob_file_count_;
167 private:
168 static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() {
169 static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky
170 s_factory = LAZY_INSTANCE_INITIALIZER;
171 return s_factory.Pointer();
174 static IndexedDBClassFactory* GetIDBClassFactory() {
175 return GetTestClassFactory();
178 virtual void DidGetDiskUsage(int64 bytes) {
179 disk_usage_ = bytes;
182 virtual void DidGetBlobFileCount(int count) { blob_file_count_ = count; }
184 int64 disk_usage_;
185 int blob_file_count_ = 0;
187 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest);
190 class IndexedDBBrowserTestWithExperimentalAPIs
191 : public IndexedDBBrowserTest,
192 public ::testing::WithParamInterface<const char*> {
193 public:
194 void SetUpCommandLine(base::CommandLine* command_line) override {
195 command_line->AppendSwitch(
196 switches::kEnableExperimentalWebPlatformFeatures);
200 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) {
201 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
204 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) {
205 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
206 true /* incognito */);
209 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) {
210 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
213 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) {
214 SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
217 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) {
218 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
221 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) {
222 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
225 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) {
226 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
229 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) {
230 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
233 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) {
234 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
237 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) {
238 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
241 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) {
242 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
245 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithExperimentalAPIs,
246 GetAllMaxMessageSize) {
247 SimpleTest(GetTestUrl("indexeddb", "getall_max_message_size.html"));
250 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DoesntHangTest) {
251 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html"));
252 CrashTab(shell()->web_contents());
253 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html"));
256 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) {
257 const GURL url = GetTestUrl("indexeddb", "bug_84933.html");
259 // Just navigate to the URL. Test will crash if it fails.
260 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
263 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) {
264 const GURL url = GetTestUrl("indexeddb", "bug_106883.html");
266 // Just navigate to the URL. Test will crash if it fails.
267 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
270 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) {
271 const GURL url = GetTestUrl("indexeddb", "bug_109187.html");
273 // Just navigate to the URL. Test will crash if it fails.
274 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
277 class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest {
278 public:
279 IndexedDBBrowserTestWithLowQuota() {}
281 void SetUpOnMainThread() override {
282 const int kInitialQuotaKilobytes = 5000;
283 SetQuota(kInitialQuotaKilobytes);
286 private:
287 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota);
290 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
291 SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
294 class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest {
295 public:
296 IndexedDBBrowserTestWithGCExposed() {}
298 void SetUpCommandLine(base::CommandLine* command_line) override {
299 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc");
302 private:
303 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed);
306 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
307 DatabaseCallbacksTest) {
308 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
311 static void CopyLevelDBToProfile(Shell* shell,
312 scoped_refptr<IndexedDBContextImpl> context,
313 const std::string& test_directory) {
314 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
315 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
316 base::FilePath test_data_dir =
317 GetTestFilePath("indexeddb", test_directory.c_str()).Append(leveldb_dir);
318 base::FilePath dest = context->data_path().Append(leveldb_dir);
319 // If we don't create the destination directory first, the contents of the
320 // leveldb directory are copied directly into profile/IndexedDB instead of
321 // profile/IndexedDB/file__0.xxx/
322 ASSERT_TRUE(base::CreateDirectory(dest));
323 const bool kRecursive = true;
324 ASSERT_TRUE(base::CopyDirectory(test_data_dir,
325 context->data_path(),
326 kRecursive));
329 class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest {
330 public:
331 IndexedDBBrowserTestWithPreexistingLevelDB() {}
332 void SetUpOnMainThread() override {
333 scoped_refptr<IndexedDBContextImpl> context = GetContext();
334 context->TaskRunner()->PostTask(
335 FROM_HERE,
336 base::Bind(
337 &CopyLevelDBToProfile, shell(), context, EnclosingLevelDBDir()));
338 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
339 BrowserMainLoop::GetInstance()->indexed_db_thread()->task_runner()));
340 ASSERT_TRUE(helper->Run());
343 virtual std::string EnclosingLevelDBDir() = 0;
345 private:
346 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB);
349 class IndexedDBBrowserTestWithVersion0Schema : public
350 IndexedDBBrowserTestWithPreexistingLevelDB {
351 std::string EnclosingLevelDBDir() override { return "migration_from_0"; }
354 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) {
355 SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
358 class IndexedDBBrowserTestWithVersion123456Schema : public
359 IndexedDBBrowserTestWithPreexistingLevelDB {
360 std::string EnclosingLevelDBDir() override { return "schema_version_123456"; }
363 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema,
364 DestroyTest) {
365 int64 original_size = RequestDiskUsage();
366 EXPECT_GT(original_size, 0);
367 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
368 int64 new_size = RequestDiskUsage();
369 EXPECT_GT(new_size, 0);
370 EXPECT_NE(original_size, new_size);
373 class IndexedDBBrowserTestWithVersion987654SSVData : public
374 IndexedDBBrowserTestWithPreexistingLevelDB {
375 std::string EnclosingLevelDBDir() override { return "ssv_version_987654"; }
378 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData,
379 DestroyTest) {
380 int64 original_size = RequestDiskUsage();
381 EXPECT_GT(original_size, 0);
382 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
383 int64 new_size = RequestDiskUsage();
384 EXPECT_GT(new_size, 0);
385 EXPECT_NE(original_size, new_size);
388 class IndexedDBBrowserTestWithCorruptLevelDB : public
389 IndexedDBBrowserTestWithPreexistingLevelDB {
390 std::string EnclosingLevelDBDir() override { return "corrupt_leveldb"; }
393 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB,
394 DestroyTest) {
395 int64 original_size = RequestDiskUsage();
396 EXPECT_GT(original_size, 0);
397 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
398 int64 new_size = RequestDiskUsage();
399 EXPECT_GT(new_size, 0);
400 EXPECT_NE(original_size, new_size);
403 class IndexedDBBrowserTestWithMissingSSTFile : public
404 IndexedDBBrowserTestWithPreexistingLevelDB {
405 std::string EnclosingLevelDBDir() override { return "missing_sst"; }
408 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile,
409 DestroyTest) {
410 int64 original_size = RequestDiskUsage();
411 EXPECT_GT(original_size, 0);
412 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html"));
413 int64 new_size = RequestDiskUsage();
414 EXPECT_GT(new_size, 0);
415 EXPECT_NE(original_size, new_size);
418 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, LevelDBLogFileTest) {
419 // Any page that opens an IndexedDB will work here.
420 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
421 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
422 base::FilePath log_file(FILE_PATH_LITERAL("LOG"));
423 base::FilePath log_file_path =
424 GetContext()->data_path().Append(leveldb_dir).Append(log_file);
425 int64 size;
426 EXPECT_TRUE(base::GetFileSize(log_file_path, &size));
427 EXPECT_GT(size, 0);
430 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) {
431 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html"));
432 int64 size = RequestDiskUsage();
433 const int kQuotaKilobytes = 2;
434 EXPECT_GT(size, kQuotaKilobytes * 1024);
435 SetQuota(kQuotaKilobytes);
436 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
439 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, EmptyBlob) {
440 // First delete all IDB's for the test origin
441 GetContext()->TaskRunner()->PostTask(
442 FROM_HERE, base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
443 GetContext(), GURL("file:///")));
444 EXPECT_EQ(0, RequestBlobFileCount()); // Start with no blob files.
445 const GURL test_url = GetTestUrl("indexeddb", "empty_blob.html");
446 // For some reason Android's futimes fails (EPERM) in this test. Do not assert
447 // file times on Android, but do so on other platforms. crbug.com/467247
448 // TODO(cmumford): Figure out why this is the case and fix if possible.
449 #if defined(OS_ANDROID)
450 SimpleTest(GURL(test_url.spec() + "#ignoreTimes"));
451 #else
452 SimpleTest(GURL(test_url.spec()));
453 #endif
454 // Test stores one blob and one file to disk, so expect two files.
455 EXPECT_EQ(2, RequestBlobFileCount());
458 // Very flaky on many bots. See crbug.com/459835
459 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, DISABLED_BlobDidAck) {
460 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack.html"));
461 // Wait for idle so that the blob ack has time to be received/processed by
462 // the browser process.
463 base::MessageLoop::current()->RunUntilIdle();
464 content::ChromeBlobStorageContext* blob_context =
465 ChromeBlobStorageContext::GetFor(
466 shell()->web_contents()->GetBrowserContext());
467 EXPECT_EQ(0UL, blob_context->context()->blob_count());
470 // Very flaky on Linux. See crbug.com/459835.
471 #if defined(OS_LINUX)
472 #define MAYBE_BlobDidAckPrefetch DISABLED_BlobDidAckPrefetch
473 #else
474 #define MAYBE_BlobDidAckPrefetch BlobDidAckPrefetch
475 #endif
476 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
477 MAYBE_BlobDidAckPrefetch) {
478 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack_prefetch.html"));
479 // Wait for idle so that the blob ack has time to be received/processed by
480 // the browser process.
481 base::MessageLoop::current()->RunUntilIdle();
482 content::ChromeBlobStorageContext* blob_context =
483 ChromeBlobStorageContext::GetFor(
484 shell()->web_contents()->GetBrowserContext());
485 EXPECT_EQ(0UL, blob_context->context()->blob_count());
488 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, BlobsCountAgainstQuota) {
489 SimpleTest(GetTestUrl("indexeddb", "blobs_use_quota.html"));
492 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DeleteForOriginDeletesBlobs) {
493 SimpleTest(GetTestUrl("indexeddb", "write_20mb_blob.html"));
494 int64 size = RequestDiskUsage();
495 // This assertion assumes that we do not compress blobs.
496 EXPECT_GT(size, 20 << 20 /* 20 MB */);
497 GetContext()->TaskRunner()->PostTask(
498 FROM_HERE, base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
499 GetContext(), GURL("file:///")));
500 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
501 BrowserMainLoop::GetInstance()->indexed_db_thread()->task_runner()));
502 ASSERT_TRUE(helper->Run());
503 EXPECT_EQ(0, RequestDiskUsage());
506 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DiskFullOnCommit) {
507 // Ignore several preceding transactions:
508 // * The test calls deleteDatabase() which opens the backing store:
509 // #1: IndexedDBBackingStore::OpenBackingStore
510 // => IndexedDBBackingStore::SetUpMetadata
511 // #2: IndexedDBBackingStore::OpenBackingStore
512 // => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
513 // * The test calls open(), to create a new database:
514 // #3: IndexedDBFactoryImpl::Open
515 // => IndexedDBDatabase::Create
516 // => IndexedDBBackingStore::CreateIDBDatabaseMetaData
517 // #4: IndexedDBTransaction::Commit - initial "versionchange" transaction
518 // * Once the connection is opened, the test runs:
519 // #5: IndexedDBTransaction::Commit - the test's "readwrite" transaction)
520 const int instance_num = 5;
521 const int call_num = 1;
522 FailOperation(FAIL_CLASS_LEVELDB_TRANSACTION, FAIL_METHOD_COMMIT_DISK_FULL,
523 instance_num, call_num);
524 SimpleTest(GetTestUrl("indexeddb", "disk_full_on_commit.html"));
527 namespace {
529 static void CompactIndexedDBBackingStore(
530 scoped_refptr<IndexedDBContextImpl> context,
531 const GURL& origin_url) {
532 IndexedDBFactory* factory = context->GetIDBFactory();
534 std::pair<IndexedDBFactory::OriginDBMapIterator,
535 IndexedDBFactory::OriginDBMapIterator> range =
536 factory->GetOpenDatabasesForOrigin(origin_url);
538 if (range.first == range.second) // If no open db's for this origin
539 return;
541 // Compact the first db's backing store since all the db's are in the same
542 // backing store.
543 IndexedDBDatabase* db = range.first->second;
544 IndexedDBBackingStore* backing_store = db->backing_store();
545 backing_store->Compact();
548 static void CorruptIndexedDBDatabase(
549 IndexedDBContextImpl* context,
550 const GURL& origin_url,
551 base::WaitableEvent* signal_when_finished) {
553 CompactIndexedDBBackingStore(context, origin_url);
555 int num_files = 0;
556 int num_errors = 0;
557 const bool recursive = false;
558 for (const base::FilePath& idb_data_path :
559 context->GetStoragePaths(origin_url)) {
560 base::FileEnumerator enumerator(
561 idb_data_path, recursive, base::FileEnumerator::FILES);
562 for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty();
563 idb_file = enumerator.Next()) {
564 int64 size(0);
565 GetFileSize(idb_file, &size);
567 if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) {
568 num_files++;
569 base::File file(
570 idb_file, base::File::FLAG_WRITE | base::File::FLAG_OPEN_TRUNCATED);
571 if (file.IsValid()) {
572 // Was opened truncated, expand back to the original
573 // file size and fill with zeros (corrupting the file).
574 file.SetLength(size);
575 } else {
576 num_errors++;
580 VLOG(0) << "There were " << num_files << " in " << idb_data_path.value()
581 << " with " << num_errors << " errors";
584 signal_when_finished->Signal();
587 const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
589 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
590 IndexedDBContextImpl* context,
591 const GURL& origin_url,
592 const std::string& path,
593 IndexedDBBrowserTest* test,
594 const net::test_server::HttpRequest& request) {
595 std::string request_path;
596 if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
597 request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
598 else
599 return scoped_ptr<net::test_server::HttpResponse>();
601 // Remove the query string if present.
602 std::string request_query;
603 size_t query_pos = request_path.find('?');
604 if (query_pos != std::string::npos) {
605 request_query = request_path.substr(query_pos + 1);
606 request_path = request_path.substr(0, query_pos);
609 if (request_path == "corruptdb" && !request_query.empty()) {
610 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
611 base::WaitableEvent signal_when_finished(false, false);
612 context->TaskRunner()->PostTask(FROM_HERE,
613 base::Bind(&CorruptIndexedDBDatabase,
614 base::ConstRef(context),
615 origin_url,
616 &signal_when_finished));
617 signal_when_finished.Wait();
619 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
620 new net::test_server::BasicHttpResponse);
621 http_response->set_code(net::HTTP_OK);
622 return http_response.Pass();
623 } else if (request_path == "fail" && !request_query.empty()) {
624 FailClass failure_class = FAIL_CLASS_NOTHING;
625 FailMethod failure_method = FAIL_METHOD_NOTHING;
626 int instance_num = 1;
627 int call_num = 1;
628 std::string fail_class;
629 std::string fail_method;
631 url::Component query(0, request_query.length()), key_pos, value_pos;
632 while (url::ExtractQueryKeyValue(
633 request_query.c_str(), &query, &key_pos, &value_pos)) {
634 std::string escaped_key(request_query.substr(key_pos.begin, key_pos.len));
635 std::string escaped_value(
636 request_query.substr(value_pos.begin, value_pos.len));
638 std::string key = net::UnescapeURLComponent(
639 escaped_key,
640 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
641 net::UnescapeRule::URL_SPECIAL_CHARS);
643 std::string value = net::UnescapeURLComponent(
644 escaped_value,
645 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
646 net::UnescapeRule::URL_SPECIAL_CHARS);
648 if (key == "method")
649 fail_method = value;
650 else if (key == "class")
651 fail_class = value;
652 else if (key == "instNum")
653 instance_num = atoi(value.c_str());
654 else if (key == "callNum")
655 call_num = atoi(value.c_str());
656 else
657 NOTREACHED() << "Unknown param: \"" << key << "\"";
660 if (fail_class == "LevelDBTransaction") {
661 failure_class = FAIL_CLASS_LEVELDB_TRANSACTION;
662 if (fail_method == "Get")
663 failure_method = FAIL_METHOD_GET;
664 else if (fail_method == "Commit")
665 failure_method = FAIL_METHOD_COMMIT;
666 else
667 NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
668 } else if (fail_class == "LevelDBIterator") {
669 failure_class = FAIL_CLASS_LEVELDB_ITERATOR;
670 if (fail_method == "Seek")
671 failure_method = FAIL_METHOD_SEEK;
672 else
673 NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
674 } else {
675 NOTREACHED() << "Unknown class: \"" << fail_class << "\"";
678 DCHECK_GE(instance_num, 1);
679 DCHECK_GE(call_num, 1);
681 test->FailOperation(failure_class, failure_method, instance_num, call_num);
683 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
684 new net::test_server::BasicHttpResponse);
685 http_response->set_code(net::HTTP_OK);
686 return http_response.Pass();
689 // A request for a test resource
690 base::FilePath resource_path =
691 content::GetTestFilePath("indexeddb", request_path.c_str());
692 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
693 new net::test_server::BasicHttpResponse);
694 http_response->set_code(net::HTTP_OK);
695 std::string file_contents;
696 if (!base::ReadFileToString(resource_path, &file_contents))
697 return scoped_ptr<net::test_server::HttpResponse>();
698 http_response->set_content(file_contents);
699 return http_response.Pass();
702 } // namespace
704 // Experimental for IDBObjectStore.getAll()
705 using IndexedDBBrowserCorruptionTest = IndexedDBBrowserTestWithExperimentalAPIs;
707 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
708 OperationOnCorruptedOpenDatabase) {
709 ASSERT_TRUE(embedded_test_server()->Started() ||
710 embedded_test_server()->InitializeAndWaitUntilReady());
711 const GURL& origin_url = embedded_test_server()->base_url();
712 embedded_test_server()->RegisterRequestHandler(
713 base::Bind(&CorruptDBRequestHandler,
714 base::Unretained(GetContext()),
715 origin_url,
716 s_corrupt_db_test_prefix,
717 this));
719 std::string test_file = s_corrupt_db_test_prefix +
720 "corrupted_open_db_detection.html#" + GetParam();
721 SimpleTest(embedded_test_server()->GetURL(test_file));
723 test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
724 SimpleTest(embedded_test_server()->GetURL(test_file));
727 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
728 IndexedDBBrowserCorruptionTest,
729 ::testing::Values("failGetBlobJournal",
730 "get",
731 "getAll",
732 "failWebkitGetDatabaseNames",
733 "iterate",
734 "failTransactionCommit",
735 "clearObjectStore"));
737 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
738 DeleteCompactsBackingStore) {
739 const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
740 SimpleTest(GURL(test_url.spec() + "#fill"));
741 int64 after_filling = RequestDiskUsage();
742 EXPECT_GT(after_filling, 0);
744 SimpleTest(GURL(test_url.spec() + "#purge"));
745 int64 after_deleting = RequestDiskUsage();
746 EXPECT_LT(after_deleting, after_filling);
748 // The above tests verify basic assertions - that filling writes data and
749 // deleting reduces the amount stored.
751 // The below tests make assumptions about implementation specifics, such as
752 // data compression, compaction efficiency, and the maximum amount of
753 // metadata and log data remains after a deletion. It is possible that
754 // changes to the implementation may require these constants to be tweaked.
756 const int kTestFillBytes = 1024 * 1024 * 5; // 5MB
757 EXPECT_GT(after_filling, kTestFillBytes);
759 const int kTestCompactBytes = 1024 * 10; // 10kB
760 EXPECT_LT(after_deleting, kTestCompactBytes);
763 // Complex multi-step (converted from pyauto) tests begin here.
765 // Verify null key path persists after restarting browser.
766 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) {
767 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
768 "pass - first run");
771 // Verify null key path persists after restarting browser.
772 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) {
773 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
774 "pass - second run");
777 // Verify that a VERSION_CHANGE transaction is rolled back after a
778 // renderer/browser crash
779 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
780 PRE_PRE_VersionChangeCrashResilience) {
781 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
782 "pass - part1 - complete");
785 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) {
786 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
787 "pass - part2 - crash me");
788 // If we actually crash here then googletest will not run the next step
789 // (VersionChangeCrashResilience) as an optimization. googletest's
790 // ASSERT_DEATH/EXIT fails to work properly (on Windows) due to how we
791 // implement the PRE_* test mechanism.
792 exit(0);
795 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) {
796 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
797 "pass - part3 - rolled back");
800 // crbug.com/427529
801 // Disable this test for ASAN on Android because it takes too long to run.
802 #if defined(ANDROID) && defined(ADDRESS_SANITIZER)
803 #define MAYBE_ConnectionsClosedOnTabClose DISABLED_ConnectionsClosedOnTabClose
804 #else
805 #define MAYBE_ConnectionsClosedOnTabClose ConnectionsClosedOnTabClose
806 #endif
807 // Verify that open DB connections are closed when a tab is destroyed.
808 IN_PROC_BROWSER_TEST_F(
809 IndexedDBBrowserTest, MAYBE_ConnectionsClosedOnTabClose) {
810 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
811 "setVersion(2) complete");
813 // Start on a different URL to force a new renderer process.
814 Shell* new_shell = CreateBrowser();
815 NavigateToURL(new_shell, GURL(url::kAboutBlankURL));
816 NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2",
817 "setVersion(3) blocked");
819 base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete"));
820 TitleWatcher title_watcher(new_shell->web_contents(), expected_title16);
822 shell()->web_contents()->GetRenderProcessHost()->Shutdown(0, true);
823 shell()->Close();
825 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
828 // Verify that a "close" event is fired at database connections when
829 // the backing store is deleted.
830 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
831 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL,
832 "connection ready");
834 GetContext()->TaskRunner()->PostTask(
835 FROM_HERE,
836 base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
837 GetContext(),
838 GURL("file:///")));
840 base::string16 expected_title16(ASCIIToUTF16("connection closed"));
841 TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
842 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
843 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
846 class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest {
847 public:
848 void SetUpCommandLine(base::CommandLine* command_line) override {
849 command_line->AppendSwitch(switches::kSingleProcess);
853 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
854 RenderThreadShutdownTest) {
855 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
858 } // namespace content