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 (extension
->from_bookmark())
163 pattern
.SetValidSchemes(Extension::kValidBookmarkAppSchemes
);
164 if ((!url
.is_valid() || !pattern
.SetScheme(url
.scheme()))) {
165 *error
= ErrorUtils::FormatErrorMessageUTF16(
166 errors::kInvalidLaunchValue
,
167 keys::kLaunchWebURL
);
171 launch_web_url_
= url
;
172 } else if (extension
->is_legacy_packaged_app()) {
173 *error
= base::ASCIIToUTF16(errors::kLaunchURLRequired
);
177 // For the Chrome component app, override launch url to new tab.
178 if (extension
->id() == extension_misc::kChromeAppId
) {
179 launch_web_url_
= GURL(chrome::kChromeUINewTabURL
);
183 // If there is no extent, we default the extent based on the launch URL.
184 // Skip this step if the extension is from a bookmark app, as they are
186 if (extension
->web_extent().is_empty() && !launch_web_url_
.is_empty() &&
187 !extension
->from_bookmark()) {
188 URLPattern
pattern(Extension::kValidWebExtentSchemes
);
189 if (!pattern
.SetScheme("*")) {
190 *error
= ErrorUtils::FormatErrorMessageUTF16(
191 errors::kInvalidLaunchValue
,
192 keys::kLaunchWebURL
);
195 pattern
.SetHost(launch_web_url_
.host());
196 pattern
.SetPath("/*");
197 extension
->AddWebExtentPattern(pattern
);
200 // In order for the --apps-gallery-url switch to work with the gallery
201 // process isolation, we must insert any provided value into the component
202 // app's launch url and web extent.
203 if (extension
->id() == extensions::kWebStoreAppId
) {
204 std::string gallery_url_str
=
205 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
206 switches::kAppsGalleryURL
);
208 // Empty string means option was not used.
209 if (!gallery_url_str
.empty()) {
210 GURL
gallery_url(gallery_url_str
);
211 OverrideLaunchURL(extension
, gallery_url
);
213 } else if (extension
->id() == extension_misc::kCloudPrintAppId
) {
214 // In order for the --cloud-print-service switch to work, we must update
215 // the launch URL and web extent.
217 cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
218 if (!url
.is_empty()) {
219 OverrideLaunchURL(extension
, url
);
226 bool AppLaunchInfo::LoadLaunchContainer(Extension
* extension
,
227 base::string16
* error
) {
228 const base::Value
* tmp_launcher_container
= NULL
;
229 if (!extension
->manifest()->Get(keys::kLaunchContainer
,
230 &tmp_launcher_container
))
233 std::string launch_container_string
;
234 if (!tmp_launcher_container
->GetAsString(&launch_container_string
)) {
235 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
239 if (launch_container_string
== values::kLaunchContainerPanel
) {
240 launch_container_
= LAUNCH_CONTAINER_PANEL
;
241 } else if (launch_container_string
== values::kLaunchContainerTab
) {
242 launch_container_
= LAUNCH_CONTAINER_TAB
;
244 *error
= base::ASCIIToUTF16(errors::kInvalidLaunchContainer
);
248 bool can_specify_initial_size
= launch_container_
== LAUNCH_CONTAINER_PANEL
;
250 // Validate the container width if present.
251 if (!ReadLaunchDimension(extension
->manifest(),
254 can_specify_initial_size
,
259 // Validate container height if present.
260 if (!ReadLaunchDimension(extension
->manifest(),
263 can_specify_initial_size
,
271 void AppLaunchInfo::OverrideLaunchURL(Extension
* extension
,
273 if (!override_url
.is_valid()) {
274 DLOG(WARNING
) << "Invalid override url given for " << extension
->name();
277 if (override_url
.has_port()) {
278 DLOG(WARNING
) << "Override URL passed for " << extension
->name()
279 << " should not contain a port. Removing it.";
281 GURL::Replacements remove_port
;
282 remove_port
.ClearPort();
283 override_url
= override_url
.ReplaceComponents(remove_port
);
286 launch_web_url_
= override_url
;
288 URLPattern
pattern(Extension::kValidWebExtentSchemes
);
289 URLPattern::ParseResult result
= pattern
.Parse(override_url
.spec());
290 DCHECK_EQ(result
, URLPattern::PARSE_SUCCESS
);
291 pattern
.SetPath(pattern
.path() + '*');
292 extension
->AddWebExtentPattern(pattern
);
295 AppLaunchManifestHandler::AppLaunchManifestHandler() {
298 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
301 bool AppLaunchManifestHandler::Parse(Extension
* extension
,
302 base::string16
* error
) {
303 scoped_ptr
<AppLaunchInfo
> info(new AppLaunchInfo
);
304 if (!info
->Parse(extension
, error
))
306 extension
->SetManifestData(keys::kLaunch
, info
.release());
310 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type
) const {
311 return type
== Manifest::TYPE_LEGACY_PACKAGED_APP
;
314 const std::vector
<std::string
> AppLaunchManifestHandler::Keys() const {
315 static const char* const keys
[] = {
316 keys::kLaunchLocalPath
,
318 keys::kLaunchContainer
,
322 return std::vector
<std::string
>(keys
, keys
+ arraysize(keys
));
325 } // namespace extensions