- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / manifest_handlers / app_launch_info.cc
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/url_constants.h"
14 #include "extensions/common/error_utils.h"
15 #include "extensions/common/manifest_constants.h"
16
17 namespace extensions {
18
19 namespace keys = manifest_keys;
20 namespace values = manifest_values;
21 namespace errors = manifest_errors;
22
23 namespace {
24
25 bool 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
49 static base::LazyInstance<AppLaunchInfo> g_empty_app_launch_info =
50     LAZY_INSTANCE_INITIALIZER;
51
52 const 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
60 AppLaunchInfo::AppLaunchInfo()
61     : launch_container_(extension_misc::LAUNCH_TAB),
62       launch_width_(0),
63       launch_height_(0) {
64 }
65
66 AppLaunchInfo::~AppLaunchInfo() {
67 }
68
69 // static
70 const std::string& AppLaunchInfo::GetLaunchLocalPath(
71     const Extension* extension) {
72   return GetAppLaunchInfo(extension).launch_local_path_;
73 }
74
75 // static
76 const GURL& AppLaunchInfo::GetLaunchWebURL(
77     const Extension* extension) {
78   return GetAppLaunchInfo(extension).launch_web_url_;
79 }
80
81 // static
82 extension_misc::LaunchContainer AppLaunchInfo::GetLaunchContainer(
83     const Extension* extension) {
84   return GetAppLaunchInfo(extension).launch_container_;
85 }
86
87 // static
88 int AppLaunchInfo::GetLaunchWidth(const Extension* extension) {
89   return GetAppLaunchInfo(extension).launch_width_;
90 }
91
92 // static
93 int AppLaunchInfo::GetLaunchHeight(const Extension* extension) {
94   return GetAppLaunchInfo(extension).launch_height_;
95 }
96
97 // static
98 GURL 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
106 bool AppLaunchInfo::Parse(Extension* extension, string16* error) {
107   if (!LoadLaunchURL(extension, error) ||
108       !LoadLaunchContainer(extension, error))
109     return false;
110   return true;
111 }
112
113 bool 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
226 bool 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
272 void 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
296 AppLaunchManifestHandler::AppLaunchManifestHandler() {
297 }
298
299 AppLaunchManifestHandler::~AppLaunchManifestHandler() {
300 }
301
302 bool 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
310 bool AppLaunchManifestHandler::AlwaysParseForType(Manifest::Type type) const {
311   return type == Manifest::TYPE_LEGACY_PACKAGED_APP;
312 }
313
314 const 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