Home | History | Annotate | Download | only in indexed_db
      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