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 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::GetDetailsURL(const Extension* extension) { 89 return extension->from_webstore() ? 90 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) : 91 GURL::EmptyGURL(); 92 } 93 94 URLOverrides::URLOverrides() { 95 } 96 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& 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 113 DevToolsPageHandler::DevToolsPageHandler() { 114 } 115 116 DevToolsPageHandler::~DevToolsPageHandler() { 117 } 118 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 133 const std::vector<std::string> DevToolsPageHandler::Keys() const { 134 return SingleKey(keys::kDevToolsPage); 135 } 136 137 HomepageURLHandler::HomepageURLHandler() { 138 } 139 140 HomepageURLHandler::~HomepageURLHandler() { 141 } 142 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 163 const std::vector<std::string> HomepageURLHandler::Keys() const { 164 return SingleKey(keys::kHomepageURL); 165 } 166 167 UpdateURLHandler::UpdateURLHandler() { 168 } 169 170 UpdateURLHandler::~UpdateURLHandler() { 171 } 172 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 195 const std::vector<std::string> UpdateURLHandler::Keys() const { 196 return SingleKey(keys::kUpdateURL); 197 } 198 199 OptionsPageHandler::OptionsPageHandler() { 200 } 201 202 OptionsPageHandler::~OptionsPageHandler() { 203 } 204 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 239 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 262 const std::vector<std::string> OptionsPageHandler::Keys() const { 263 return SingleKey(keys::kOptionsPage); 264 } 265 266 URLOverridesHandler::URLOverridesHandler() { 267 } 268 269 URLOverridesHandler::~URLOverridesHandler() { 270 } 271 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 334 const std::vector<std::string> URLOverridesHandler::Keys() const { 335 return SingleKey(keys::kChromeURLOverrides); 336 } 337 338 } // namespace extensions 339