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