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/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"
36 namespace errors = xwalk::application_manifest_errors;
37 namespace keys = xwalk::application_manifest_keys;
38 namespace widget_keys = xwalk::application_widget_keys;
41 const char kAttributePrefix[] = "@";
42 const char kNamespaceKey[] = "@namespace";
43 const char kTextKey[] = "#text";
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";
54 const char kDirLTRKey[] = "ltr";
55 const char kDirRTLKey[] = "rtl";
56 const char kDirLROKey[] = "lro";
57 const char kDirRLOKey[] = "rlo";
59 inline char* ToCharPointer(void* ptr) {
60 return reinterpret_cast<char *>(ptr);
63 inline const char* ToConstCharPointer(const void* ptr) {
64 return reinterpret_cast<const char*>(ptr);
67 base::string16 ToSting16(const xmlChar* string_ptr) {
68 return base::UTF8ToUTF16(std::string(ToConstCharPointer(string_ptr)));
71 base::string16 GetDirText(const base::string16& text, const std::string& dir) {
72 if (dir == kDirLTRKey)
73 return base::i18n::kLeftToRightEmbeddingMark
75 + base::i18n::kPopDirectionalFormatting;
77 if (dir == kDirRTLKey)
78 return base::i18n::kRightToLeftEmbeddingMark
80 + base::i18n::kPopDirectionalFormatting;
82 if (dir == kDirLROKey)
83 return base::i18n::kLeftToRightOverride
85 + base::i18n::kPopDirectionalFormatting;
87 if (dir == kDirRLOKey)
88 return base::i18n::kRightToLeftOverride
90 + base::i18n::kPopDirectionalFormatting;
95 std::string GetNodeDir(xmlNode* node, const std::string& inherit_dir) {
97 std::string dir(inherit_dir);
100 for (prop = node->properties; prop; prop = prop->next) {
101 if (xmlStrEqual(prop->name, kDirAttributeKey)) {
102 char* prop_value = ToCharPointer(xmlNodeListGetString(
103 node->doc, prop->children, 1));
113 base::string16 GetNodeText(xmlNode* root, const std::string& inherit_dir) {
115 if (root->type != XML_ELEMENT_NODE)
116 return base::string16();
118 std::string current_dir(GetNodeDir(root, inherit_dir));
120 for (xmlNode* node = root->children; node; node = node->next) {
121 if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
122 text = text + base::i18n::StripWrappingBidiControlCharacters(
123 ToSting16(node->content));
125 text = text + GetNodeText(node, current_dir);
128 return GetDirText(text, current_dir);
131 // According to widget specification, this two prop need to support dir.
132 // see detail on http://www.w3.org/TR/widgets/#the-dir-attribute
133 inline bool IsPropSupportDir(xmlNode* root, xmlAttr* prop) {
134 if (xmlStrEqual(root->name, kWidgetNodeKey)
135 && xmlStrEqual(prop->name, kVersionAttributeKey))
137 if (xmlStrEqual(root->name, kNameNodeKey)
138 && xmlStrEqual(prop->name, kShortAttributeKey))
143 // Only this four items need to support span and ignore other element.
144 // Besides xmlNodeListGetString can not support dir prop of span.
145 // See http://www.w3.org/TR/widgets/#the-span-element-and-its-attributes
146 inline bool IsElementSupportSpanAndDir(xmlNode* root) {
147 if (xmlStrEqual(root->name, kNameNodeKey)
148 || xmlStrEqual(root->name, kDescriptionNodeKey)
149 || xmlStrEqual(root->name, kAuthorNodeKey)
150 || xmlStrEqual(root->name, kLicenseNodeKey))
155 bool GetPackageType(const base::FilePath& path,
156 xwalk::application::Package::Type* package_type,
157 std::string* error) {
158 base::FilePath manifest_path;
160 manifest_path = path.Append(xwalk::application::kManifestXpkFilename);
161 if (base::PathExists(manifest_path)) {
162 *package_type = xwalk::application::Package::XPK;
166 manifest_path = path.Append(xwalk::application::kManifestWgtFilename);
167 if (base::PathExists(manifest_path)) {
168 *package_type = xwalk::application::Package::WGT;
172 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
179 namespace application {
181 // Load XML node into Dictionary structure.
182 // The keys for the XML node to Dictionary mapping are described below:
184 // <e></e> "e":{"#text": ""}
185 // <e>textA</e> "e":{"#text":"textA"}
186 // <e attr="val">textA</e> "e":{ "@attr":"val", "#text": "textA"}
187 // <e> <a>textA</a> <b>textB</b> </e> "e":{
188 // "a":{"#text":"textA"}
189 // "b":{"#text":"textB"}
191 // <e> <a>textX</a> <a>textY</a> </e> "e":{
192 // "a":[ {"#text":"textX"},
193 // {"#text":"textY"}]
195 // <e> textX <a>textY</a> </e> "e":{ "#text":"textX",
196 // "a":{"#text":"textY"}
199 // For elements that are specified under a namespace, the dictionary
200 // will add '@namespace' key for them, e.g.,
202 // <e xmln="linkA" xmlns:N="LinkB">
203 // <sub-e1> text1 </sub-e>
204 // <N:sub-e2 text2 />
206 // will be saved in Dictionary as,
209 // "@namespace": "linkA"
212 // "@namespace": "linkA"
216 // "@namespace": "linkB"
219 base::DictionaryValue* LoadXMLNode(
220 xmlNode* root, const std::string& inherit_dir = "") {
221 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
222 if (root->type != XML_ELEMENT_NODE)
225 std::string current_dir(GetNodeDir(root, inherit_dir));
227 xmlAttr* prop = NULL;
228 for (prop = root->properties; prop; prop = prop->next) {
229 xmlChar* value_ptr = xmlNodeListGetString(root->doc, prop->children, 1);
230 base::string16 prop_value(ToSting16(value_ptr));
233 if (IsPropSupportDir(root, prop))
234 prop_value = GetDirText(prop_value, current_dir);
237 std::string(kAttributePrefix) + ToConstCharPointer(prop->name),
242 value->SetString(kNamespaceKey, ToConstCharPointer(root->ns->href));
244 for (xmlNode* node = root->children; node; node = node->next) {
245 std::string sub_node_name(ToConstCharPointer(node->name));
246 base::DictionaryValue* sub_value = LoadXMLNode(node, current_dir);
250 if (!value->HasKey(sub_node_name)) {
251 value->Set(sub_node_name, sub_value);
256 value->Get(sub_node_name, &temp);
259 if (temp->IsType(base::Value::TYPE_LIST)) {
260 base::ListValue* list;
261 temp->GetAsList(&list);
262 list->Append(sub_value);
264 DCHECK(temp->IsType(base::Value::TYPE_DICTIONARY));
265 base::DictionaryValue* dict;
266 temp->GetAsDictionary(&dict);
267 base::DictionaryValue* prev_value(new base::DictionaryValue());
268 prev_value = dict->DeepCopy();
270 base::ListValue* list = new base::ListValue();
271 list->Append(prev_value);
272 list->Append(sub_value);
273 value->Set(sub_node_name, list);
278 if (IsElementSupportSpanAndDir(root)) {
279 text = GetNodeText(root, current_dir);
281 xmlChar* text_ptr = xmlNodeListGetString(root->doc, root->children, 1);
283 text = ToSting16(text_ptr);
289 value->SetString(kTextKey, text);
291 return value.release();
294 scoped_refptr<ApplicationData> LoadApplication(
295 const base::FilePath& application_path,
296 Manifest::SourceType source_type,
297 std::string* error) {
298 Package::Type package_type;
299 if (!GetPackageType(application_path, &package_type, error))
302 return LoadApplication(application_path, std::string(),
303 source_type, package_type, error);
306 scoped_refptr<ApplicationData> LoadApplication(
307 const base::FilePath& application_path,
308 const std::string& application_id,
309 Manifest::SourceType source_type,
310 Package::Type package_type,
311 std::string* error) {
312 scoped_ptr<base::DictionaryValue> manifest(
313 LoadManifest(application_path, package_type, error));
317 scoped_refptr<ApplicationData> application = ApplicationData::Create(
323 if (!application.get())
326 std::vector<InstallWarning> warnings;
327 ManifestHandlerRegistry* registry =
328 manifest->HasKey(widget_keys::kWidgetKey)
329 ? ManifestHandlerRegistry::GetInstance(Package::WGT)
330 : ManifestHandlerRegistry::GetInstance(Package::XPK);
332 if (!registry->ValidateAppManifest(application, error, &warnings))
335 if (!warnings.empty()) {
336 LOG(WARNING) << "There are some warnings when validating the application "
337 << application->ID();
343 static base::DictionaryValue* LoadManifestXpk(
344 const base::FilePath& manifest_path,
345 std::string* error) {
346 JSONFileValueSerializer serializer(manifest_path);
347 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, error));
349 if (error->empty()) {
350 // If |error| is empty, than the file could not be read.
351 // It would be cleaner to have the JSON reader give a specific error
352 // in this case, but other code tests for a file error with
353 // error->empty(). For now, be consistent.
354 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
356 *error = base::StringPrintf("%s %s",
357 errors::kManifestParseError,
363 if (!root->IsType(base::Value::TYPE_DICTIONARY)) {
364 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
368 base::DictionaryValue* dv =
369 static_cast<base::DictionaryValue*>(root.release());
370 #if defined(OS_TIZEN)
371 // Ignore any Tizen application ID, as this is automatically generated.
372 dv->Remove(keys::kTizenAppIdKey, NULL);
378 static base::DictionaryValue* LoadManifestWgt(
379 const base::FilePath& manifest_path,
380 std::string* error) {
382 xmlNode* root_node = NULL;
383 doc = xmlReadFile(manifest_path.MaybeAsASCII().c_str(), NULL, 0);
385 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
388 root_node = xmlDocGetRootElement(doc);
389 base::DictionaryValue* dv = LoadXMLNode(root_node);
390 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
392 result->Set(ToConstCharPointer(root_node->name), dv);
394 return result.release();
397 base::DictionaryValue* LoadManifest(const base::FilePath& application_path,
398 Package::Type package_type,
399 std::string* error) {
400 base::FilePath manifest_path;
402 manifest_path = application_path.Append(kManifestXpkFilename);
403 if (package_type == Package::XPK)
404 return LoadManifestXpk(manifest_path, error);
406 manifest_path = application_path.Append(kManifestWgtFilename);
407 if (package_type == Package::WGT)
408 return LoadManifestWgt(manifest_path, error);
410 *error = base::StringPrintf("%s", errors::kManifestUnreadable);
414 base::FilePath ApplicationURLToRelativeFilePath(const GURL& url) {
415 std::string url_path = url.path();
416 if (url_path.empty() || url_path[0] != '/')
417 return base::FilePath();
419 // Drop the leading slashes and convert %-encoded UTF8 to regular UTF8.
420 std::string file_path = net::UnescapeURLComponent(url_path,
421 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS);
422 size_t skip = file_path.find_first_not_of("/\\");
423 if (skip != file_path.npos)
424 file_path = file_path.substr(skip);
426 base::FilePath path =
427 #if defined(OS_POSIX)
428 base::FilePath(file_path);
429 #elif defined(OS_WIN)
430 base::FilePath(base::UTF8ToWide(file_path));
436 // It's still possible for someone to construct an annoying URL whose path
437 // would still wind up not being considered relative at this point.
438 // For example: app://id/c:////foo.html
439 if (path.IsAbsolute())
440 return base::FilePath();
445 } // namespace application