1173a85891edc41e6d75a81b109a7e4275d06829
[platform/core/appfw/wgt-backend.git] / src / wgt / step / pkgmgr / step_generate_xml.cc
1 /* 2014, Copyright © Intel Coporation, license APACHE-2.0, see LICENSE file */
2 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3 // Use of this source code is governed by a apache 2.0 license that can be
4 // found in the LICENSE file.
5
6 #include "wgt/step/pkgmgr/step_generate_xml.h"
7
8 #include <boost/filesystem/path.hpp>
9 #include <boost/system/error_code.hpp>
10
11 #include <common/utils/file_util.h>
12 #include <common/utils/glist_range.h>
13 #include <common/privileges.h>
14
15 #include <libxml/parser.h>
16 #include <libxml/xmlreader.h>
17 #include <pkgmgr-info.h>
18 #include <pkgmgr_parser.h>
19 #include <tzplatform_config.h>
20 #include <unistd.h>
21
22 #include <cassert>
23 #include <cstring>
24 #include <string>
25
26 #include "wgt/wgt_backend_data.h"
27
28 namespace bs = boost::system;
29 namespace bf = boost::filesystem;
30
31 namespace {
32
33 const char kResWgt[] = "res/wgt";
34 const char kSharedRes[] = "shared/res";
35
36 void WriteUIApplicationAttributes(
37     xmlTextWriterPtr writer, application_x *app) {
38   if (app->taskmanage)
39     xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
40         BAD_CAST app->taskmanage);
41   if (app->nodisplay)
42     xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
43         BAD_CAST app->nodisplay);
44   if (app->multiple)
45     xmlTextWriterWriteAttribute(writer, BAD_CAST "multiple",
46         BAD_CAST app->multiple);
47   if (app->launch_mode && strlen(app->launch_mode))
48     xmlTextWriterWriteAttribute(writer, BAD_CAST "launch_mode",
49         BAD_CAST app->launch_mode);
50   if (app->ui_gadget && strlen(app->ui_gadget))
51     xmlTextWriterWriteAttribute(writer, BAD_CAST "ui-gadget",
52         BAD_CAST app->ui_gadget);
53   if (app->submode && strlen(app->submode))
54     xmlTextWriterWriteAttribute(writer, BAD_CAST "submode",
55         BAD_CAST app->submode);
56   if (app->submode_mainid && strlen(app->submode_mainid))
57     xmlTextWriterWriteAttribute(writer, BAD_CAST "submode-mainid",
58         BAD_CAST app->submode_mainid);
59   if (app->indicatordisplay && strlen(app->indicatordisplay))
60     xmlTextWriterWriteAttribute(writer, BAD_CAST "indicatordisplay",
61         BAD_CAST app->indicatordisplay);
62   if (app->portraitimg && strlen(app->portraitimg))
63     xmlTextWriterWriteAttribute(writer, BAD_CAST "portrait-effectimage",
64         BAD_CAST app->portraitimg);
65   if (app->landscapeimg && strlen(app->landscapeimg))
66     xmlTextWriterWriteAttribute(writer, BAD_CAST "landscape-effectimage",
67         BAD_CAST app->landscapeimg);
68   if (app->effectimage_type && strlen(app->effectimage_type))
69     xmlTextWriterWriteAttribute(writer, BAD_CAST "effectimage-type",
70         BAD_CAST app->effectimage_type);
71   if (app->hwacceleration && strlen(app->hwacceleration))
72     xmlTextWriterWriteAttribute(writer, BAD_CAST "hwacceleration",
73         BAD_CAST app->hwacceleration);
74 }
75
76 void WriteServiceApplicationAttributes(
77     xmlTextWriterPtr writer, application_x *app) {
78   xmlTextWriterWriteAttribute(writer, BAD_CAST "auto-restart",
79       BAD_CAST(app->autorestart ? app->autorestart : "false"));
80   xmlTextWriterWriteAttribute(writer, BAD_CAST "on-boot",
81       BAD_CAST(app->onboot ? app->onboot : "false"));
82   if (app->taskmanage)
83       xmlTextWriterWriteAttribute(writer, BAD_CAST "taskmanage",
84          BAD_CAST app->taskmanage);
85 }
86
87 bool WriteWidgetApplicationAttributesAndElements(
88     xmlTextWriterPtr writer, application_x *app,
89     const wgt::parse::AppWidgetInfo& widget_info,
90     const bf::path& shared_path) {
91   if (app->nodisplay)
92     xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay",
93         BAD_CAST app->nodisplay);
94   if (app->multiple)
95     xmlTextWriterWriteAttribute(writer, BAD_CAST "multiple",
96         BAD_CAST app->multiple);
97
98   // Generate attributes and elements not covered in manifest.xsd
99   auto& appwidgets = widget_info.app_widgets();
100   const auto& appwidget = std::find_if(appwidgets.begin(), appwidgets.end(),
101                                  [app](const wgt::parse::AppWidget& widget) {
102                                     return widget.id == app->appid;
103                                  });
104   if (appwidget == appwidgets.end()) {
105     LOG(ERROR) << "Failed to generate appwidget extra elements";
106     return false;
107   }
108
109   xmlTextWriterWriteAttribute(writer, BAD_CAST "main",
110       BAD_CAST (appwidget->primary ? "true" : "false"));  // NOLINT
111   if (!appwidget->update_period.empty()) {
112         xmlTextWriterWriteAttribute(writer, BAD_CAST "update-period", BAD_CAST
113             std::to_string(static_cast<int>(
114                     appwidget->update_period.front())).c_str());
115   }
116
117   for (auto& size : appwidget->content_size) {
118     xmlTextWriterStartElement(writer, BAD_CAST "support-size");
119
120     std::string type = wgt::parse::AppWidgetSizeTypeToString(size.type);
121     if (!size.preview.empty()) {
122       std::string icon_name = shared_path.string() + "/" + appwidget->id + "." + type + "." + "preview" +
123           bf::path(size.preview).extension().string();
124       xmlTextWriterWriteAttribute(writer, BAD_CAST "preview",
125           BAD_CAST icon_name.c_str());  // NOLINT
126     }
127
128     xmlTextWriterWriteAttribute(writer, BAD_CAST "frame",
129                                 BAD_CAST "true");
130     xmlTextWriterWriteString(writer,
131         BAD_CAST type.c_str());
132     xmlTextWriterEndElement(writer);
133   }
134   return true;
135 }
136
137 void WriteWatchApplicationAttributes(
138     xmlTextWriterPtr writer, application_x* app) {
139   if (app->ambient_support)
140     xmlTextWriterWriteAttribute(writer, BAD_CAST "ambient-support",
141         BAD_CAST app->ambient_support);
142 }
143
144 }  // namespace
145
146 namespace wgt {
147 namespace pkgmgr {
148
149 common_installer::Step::Status StepGenerateXml::GenerateApplicationCommonXml(
150     application_x* app, xmlTextWriterPtr writer, AppCompType type) {
151   xmlTextWriterWriteAttribute(writer, BAD_CAST "appid", BAD_CAST app->appid);
152
153   // binary is a symbolic link named <appid> and is located in <pkgid>/<appid>
154   bf::path exec_path = context_->pkg_path.get()
155       / bf::path("bin") / bf::path(app->appid);
156   xmlTextWriterWriteAttribute(writer, BAD_CAST "exec",
157                               BAD_CAST exec_path.string().c_str());
158   if (app->type)
159     xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST app->type);
160   else
161     xmlTextWriterWriteAttribute(writer, BAD_CAST "type", BAD_CAST "capp");
162
163   if (app->process_pool && strlen(app->process_pool))
164     xmlTextWriterWriteAttribute(writer, BAD_CAST "process-pool",
165                                 BAD_CAST app->process_pool);
166   // app-specific attributes
167   switch (type) {
168   case AppCompType::UIAPP:
169     WriteUIApplicationAttributes(writer, app);
170     break;
171   case AppCompType::SVCAPP:
172     WriteServiceApplicationAttributes(writer, app);
173     break;
174   case AppCompType::WIDGETAPP:
175     if (!WriteWidgetApplicationAttributesAndElements(writer, app,
176         static_cast<WgtBackendData*>(
177             context_->backend_data.get())->appwidgets.get(),
178         context_->pkg_path.get() / "shared" / "res"))
179       return Status::MANIFEST_ERROR;
180     break;
181   case AppCompType::WATCHAPP:
182     WriteWatchApplicationAttributes(writer, app);
183     break;
184   }
185
186   for (label_x* label : GListRange<label_x*>(app->label)) {
187     xmlTextWriterStartElement(writer, BAD_CAST "label");
188     if (label->lang && strcmp(DEFAULT_LOCALE, label->lang) != 0) {
189       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
190                                   BAD_CAST label->lang);
191     }
192     xmlTextWriterWriteString(writer, BAD_CAST label->name);
193     xmlTextWriterEndElement(writer);
194   }
195
196   if (app->icon) {
197     icon_x* iconx = reinterpret_cast<icon_x*>(app->icon->data);
198     xmlTextWriterWriteFormatElement(
199         writer, BAD_CAST "icon", "%s", BAD_CAST iconx->text);
200   } else {
201     // Default icon setting is role of the platform
202     LOG(DEBUG) << "Icon was not found in application";
203   }
204
205   for (image_x* image : GListRange<image_x*>(app->image)) {
206     xmlTextWriterStartElement(writer, BAD_CAST "image");
207     if (image->lang && strcmp(DEFAULT_LOCALE, image->lang) != 0) {
208       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
209
210                                   BAD_CAST image->lang);
211     }
212     if (image->section)
213       xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
214                                   BAD_CAST image->section);
215     xmlTextWriterEndElement(writer);
216   }
217
218   for (appcontrol_x* appc : GListRange<appcontrol_x*>(app->appcontrol)) {
219     xmlTextWriterStartElement(writer, BAD_CAST "app-control");
220
221     if (appc->operation) {
222       xmlTextWriterStartElement(writer, BAD_CAST "operation");
223       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
224           BAD_CAST appc->operation);
225       xmlTextWriterEndElement(writer);
226     }
227
228     if (appc->uri) {
229       xmlTextWriterStartElement(writer, BAD_CAST "uri");
230       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
231           BAD_CAST appc->uri);
232       xmlTextWriterEndElement(writer);
233     }
234
235     if (appc->mime) {
236       xmlTextWriterStartElement(writer, BAD_CAST "mime");
237       xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
238           BAD_CAST appc->mime);
239       xmlTextWriterEndElement(writer);
240     }
241
242     xmlTextWriterEndElement(writer);
243   }
244
245   for (datacontrol_x* datacontrol :
246        GListRange<datacontrol_x*>(app->datacontrol)) {
247     xmlTextWriterStartElement(writer, BAD_CAST "datacontrol");
248     if (datacontrol->access) {
249       xmlTextWriterWriteAttribute(writer, BAD_CAST "access",
250           BAD_CAST datacontrol->access);
251     }
252     if (datacontrol->providerid) {
253       xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
254           BAD_CAST datacontrol->providerid);
255     }
256     if (datacontrol->type) {
257       xmlTextWriterWriteAttribute(writer, BAD_CAST "type",
258           BAD_CAST datacontrol->type);
259     }
260     xmlTextWriterEndElement(writer);
261   }
262
263   for (metadata_x* meta : GListRange<metadata_x*>(app->metadata)) {
264     xmlTextWriterStartElement(writer, BAD_CAST "metadata");
265     xmlTextWriterWriteAttribute(writer, BAD_CAST "key",
266         BAD_CAST meta->key);
267     if (meta->value)
268       xmlTextWriterWriteAttribute(writer, BAD_CAST "value",
269           BAD_CAST meta->value);
270     xmlTextWriterEndElement(writer);
271   }
272
273   for (const char* category : GListRange<char*>(app->category)) {
274     xmlTextWriterStartElement(writer, BAD_CAST "category");
275     xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST category);
276     xmlTextWriterEndElement(writer);
277   }
278
279   for (const char* background_category : GListRange<char*>(
280       app->background_category)) {
281     xmlTextWriterStartElement(writer, BAD_CAST "background-category");
282     xmlTextWriterWriteAttribute(writer, BAD_CAST "value",
283         BAD_CAST background_category);
284     xmlTextWriterEndElement(writer);
285   }
286
287   return Step::Status::OK;
288 }
289
290 common_installer::Step::Status StepGenerateXml::undo() {
291   bs::error_code error;
292   if (bf::exists(context_->xml_path.get()))
293     bf::remove_all(context_->xml_path.get(), error);
294   return Status::OK;
295 }
296
297 common_installer::Step::Status StepGenerateXml::precheck() {
298   if (!context_->manifest_data.get()) {
299     LOG(ERROR) << "manifest_data attribute is empty";
300     return Step::Status::INVALID_VALUE;
301   }
302   if (context_->pkgid.get().empty()) {
303     LOG(ERROR) << "pkgid attribute is empty";
304     return Step::Status::PACKAGE_NOT_FOUND;
305   }
306
307   if (!context_->manifest_data.get()->application) {
308     LOG(ERROR) << "No application in package";
309     return Step::Status::INVALID_VALUE;
310   }
311   // TODO(p.sikorski) check context_->uid.get()
312
313   return Step::Status::OK;
314 }
315
316 common_installer::Step::Status StepGenerateXml::process() {
317   bf::path xml_path =
318       bf::path(getUserManifestPath(context_->uid.get(), false))
319       / bf::path(context_->pkgid.get());
320   xml_path += ".xml";
321   context_->xml_path.set(xml_path.string());
322
323   bs::error_code error;
324   if (!bf::exists(xml_path.parent_path(), error)) {
325     if (!common_installer::CreateDir(xml_path.parent_path())) {
326       LOG(ERROR) <<
327           "Directory for manifest xml is missing and cannot be created";
328       return Status::MANIFEST_ERROR;
329     }
330   }
331
332   xmlTextWriterPtr writer;
333   writer = xmlNewTextWriterFilename(context_->xml_path.get().c_str(), 0);
334   if (!writer) {
335     LOG(ERROR) << "Failed to create new file";
336     return Step::Status::MANIFEST_ERROR;
337   }
338
339   xmlTextWriterStartDocument(writer, nullptr, nullptr, nullptr);
340   xmlTextWriterSetIndent(writer, 1);
341
342   Status status = GenerateManifestElement(writer);
343   if (status != Status::OK) {
344     return status;
345   }
346
347   xmlTextWriterEndDocument(writer);
348   xmlFreeTextWriter(writer);
349
350   if (pkgmgr_parser_check_manifest_validation(
351       context_->xml_path.get().c_str()) != 0) {
352     LOG(ERROR) << "Manifest is not valid";
353     return Step::Status::MANIFEST_ERROR;
354   }
355
356   LOG(DEBUG) << "Successfully create manifest xml "
357       << context_->xml_path.get();
358   return Status::OK;
359 }
360
361 common_installer::Step::Status StepGenerateXml::GenerateManifestElement(
362         xmlTextWriterPtr writer) {
363   xmlTextWriterStartElement(writer, BAD_CAST "manifest");
364
365   GenerateManifestElementAttributes(writer);
366   GenerateLangLabels(writer);
367   GenerateAuthor(writer);
368   GenerateDescription(writer);
369   Status status = GenerateApplications(writer);
370   if (status != Status::OK) {
371     return status;
372   }
373   GeneratePrivilege(writer);
374   GenerateAccount(writer);
375   GenerateIme(writer);
376   GenerateProfiles(writer);
377   GenerateShortcuts(writer);
378
379   xmlTextWriterEndElement(writer);
380   return Status::OK;
381 }
382
383 void StepGenerateXml::GenerateManifestElementAttributes(
384         xmlTextWriterPtr writer) {
385   xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns",
386       BAD_CAST "http://tizen.org/ns/packages");
387   xmlTextWriterWriteAttribute(writer, BAD_CAST "package",
388       BAD_CAST context_->manifest_data.get()->package);
389   xmlTextWriterWriteAttribute(writer, BAD_CAST "type",
390       BAD_CAST context_->manifest_data.get()->type);
391   xmlTextWriterWriteAttribute(writer, BAD_CAST "version",
392       BAD_CAST context_->manifest_data.get()->version);
393   xmlTextWriterWriteAttribute(writer, BAD_CAST "api-version",
394       BAD_CAST context_->manifest_data.get()->api_version);
395   xmlTextWriterWriteAttribute(writer, BAD_CAST "nodisplay-setting",
396       BAD_CAST context_->manifest_data.get()->nodisplay_setting);
397 }
398
399 void StepGenerateXml::GenerateLangLabels(xmlTextWriterPtr writer) {
400   for (label_x* label :
401        GListRange<label_x*>(context_->manifest_data.get()->label)) {
402     xmlTextWriterStartElement(writer, BAD_CAST "label");
403     if (label->lang && strcmp(DEFAULT_LOCALE, label->lang) != 0) {
404       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
405                                   BAD_CAST label->lang);
406     }
407     xmlTextWriterWriteString(writer, BAD_CAST label->name);
408     xmlTextWriterEndElement(writer);
409   }
410 }
411
412 void StepGenerateXml::GenerateAuthor(xmlTextWriterPtr writer) {
413   for (author_x* author :
414        GListRange<author_x*>(context_->manifest_data.get()->author)) {
415     xmlTextWriterStartElement(writer, BAD_CAST "author");
416     if (author->email && strlen(author->email)) {
417       xmlTextWriterWriteAttribute(writer, BAD_CAST "email",
418                                   BAD_CAST author->email);
419     }
420     if (author->href && strlen(author->href)) {
421       xmlTextWriterWriteAttribute(writer, BAD_CAST "href",
422                                   BAD_CAST author->href);
423     }
424     xmlTextWriterWriteString(writer, BAD_CAST author->text);
425     xmlTextWriterEndElement(writer);
426   }
427 }
428
429 void StepGenerateXml::GenerateDescription(xmlTextWriterPtr writer) {
430   for (description_x* description :
431        GListRange<description_x*>(context_->manifest_data.get()->description)) {
432     xmlTextWriterStartElement(writer, BAD_CAST "description");
433     if (description->lang && strcmp(DEFAULT_LOCALE, description->lang) != 0) {
434       xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
435                                   BAD_CAST description->lang);
436     }
437     xmlTextWriterWriteString(writer, BAD_CAST description->text);
438     xmlTextWriterEndElement(writer);
439   }
440 }
441
442 common_installer::Step::Status StepGenerateXml::GenerateApplications(
443         xmlTextWriterPtr writer) {
444   for (application_x* app :
445        GListRange<application_x*>(context_->manifest_data.get()->application)) {
446     AppCompType type;
447     if (strcmp(app->component_type, "uiapp") == 0) {
448       type = AppCompType::UIAPP;
449       xmlTextWriterStartElement(writer, BAD_CAST "ui-application");
450     } else if (strcmp(app->component_type, "svcapp") == 0) {
451       type = AppCompType::SVCAPP;
452       xmlTextWriterStartElement(writer, BAD_CAST "service-application");
453     } else if (strcmp(app->component_type, "widgetapp") == 0) {
454       type = AppCompType::WIDGETAPP;
455       xmlTextWriterStartElement(writer, BAD_CAST "widget-application");
456     } else if (strcmp(app->component_type, "watchapp") == 0) {
457       type = AppCompType::WATCHAPP;
458       xmlTextWriterStartElement(writer, BAD_CAST "watch-application");
459     } else {
460       LOG(ERROR) << "Unknown application component_type";
461       xmlFreeTextWriter(writer);
462       return Status::ERROR;
463     }
464     Status status = GenerateApplicationCommonXml(app, writer, type);
465     if (status != Status::OK) {
466       xmlFreeTextWriter(writer);
467       return status;
468     }
469     xmlTextWriterEndElement(writer);
470   }
471   return Status::OK;
472 }
473
474 void StepGenerateXml::GeneratePrivilege(xmlTextWriterPtr writer) {
475     if (context_->manifest_data.get()->privileges) {
476     xmlTextWriterStartElement(writer, BAD_CAST "privileges");
477     for (const char* priv :
478          GListRange<char*>(context_->manifest_data.get()->privileges)) {
479       xmlTextWriterWriteFormatElement(writer, BAD_CAST "privilege",
480         "%s", BAD_CAST priv);
481     }
482
483     xmlTextWriterEndElement(writer);
484   }
485 }
486
487 void StepGenerateXml::GenerateAccount(xmlTextWriterPtr writer) {
488   const auto& accounts =
489       context_->manifest_plugins_data.get().account_info.get().accounts();
490   if (!accounts.empty()) {
491     xmlTextWriterStartElement(writer, BAD_CAST "account");
492     // add account info
493     for (auto& account : accounts) {
494       xmlTextWriterStartElement(writer, BAD_CAST "account-provider");
495
496       xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
497                                   BAD_CAST account.appid.c_str());
498
499       if (!account.providerid.empty())
500         xmlTextWriterWriteAttribute(writer, BAD_CAST "providerid",
501                                     BAD_CAST account.providerid.c_str());
502
503       if (account.multiple_account_support)
504         xmlTextWriterWriteAttribute(writer,
505                                     BAD_CAST "multiple-accounts-support",
506                                     BAD_CAST "true");
507       for (auto& icon_pair : account.icon_paths) {
508         xmlTextWriterStartElement(writer, BAD_CAST "icon");
509         if (icon_pair.first == "AccountSmall")
510           xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
511                                       BAD_CAST "account-small");
512         else
513           xmlTextWriterWriteAttribute(writer, BAD_CAST "section",
514                                       BAD_CAST "account");
515         xmlTextWriterWriteString(writer, BAD_CAST icon_pair.second.c_str());
516         xmlTextWriterEndElement(writer);
517       }
518
519       for (auto& name_pair : account.names) {
520         xmlTextWriterStartElement(writer, BAD_CAST "label");
521         if (!name_pair.second.empty())
522           xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
523                                       BAD_CAST name_pair.second.c_str());
524         xmlTextWriterWriteString(writer, BAD_CAST name_pair.first.c_str());
525         xmlTextWriterEndElement(writer);
526       }
527
528       for (auto& capability : account.capabilities) {
529         xmlTextWriterWriteFormatElement(writer, BAD_CAST "capability",
530           "%s", BAD_CAST capability.c_str());
531       }
532
533       xmlTextWriterEndElement(writer);
534     }
535     xmlTextWriterEndElement(writer);
536   }
537 }
538
539 void StepGenerateXml::GenerateIme(xmlTextWriterPtr writer) {
540   const auto &ime = context_->manifest_plugins_data.get().ime_info.get();
541   const auto ime_uuid = ime.uuid();
542   if (!ime_uuid.empty()) {
543     xmlTextWriterStartElement(writer, BAD_CAST "ime");
544
545     GListRange<application_x*> app_range(
546         context_->manifest_data.get()->application);
547     if (!app_range.Empty()) {
548       // wgt app have ui-application as first application element.
549       // there may be service-applications but not as first element.
550       application_x* app = *app_range.begin();
551       xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
552                                   BAD_CAST app->appid);
553     }
554
555     xmlTextWriterStartElement(writer, BAD_CAST "uuid");
556     xmlTextWriterWriteString(writer, BAD_CAST ime_uuid.c_str());
557     xmlTextWriterEndElement(writer);
558
559     xmlTextWriterStartElement(writer, BAD_CAST "languages");
560
561     for (auto it = ime.LanguagesBegin(); it != ime.LanguagesEnd(); ++it) {
562       xmlTextWriterStartElement(writer, BAD_CAST "language");
563       xmlTextWriterWriteString(writer, BAD_CAST it->c_str());
564       xmlTextWriterEndElement(writer);
565     }
566
567     xmlTextWriterEndElement(writer);
568
569     xmlTextWriterEndElement(writer);
570   }
571 }
572
573 void StepGenerateXml::GenerateProfiles(xmlTextWriterPtr writer) {
574   for (const char* profile :
575        GListRange<char*>(context_->manifest_data.get()->deviceprofile)) {
576     xmlTextWriterStartElement(writer, BAD_CAST "profile");
577     xmlTextWriterWriteAttribute(writer, BAD_CAST "name",
578                                 BAD_CAST profile);
579     xmlTextWriterEndElement(writer);
580   }
581 }
582
583 void StepGenerateXml::GenerateShortcuts(xmlTextWriterPtr writer) {
584   const auto& shortcuts =
585       context_->manifest_plugins_data.get().shortcut_info.get();
586   if (!shortcuts.empty()) {
587     xmlTextWriterStartElement(writer, BAD_CAST "shortcut-list");
588     for (auto& shortcut : shortcuts) {
589       xmlTextWriterStartElement(writer, BAD_CAST "shortcut");
590       if (!shortcut.app_id.empty())
591         xmlTextWriterWriteAttribute(writer, BAD_CAST "appid",
592                                     BAD_CAST shortcut.app_id.c_str());
593       if (!shortcut.app_id.empty())
594         xmlTextWriterWriteAttribute(writer, BAD_CAST "extra_data",
595                                     BAD_CAST shortcut.extra_data.c_str());
596       if (!shortcut.app_id.empty())
597         xmlTextWriterWriteAttribute(writer, BAD_CAST "extra_key",
598                                     BAD_CAST shortcut.extra_key.c_str());
599       if (!shortcut.icon.empty()) {
600         xmlTextWriterStartElement(writer, BAD_CAST "icon");
601         xmlTextWriterWriteString(writer, BAD_CAST shortcut.icon.c_str());
602         xmlTextWriterEndElement(writer);
603       }
604       for (auto& label : shortcut.labels) {
605         xmlTextWriterStartElement(writer, BAD_CAST "label");
606         if (!label.first.empty())
607           xmlTextWriterWriteAttribute(writer, BAD_CAST "xml:lang",
608                                       BAD_CAST label.first.c_str());
609         xmlTextWriterWriteString(writer, BAD_CAST label.second.c_str());
610         xmlTextWriterEndElement(writer);
611       }
612       xmlTextWriterEndElement(writer);
613     }
614     xmlTextWriterEndElement(writer);
615   }
616 }
617
618 }  // namespace pkgmgr
619 }  // namespace wgt