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 30 using quota::QuotaManager; 31 using webkit_database::DatabaseUtil; 32 33 namespace content { 34 35 // This browser test is aimed towards exercising the IndexedDB bindings and 36 // the actual implementation that lives in the browser side. 37 class IndexedDBBrowserTest : public ContentBrowserTest { 38 public: 39 IndexedDBBrowserTest() : disk_usage_(-1) {} 40 41 void SimpleTest(const GURL& test_url, bool incognito = false) { 42 // The test page will perform tests on IndexedDB, then navigate to either 43 // a #pass or #fail ref. 44 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell(); 45 46 VLOG(0) << "Navigating to URL and blocking."; 47 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); 48 VLOG(0) << "Navigation done."; 49 std::string result = 50 the_browser->web_contents()->GetLastCommittedURL().ref(); 51 if (result != "pass") { 52 std::string js_result; 53 ASSERT_TRUE(ExecuteScriptAndExtractString( 54 the_browser->web_contents(), 55 "window.domAutomationController.send(getLog())", 56 &js_result)); 57 FAIL() << "Failed: " << js_result; 58 } 59 } 60 61 void NavigateAndWaitForTitle(Shell* shell, 62 const char* filename, 63 const char* hash, 64 const char* expected_string) { 65 GURL url = GetTestUrl("indexeddb", filename); 66 if (hash) 67 url = GURL(url.spec() + hash); 68 69 base::string16 expected_title16(ASCIIToUTF16(expected_string)); 70 TitleWatcher title_watcher(shell->web_contents(), expected_title16); 71 NavigateToURL(shell, url); 72 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 73 } 74 75 IndexedDBContextImpl* GetContext() { 76 StoragePartition* partition = 77 BrowserContext::GetDefaultStoragePartition( 78 shell()->web_contents()->GetBrowserContext()); 79 return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext()); 80 } 81 82 void SetQuota(int quotaKilobytes) { 83 const int kTemporaryStorageQuotaSize = quotaKilobytes 84 * 1024 * QuotaManager::kPerHostTemporaryPortion; 85 SetTempQuota(kTemporaryStorageQuotaSize, 86 BrowserContext::GetDefaultStoragePartition( 87 shell()->web_contents()->GetBrowserContext())->GetQuotaManager()); 88 } 89 90 static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) { 91 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 92 BrowserThread::PostTask( 93 BrowserThread::IO, FROM_HERE, 94 base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm)); 95 return; 96 } 97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 98 qm->SetTemporaryGlobalOverrideQuota(bytes, quota::QuotaCallback()); 99 // Don't return until the quota has been set. 100 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 101 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); 102 ASSERT_TRUE(helper->Run()); 103 } 104 105 virtual int64 RequestDiskUsage() { 106 PostTaskAndReplyWithResult( 107 GetContext()->TaskRunner(), 108 FROM_HERE, 109 base::Bind(&IndexedDBContext::GetOriginDiskUsage, 110 GetContext(), 111 GURL("file:///")), 112 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage, this)); 113 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 114 BrowserMainLoop::GetInstance()->indexed_db_thread()-> 115 message_loop_proxy())); 116 EXPECT_TRUE(helper->Run()); 117 // Wait for DidGetDiskUsage to be called. 118 base::MessageLoop::current()->RunUntilIdle(); 119 return disk_usage_; 120 } 121 private: 122 virtual void DidGetDiskUsage(int64 bytes) { 123 EXPECT_GT(bytes, 0); 124 disk_usage_ = bytes; 125 } 126 127 int64 disk_usage_; 128 }; 129 130 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) { 131 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html")); 132 } 133 134 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) { 135 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"), 136 true /* incognito */); 137 } 138 139 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) { 140 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html")); 141 } 142 143 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) { 144 SimpleTest(GetTestUrl("indexeddb", "index_test.html")); 145 } 146 147 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) { 148 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html")); 149 } 150 151 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) { 152 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html")); 153 } 154 155 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) { 156 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html")); 157 } 158 159 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) { 160 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html")); 161 } 162 163 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) { 164 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 165 } 166 167 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) { 168 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html")); 169 } 170 171 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) { 172 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html")); 173 } 174 175 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DoesntHangTest) { 176 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html")); 177 CrashTab(shell()->web_contents()); 178 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html")); 179 } 180 181 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) { 182 const GURL url = GetTestUrl("indexeddb", "bug_84933.html"); 183 184 // Just navigate to the URL. Test will crash if it fails. 185 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 186 } 187 188 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) { 189 const GURL url = GetTestUrl("indexeddb", "bug_106883.html"); 190 191 // Just navigate to the URL. Test will crash if it fails. 192 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 193 } 194 195 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) { 196 const GURL url = GetTestUrl("indexeddb", "bug_109187.html"); 197 198 // Just navigate to the URL. Test will crash if it fails. 199 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 200 } 201 202 class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest { 203 public: 204 virtual void SetUpOnMainThread() OVERRIDE { 205 const int kInitialQuotaKilobytes = 5000; 206 SetQuota(kInitialQuotaKilobytes); 207 } 208 }; 209 210 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) { 211 SimpleTest(GetTestUrl("indexeddb", "quota_test.html")); 212 } 213 214 class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest { 215 public: 216 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 217 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc"); 218 } 219 }; 220 221 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, 222 DatabaseCallbacksTest) { 223 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html")); 224 } 225 226 static void CopyLevelDBToProfile(Shell* shell, 227 scoped_refptr<IndexedDBContextImpl> context, 228 const std::string& test_directory) { 229 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread()); 230 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb")); 231 base::FilePath test_data_dir = 232 GetTestFilePath("indexeddb", test_directory.c_str()).Append(leveldb_dir); 233 base::FilePath dest = context->data_path().Append(leveldb_dir); 234 // If we don't create the destination directory first, the contents of the 235 // leveldb directory are copied directly into profile/IndexedDB instead of 236 // profile/IndexedDB/file__0.xxx/ 237 ASSERT_TRUE(base::CreateDirectory(dest)); 238 const bool kRecursive = true; 239 ASSERT_TRUE(base::CopyDirectory(test_data_dir, 240 context->data_path(), 241 kRecursive)); 242 } 243 244 class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest { 245 public: 246 virtual void SetUpOnMainThread() OVERRIDE { 247 scoped_refptr<IndexedDBContextImpl> context = GetContext(); 248 context->TaskRunner()->PostTask( 249 FROM_HERE, 250 base::Bind( 251 &CopyLevelDBToProfile, shell(), context, EnclosingLevelDBDir())); 252 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 253 BrowserMainLoop::GetInstance()->indexed_db_thread()-> 254 message_loop_proxy())); 255 ASSERT_TRUE(helper->Run()); 256 } 257 258 virtual std::string EnclosingLevelDBDir() = 0; 259 260 }; 261 262 class IndexedDBBrowserTestWithVersion0Schema : public 263 IndexedDBBrowserTestWithPreexistingLevelDB { 264 virtual std::string EnclosingLevelDBDir() OVERRIDE { 265 return "migration_from_0"; 266 } 267 }; 268 269 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) { 270 SimpleTest(GetTestUrl("indexeddb", "migration_test.html")); 271 } 272 273 class IndexedDBBrowserTestWithVersion123456Schema : public 274 IndexedDBBrowserTestWithPreexistingLevelDB { 275 virtual std::string EnclosingLevelDBDir() OVERRIDE { 276 return "schema_version_123456"; 277 } 278 }; 279 280 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema, 281 DestroyTest) { 282 int64 original_size = RequestDiskUsage(); 283 EXPECT_GT(original_size, 0); 284 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 285 int64 new_size = RequestDiskUsage(); 286 EXPECT_NE(original_size, new_size); 287 } 288 289 class IndexedDBBrowserTestWithVersion987654SSVData : public 290 IndexedDBBrowserTestWithPreexistingLevelDB { 291 virtual std::string EnclosingLevelDBDir() OVERRIDE { 292 return "ssv_version_987654"; 293 } 294 }; 295 296 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData, 297 DestroyTest) { 298 int64 original_size = RequestDiskUsage(); 299 EXPECT_GT(original_size, 0); 300 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 301 int64 new_size = RequestDiskUsage(); 302 EXPECT_NE(original_size, new_size); 303 } 304 305 class IndexedDBBrowserTestWithCorruptLevelDB : public 306 IndexedDBBrowserTestWithPreexistingLevelDB { 307 virtual std::string EnclosingLevelDBDir() OVERRIDE { 308 return "corrupt_leveldb"; 309 } 310 }; 311 312 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB, 313 DestroyTest) { 314 int64 original_size = RequestDiskUsage(); 315 EXPECT_GT(original_size, 0); 316 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 317 int64 new_size = RequestDiskUsage(); 318 EXPECT_NE(original_size, new_size); 319 } 320 321 class IndexedDBBrowserTestWithMissingSSTFile : public 322 IndexedDBBrowserTestWithPreexistingLevelDB { 323 virtual std::string EnclosingLevelDBDir() OVERRIDE { 324 return "missing_sst"; 325 } 326 }; 327 328 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile, 329 DestroyTest) { 330 int64 original_size = RequestDiskUsage(); 331 EXPECT_GT(original_size, 0); 332 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html")); 333 int64 new_size = RequestDiskUsage(); 334 EXPECT_NE(original_size, new_size); 335 } 336 337 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, LevelDBLogFileTest) { 338 // Any page that opens an IndexedDB will work here. 339 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 340 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb")); 341 base::FilePath log_file(FILE_PATH_LITERAL("LOG")); 342 base::FilePath log_file_path = 343 GetContext()->data_path().Append(leveldb_dir).Append(log_file); 344 int64 size; 345 EXPECT_TRUE(base::GetFileSize(log_file_path, &size)); 346 EXPECT_GT(size, 0); 347 } 348 349 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) { 350 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html")); 351 int64 size = RequestDiskUsage(); 352 const int kQuotaKilobytes = 2; 353 EXPECT_GT(size, kQuotaKilobytes * 1024); 354 SetQuota(kQuotaKilobytes); 355 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html")); 356 } 357 358 // Complex multi-step (converted from pyauto) tests begin here. 359 360 // Verify null key path persists after restarting browser. 361 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) { 362 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1", 363 "pass - first run"); 364 } 365 366 // Verify null key path persists after restarting browser. 367 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) { 368 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2", 369 "pass - second run"); 370 } 371 372 // Verify that a VERSION_CHANGE transaction is rolled back after a 373 // renderer/browser crash 374 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, 375 PRE_PRE_VersionChangeCrashResilience) { 376 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1", 377 "pass - part1 - complete"); 378 } 379 380 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) { 381 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2", 382 "pass - part2 - crash me"); 383 NavigateToURL(shell(), GURL(kChromeUIBrowserCrashHost)); 384 } 385 386 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) { 387 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3", 388 "pass - part3 - rolled back"); 389 } 390 391 // Verify that open DB connections are closed when a tab is destroyed. 392 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) { 393 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1", 394 "setVersion(2) complete"); 395 396 // Start on a different URL to force a new renderer process. 397 Shell* new_shell = CreateBrowser(); 398 NavigateToURL(new_shell, GURL(kAboutBlankURL)); 399 NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2", 400 "setVersion(3) blocked"); 401 402 base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete")); 403 TitleWatcher title_watcher(new_shell->web_contents(), expected_title16); 404 405 base::KillProcess( 406 shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true); 407 shell()->Close(); 408 409 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 410 } 411 412 // Verify that a "close" event is fired at database connections when 413 // the backing store is deleted. 414 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) { 415 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL, 416 "connection ready"); 417 418 GetContext()->TaskRunner()->PostTask( 419 FROM_HERE, 420 base::Bind(&IndexedDBContextImpl::DeleteForOrigin, 421 GetContext(), 422 GURL("file:///"))); 423 424 base::string16 expected_title16(ASCIIToUTF16("connection closed")); 425 TitleWatcher title_watcher(shell()->web_contents(), expected_title16); 426 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 427 } 428 429 class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest { 430 public: 431 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 432 command_line->AppendSwitch(switches::kSingleProcess); 433 } 434 }; 435 436 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, 437 RenderThreadShutdownTest) { 438 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); 439 } 440 441 } // namespace content 442