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_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/manifest_handlers/permissions_parser.h"
     22 #include "extensions/common/permissions/api_permission.h"
     23 #include "extensions/common/permissions/api_permission_set.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::GetAboutPage(const Extension* extension) {
     89   return GetManifestURL(extension, keys::kAboutPage);
     90 }
     91 
     92 // static
     93 const GURL ManifestURL::GetDetailsURL(const Extension* extension) {
     94   return extension->from_webstore() ?
     95       GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + extension->id()) :
     96       GURL::EmptyGURL();
     97 }
     98 
     99 URLOverrides::URLOverrides() {
    100 }
    101 
    102 URLOverrides::~URLOverrides() {
    103 }
    104 
    105 static base::LazyInstance<URLOverrides::URLOverrideMap> g_empty_url_overrides =
    106     LAZY_INSTANCE_INITIALIZER;
    107 
    108 // static
    109 const URLOverrides::URLOverrideMap&
    110     URLOverrides::GetChromeURLOverrides(const Extension* extension) {
    111   URLOverrides* url_overrides = static_cast<URLOverrides*>(
    112       extension->GetManifestData(keys::kChromeURLOverrides));
    113   return url_overrides ?
    114          url_overrides->chrome_url_overrides_ :
    115          g_empty_url_overrides.Get();
    116 }
    117 
    118 DevToolsPageHandler::DevToolsPageHandler() {
    119 }
    120 
    121 DevToolsPageHandler::~DevToolsPageHandler() {
    122 }
    123 
    124 bool DevToolsPageHandler::Parse(Extension* extension, base::string16* error) {
    125   scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
    126   std::string devtools_str;
    127   if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) {
    128     *error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage);
    129     return false;
    130   }
    131   manifest_url->url_ = extension->GetResourceURL(devtools_str);
    132   extension->SetManifestData(keys::kDevToolsPage, manifest_url.release());
    133   PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools);
    134   return true;
    135 }
    136 
    137 const std::vector<std::string> DevToolsPageHandler::Keys() const {
    138   return SingleKey(keys::kDevToolsPage);
    139 }
    140 
    141 HomepageURLHandler::HomepageURLHandler() {
    142 }
    143 
    144 HomepageURLHandler::~HomepageURLHandler() {
    145 }
    146 
    147 bool HomepageURLHandler::Parse(Extension* extension, base::string16* error) {
    148   scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
    149   std::string homepage_url_str;
    150   if (!extension->manifest()->GetString(keys::kHomepageURL,
    151                                         &homepage_url_str)) {
    152     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidHomepageURL,
    153                                                  std::string());
    154     return false;
    155   }
    156   manifest_url->url_ = GURL(homepage_url_str);
    157   if (!manifest_url->url_.is_valid() ||
    158       !manifest_url->url_.SchemeIsHTTPOrHTTPS()) {
    159     *error = ErrorUtils::FormatErrorMessageUTF16(
    160         errors::kInvalidHomepageURL, homepage_url_str);
    161     return false;
    162   }
    163   extension->SetManifestData(keys::kHomepageURL, manifest_url.release());
    164   return true;
    165 }
    166 
    167 const std::vector<std::string> HomepageURLHandler::Keys() const {
    168   return SingleKey(keys::kHomepageURL);
    169 }
    170 
    171 UpdateURLHandler::UpdateURLHandler() {
    172 }
    173 
    174 UpdateURLHandler::~UpdateURLHandler() {
    175 }
    176 
    177 bool UpdateURLHandler::Parse(Extension* extension, base::string16* error) {
    178   scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
    179   std::string tmp_update_url;
    180 
    181   if (!extension->manifest()->GetString(keys::kUpdateURL, &tmp_update_url)) {
    182     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidUpdateURL,
    183                                                  std::string());
    184     return false;
    185   }
    186 
    187   manifest_url->url_ = GURL(tmp_update_url);
    188   if (!manifest_url->url_.is_valid() ||
    189       manifest_url->url_.has_ref()) {
    190     *error = ErrorUtils::FormatErrorMessageUTF16(
    191         errors::kInvalidUpdateURL, tmp_update_url);
    192     return false;
    193   }
    194 
    195   extension->SetManifestData(keys::kUpdateURL, manifest_url.release());
    196   return true;
    197 }
    198 
    199 const std::vector<std::string> UpdateURLHandler::Keys() const {
    200   return SingleKey(keys::kUpdateURL);
    201 }
    202 
    203 OptionsPageHandler::OptionsPageHandler() {
    204 }
    205 
    206 OptionsPageHandler::~OptionsPageHandler() {
    207 }
    208 
    209 bool OptionsPageHandler::Parse(Extension* extension, base::string16* error) {
    210   scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
    211   std::string options_str;
    212   if (!extension->manifest()->GetString(keys::kOptionsPage, &options_str)) {
    213     *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage);
    214     return false;
    215   }
    216 
    217   if (extension->is_hosted_app()) {
    218     // hosted apps require an absolute URL.
    219     GURL options_url(options_str);
    220     if (!options_url.is_valid() ||
    221         !options_url.SchemeIsHTTPOrHTTPS()) {
    222       *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
    223       return false;
    224     }
    225     manifest_url->url_ = options_url;
    226   } else {
    227     GURL absolute(options_str);
    228     if (absolute.is_valid()) {
    229       *error =
    230           base::ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
    231       return false;
    232     }
    233     manifest_url->url_ = extension->GetResourceURL(options_str);
    234     if (!manifest_url->url_.is_valid()) {
    235       *error = base::ASCIIToUTF16(errors::kInvalidOptionsPage);
    236       return false;
    237     }
    238   }
    239 
    240   extension->SetManifestData(keys::kOptionsPage, manifest_url.release());
    241   return true;
    242 }
    243 
    244 bool OptionsPageHandler::Validate(const Extension* extension,
    245                                   std::string* error,
    246                                   std::vector<InstallWarning>* warnings) const {
    247   // Validate path to the options page.  Don't check the URL for hosted apps,
    248   // because they are expected to refer to an external URL.
    249   if (!extensions::ManifestURL::GetOptionsPage(extension).is_empty() &&
    250       !extension->is_hosted_app()) {
    251     const base::FilePath options_path =
    252         extensions::file_util::ExtensionURLToRelativeFilePath(
    253             extensions::ManifestURL::GetOptionsPage(extension));
    254     const base::FilePath path =
    255         extension->GetResource(options_path).GetFilePath();
    256     if (path.empty() || !base::PathExists(path)) {
    257       *error =
    258           l10n_util::GetStringFUTF8(
    259               IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED,
    260               options_path.LossyDisplayName());
    261       return false;
    262     }
    263   }
    264   return true;
    265 }
    266 
    267 const std::vector<std::string> OptionsPageHandler::Keys() const {
    268   return SingleKey(keys::kOptionsPage);
    269 }
    270 
    271 AboutPageHandler::AboutPageHandler() {
    272 }
    273 
    274 AboutPageHandler::~AboutPageHandler() {
    275 }
    276 
    277 bool AboutPageHandler::Parse(Extension* extension, base::string16* error) {
    278   scoped_ptr<ManifestURL> manifest_url(new ManifestURL);
    279   std::string about_str;
    280   if (!extension->manifest()->GetString(keys::kAboutPage, &about_str)) {
    281     *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
    282     return false;
    283   }
    284 
    285   GURL absolute(about_str);
    286   if (absolute.is_valid()) {
    287     *error = base::ASCIIToUTF16(errors::kInvalidAboutPageExpectRelativePath);
    288     return false;
    289   }
    290   manifest_url->url_ = extension->GetResourceURL(about_str);
    291   if (!manifest_url->url_.is_valid()) {
    292     *error = base::ASCIIToUTF16(errors::kInvalidAboutPage);
    293     return false;
    294   }
    295   extension->SetManifestData(keys::kAboutPage, manifest_url.release());
    296   return true;
    297 }
    298 
    299 bool AboutPageHandler::Validate(const Extension* extension,
    300                                 std::string* error,
    301                                 std::vector<InstallWarning>* warnings) const {
    302   // Validate path to the options page.
    303   if (!extensions::ManifestURL::GetAboutPage(extension).is_empty()) {
    304     const base::FilePath about_path =
    305         extensions::file_util::ExtensionURLToRelativeFilePath(
    306             extensions::ManifestURL::GetAboutPage(extension));
    307     const base::FilePath path =
    308         extension->GetResource(about_path).GetFilePath();
    309     if (path.empty() || !base::PathExists(path)) {
    310       *error = l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ABOUT_PAGE_FAILED,
    311                                          about_path.LossyDisplayName());
    312       return false;
    313     }
    314   }
    315   return true;
    316 }
    317 
    318 const std::vector<std::string> AboutPageHandler::Keys() const {
    319   return SingleKey(keys::kAboutPage);
    320 }
    321 
    322 URLOverridesHandler::URLOverridesHandler() {
    323 }
    324 
    325 URLOverridesHandler::~URLOverridesHandler() {
    326 }
    327 
    328 bool URLOverridesHandler::Parse(Extension* extension, base::string16* error) {
    329   const base::DictionaryValue* overrides = NULL;
    330   if (!extension->manifest()->GetDictionary(keys::kChromeURLOverrides,
    331                                             &overrides)) {
    332     *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
    333     return false;
    334   }
    335   scoped_ptr<URLOverrides> url_overrides(new URLOverrides);
    336   // Validate that the overrides are all strings
    337   for (base::DictionaryValue::Iterator iter(*overrides); !iter.IsAtEnd();
    338          iter.Advance()) {
    339     std::string page = iter.key();
    340     std::string val;
    341     // Restrict override pages to a list of supported URLs.
    342     bool is_override = (page != chrome::kChromeUINewTabHost &&
    343                         page != chrome::kChromeUIBookmarksHost &&
    344                         page != chrome::kChromeUIHistoryHost);
    345 #if defined(OS_CHROMEOS)
    346     is_override = (is_override &&
    347                    page != chrome::kChromeUIActivationMessageHost);
    348 #endif
    349 #if defined(OS_CHROMEOS)
    350     is_override = (is_override && page != keyboard::kKeyboardHost);
    351 #endif
    352 
    353     if (is_override || !iter.value().GetAsString(&val)) {
    354       *error = base::ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
    355       return false;
    356     }
    357     // Replace the entry with a fully qualified chrome-extension:// URL.
    358     url_overrides->chrome_url_overrides_[page] = extension->GetResourceURL(val);
    359 
    360     // For component extensions, add override URL to extent patterns.
    361     if (extension->is_legacy_packaged_app() &&
    362         extension->location() == Manifest::COMPONENT) {
    363       URLPattern pattern(URLPattern::SCHEME_CHROMEUI);
    364       std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat,
    365                                            page.c_str());
    366       if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
    367         *error = ErrorUtils::FormatErrorMessageUTF16(
    368             errors::kInvalidURLPatternError, url);
    369         return false;
    370       }
    371       extension->AddWebExtentPattern(pattern);
    372     }
    373   }
    374 
    375   // An extension may override at most one page.
    376   if (overrides->size() > 1) {
    377     *error = base::ASCIIToUTF16(errors::kMultipleOverrides);
    378     return false;
    379   }
    380   extension->SetManifestData(keys::kChromeURLOverrides,
    381                              url_overrides.release());
    382   return true;
    383 }
    384 
    385 const std::vector<std::string> URLOverridesHandler::Keys() const {
    386   return SingleKey(keys::kChromeURLOverrides);
    387 }
    388 
    389 }  // namespace extensions
    390