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