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