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.
5 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
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"
19 namespace extensions {
21 namespace keys = manifest_keys;
22 namespace values = manifest_values;
23 namespace errors = manifest_errors;
27 bool ReadLaunchDimension(const extensions::Manifest* manifest,
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,
40 if (!temp->GetAsInteger(target) || *target < 0) {
42 *error = ErrorUtils::FormatErrorMessageUTF16(
43 errors::kInvalidLaunchValue,
51 static base::LazyInstance<AppLaunchInfo> g_empty_app_launch_info =
52 LAZY_INSTANCE_INITIALIZER;
54 const 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();
62 AppLaunchInfo::AppLaunchInfo()
63 : launch_container_(LAUNCH_CONTAINER_TAB),
68 AppLaunchInfo::~AppLaunchInfo() {
72 const std::string& AppLaunchInfo::GetLaunchLocalPath(
73 const Extension* extension) {
74 return GetAppLaunchInfo(extension).launch_local_path_;
78 const GURL& AppLaunchInfo::GetLaunchWebURL(
79 const Extension* extension) {
80 return GetAppLaunchInfo(extension).launch_web_url_;
84 extensions::LaunchContainer AppLaunchInfo::GetLaunchContainer(
85 const Extension* extension) {
86 return GetAppLaunchInfo(extension).launch_container_;
90 int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
91 return GetAppLaunchInfo(extension).launch_width_;
95 int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
96 return GetAppLaunchInfo(extension).launch_height_;
100 GURL AppLaunchInfo::GetFullLaunchURL(const Extension* extension) {
101 const AppLaunchInfo& info = GetAppLaunchInfo(extension);
102 if (info.launch_local_path_.empty())
103 return info.launch_web_url_;
105 return extension->url().Resolve(info.launch_local_path_);
108 bool AppLaunchInfo::Parse(Extension* extension, base::string16* error) {
109 if (!LoadLaunchURL(extension, error) ||
110 !LoadLaunchContainer(extension, error))
115 bool AppLaunchInfo::LoadLaunchURL(Extension* extension, base::string16* error) {
116 const base::Value* temp = NULL;
118 // Launch URL can be either local (to chrome-extension:// root) or an absolute
120 if (extension->manifest()->Get(keys::kLaunchLocalPath, &temp)) {
121 if (extension->manifest()->Get(keys::kLaunchWebURL, NULL)) {
122 *error = base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
126 if (extension->manifest()->Get(keys::kWebURLs, NULL)) {
127 *error = base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
131 std::string launch_path;
132 if (!temp->GetAsString(&launch_path)) {
133 *error = ErrorUtils::FormatErrorMessageUTF16(
134 errors::kInvalidLaunchValue,
135 keys::kLaunchLocalPath);
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);
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);
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);
168 launch_web_url_ = url;
169 } else if (extension->is_legacy_packaged_app()) {
170 *error = base::ASCIIToUTF16(errors::kLaunchURLRequired);
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);
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);
189 pattern.SetHost(launch_web_url_.host());
190 pattern.SetPath("/*");
191 extension->AddWebExtentPattern(pattern);
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);
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);
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.
210 cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
211 if (!url.is_empty()) {
212 OverrideLaunchURL(extension, url);
219 bool 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))
226 std::string launch_container_string;
227 if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
228 *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
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;
237 *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
241 bool can_specify_initial_size = launch_container_ == LAUNCH_CONTAINER_PANEL;
243 // Validate the container width if present.
244 if (!ReadLaunchDimension(extension->manifest(),
247 can_specify_initial_size,
252 // Validate container height if present.
253 if (!ReadLaunchDimension(extension->manifest(),
256 can_specify_initial_size,
264 void AppLaunchInfo::OverrideLaunchURL(Extension* extension,
266 if (!override_url.is_valid()) {
267 DLOG(WARNING) << "Invalid override url given for " << extension->name();
270 if (override_url.has_port()) {
271 DLOG(WARNING) << "Override URL passed for " << extension->name()
272 << " should not contain a port. Removing it.";
274 GURL::Replacements remove_port;
275 remove_port.ClearPort();
276 override_url = override_url.ReplaceComponents(remove_port);
279 launch_web_url_ = override_url;
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);
288 AppLaunchManifestHandler::AppLaunchManifestHandler() {
291 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
294 bool AppLaunchManifestHandler::Parse(Extension* extension,
295 base::string16* error) {
296 scoped_ptr<AppLaunchInfo> info(new AppLaunchInfo);
297 if (!info->Parse(extension, error))
299 extension->SetManifestData(keys::kLaunch, info.release());
303 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type) const {
304 return type == Manifest::TYPE_LEGACY_PACKAGED_APP;
307 const std::vector<std::string> AppLaunchManifestHandler::Keys() const {
308 static const char* keys[] = {
309 keys::kLaunchLocalPath,
311 keys::kLaunchContainer,
315 return std::vector<std::string>(keys, keys + arraysize(keys));
318 } // namespace extensions