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
25 namespace {
26 const char kModuleQuery[] = "module=";
27 } // namespace
28
TestDataSource(std::string root)29 TestDataSource::TestDataSource(std::string root) {
30 base::FilePath test_data;
31 CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data));
32 src_root_ = test_data.AppendASCII(root).NormalizePathSeparators();
33 DCHECK(test_data.IsParent(src_root_));
34
35 base::FilePath exe_dir;
36 base::PathService::Get(base::DIR_EXE, &exe_dir);
37 gen_root_ = exe_dir.AppendASCII("gen/chrome/test/data/" + root)
38 .NormalizePathSeparators();
39 DCHECK(exe_dir.IsParent(gen_root_));
40 }
41
GetSource()42 std::string TestDataSource::GetSource() {
43 return "test";
44 }
45
StartDataRequest(const GURL & url,const content::WebContents::Getter & wc_getter,content::URLDataSource::GotDataCallback callback)46 void TestDataSource::StartDataRequest(
47 const GURL& url,
48 const content::WebContents::Getter& wc_getter,
49 content::URLDataSource::GotDataCallback callback) {
50 const std::string path = content::URLDataSource::URLToRequestPath(url);
51 base::ThreadPool::PostTask(
52 FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
53 base::BindOnce(&TestDataSource::ReadFile, base::Unretained(this), path,
54 std::move(callback)));
55 }
56
GetMimeType(const std::string & path)57 std::string TestDataSource::GetMimeType(const std::string& path) {
58 if (base::EndsWith(path, ".html", base::CompareCase::INSENSITIVE_ASCII) ||
59 base::StartsWith(GetURLForPath(path).query(), kModuleQuery,
60 base::CompareCase::INSENSITIVE_ASCII)) {
61 // Direct request for HTML, or autogenerated HTML response for module query.
62 return "text/html";
63 }
64 // The test data source currently only serves HTML and JS.
65 CHECK(base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII))
66 << "Tried to read file with unexpected type from test data source: "
67 << path;
68 return "application/javascript";
69 }
70
ShouldServeMimeTypeAsContentTypeHeader()71 bool TestDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
72 return true;
73 }
74
AllowCaching()75 bool TestDataSource::AllowCaching() {
76 return false;
77 }
78
GetContentSecurityPolicyScriptSrc()79 std::string TestDataSource::GetContentSecurityPolicyScriptSrc() {
80 return "script-src chrome://* 'self';";
81 }
82
GetURLForPath(const std::string & path)83 GURL TestDataSource::GetURLForPath(const std::string& path) {
84 return GURL(std::string(content::kChromeUIScheme) + "://" + GetSource() +
85 "/" + path);
86 }
87
ReadFile(const std::string & path,content::URLDataSource::GotDataCallback callback)88 void TestDataSource::ReadFile(
89 const std::string& path,
90 content::URLDataSource::GotDataCallback callback) {
91 std::string content;
92
93 GURL url = GetURLForPath(path);
94 CHECK(url.is_valid());
95 if (base::StartsWith(url.query(), kModuleQuery,
96 base::CompareCase::INSENSITIVE_ASCII)) {
97 std::string js_path = url.query().substr(strlen(kModuleQuery));
98
99 base::FilePath file_path =
100 src_root_.Append(base::FilePath::FromUTF8Unsafe(js_path));
101 // Do some basic validation of the JS file path provided in the query.
102 CHECK_EQ(file_path.Extension(), FILE_PATH_LITERAL(".js"));
103
104 base::FilePath file_path2 =
105 gen_root_.Append(base::FilePath::FromUTF8Unsafe(js_path));
106 CHECK(base::PathExists(file_path) || base::PathExists(file_path2))
107 << url.spec() << "=" << file_path.value();
108 content = "<script type=\"module\" src=\"" + js_path + "\"></script>";
109 } else {
110 // Try the |src_root_| folder first.
111 base::FilePath file_path =
112 src_root_.Append(base::FilePath::FromUTF8Unsafe(path));
113 if (base::PathExists(file_path)) {
114 CHECK(base::ReadFileToString(file_path, &content))
115 << url.spec() << "=" << file_path.value();
116 } else {
117 // Then try the |gen_root_| folder, covering cases where the test file is
118 // generated at build time.
119 base::FilePath file_path =
120 gen_root_.Append(base::FilePath::FromUTF8Unsafe(path));
121 CHECK(base::ReadFileToString(file_path, &content))
122 << url.spec() << "=" << file_path.value();
123 }
124 }
125
126 scoped_refptr<base::RefCountedString> response =
127 base::RefCountedString::TakeString(&content);
128 std::move(callback).Run(response.get());
129 }
130