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