blob: 598274405cbf0ba1d9ad9b6ab5dafc4c5476a21a [file] [log] [blame]
[email protected]ca90e82b2012-03-07 18:30:311// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]755e1b732010-06-24 23:28:532// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]33c1c26a2013-01-24 21:56:265#include "content/browser/webui/shared_resources_data_source.h"
[email protected]755e1b732010-06-24 23:28:536
avib7348942015-12-25 20:57:107#include <stddef.h>
rbpotter7d574ca2019-01-30 22:16:178#include <string>
avib7348942015-12-25 20:57:109
dpapad2313a4f92018-07-17 17:03:0910#include "base/feature_list.h"
dbeam86781792016-04-30 03:26:2711#include "base/files/file_path.h"
[email protected]be22ebe2013-01-23 17:03:0812#include "base/logging.h"
[email protected]90fa2652012-04-24 16:18:3513#include "base/memory/ref_counted_memory.h"
dbeam41c9c722014-11-26 03:59:0614#include "base/strings/string_piece.h"
[email protected]30e4f872013-08-17 13:54:4715#include "base/strings/string_util.h"
Eric Seckler8652dcd52018-09-20 10:42:2816#include "base/task/post_task.h"
dpapad2313a4f92018-07-17 17:03:0917#include "build/build_config.h"
Kyle Horimotoa69ab462019-04-23 00:08:2118#include "content/grit/content_resources.h"
19#include "content/grit/content_resources_map.h"
Eric Seckler8652dcd52018-09-20 10:42:2820#include "content/public/browser/browser_task_traits.h"
tzikbd171b12017-02-23 04:24:0421#include "content/public/browser/browser_thread.h"
rbpotter7d574ca2019-01-30 22:16:1722#include "content/public/browser/web_contents.h"
[email protected]be22ebe2013-01-23 17:03:0823#include "content/public/common/content_client.h"
dpapad2313a4f92018-07-17 17:03:0924#include "content/public/common/content_features.h"
[email protected]be22ebe2013-01-23 17:03:0825#include "content/public/common/url_constants.h"
Yuzhu Shen2efe42772017-11-11 02:01:0926#include "mojo/public/js/grit/mojo_bindings_resources.h"
Dan Beam78cd41e32017-12-21 04:23:0227#include "mojo/public/js/grit/mojo_bindings_resources_map.h"
dbeam41c9c722014-11-26 03:59:0628#include "ui/base/layout.h"
29#include "ui/base/webui/web_ui_util.h"
30#include "ui/resources/grit/webui_resources.h"
tfarina272c8d72014-09-07 05:48:4631#include "ui/resources/grit/webui_resources_map.h"
[email protected]755e1b732010-06-24 23:28:5332
Kyle Horimoto93a81e472018-09-21 23:30:5033#if defined(OS_CHROMEOS)
34#include "chromeos/grit/chromeos_resources.h"
35#include "chromeos/grit/chromeos_resources_map.h"
36#endif
37
dbeam86781792016-04-30 03:26:2738#if defined(OS_WIN)
39#include "base/strings/utf_string_conversions.h"
40#endif
41
dbeam41c9c722014-11-26 03:59:0642namespace content {
43
[email protected]755e1b732010-06-24 23:28:5344namespace {
45
Dan Beam78cd41e32017-12-21 04:23:0246struct IdrGzipped {
47 int idr;
48 bool gzipped;
49};
Takuto Ikutaadf31eb2019-01-05 00:32:4850using ResourcesMap = std::unordered_map<std::string, IdrGzipped>;
[email protected]30e4f872013-08-17 13:54:4751
rbpotter7d574ca2019-01-30 22:16:1752#if defined(OS_CHROMEOS)
53const char kPolymerHtml[] = "polymer/v1_0/polymer/polymer.html";
54const char kPolymerJs[] = "polymer/v1_0/polymer/polymer-extracted.js";
55const char kPolymer2Html[] = "polymer/v1_0/polymer2/polymer.html";
56const char kPolymer2Js[] = "polymer/v1_0/polymer2/polymer-extracted.js";
rbpotter360a0552019-02-27 05:15:3257const char kHtmlImportsJs[] = "polymer/v1_0/html-imports/html-imports.min.js";
58const char kHtmlImportsV0Js[] =
59 "polymer/v1_0/html-imports-v0/html-imports.min.js";
rbpotter7d574ca2019-01-30 22:16:1760
61// Utility for determining if both Polymer 1 and Polymer 2 are needed.
62bool UsingMultiplePolymerVersions() {
63 return base::FeatureList::IsEnabled(features::kWebUIPolymer2) &&
64 base::FeatureList::IsEnabled(features::kWebUIPolymer2Exceptions);
65}
66#endif // defined(OS_CHROMEOS)
67
Kyle Horimoto93a81e472018-09-21 23:30:5068const std::map<std::string, std::string> CreatePathPrefixAliasesMap() {
dpapad2313a4f92018-07-17 17:03:0969 // TODO(rkc): Once we have a separate source for apps, remove '*/apps/'
70 // aliases.
71 std::map<std::string, std::string> aliases = {
72 {"../../../third_party/polymer/v1_0/components-chromium/",
73 "polymer/v1_0/"},
74 {"../../../third_party/web-animations-js/sources/",
75 "polymer/v1_0/web-animations-js/"},
76 {"../../views/resources/default_100_percent/common/", "images/apps/"},
77 {"../../views/resources/default_200_percent/common/", "images/2x/apps/"},
78 {"../../webui/resources/cr_components/", "cr_components/"},
79 {"../../webui/resources/cr_elements/", "cr_elements/"},
80 };
81
rbpotter7d574ca2019-01-30 22:16:1782#if defined(OS_CHROMEOS)
83 if (UsingMultiplePolymerVersions())
84 return aliases;
85#endif // defined(OS_CHROMEOS)
86
dpapad2313a4f92018-07-17 17:03:0987#if !defined(OS_ANDROID)
88 if (base::FeatureList::IsEnabled(features::kWebUIPolymer2)) {
89 aliases["../../../third_party/polymer/v1_0/components-chromium/polymer2/"] =
90 "polymer/v1_0/polymer/";
rbpotter360a0552019-02-27 05:15:3291 } else {
92 aliases
93 ["../../../third_party/polymer/v1_0/components-chromium/"
94 "html-imports-v0/"] = "polymer/v1_0/html-imports/";
dpapad2313a4f92018-07-17 17:03:0995 }
96#endif // !defined(OS_ANDROID)
97 return aliases;
98}
99
Kyle Horimotoa69ab462019-04-23 00:08:21100const std::map<int, std::string> CreateContentResourceIdToAliasMap() {
101 return std::map<int, std::string>{
102 {IDR_URL_MOJO_JS, "js/url.mojom-lite.js"},
103 };
104}
105
Kyle Horimoto93a81e472018-09-21 23:30:50106const std::map<int, std::string> CreateMojoResourceIdToAliasMap() {
107 return std::map<int, std::string> {
Reilly Grantdfcfe3132019-03-21 12:46:56108 {IDR_MOJO_MOJO_BINDINGS_LITE_JS, "js/mojo_bindings_lite.js"},
Nancy Li19c9bbc2019-03-05 20:56:56109 {IDR_MOJO_BIG_BUFFER_MOJOM_LITE_JS, "js/big_buffer.mojom-lite.js"},
110 {IDR_MOJO_FILE_MOJOM_LITE_JS, "js/file.mojom-lite.js"},
111 {IDR_MOJO_STRING16_MOJOM_LITE_JS, "js/string16.mojom-lite.js"},
Sigurdur Asgeirsson10609e5c2018-10-10 19:58:49112#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
Reilly Grant9a1f2d22019-02-21 02:47:03113 {IDR_MOJO_TIME_MOJOM_LITE_JS, "js/time.mojom-lite.js"},
Sigurdur Asgeirsson10609e5c2018-10-10 19:58:49114#endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
Kyle Horimoto93a81e472018-09-21 23:30:50115 };
116}
117
118#if defined(OS_CHROMEOS)
119const std::map<int, std::string> CreateChromeosMojoResourceIdToAliasMap() {
120 return std::map<int, std::string>{
Reilly Grant9a1f2d22019-02-21 02:47:03121 {IDR_MULTIDEVICE_DEVICE_SYNC_MOJOM_LITE_JS,
122 "js/chromeos/device_sync.mojom-lite.js"},
123 {IDR_MULTIDEVICE_MULTIDEVICE_SETUP_MOJOM_LITE_JS,
124 "js/chromeos/multidevice_setup.mojom-lite.js"},
125 {IDR_MULTIDEVICE_MULTIDEVICE_SETUP_CONSTANTS_MOJOM_LITE_JS,
126 "js/chromeos/multidevice_setup_constants.mojom-lite.js"},
127 {IDR_MULTIDEVICE_MULTIDEVICE_TYPES_MOJOM_LITE_JS,
128 "js/chromeos/multidevice_types.mojom-lite.js"},
Kyle Horimoto93a81e472018-09-21 23:30:50129 };
130}
131#endif // !defined(OS_CHROMEOS)
132
dpapad2313a4f92018-07-17 17:03:09133#if !defined(OS_ANDROID)
134bool ShouldIgnore(std::string resource) {
rbpotter7d574ca2019-01-30 22:16:17135#if defined(OS_CHROMEOS)
136 if (UsingMultiplePolymerVersions())
137 return false;
138#endif // defined(OS_CHROMEOS)
139
dpapad2313a4f92018-07-17 17:03:09140 if (base::FeatureList::IsEnabled(features::kWebUIPolymer2) &&
rbpotter360a0552019-02-27 05:15:32141 (base::StartsWith(
142 resource,
143 "../../../third_party/polymer/v1_0/components-chromium/polymer/",
144 base::CompareCase::SENSITIVE) ||
145 base::StartsWith(resource,
146 "../../../third_party/polymer/v1_0/components-chromium/"
147 "html-imports-v0/",
148 base::CompareCase::SENSITIVE))) {
dpapad2313a4f92018-07-17 17:03:09149 return true;
150 }
151
152 if (!base::FeatureList::IsEnabled(features::kWebUIPolymer2) &&
rbpotter360a0552019-02-27 05:15:32153 (base::StartsWith(
154 resource,
155 "../../../third_party/polymer/v1_0/components-chromium/polymer2/",
156 base::CompareCase::SENSITIVE) ||
157 base::StartsWith(resource,
158 "../../../third_party/polymer/v1_0/components-chromium/"
159 "html-imports/",
160 base::CompareCase::SENSITIVE))) {
dpapad2313a4f92018-07-17 17:03:09161 return true;
162 }
163
164 return false;
165}
166#endif // !defined(OS_ANDROID)
[email protected]30e4f872013-08-17 13:54:47167
dzhioev0b460322014-12-03 13:10:42168void AddResource(const std::string& path,
169 int resource_id,
Dan Beam78cd41e32017-12-21 04:23:02170 bool gzipped,
dzhioev0b460322014-12-03 13:10:42171 ResourcesMap* resources_map) {
Dan Beam78cd41e32017-12-21 04:23:02172 IdrGzipped idr_gzipped = {resource_id, gzipped};
173 if (!resources_map->insert(std::make_pair(path, idr_gzipped)).second)
dzhioev0b460322014-12-03 13:10:42174 NOTREACHED() << "Redefinition of '" << path << "'";
[email protected]30e4f872013-08-17 13:54:47175}
176
Kyle Horimoto93a81e472018-09-21 23:30:50177void AddResourcesToMap(ResourcesMap* resources_map) {
178 const std::map<std::string, std::string> aliases =
179 CreatePathPrefixAliasesMap();
dpapad2313a4f92018-07-17 17:03:09180
[email protected]e28b283e42013-01-17 17:50:15181 for (size_t i = 0; i < kWebuiResourcesSize; ++i) {
Dan Beam78cd41e32017-12-21 04:23:02182 const auto& resource = kWebuiResources[i];
dpapad2313a4f92018-07-17 17:03:09183
184#if !defined(OS_ANDROID)
185 if (ShouldIgnore(resource.name))
186 continue;
187#endif // !defined(OS_ANDROID)
188
Kyle Horimoto93a81e472018-09-21 23:30:50189 AddResource(resource.name, resource.value, resource.gzipped, resources_map);
dpapad2313a4f92018-07-17 17:03:09190
191 for (auto it = aliases.begin(); it != aliases.end(); ++it) {
192 if (base::StartsWith(resource.name, it->first,
brettw95509312015-07-16 23:57:33193 base::CompareCase::SENSITIVE)) {
Dan Beam78cd41e32017-12-21 04:23:02194 std::string resource_name(resource.name);
dpapad2313a4f92018-07-17 17:03:09195 AddResource(it->second + resource_name.substr(it->first.length()),
Kyle Horimoto93a81e472018-09-21 23:30:50196 resource.value, resource.gzipped, resources_map);
dzhioev0b460322014-12-03 13:10:42197 }
[email protected]755e1b732010-06-24 23:28:53198 }
199 }
Kyle Horimoto93a81e472018-09-21 23:30:50200}
201
202// Adds |resources| to |resources_map|, but renames each resource according to
203// the scheme in |resource_aliases|, which maps from resource ID to resource
204// alias. Note that resources which do not have an alias will not be added.
205void AddAliasedResourcesToMap(
206 const std::map<int, std::string>& resource_aliases,
207 const GzippedGritResourceMap resources[],
208 size_t resources_size,
209 ResourcesMap* resources_map) {
210 for (size_t i = 0; i < resources_size; ++i) {
211 const auto& resource = resources[i];
212
213 const auto it = resource_aliases.find(resource.value);
214 if (it == resource_aliases.end())
215 continue;
216
217 AddResource(it->second, resource.value, resource.gzipped, resources_map);
Ken Rockotea19acc2017-06-07 00:34:31218 }
Kyle Horimoto93a81e472018-09-21 23:30:50219}
220
221const ResourcesMap* CreateResourcesMap() {
222 ResourcesMap* result = new ResourcesMap();
223 AddResourcesToMap(result);
Kyle Horimotoa69ab462019-04-23 00:08:21224 AddAliasedResourcesToMap(CreateContentResourceIdToAliasMap(),
225 kContentResources, kContentResourcesSize, result);
Kyle Horimoto93a81e472018-09-21 23:30:50226 AddAliasedResourcesToMap(CreateMojoResourceIdToAliasMap(),
227 kMojoBindingsResources, kMojoBindingsResourcesSize,
228 result);
229#if defined(OS_CHROMEOS)
230 AddAliasedResourcesToMap(CreateChromeosMojoResourceIdToAliasMap(),
231 kChromeosResources, kChromeosResourcesSize, result);
232#endif // !defined(OS_CHROMEOS)
dzhioev0b460322014-12-03 13:10:42233 return result;
234}
235
236const ResourcesMap& GetResourcesMap() {
237 // This pointer will be intentionally leaked on shutdown.
238 static const ResourcesMap* resources_map = CreateResourcesMap();
239 return *resources_map;
[email protected]755e1b732010-06-24 23:28:53240}
241
tzikbd171b12017-02-23 04:24:04242int GetIdrForPath(const std::string& path) {
243 const ResourcesMap& resources_map = GetResourcesMap();
244 auto it = resources_map.find(path);
Dan Beam78cd41e32017-12-21 04:23:02245 return it != resources_map.end() ? it->second.idr : -1;
tzikbd171b12017-02-23 04:24:04246}
247
[email protected]755e1b732010-06-24 23:28:53248} // namespace
249
[email protected]92253622013-01-14 20:40:50250SharedResourcesDataSource::SharedResourcesDataSource() {
[email protected]755e1b732010-06-24 23:28:53251}
252
253SharedResourcesDataSource::~SharedResourcesDataSource() {
254}
255
[email protected]305b8e82013-04-17 16:12:33256std::string SharedResourcesDataSource::GetSource() const {
dbeam41c9c722014-11-26 03:59:06257 return kChromeUIResourcesHost;
[email protected]92253622013-01-14 20:40:50258}
259
[email protected]90d9f1e2013-01-16 02:46:41260void SharedResourcesDataSource::StartDataRequest(
261 const std::string& path,
jamedcd8b012016-09-20 02:03:58262 const ResourceRequestInfo::WebContentsGetter& wc_getter,
dbeam41c9c722014-11-26 03:59:06263 const URLDataSource::GotDataCallback& callback) {
rbpotter7d574ca2019-01-30 22:16:17264 std::string updated_path = path;
265#if defined(OS_CHROMEOS)
266 // If this is a Polymer request and multiple Polymer versions are enabled,
267 // return the Polymer 2 path unless the request is from the
268 // |disabled_polymer2_host_|.
rbpotter360a0552019-02-27 05:15:32269 if ((path == kPolymerHtml || path == kPolymerJs || path == kHtmlImportsJs) &&
270 UsingMultiplePolymerVersions()) {
271 bool polymer2 = !IsPolymer2DisabledForPage(wc_getter);
272 if (polymer2 && (path == kPolymerHtml || path == kPolymerJs)) {
273 updated_path = path == kPolymerHtml ? kPolymer2Html : kPolymer2Js;
274 } else if (!polymer2 && path == kHtmlImportsJs) {
275 updated_path = kHtmlImportsV0Js;
276 }
rbpotter7d574ca2019-01-30 22:16:17277 }
278#endif // defined(OS_CHROMEOS)
279
280 int idr = GetIdrForPath(updated_path);
281 DCHECK_NE(-1, idr) << " path: " << updated_path;
dbeam41c9c722014-11-26 03:59:06282 scoped_refptr<base::RefCountedMemory> bytes;
283
dbeam41c9c722014-11-26 03:59:06284 if (idr == IDR_WEBUI_CSS_TEXT_DEFAULTS) {
bauerbd0399182015-01-26 10:20:45285 std::string css = webui::GetWebUiCssTextDefaults();
286 bytes = base::RefCountedString::TakeString(&css);
edwardjungb7dac1a2016-01-15 14:18:35287 } else if (idr == IDR_WEBUI_CSS_TEXT_DEFAULTS_MD) {
288 std::string css = webui::GetWebUiCssTextDefaultsMd();
289 bytes = base::RefCountedString::TakeString(&css);
dbeam41c9c722014-11-26 03:59:06290 } else {
dbeam9e1b0f02014-12-03 01:34:47291 bytes = GetContentClient()->GetDataResourceBytes(idr);
dbeam41c9c722014-11-26 03:59:06292 }
[email protected]47b541d92012-03-27 19:47:49293
[email protected]fc72bb12013-06-02 21:13:46294 callback.Run(bytes.get());
[email protected]755e1b732010-06-24 23:28:53295}
296
toyoshimbb7ab182017-03-30 05:28:08297bool SharedResourcesDataSource::AllowCaching() const {
298 // Should not be cached to reflect dynamically-generated contents that may
299 // depend on the current locale.
300 return false;
301}
302
[email protected]755e1b732010-06-24 23:28:53303std::string SharedResourcesDataSource::GetMimeType(
304 const std::string& path) const {
dbeam86781792016-04-30 03:26:27305 if (path.empty())
306 return "text/html";
[email protected]db7281962013-01-17 19:58:00307
dbeam86781792016-04-30 03:26:27308#if defined(OS_WIN)
309 base::FilePath file(base::UTF8ToWide(path));
dbeame3081ca2016-06-01 02:28:05310 std::string extension = base::WideToUTF8(file.FinalExtension());
dbeam86781792016-04-30 03:26:27311#else
312 base::FilePath file(path);
dbeame3081ca2016-06-01 02:28:05313 std::string extension = file.FinalExtension();
dbeam86781792016-04-30 03:26:27314#endif
315
dbeame3081ca2016-06-01 02:28:05316 if (!extension.empty())
317 extension.erase(0, 1);
318
dbeam86781792016-04-30 03:26:27319 if (extension == "html")
320 return "text/html";
321
322 if (extension == "css")
323 return "text/css";
324
325 if (extension == "js")
326 return "application/javascript";
327
328 if (extension == "png")
329 return "image/png";
330
331 if (extension == "gif")
332 return "image/gif";
333
334 if (extension == "svg")
335 return "image/svg+xml";
336
337 if (extension == "woff2")
338 return "application/font-woff2";
339
dbeame3081ca2016-06-01 02:28:05340 NOTREACHED() << path;
dbeam86781792016-04-30 03:26:27341 return "text/plain";
342}
343
dpapadf9becc92019-01-15 03:39:29344bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
345 return true;
346}
347
gab143ea622016-11-03 12:47:00348scoped_refptr<base::SingleThreadTaskRunner>
349SharedResourcesDataSource::TaskRunnerForRequestPath(
dbeam86781792016-04-30 03:26:27350 const std::string& path) const {
rbpotter7d574ca2019-01-30 22:16:17351 // Since WebContentsGetter can only be run on the UI thread, always return
352 // a task runner if we need to choose between Polymer resources based on the
353 // WebContents that is requesting the resource.
354 // TODO (rbpotter): Remove this once the OOBE Polymer 2 migration is complete.
355#if defined(OS_CHROMEOS)
356 if (UsingMultiplePolymerVersions())
357 return base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI});
358#endif // defined(OS_CHROMEOS)
359
tzikbd171b12017-02-23 04:24:04360 int idr = GetIdrForPath(path);
361 if (idr == IDR_WEBUI_CSS_TEXT_DEFAULTS ||
362 idr == IDR_WEBUI_CSS_TEXT_DEFAULTS_MD) {
363 // Use UI thread to load CSS since its construction touches non-thread-safe
364 // gfx::Font names in ui::ResourceBundle.
Eric Seckler8652dcd52018-09-20 10:42:28365 return base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI});
tzikbd171b12017-02-23 04:24:04366 }
367
dbeam86781792016-04-30 03:26:27368 return nullptr;
[email protected]755e1b732010-06-24 23:28:53369}
dzhioevfd3b2572014-10-10 18:55:45370
371std::string
372SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin(
373 const std::string& origin) const {
374 // For now we give access only for "chrome://*" origins.
375 // According to CORS spec, Access-Control-Allow-Origin header doesn't support
376 // wildcards, so we need to set its value explicitly by passing the |origin|
377 // back.
dbeam41c9c722014-11-26 03:59:06378 std::string allowed_origin_prefix = kChromeUIScheme;
dzhioevfd3b2572014-10-10 18:55:45379 allowed_origin_prefix += "://";
markusheintz74e10b22016-07-08 13:19:36380 if (!base::StartsWith(origin, allowed_origin_prefix,
381 base::CompareCase::SENSITIVE)) {
sammcf01aed1962014-12-18 06:35:28382 return "null";
markusheintz74e10b22016-07-08 13:19:36383 }
dzhioevfd3b2572014-10-10 18:55:45384 return origin;
385}
dbeam41c9c722014-11-26 03:59:06386
yzshen1a4797782017-06-09 08:11:33387bool SharedResourcesDataSource::IsGzipped(const std::string& path) const {
Dan Beam78cd41e32017-12-21 04:23:02388 auto it = GetResourcesMap().find(path);
389 DCHECK(it != GetResourcesMap().end()) << "missing shared resource: " << path;
390 return it != GetResourcesMap().end() ? it->second.gzipped : false;
yzshen1a4797782017-06-09 08:11:33391}
392
rbpotter7d574ca2019-01-30 22:16:17393#if defined(OS_CHROMEOS)
394void SharedResourcesDataSource::DisablePolymer2ForHost(
395 const std::string& host) {
396 DCHECK(disabled_polymer2_host_.empty() || host == disabled_polymer2_host_);
397 disabled_polymer2_host_ = host;
398}
399
400// Returns true if the WebContents making the request has disabled Polymer 2.
401bool SharedResourcesDataSource::IsPolymer2DisabledForPage(
402 const ResourceRequestInfo::WebContentsGetter& wc_getter) {
403 // Return false in these cases, which sometimes occur in tests.
404 if (!wc_getter)
405 return false;
406
407 content::WebContents* web_contents = wc_getter.Run();
408 if (!web_contents)
409 return false;
410
411 return web_contents->GetLastCommittedURL().host_piece() ==
412 disabled_polymer2_host_;
413}
414#endif // defined(OS_CHROMEOS)
dbeam41c9c722014-11-26 03:59:06415} // namespace content