app_launch_info.cc revision 3551c9c881056c480085172ff9840cab31610854
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_manifest_constants.h"
14#include "chrome/common/url_constants.h"
15#include "extensions/common/error_utils.h"
16
17namespace keys = extensions::manifest_keys;
18namespace values = extension_manifest_values;
19namespace errors = extension_manifest_errors;
20
21namespace extensions {
22
23namespace {
24
25bool ReadLaunchDimension(const extensions::Manifest* manifest,
26                         const char* key,
27                         int* target,
28                         bool is_valid_container,
29                         string16* error) {
30  const Value* temp = NULL;
31  if (manifest->Get(key, &temp)) {
32    if (!is_valid_container) {
33      *error = ErrorUtils::FormatErrorMessageUTF16(
34          errors::kInvalidLaunchValueContainer,
35          key);
36      return false;
37    }
38    if (!temp->GetAsInteger(target) || *target < 0) {
39      *target = 0;
40      *error = ErrorUtils::FormatErrorMessageUTF16(
41          errors::kInvalidLaunchValue,
42          key);
43      return false;
44    }
45  }
46  return true;
47}
48
49static base::LazyInstance<AppLaunchInfo> g_empty_app_launch_info =
50    LAZY_INSTANCE_INITIALIZER;
51
52const AppLaunchInfo& GetAppLaunchInfo(const Extension* extension) {
53  AppLaunchInfo* info = static_cast<AppLaunchInfo*>(
54      extension->GetManifestData(keys::kLaunch));
55  return info ? *info : g_empty_app_launch_info.Get();
56}
57
58}  // namespace
59
60AppLaunchInfo::AppLaunchInfo()
61    : launch_container_(extension_misc::LAUNCH_TAB),
62      launch_width_(0),
63      launch_height_(0) {
64}
65
66AppLaunchInfo::~AppLaunchInfo() {
67}
68
69// static
70const std::string& AppLaunchInfo::GetLaunchLocalPath(
71    const Extension* extension) {
72  return GetAppLaunchInfo(extension).launch_local_path_;
73}
74
75// static
76const GURL& AppLaunchInfo::GetLaunchWebURL(
77    const Extension* extension) {
78  return GetAppLaunchInfo(extension).launch_web_url_;
79}
80
81// static
82extension_misc::LaunchContainer AppLaunchInfo::GetLaunchContainer(
83    const Extension* extension) {
84  return GetAppLaunchInfo(extension).launch_container_;
85}
86
87// static
88int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
89  return GetAppLaunchInfo(extension).launch_width_;
90}
91
92// static
93int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
94  return GetAppLaunchInfo(extension).launch_height_;
95}
96
97// static
98GURL AppLaunchInfo::GetFullLaunchURL(const Extension* extension) {
99  const AppLaunchInfo& info = GetAppLaunchInfo(extension);
100  if (info.launch_local_path_.empty())
101    return info.launch_web_url_;
102  else
103    return extension->url().Resolve(info.launch_local_path_);
104}
105
106bool AppLaunchInfo::Parse(Extension* extension, string16* error) {
107  if (!LoadLaunchURL(extension, error) ||
108      !LoadLaunchContainer(extension, error))
109    return false;
110  return true;
111}
112
113bool AppLaunchInfo::LoadLaunchURL(Extension* extension, string16* error) {
114  const Value* temp = NULL;
115
116  // Launch URL can be either local (to chrome-extension:// root) or an absolute
117  // web URL.
118  if (extension->manifest()->Get(keys::kLaunchLocalPath, &temp)) {
119    if (extension->manifest()->Get(keys::kLaunchWebURL, NULL)) {
120      *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
121      return false;
122    }
123
124    if (extension->manifest()->Get(keys::kWebURLs, NULL)) {
125      *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
126      return false;
127    }
128
129    std::string launch_path;
130    if (!temp->GetAsString(&launch_path)) {
131      *error = ErrorUtils::FormatErrorMessageUTF16(
132          errors::kInvalidLaunchValue,
133          keys::kLaunchLocalPath);
134      return false;
135    }
136
137    // Ensure the launch path is a valid relative URL.
138    GURL resolved = extension->url().Resolve(launch_path);
139    if (!resolved.is_valid() || resolved.GetOrigin() != extension->url()) {
140      *error = ErrorUtils::FormatErrorMessageUTF16(
141          errors::kInvalidLaunchValue,
142          keys::kLaunchLocalPath);
143      return false;
144    }
145
146    launch_local_path_ = launch_path;
147  } else if (extension->manifest()->Get(keys::kLaunchWebURL, &temp)) {
148    std::string launch_url;
149    if (!temp->GetAsString(&launch_url)) {
150      *error = ErrorUtils::FormatErrorMessageUTF16(
151          errors::kInvalidLaunchValue,
152          keys::kLaunchWebURL);
153      return false;
154    }
155
156    // Ensure the launch web URL is a valid absolute URL and web extent scheme.
157    GURL url(launch_url);
158    URLPattern pattern(Extension::kValidWebExtentSchemes);
159    if (!url.is_valid() || !pattern.SetScheme(url.scheme())) {
160      *error = ErrorUtils::FormatErrorMessageUTF16(
161          errors::kInvalidLaunchValue,
162          keys::kLaunchWebURL);
163      return false;
164    }
165
166    launch_web_url_ = url;
167  } else if (extension->is_legacy_packaged_app()) {
168    *error = ASCIIToUTF16(errors::kLaunchURLRequired);
169    return false;
170  }
171
172  // For the Chrome component app, override launch url to new tab.
173  if (extension->id() == extension_misc::kChromeAppId) {
174    launch_web_url_ = GURL(chrome::kChromeUINewTabURL);
175    return true;
176  }
177
178  // If there is no extent, we default the extent based on the launch URL.
179  if (extension->web_extent().is_empty() && !launch_web_url_.is_empty()) {
180    URLPattern pattern(Extension::kValidWebExtentSchemes);
181    if (!pattern.SetScheme("*")) {
182      *error = ErrorUtils::FormatErrorMessageUTF16(
183          errors::kInvalidLaunchValue,
184          keys::kLaunchWebURL);
185      return false;
186    }
187    pattern.SetHost(launch_web_url_.host());
188    pattern.SetPath("/*");
189    extension->AddWebExtentPattern(pattern);
190  }
191
192  // In order for the --apps-gallery-url switch to work with the gallery
193  // process isolation, we must insert any provided value into the component
194  // app's launch url and web extent.
195  if (extension->id() == extension_misc::kWebStoreAppId) {
196    std::string gallery_url_str = CommandLine::ForCurrentProcess()->
197        GetSwitchValueASCII(switches::kAppsGalleryURL);
198
199    // Empty string means option was not used.
200    if (!gallery_url_str.empty()) {
201      GURL gallery_url(gallery_url_str);
202      OverrideLaunchURL(extension, gallery_url);
203    }
204  } else if (extension->id() == extension_misc::kCloudPrintAppId) {
205    // In order for the --cloud-print-service switch to work, we must update
206    // the launch URL and web extent.
207    // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is
208    // currently under chrome/browser.
209    const CommandLine& command_line = *CommandLine::ForCurrentProcess();
210    GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII(
211        switches::kCloudPrintServiceURL));
212    if (!cloud_print_service_url.is_empty()) {
213      std::string path(
214          cloud_print_service_url.path() + "/enable_chrome_connector");
215      GURL::Replacements replacements;
216      replacements.SetPathStr(path);
217      GURL cloud_print_enable_connector_url =
218          cloud_print_service_url.ReplaceComponents(replacements);
219      OverrideLaunchURL(extension, cloud_print_enable_connector_url);
220    }
221  }
222
223  return true;
224}
225
226bool AppLaunchInfo::LoadLaunchContainer(Extension* extension,
227                                        string16* error) {
228  const Value* tmp_launcher_container = NULL;
229  if (!extension->manifest()->Get(keys::kLaunchContainer,
230                                  &tmp_launcher_container))
231    return true;
232
233  std::string launch_container_string;
234  if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
235    *error = ASCIIToUTF16(errors::kInvalidLaunchContainer);
236    return false;
237  }
238
239  if (launch_container_string == values::kLaunchContainerPanel) {
240    launch_container_ = extension_misc::LAUNCH_PANEL;
241  } else if (launch_container_string == values::kLaunchContainerTab) {
242    launch_container_ = extension_misc::LAUNCH_TAB;
243  } else {
244    *error = ASCIIToUTF16(errors::kInvalidLaunchContainer);
245    return false;
246  }
247
248  bool can_specify_initial_size =
249      launch_container_ == extension_misc::LAUNCH_PANEL;
250
251  // Validate the container width if present.
252  if (!ReadLaunchDimension(extension->manifest(),
253                           keys::kLaunchWidth,
254                           &launch_width_,
255                           can_specify_initial_size,
256                           error)) {
257    return false;
258  }
259
260  // Validate container height if present.
261  if (!ReadLaunchDimension(extension->manifest(),
262                           keys::kLaunchHeight,
263                           &launch_height_,
264                           can_specify_initial_size,
265                           error)) {
266    return false;
267  }
268
269  return true;
270}
271
272void AppLaunchInfo::OverrideLaunchURL(Extension* extension,
273                                      GURL override_url) {
274  if (!override_url.is_valid()) {
275    DLOG(WARNING) << "Invalid override url given for " << extension->name();
276    return;
277  }
278  if (override_url.has_port()) {
279    DLOG(WARNING) << "Override URL passed for " << extension->name()
280                  << " should not contain a port.  Removing it.";
281
282    GURL::Replacements remove_port;
283    remove_port.ClearPort();
284    override_url = override_url.ReplaceComponents(remove_port);
285  }
286
287  launch_web_url_ = override_url;
288
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);
294}
295
296AppLaunchManifestHandler::AppLaunchManifestHandler() {
297}
298
299AppLaunchManifestHandler::~AppLaunchManifestHandler() {
300}
301
302bool AppLaunchManifestHandler::Parse(Extension* extension, 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;
308}
309
310bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type) const {
311  return type == Manifest::TYPE_LEGACY_PACKAGED_APP;
312}
313
314const std::vector<std::string> AppLaunchManifestHandler::Keys() const {
315  static const char* keys[] = {
316    keys::kLaunchLocalPath,
317    keys::kLaunchWebURL,
318    keys::kLaunchContainer,
319    keys::kLaunchHeight,
320    keys::kLaunchWidth
321  };
322  return std::vector<std::string>(keys, keys + arraysize(keys));
323}
324
325}  // namespace extensions
326