1 // Copyright (c) 2014 Samsung Electronics Co., Ltd 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/manifest_handlers/tizen_appwidget_handler.h"
10 #include "base/macros.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "third_party/re2/re2/re2.h"
15 #include "xwalk/application/common/application_manifest_constants.h"
16 #include "xwalk/application/common/manifest_handlers/tizen_application_handler.h"
20 namespace keys = application_widget_keys;
22 namespace application {
26 const char kErrMsgInvalidDictionary[] =
27 "Cannot get key value as a dictionary. Key name: ";
28 const char kErrMsgInvalidList[] =
29 "Cannot get key value as a list. Key name: ";
30 const char kErrMsgNoMandatoryKey[] =
31 "Cannot find mandatory key. Key name: ";
32 const char kErrMsgInvalidKeyValue[] =
33 "Invalid key value. Key name: ";
34 const char kErrMsgMultipleKeys[] =
35 "Too many keys found. Key name: ";
36 const char kErrMsgNoNamespace[] =
37 "Element pointed by key has no namespace specified. Key name: ";
38 const char kErrMsgInvalidNamespace[] =
39 "Invalid namespace of element pointed by key. Key name: ";
40 const char kErrMsgAppWidgetInfoNotFound[] =
41 "Cannot access app-widget info object.";
42 const char kErrMsgApplicationInfoNotFound[] =
43 "Cannot access application info object.";
44 const char kErrMsgDuplicatedAppWidgetId[] =
45 "Duplicated value of an id attribute in app-widget element. The value: ";
46 const char kErrMsgInvalidAppWidgetIdBeginning[] =
47 "Invalid beginning of an id attribute value in app-widget element."
49 const char kErrMsgInvalidAppWidgetIdFormat[] =
50 "Invalid format of an id attribute value in app-widget element."
52 const char kErrMsgUpdatePeriodOutOfDomain[] =
53 "Value of an update-period attribute in app-widget element out of domain."
55 const char kErrMsgNoLabel[] =
56 "No box-label element in app-widget element.";
57 const char kErrMsgInvalidIconSrc[] =
58 "Invalid path in a src attribute of box-icon element. The value: ";
59 const char kErrMsgInvalidContentSrc[] =
60 "Invalid path or url in a src attribute of box-content element."
62 const char kErrMsgInvalidContentSizePreview[] =
63 "Invalid path in a preview attribute of box-size element. The value: ";
64 const char kErrMsgNoMandatoryContentSize1x1[] =
65 "No mandatory box-size element (1x1) in box-content element.";
66 const char kErrMsgInvalidContentDropViewSrc[] =
67 "Invalid path or url in a src attribute of pd element. The value: ";
68 const char kErrMsgContentDropViewHeightOutOfDomain[] =
69 "Value of a height attribute in box-content element out of domain."
72 // If the error parameter is specified, it is filled with the given message
73 // otherwise it does nothing.
74 void SetError(const std::string& message,
80 // If the error parameter is specified, it is filled with concatenation
81 // of message and arg parameters otherwise it does nothing.
82 void SetError(const std::string& message,
83 const std::string& arg, std::string* error) {
85 *error = message + arg;
88 // If the error parameter is specified, it is filled with the given message
89 // otherwise it does nothing.
90 void SetError(const std::string& message,
91 base::string16* error) {
93 *error = base::ASCIIToUTF16(message);
96 // If the error parameter is specified, it is filled with concatenation
97 // of message and arg parameters otherwise it does nothing.
98 void SetError(const std::string& message,
99 const std::string& arg, base::string16* error) {
101 *error = base::ASCIIToUTF16(message + arg);
104 // Retrieves a mandatory dictionary from specified manifest and specified key.
105 // Returns true, if the ditionary is found or false otherwise. If the error
106 // parameter is specified, it is also filled with proper message.
107 bool GetMandatoryDictionary(const Manifest& manifest, const std::string& key,
108 const base::DictionaryValue** dict, base::string16* error) {
110 if (!manifest.HasPath(key)) {
111 SetError(kErrMsgNoMandatoryKey, key, error);
114 if (!manifest.GetDictionary(key, dict) || !*dict) {
115 SetError(kErrMsgInvalidDictionary, key, error);
121 // Converts given text value to a value of specific type. Returns true
122 // if convertion is successful or false otherwise.
123 template <typename ValueType>
124 bool ConvertValue(const std::string& str_value, ValueType* value) {
125 NOTREACHED() << "Use one of already defined template specializations"
126 " or define a new one.";
130 // Converts given text value to a string value. Returns true
131 // if convertion is successful or false otherwise.
133 bool ConvertValue(const std::string& str_value, std::string* value) {
139 // Converts given text value to a boolean value. Returns true
140 // if convertion is successful or false otherwise.
142 bool ConvertValue(const std::string& str_value, bool* value) {
144 if (str_value == "true") {
148 if (str_value == "false") {
155 // Converts given text value to an integer value. Returns true
156 // if convertion is successful or false otherwise.
158 bool ConvertValue(const std::string& str_value, int* value) {
160 return base::StringToInt(str_value, value);
163 // Converts given text value to a floating point value. Returns true
164 // if convertion is successful or false otherwise.
166 bool ConvertValue(const std::string& str_value, double* value) {
168 return base::StringToDouble(str_value, value);
171 // Retrieves a mandatory value from specified dictionary and specified key.
172 // Returns true, if the value is found or false otherwise. If the error
173 // parameter is specified, it is also filled with proper message.
174 template <typename ValueType>
175 bool GetMandatoryValue(const base::DictionaryValue& dict,
176 const std::string& key, ValueType* value, base::string16* error) {
179 if (!dict.GetString(key, &tmp)) {
180 SetError(kErrMsgNoMandatoryKey, key, error);
183 bool result = ConvertValue(tmp, value);
185 SetError(kErrMsgInvalidKeyValue, key, error);
189 // Retrieves an optional value from specified dictionary and specified key.
190 // If the value is found, the function returns true and fills value
191 // parameter. If the value is not found, the function returns true and fills
192 // value parameter with default value. If an error occurs, it returns false
193 // and fills error parameter if it is set.
194 template <typename ValueType>
195 bool GetOptionalValue(const base::DictionaryValue& dict,
196 const std::string& key, ValueType default_value, ValueType* value,
197 base::string16* error) {
200 if (!dict.GetString(key, &tmp)) {
201 *value = default_value;
204 bool result = ConvertValue(tmp, value);
206 SetError(kErrMsgInvalidKeyValue, key, error);
210 // Helper function for ParseEach. Do not use directly.
211 template <typename ParseSingleType, typename DataContainerType>
212 bool ParseEachInternal(const base::Value& value, const std::string& key,
213 ParseSingleType parse_single, DataContainerType* data_container,
214 base::string16* error) {
215 DCHECK(data_container);
216 const base::DictionaryValue* inner_dict;
217 if (!value.GetAsDictionary(&inner_dict)) {
218 SetError(kErrMsgInvalidDictionary, key, error);
221 if (!parse_single(*inner_dict, key, data_container, error))
226 // Parsing helper function calling 'parse_single' for each dictionary contained
227 // in 'dict' under a 'key'. This helper function takes two template arguments:
228 // - a function with following prototype:
229 // bool ParseSingleExample(const base::Value& value, const std::string& key,
230 // DataContainerType* data_container, base::string16* error);
231 // - a DataContainerType object where the above function stores data
232 template <typename ParseSingleType, typename DataContainerType>
233 bool ParseEach(const base::DictionaryValue& dict, const std::string& key,
234 bool mandatory, ParseSingleType parse_single,
235 DataContainerType* data_container, base::string16* error) {
236 DCHECK(data_container);
238 const base::Value* value = nullptr;
239 if (!dict.Get(key, &value) || !value) {
241 SetError(kErrMsgNoMandatoryKey, key, error);
247 if (value->IsType(base::Value::TYPE_DICTIONARY)) {
248 if (!ParseEachInternal(*value, key, parse_single, data_container, error))
250 } else if (value->IsType(base::Value::TYPE_LIST)) {
251 const base::ListValue* list;
252 if (!value->GetAsList(&list)) {
253 SetError(kErrMsgInvalidList, key, error);
256 for (const base::Value* value : *list)
257 if (!ParseEachInternal(*value, key, parse_single, data_container, error))
264 // Verifies whether specified dictionary represents an element in specified
265 // namespace. Returns true, if the namespace is set and equal to the specified
266 // one or false otherwise. If the error parameter is specified, it is also
267 // filled with proper message.
268 bool VerifyElementNamespace(const base::DictionaryValue& dict,
269 const std::string& key, const std::string& desired_namespace_value,
270 base::string16* error) {
271 std::string namespace_value;
272 if (!GetMandatoryValue(dict, keys::kNamespaceKey,
273 &namespace_value, nullptr)) {
274 SetError(kErrMsgNoNamespace, key, error);
277 if (namespace_value != desired_namespace_value) {
278 SetError(kErrMsgInvalidNamespace, key, error);
284 // Parses box-label part
285 bool ParseLabel(const base::DictionaryValue& dict,
286 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
289 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
293 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxLabelLangKey,
294 std::string(), &lang, error))
298 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxLabelTextKey,
303 // Note: Tizen 2.2 WRT Core Spec does not determine how many times the value
304 // without lang attribute can appear in one app-widget, so overwrite.
305 app_widget->label.default_value = text;
307 // Note: Tizen 2.2 WRT Core Spec does not determine how many times the value
308 // with specific lang attribute can appear in one app-widget, so overwrite.
309 app_widget->label.lang_value_map[lang] = text;
315 // Parses box-icon part
316 bool ParseIcon(const base::DictionaryValue& dict,
317 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
320 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
323 if (!app_widget->icon_src.empty()) {
324 SetError(kErrMsgMultipleKeys, key, error);
327 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxIconSrcKey,
328 &app_widget->icon_src, error))
334 // Converts size type from text to enum representation
335 bool StringToSizeType(const std::string& str_type,
336 TizenAppWidgetSizeType* enum_type) {
338 if (str_type == "1x1") {
339 *enum_type = TizenAppWidgetSizeType::k1x1;
342 if (str_type == "2x1") {
343 *enum_type = TizenAppWidgetSizeType::k2x1;
346 if (str_type == "2x2") {
347 *enum_type = TizenAppWidgetSizeType::k2x2;
353 // Parses box-size part
354 bool ParseContentSizes(const base::DictionaryValue& dict,
355 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
358 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
361 TizenAppWidgetSize size;
363 std::string str_type;
364 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentSizeTextKey,
368 TizenAppWidgetSizeType type;
369 if (!StringToSizeType(str_type, &type)) {
370 SetError(kErrMsgInvalidKeyValue,
371 keys::kTizenAppWidgetBoxContentSizeTextKey, error);
376 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentSizePreviewKey,
377 std::string(), &size.preview, error))
380 if (!GetOptionalValue(dict,
381 keys::kTizenAppWidgetBoxContentSizeUseDecorationKey,
382 true, &size.use_decoration, error))
385 app_widget->content_size.push_back(size);
391 bool ParseContentDropView(const base::DictionaryValue& dict,
392 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
395 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
398 if (!app_widget->content_drop_view.empty()) {
399 SetError(kErrMsgMultipleKeys, key, error);
403 TizenAppWidgetDropView drop_view;
405 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentDropViewSrcKey,
406 &drop_view.src, error))
409 if (!GetMandatoryValue(dict,
410 keys::kTizenAppWidgetBoxContentDropViewWidthKey,
411 &drop_view.width, error))
414 if (!GetMandatoryValue(dict,
415 keys::kTizenAppWidgetBoxContentDropViewHeightKey,
416 &drop_view.height, error))
419 app_widget->content_drop_view.push_back(drop_view);
424 // Parses box-content part
425 bool ParseContent(const base::DictionaryValue& dict,
426 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
429 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
432 if (!app_widget->content_src.empty()) {
433 SetError(kErrMsgMultipleKeys, key, error);
436 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentSrcKey,
437 &app_widget->content_src, error))
440 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentMouseEventKey,
441 false, &app_widget->content_mouse_event, error))
444 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentTouchEffectKey,
445 true, &app_widget->content_touch_effect, error))
448 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentSizeKey,
449 true, ParseContentSizes, app_widget, error))
452 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentDropViewKey,
453 false, ParseContentDropView, app_widget, error))
459 // Parses app-widget part
460 bool ParseAppWidget(const base::DictionaryValue& dict,
461 const std::string& key, TizenAppWidgetVector* app_widgets,
462 base::string16* error) {
465 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
468 TizenAppWidget app_widget;
470 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetIdKey,
471 &app_widget.id, error))
474 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetPrimaryKey,
475 &app_widget.primary, error))
478 double update_period;
479 double no_update_period = std::numeric_limits<double>::min();
480 if (!GetOptionalValue(dict, keys::kTizenAppWidgetUpdatePeriodKey,
481 no_update_period, &update_period, error))
483 if (update_period != no_update_period)
484 app_widget.update_period.push_back(update_period);
486 if (!GetOptionalValue(dict, keys::kTizenAppWidgetAutoLaunchKey,
487 false, &app_widget.auto_launch, error))
490 if (!ParseEach(dict, keys::kTizenAppWidgetBoxLabelKey,
491 true, ParseLabel, &app_widget, error))
494 if (!ParseEach(dict, keys::kTizenAppWidgetBoxIconKey,
495 false, ParseIcon, &app_widget, error))
498 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentKey,
499 true, ParseContent, &app_widget, error))
502 app_widgets->push_back(app_widget);
507 // Validates all app-widget ids
508 bool ValidateEachId(const TizenAppWidgetVector& app_widgets,
509 const std::string& app_id, std::string* error) {
510 std::set<std::string> unique_values;
512 for (const TizenAppWidget& app_widget : app_widgets) {
513 if (!unique_values.insert(app_widget.id).second) {
514 SetError(kErrMsgDuplicatedAppWidgetId, app_widget.id, error);
518 const size_t app_id_len = app_id.length();
520 if (app_widget.id.find(app_id) != 0) {
521 SetError(kErrMsgInvalidAppWidgetIdBeginning, app_widget.id, error);
525 const char kStringPattern[] = "[.][0-9a-zA-Z]+";
526 if (!RE2::FullMatch(app_widget.id.substr(app_id_len), kStringPattern)) {
527 SetError(kErrMsgInvalidAppWidgetIdFormat, app_widget.id, error);
535 // Tests if specified string represents valid remote url
536 bool IsValidUrl(const std::string& value) {
537 // TODO(tweglarski): implement me (it's not crucial atm)
541 // Tests if specified string represents valid path
542 bool IsValidPath(const std::string& value) {
543 // TODO(tweglarski): implement me (it's not crucial atm)
547 // Tests if specified string represents valid path or remote url
548 bool IsValidPathOrUrl(const std::string& value) {
549 return IsValidPath(value) || IsValidUrl(value);
552 // Validates all content sizes in an app-widget
553 bool ValidateContentSize(const TizenAppWidgetSizeVector& content_size,
554 std::string* error) {
555 bool mandatory_1x1_found = false;
557 for (const TizenAppWidgetSize& size : content_size) {
558 mandatory_1x1_found |= size.type == TizenAppWidgetSizeType::k1x1;
560 if (!size.preview.empty() && !IsValidPath(size.preview)) {
561 SetError(kErrMsgInvalidContentSizePreview, size.preview, error);
566 if (!mandatory_1x1_found) {
567 SetError(kErrMsgNoMandatoryContentSize1x1, error);
576 TizenAppWidgetInfo::TizenAppWidgetInfo(const TizenAppWidgetVector& app_widgets)
577 : app_widgets_(app_widgets) {
580 TizenAppWidgetInfo::~TizenAppWidgetInfo() {
583 TizenAppWidgetHandler::TizenAppWidgetHandler() {
586 TizenAppWidgetHandler::~TizenAppWidgetHandler() {
589 bool TizenAppWidgetHandler::Parse(scoped_refptr<ApplicationData> application,
590 base::string16* error) {
591 const Manifest* manifest = application->GetManifest();
594 const base::DictionaryValue* dict = nullptr;
595 if (!GetMandatoryDictionary(*manifest, keys::kTizenWidgetKey, &dict, error))
598 TizenAppWidgetVector app_widgets;
600 if (!ParseEach(*dict, keys::kTizenAppWidgetKey,
601 false, ParseAppWidget, &app_widgets, error))
604 scoped_ptr<TizenAppWidgetInfo> info(new TizenAppWidgetInfo(app_widgets));
605 application->SetManifestData(keys::kTizenAppWidgetFullKey, info.release());
610 bool TizenAppWidgetHandler::Validate(
611 scoped_refptr<const ApplicationData> application,
612 std::string* error) const {
613 const TizenAppWidgetInfo* app_widget_info =
614 static_cast<const TizenAppWidgetInfo*>(
615 application->GetManifestData(keys::kTizenAppWidgetFullKey));
616 const TizenApplicationInfo* app_info =
617 static_cast<const TizenApplicationInfo*>(
618 application->GetManifestData(keys::kTizenApplicationKey));
620 if (!app_widget_info) {
621 SetError(kErrMsgAppWidgetInfoNotFound, error);
625 SetError(kErrMsgApplicationInfoNotFound, error);
629 const TizenAppWidgetVector& app_widgets = app_widget_info->app_widgets();
631 if (!ValidateEachId(app_widgets, app_info->id(), error))
634 for (const TizenAppWidget& app_widget : app_widgets) {
635 if (!app_widget.update_period.empty()
636 && app_widget.update_period.front() < 1800) {
637 SetError(kErrMsgUpdatePeriodOutOfDomain,
638 base::DoubleToString(app_widget.update_period.front()), error);
642 if (app_widget.label.default_value.empty()
643 && app_widget.label.lang_value_map.empty()) {
644 SetError(kErrMsgNoLabel, error);
648 if (!app_widget.icon_src.empty()
649 && !IsValidPathOrUrl(app_widget.icon_src)) {
650 SetError(kErrMsgInvalidIconSrc, app_widget.icon_src, error);
654 if (!IsValidPathOrUrl(app_widget.content_src)) {
655 SetError(kErrMsgInvalidContentSrc, app_widget.content_src, error);
659 if (!ValidateContentSize(app_widget.content_size, error))
662 if (!app_widget.content_drop_view.empty()) {
663 const TizenAppWidgetDropView& drop_view
664 = app_widget.content_drop_view.front();
666 if (!IsValidPathOrUrl(drop_view.src)) {
667 SetError(kErrMsgInvalidContentDropViewSrc, drop_view.src, error);
671 if (drop_view.height < 1 || drop_view.height > 380) {
672 SetError(kErrMsgContentDropViewHeightOutOfDomain,
673 base::IntToString(drop_view.height), error);
682 std::vector<std::string> TizenAppWidgetHandler::Keys() const {
683 return std::vector<std::string>(1, keys::kTizenAppWidgetFullKey);
686 } // namespace application