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