1 // Copyright 2020 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/run_loop.h"
6 #include "base/test/bind.h"
7 #include "base/test/scoped_feature_list.h"
8 #include "base/threading/sequenced_task_runner_handle.h"
9 #include "build/build_config.h"
10 #include "components/services/storage/public/mojom/storage_service.mojom.h"
11 #include "components/services/storage/public/mojom/test_api.test-mojom.h"
12 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
13 #include "content/browser/storage_partition_impl.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/common/content_features.h"
17 #include "content/public/test/browser_test.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "content/public/test/content_browser_test.h"
20 #include "content/public/test/content_browser_test_utils.h"
21 #include "content/shell/browser/shell.h"
22 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
23 
24 namespace content {
25 namespace {
26 
27 class StorageServiceSandboxBrowserTest : public ContentBrowserTest {
28  public:
StorageServiceSandboxBrowserTest()29   StorageServiceSandboxBrowserTest() {
30     // These tests only make sense when the service is running out-of-process
31     // with sandboxing enabled.
32     feature_list_.InitWithFeatures({features::kStorageServiceOutOfProcess}, {});
33   }
34 
dom_storage()35   DOMStorageContextWrapper* dom_storage() {
36     auto* partition = static_cast<StoragePartitionImpl*>(
37         BrowserContext::GetDefaultStoragePartition(
38             shell()->web_contents()->GetBrowserContext()));
39     return partition->GetDOMStorageContext();
40   }
41 
WaitForAnyLocalStorageDataAsync(base::OnceClosure callback)42   void WaitForAnyLocalStorageDataAsync(base::OnceClosure callback) {
43     dom_storage()->GetLocalStorageControl()->GetUsage(base::BindOnce(
44         [](StorageServiceSandboxBrowserTest* test, base::OnceClosure callback,
45            std::vector<storage::mojom::LocalStorageUsageInfoPtr> usage) {
46           if (!usage.empty()) {
47             std::move(callback).Run();
48             return;
49           }
50 
51           base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
52               FROM_HERE,
53               base::BindOnce(&StorageServiceSandboxBrowserTest::
54                                  WaitForAnyLocalStorageDataAsync,
55                              base::Unretained(test), std::move(callback)),
56               base::TimeDelta::FromMilliseconds(50));
57         },
58         this, std::move(callback)));
59   }
60 
WaitForAnyLocalStorageData()61   void WaitForAnyLocalStorageData() {
62     base::RunLoop loop;
63     WaitForAnyLocalStorageDataAsync(loop.QuitClosure());
64     loop.Run();
65   }
66 
FlushLocalStorage()67   void FlushLocalStorage() {
68     base::RunLoop loop;
69     dom_storage()->GetLocalStorageControl()->Flush(loop.QuitClosure());
70     loop.Run();
71   }
72 
GetTestApi()73   mojo::Remote<storage::mojom::TestApi>& GetTestApi() {
74     if (!test_api_) {
75       StoragePartitionImpl::GetStorageServiceForTesting()->BindTestApi(
76           test_api_.BindNewPipeAndPassReceiver().PassPipe());
77     }
78     return test_api_;
79   }
80 
81  private:
82   base::test::ScopedFeatureList feature_list_;
83   mojo::Remote<storage::mojom::TestApi> test_api_;
84 };
85 
IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest,BasicLaunch)86 IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest, BasicLaunch) {
87   // Basic smoke test to ensure that we can launch the Storage Service in a
88   // sandboxed and it won't crash immediately.
89   GetTestApi().FlushForTesting();
90   EXPECT_TRUE(GetTestApi().is_connected());
91 }
92 
IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest,PRE_DomStorage)93 IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest, PRE_DomStorage) {
94   EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
95   ignore_result(
96       EvalJs(shell()->web_contents(), R"(window.localStorage.yeet = 42)"));
97   WaitForAnyLocalStorageData();
98   FlushLocalStorage();
99 }
100 
IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest,DomStorage)101 IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest, DomStorage) {
102   // Tests that Local Storage data persists from the PRE test setup above,
103   // providing basic assurance that the sandboxed process is able to manipulate
104   // filesystem contents as needed.
105   EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
106   EXPECT_EQ("42",
107             EvalJs(shell()->web_contents(), R"(window.localStorage.yeet)"));
108 }
109 
IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest,CompactDatabase)110 IN_PROC_BROWSER_TEST_F(StorageServiceSandboxBrowserTest, CompactDatabase) {
111   // Tests that the sandboxed service can execute a LevelDB database compaction
112   // operation without crashing. If the service crashes, the sync call below
113   // will return false.
114   mojo::ScopedAllowSyncCallForTesting allow_sync_calls;
115   EXPECT_TRUE(
116       GetTestApi()->ForceLeveldbDatabaseCompaction("CompactDatabaseTestDb"));
117 }
118 
119 }  // namespace
120 }  // namespace content
121