Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / app_launch_info.cc
blob970796c7d177d8eca55d01176dba3460f7fb7846
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;
26 namespace {
28 bool ReadLaunchDimension(const extensions::Manifest* manifest,
29 const char* key,
30 int* target,
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,
38 key);
39 return false;
41 if (!temp->GetAsInteger(target) || *target < 0) {
42 *target = 0;
43 *error = ErrorUtils::FormatErrorMessageUTF16(
44 errors::kInvalidLaunchValue,
45 key);
46 return false;
49 return true;
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();
61 } // namespace
63 AppLaunchInfo::AppLaunchInfo()
64 : launch_container_(LAUNCH_CONTAINER_TAB),
65 launch_width_(0),
66 launch_height_(0) {
69 AppLaunchInfo::~AppLaunchInfo() {
72 // static
73 const std::string& AppLaunchInfo::GetLaunchLocalPath(
74 const Extension* extension) {
75 return GetAppLaunchInfo(extension).launch_local_path_;
78 // static
79 const GURL& AppLaunchInfo::GetLaunchWebURL(
80 const Extension* extension) {
81 return GetAppLaunchInfo(extension).launch_web_url_;
84 // static
85 extensions::LaunchContainer AppLaunchInfo::GetLaunchContainer(
86 const Extension* extension) {
87 return GetAppLaunchInfo(extension).launch_container_;
90 // static
91 int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
92 return GetAppLaunchInfo(extension).launch_width_;
95 // static
96 int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
97 return GetAppLaunchInfo(extension).launch_height_;
100 // static
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_;
105 else
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))
112 return false;
113 return true;
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
120 // web URL.
121 if (extension->manifest()->Get(keys::kLaunchLocalPath, &temp)) {
122 if (extension->manifest()->Get(keys::kLaunchWebURL, NULL)) {
123 *error = base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
124 return false;
127 if (extension->manifest()->Get(keys::kWebURLs, NULL)) {
128 *error = base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
129 return false;
132 std::string launch_path;
133 if (!temp->GetAsString(&launch_path)) {
134 *error = ErrorUtils::FormatErrorMessageUTF16(
135 errors::kInvalidLaunchValue,
136 keys::kLaunchLocalPath);
137 return false;
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);
146 return false;
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);
156 return false;
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);
168 return false;
171 launch_web_url_ = url;
172 } else if (extension->is_legacy_packaged_app()) {
173 *error = base::ASCIIToUTF16(errors::kLaunchURLRequired);
174 return false;
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);
180 return true;
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
185 // permissionless.
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);
193 return false;
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.
216 GURL url =
217 cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
218 if (!url.is_empty()) {
219 OverrideLaunchURL(extension, url);
223 return true;
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))
231 return true;
233 std::string launch_container_string;
234 if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
235 *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
236 return false;
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;
243 } else {
244 *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
245 return false;
248 bool can_specify_initial_size = launch_container_ == LAUNCH_CONTAINER_PANEL;
250 // Validate the container width if present.
251 if (!ReadLaunchDimension(extension->manifest(),
252 keys::kLaunchWidth,
253 &launch_width_,
254 can_specify_initial_size,
255 error)) {
256 return false;
259 // Validate container height if present.
260 if (!ReadLaunchDimension(extension->manifest(),
261 keys::kLaunchHeight,
262 &launch_height_,
263 can_specify_initial_size,
264 error)) {
265 return false;
268 return true;
271 void AppLaunchInfo::OverrideLaunchURL(Extension* extension,
272 GURL override_url) {
273 if (!override_url.is_valid()) {
274 DLOG(WARNING) << "Invalid override url given for " << extension->name();
275 return;
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))
305 return false;
306 extension->SetManifestData(keys::kLaunch, info.release());
307 return true;
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,
317 keys::kLaunchWebURL,
318 keys::kLaunchContainer,
319 keys::kLaunchHeight,
320 keys::kLaunchWidth
322 return std::vector<std::string>(keys, keys + arraysize(keys));
325 } // namespace extensions