Home | History | Annotate | Download | only in extensions
      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