1 // Copyright (c) 2012 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/common/extensions/manifest_url_handler.h"
6
7 #include "base/file_util.h"
8 #include "base/lazy_instance.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/common/chrome_constants.h"
15 #include "chrome/common/extensions/extension_constants.h"
16 #include "chrome/common/url_constants.h"
17 #include "extensions/common/error_utils.h"
18 #include "extensions/common/file_util.h"
19 #include "extensions/common/manifest.h"
20 #include "extensions/common/manifest_constants.h"
21 #include "extensions/common/manifest_handlers/permissions_parser.h"
22 #include "extensions/common/permissions/api_permission.h"
23 #include "extensions/common/permissions/api_permission_set.h"
24 #include "grit/generated_resources.h"
25 #include "ui/base/l10n/l10n_util.h"
26
27 #if defined(USE_AURA)
28 #include "ui/keyboard/keyboard_constants.h"
29 #endif
30
31 namespace extensions {
32
33 namespace keys = manifest_keys;
34 namespace errors = manifest_errors;
35
36 namespace {
37
38 const char kOverrideExtentUrlPatternFormat[] = "chrome://%s/*";
39
GetManifestURL(const Extension * extension,const std::string & key)40 const GURL& GetManifestURL(const Extension* extension,
41 const std::string& key) {
42 ManifestURL* manifest_url =
43 static_cast<ManifestURL*>(extension->GetManifestData(key));
44 return manifest_url ? manifest_url->url_ : GURL::EmptyGURL();
45 }
46
47 } // namespace
48
49 // static
GetDevToolsPage(const Extension * extension)50 const GURL& ManifestURL::GetDevToolsPage(const Extension* extension) {
51 return GetManifestURL(extension, keys::kDevToolsPage);
52 }
53
54 // static
GetHomepageURL(const Extension * extension)55 const GURL ManifestURL::GetHomepageURL(const Extension* extension) {
56 const GURL& homepage_url = GetManifestURL(extension, keys::kHomepageURL);
57 if (homepage_url.is_valid())
58 return homepage_url;
59 return UpdatesFromGallery(extension) ?
60 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) :
61 GURL::EmptyGURL();
62 }
63
64 // static
GetUpdateURL(const Extension * extension)65 const GURL& ManifestURL::GetUpdateURL(const Extension* extension) {
66 return GetManifestURL(extension, keys::kUpdateURL);
67 }
68
69 // static
UpdatesFromGallery(const Extension * extension)70 bool ManifestURL::UpdatesFromGallery(const Extension* extension) {
71 return extension_urls::IsWebstoreUpdateUrl(GetUpdateURL(extension));
72 }
73
74 // static
UpdatesFromGallery(const base::DictionaryValue * manifest)75 bool ManifestURL::UpdatesFromGallery(const base::DictionaryValue* manifest) {
76 std::string url;
77 if (!manifest->GetString(keys::kUpdateURL, &url))
78 return false;
79 return extension_urls::IsWebstoreUpdateUrl(GURL(url));
80 }
81
82 // static
GetOptionsPage(const Extension * extension)83 const GURL& ManifestURL::GetOptionsPage(const Extension* extension) {
84 return GetManifestURL(extension, keys::kOptionsPage);
85 }
86
87 // static
GetAboutPage(const Extension * extension)88 const GURL& ManifestURL::GetAboutPage(const Extension* extension) {
89 return GetManifestURL(extension, keys::kAboutPage);
90 }
91
92 // static
GetDetailsURL(const Extension * extension)93 const GURL ManifestURL::GetDetailsURL(const Extension* extension) {
94 return extension->from_webstore() ?
95 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) :
96 GURL::EmptyGURL();
97 }
98
URLOverrides()99 URLOverrides::URLOverrides() {
100 }
101
~URLOverrides()102 URLOverrides::~URLOverrides() {
103 }
104
105 static base::LazyInstance<URLOverrides::URLOverrideMap> g_empty_url_overrides =
106 LAZY_INSTANCE_INITIALIZER;
107
108 // static
109 const URLOverrides::URLOverrideMap&
GetChromeURLOverrides(const Extension * extension)110 URLOverrides::GetChromeURLOverrides(const Extension* extension) {
111 URLOverrides* url_overrides = static_cast<URLOverrides*>(
112 extension->GetManifestData(keys::kChromeURLOverrides));
113 return url_overrides ?
114 url_overrides->chrome_url_overrides_ :
115 g_empty_url_overrides.Get();
116 }
117
DevToolsPageHandler()118 DevToolsPageHandler::DevToolsPageHandler() {
119 }
120
~DevToolsPageHandler()121 DevToolsPageHandler::~DevToolsPageHandler() {
122 }
123
Parse(Extension * extension,base::string16 * error)124 bool DevToolsPageHandler::Parse(Extension* extension, base::string16* error) {
125 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
126 std::string devtools_str;
127 if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) {
128 *error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage);
129 return false;
130 }
131 manifest_url->url_ = extension->GetResourceURL(devtools_str);
132 extension->SetManifestData(keys::kDevToolsPage, manifest_url.release());
133 PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools);
134 return true;
135 }
136
Keys() const137 const std::vector<std::string> DevToolsPageHandler::Keys() const {
138 return SingleKey(keys::kDevToolsPage);
139 }
140
HomepageURLHandler()141 HomepageURLHandler::HomepageURLHandler() {
142 }
143
~HomepageURLHandler()144 HomepageURLHandler::~HomepageURLHandler() {
145 }
146
Parse(Extension * extension,base::string16 * error)147 bool HomepageURLHandler::Parse(Extension* extension, base::string16* error) {
148 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
149 std::string homepage_url_str;
150 if (!extension->manifest()->GetString(keys::kHomepageURL,
151 &homepage_url_str)) {
152 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidHomepageURL,
153 std::string());
154 return false;
155 }
156 manifest_url->url_ = GURL(homepage_url_str);
157 if (!manifest_url->url_.is_valid() ||
158 !manifest_url->url_.SchemeIsHTTPOrHTTPS()) {
159 *error = ErrorUtils::FormatErrorMessageUTF16(
160 errors::kInvalidHomepageURL, homepage_url_str);
161 return false;
162 }
163 extension->SetManifestData(keys::kHomepageURL, manifest_url.release());
164 return true;
165 }
166
Keys() const167 const std::vector<std::string> HomepageURLHandler::Keys() const {
168 return SingleKey(keys::kHomepageURL);
169 }
170
UpdateURLHandler()171 UpdateURLHandler::UpdateURLHandler() {
172 }
173
~UpdateURLHandler()174 UpdateURLHandler::~UpdateURLHandler() {
175 }
176
Parse(Extension * extension,base::string16 * error)177 bool UpdateURLHandler::Parse(Extension* extension, base::string16* error) {
178 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
179 std::string tmp_update_url;
180
181 if (!extension->manifest()->GetString(keys::kUpdateURL, &tmp_update_url)) {
182 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidUpdateURL,
183 std::string());
184 return false;
185 }
186
187 manifest_url->url_ = GURL(tmp_update_url);
188 if (!manifest_url->url_.is_valid() ||
189 manifest_url->url_.has_ref()) {
190 *error = ErrorUtils::FormatErrorMessageUTF16(
191 errors::kInvalidUpdateURL, tmp_update_url);
192 return false;
193 }
194
195 extension->SetManifestData(keys::kUpdateURL, manifest_url.release());
196 return true;
197 }
198
Keys() const199 const std::vector<std::string> UpdateURLHandler::Keys() const {
200 return SingleKey(keys::kUpdateURL);
201 }
202
OptionsPageHandler()203 OptionsPageHandler::OptionsPageHandler() {
204 }
205
~OptionsPageHandler()206 OptionsPageHandler::~OptionsPageHandler() {
207 }
208
Parse(Extension * extension,base::string16 * error)209 bool OptionsPageHandler::Parse(Extension* extension, base::string16* error) {
210 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
211 std::string options_str;
212 if (!extension->manifest()->GetString(keys::kOptionsPage, &options_str)) {
213 *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage);
214 return false;
215 }
216
217 if (extension->is_hosted_app()) {
218 // hosted apps require an absolute URL.
219 GURL options_url(options_str);
220 if (!options_url.is_valid() ||
221 !options_url.SchemeIsHTTPOrHTTPS()) {
222 *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
223 return false;
224 }
225 manifest_url->url_ = options_url;
226 } else {
227 GURL absolute(options_str);
228 if (absolute.is_valid()) {
229 *error =
230 base::ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
231 return false;
232 }
233 manifest_url->url_ = extension->GetResourceURL(options_str);
234 if (!manifest_url->url_.is_valid()) {
235 *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage);
236 return false;
237 }
238 }
239
240 extension->SetManifestData(keys::kOptionsPage, manifest_url.release());
241 return true;
242 }
243
Validate(const Extension * extension,std::string * error,std::vector<InstallWarning> * warnings) const244 bool OptionsPageHandler::Validate(const Extension* extension,
245 std::string* error,
246 std::vector<InstallWarning>* warnings) const {
247 // Validate path to the options page. Don't check the URL for hosted apps,
248 // because they are expected to refer to an external URL.
249 if (!extensions::ManifestURL::GetOptionsPage(extension).is_empty() &&
250 !extension->is_hosted_app()) {
251 const base::FilePath options_path =
252 extensions::file_util::ExtensionURLToRelativeFilePath(
253 extensions::ManifestURL::GetOptionsPage(extension));
254 const base::FilePath path =
255 extension->GetResource(options_path).GetFilePath();
256 if (path.empty() || !base::PathExists(path)) {
257 *error =
258 l10n_util::GetStringFUTF8(
259 IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED,
260 options_path.LossyDisplayName());
261 return false;
262 }
263 }
264 return true;
265 }
266
Keys() const267 const std::vector<std::string> OptionsPageHandler::Keys() const {
268 return SingleKey(keys::kOptionsPage);
269 }
270
AboutPageHandler()271 AboutPageHandler::AboutPageHandler() {
272 }
273
~AboutPageHandler()274 AboutPageHandler::~AboutPageHandler() {
275 }
276
Parse(Extension * extension,base::string16 * error)277 bool AboutPageHandler::Parse(Extension* extension, base::string16* error) {
278 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
279 std::string about_str;
280 if (!extension->manifest()->GetString(keys::kAboutPage, &about_str)) {
281 *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
282 return false;
283 }
284
285 GURL absolute(about_str);
286 if (absolute.is_valid()) {
287 *error = base::ASCIIToUTF16(errors::kInvalidAboutPageExpectRelativePath);
288 return false;
289 }
290 manifest_url->url_ = extension->GetResourceURL(about_str);
291 if (!manifest_url->url_.is_valid()) {
292 *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
293 return false;
294 }
295 extension->SetManifestData(keys::kAboutPage, manifest_url.release());
296 return true;
297 }
298
Validate(const Extension * extension,std::string * error,std::vector<InstallWarning> * warnings) const299 bool AboutPageHandler::Validate(const Extension* extension,
300 std::string* error,
301 std::vector<InstallWarning>* warnings) const {
302 // Validate path to the options page.
303 if (!extensions::ManifestURL::GetAboutPage(extension).is_empty()) {
304 const base::FilePath about_path =
305 extensions::file_util::ExtensionURLToRelativeFilePath(
306 extensions::ManifestURL::GetAboutPage(extension));
307 const base::FilePath path =
308 extension->GetResource(about_path).GetFilePath();
309 if (path.empty() || !base::PathExists(path)) {
310 *error = l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ABOUT_PAGE_FAILED,
311 about_path.LossyDisplayName());
312 return false;
313 }
314 }
315 return true;
316 }
317
Keys() const318 const std::vector<std::string> AboutPageHandler::Keys() const {
319 return SingleKey(keys::kAboutPage);
320 }
321
URLOverridesHandler()322 URLOverridesHandler::URLOverridesHandler() {
323 }
324
~URLOverridesHandler()325 URLOverridesHandler::~URLOverridesHandler() {
326 }
327
Parse(Extension * extension,base::string16 * error)328 bool URLOverridesHandler::Parse(Extension* extension, base::string16* error) {
329 const base::DictionaryValue* overrides = NULL;
330 if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides,
331 &overrides)) {
332 *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
333 return false;
334 }
335 scoped_ptr<URLOverrides> url_overrides(new URLOverrides);
336 // Validate that the overrides are all strings
337 for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd();
338 iter.Advance()) {
339 std::string page = iter.key();
340 std::string val;
341 // Restrict override pages to a list of supported URLs.
342 bool is_override = (page != chrome::kChromeUINewTabHost &&
343 page != chrome::kChromeUIBookmarksHost &&
344 page != chrome::kChromeUIHistoryHost);
345 #if defined(OS_CHROMEOS)
346 is_override = (is_override &&
347 page != chrome::kChromeUIActivationMessageHost);
348 #endif
349 #if defined(OS_CHROMEOS)
350 is_override = (is_override && page != keyboard::kKeyboardHost);
351 #endif
352
353 if (is_override || !iter.value().GetAsString(&val)) {
354 *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
355 return false;
356 }
357 // Replace the entry with a fully qualified chrome-extension:// URL.
358 url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val);
359
360 // For component extensions, add override URL to extent patterns.
361 if (extension->is_legacy_packaged_app() &&
362 extension->location() == Manifest::COMPONENT) {
363 URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
364 std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat,
365 page.c_str());
366 if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
367 *error = ErrorUtils::FormatErrorMessageUTF16(
368 errors::kInvalidURLPatternError, url);
369 return false;
370 }
371 extension->AddWebExtentPattern(pattern);
372 }
373 }
374
375 // An extension may override at most one page.
376 if (overrides->size() > 1) {
377 *error = base::ASCIIToUTF16(errors::kMultipleOverrides);
378 return false;
379 }
380 extension->SetManifestData(keys::kChromeURLOverrides,
381 url_overrides.release());
382 return true;
383 }
384
Keys() const385 const std::vector<std::string> URLOverridesHandler::Keys() const {
386 return SingleKey(keys::kChromeURLOverrides);
387 }
388
389 } // namespace extensions
390