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 "chromeos/components/telemetry_extension_ui/telemetry_extension_untrusted_source.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/task/post_task.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool.h"
16 #include "chromeos/components/telemetry_extension_ui/url_constants.h"
17 #include "chromeos/constants/chromeos_switches.h"
18 #include "net/base/mime_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20
21 namespace chromeos {
22
23 namespace {
24
25 constexpr char kDefaultMime[] = "text/html";
26
GetTelemetryDirectory()27 base::FilePath GetTelemetryDirectory() {
28 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
29 bool has_switch =
30 command_line->HasSwitch(chromeos::switches::kTelemetryExtensionDirectory);
31
32 if (!has_switch) {
33 LOG(WARNING) << "Switch value '--telemetry-extension-dir' is not "
34 "specified, some resources might not be loaded";
35 return base::FilePath();
36 }
37
38 return base::FilePath(command_line->GetSwitchValueASCII(
39 chromeos::switches::kTelemetryExtensionDirectory));
40 }
41
ReadFile(const base::FilePath & path,content::URLDataSource::GotDataCallback callback)42 void ReadFile(const base::FilePath& path,
43 content::URLDataSource::GotDataCallback callback) {
44 std::string content;
45 if (!base::ReadFileToString(path, &content)) {
46 PLOG(ERROR) << "Failed to read content from file: " << path;
47 std::move(callback).Run(nullptr);
48 return;
49 }
50
51 scoped_refptr<base::RefCountedString> response =
52 base::RefCountedString::TakeString(&content);
53 std::move(callback).Run(response.get());
54 }
55
56 } // namespace
57
58 // static
59 std::unique_ptr<TelemetryExtensionUntrustedSource>
Create(std::string source)60 TelemetryExtensionUntrustedSource::Create(std::string source) {
61 return base::WrapUnique(new TelemetryExtensionUntrustedSource(source));
62 }
63
TelemetryExtensionUntrustedSource(const std::string & source)64 TelemetryExtensionUntrustedSource::TelemetryExtensionUntrustedSource(
65 const std::string& source)
66 : root_directory_(GetTelemetryDirectory()), source_(source) {}
67
68 TelemetryExtensionUntrustedSource::~TelemetryExtensionUntrustedSource() =
69 default;
70
AddResourcePath(base::StringPiece path,int resource_id)71 void TelemetryExtensionUntrustedSource::AddResourcePath(base::StringPiece path,
72 int resource_id) {
73 path_to_idr_map_[path.as_string()] = resource_id;
74 }
75
OverrideContentSecurityPolicy(network::mojom::CSPDirectiveName directive,const std::string & value)76 void TelemetryExtensionUntrustedSource::OverrideContentSecurityPolicy(
77 network::mojom::CSPDirectiveName directive,
78 const std::string& value) {
79 csp_overrides_map_.insert_or_assign(directive, value);
80 }
81
GetSource()82 std::string TelemetryExtensionUntrustedSource::GetSource() {
83 return source_;
84 }
85
StartDataRequest(const GURL & url,const content::WebContents::Getter & wc_getter,content::URLDataSource::GotDataCallback callback)86 void TelemetryExtensionUntrustedSource::StartDataRequest(
87 const GURL& url,
88 const content::WebContents::Getter& wc_getter,
89 content::URLDataSource::GotDataCallback callback) {
90 std::string path = content::URLDataSource::URLToRequestPath(url);
91 // Remove the query string for named resource lookups.
92 path = path.substr(0, path.find_first_of('?'));
93
94 base::Optional<int> resource_id = PathToIdr(path);
95 if (resource_id.has_value()) {
96 scoped_refptr<base::RefCountedMemory> response(
97 ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
98 resource_id.value()));
99 std::move(callback).Run(response.get());
100 return;
101 }
102
103 if (root_directory_.empty()) {
104 LOG(WARNING) << "Cannot load resources from disk, switch value "
105 "'--telemetry-extension-dir' is not specified";
106 std::move(callback).Run(nullptr);
107 return;
108 }
109
110 base::ThreadPool::PostTask(
111 FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
112 base::BindOnce(&ReadFile, root_directory_.Append(path),
113 std::move(callback)));
114 }
115
GetMimeType(const std::string & path)116 std::string TelemetryExtensionUntrustedSource::GetMimeType(
117 const std::string& path) {
118 const std::string ext = base::FilePath(path).Extension();
119 if (ext.empty()) {
120 return kDefaultMime;
121 }
122
123 std::string mime_type;
124 net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type);
125 return mime_type;
126 }
127
GetContentSecurityPolicy(network::mojom::CSPDirectiveName directive)128 std::string TelemetryExtensionUntrustedSource::GetContentSecurityPolicy(
129 network::mojom::CSPDirectiveName directive) {
130 const auto& it = csp_overrides_map_.find(directive);
131 if (it == csp_overrides_map_.end()) {
132 return URLDataSource::GetContentSecurityPolicy(directive);
133 }
134 return it->second;
135 }
136
PathToIdr(const std::string & path)137 base::Optional<int> TelemetryExtensionUntrustedSource::PathToIdr(
138 const std::string& path) {
139 const auto& it = path_to_idr_map_.find(path);
140 if (it == path_to_idr_map_.end()) {
141 return base::nullopt;
142 }
143 return base::Optional<int>(it->second);
144 }
145
146 } // namespace chromeos
147