Home | History | Annotate | Download | only in manifest_handlers
      1 // Copyright 2013 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_handlers/app_launch_info.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/values.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/common/extensions/extension_constants.h"
     14 #include "chrome/common/url_constants.h"
     15 #include "components/cloud_devices/common/cloud_devices_urls.h"
     16 #include "extensions/common/error_utils.h"
     17 #include "extensions/common/manifest_constants.h"
     18 
     19 namespace extensions {
     20 
     21 namespace keys = manifest_keys;
     22 namespace values = manifest_values;
     23 namespace errors = manifest_errors;
     24 
     25 namespace {
     26 
     27 bool ReadLaunchDimension(const extensions::Manifest* manifest,
     28                          const char* key,
     29                          int* target,
     30                          bool is_valid_container,
     31                          base::string16* error) {
     32   const base::Value* temp = NULL;
     33   if (manifest->Get(key, &temp)) {
     34     if (!is_valid_container) {
     35       *error = ErrorUtils::FormatErrorMessageUTF16(
     36           errors::kInvalidLaunchValueContainer,
     37           key);
     38       return false;
     39     }
     40     if (!temp->GetAsInteger(target) || *target < 0) {
     41       *target = 0;
     42       *error = ErrorUtils::FormatErrorMessageUTF16(
     43           errors::kInvalidLaunchValue,
     44           key);
     45       return false;
     46     }
     47   }
     48   return true;
     49 }
     50 
     51 static base::LazyInstance<AppLaunchInfo> g_empty_app_launch_info =
     52     LAZY_INSTANCE_INITIALIZER;
     53 
     54 const AppLaunchInfo& GetAppLaunchInfo(const Extension* extension) {
     55   AppLaunchInfo* info = static_cast<AppLaunchInfo*>(
     56       extension->GetManifestData(keys::kLaunch));
     57   return info ? *info : g_empty_app_launch_info.Get();
     58 }
     59 
     60 }  // namespace
     61 
     62 AppLaunchInfo::AppLaunchInfo()
     63     : launch_container_(LAUNCH_CONTAINER_TAB),
     64       launch_width_(0),
     65       launch_height_(0) {
     66 }
     67 
     68 AppLaunchInfo::~AppLaunchInfo() {
     69 }
     70 
     71 // static
     72 const std::string& AppLaunchInfo::GetLaunchLocalPath(
     73     const Extension* extension) {
     74   return GetAppLaunchInfo(extension).launch_local_path_;
     75 }
     76 
     77 // static
     78 const GURL& AppLaunchInfo::GetLaunchWebURL(
     79     const Extension* extension) {
     80   return GetAppLaunchInfo(extension).launch_web_url_;
     81 }
     82 
     83 // static
     84 extensions::LaunchContainer AppLaunchInfo::GetLaunchContainer(
     85     const Extension* extension) {
     86   return GetAppLaunchInfo(extension).launch_container_;
     87 }
     88 
     89 // static
     90 int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
     91   return GetAppLaunchInfo(extension).launch_width_;
     92 }
     93 
     94 // static
     95 int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
     96   return GetAppLaunchInfo(extension).launch_height_;
     97 }
     98 
     99 // static
    100 GURL AppLaunchInfo::GetFullLaunchURL(const Extension* extension) {
    101   const AppLaunchInfo& info = GetAppLaunchInfo(extension);
    102   if (info.launch_local_path_.empty())
    103     return info.launch_web_url_;
    104   else
    105     return extension->url().Resolve(info.launch_local_path_);
    106 }
    107 
    108 bool AppLaunchInfo::Parse(Extension* extension, base::string16* error) {
    109   if (!LoadLaunchURL(extension, error) ||
    110       !LoadLaunchContainer(extension, error))
    111     return false;
    112   return true;
    113 }
    114 
    115 bool AppLaunchInfo::LoadLaunchURL(Extension* extension, base::string16* error) {
    116   const base::Value* temp = NULL;
    117 
    118   // Launch URL can be either local (to chrome-extension:// root) or an absolute
    119   // web URL.
    120   if (extension->manifest()->Get(keys::kLaunchLocalPath, &temp)) {
    121     if (extension->manifest()->Get(keys::kLaunchWebURL, NULL)) {
    122       *error = base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
    123       return false;
    124     }
    125 
    126     if (extension->manifest()->Get(keys::kWebURLs, NULL)) {
    127       *error = base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
    128       return false;
    129     }
    130 
    131     std::string launch_path;
    132     if (!temp->GetAsString(&launch_path)) {
    133       *error = ErrorUtils::FormatErrorMessageUTF16(
    134           errors::kInvalidLaunchValue,
    135           keys::kLaunchLocalPath);
    136       return false;
    137     }
    138 
    139     // Ensure the launch path is a valid relative URL.
    140     GURL resolved = extension->url().Resolve(launch_path);
    141     if (!resolved.is_valid() || resolved.GetOrigin() != extension->url()) {
    142       *error = ErrorUtils::FormatErrorMessageUTF16(
    143           errors::kInvalidLaunchValue,
    144           keys::kLaunchLocalPath);
    145       return false;
    146     }
    147 
    148     launch_local_path_ = launch_path;
    149   } else if (extension->manifest()->Get(keys::kLaunchWebURL, &temp)) {
    150     std::string launch_url;
    151     if (!temp->GetAsString(&launch_url)) {
    152       *error = ErrorUtils::FormatErrorMessageUTF16(
    153           errors::kInvalidLaunchValue,
    154           keys::kLaunchWebURL);
    155       return false;
    156     }
    157 
    158     // Ensure the launch web URL is a valid absolute URL and web extent scheme.
    159     GURL url(launch_url);
    160     URLPattern pattern(Extension::kValidWebExtentSchemes);
    161     if (!url.is_valid() || !pattern.SetScheme(url.scheme())) {
    162       *error = ErrorUtils::FormatErrorMessageUTF16(
    163           errors::kInvalidLaunchValue,
    164           keys::kLaunchWebURL);
    165       return false;
    166     }
    167 
    168     launch_web_url_ = url;
    169   } else if (extension->is_legacy_packaged_app()) {
    170     *error = base::ASCIIToUTF16(errors::kLaunchURLRequired);
    171     return false;
    172   }
    173 
    174   // For the Chrome component app, override launch url to new tab.
    175   if (extension->id() == extension_misc::kChromeAppId) {
    176     launch_web_url_ = GURL(chrome::kChromeUINewTabURL);
    177     return true;
    178   }
    179 
    180   // If there is no extent, we default the extent based on the launch URL.
    181   if (extension->web_extent().is_empty() && !launch_web_url_.is_empty()) {
    182     URLPattern pattern(Extension::kValidWebExtentSchemes);
    183     if (!pattern.SetScheme("*")) {
    184       *error = ErrorUtils::FormatErrorMessageUTF16(
    185           errors::kInvalidLaunchValue,
    186           keys::kLaunchWebURL);
    187       return false;
    188     }
    189     pattern.SetHost(launch_web_url_.host());
    190     pattern.SetPath("/*");
    191     extension->AddWebExtentPattern(pattern);
    192   }
    193 
    194   // In order for the --apps-gallery-url switch to work with the gallery
    195   // process isolation, we must insert any provided value into the component
    196   // app's launch url and web extent.
    197   if (extension->id() == extension_misc::kWebStoreAppId) {
    198     std::string gallery_url_str = CommandLine::ForCurrentProcess()->
    199         GetSwitchValueASCII(switches::kAppsGalleryURL);
    200 
    201     // Empty string means option was not used.
    202     if (!gallery_url_str.empty()) {
    203       GURL gallery_url(gallery_url_str);
    204       OverrideLaunchURL(extension, gallery_url);
    205     }
    206   } else if (extension->id() == extension_misc::kCloudPrintAppId) {
    207     // In order for the --cloud-print-service switch to work, we must update
    208     // the launch URL and web extent.
    209     GURL url =
    210         cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
    211     if (!url.is_empty()) {
    212       OverrideLaunchURL(extension, url);
    213     }
    214   }
    215 
    216   return true;
    217 }
    218 
    219 bool AppLaunchInfo::LoadLaunchContainer(Extension* extension,
    220                                         base::string16* error) {
    221   const base::Value* tmp_launcher_container = NULL;
    222   if (!extension->manifest()->Get(keys::kLaunchContainer,
    223                                   &tmp_launcher_container))
    224     return true;
    225 
    226   std::string launch_container_string;
    227   if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
    228     *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
    229     return false;
    230   }
    231 
    232   if (launch_container_string == values::kLaunchContainerPanel) {
    233     launch_container_ = LAUNCH_CONTAINER_PANEL;
    234   } else if (launch_container_string == values::kLaunchContainerTab) {
    235     launch_container_ = LAUNCH_CONTAINER_TAB;
    236   } else {
    237     *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
    238     return false;
    239   }
    240 
    241   bool can_specify_initial_size = launch_container_ == LAUNCH_CONTAINER_PANEL;
    242 
    243   // Validate the container width if present.
    244   if (!ReadLaunchDimension(extension->manifest(),
    245                            keys::kLaunchWidth,
    246                            &launch_width_,
    247                            can_specify_initial_size,
    248                            error)) {
    249     return false;
    250   }
    251 
    252   // Validate container height if present.
    253   if (!ReadLaunchDimension(extension->manifest(),
    254                            keys::kLaunchHeight,
    255                            &launch_height_,
    256                            can_specify_initial_size,
    257                            error)) {
    258     return false;
    259   }
    260 
    261   return true;
    262 }
    263 
    264 void AppLaunchInfo::OverrideLaunchURL(Extension* extension,
    265                                       GURL override_url) {
    266   if (!override_url.is_valid()) {
    267     DLOG(WARNING) << "Invalid override url given for " << extension->name();
    268     return;
    269   }
    270   if (override_url.has_port()) {
    271     DLOG(WARNING) << "Override URL passed for " << extension->name()
    272                   << " should not contain a port.  Removing it.";
    273 
    274     GURL::Replacements remove_port;
    275     remove_port.ClearPort();
    276     override_url = override_url.ReplaceComponents(remove_port);
    277   }
    278 
    279   launch_web_url_ = override_url;
    280 
    281   URLPattern pattern(Extension::kValidWebExtentSchemes);
    282   URLPattern::ParseResult result = pattern.Parse(override_url.spec());
    283   DCHECK_EQ(result, URLPattern::PARSE_SUCCESS);
    284   pattern.SetPath(pattern.path() + '*');
    285   extension->AddWebExtentPattern(pattern);
    286 }
    287 
    288 AppLaunchManifestHandler::AppLaunchManifestHandler() {
    289 }
    290 
    291 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
    292 }
    293 
    294 bool AppLaunchManifestHandler::Parse(Extension* extension,
    295                                      base::string16* error) {
    296   scoped_ptr<AppLaunchInfo> info(new AppLaunchInfo);
    297   if (!info->Parse(extension, error))
    298     return false;
    299   extension->SetManifestData(keys::kLaunch, info.release());
    300   return true;
    301 }
    302 
    303 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type) const {
    304   return type == Manifest::TYPE_LEGACY_PACKAGED_APP;
    305 }
    306 
    307 const std::vector<std::string> AppLaunchManifestHandler::Keys() const {
    308   static const char* keys[] = {
    309     keys::kLaunchLocalPath,
    310     keys::kLaunchWebURL,
    311     keys::kLaunchContainer,
    312     keys::kLaunchHeight,
    313     keys::kLaunchWidth
    314   };
    315   return std::vector<std::string>(keys, keys + arraysize(keys));
    316 }
    317 
    318 }  // namespace extensions
    319