indexed_db_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/bind.h" 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/shell/browser/shell.h" 25#include "content/test/content_browser_test.h" 26#include "content/test/content_browser_test_utils.h" 27#include "webkit/browser/database/database_util.h" 28#include "webkit/browser/quota/quota_manager.h" 29 30using base::ASCIIToUTF16; 31using quota::QuotaManager; 32using webkit_database::DatabaseUtil; 33 34namespace content { 35 36// This browser test is aimed towards exercising the IndexedDB bindings and 37// the actual implementation that lives in the browser side. 38class IndexedDBBrowserTest : public ContentBrowserTest { 39 public: 40 IndexedDBBrowserTest() : disk_usage_(-1) {} 41 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(); 46 47 VLOG(0) << "Navigating to URL and blocking."; 48 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); 49 VLOG(0) << "Navigation done."; 50 std::string result = 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())", 57 &js_result)); 58 FAIL() << "Failed: " << js_result; 59 } 60 } 61 62 void NavigateAndWaitForTitle(Shell* shell, 63 const char* filename, 64 const char* hash, 65 const char* expected_string) { 66 GURL url = GetTestUrl("indexeddb", filename); 67 if (hash) 68 url = GURL(url.spec() + hash); 69 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()); 74 } 75 76 IndexedDBContextImpl* GetContext() { 77 StoragePartition* partition = 78 BrowserContext::GetDefaultStoragePartition( 79 shell()->web_contents()->GetBrowserContext()); 80 return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext()); 81 } 82 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()); 89 } 90 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)); 96 return; 97 } 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()); 104 } 105 106 virtual int64 RequestDiskUsage() { 107 PostTaskAndReplyWithResult( 108 GetContext()->TaskRunner(), 109 FROM_HERE, 110 base::Bind(&IndexedDBContext::GetOriginDiskUsage, 111 GetContext(), 112 GURL("file:///")), 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(); 120 return disk_usage_; 121 } 122 private: 123 virtual void DidGetDiskUsage(int64 bytes) { 124 EXPECT_GT(bytes, 0); 125 disk_usage_ = bytes; 126 } 127 128 int64 disk_usage_; 129}; 130 131IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) { 132 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html")); 133} 134 135IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) { 136 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"), 137 true /* incognito */); 138} 139 140IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) { 141 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html")); 142} 143 144IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) { 145 SimpleTest(GetTestUrl("indexeddb", "index_test.html")); 146} 147 148IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) { 149 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html")); 150} 151 152IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) { 153 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html")); 154} 155 156IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) { 157 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html")); 158} 159 160IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) { 161 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html")); 162} 163 164IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) { 165 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 166} 167 168IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) { 169 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html")); 170} 171 172IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) { 173 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html")); 174} 175 176IN_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")); 180} 181 182IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) { 183 const GURL url = GetTestUrl("indexeddb", "bug_84933.html"); 184 185 // Just navigate to the URL. Test will crash if it fails. 186 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 187} 188 189IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) { 190 const GURL url = GetTestUrl("indexeddb", "bug_106883.html"); 191 192 // Just navigate to the URL. Test will crash if it fails. 193 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 194} 195 196IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) { 197 const GURL url = GetTestUrl("indexeddb", "bug_109187.html"); 198 199 // Just navigate to the URL. Test will crash if it fails. 200 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 201} 202 203class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest { 204 public: 205 virtual void SetUpOnMainThread() OVERRIDE { 206 const int kInitialQuotaKilobytes = 5000; 207 SetQuota(kInitialQuotaKilobytes); 208 } 209}; 210 211IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) { 212 SimpleTest(GetTestUrl("indexeddb", "quota_test.html")); 213} 214 215class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest { 216 public: 217 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 218 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc"); 219 } 220}; 221 222IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, 223 DatabaseCallbacksTest) { 224 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html")); 225} 226 227static 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(), 242 kRecursive)); 243} 244 245class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest { 246 public: 247 virtual void SetUpOnMainThread() OVERRIDE { 248 scoped_refptr<IndexedDBContextImpl> context = GetContext(); 249 context->TaskRunner()->PostTask( 250 FROM_HERE, 251 base::Bind( 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()); 257 } 258 259 virtual std::string EnclosingLevelDBDir() = 0; 260 261}; 262 263class IndexedDBBrowserTestWithVersion0Schema : public 264 IndexedDBBrowserTestWithPreexistingLevelDB { 265 virtual std::string EnclosingLevelDBDir() OVERRIDE { 266 return "migration_from_0"; 267 } 268}; 269 270IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) { 271 SimpleTest(GetTestUrl("indexeddb", "migration_test.html")); 272} 273 274class IndexedDBBrowserTestWithVersion123456Schema : public 275 IndexedDBBrowserTestWithPreexistingLevelDB { 276 virtual std::string EnclosingLevelDBDir() OVERRIDE { 277 return "schema_version_123456"; 278 } 279}; 280 281IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema, 282 DestroyTest) { 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); 288} 289 290class IndexedDBBrowserTestWithVersion987654SSVData : public 291 IndexedDBBrowserTestWithPreexistingLevelDB { 292 virtual std::string EnclosingLevelDBDir() OVERRIDE { 293 return "ssv_version_987654"; 294 } 295}; 296 297IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData, 298 DestroyTest) { 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); 304} 305 306class IndexedDBBrowserTestWithCorruptLevelDB : public 307 IndexedDBBrowserTestWithPreexistingLevelDB { 308 virtual std::string EnclosingLevelDBDir() OVERRIDE { 309 return "corrupt_leveldb"; 310 } 311}; 312 313IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB, 314 DestroyTest) { 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); 320} 321 322class IndexedDBBrowserTestWithMissingSSTFile : public 323 IndexedDBBrowserTestWithPreexistingLevelDB { 324 virtual std::string EnclosingLevelDBDir() OVERRIDE { 325 return "missing_sst"; 326 } 327}; 328 329IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile, 330 DestroyTest) { 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); 336} 337 338IN_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); 345 int64 size; 346 EXPECT_TRUE(base::GetFileSize(log_file_path, &size)); 347 EXPECT_GT(size, 0); 348} 349 350IN_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")); 357} 358 359IN_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); 364 365 SimpleTest(GURL(test_url.spec() + "#purge")); 366 int64 after_deleting = RequestDiskUsage(); 367 EXPECT_LT(after_deleting, after_filling); 368 369 // The above tests verify basic assertions - that filling writes data and 370 // deleting reduces the amount stored. 371 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. 376 377 const int kTestFillBytes = 1024 * 1024 * 5; // 5MB 378 EXPECT_GT(after_filling, kTestFillBytes); 379 380 const int kTestCompactBytes = 1024 * 1024 * 1; // 1MB 381 EXPECT_LT(after_deleting, kTestCompactBytes); 382} 383 384// Complex multi-step (converted from pyauto) tests begin here. 385 386// Verify null key path persists after restarting browser. 387IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) { 388 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1", 389 "pass - first run"); 390} 391 392// Verify null key path persists after restarting browser. 393IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) { 394 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2", 395 "pass - second run"); 396} 397 398// Verify that a VERSION_CHANGE transaction is rolled back after a 399// renderer/browser crash 400IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, 401 PRE_PRE_VersionChangeCrashResilience) { 402 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1", 403 "pass - part1 - complete"); 404} 405 406IN_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)); 410} 411 412IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) { 413 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3", 414 "pass - part3 - rolled back"); 415} 416 417// Verify that open DB connections are closed when a tab is destroyed. 418IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) { 419 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1", 420 "setVersion(2) complete"); 421 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"); 427 428 base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete")); 429 TitleWatcher title_watcher(new_shell->web_contents(), expected_title16); 430 431 base::KillProcess( 432 shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true); 433 shell()->Close(); 434 435 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 436} 437 438// Verify that a "close" event is fired at database connections when 439// the backing store is deleted. 440IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) { 441 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL, 442 "connection ready"); 443 444 GetContext()->TaskRunner()->PostTask( 445 FROM_HERE, 446 base::Bind(&IndexedDBContextImpl::DeleteForOrigin, 447 GetContext(), 448 GURL("file:///"))); 449 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()); 453} 454 455class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest { 456 public: 457 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 458 command_line->AppendSwitch(switches::kSingleProcess); 459 } 460}; 461 462// Crashing on Android due to kSingleProcess flag: http://crbug.com/342525 463#if defined(OS_ANDROID) 464#define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest 465#else 466#define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest 467#endif 468IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, 469 MAYBE_RenderThreadShutdownTest) { 470 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); 471} 472 473} // namespace content 474