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 "extensions/common/error_utils.h"
16 #include "extensions/common/manifest_constants.h"
18 namespace extensions
{
20 namespace keys
= manifest_keys
;
21 namespace values
= manifest_values
;
22 namespace errors
= manifest_errors
;
26 bool ReadLaunchDimension(const extensions::Manifest
* manifest
,
29 bool is_valid_container
,
30 base::string16
* error
) {
31 const base::Value
* temp
= NULL
;
32 if (manifest
->Get(key
, &temp
)) {
33 if (!is_valid_container
) {
34 *error
= ErrorUtils::FormatErrorMessageUTF16(
35 errors::kInvalidLaunchValueContainer
,
39 if (!temp
->GetAsInteger(target
) || *target
< 0) {
41 *error
= ErrorUtils::FormatErrorMessageUTF16(
42 errors::kInvalidLaunchValue
,
50 static base::LazyInstance
<AppLaunchInfo
> g_empty_app_launch_info
=
51 LAZY_INSTANCE_INITIALIZER
;
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();
61 AppLaunchInfo::AppLaunchInfo()
62 : launch_container_(LAUNCH_CONTAINER_TAB
),
67 AppLaunchInfo::~AppLaunchInfo() {
71 const std::string
& AppLaunchInfo::GetLaunchLocalPath(
72 const Extension
* extension
) {
73 return GetAppLaunchInfo(extension
).launch_local_path_
;
77 const GURL
& AppLaunchInfo::GetLaunchWebURL(
78 const Extension
* extension
) {
79 return GetAppLaunchInfo(extension
).launch_web_url_
;
83 extensions::LaunchContainer
AppLaunchInfo::GetLaunchContainer(
84 const Extension
* extension
) {
85 return GetAppLaunchInfo(extension
).launch_container_
;
89 int AppLaunchInfo::GetLaunchWidth(const Extension
* extension
) {
90 return GetAppLaunchInfo(extension
).launch_width_
;
94 int AppLaunchInfo::GetLaunchHeight(const Extension
* extension
) {
95 return GetAppLaunchInfo(extension
).launch_height_
;
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_
;
104 return extension
->url().Resolve(info
.launch_local_path_
);
107 bool AppLaunchInfo::Parse(Extension
* extension
, base::string16
* error
) {
108 if (!LoadLaunchURL(extension
, error
) ||
109 !LoadLaunchContainer(extension
, error
))
114 bool AppLaunchInfo::LoadLaunchURL(Extension
* extension
, base::string16
* error
) {
115 const base::Value
* temp
= NULL
;
117 // Launch URL can be either local (to chrome-extension:// root) or an absolute
119 if (extension
->manifest()->Get(keys::kLaunchLocalPath
, &temp
)) {
120 if (extension
->manifest()->Get(keys::kLaunchWebURL
, NULL
)) {
121 *error
= base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive
);
125 if (extension
->manifest()->Get(keys::kWebURLs
, NULL
)) {
126 *error
= base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive
);
130 std::string launch_path
;
131 if (!temp
->GetAsString(&launch_path
)) {
132 *error
= ErrorUtils::FormatErrorMessageUTF16(
133 errors::kInvalidLaunchValue
,
134 keys::kLaunchLocalPath
);
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
);
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
);
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
);
167 launch_web_url_
= url
;
168 } else if (extension
->is_legacy_packaged_app()) {
169 *error
= base::ASCIIToUTF16(errors::kLaunchURLRequired
);
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
);
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
);
188 pattern
.SetHost(launch_web_url_
.host());
189 pattern
.SetPath("/*");
190 extension
->AddWebExtentPattern(pattern
);
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
);
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
);
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()) {
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
);
227 bool AppLaunchInfo::LoadLaunchContainer(Extension
* extension
,
228 base::string16
* error
) {
229 const base::Value
* tmp_launcher_container
= NULL
;
230 if (!extension
->manifest()->Get(keys::kLaunchContainer
,
231 &tmp_launcher_container
))
234 std::string launch_container_string
;
235 if (!tmp_launcher_container
->GetAsString(&launch_container_string
)) {
236 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
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
;
245 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
249 bool can_specify_initial_size
= launch_container_
== LAUNCH_CONTAINER_PANEL
;
251 // Validate the container width if present.
252 if (!ReadLaunchDimension(extension
->manifest(),
255 can_specify_initial_size
,
260 // Validate container height if present.
261 if (!ReadLaunchDimension(extension
->manifest(),
264 can_specify_initial_size
,
272 void AppLaunchInfo::OverrideLaunchURL(Extension
* extension
,
274 if (!override_url
.is_valid()) {
275 DLOG(WARNING
) << "Invalid override url given for " << extension
->name();
278 if (override_url
.has_port()) {
279 DLOG(WARNING
) << "Override URL passed for " << extension
->name()
280 << " should not contain a port. Removing it.";
282 GURL::Replacements remove_port
;
283 remove_port
.ClearPort();
284 override_url
= override_url
.ReplaceComponents(remove_port
);
287 launch_web_url_
= override_url
;
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
);
296 AppLaunchManifestHandler::AppLaunchManifestHandler() {
299 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
302 bool AppLaunchManifestHandler::Parse(Extension
* extension
,
303 base::string16
* error
) {
304 scoped_ptr
<AppLaunchInfo
> info(new AppLaunchInfo
);
305 if (!info
->Parse(extension
, error
))
307 extension
->SetManifestData(keys::kLaunch
, info
.release());
311 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type
) const {
312 return type
== Manifest::TYPE_LEGACY_PACKAGED_APP
;
315 const std::vector
<std::string
> AppLaunchManifestHandler::Keys() const {
316 static const char* keys
[] = {
317 keys::kLaunchLocalPath
,
319 keys::kLaunchContainer
,
323 return std::vector
<std::string
>(keys
, keys
+ arraysize(keys
));
326 } // namespace extensions