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/permissions/api_permission.h"
22 #include "extensions/common/permissions/api_permission_set.h"
23 #include "extensions/common/permissions/permissions_data.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
GetDetailsURL(const Extension * extension)88 const GURL ManifestURL::GetDetailsURL(const Extension* extension) {
89 return extension->from_webstore() ?
90 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) :
91 GURL::EmptyGURL();
92 }
93
URLOverrides()94 URLOverrides::URLOverrides() {
95 }
96
~URLOverrides()97 URLOverrides::~URLOverrides() {
98 }
99
100 static base::LazyInstance<URLOverrides::URLOverrideMap> g_empty_url_overrides =
101 LAZY_INSTANCE_INITIALIZER;
102
103 // static
104 const URLOverrides::URLOverrideMap&
GetChromeURLOverrides(const Extension * extension)105 URLOverrides::GetChromeURLOverrides(const Extension* extension) {
106 URLOverrides* url_overrides = static_cast<URLOverrides*>(
107 extension->GetManifestData(keys::kChromeURLOverrides));
108 return url_overrides ?
109 url_overrides->chrome_url_overrides_ :
110 g_empty_url_overrides.Get();
111 }
112
DevToolsPageHandler()113 DevToolsPageHandler::DevToolsPageHandler() {
114 }
115
~DevToolsPageHandler()116 DevToolsPageHandler::~DevToolsPageHandler() {
117 }
118
Parse(Extension * extension,base::string16 * error)119 bool DevToolsPageHandler::Parse(Extension* extension, base::string16* error) {
120 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
121 std::string devtools_str;
122 if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) {
123 *error = ASCIIToUTF16(errors::kInvalidDevToolsPage);
124 return false;
125 }
126 manifest_url->url_ = extension->GetResourceURL(devtools_str);
127 extension->SetManifestData(keys::kDevToolsPage, manifest_url.release());
128 PermissionsData::GetInitialAPIPermissions(extension)->insert(
129 APIPermission::kDevtools);
130 return true;
131 }
132
Keys() const133 const std::vector<std::string> DevToolsPageHandler::Keys() const {
134 return SingleKey(keys::kDevToolsPage);
135 }
136
HomepageURLHandler()137 HomepageURLHandler::HomepageURLHandler() {
138 }
139
~HomepageURLHandler()140 HomepageURLHandler::~HomepageURLHandler() {
141 }
142
Parse(Extension * extension,base::string16 * error)143 bool HomepageURLHandler::Parse(Extension* extension, base::string16* error) {
144 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
145 std::string homepage_url_str;
146 if (!extension->manifest()->GetString(keys::kHomepageURL,
147 &homepage_url_str)) {
148 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidHomepageURL,
149 std::string());
150 return false;
151 }
152 manifest_url->url_ = GURL(homepage_url_str);
153 if (!manifest_url->url_.is_valid() ||
154 !manifest_url->url_.SchemeIsHTTPOrHTTPS()) {
155 *error = ErrorUtils::FormatErrorMessageUTF16(
156 errors::kInvalidHomepageURL, homepage_url_str);
157 return false;
158 }
159 extension->SetManifestData(keys::kHomepageURL, manifest_url.release());
160 return true;
161 }
162
Keys() const163 const std::vector<std::string> HomepageURLHandler::Keys() const {
164 return SingleKey(keys::kHomepageURL);
165 }
166
UpdateURLHandler()167 UpdateURLHandler::UpdateURLHandler() {
168 }
169
~UpdateURLHandler()170 UpdateURLHandler::~UpdateURLHandler() {
171 }
172
Parse(Extension * extension,base::string16 * error)173 bool UpdateURLHandler::Parse(Extension* extension, base::string16* error) {
174 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
175 std::string tmp_update_url;
176
177 if (!extension->manifest()->GetString(keys::kUpdateURL, &tmp_update_url)) {
178 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidUpdateURL,
179 std::string());
180 return false;
181 }
182
183 manifest_url->url_ = GURL(tmp_update_url);
184 if (!manifest_url->url_.is_valid() ||
185 manifest_url->url_.has_ref()) {
186 *error = ErrorUtils::FormatErrorMessageUTF16(
187 errors::kInvalidUpdateURL, tmp_update_url);
188 return false;
189 }
190
191 extension->SetManifestData(keys::kUpdateURL, manifest_url.release());
192 return true;
193 }
194
Keys() const195 const std::vector<std::string> UpdateURLHandler::Keys() const {
196 return SingleKey(keys::kUpdateURL);
197 }
198
OptionsPageHandler()199 OptionsPageHandler::OptionsPageHandler() {
200 }
201
~OptionsPageHandler()202 OptionsPageHandler::~OptionsPageHandler() {
203 }
204
Parse(Extension * extension,base::string16 * error)205 bool OptionsPageHandler::Parse(Extension* extension, base::string16* error) {
206 scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
207 std::string options_str;
208 if (!extension->manifest()->GetString(keys::kOptionsPage, &options_str)) {
209 *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
210 return false;
211 }
212
213 if (extension->is_hosted_app()) {
214 // hosted apps require an absolute URL.
215 GURL options_url(options_str);
216 if (!options_url.is_valid() ||
217 !options_url.SchemeIsHTTPOrHTTPS()) {
218 *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
219 return false;
220 }
221 manifest_url->url_ = options_url;
222 } else {
223 GURL absolute(options_str);
224 if (absolute.is_valid()) {
225 *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
226 return false;
227 }
228 manifest_url->url_ = extension->GetResourceURL(options_str);
229 if (!manifest_url->url_.is_valid()) {
230 *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
231 return false;
232 }
233 }
234
235 extension->SetManifestData(keys::kOptionsPage, manifest_url.release());
236 return true;
237 }
238
Validate(const Extension * extension,std::string * error,std::vector<InstallWarning> * warnings) const239 bool OptionsPageHandler::Validate(const Extension* extension,
240 std::string* error,
241 std::vector<InstallWarning>* warnings) const {
242 // Validate path to the options page. Don't check the URL for hosted apps,
243 // because they are expected to refer to an external URL.
244 if (!extensions::ManifestURL::GetOptionsPage(extension).is_empty() &&
245 !extension->is_hosted_app()) {
246 const base::FilePath options_path =
247 extensions::file_util::ExtensionURLToRelativeFilePath(
248 extensions::ManifestURL::GetOptionsPage(extension));
249 const base::FilePath path =
250 extension->GetResource(options_path).GetFilePath();
251 if (path.empty() || !base::PathExists(path)) {
252 *error =
253 l10n_util::GetStringFUTF8(
254 IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED,
255 options_path.LossyDisplayName());
256 return false;
257 }
258 }
259 return true;
260 }
261
Keys() const262 const std::vector<std::string> OptionsPageHandler::Keys() const {
263 return SingleKey(keys::kOptionsPage);
264 }
265
URLOverridesHandler()266 URLOverridesHandler::URLOverridesHandler() {
267 }
268
~URLOverridesHandler()269 URLOverridesHandler::~URLOverridesHandler() {
270 }
271
Parse(Extension * extension,base::string16 * error)272 bool URLOverridesHandler::Parse(Extension* extension, base::string16* error) {
273 const base::DictionaryValue* overrides = NULL;
274 if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides,
275 &overrides)) {
276 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
277 return false;
278 }
279 scoped_ptr<URLOverrides> url_overrides(new URLOverrides);
280 // Validate that the overrides are all strings
281 for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd();
282 iter.Advance()) {
283 std::string page = iter.key();
284 std::string val;
285 // Restrict override pages to a list of supported URLs.
286 bool is_override = (page != chrome::kChromeUINewTabHost &&
287 page != chrome::kChromeUIBookmarksHost &&
288 page != chrome::kChromeUIHistoryHost);
289 #if defined(OS_CHROMEOS)
290 is_override = (is_override &&
291 page != chrome::kChromeUIActivationMessageHost);
292 #endif
293 #if defined(OS_CHROMEOS)
294 is_override = (is_override && page != keyboard::kKeyboardWebUIHost);
295 #endif
296 #if defined(ENABLE_ENHANCED_BOOKMARKS)
297 is_override = (is_override &&
298 !(extension->location() == Manifest::COMPONENT &&
299 page == chrome::kChromeUIEnhancedBookmarksHost));
300 #endif
301
302 if (is_override || !iter.value().GetAsString(&val)) {
303 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
304 return false;
305 }
306 // Replace the entry with a fully qualified chrome-extension:// URL.
307 url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val);
308
309 // For component extensions, add override URL to extent patterns.
310 if (extension->is_legacy_packaged_app() &&
311 extension->location() == Manifest::COMPONENT) {
312 URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
313 std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat,
314 page.c_str());
315 if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
316 *error = ErrorUtils::FormatErrorMessageUTF16(
317 errors::kInvalidURLPatternError, url);
318 return false;
319 }
320 extension->AddWebExtentPattern(pattern);
321 }
322 }
323
324 // An extension may override at most one page.
325 if (overrides->size() > 1) {
326 *error = ASCIIToUTF16(errors::kMultipleOverrides);
327 return false;
328 }
329 extension->SetManifestData(keys::kChromeURLOverrides,
330 url_overrides.release());
331 return true;
332 }
333
Keys() const334 const std::vector<std::string> URLOverridesHandler::Keys() const {
335 return SingleKey(keys::kChromeURLOverrides);
336 }
337
338 } // namespace extensions
339