1 // Copyright 2019 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 <lib/fdio/directory.h>
6 #include <lib/vfs/cpp/pseudo_dir.h>
7 #include <lib/vfs/cpp/vmo_file.h>
8 
9 #include "fuchsia/engine/test/web_engine_browser_test.h"
10 
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/fuchsia/file_utils.h"
14 #include "base/fuchsia/fuchsia_logging.h"
15 #include "base/path_service.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "content/public/test/browser_test.h"
18 #include "content/public/test/content_test_suite_base.h"
19 #include "fuchsia/base/frame_test_util.h"
20 #include "fuchsia/base/test_navigation_listener.h"
21 #include "fuchsia/engine/browser/content_directory_loader_factory.h"
22 #include "fuchsia/engine/switches.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "url/url_util.h"
25 
26 namespace {
27 
28 // Adds an in-memory file containing |data| to |dir| at the location |path|.
AddFileToPseudoDir(base::StringPiece data,const base::FilePath & path,vfs::PseudoDir * dir)29 void AddFileToPseudoDir(base::StringPiece data,
30                         const base::FilePath& path,
31                         vfs::PseudoDir* dir) {
32   zx::vmo contents_vmo;
33   zx_status_t status = zx::vmo::create(data.size(), 0, &contents_vmo);
34   ASSERT_EQ(status, ZX_OK);
35   status = contents_vmo.write(data.data(), 0, data.size());
36   ASSERT_EQ(status, ZX_OK);
37 
38   auto vmo_file = std::make_unique<vfs::VmoFile>(
39       std::move(contents_vmo), 0, data.size(),
40       vfs::VmoFile::WriteOption::READ_ONLY, vfs::VmoFile::Sharing::CLONE_COW);
41   status = dir->AddEntry(path.value(), std::move(vmo_file));
42   ASSERT_EQ(status, ZX_OK);
43 }
44 
45 // Serves |dir| as a ContentDirectory under the path |name|.
ServePseudoDir(base::StringPiece name,vfs::PseudoDir * dir)46 void ServePseudoDir(base::StringPiece name, vfs::PseudoDir* dir) {
47   fuchsia::web::ContentDirectoryProvider provider;
48   provider.set_name(name.as_string());
49   fidl::InterfaceHandle<fuchsia::io::Directory> directory_channel;
50   dir->Serve(
51       fuchsia::io::OPEN_FLAG_DIRECTORY | fuchsia::io::OPEN_RIGHT_READABLE,
52       directory_channel.NewRequest().TakeChannel());
53   provider.set_directory(std::move(directory_channel));
54   std::vector<fuchsia::web::ContentDirectoryProvider> providers;
55   providers.emplace_back(std::move(provider));
56   ContentDirectoryLoaderFactory::SetContentDirectoriesForTest(
57       std::move(providers));
58 }
59 
60 class ContentDirectoryTest : public cr_fuchsia::WebEngineBrowserTest {
61  public:
62   ContentDirectoryTest() = default;
63   ~ContentDirectoryTest() override = default;
64 
SetUp()65   void SetUp() override {
66     // Set this flag early so that the fuchsia-dir:// scheme will be
67     // registered at browser startup.
68     base::CommandLine::ForCurrentProcess()->AppendSwitch(
69         switches::kContentDirectories);
70 
71     // Scheme initialization for the WebEngineContentClient depends on the above
72     // command line modification, which won't have been present when the schemes
73     // were initially registered.
74     content::ContentTestSuiteBase::ReRegisterContentSchemes();
75 
76     cr_fuchsia::WebEngineBrowserTest::SetUp();
77   }
78 
SetUpOnMainThread()79   void SetUpOnMainThread() override {
80     std::vector<fuchsia::web::ContentDirectoryProvider> providers;
81 
82     fuchsia::web::ContentDirectoryProvider provider;
83     provider.set_name("testdata");
84     base::FilePath pkg_path;
85     base::PathService::Get(base::DIR_ASSETS, &pkg_path);
86     provider.set_directory(base::OpenDirectoryHandle(
87         pkg_path.AppendASCII("fuchsia/engine/test/data")));
88     providers.emplace_back(std::move(provider));
89 
90     provider = {};
91     provider.set_name("alternate");
92     provider.set_directory(base::OpenDirectoryHandle(
93         pkg_path.AppendASCII("fuchsia/engine/test/data")));
94     providers.emplace_back(std::move(provider));
95 
96     ContentDirectoryLoaderFactory::SetContentDirectoriesForTest(
97         std::move(providers));
98 
99     cr_fuchsia::WebEngineBrowserTest::SetUpOnMainThread();
100   }
101 
TearDown()102   void TearDown() override {
103     ContentDirectoryLoaderFactory::SetContentDirectoriesForTest({});
104   }
105 
106  protected:
107   // Creates a Frame with |navigation_listener_| attached.
CreateFrame()108   fuchsia::web::FramePtr CreateFrame() {
109     return WebEngineBrowserTest::CreateFrame(&navigation_listener_);
110   }
111 
112   cr_fuchsia::TestNavigationListener navigation_listener_;
113 
114  private:
115   url::ScopedSchemeRegistryForTests scoped_registry_;
116 
117   DISALLOW_COPY_AND_ASSIGN(ContentDirectoryTest);
118 };
119 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,Navigate)120 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, Navigate) {
121   const GURL kUrl("fuchsia-dir://testdata/title1.html");
122 
123   fuchsia::web::FramePtr frame = CreateFrame();
124 
125   fuchsia::web::NavigationControllerPtr controller;
126   frame->GetNavigationController(controller.NewRequest());
127 
128   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
129       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
130   navigation_listener_.RunUntilUrlEquals(kUrl);
131 }
132 
133 // Navigate to a resource stored under a secondary provider.
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,NavigateAlternate)134 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, NavigateAlternate) {
135   const GURL kUrl("fuchsia-dir://alternate/title1.html");
136 
137   fuchsia::web::FramePtr frame = CreateFrame();
138 
139   fuchsia::web::NavigationControllerPtr controller;
140   frame->GetNavigationController(controller.NewRequest());
141 
142   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
143       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
144   navigation_listener_.RunUntilUrlEquals(kUrl);
145 }
146 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,ScriptSubresource)147 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ScriptSubresource) {
148   const GURL kUrl("fuchsia-dir://testdata/include_script.html");
149 
150   fuchsia::web::FramePtr frame = CreateFrame();
151 
152   fuchsia::web::NavigationControllerPtr controller;
153   frame->GetNavigationController(controller.NewRequest());
154 
155   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
156       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
157   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "title set by script");
158 }
159 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,ImgSubresource)160 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ImgSubresource) {
161   const GURL kUrl("fuchsia-dir://testdata/include_image.html");
162 
163   fuchsia::web::FramePtr frame = CreateFrame();
164 
165   fuchsia::web::NavigationControllerPtr controller;
166   frame->GetNavigationController(controller.NewRequest());
167 
168   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
169       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
170   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "image fetched");
171 }
172 
173 // Reads content sourced from VFS PseudoDirs and VmoFiles.
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,FromVfsPseudoDir)174 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, FromVfsPseudoDir) {
175   base::ScopedAllowBlockingForTesting allow_block;
176 
177   std::string contents;
178   base::FilePath pkg_path;
179   base::PathService::Get(base::DIR_ASSETS, &pkg_path);
180   ASSERT_TRUE(base::ReadFileToString(
181       pkg_path.AppendASCII("fuchsia/engine/test/data/title1.html"), &contents));
182 
183   vfs::PseudoDir pseudo_dir;
184   AddFileToPseudoDir(contents, base::FilePath("title1.html"), &pseudo_dir);
185   ServePseudoDir("pseudo-dir", &pseudo_dir);
186 
187   // Access the VmoFile under the PseudoDir.
188   const GURL kUrl("fuchsia-dir://pseudo-dir/title1.html");
189   fuchsia::web::FramePtr frame = CreateFrame();
190   fuchsia::web::NavigationControllerPtr controller;
191   frame->GetNavigationController(controller.NewRequest());
192   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
193       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
194   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "title 1");
195 }
196 
197 // Verify that resource providers are origin-isolated.
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,ScriptSrcCrossOriginBlocked)198 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, ScriptSrcCrossOriginBlocked) {
199   const GURL kUrl("fuchsia-dir://testdata/cross_origin_include_script.html");
200 
201   fuchsia::web::FramePtr frame = CreateFrame();
202 
203   fuchsia::web::NavigationControllerPtr controller;
204   frame->GetNavigationController(controller.NewRequest());
205 
206   // If the cross-origin script succeeded, then we should see "title set by
207   // script". If "not clobbered" remains set, then we know that CROS enforcement
208   // is working.
209   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
210       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
211   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "same origin ftw");
212 }
213 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,CrossOriginImgBlocked)214 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, CrossOriginImgBlocked) {
215   const GURL kUrl("fuchsia-dir://testdata/cross_origin_include_image.html");
216 
217   fuchsia::web::FramePtr frame = CreateFrame();
218 
219   fuchsia::web::NavigationControllerPtr controller;
220   frame->GetNavigationController(controller.NewRequest());
221 
222   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
223       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
224 
225   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl, "image rejected");
226 }
227 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,MetadataFileParsed)228 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, MetadataFileParsed) {
229   const GURL kUrl("fuchsia-dir://testdata/mime_override.html");
230 
231   fuchsia::web::FramePtr frame = CreateFrame();
232 
233   fuchsia::web::NavigationControllerPtr controller;
234   frame->GetNavigationController(controller.NewRequest());
235 
236   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
237       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
238   navigation_listener_.RunUntilUrlAndTitleEquals(
239       kUrl, "content-type: text/bleep; charset=US-ASCII");
240 }
241 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,BadMetadataFile)242 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, BadMetadataFile) {
243   const GURL kUrl("fuchsia-dir://testdata/mime_override_invalid.html");
244 
245   fuchsia::web::FramePtr frame = CreateFrame();
246 
247   fuchsia::web::NavigationControllerPtr controller;
248   frame->GetNavigationController(controller.NewRequest());
249 
250   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
251       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
252   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl,
253                                                  "content-type: text/html");
254 }
255 
IN_PROC_BROWSER_TEST_F(ContentDirectoryTest,BigFilesAreSniffable)256 IN_PROC_BROWSER_TEST_F(ContentDirectoryTest, BigFilesAreSniffable) {
257   base::ScopedAllowBlockingForTesting allow_block;
258 
259   std::string contents;
260   base::FilePath pkg_path;
261   base::PathService::Get(base::DIR_ASSETS, &pkg_path);
262   ASSERT_TRUE(base::ReadFileToString(
263       pkg_path.AppendASCII("fuchsia/engine/test/data/mime_override.html"),
264       &contents));
265 
266   vfs::PseudoDir pseudo_dir;
267   AddFileToPseudoDir(contents, base::FilePath("test.html"), &pseudo_dir);
268 
269   // Produce an HTML file that's a megabyte in size by appending a lot of
270   // zeroes to the end of an existing HTML file.
271   contents.resize(1000000, ' ');
272   AddFileToPseudoDir(contents, base::FilePath("blob.bin"), &pseudo_dir);
273 
274   ServePseudoDir("pseudo-dir", &pseudo_dir);
275 
276   // Access the VmoFile under the PseudoDir.
277   const GURL kUrl("fuchsia-dir://pseudo-dir/test.html");
278   fuchsia::web::FramePtr frame = CreateFrame();
279   fuchsia::web::NavigationControllerPtr controller;
280   frame->GetNavigationController(controller.NewRequest());
281   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
282       controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
283   navigation_listener_.RunUntilUrlAndTitleEquals(kUrl,
284                                                  "content-type: text/html");
285 }
286 
287 }  // namespace
288