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