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/files/file.h"
      8 #include "base/files/file_enumerator.h"
      9 #include "base/files/file_path.h"
     10 #include "base/files/file_util.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "base/test/thread_test_helper.h"
     16 #include "content/browser/browser_main_loop.h"
     17 #include "content/browser/indexed_db/indexed_db_class_factory.h"
     18 #include "content/browser/indexed_db/indexed_db_context_impl.h"
     19 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
     20 #include "content/browser/web_contents/web_contents_impl.h"
     21 #include "content/public/browser/browser_context.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/browser/render_process_host.h"
     24 #include "content/public/browser/storage_partition.h"
     25 #include "content/public/browser/web_contents.h"
     26 #include "content/public/common/content_switches.h"
     27 #include "content/public/common/url_constants.h"
     28 #include "content/public/test/browser_test_utils.h"
     29 #include "content/public/test/content_browser_test.h"
     30 #include "content/public/test/content_browser_test_utils.h"
     31 #include "content/shell/browser/shell.h"
     32 #include "net/base/escape.h"
     33 #include "net/base/net_errors.h"
     34 #include "net/test/embedded_test_server/embedded_test_server.h"
     35 #include "net/test/embedded_test_server/http_request.h"
     36 #include "net/test/embedded_test_server/http_response.h"
     37 #include "storage/browser/database/database_util.h"
     38 #include "storage/browser/quota/quota_manager.h"
     39 
     40 using base::ASCIIToUTF16;
     41 using storage::QuotaManager;
     42 using storage::DatabaseUtil;
     43 
     44 namespace content {
     45 
     46 // This browser test is aimed towards exercising the IndexedDB bindings and
     47 // the actual implementation that lives in the browser side.
     48 class IndexedDBBrowserTest : public ContentBrowserTest {
     49  public:
     50   IndexedDBBrowserTest() : disk_usage_(-1) {}
     51 
     52   virtual void SetUp() OVERRIDE {
     53     GetTestClassFactory()->Reset();
     54     IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
     55     ContentBrowserTest::SetUp();
     56   }
     57 
     58   virtual void TearDown() OVERRIDE {
     59     IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL);
     60     ContentBrowserTest::TearDown();
     61   }
     62 
     63   void FailOperation(FailClass failure_class,
     64                      FailMethod failure_method,
     65                      int fail_on_instance_num,
     66                      int fail_on_call_num) {
     67     GetTestClassFactory()->FailOperation(
     68         failure_class, failure_method, fail_on_instance_num, fail_on_call_num);
     69   }
     70 
     71   void SimpleTest(const GURL& test_url, bool incognito = false) {
     72     // The test page will perform tests on IndexedDB, then navigate to either
     73     // a #pass or #fail ref.
     74     Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
     75 
     76     VLOG(0) << "Navigating to URL and blocking.";
     77     NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
     78     VLOG(0) << "Navigation done.";
     79     std::string result =
     80         the_browser->web_contents()->GetLastCommittedURL().ref();
     81     if (result != "pass") {
     82       std::string js_result;
     83       ASSERT_TRUE(ExecuteScriptAndExtractString(
     84           the_browser->web_contents(),
     85           "window.domAutomationController.send(getLog())",
     86           &js_result));
     87       FAIL() << "Failed: " << js_result;
     88     }
     89   }
     90 
     91   void NavigateAndWaitForTitle(Shell* shell,
     92                                const char* filename,
     93                                const char* hash,
     94                                const char* expected_string) {
     95     GURL url = GetTestUrl("indexeddb", filename);
     96     if (hash)
     97       url = GURL(url.spec() + hash);
     98 
     99     base::string16 expected_title16(ASCIIToUTF16(expected_string));
    100     TitleWatcher title_watcher(shell->web_contents(), expected_title16);
    101     NavigateToURL(shell, url);
    102     EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
    103   }
    104 
    105   IndexedDBContextImpl* GetContext() {
    106     StoragePartition* partition =
    107         BrowserContext::GetDefaultStoragePartition(
    108             shell()->web_contents()->GetBrowserContext());
    109     return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext());
    110   }
    111 
    112   void SetQuota(int quotaKilobytes) {
    113     const int kTemporaryStorageQuotaSize = quotaKilobytes
    114         * 1024 * QuotaManager::kPerHostTemporaryPortion;
    115     SetTempQuota(kTemporaryStorageQuotaSize,
    116         BrowserContext::GetDefaultStoragePartition(
    117             shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
    118   }
    119 
    120   static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) {
    121     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    122       BrowserThread::PostTask(
    123           BrowserThread::IO, FROM_HERE,
    124           base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm));
    125       return;
    126     }
    127     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    128     qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback());
    129     // Don't return until the quota has been set.
    130     scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
    131         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)));
    132     ASSERT_TRUE(helper->Run());
    133   }
    134 
    135   virtual int64 RequestDiskUsage() {
    136     PostTaskAndReplyWithResult(
    137         GetContext()->TaskRunner(),
    138         FROM_HERE,
    139         base::Bind(&IndexedDBContext::GetOriginDiskUsage,
    140                    GetContext(),
    141                    GURL("file:///")),
    142         base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage, this));
    143     scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
    144         BrowserMainLoop::GetInstance()->indexed_db_thread()->
    145             message_loop_proxy()));
    146     EXPECT_TRUE(helper->Run());
    147     // Wait for DidGetDiskUsage to be called.
    148     base::MessageLoop::current()->RunUntilIdle();
    149     return disk_usage_;
    150   }
    151 
    152  private:
    153   static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() {
    154     static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky
    155         s_factory = LAZY_INSTANCE_INITIALIZER;
    156     return s_factory.Pointer();
    157   }
    158 
    159   static IndexedDBClassFactory* GetIDBClassFactory() {
    160     return GetTestClassFactory();
    161   }
    162 
    163   virtual void DidGetDiskUsage(int64 bytes) {
    164     EXPECT_GT(bytes, 0);
    165     disk_usage_ = bytes;
    166   }
    167 
    168   int64 disk_usage_;
    169 
    170   DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest);
    171 };
    172 
    173 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) {
    174   SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
    175 }
    176 
    177 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) {
    178   SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
    179              true /* incognito */);
    180 }
    181 
    182 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) {
    183   SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
    184 }
    185 
    186 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) {
    187   SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
    188 }
    189 
    190 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) {
    191   SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
    192 }
    193 
    194 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) {
    195   SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
    196 }
    197 
    198 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) {
    199   SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
    200 }
    201 
    202 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) {
    203   SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
    204 }
    205 
    206 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) {
    207   SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
    208 }
    209 
    210 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) {
    211   SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
    212 }
    213 
    214 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) {
    215   SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
    216 }
    217 
    218 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DoesntHangTest) {
    219   SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html"));
    220   CrashTab(shell()->web_contents());
    221   SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html"));
    222 }
    223 
    224 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) {
    225   const GURL url = GetTestUrl("indexeddb", "bug_84933.html");
    226 
    227   // Just navigate to the URL. Test will crash if it fails.
    228   NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
    229 }
    230 
    231 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) {
    232   const GURL url = GetTestUrl("indexeddb", "bug_106883.html");
    233 
    234   // Just navigate to the URL. Test will crash if it fails.
    235   NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
    236 }
    237 
    238 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) {
    239   const GURL url = GetTestUrl("indexeddb", "bug_109187.html");
    240 
    241   // Just navigate to the URL. Test will crash if it fails.
    242   NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
    243 }
    244 
    245 class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest {
    246  public:
    247   IndexedDBBrowserTestWithLowQuota() {}
    248 
    249   virtual void SetUpOnMainThread() OVERRIDE {
    250     const int kInitialQuotaKilobytes = 5000;
    251     SetQuota(kInitialQuotaKilobytes);
    252   }
    253 
    254  private:
    255   DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota);
    256 };
    257 
    258 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
    259   SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
    260 }
    261 
    262 class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest {
    263  public:
    264   IndexedDBBrowserTestWithGCExposed() {}
    265 
    266   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    267     command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc");
    268   }
    269 
    270  private:
    271   DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed);
    272 };
    273 
    274 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
    275                        DatabaseCallbacksTest) {
    276   SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
    277 }
    278 
    279 static void CopyLevelDBToProfile(Shell* shell,
    280                                  scoped_refptr<IndexedDBContextImpl> context,
    281                                  const std::string& test_directory) {
    282   DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
    283   base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
    284   base::FilePath test_data_dir =
    285       GetTestFilePath("indexeddb", test_directory.c_str()).Append(leveldb_dir);
    286   base::FilePath dest = context->data_path().Append(leveldb_dir);
    287   // If we don't create the destination directory first, the contents of the
    288   // leveldb directory are copied directly into profile/IndexedDB instead of
    289   // profile/IndexedDB/file__0.xxx/
    290   ASSERT_TRUE(base::CreateDirectory(dest));
    291   const bool kRecursive = true;
    292   ASSERT_TRUE(base::CopyDirectory(test_data_dir,
    293                                   context->data_path(),
    294                                   kRecursive));
    295 }
    296 
    297 class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest {
    298  public:
    299   IndexedDBBrowserTestWithPreexistingLevelDB() {}
    300   virtual void SetUpOnMainThread() OVERRIDE {
    301     scoped_refptr<IndexedDBContextImpl> context = GetContext();
    302     context->TaskRunner()->PostTask(
    303         FROM_HERE,
    304         base::Bind(
    305             &CopyLevelDBToProfile, shell(), context, EnclosingLevelDBDir()));
    306     scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
    307         BrowserMainLoop::GetInstance()->indexed_db_thread()->
    308             message_loop_proxy()));
    309     ASSERT_TRUE(helper->Run());
    310   }
    311 
    312   virtual std::string EnclosingLevelDBDir() = 0;
    313 
    314  private:
    315   DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB);
    316 };
    317 
    318 class IndexedDBBrowserTestWithVersion0Schema : public
    319     IndexedDBBrowserTestWithPreexistingLevelDB {
    320   virtual std::string EnclosingLevelDBDir() OVERRIDE {
    321     return "migration_from_0";
    322   }
    323 };
    324 
    325 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) {
    326   SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
    327 }
    328 
    329 class IndexedDBBrowserTestWithVersion123456Schema : public
    330     IndexedDBBrowserTestWithPreexistingLevelDB {
    331   virtual std::string EnclosingLevelDBDir() OVERRIDE {
    332     return "schema_version_123456";
    333   }
    334 };
    335 
    336 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema,
    337                        DestroyTest) {
    338   int64 original_size = RequestDiskUsage();
    339   EXPECT_GT(original_size, 0);
    340   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
    341   int64 new_size = RequestDiskUsage();
    342   EXPECT_NE(original_size, new_size);
    343 }
    344 
    345 class IndexedDBBrowserTestWithVersion987654SSVData : public
    346     IndexedDBBrowserTestWithPreexistingLevelDB {
    347   virtual std::string EnclosingLevelDBDir() OVERRIDE {
    348     return "ssv_version_987654";
    349   }
    350 };
    351 
    352 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData,
    353                        DestroyTest) {
    354   int64 original_size = RequestDiskUsage();
    355   EXPECT_GT(original_size, 0);
    356   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
    357   int64 new_size = RequestDiskUsage();
    358   EXPECT_NE(original_size, new_size);
    359 }
    360 
    361 class IndexedDBBrowserTestWithCorruptLevelDB : public
    362     IndexedDBBrowserTestWithPreexistingLevelDB {
    363   virtual std::string EnclosingLevelDBDir() OVERRIDE {
    364     return "corrupt_leveldb";
    365   }
    366 };
    367 
    368 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB,
    369                        DestroyTest) {
    370   int64 original_size = RequestDiskUsage();
    371   EXPECT_GT(original_size, 0);
    372   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
    373   int64 new_size = RequestDiskUsage();
    374   EXPECT_NE(original_size, new_size);
    375 }
    376 
    377 class IndexedDBBrowserTestWithMissingSSTFile : public
    378     IndexedDBBrowserTestWithPreexistingLevelDB {
    379   virtual std::string EnclosingLevelDBDir() OVERRIDE {
    380     return "missing_sst";
    381   }
    382 };
    383 
    384 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile,
    385                        DestroyTest) {
    386   int64 original_size = RequestDiskUsage();
    387   EXPECT_GT(original_size, 0);
    388   SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html"));
    389   int64 new_size = RequestDiskUsage();
    390   EXPECT_NE(original_size, new_size);
    391 }
    392 
    393 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, LevelDBLogFileTest) {
    394   // Any page that opens an IndexedDB will work here.
    395   SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
    396   base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
    397   base::FilePath log_file(FILE_PATH_LITERAL("LOG"));
    398   base::FilePath log_file_path =
    399       GetContext()->data_path().Append(leveldb_dir).Append(log_file);
    400   int64 size;
    401   EXPECT_TRUE(base::GetFileSize(log_file_path, &size));
    402   EXPECT_GT(size, 0);
    403 }
    404 
    405 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) {
    406   SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html"));
    407   int64 size = RequestDiskUsage();
    408   const int kQuotaKilobytes = 2;
    409   EXPECT_GT(size, kQuotaKilobytes * 1024);
    410   SetQuota(kQuotaKilobytes);
    411   SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
    412 }
    413 
    414 namespace {
    415 
    416 static void CompactIndexedDBBackingStore(
    417     scoped_refptr<IndexedDBContextImpl> context,
    418     const GURL& origin_url) {
    419   IndexedDBFactory* factory = context->GetIDBFactory();
    420 
    421   std::pair<IndexedDBFactory::OriginDBMapIterator,
    422             IndexedDBFactory::OriginDBMapIterator> range =
    423       factory->GetOpenDatabasesForOrigin(origin_url);
    424 
    425   if (range.first == range.second)  // If no open db's for this origin
    426     return;
    427 
    428   // Compact the first db's backing store since all the db's are in the same
    429   // backing store.
    430   IndexedDBDatabase* db = range.first->second;
    431   IndexedDBBackingStore* backing_store = db->backing_store();
    432   backing_store->Compact();
    433 }
    434 
    435 static void CorruptIndexedDBDatabase(
    436     IndexedDBContextImpl* context,
    437     const GURL& origin_url,
    438     base::WaitableEvent* signal_when_finished) {
    439 
    440   CompactIndexedDBBackingStore(context, origin_url);
    441 
    442   int numFiles = 0;
    443   int numErrors = 0;
    444   base::FilePath idb_data_path = context->GetFilePath(origin_url);
    445   const bool recursive = false;
    446   base::FileEnumerator enumerator(
    447       idb_data_path, recursive, base::FileEnumerator::FILES);
    448   for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty();
    449        idb_file = enumerator.Next()) {
    450     int64 size(0);
    451     GetFileSize(idb_file, &size);
    452 
    453     if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) {
    454       numFiles++;
    455       base::File file(idb_file,
    456                       base::File::FLAG_WRITE | base::File::FLAG_OPEN_TRUNCATED);
    457       if (file.IsValid()) {
    458         // Was opened truncated, expand back to the original
    459         // file size and fill with zeros (corrupting the file).
    460         file.SetLength(size);
    461       } else {
    462         numErrors++;
    463       }
    464     }
    465   }
    466 
    467   VLOG(0) << "There were " << numFiles << " in " << idb_data_path.value()
    468           << " with " << numErrors << " errors";
    469   signal_when_finished->Signal();
    470 }
    471 
    472 const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
    473 
    474 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
    475     IndexedDBContextImpl* context,
    476     const GURL& origin_url,
    477     const std::string& path,
    478     IndexedDBBrowserTest* test,
    479     const net::test_server::HttpRequest& request) {
    480   std::string request_path;
    481   if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
    482     request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
    483   else
    484     return scoped_ptr<net::test_server::HttpResponse>();
    485 
    486   // Remove the query string if present.
    487   std::string request_query;
    488   size_t query_pos = request_path.find('?');
    489   if (query_pos != std::string::npos) {
    490     request_query = request_path.substr(query_pos + 1);
    491     request_path = request_path.substr(0, query_pos);
    492   }
    493 
    494   if (request_path == "corruptdb" && !request_query.empty()) {
    495     VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
    496     base::WaitableEvent signal_when_finished(false, false);
    497     context->TaskRunner()->PostTask(FROM_HERE,
    498                                     base::Bind(&CorruptIndexedDBDatabase,
    499                                                base::ConstRef(context),
    500                                                origin_url,
    501                                                &signal_when_finished));
    502     signal_when_finished.Wait();
    503 
    504     scoped_ptr<net::test_server::BasicHttpResponse> http_response(
    505         new net::test_server::BasicHttpResponse);
    506     http_response->set_code(net::HTTP_OK);
    507     return http_response.PassAs<net::test_server::HttpResponse>();
    508   } else if (request_path == "fail" && !request_query.empty()) {
    509     FailClass failure_class = FAIL_CLASS_NOTHING;
    510     FailMethod failure_method = FAIL_METHOD_NOTHING;
    511     int instance_num = 1;
    512     int call_num = 1;
    513     std::string fail_class;
    514     std::string fail_method;
    515 
    516     url::Component query(0, request_query.length()), key_pos, value_pos;
    517     while (url::ExtractQueryKeyValue(
    518         request_query.c_str(), &query, &key_pos, &value_pos)) {
    519       std::string escaped_key(request_query.substr(key_pos.begin, key_pos.len));
    520       std::string escaped_value(
    521           request_query.substr(value_pos.begin, value_pos.len));
    522 
    523       std::string key = net::UnescapeURLComponent(
    524           escaped_key,
    525           net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
    526               net::UnescapeRule::URL_SPECIAL_CHARS);
    527 
    528       std::string value = net::UnescapeURLComponent(
    529           escaped_value,
    530           net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
    531               net::UnescapeRule::URL_SPECIAL_CHARS);
    532 
    533       if (key == "method")
    534         fail_method = value;
    535       else if (key == "class")
    536         fail_class = value;
    537       else if (key == "instNum")
    538         instance_num = atoi(value.c_str());
    539       else if (key == "callNum")
    540         call_num = atoi(value.c_str());
    541       else
    542         NOTREACHED() << "Unknown param: \"" << key << "\"";
    543     }
    544 
    545     if (fail_class == "LevelDBTransaction") {
    546       failure_class = FAIL_CLASS_LEVELDB_TRANSACTION;
    547       if (fail_method == "Get")
    548         failure_method = FAIL_METHOD_GET;
    549       else if (fail_method == "Commit")
    550         failure_method = FAIL_METHOD_COMMIT;
    551       else
    552         NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
    553     } else if (fail_class == "LevelDBIterator") {
    554       failure_class = FAIL_CLASS_LEVELDB_ITERATOR;
    555       if (fail_method == "Seek")
    556         failure_method = FAIL_METHOD_SEEK;
    557       else
    558         NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
    559     } else {
    560       NOTREACHED() << "Unknown class: \"" << fail_class << "\"";
    561     }
    562 
    563     DCHECK_GE(instance_num, 1);
    564     DCHECK_GE(call_num, 1);
    565 
    566     test->FailOperation(failure_class, failure_method, instance_num, call_num);
    567 
    568     scoped_ptr<net::test_server::BasicHttpResponse> http_response(
    569         new net::test_server::BasicHttpResponse);
    570     http_response->set_code(net::HTTP_OK);
    571     return http_response.PassAs<net::test_server::HttpResponse>();
    572   }
    573 
    574   // A request for a test resource
    575   base::FilePath resourcePath =
    576       content::GetTestFilePath("indexeddb", request_path.c_str());
    577   scoped_ptr<net::test_server::BasicHttpResponse> http_response(
    578       new net::test_server::BasicHttpResponse);
    579   http_response->set_code(net::HTTP_OK);
    580   std::string file_contents;
    581   if (!base::ReadFileToString(resourcePath, &file_contents))
    582     return scoped_ptr<net::test_server::HttpResponse>();
    583   http_response->set_content(file_contents);
    584   return http_response.PassAs<net::test_server::HttpResponse>();
    585 }
    586 
    587 }  // namespace
    588 
    589 class IndexedDBBrowserCorruptionTest
    590     : public IndexedDBBrowserTest,
    591       public ::testing::WithParamInterface<const char*> {};
    592 
    593 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
    594                        OperationOnCorruptedOpenDatabase) {
    595   ASSERT_TRUE(embedded_test_server()->Started() ||
    596               embedded_test_server()->InitializeAndWaitUntilReady());
    597   const GURL& origin_url = embedded_test_server()->base_url();
    598   embedded_test_server()->RegisterRequestHandler(
    599       base::Bind(&CorruptDBRequestHandler,
    600                  base::Unretained(GetContext()),
    601                  origin_url,
    602                  s_corrupt_db_test_prefix,
    603                  this));
    604 
    605   std::string test_file = s_corrupt_db_test_prefix +
    606                           "corrupted_open_db_detection.html#" + GetParam();
    607   SimpleTest(embedded_test_server()->GetURL(test_file));
    608 
    609   test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
    610   SimpleTest(embedded_test_server()->GetURL(test_file));
    611 }
    612 
    613 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
    614                         IndexedDBBrowserCorruptionTest,
    615                         ::testing::Values("failGetBlobJournal",
    616                                           "get",
    617                                           "failWebkitGetDatabaseNames",
    618                                           "iterate",
    619                                           "failTransactionCommit",
    620                                           "clearObjectStore"));
    621 
    622 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
    623                        DeleteCompactsBackingStore) {
    624   const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
    625   SimpleTest(GURL(test_url.spec() + "#fill"));
    626   int64 after_filling = RequestDiskUsage();
    627   EXPECT_GT(after_filling, 0);
    628 
    629   SimpleTest(GURL(test_url.spec() + "#purge"));
    630   int64 after_deleting = RequestDiskUsage();
    631   EXPECT_LT(after_deleting, after_filling);
    632 
    633   // The above tests verify basic assertions - that filling writes data and
    634   // deleting reduces the amount stored.
    635 
    636   // The below tests make assumptions about implementation specifics, such as
    637   // data compression, compaction efficiency, and the maximum amount of
    638   // metadata and log data remains after a deletion. It is possible that
    639   // changes to the implementation may require these constants to be tweaked.
    640 
    641   const int kTestFillBytes = 1024 * 1024 * 5;  // 5MB
    642   EXPECT_GT(after_filling, kTestFillBytes);
    643 
    644   const int kTestCompactBytes = 1024 * 10;  // 10kB
    645   EXPECT_LT(after_deleting, kTestCompactBytes);
    646 }
    647 
    648 // Complex multi-step (converted from pyauto) tests begin here.
    649 
    650 // Verify null key path persists after restarting browser.
    651 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) {
    652   NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
    653                           "pass - first run");
    654 }
    655 
    656 // Verify null key path persists after restarting browser.
    657 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) {
    658   NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
    659                           "pass - second run");
    660 }
    661 
    662 // Verify that a VERSION_CHANGE transaction is rolled back after a
    663 // renderer/browser crash
    664 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
    665                        PRE_PRE_VersionChangeCrashResilience) {
    666   NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
    667                           "pass - part1 - complete");
    668 }
    669 
    670 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) {
    671   NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
    672                           "pass - part2 - crash me");
    673   // If we actually crash here then googletest will not run the next step
    674   // (VersionChangeCrashResilience) as an optimization. googletest's
    675   // ASSERT_DEATH/EXIT fails to work properly (on Windows) due to how we
    676   // implement the PRE_* test mechanism.
    677   exit(0);
    678 }
    679 
    680 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) {
    681   NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
    682                           "pass - part3 - rolled back");
    683 }
    684 
    685 // Verify that open DB connections are closed when a tab is destroyed.
    686 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) {
    687   NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
    688                           "setVersion(2) complete");
    689 
    690   // Start on a different URL to force a new renderer process.
    691   Shell* new_shell = CreateBrowser();
    692   NavigateToURL(new_shell, GURL(url::kAboutBlankURL));
    693   NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2",
    694                           "setVersion(3) blocked");
    695 
    696   base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete"));
    697   TitleWatcher title_watcher(new_shell->web_contents(), expected_title16);
    698 
    699   base::KillProcess(
    700       shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true);
    701   shell()->Close();
    702 
    703   EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
    704 }
    705 
    706 // Verify that a "close" event is fired at database connections when
    707 // the backing store is deleted.
    708 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
    709   NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL,
    710                           "connection ready");
    711 
    712   GetContext()->TaskRunner()->PostTask(
    713       FROM_HERE,
    714       base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
    715                  GetContext(),
    716                  GURL("file:///")));
    717 
    718   base::string16 expected_title16(ASCIIToUTF16("connection closed"));
    719   TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
    720   title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
    721   EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
    722 }
    723 
    724 class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest {
    725  public:
    726   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    727     command_line->AppendSwitch(switches::kSingleProcess);
    728   }
    729 };
    730 
    731 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
    732                        RenderThreadShutdownTest) {
    733   SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
    734 }
    735 
    736 }  // namespace content
    737