8a03083da6bd540c6a245472a0fc993641a92e0e
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / application_data.cc
1 // Copyright (c) 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 "xwalk/application/common/application_data.h"
6
7 #include "base/base64.h"
8 #include "base/basictypes.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/i18n/rtl.h"
13 #include "base/logging.h"
14 #include "base/memory/singleton.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string16.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/values.h"
23 #include "base/version.h"
24 #include "xwalk/application/common/application_manifest_constants.h"
25 #include "xwalk/application/common/id_util.h"
26 #include "xwalk/application/common/constants.h"
27 #include "xwalk/application/common/manifest.h"
28 #include "xwalk/application/common/manifest_handler.h"
29 #include "xwalk/application/common/manifest_handlers/permissions_handler.h"
30 #include "xwalk/application/common/manifest_handlers/widget_handler.h"
31 #include "xwalk/application/common/manifest_handlers/tizen_application_handler.h"
32 #include "xwalk/application/common/permission_policy_manager.h"
33 #include "content/public/common/url_constants.h"
34 #include "url/url_util.h"
35 #include "ui/base/l10n/l10n_util.h"
36
37 namespace keys = xwalk::application_manifest_keys;
38 namespace widget_keys = xwalk::application_widget_keys;
39 namespace errors = xwalk::application_manifest_errors;
40
41 namespace xwalk {
42 namespace application {
43
44 // static
45 scoped_refptr<ApplicationData> ApplicationData::Create(
46     const base::FilePath& path,
47     Manifest::SourceType source_type,
48     const base::DictionaryValue& manifest_data,
49     const std::string& explicit_id,
50     std::string* error_message) {
51   DCHECK(error_message);
52   base::string16 error;
53   scoped_ptr<xwalk::application::Manifest> manifest(
54       new xwalk::application::Manifest(source_type,
55                  scoped_ptr<base::DictionaryValue>(manifest_data.DeepCopy())));
56
57   std::vector<InstallWarning> install_warnings;
58   if (!manifest->ValidateManifest(error_message, &install_warnings)) {
59     return NULL;
60   }
61
62   scoped_refptr<ApplicationData> application = new ApplicationData(path,
63                                                            manifest.Pass());
64   application->install_warnings_.swap(install_warnings);
65
66   if (!application->Init(explicit_id, &error)) {
67     *error_message = base::UTF16ToUTF8(error);
68     return NULL;
69   }
70
71   return application;
72 }
73
74 // static
75 scoped_refptr<ApplicationData> ApplicationData::Create(
76     const GURL& url,
77     std::string* error_message) {
78   const std::string& url_spec = url.spec();
79   DCHECK(!url_spec.empty());
80   const std::string& app_id = GenerateId(url_spec);
81
82   base::DictionaryValue manifest;
83   // FIXME: define permissions!
84   manifest.SetString(application_manifest_keys::kStartURLKey, url_spec);
85   // FIXME: Why use URL as name?
86   manifest.SetString(application_manifest_keys::kNameKey, url_spec);
87   manifest.SetString(application_manifest_keys::kXWalkVersionKey, "0");
88   scoped_refptr<ApplicationData> application_data =
89       ApplicationData::Create(base::FilePath(), Manifest::COMMAND_LINE,
90                               manifest, app_id, error_message);
91
92   return application_data;
93 }
94
95 // static
96 GURL ApplicationData::GetBaseURLFromApplicationId(
97     const std::string& application_id) {
98   return GURL(std::string(xwalk::application::kApplicationScheme) +
99               url::kStandardSchemeSeparator + application_id + "/");
100 }
101
102 ApplicationData::ManifestData* ApplicationData::GetManifestData(
103         const std::string& key) const {
104   DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
105   ManifestDataMap::const_iterator iter = manifest_data_.find(key);
106   if (iter != manifest_data_.end())
107     return iter->second.get();
108   return NULL;
109 }
110
111 void ApplicationData::SetManifestData(const std::string& key,
112                                       ApplicationData::ManifestData* data) {
113   DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
114   manifest_data_[key] = linked_ptr<ManifestData>(data);
115 }
116
117 Manifest::SourceType ApplicationData::GetSourceType() const {
118   return manifest_->GetSourceType();
119 }
120
121 const std::string& ApplicationData::ID() const {
122   return manifest_->GetApplicationID();
123 }
124
125 const std::string ApplicationData::VersionString() const {
126   if (!version_->components().empty())
127     return Version()->GetString();
128
129   return "";
130 }
131
132 bool ApplicationData::IsPlatformApp() const {
133   return manifest_->IsPackaged();
134 }
135
136 bool ApplicationData::IsHostedApp() const {
137   return GetManifest()->IsHosted();
138 }
139
140 ApplicationData::ApplicationData(const base::FilePath& path,
141                      scoped_ptr<xwalk::application::Manifest> manifest)
142     : manifest_version_(0),
143       manifest_(manifest.release()),
144       finished_parsing_manifest_(false) {
145   DCHECK(path.empty() || path.IsAbsolute());
146   path_ = path;
147   if (manifest_->HasPath(widget_keys::kWidgetKey))
148     package_type_ = Package::WGT;
149   else
150     package_type_ = Package::XPK;
151 }
152
153 ApplicationData::~ApplicationData() {
154 }
155
156 // static
157 GURL ApplicationData::GetResourceURL(const GURL& application_url,
158                                const std::string& relative_path) {
159   DCHECK(application_url.SchemeIs(xwalk::application::kApplicationScheme));
160   DCHECK_EQ("/", application_url.path());
161
162   std::string path = relative_path;
163
164   // If the relative path starts with "/", it is "absolute" relative to the
165   // application base directory, but application_url is already specified to
166   // refer to that base directory, so strip the leading "/" if present.
167   if (relative_path.size() > 0 && relative_path[0] == '/')
168     path = relative_path.substr(1);
169
170   GURL ret_val = GURL(application_url.spec() + path);
171   DCHECK(StartsWithASCII(ret_val.spec(), application_url.spec(), false));
172
173   return ret_val;
174 }
175
176 Manifest::Type ApplicationData::GetType() const {
177   return manifest_->GetType();
178 }
179
180 bool ApplicationData::Init(const std::string& explicit_id,
181                            base::string16* error) {
182   DCHECK(error);
183   ManifestHandlerRegistry* registry =
184       ManifestHandlerRegistry::GetInstance(GetPackageType());
185   if (!registry->ParseAppManifest(this, error))
186     return false;
187
188   if (!LoadID(explicit_id, error))
189     return false;
190   if (!LoadName(error))
191     return false;
192   if (!LoadVersion(error))
193     return false;
194   if (!LoadDescription(error))
195     return false;
196
197   application_url_ = ApplicationData::GetBaseURLFromApplicationId(ID());
198
199   finished_parsing_manifest_ = true;
200   return true;
201 }
202
203 bool ApplicationData::LoadID(const std::string& explicit_id,
204                              base::string16* error) {
205   std::string application_id;
206 #if defined(OS_TIZEN)
207   if (GetPackageType() == Package::WGT) {
208     const TizenApplicationInfo* tizen_app_info =
209         static_cast<TizenApplicationInfo*>(GetManifestData(
210             widget_keys::kTizenApplicationKey));
211     CHECK(tizen_app_info);
212     application_id = tizen_app_info->id();
213   } else if (manifest_->HasKey(keys::kTizenAppIdKey)) {
214     if (!manifest_->GetString(keys::kTizenAppIdKey, &application_id)) {
215       NOTREACHED() << "Could not get Tizen application key";
216       return false;
217     }
218   }
219
220   if (!application_id.empty()) {
221     manifest_->SetApplicationID(application_id);
222     return true;
223   }
224 #endif
225
226   if (!explicit_id.empty()) {
227     manifest_->SetApplicationID(explicit_id);
228     return true;
229   }
230
231   application_id = GenerateIdForPath(path_);
232   if (application_id.empty()) {
233     NOTREACHED() << "Could not create ID from path.";
234     return false;
235   }
236   manifest_->SetApplicationID(application_id);
237   return true;
238 }
239
240 bool ApplicationData::LoadName(base::string16* error) {
241   DCHECK(error);
242   base::string16 localized_name;
243   std::string name_key(GetNameKey(GetPackageType()));
244
245   if (!manifest_->GetString(name_key, &localized_name) &&
246       package_type_ == Package::XPK) {
247     *error = base::ASCIIToUTF16(errors::kInvalidName);
248     return false;
249   }
250   non_localized_name_ = base::UTF16ToUTF8(localized_name);
251   base::i18n::AdjustStringForLocaleDirection(&localized_name);
252   name_ = base::UTF16ToUTF8(localized_name);
253   return true;
254 }
255
256 bool ApplicationData::LoadVersion(base::string16* error) {
257   DCHECK(error);
258   std::string version_str;
259
260   version_.reset(new base::Version());
261
262   if (package_type_ == Package::WGT) {
263     bool ok = manifest_->GetString(widget_keys::kVersionKey, &version_str);
264     if (!ok) {
265       *error = base::ASCIIToUTF16(errors::kInvalidVersion);
266       return false;
267     }
268
269     version_.reset(new base::Version(version_str));
270     return true;
271   }
272
273   // W3C Manifest (XPK and hosted):
274
275   bool hasDeprecatedKey = manifest_->HasKey(keys::kDeprecatedVersionKey);
276   bool hasKey = manifest_->HasKey(keys::kXWalkVersionKey);
277
278   if (!hasKey && !hasDeprecatedKey) {
279     // xwalk_version is optional.
280     return true;
281   }
282
283   bool ok = false;
284   if (hasKey) {
285     if (hasDeprecatedKey) {
286       LOG(WARNING) << "Deprecated key '" << keys::kDeprecatedVersionKey
287           << "' found in addition to '" << keys::kXWalkVersionKey
288           << "'. Consider removing.";
289     }
290     ok = manifest_->GetString(keys::kXWalkVersionKey, &version_str);
291   }
292
293   if (!hasKey && hasDeprecatedKey) {
294     LOG(WARNING) << "Deprecated key '" << keys::kDeprecatedVersionKey
295         << "' found. Please migrate to using '" << keys::kXWalkVersionKey
296         << "' instead.";
297     ok = manifest_->GetString(keys::kDeprecatedVersionKey, &version_str);
298   }
299
300   version_.reset(new base::Version(version_str));
301
302   if (!ok || !version_->IsValid() || version_->components().size() > 4) {
303     *error = base::ASCIIToUTF16(errors::kInvalidVersion);
304     version_.reset(new base::Version());
305     return false;
306   }
307
308   return ok;
309 }
310
311 bool ApplicationData::LoadDescription(base::string16* error) {
312   DCHECK(error);
313   // FIXME: Better to assert on use from Widget.
314   if (package_type_ != Package::XPK)
315     return true;  // No error.
316
317   bool hasDeprecatedKey = manifest_->HasKey(keys::kDeprecatedDescriptionKey);
318   bool hasKey = manifest_->HasKey(keys::kXWalkDescriptionKey);
319
320   if (hasKey) {
321     if (hasDeprecatedKey) {
322       LOG(WARNING) << "Deprecated key '" << keys::kDeprecatedDescriptionKey
323           << "' found in addition to '" << keys::kXWalkDescriptionKey
324           << "'. Consider removing.";
325     }
326     bool ok = manifest_->GetString(keys::kXWalkDescriptionKey, &description_);
327     if (!ok)
328       *error = base::ASCIIToUTF16(errors::kInvalidDescription);
329     return ok;
330   }
331
332   if (hasDeprecatedKey) {
333     LOG(WARNING) << "Deprecated key '" << keys::kDeprecatedDescriptionKey
334         << "' found. Please migrate to using '" << keys::kXWalkDescriptionKey
335         << "' instead.";
336     bool ok = manifest_->GetString(
337         keys::kDeprecatedDescriptionKey, &description_);
338     if (!ok)
339       *error = base::ASCIIToUTF16(errors::kInvalidDescription);
340     return ok;
341   }
342
343   // No error but also no description found.
344   return true;
345 }
346
347 StoredPermission ApplicationData::GetPermission(
348     const std::string& permission_name) const {
349   StoredPermissionMap::const_iterator iter =
350       permission_map_.find(permission_name);
351   if (iter == permission_map_.end())
352     return UNDEFINED_STORED_PERM;
353   return iter->second;
354 }
355
356 bool ApplicationData::SetPermission(const std::string& permission_name,
357                                     StoredPermission perm) {
358   if (perm != UNDEFINED_STORED_PERM) {
359     permission_map_[permission_name] = perm;
360     return true;
361   }
362   return false;
363 }
364
365 void ApplicationData::ClearPermissions() {
366   permission_map_.clear();
367 }
368
369 PermissionSet ApplicationData::GetManifestPermissions() const {
370   PermissionSet permissions;
371   if (manifest_->value()->HasKey(keys::kPermissionsKey)) {
372     const PermissionsInfo* perm_info = static_cast<PermissionsInfo*>(
373                            GetManifestData(keys::kPermissionsKey));
374     permissions = perm_info->GetAPIPermissions();
375   }
376   return permissions;
377 }
378
379 bool ApplicationData::HasCSPDefined() const {
380 #if defined(OS_TIZEN)
381   return  manifest_->HasPath(GetCSPKey(package_type_)) ||
382           manifest_->HasPath(widget_keys::kCSPReportOnlyKey) ||
383           manifest_->HasPath(widget_keys::kAllowNavigationKey);
384 #else
385   return manifest_->HasPath(GetCSPKey(package_type_));
386 #endif
387 }
388
389 bool ApplicationData::SetApplicationLocale(const std::string& locale,
390                                            base::string16* error) {
391   DCHECK(thread_checker_.CalledOnValidThread());
392   manifest_->SetSystemLocale(locale);
393   if (!LoadName(error))
394     return false;
395   if (!LoadDescription(error))
396     return false;
397
398   // Only update when the package is wgt and we have parsed the widget handler,
399   // otherwise we can not get widget_info.
400   if (WidgetInfo* widget_info = static_cast<WidgetInfo*>(
401           GetManifestData(widget_keys::kWidgetKey))) {
402     std::string string_value;
403     if (manifest_->GetString(widget_keys::kNameKey, &string_value))
404       widget_info->SetName(string_value);
405     if (manifest_->GetString(widget_keys::kShortNameKey, &string_value))
406       widget_info->SetShortName(string_value);
407     if (manifest_->GetString(widget_keys::kDescriptionKey, &string_value))
408       widget_info->SetDescription(string_value);
409   }
410   return true;
411 }
412
413 }   // namespace application
414 }   // namespace xwalk