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.
5 #include "xwalk/application/common/application_file_util.h"
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"
33 namespace errors = xwalk::application_manifest_errors;
34 namespace keys = xwalk::application_manifest_keys;
35 namespace widget_keys = xwalk::application_widget_keys;
38 const char kAttributePrefix[] = "@";
39 const char kNamespaceKey[] = "@namespace";
40 const char kTextKey[] = "#text";
44 namespace application {
46 inline char* ToCharPointer(void* ptr) {
47 return reinterpret_cast<char *>(ptr);
50 inline const char* ToConstCharPointer(const void* ptr) {
51 return reinterpret_cast<const char*>(ptr);
54 // Load XML node into Dictionary structure.
55 // The keys for the XML node to Dictionary mapping are described below:
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"}
64 // <e> <a>textX</a> <a>textY</a> </e> "e":{
65 // "a":[ {"#text":"textX"},
68 // <e> textX <a>textY</a> </e> "e":{ "#text":"textX",
69 // "a":{"#text":"textY"}
72 // For elements that are specified under a namespace, the dictionary
73 // will add '@namespace' key for them, e.g.,
75 // <e xmln="linkA" xmlns:N="LinkB">
76 // <sub-e1> text1 </sub-e>
79 // will be saved in Dictionary as,
82 // "@namespace": "linkA"
85 // "@namespace": "linkA"
89 // "@namespace": "linkB"
92 base::DictionaryValue* LoadXMLNode(xmlNode* root) {
93 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
94 if (root->type != XML_ELEMENT_NODE)
98 for (prop = root->properties; prop; prop = prop->next) {
99 char* prop_value = ToCharPointer(xmlNodeListGetString(
100 root->doc, prop->children, 1));
102 std::string(kAttributePrefix) + ToConstCharPointer(prop->name),
108 value->SetString(kNamespaceKey, ToConstCharPointer(root->ns->href));
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);
116 if (!value->HasKey(sub_node_name)) {
117 value->Set(sub_node_name, sub_value);
122 value->Get(sub_node_name, &temp);
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);
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();
137 base::ListValue* list = new base::ListValue();
138 list->Append(prev_value);
139 list->Append(sub_value);
140 value->Set(sub_node_name, list);
144 char* text = ToCharPointer(
145 xmlNodeListGetString(root->doc, root->children, 1));
147 value->SetString(kTextKey, std::string());
149 value->SetString(kTextKey, text);
153 return value.release();
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(),
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));
174 scoped_refptr<ApplicationData> application = ApplicationData::Create(
180 if (!application.get())
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);
189 if (!registry->ValidateAppManifest(application, error, &warnings))
192 if (!warnings.empty()) {
193 LOG(WARNING) << "There are some warnings when validating the application "
194 << application->ID();
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));
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);
213 *error = base::StringPrintf("%s %s",
214 errors::kManifestParseError,
220 if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
221 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
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);
235 static base::DictionaryValue* LoadManifestWgt(
236 const base::FilePath& manifest_path,
237 std::string* error) {
239 xmlNode* root_node = NULL;
240 doc = xmlReadFile(manifest_path.MaybeAsASCII().c_str(), NULL, 0);
242 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
245 root_node = xmlDocGetRootElement(doc);
246 base::DictionaryValue* dv = LoadXMLNode(root_node);
247 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
249 result->Set(ToConstCharPointer(root_node->name), dv);
251 return result.release();
254 base::DictionaryValue* LoadManifest(const base::FilePath& application_path,
255 std::string* error) {
256 base::FilePath manifest_path;
258 manifest_path = application_path.Append(kManifestXpkFilename);
259 if (base::PathExists(manifest_path))
260 return LoadManifestXpk(manifest_path, error);
262 manifest_path = application_path.Append(kManifestWgtFilename);
263 if (base::PathExists(manifest_path))
264 return LoadManifestWgt(manifest_path, error);
266 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
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();
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);
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));
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();
301 } // namespace application