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