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