// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/common/extensions/manifest_url_handler.h" #include "base/file_util.h" #include "base/lazy_instance.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/url_constants.h" #include "extensions/common/error_utils.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_handlers/permissions_parser.h" #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/api_permission_set.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #if defined(USE_AURA) #include "ui/keyboard/keyboard_constants.h" #endif namespace extensions { namespace keys = manifest_keys; namespace errors = manifest_errors; namespace { const char kOverrideExtentUrlPatternFormat[] = "chrome://%s/*"; const GURL& GetManifestURL(const Extension* extension, const std::string& key) { ManifestURL* manifest_url = static_cast<ManifestURL*>(extension->GetManifestData(key)); return manifest_url ? manifest_url->url_ : GURL::EmptyGURL(); } } // namespace // static const GURL& ManifestURL::GetDevToolsPage(const Extension* extension) { return GetManifestURL(extension, keys::kDevToolsPage); } // static const GURL ManifestURL::GetHomepageURL(const Extension* extension) { const GURL& homepage_url = GetManifestURL(extension, keys::kHomepageURL); if (homepage_url.is_valid()) return homepage_url; return UpdatesFromGallery(extension) ? GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) : GURL::EmptyGURL(); } // static const GURL& ManifestURL::GetUpdateURL(const Extension* extension) { return GetManifestURL(extension, keys::kUpdateURL); } // static bool ManifestURL::UpdatesFromGallery(const Extension* extension) { return extension_urls::IsWebstoreUpdateUrl(GetUpdateURL(extension)); } // static bool ManifestURL::UpdatesFromGallery(const base::DictionaryValue* manifest) { std::string url; if (!manifest->GetString(keys::kUpdateURL, &url)) return false; return extension_urls::IsWebstoreUpdateUrl(GURL(url)); } // static const GURL& ManifestURL::GetOptionsPage(const Extension* extension) { return GetManifestURL(extension, keys::kOptionsPage); } // static const GURL& ManifestURL::GetAboutPage(const Extension* extension) { return GetManifestURL(extension, keys::kAboutPage); } // static const GURL ManifestURL::GetDetailsURL(const Extension* extension) { return extension->from_webstore() ? GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) : GURL::EmptyGURL(); } URLOverrides::URLOverrides() { } URLOverrides::~URLOverrides() { } static base::LazyInstance<URLOverrides::URLOverrideMap> g_empty_url_overrides = LAZY_INSTANCE_INITIALIZER; // static const URLOverrides::URLOverrideMap& URLOverrides::GetChromeURLOverrides(const Extension* extension) { URLOverrides* url_overrides = static_cast<URLOverrides*>( extension->GetManifestData(keys::kChromeURLOverrides)); return url_overrides ? url_overrides->chrome_url_overrides_ : g_empty_url_overrides.Get(); } DevToolsPageHandler::DevToolsPageHandler() { } DevToolsPageHandler::~DevToolsPageHandler() { } bool DevToolsPageHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<ManifestURL> manifest_url(new ManifestURL); std::string devtools_str; if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) { *error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage); return false; } manifest_url->url_ = extension->GetResourceURL(devtools_str); extension->SetManifestData(keys::kDevToolsPage, manifest_url.release()); PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools); return true; } const std::vector<std::string> DevToolsPageHandler::Keys() const { return SingleKey(keys::kDevToolsPage); } HomepageURLHandler::HomepageURLHandler() { } HomepageURLHandler::~HomepageURLHandler() { } bool HomepageURLHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<ManifestURL> manifest_url(new ManifestURL); std::string homepage_url_str; if (!extension->manifest()->GetString(keys::kHomepageURL, &homepage_url_str)) { *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidHomepageURL, std::string()); return false; } manifest_url->url_ = GURL(homepage_url_str); if (!manifest_url->url_.is_valid() || !manifest_url->url_.SchemeIsHTTPOrHTTPS()) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidHomepageURL, homepage_url_str); return false; } extension->SetManifestData(keys::kHomepageURL, manifest_url.release()); return true; } const std::vector<std::string> HomepageURLHandler::Keys() const { return SingleKey(keys::kHomepageURL); } UpdateURLHandler::UpdateURLHandler() { } UpdateURLHandler::~UpdateURLHandler() { } bool UpdateURLHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<ManifestURL> manifest_url(new ManifestURL); std::string tmp_update_url; if (!extension->manifest()->GetString(keys::kUpdateURL, &tmp_update_url)) { *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidUpdateURL, std::string()); return false; } manifest_url->url_ = GURL(tmp_update_url); if (!manifest_url->url_.is_valid() || manifest_url->url_.has_ref()) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidUpdateURL, tmp_update_url); return false; } extension->SetManifestData(keys::kUpdateURL, manifest_url.release()); return true; } const std::vector<std::string> UpdateURLHandler::Keys() const { return SingleKey(keys::kUpdateURL); } OptionsPageHandler::OptionsPageHandler() { } OptionsPageHandler::~OptionsPageHandler() { } bool OptionsPageHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<ManifestURL> manifest_url(new ManifestURL); std::string options_str; if (!extension->manifest()->GetString(keys::kOptionsPage, &options_str)) { *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage); return false; } if (extension->is_hosted_app()) { // hosted apps require an absolute URL. GURL options_url(options_str); if (!options_url.is_valid() || !options_url.SchemeIsHTTPOrHTTPS()) { *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); return false; } manifest_url->url_ = options_url; } else { GURL absolute(options_str); if (absolute.is_valid()) { *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); return false; } manifest_url->url_ = extension->GetResourceURL(options_str); if (!manifest_url->url_.is_valid()) { *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage); return false; } } extension->SetManifestData(keys::kOptionsPage, manifest_url.release()); return true; } bool OptionsPageHandler::Validate(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings) const { // Validate path to the options page. Don't check the URL for hosted apps, // because they are expected to refer to an external URL. if (!extensions::ManifestURL::GetOptionsPage(extension).is_empty() && !extension->is_hosted_app()) { const base::FilePath options_path = extensions::file_util::ExtensionURLToRelativeFilePath( extensions::ManifestURL::GetOptionsPage(extension)); const base::FilePath path = extension->GetResource(options_path).GetFilePath(); if (path.empty() || !base::PathExists(path)) { *error = l10n_util::GetStringFUTF8( IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED, options_path.LossyDisplayName()); return false; } } return true; } const std::vector<std::string> OptionsPageHandler::Keys() const { return SingleKey(keys::kOptionsPage); } AboutPageHandler::AboutPageHandler() { } AboutPageHandler::~AboutPageHandler() { } bool AboutPageHandler::Parse(Extension* extension, base::string16* error) { scoped_ptr<ManifestURL> manifest_url(new ManifestURL); std::string about_str; if (!extension->manifest()->GetString(keys::kAboutPage, &about_str)) { *error = base::ASCIIToUTF16(errors::kInvalidAboutPage); return false; } GURL absolute(about_str); if (absolute.is_valid()) { *error = base::ASCIIToUTF16(errors::kInvalidAboutPageExpectRelativePath); return false; } manifest_url->url_ = extension->GetResourceURL(about_str); if (!manifest_url->url_.is_valid()) { *error = base::ASCIIToUTF16(errors::kInvalidAboutPage); return false; } extension->SetManifestData(keys::kAboutPage, manifest_url.release()); return true; } bool AboutPageHandler::Validate(const Extension* extension, std::string* error, std::vector<InstallWarning>* warnings) const { // Validate path to the options page. if (!extensions::ManifestURL::GetAboutPage(extension).is_empty()) { const base::FilePath about_path = extensions::file_util::ExtensionURLToRelativeFilePath( extensions::ManifestURL::GetAboutPage(extension)); const base::FilePath path = extension->GetResource(about_path).GetFilePath(); if (path.empty() || !base::PathExists(path)) { *error = l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ABOUT_PAGE_FAILED, about_path.LossyDisplayName()); return false; } } return true; } const std::vector<std::string> AboutPageHandler::Keys() const { return SingleKey(keys::kAboutPage); } URLOverridesHandler::URLOverridesHandler() { } URLOverridesHandler::~URLOverridesHandler() { } bool URLOverridesHandler::Parse(Extension* extension, base::string16* error) { const base::DictionaryValue* overrides = NULL; if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides, &overrides)) { *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides); return false; } scoped_ptr<URLOverrides> url_overrides(new URLOverrides); // Validate that the overrides are all strings for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd(); iter.Advance()) { std::string page = iter.key(); std::string val; // Restrict override pages to a list of supported URLs. bool is_override = (page != chrome::kChromeUINewTabHost && page != chrome::kChromeUIBookmarksHost && page != chrome::kChromeUIHistoryHost); #if defined(OS_CHROMEOS) is_override = (is_override && page != chrome::kChromeUIActivationMessageHost); #endif #if defined(OS_CHROMEOS) is_override = (is_override && page != keyboard::kKeyboardHost); #endif if (is_override || !iter.value().GetAsString(&val)) { *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides); return false; } // Replace the entry with a fully qualified chrome-extension:// URL. url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val); // For component extensions, add override URL to extent patterns. if (extension->is_legacy_packaged_app() && extension->location() == Manifest::COMPONENT) { URLPattern pattern(URLPattern::SCHEME_CHROMEUI); std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat, page.c_str()); if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidURLPatternError, url); return false; } extension->AddWebExtentPattern(pattern); } } // An extension may override at most one page. if (overrides->size() > 1) { *error = base::ASCIIToUTF16(errors::kMultipleOverrides); return false; } extension->SetManifestData(keys::kChromeURLOverrides, url_overrides.release()); return true; } const std::vector<std::string> URLOverridesHandler::Keys() const { return SingleKey(keys::kChromeURLOverrides); } } // namespace extensions