Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / application_file_util.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_file_util.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <vector>
10
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/file_util.h"
15 #include "base/i18n/rtl.h"
16 #include "base/json/json_file_value_serializer.h"
17 #include "base/logging.h"
18 #include "base/metrics/histogram.h"
19 #include "base/path_service.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "net/base/escape.h"
25 #include "net/base/file_stream.h"
26 #include "third_party/libxml/src/include/libxml/tree.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "xwalk/application/common/installer/package.h"
29 #include "xwalk/application/common/application_data.h"
30 #include "xwalk/application/common/application_manifest_constants.h"
31 #include "xwalk/application/common/constants.h"
32 #include "xwalk/application/common/manifest.h"
33 #include "xwalk/application/common/manifest_handler.h"
34
35 #if defined(OS_TIZEN)
36 #include "xwalk/application/common/id_util.h"
37 #endif
38
39 namespace errors = xwalk::application_manifest_errors;
40 namespace keys = xwalk::application_manifest_keys;
41 namespace widget_keys = xwalk::application_widget_keys;
42
43 namespace {
44 const char kAttributePrefix[] = "@";
45 const char kNamespaceKey[] = "@namespace";
46 const char kTextKey[] = "#text";
47
48 const char kContentKey[] = "content";
49
50 const xmlChar kWidgetNodeKey[] = "widget";
51 const xmlChar kNameNodeKey[] = "name";
52 const xmlChar kDescriptionNodeKey[] = "description";
53 const xmlChar kAuthorNodeKey[] = "author";
54 const xmlChar kLicenseNodeKey[] = "license";
55 const xmlChar kVersionAttributeKey[] = "version";
56 const xmlChar kShortAttributeKey[] = "short";
57 const xmlChar kDirAttributeKey[] = "dir";
58
59 const char kDirLTRKey[] = "ltr";
60 const char kDirRTLKey[] = "rtl";
61 const char kDirLROKey[] = "lro";
62 const char kDirRLOKey[] = "rlo";
63
64 const char* kSingletonElements[] = {
65   "allow-navigation",
66   "content-security-policy-report-only",
67   "content-security-policy",
68   "content"
69 };
70
71 inline char* ToCharPointer(void* ptr) {
72   return reinterpret_cast<char *>(ptr);
73 }
74
75 inline const char* ToConstCharPointer(const void* ptr) {
76   return reinterpret_cast<const char*>(ptr);
77 }
78
79 base::string16 ToSting16(const xmlChar* string_ptr) {
80   return base::UTF8ToUTF16(std::string(ToConstCharPointer(string_ptr)));
81 }
82
83 base::string16 GetDirText(const base::string16& text, const std::string& dir) {
84   if (dir == kDirLTRKey)
85     return base::i18n::kLeftToRightEmbeddingMark
86            + text
87            + base::i18n::kPopDirectionalFormatting;
88
89   if (dir == kDirRTLKey)
90     return base::i18n::kRightToLeftEmbeddingMark
91            + text
92            + base::i18n::kPopDirectionalFormatting;
93
94   if (dir == kDirLROKey)
95     return base::i18n::kLeftToRightOverride
96            + text
97            + base::i18n::kPopDirectionalFormatting;
98
99   if (dir == kDirRLOKey)
100     return base::i18n::kRightToLeftOverride
101            + text
102            + base::i18n::kPopDirectionalFormatting;
103
104   return text;
105 }
106
107 std::string GetNodeDir(xmlNode* node, const std::string& inherit_dir) {
108   DCHECK(node);
109   std::string dir(inherit_dir);
110
111   xmlAttr* prop = NULL;
112   for (prop = node->properties; prop; prop = prop->next) {
113     if (xmlStrEqual(prop->name, kDirAttributeKey)) {
114       char* prop_value = ToCharPointer(xmlNodeListGetString(
115           node->doc, prop->children, 1));
116       dir = prop_value;
117       xmlFree(prop_value);
118       break;
119     }
120   }
121
122   return dir;
123 }
124
125 base::string16 GetNodeText(xmlNode* root, const std::string& inherit_dir) {
126   DCHECK(root);
127   if (root->type != XML_ELEMENT_NODE)
128     return base::string16();
129
130   std::string current_dir(GetNodeDir(root, inherit_dir));
131   base::string16 text;
132   for (xmlNode* node = root->children; node; node = node->next) {
133     if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
134       text = text + base::i18n::StripWrappingBidiControlCharacters(
135                         ToSting16(node->content));
136     } else {
137       text = text + GetNodeText(node, current_dir);
138     }
139   }
140   return GetDirText(text, current_dir);
141 }
142
143 // According to widget specification, this two prop need to support dir.
144 // see detail on http://www.w3.org/TR/widgets/#the-dir-attribute
145 inline bool IsPropSupportDir(xmlNode* root, xmlAttr* prop) {
146   if (xmlStrEqual(root->name, kWidgetNodeKey)
147      && xmlStrEqual(prop->name, kVersionAttributeKey))
148     return true;
149   if (xmlStrEqual(root->name, kNameNodeKey)
150      && xmlStrEqual(prop->name, kShortAttributeKey))
151     return true;
152   return false;
153 }
154
155 // Only this four items need to support span and ignore other element.
156 // Besides xmlNodeListGetString can not support dir prop of span.
157 // See http://www.w3.org/TR/widgets/#the-span-element-and-its-attributes
158 inline bool IsElementSupportSpanAndDir(xmlNode* root) {
159   if (xmlStrEqual(root->name, kNameNodeKey)
160      || xmlStrEqual(root->name, kDescriptionNodeKey)
161      || xmlStrEqual(root->name, kAuthorNodeKey)
162      || xmlStrEqual(root->name, kLicenseNodeKey))
163     return true;
164   return false;
165 }
166
167 // FIXME: This function is wrong and has to be re-implemented
168 // further (see XWALK-2230)
169 bool GetPackageType(const base::FilePath& path,
170                     xwalk::application::Package::Type* package_type,
171                     std::string* error) {
172   base::FilePath manifest_path;
173
174   manifest_path = path.Append(xwalk::application::kManifestXpkFilename);
175   if (base::PathExists(manifest_path)) {
176     *package_type = xwalk::application::Package::XPK;
177     return true;
178   }
179
180   manifest_path = path.Append(xwalk::application::kManifestWgtFilename);
181   if (base::PathExists(manifest_path)) {
182     *package_type = xwalk::application::Package::WGT;
183     return true;
184   }
185
186   *error = base::StringPrintf("%s", errors::kManifestUnreadable);
187   return false;
188 }
189
190 #if defined(OS_TIZEN)
191 bool GetPackageType(const std::string& application_id,
192                     xwalk::application::Package::Type* package_type,
193                     std::string* error) {
194   if (xwalk::application::IsValidWGTID(application_id)) {
195     *package_type = xwalk::application::Package::WGT;
196     return true;
197   } else if (xwalk::application::IsValidXPKID(application_id)) {
198     *package_type = xwalk::application::Package::XPK;
199     return true;
200   }
201
202   *error = base::StringPrintf("Invalid application id: %s",
203                               application_id.c_str());
204   return false;
205 }
206 #endif
207
208 bool IsSingletonElement(const std::string& name) {
209   for (int i = 0; i < arraysize(kSingletonElements); ++i)
210     if (kSingletonElements[i] == name)
211 #if defined(OS_TIZEN)
212       // On Tizen platform, need to check namespace of 'content'
213       // element further, a content element with tizen namespace
214       // will replace the one with widget namespace.
215       return name != kContentKey;
216 #else
217       return true;
218 #endif
219   return false;
220 }
221
222 }  // namespace
223
224 namespace xwalk {
225 namespace application {
226
227 FileDeleter::FileDeleter(const base::FilePath& path, bool recursive)
228     : path_(path),
229       recursive_(recursive) {}
230
231 FileDeleter::~FileDeleter() {
232   base::DeleteFile(path_, recursive_);
233 }
234
235 // Load XML node into Dictionary structure.
236 // The keys for the XML node to Dictionary mapping are described below:
237 // XML                                 Dictionary
238 // <e></e>                             "e":{"#text": ""}
239 // <e>textA</e>                        "e":{"#text":"textA"}
240 // <e attr="val">textA</e>             "e":{ "@attr":"val", "#text": "textA"}
241 // <e> <a>textA</a> <b>textB</b> </e>  "e":{
242 //                                       "a":{"#text":"textA"}
243 //                                       "b":{"#text":"textB"}
244 //                                     }
245 // <e> <a>textX</a> <a>textY</a> </e>  "e":{
246 //                                       "a":[ {"#text":"textX"},
247 //                                             {"#text":"textY"}]
248 //                                     }
249 // <e> textX <a>textY</a> </e>         "e":{ "#text":"textX",
250 //                                           "a":{"#text":"textY"}
251 //                                     }
252 //
253 // For elements that are specified under a namespace, the dictionary
254 // will add '@namespace' key for them, e.g.,
255 // XML:
256 // <e xmln="linkA" xmlns:N="LinkB">
257 //   <sub-e1> text1 </sub-e>
258 //   <N:sub-e2 text2 />
259 // </e>
260 // will be saved in Dictionary as,
261 // "e":{
262 //   "#text": "",
263 //   "@namespace": "linkA"
264 //   "sub-e1": {
265 //     "#text": "text1",
266 //     "@namespace": "linkA"
267 //   },
268 //   "sub-e2": {
269 //     "#text":"text2"
270 //     "@namespace": "linkB"
271 //   }
272 // }
273 base::DictionaryValue* LoadXMLNode(
274     xmlNode* root, const std::string& inherit_dir = "") {
275   scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
276   if (root->type != XML_ELEMENT_NODE)
277     return NULL;
278
279   std::string current_dir(GetNodeDir(root, inherit_dir));
280
281   xmlAttr* prop = NULL;
282   for (prop = root->properties; prop; prop = prop->next) {
283     xmlChar* value_ptr = xmlNodeListGetString(root->doc, prop->children, 1);
284     base::string16 prop_value(ToSting16(value_ptr));
285     xmlFree(value_ptr);
286
287     if (IsPropSupportDir(root, prop))
288       prop_value = GetDirText(prop_value, current_dir);
289
290     value->SetString(
291         std::string(kAttributePrefix) + ToConstCharPointer(prop->name),
292         prop_value);
293   }
294
295   if (root->ns)
296     value->SetString(kNamespaceKey, ToConstCharPointer(root->ns->href));
297
298   for (xmlNode* node = root->children; node; node = node->next) {
299     std::string sub_node_name(ToConstCharPointer(node->name));
300     base::DictionaryValue* sub_value = LoadXMLNode(node, current_dir);
301     if (!sub_value)
302       continue;
303
304     if (!value->HasKey(sub_node_name)) {
305       value->Set(sub_node_name, sub_value);
306       continue;
307     } else if (IsSingletonElement(sub_node_name)) {
308       continue;
309 #if defined(OS_TIZEN)
310     } else if (sub_node_name == kContentKey) {
311       std::string current_namespace, new_namespace;
312       base::DictionaryValue* current_value;
313       value->GetDictionary(sub_node_name, &current_value);
314
315       current_value->GetString(kNamespaceKey, &current_namespace);
316       sub_value->GetString(kNamespaceKey, &new_namespace);
317       if (current_namespace != new_namespace &&
318           new_namespace == kTizenNamespacePrefix)
319         value->Set(sub_node_name, sub_value);
320       continue;
321 #endif
322     }
323
324     base::Value* temp;
325     value->Get(sub_node_name, &temp);
326     DCHECK(temp);
327
328     if (temp->IsType(base::Value::TYPE_LIST)) {
329       base::ListValue* list;
330       temp->GetAsList(&list);
331       list->Append(sub_value);
332     } else {
333       DCHECK(temp->IsType(base::Value::TYPE_DICTIONARY));
334       base::DictionaryValue* dict;
335       temp->GetAsDictionary(&dict);
336       base::DictionaryValue* prev_value(new base::DictionaryValue());
337       prev_value = dict->DeepCopy();
338
339       base::ListValue* list = new base::ListValue();
340       list->Append(prev_value);
341       list->Append(sub_value);
342       value->Set(sub_node_name, list);
343     }
344   }
345
346   base::string16 text;
347   if (IsElementSupportSpanAndDir(root)) {
348     text = GetNodeText(root, current_dir);
349   } else {
350     xmlChar* text_ptr = xmlNodeListGetString(root->doc, root->children, 1);
351     if (text_ptr) {
352       text = ToSting16(text_ptr);
353       xmlFree(text_ptr);
354     }
355   }
356
357   if (!text.empty())
358     value->SetString(kTextKey, text);
359
360   return value.release();
361 }
362
363 scoped_refptr<ApplicationData> LoadApplication(
364     const base::FilePath& application_path,
365     Manifest::SourceType source_type,
366     std::string* error) {
367   Package::Type package_type;
368   if (!GetPackageType(application_path, &package_type, error))
369     return NULL;
370
371   return LoadApplication(application_path, std::string(),
372                          source_type, package_type, error);
373 }
374
375 scoped_refptr<ApplicationData> LoadApplication(
376     const base::FilePath& application_path,
377     const std::string& application_id,
378     Manifest::SourceType source_type,
379     std::string* error) {
380   Package::Type package_type;
381 #if defined(OS_TIZEN)
382   if (!GetPackageType(application_id, &package_type, error))
383 #else
384   if (!GetPackageType(application_path, &package_type, error))
385 #endif
386     return NULL;
387
388   return LoadApplication(application_path, application_id,
389                          source_type, package_type, error);
390 }
391
392 scoped_refptr<ApplicationData> LoadApplication(
393     const base::FilePath& application_path,
394     const std::string& application_id,
395     Manifest::SourceType source_type,
396     Package::Type package_type,
397     std::string* error) {
398   scoped_ptr<base::DictionaryValue> manifest(
399       LoadManifest(application_path, package_type, error));
400   if (!manifest.get())
401     return NULL;
402
403   scoped_refptr<ApplicationData> application = ApplicationData::Create(
404                                                              application_path,
405                                                              source_type,
406                                                              *manifest,
407                                                              application_id,
408                                                              error);
409   if (!application)
410     return NULL;
411
412   ManifestHandlerRegistry* registry =
413       manifest->HasKey(widget_keys::kWidgetKey)
414       ? ManifestHandlerRegistry::GetInstance(Package::WGT)
415       : ManifestHandlerRegistry::GetInstance(Package::XPK);
416
417   if (!registry->ValidateAppManifest(application, error))
418     return NULL;
419
420   return application;
421 }
422
423 static base::DictionaryValue* LoadManifestXpk(
424     const base::FilePath& manifest_path,
425     std::string* error) {
426   JSONFileValueSerializer serializer(manifest_path);
427   scoped_ptr<base::Value> root(serializer.Deserialize(NULL, error));
428   if (!root.get()) {
429     if (error->empty()) {
430       // If |error| is empty, than the file could not be read.
431       // It would be cleaner to have the JSON reader give a specific error
432       // in this case, but other code tests for a file error with
433       // error->empty().  For now, be consistent.
434       *error = base::StringPrintf("%s", errors::kManifestUnreadable);
435     } else {
436       *error = base::StringPrintf("%s  %s",
437                                   errors::kManifestParseError,
438                                   error->c_str());
439     }
440     return NULL;
441   }
442
443   if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
444     *error = base::StringPrintf("%s", errors::kManifestUnreadable);
445     return NULL;
446   }
447
448   base::DictionaryValue* dv =
449       static_cast<base::DictionaryValue*>(root.release());
450 #if defined(OS_TIZEN)
451   // Ignore any Tizen application ID, as this is automatically generated.
452   dv->Remove(keys::kTizenAppIdKey, NULL);
453 #endif
454
455   return dv;
456 }
457
458 static base::DictionaryValue* LoadManifestWgt(
459     const base::FilePath& manifest_path,
460     std::string* error) {
461   xmlDoc * doc = NULL;
462   xmlNode* root_node = NULL;
463   doc = xmlReadFile(manifest_path.MaybeAsASCII().c_str(), NULL, 0);
464   if (doc == NULL) {
465     *error = base::StringPrintf("%s", errors::kManifestUnreadable);
466     return NULL;
467   }
468   root_node = xmlDocGetRootElement(doc);
469   base::DictionaryValue* dv = LoadXMLNode(root_node);
470   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
471   if (dv)
472     result->Set(ToConstCharPointer(root_node->name), dv);
473
474   return result.release();
475 }
476
477 base::DictionaryValue* LoadManifest(const base::FilePath& application_path,
478                                     Package::Type package_type,
479                                     std::string* error) {
480   base::FilePath manifest_path;
481
482   manifest_path = application_path.Append(kManifestXpkFilename);
483   if (package_type == Package::XPK)
484     return LoadManifestXpk(manifest_path, error);
485
486   manifest_path = application_path.Append(kManifestWgtFilename);
487   if (package_type == Package::WGT)
488     return LoadManifestWgt(manifest_path, error);
489
490   *error = base::StringPrintf("%s", errors::kManifestUnreadable);
491   return NULL;
492 }
493
494 base::FilePath ApplicationURLToRelativeFilePath(const GURL& url) {
495   std::string url_path = url.path();
496   if (url_path.empty() || url_path[0] != '/')
497     return base::FilePath();
498
499   // Drop the leading slashes and convert %-encoded UTF8 to regular UTF8.
500   std::string file_path = net::UnescapeURLComponent(url_path,
501       net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS);
502   size_t skip = file_path.find_first_not_of("/\\");
503   if (skip != file_path.npos)
504     file_path = file_path.substr(skip);
505
506   base::FilePath path =
507 #if defined(OS_POSIX)
508     base::FilePath(file_path);
509 #elif defined(OS_WIN)
510     base::FilePath(base::UTF8ToWide(file_path));
511 #else
512     base::FilePath();
513     NOTIMPLEMENTED();
514 #endif
515
516   // It's still possible for someone to construct an annoying URL whose path
517   // would still wind up not being considered relative at this point.
518   // For example: app://id/c:////foo.html
519   if (path.IsAbsolute())
520     return base::FilePath();
521
522   return path;
523 }
524
525 }  // namespace application
526 }  // namespace xwalk