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