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