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 "chrome/browser/ui/webui/test_data_source.h"
6
7 #include <memory>
8
9 #include "base/base_paths.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/path_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/task/post_task.h"
16 #include "base/task/task_traits.h"
17 #include "base/task/thread_pool.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/common/webui_url_constants.h"
22 #include "content/public/browser/url_data_source.h"
23 #include "content/public/common/url_constants.h"
24 #include "services/network/public/mojom/content_security_policy.mojom.h"
25
26 namespace {
27 const char kModuleQuery[] = "module=";
28 } // namespace
29
TestDataSource(std::string root)30 TestDataSource::TestDataSource(std::string root) {
31 base::FilePath test_data;
32 CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data));
33 src_root_ = test_data.AppendASCII(root).NormalizePathSeparators();
34 DCHECK(test_data.IsParent(src_root_));
35
36 base::FilePath exe_dir;
37 base::PathService::Get(base::DIR_EXE, &exe_dir);
38 gen_root_ = exe_dir.AppendASCII("gen/chrome/test/data/" + root)
39 .NormalizePathSeparators();
40 DCHECK(exe_dir.IsParent(gen_root_));
41 }
42
GetSource()43 std::string TestDataSource::GetSource() {
44 return "test";
45 }
46
StartDataRequest(const GURL & url,const content::WebContents::Getter & wc_getter,content::URLDataSource::GotDataCallback callback)47 void TestDataSource::StartDataRequest(
48 const GURL& url,
49 const content::WebContents::Getter& wc_getter,
50 content::URLDataSource::GotDataCallback callback) {
51 const std::string path = content::URLDataSource::URLToRequestPath(url);
52 base::ThreadPool::PostTask(
53 FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
54 base::BindOnce(&TestDataSource::ReadFile, base::Unretained(this), path,
55 std::move(callback)));
56 }
57
GetMimeType(const std::string & path)58 std::string TestDataSource::GetMimeType(const std::string& path) {
59 if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII) ||
60 base::StartsWith(GetURLForPath(path).query(), kModuleQuery,
61 base::CompareCase::INSENSITIVE_ASCII)) {
62 // Direct request for HTML, or autogenerated HTML response for module query.
63 return "text/html";
64 }
65 // The test data source currently only serves HTML and JS.
66 CHECK(base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
67 << "Tried to read file with unexpected type from test data source: "
68 << path;
69 return "application/javascript";
70 }
71
ShouldServeMimeTypeAsContentTypeHeader()72 bool TestDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
73 return true;
74 }
75
AllowCaching()76 bool TestDataSource::AllowCaching() {
77 return false;
78 }
79
GetContentSecurityPolicy(network::mojom::CSPDirectiveName directive)80 std::string TestDataSource::GetContentSecurityPolicy(
81 network::mojom::CSPDirectiveName directive) {
82 if (directive == network::mojom::CSPDirectiveName::ScriptSrc) {
83 return "script-src chrome://* 'self';";
84 } else if (directive == network::mojom::CSPDirectiveName::WorkerSrc) {
85 return "worker-src blob: 'self';";
86 } else if (directive ==
87 network::mojom::CSPDirectiveName::RequireTrustedTypesFor ||
88 directive == network::mojom::CSPDirectiveName::TrustedTypes) {
89 return std::string();
90 }
91
92 return content::URLDataSource::GetContentSecurityPolicy(directive);
93 }
94
GetURLForPath(const std::string & path)95 GURL TestDataSource::GetURLForPath(const std::string& path) {
96 return GURL(std::string(content::kChromeUIScheme) + "://" + GetSource() +
97 "/" + path);
98 }
99
ReadFile(const std::string & path,content::URLDataSource::GotDataCallback callback)100 void TestDataSource::ReadFile(
101 const std::string& path,
102 content::URLDataSource::GotDataCallback callback) {
103 std::string content;
104
105 GURL url = GetURLForPath(path);
106 CHECK(url.is_valid());
107 if (url.path() == "/chai.js") {
108 base::FilePath src_root;
109 CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_root));
110 base::FilePath file_path =
111 src_root.AppendASCII("third_party/chaijs/chai.js")
112 .NormalizePathSeparators();
113 CHECK(base::ReadFileToString(file_path, &content))
114 << url.spec() << "=" << file_path.value();
115 scoped_refptr<base::RefCountedString> response =
116 base::RefCountedString::TakeString(&content);
117 std::move(callback).Run(response.get());
118 return;
119 }
120
121 if (base::StartsWith(url.query(), kModuleQuery,
122 base::CompareCase::INSENSITIVE_ASCII)) {
123 std::string js_path = url.query().substr(strlen(kModuleQuery));
124
125 base::FilePath file_path =
126 src_root_.Append(base::FilePath::FromUTF8Unsafe(js_path));
127 // Do some basic validation of the JS file path provided in the query.
128 CHECK_EQ(file_path.Extension(), FILE_PATH_LITERAL(".js"));
129
130 base::FilePath file_path2 =
131 gen_root_.Append(base::FilePath::FromUTF8Unsafe(js_path));
132 CHECK(base::PathExists(file_path) || base::PathExists(file_path2))
133 << url.spec() << "=" << file_path.value();
134 content = "<script type=\"module\" src=\"" + js_path + "\"></script>";
135 } else {
136 // Try the |src_root_| folder first.
137 base::FilePath file_path =
138 src_root_.Append(base::FilePath::FromUTF8Unsafe(path));
139 if (base::PathExists(file_path)) {
140 CHECK(base::ReadFileToString(file_path, &content))
141 << url.spec() << "=" << file_path.value();
142 } else {
143 // Then try the |gen_root_| folder, covering cases where the test file is
144 // generated at build time.
145 base::FilePath file_path =
146 gen_root_.Append(base::FilePath::FromUTF8Unsafe(path));
147 CHECK(base::ReadFileToString(file_path, &content))
148 << url.spec() << "=" << file_path.value();
149 }
150 }
151
152 scoped_refptr<base::RefCountedString> response =
153 base::RefCountedString::TakeString(&content);
154 std::move(callback).Run(response.get());
155 }
156