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