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