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/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