Upstream version 5.34.92.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/json/json_file_value_serializer.h"
16 #include "base/logging.h"
17 #include "base/metrics/histogram.h"
18 #include "base/path_service.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "net/base/escape.h"
23 #include "net/base/file_stream.h"
24 #include "third_party/libxml/src/include/libxml/tree.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "xwalk/application/common/application_data.h"
27 #include "xwalk/application/common/application_manifest_constants.h"
28 #include "xwalk/application/common/constants.h"
29 #include "xwalk/application/common/install_warning.h"
30 #include "xwalk/application/common/manifest.h"
31 #include "xwalk/application/common/manifest_handler.h"
32
33 namespace errors = xwalk::application_manifest_errors;
34 namespace keys = xwalk::application_manifest_keys;
35 namespace widget_keys = xwalk::application_widget_keys;
36
37 namespace {
38 const char kAttributePrefix[] = "@";
39 const char kNamespaceKey[] = "@namespace";
40 const char kTextKey[] = "#text";
41 }
42
43 namespace xwalk {
44 namespace application {
45
46 inline char* ToCharPointer(void* ptr) {
47   return reinterpret_cast<char *>(ptr);
48 }
49
50 inline const char* ToConstCharPointer(const void* ptr) {
51   return reinterpret_cast<const char*>(ptr);
52 }
53
54 // Load XML node into Dictionary structure.
55 // The keys for the XML node to Dictionary mapping are described below:
56 // XML                                 Dictionary
57 // <e></e>                             "e":{"#text": ""}
58 // <e>textA</e>                        "e":{"#text":"textA"}
59 // <e attr="val">textA</e>             "e":{ "@attr":"val", "#text": "textA"}
60 // <e> <a>textA</a> <b>textB</b> </e>  "e":{
61 //                                       "a":{"#text":"textA"}
62 //                                       "b":{"#text":"textB"}
63 //                                     }
64 // <e> <a>textX</a> <a>textY</a> </e>  "e":{
65 //                                       "a":[ {"#text":"textX"},
66 //                                             {"#text":"textY"}]
67 //                                     }
68 // <e> textX <a>textY</a> </e>         "e":{ "#text":"textX",
69 //                                           "a":{"#text":"textY"}
70 //                                     }
71 //
72 // For elements that are specified under a namespace, the dictionary
73 // will add '@namespace' key for them, e.g.,
74 // XML:
75 // <e xmln="linkA" xmlns:N="LinkB">
76 //   <sub-e1> text1 </sub-e>
77 //   <N:sub-e2 text2 />
78 // </e>
79 // will be saved in Dictionary as,
80 // "e":{
81 //   "#text": "",
82 //   "@namespace": "linkA"
83 //   "sub-e1": {
84 //     "#text": "text1",
85 //     "@namespace": "linkA"
86 //   },
87 //   "sub-e2": {
88 //     "#text":"text2"
89 //     "@namespace": "linkB"
90 //   }
91 // }
92 base::DictionaryValue* LoadXMLNode(xmlNode* root) {
93   scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
94   if (root->type != XML_ELEMENT_NODE)
95     return NULL;
96
97   xmlAttr* prop = NULL;
98   for (prop = root->properties; prop; prop = prop->next) {
99     char* prop_value = ToCharPointer(xmlNodeListGetString(
100         root->doc, prop->children, 1));
101     value->SetString(
102         std::string(kAttributePrefix) + ToConstCharPointer(prop->name),
103         prop_value);
104     xmlFree(prop_value);
105   }
106
107   if (root->ns)
108     value->SetString(kNamespaceKey, ToConstCharPointer(root->ns->href));
109
110   for (xmlNode* node = root->children; node; node = node->next) {
111     std::string sub_node_name(ToConstCharPointer(node->name));
112     base::DictionaryValue* sub_value = LoadXMLNode(node);
113     if (!sub_value)
114       continue;
115
116     if (!value->HasKey(sub_node_name)) {
117       value->Set(sub_node_name, sub_value);
118       continue;
119     }
120
121     base::Value* temp;
122     value->Get(sub_node_name, &temp);
123     DCHECK(temp);
124
125     if (temp->IsType(base::Value::TYPE_LIST)) {
126       base::ListValue* list;
127       temp->GetAsList(&list);
128       list->Append(sub_value);
129       value->Set(sub_node_name, list);
130     } else {
131       DCHECK(temp->IsType(base::Value::TYPE_DICTIONARY));
132       base::DictionaryValue* dict;
133       temp->GetAsDictionary(&dict);
134       base::DictionaryValue* prev_value(new base::DictionaryValue());
135       prev_value = dict->DeepCopy();
136
137       base::ListValue* list = new base::ListValue();
138       list->Append(prev_value);
139       list->Append(sub_value);
140       value->Set(sub_node_name, list);
141     }
142   }
143
144   char* text = ToCharPointer(
145       xmlNodeListGetString(root->doc, root->children, 1));
146   if (!text) {
147     value->SetString(kTextKey, std::string());
148   } else {
149     value->SetString(kTextKey, text);
150   }
151   xmlFree(text);
152
153   return value.release();
154 }
155
156 scoped_refptr<ApplicationData> LoadApplication(
157     const base::FilePath& application_path,
158     Manifest::SourceType source_type,
159     std::string* error) {
160   return LoadApplication(application_path, std::string(),
161                          source_type, error);
162 }
163
164 scoped_refptr<ApplicationData> LoadApplication(
165     const base::FilePath& application_path,
166     const std::string& application_id,
167     Manifest::SourceType source_type,
168     std::string* error) {
169   scoped_ptr<base::DictionaryValue> manifest(
170       LoadManifest(application_path, error));
171   if (!manifest.get())
172     return NULL;
173
174   scoped_refptr<ApplicationData> application = ApplicationData::Create(
175                                                              application_path,
176                                                              source_type,
177                                                              *manifest,
178                                                              application_id,
179                                                              error);
180   if (!application.get())
181     return NULL;
182
183   std::vector<InstallWarning> warnings;
184   ManifestHandlerRegistry* registry =
185       manifest->HasKey(widget_keys::kWidgetKey)
186       ? ManifestHandlerRegistry::GetInstance(Manifest::TYPE_WGT)
187       : ManifestHandlerRegistry::GetInstance(Manifest::TYPE_XPK);
188
189   if (!registry->ValidateAppManifest(application, error, &warnings))
190     return NULL;
191
192   if (!warnings.empty()) {
193     LOG(WARNING) << "There are some warnings when validating the application "
194                  << application->ID();
195   }
196
197   return application;
198 }
199
200 static base::DictionaryValue* LoadManifestXpk(
201     const base::FilePath& manifest_path,
202     std::string* error) {
203   JSONFileValueSerializer serializer(manifest_path);
204   scoped_ptr<base::Value> root(serializer.Deserialize(NULL, error));
205   if (!root.get()) {
206     if (error->empty()) {
207       // If |error| is empty, than the file could not be read.
208       // It would be cleaner to have the JSON reader give a specific error
209       // in this case, but other code tests for a file error with
210       // error->empty().  For now, be consistent.
211       *error = base::StringPrintf("%s", errors::kManifestUnreadable);
212     } else {
213       *error = base::StringPrintf("%s  %s",
214                                   errors::kManifestParseError,
215                                   error->c_str());
216     }
217     return NULL;
218   }
219
220   if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
221     *error = base::StringPrintf("%s", errors::kManifestUnreadable);
222     return NULL;
223   }
224
225   base::DictionaryValue* dv =
226       static_cast<base::DictionaryValue*>(root.release());
227 #if defined(OS_TIZEN)
228   // Ignore any Tizen application ID, as this is automatically generated.
229   dv->Remove(keys::kTizenAppIdKey, NULL);
230 #endif
231
232   return dv;
233 }
234
235 static base::DictionaryValue* LoadManifestWgt(
236     const base::FilePath& manifest_path,
237     std::string* error) {
238   xmlDoc * doc = NULL;
239   xmlNode* root_node = NULL;
240   doc = xmlReadFile(manifest_path.MaybeAsASCII().c_str(), NULL, 0);
241   if (doc == NULL) {
242     *error = base::StringPrintf("%s", errors::kManifestUnreadable);
243     return NULL;
244   }
245   root_node = xmlDocGetRootElement(doc);
246   base::DictionaryValue* dv = LoadXMLNode(root_node);
247   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
248   if (dv)
249     result->Set(ToConstCharPointer(root_node->name), dv);
250
251   return result.release();
252 }
253
254 base::DictionaryValue* LoadManifest(const base::FilePath& application_path,
255       std::string* error) {
256   base::FilePath manifest_path;
257
258   manifest_path = application_path.Append(kManifestXpkFilename);
259   if (base::PathExists(manifest_path))
260     return LoadManifestXpk(manifest_path, error);
261
262   manifest_path = application_path.Append(kManifestWgtFilename);
263   if (base::PathExists(manifest_path))
264     return LoadManifestWgt(manifest_path, error);
265
266   *error = base::StringPrintf("%s", errors::kManifestUnreadable);
267   return NULL;
268 }
269
270 base::FilePath ApplicationURLToRelativeFilePath(const GURL& url) {
271   std::string url_path = url.path();
272   if (url_path.empty() || url_path[0] != '/')
273     return base::FilePath();
274
275   // Drop the leading slashes and convert %-encoded UTF8 to regular UTF8.
276   std::string file_path = net::UnescapeURLComponent(url_path,
277       net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS);
278   size_t skip = file_path.find_first_not_of("/\\");
279   if (skip != file_path.npos)
280     file_path = file_path.substr(skip);
281
282   base::FilePath path =
283 #if defined(OS_POSIX)
284     base::FilePath(file_path);
285 #elif defined(OS_WIN)
286     base::FilePath(base::UTF8ToWide(file_path));
287 #else
288     base::FilePath();
289     NOTIMPLEMENTED();
290 #endif
291
292   // It's still possible for someone to construct an annoying URL whose path
293   // would still wind up not being considered relative at this point.
294   // For example: app://id/c:////foo.html
295   if (path.IsAbsolute())
296     return base::FilePath();
297
298   return path;
299 }
300
301 }  // namespace application
302 }  // namespace xwalk