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