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