| // 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/extensions/extension_file_util.h" |
| #include "chrome/common/extensions/permissions/permissions_data.h" |
| #include "chrome/common/url_constants.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_constants.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 |
| const GURL& ManifestURL::GetOptionsPage(const Extension* extension) { |
| return GetManifestURL(extension, keys::kOptionsPage); |
| } |
| |
| // 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, string16* error) { |
| scoped_ptr<ManifestURL> manifest_url(new ManifestURL); |
| std::string devtools_str; |
| if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) { |
| *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); |
| return false; |
| } |
| manifest_url->url_ = extension->GetResourceURL(devtools_str); |
| extension->SetManifestData(keys::kDevToolsPage, manifest_url.release()); |
| PermissionsData::GetInitialAPIPermissions(extension)->insert( |
| 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, 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, 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, string16* error) { |
| scoped_ptr<ManifestURL> manifest_url(new ManifestURL); |
| std::string options_str; |
| if (!extension->manifest()->GetString(keys::kOptionsPage, &options_str)) { |
| *error = 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 = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); |
| return false; |
| } |
| manifest_url->url_ = options_url; |
| } else { |
| GURL absolute(options_str); |
| if (absolute.is_valid()) { |
| *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); |
| return false; |
| } |
| manifest_url->url_ = extension->GetResourceURL(options_str); |
| if (!manifest_url->url_.is_valid()) { |
| *error = 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 = |
| extension_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); |
| } |
| |
| URLOverridesHandler::URLOverridesHandler() { |
| } |
| |
| URLOverridesHandler::~URLOverridesHandler() { |
| } |
| |
| bool URLOverridesHandler::Parse(Extension* extension, string16* error) { |
| const base::DictionaryValue* overrides = NULL; |
| if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides, |
| &overrides)) { |
| *error = 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::kKeyboardWebUIHost); |
| #endif |
| #if defined(ENABLE_ENHANCED_BOOKMARKS) |
| is_override = (is_override && |
| !(extension->location() == Manifest::COMPONENT && |
| page == chrome::kChromeUIEnhancedBookmarksHost)); |
| #endif |
| |
| if (is_override || !iter.value().GetAsString(&val)) { |
| *error = 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 = 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 |