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.h"
9#include "base/files/file_enumerator.h"
10#include "base/files/file_path.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 "webkit/browser/database/database_util.h"
38#include "webkit/browser/quota/quota_manager.h"
39
40using base::ASCIIToUTF16;
41using quota::QuotaManager;
42using webkit_database::DatabaseUtil;
43
44namespace content {
45
46// This browser test is aimed towards exercising the IndexedDB bindings and
47// the actual implementation that lives in the browser side.
48class 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, quota::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
173IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) {
174  SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
175}
176
177IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) {
178  SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
179             true /* incognito */);
180}
181
182IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) {
183  SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
184}
185
186IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) {
187  SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
188}
189
190IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) {
191  SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
192}
193
194IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) {
195  SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
196}
197
198IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) {
199  SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
200}
201
202IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) {
203  SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
204}
205
206IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) {
207  SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
208}
209
210IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) {
211  SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
212}
213
214IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) {
215  SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
216}
217
218IN_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
224IN_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
231IN_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
238IN_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
245class 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
258IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
259  SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
260}
261
262class 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
274IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
275                       DatabaseCallbacksTest) {
276  SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
277}
278
279static 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
297class 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
318class IndexedDBBrowserTestWithVersion0Schema : public
319    IndexedDBBrowserTestWithPreexistingLevelDB {
320  virtual std::string EnclosingLevelDBDir() OVERRIDE {
321    return "migration_from_0";
322  }
323};
324
325IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) {
326  SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
327}
328
329class IndexedDBBrowserTestWithVersion123456Schema : public
330    IndexedDBBrowserTestWithPreexistingLevelDB {
331  virtual std::string EnclosingLevelDBDir() OVERRIDE {
332    return "schema_version_123456";
333  }
334};
335
336IN_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
345class IndexedDBBrowserTestWithVersion987654SSVData : public
346    IndexedDBBrowserTestWithPreexistingLevelDB {
347  virtual std::string EnclosingLevelDBDir() OVERRIDE {
348    return "ssv_version_987654";
349  }
350};
351
352IN_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
361class IndexedDBBrowserTestWithCorruptLevelDB : public
362    IndexedDBBrowserTestWithPreexistingLevelDB {
363  virtual std::string EnclosingLevelDBDir() OVERRIDE {
364    return "corrupt_leveldb";
365  }
366};
367
368IN_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
377class IndexedDBBrowserTestWithMissingSSTFile : public
378    IndexedDBBrowserTestWithPreexistingLevelDB {
379  virtual std::string EnclosingLevelDBDir() OVERRIDE {
380    return "missing_sst";
381  }
382};
383
384IN_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
393IN_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
405IN_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
414namespace {
415
416static 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
435static 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
472const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
473
474static 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      }
554    }
555
556    DCHECK_GE(instance_num, 1);
557    DCHECK_GE(call_num, 1);
558
559    test->FailOperation(failure_class, failure_method, instance_num, call_num);
560
561    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
562        new net::test_server::BasicHttpResponse);
563    http_response->set_code(net::HTTP_OK);
564    return http_response.PassAs<net::test_server::HttpResponse>();
565  }
566
567  // A request for a test resource
568  base::FilePath resourcePath =
569      content::GetTestFilePath("indexeddb", request_path.c_str());
570  scoped_ptr<net::test_server::BasicHttpResponse> http_response(
571      new net::test_server::BasicHttpResponse);
572  http_response->set_code(net::HTTP_OK);
573  std::string file_contents;
574  if (!base::ReadFileToString(resourcePath, &file_contents))
575    return scoped_ptr<net::test_server::HttpResponse>();
576  http_response->set_content(file_contents);
577  return http_response.PassAs<net::test_server::HttpResponse>();
578}
579
580}  // namespace
581
582class IndexedDBBrowserCorruptionTest
583    : public IndexedDBBrowserTest,
584      public ::testing::WithParamInterface<const char*> {};
585
586IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
587                       OperationOnCorruptedOpenDatabase) {
588  ASSERT_TRUE(embedded_test_server()->Started() ||
589              embedded_test_server()->InitializeAndWaitUntilReady());
590  const GURL& origin_url = embedded_test_server()->base_url();
591  embedded_test_server()->RegisterRequestHandler(
592      base::Bind(&CorruptDBRequestHandler,
593                 base::ConstRef(GetContext()),
594                 origin_url,
595                 s_corrupt_db_test_prefix,
596                 this));
597
598  std::string test_file = s_corrupt_db_test_prefix +
599                          "corrupted_open_db_detection.html#" + GetParam();
600  SimpleTest(embedded_test_server()->GetURL(test_file));
601
602  test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
603  SimpleTest(embedded_test_server()->GetURL(test_file));
604}
605
606INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
607                        IndexedDBBrowserCorruptionTest,
608                        ::testing::Values("get",
609                                          "iterate",
610                                          "clearObjectStore"));
611
612// Crashes flakily on various platforms. crbug.com/375856
613IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
614                       DISABLED_DeleteCompactsBackingStore) {
615  const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
616  SimpleTest(GURL(test_url.spec() + "#fill"));
617  int64 after_filling = RequestDiskUsage();
618  EXPECT_GT(after_filling, 0);
619
620  SimpleTest(GURL(test_url.spec() + "#purge"));
621  int64 after_deleting = RequestDiskUsage();
622  EXPECT_LT(after_deleting, after_filling);
623
624  // The above tests verify basic assertions - that filling writes data and
625  // deleting reduces the amount stored.
626
627  // The below tests make assumptions about implementation specifics, such as
628  // data compression, compaction efficiency, and the maximum amount of
629  // metadata and log data remains after a deletion. It is possible that
630  // changes to the implementation may require these constants to be tweaked.
631
632  const int kTestFillBytes = 1024 * 1024 * 5;  // 5MB
633  EXPECT_GT(after_filling, kTestFillBytes);
634
635  const int kTestCompactBytes = 1024 * 1024 * 1;  // 1MB
636  EXPECT_LT(after_deleting, kTestCompactBytes);
637}
638
639// Complex multi-step (converted from pyauto) tests begin here.
640
641// Verify null key path persists after restarting browser.
642IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) {
643  NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
644                          "pass - first run");
645}
646
647// Verify null key path persists after restarting browser.
648IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) {
649  NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
650                          "pass - second run");
651}
652
653// Verify that a VERSION_CHANGE transaction is rolled back after a
654// renderer/browser crash
655IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
656                       PRE_PRE_VersionChangeCrashResilience) {
657  NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
658                          "pass - part1 - complete");
659}
660
661IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) {
662  NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
663                          "pass - part2 - crash me");
664  NavigateToURL(shell(), GURL(kChromeUIBrowserCrashHost));
665}
666
667IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) {
668  NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
669                          "pass - part3 - rolled back");
670}
671
672// Verify that open DB connections are closed when a tab is destroyed.
673IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) {
674  NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
675                          "setVersion(2) complete");
676
677  // Start on a different URL to force a new renderer process.
678  Shell* new_shell = CreateBrowser();
679  NavigateToURL(new_shell, GURL(url::kAboutBlankURL));
680  NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2",
681                          "setVersion(3) blocked");
682
683  base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete"));
684  TitleWatcher title_watcher(new_shell->web_contents(), expected_title16);
685
686  base::KillProcess(
687      shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true);
688  shell()->Close();
689
690  EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
691}
692
693// Verify that a "close" event is fired at database connections when
694// the backing store is deleted.
695IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
696  NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL,
697                          "connection ready");
698
699  GetContext()->TaskRunner()->PostTask(
700      FROM_HERE,
701      base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
702                 GetContext(),
703                 GURL("file:///")));
704
705  base::string16 expected_title16(ASCIIToUTF16("connection closed"));
706  TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
707  title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
708  EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
709}
710
711class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest {
712 public:
713  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
714    command_line->AppendSwitch(switches::kSingleProcess);
715  }
716};
717
718// Crashing on Android due to kSingleProcess flag: http://crbug.com/342525
719#if defined(OS_ANDROID)
720#define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest
721#else
722#define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest
723#endif
724IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
725                       MAYBE_RenderThreadShutdownTest) {
726  SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
727}
728
729}  // namespace content
730