app_launch_info.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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.
4
5#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
6
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"
18
19namespace extensions {
20
21namespace keys = manifest_keys;
22namespace values = manifest_values;
23namespace errors = manifest_errors;
24
25namespace {
26
27bool ReadLaunchDimension(const extensions::Manifest* manifest,
28                         const char* key,
29                         int* target,
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,
37          key);
38      return false;
39    }
40    if (!temp->GetAsInteger(target) || *target < 0) {
41      *target = 0;
42      *error = ErrorUtils::FormatErrorMessageUTF16(
43          errors::kInvalidLaunchValue,
44          key);
45      return false;
46    }
47  }
48  return true;
49}
50
51static base::LazyInstance<AppLaunchInfo> g_empty_app_launch_info =
52    LAZY_INSTANCE_INITIALIZER;
53
54const 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();
58}
59
60}  // namespace
61
62AppLaunchInfo::AppLaunchInfo()
63    : launch_container_(LAUNCH_CONTAINER_TAB),
64      launch_width_(0),
65      launch_height_(0) {
66}
67
68AppLaunchInfo::~AppLaunchInfo() {
69}
70
71// static
72const std::string& AppLaunchInfo::GetLaunchLocalPath(
73    const Extension* extension) {
74  return GetAppLaunchInfo(extension).launch_local_path_;
75}
76
77// static
78const GURL& AppLaunchInfo::GetLaunchWebURL(
79    const Extension* extension) {
80  return GetAppLaunchInfo(extension).launch_web_url_;
81}
82
83// static
84extensions::LaunchContainer AppLaunchInfo::GetLaunchContainer(
85    const Extension* extension) {
86  return GetAppLaunchInfo(extension).launch_container_;
87}
88
89// static
90int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
91  return GetAppLaunchInfo(extension).launch_width_;
92}
93
94// static
95int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
96  return GetAppLaunchInfo(extension).launch_height_;
97}
98
99// static
100GURL AppLaunchInfo::GetFullLaunchURL(const Extension* extension) {
101  const AppLaunchInfo& info = GetAppLaunchInfo(extension);
102  if (info.launch_local_path_.empty())
103    return info.launch_web_url_;
104  else
105    return extension->url().Resolve(info.launch_local_path_);
106}
107
108bool AppLaunchInfo::Parse(Extension* extension, base::string16* error) {
109  if (!LoadLaunchURL(extension, error) ||
110      !LoadLaunchContainer(extension, error))
111    return false;
112  return true;
113}
114
115bool AppLaunchInfo::LoadLaunchURL(Extension* extension, base::string16* error) {
116  const base::Value* temp = NULL;
117
118  // Launch URL can be either local (to chrome-extension:// root) or an absolute
119  // web URL.
120  if (extension->manifest()->Get(keys::kLaunchLocalPath, &temp)) {
121    if (extension->manifest()->Get(keys::kLaunchWebURL, NULL)) {
122      *error = base::ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
123      return false;
124    }
125
126    if (extension->manifest()->Get(keys::kWebURLs, NULL)) {
127      *error = base::ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
128      return false;
129    }
130
131    std::string launch_path;
132    if (!temp->GetAsString(&launch_path)) {
133      *error = ErrorUtils::FormatErrorMessageUTF16(
134          errors::kInvalidLaunchValue,
135          keys::kLaunchLocalPath);
136      return false;
137    }
138
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);
145      return false;
146    }
147
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);
155      return false;
156    }
157
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);
165      return false;
166    }
167
168    launch_web_url_ = url;
169  } else if (extension->is_legacy_packaged_app()) {
170    *error = base::ASCIIToUTF16(errors::kLaunchURLRequired);
171    return false;
172  }
173
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);
177    return true;
178  }
179
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);
187      return false;
188    }
189    pattern.SetHost(launch_web_url_.host());
190    pattern.SetPath("/*");
191    extension->AddWebExtentPattern(pattern);
192  }
193
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);
200
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);
205    }
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.
209    GURL url =
210        cloud_devices::GetCloudPrintRelativeURL("enable_chrome_connector");
211    if (!url.is_empty()) {
212      OverrideLaunchURL(extension, url);
213    }
214  }
215
216  return true;
217}
218
219bool 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))
224    return true;
225
226  std::string launch_container_string;
227  if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
228    *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
229    return false;
230  }
231
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;
236  } else {
237    *error = base::ASCIIToUTF16(errors::kInvalidLaunchContainer);
238    return false;
239  }
240
241  bool can_specify_initial_size = launch_container_ == LAUNCH_CONTAINER_PANEL;
242
243  // Validate the container width if present.
244  if (!ReadLaunchDimension(extension->manifest(),
245                           keys::kLaunchWidth,
246                           &launch_width_,
247                           can_specify_initial_size,
248                           error)) {
249    return false;
250  }
251
252  // Validate container height if present.
253  if (!ReadLaunchDimension(extension->manifest(),
254                           keys::kLaunchHeight,
255                           &launch_height_,
256                           can_specify_initial_size,
257                           error)) {
258    return false;
259  }
260
261  return true;
262}
263
264void AppLaunchInfo::OverrideLaunchURL(Extension* extension,
265                                      GURL override_url) {
266  if (!override_url.is_valid()) {
267    DLOG(WARNING) << "Invalid override url given for " << extension->name();
268    return;
269  }
270  if (override_url.has_port()) {
271    DLOG(WARNING) << "Override URL passed for " << extension->name()
272                  << " should not contain a port.  Removing it.";
273
274    GURL::Replacements remove_port;
275    remove_port.ClearPort();
276    override_url = override_url.ReplaceComponents(remove_port);
277  }
278
279  launch_web_url_ = override_url;
280
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);
286}
287
288AppLaunchManifestHandler::AppLaunchManifestHandler() {
289}
290
291AppLaunchManifestHandler::~AppLaunchManifestHandler() {
292}
293
294bool AppLaunchManifestHandler::Parse(Extension* extension,
295                                     base::string16* error) {
296  scoped_ptr<AppLaunchInfo> info(new AppLaunchInfo);
297  if (!info->Parse(extension, error))
298    return false;
299  extension->SetManifestData(keys::kLaunch, info.release());
300  return true;
301}
302
303bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type) const {
304  return type == Manifest::TYPE_LEGACY_PACKAGED_APP;
305}
306
307const std::vector<std::string> AppLaunchManifestHandler::Keys() const {
308  static const char* keys[] = {
309    keys::kLaunchLocalPath,
310    keys::kLaunchWebURL,
311    keys::kLaunchContainer,
312    keys::kLaunchHeight,
313    keys::kLaunchWidth
314  };
315  return std::vector<std::string>(keys, keys + arraysize(keys));
316}
317
318}  // namespace extensions
319