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 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 50 const GURL& ManifestURL::GetDevToolsPage(const Extension* extension) { 51 return GetManifestURL(extension, keys::kDevToolsPage); 52 } 53 54 // static 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 65 const GURL& ManifestURL::GetUpdateURL(const Extension* extension) { 66 return GetManifestURL(extension, keys::kUpdateURL); 67 } 68 69 // static 70 bool ManifestURL::UpdatesFromGallery(const Extension* extension) { 71 return extension_urls::IsWebstoreUpdateUrl(GetUpdateURL(extension)); 72 } 73 74 // static 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 83 const GURL& ManifestURL::GetOptionsPage(const Extension* extension) { 84 return GetManifestURL(extension, keys::kOptionsPage); 85 } 86 87 // static 88 const GURL& ManifestURL::GetAboutPage(const Extension* extension) { 89 return GetManifestURL(extension, keys::kAboutPage); 90 } 91 92 // static 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 99 URLOverrides::URLOverrides() { 100 } 101 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& 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 118 DevToolsPageHandler::DevToolsPageHandler() { 119 } 120 121 DevToolsPageHandler::~DevToolsPageHandler() { 122 } 123 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 137 const std::vector<std::string> DevToolsPageHandler::Keys() const { 138 return SingleKey(keys::kDevToolsPage); 139 } 140 141 HomepageURLHandler::HomepageURLHandler() { 142 } 143 144 HomepageURLHandler::~HomepageURLHandler() { 145 } 146 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 167 const std::vector<std::string> HomepageURLHandler::Keys() const { 168 return SingleKey(keys::kHomepageURL); 169 } 170 171 UpdateURLHandler::UpdateURLHandler() { 172 } 173 174 UpdateURLHandler::~UpdateURLHandler() { 175 } 176 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 199 const std::vector<std::string> UpdateURLHandler::Keys() const { 200 return SingleKey(keys::kUpdateURL); 201 } 202 203 OptionsPageHandler::OptionsPageHandler() { 204 } 205 206 OptionsPageHandler::~OptionsPageHandler() { 207 } 208 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 244 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 267 const std::vector<std::string> OptionsPageHandler::Keys() const { 268 return SingleKey(keys::kOptionsPage); 269 } 270 271 AboutPageHandler::AboutPageHandler() { 272 } 273 274 AboutPageHandler::~AboutPageHandler() { 275 } 276 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 299 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 318 const std::vector<std::string> AboutPageHandler::Keys() const { 319 return SingleKey(keys::kAboutPage); 320 } 321 322 URLOverridesHandler::URLOverridesHandler() { 323 } 324 325 URLOverridesHandler::~URLOverridesHandler() { 326 } 327 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 385 const std::vector<std::string> URLOverridesHandler::Keys() const { 386 return SingleKey(keys::kChromeURLOverrides); 387 } 388 389 } // namespace extensions 390