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/constants.h"
17 #include "extensions/common/error_utils.h"
18 #include "extensions/common/manifest_constants.h"
20 namespace extensions
{
22 namespace keys
= manifest_keys
;
23 namespace values
= manifest_values
;
24 namespace errors
= manifest_errors
;
28 bool ReadLaunchDimension(const extensions::Manifest
* manifest
,
31 bool is_valid_container
,
32 base::string16
* error
) {
33 const base::Value
* temp
= NULL
;
34 if (manifest
->Get(key
, &temp
)) {
35 if (!is_valid_container
) {
36 *error
= ErrorUtils::FormatErrorMessageUTF16(
37 errors::kInvalidLaunchValueContainer
,
41 if (!temp
->GetAsInteger(target
) || *target
< 0) {
43 *error
= ErrorUtils::FormatErrorMessageUTF16(
44 errors::kInvalidLaunchValue
,
52 static base::LazyInstance
<AppLaunchInfo
> g_empty_app_launch_info
=
53 LAZY_INSTANCE_INITIALIZER
;
55 const AppLaunchInfo
& GetAppLaunchInfo(const Extension
* extension
) {
56 AppLaunchInfo
* info
= static_cast<AppLaunchInfo
*>(
57 extension
->GetManifestData(keys::kLaunch
));
58 return info
? *info
: g_empty_app_launch_info
.Get();
63 AppLaunchInfo::AppLaunchInfo()
64 : launch_container_(LAUNCH_CONTAINER_TAB
),
69 AppLaunchInfo::~AppLaunchInfo() {
73 const std::string
& AppLaunchInfo::GetLaunchLocalPath(
74 const Extension
* extension
) {
75 return GetAppLaunchInfo(extension
).launch_local_path_
;
79 const GURL
& AppLaunchInfo::GetLaunchWebURL(
80 const Extension
* extension
) {
81 return GetAppLaunchInfo(extension
).launch_web_url_
;
85 extensions::LaunchContainer
AppLaunchInfo::GetLaunchContainer(
86 const Extension
* extension
) {
87 return GetAppLaunchInfo(extension
).launch_container_
;
91 int AppLaunchInfo::GetLaunchWidth(const Extension
* extension
) {
92 return GetAppLaunchInfo(extension
).launch_width_
;
96 int AppLaunchInfo::GetLaunchHeight(const Extension
* extension
) {
97 return GetAppLaunchInfo(extension
).launch_height_
;
101 GURL
AppLaunchInfo::GetFullLaunchURL(const Extension
* extension
) {
102 const AppLaunchInfo
& info
= GetAppLaunchInfo(extension
);
103 if (info
.launch_local_path_
.empty())
104 return info
.launch_web_url_
;
106 return extension
->url().Resolve(info
.launch_local_path_
);
109 bool AppLaunchInfo::Parse(Extension
* extension
, base::string16
* error
) {
110 if (!LoadLaunchURL(extension
, error
) ||
111 !LoadLaunchContainer(extension
, error
))
116 bool AppLaunchInfo::LoadLaunchURL(Extension
* extension
, base::string16
* error
) {
117 const base::Value
* temp
= NULL
;
119 // Launch URL can be either local (to chrome-extension:// root) or an absolute
121 if (extension
->manifest()->Get(keys::kLaunchLocalPath
, &temp
)) {
122 if (extension
->manifest()->Get(keys::kLaunchWebURL
, NULL
)) {
123 *error
= base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive
);
127 if (extension
->manifest()->Get(keys::kWebURLs
, NULL
)) {
128 *error
= base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive
);
132 std::string launch_path
;
133 if (!temp
->GetAsString(&launch_path
)) {
134 *error
= ErrorUtils::FormatErrorMessageUTF16(
135 errors::kInvalidLaunchValue
,
136 keys::kLaunchLocalPath
);
140 // Ensure the launch path is a valid relative URL.
141 GURL resolved
= extension
->url().Resolve(launch_path
);
142 if (!resolved
.is_valid() || resolved
.GetOrigin() != extension
->url()) {
143 *error
= ErrorUtils::FormatErrorMessageUTF16(
144 errors::kInvalidLaunchValue
,
145 keys::kLaunchLocalPath
);
149 launch_local_path_
= launch_path
;
150 } else if (extension
->manifest()->Get(keys::kLaunchWebURL
, &temp
)) {
151 std::string launch_url
;
152 if (!temp
->GetAsString(&launch_url
)) {
153 *error
= ErrorUtils::FormatErrorMessageUTF16(
154 errors::kInvalidLaunchValue
,
155 keys::kLaunchWebURL
);
159 // Ensure the launch web URL is a valid absolute URL and web extent scheme.
160 GURL
url(launch_url
);
161 URLPattern
pattern(Extension::kValidWebExtentSchemes
);
162 if (!url
.is_valid() || !pattern
.SetScheme(url
.scheme())) {
163 *error
= ErrorUtils::FormatErrorMessageUTF16(
164 errors::kInvalidLaunchValue
,
165 keys::kLaunchWebURL
);
169 launch_web_url_
= url
;
170 } else if (extension
->is_legacy_packaged_app()) {
171 *error
= base::ASCIIToUTF16(errors::kLaunchURLRequired
);
175 // For the Chrome component app, override launch url to new tab.
176 if (extension
->id() == extension_misc::kChromeAppId
) {
177 launch_web_url_
= GURL(chrome::kChromeUINewTabURL
);
181 // If there is no extent, we default the extent based on the launch URL.
182 if (extension
->web_extent().is_empty() && !launch_web_url_
.is_empty()) {
183 URLPattern
pattern(Extension::kValidWebExtentSchemes
);
184 if (!pattern
.SetScheme("*")) {
185 *error
= ErrorUtils::FormatErrorMessageUTF16(
186 errors::kInvalidLaunchValue
,
187 keys::kLaunchWebURL
);
190 pattern
.SetHost(launch_web_url_
.host());
191 pattern
.SetPath("/*");
192 extension
->AddWebExtentPattern(pattern
);
195 // In order for the --apps-gallery-url switch to work with the gallery
196 // process isolation, we must insert any provided value into the component
197 // app's launch url and web extent.
198 if (extension
->id() == extensions::kWebStoreAppId
) {
199 std::string gallery_url_str
=
200 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
201 switches::kAppsGalleryURL
);
203 // Empty string means option was not used.
204 if (!gallery_url_str
.empty()) {
205 GURL
gallery_url(gallery_url_str
);
206 OverrideLaunchURL(extension
, gallery_url
);
208 } else if (extension
->id() == extension_misc::kCloudPrintAppId
) {
209 // In order for the --cloud-print-service switch to work, we must update
210 // the launch URL and web extent.
212 cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
213 if (!url
.is_empty()) {
214 OverrideLaunchURL(extension
, url
);
221 bool AppLaunchInfo::LoadLaunchContainer(Extension
* extension
,
222 base::string16
* error
) {
223 const base::Value
* tmp_launcher_container
= NULL
;
224 if (!extension
->manifest()->Get(keys::kLaunchContainer
,
225 &tmp_launcher_container
))
228 std::string launch_container_string
;
229 if (!tmp_launcher_container
->GetAsString(&launch_container_string
)) {
230 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
234 if (launch_container_string
== values::kLaunchContainerPanel
) {
235 launch_container_
= LAUNCH_CONTAINER_PANEL
;
236 } else if (launch_container_string
== values::kLaunchContainerTab
) {
237 launch_container_
= LAUNCH_CONTAINER_TAB
;
239 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
243 bool can_specify_initial_size
= launch_container_
== LAUNCH_CONTAINER_PANEL
;
245 // Validate the container width if present.
246 if (!ReadLaunchDimension(extension
->manifest(),
249 can_specify_initial_size
,
254 // Validate container height if present.
255 if (!ReadLaunchDimension(extension
->manifest(),
258 can_specify_initial_size
,
266 void AppLaunchInfo::OverrideLaunchURL(Extension
* extension
,
268 if (!override_url
.is_valid()) {
269 DLOG(WARNING
) << "Invalid override url given for " << extension
->name();
272 if (override_url
.has_port()) {
273 DLOG(WARNING
) << "Override URL passed for " << extension
->name()
274 << " should not contain a port. Removing it.";
276 GURL::Replacements remove_port
;
277 remove_port
.ClearPort();
278 override_url
= override_url
.ReplaceComponents(remove_port
);
281 launch_web_url_
= override_url
;
283 URLPattern
pattern(Extension::kValidWebExtentSchemes
);
284 URLPattern::ParseResult result
= pattern
.Parse(override_url
.spec());
285 DCHECK_EQ(result
, URLPattern::PARSE_SUCCESS
);
286 pattern
.SetPath(pattern
.path() + '*');
287 extension
->AddWebExtentPattern(pattern
);
290 AppLaunchManifestHandler::AppLaunchManifestHandler() {
293 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
296 bool AppLaunchManifestHandler::Parse(Extension
* extension
,
297 base::string16
* error
) {
298 scoped_ptr
<AppLaunchInfo
> info(new AppLaunchInfo
);
299 if (!info
->Parse(extension
, error
))
301 extension
->SetManifestData(keys::kLaunch
, info
.release());
305 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type
) const {
306 return type
== Manifest::TYPE_LEGACY_PACKAGED_APP
;
309 const std::vector
<std::string
> AppLaunchManifestHandler::Keys() const {
310 static const char* const keys
[] = {
311 keys::kLaunchLocalPath
,
313 keys::kLaunchContainer
,
317 return std::vector
<std::string
>(keys
, keys
+ arraysize(keys
));
320 } // namespace extensions