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) {
109 if (!manifest.HasPath(key)) {
110 SetError(kErrMsgNoMandatoryKey, key, error);
113 if (!manifest.GetDictionary(key, dict) || !*dict) {
114 SetError(kErrMsgInvalidDictionary, key, error);
120 // Converts given text value to a value of specific type. Returns true
121 // if convertion is successful or false otherwise.
122 template <typename ValueType>
123 bool ConvertValue(const std::string& str_value, ValueType* value) {
124 NOTREACHED() << "Use one of already defined template specializations"
125 " or define a new one.";
129 // Converts given text value to a string value. Returns true
130 // if convertion is successful or false otherwise.
132 bool ConvertValue(const std::string& str_value, std::string* value) {
138 // Converts given text value to a boolean value. Returns true
139 // if convertion is successful or false otherwise.
141 bool ConvertValue(const std::string& str_value, bool* value) {
143 if (str_value == "true") {
147 if (str_value == "false") {
154 // Converts given text value to an integer value. Returns true
155 // if convertion is successful or false otherwise.
157 bool ConvertValue(const std::string& str_value, int* value) {
159 return base::StringToInt(str_value, value);
162 // Converts given text value to a floating point value. Returns true
163 // if convertion is successful or false otherwise.
165 bool ConvertValue(const std::string& str_value, double* value) {
167 return base::StringToDouble(str_value, value);
170 // Retrieves a mandatory value from specified dictionary and specified key.
171 // Returns true, if the value is found or false otherwise. If the error
172 // parameter is specified, it is also filled with proper message.
173 template <typename ValueType>
174 bool GetMandatoryValue(const base::DictionaryValue& dict,
175 const std::string& key, ValueType* value, base::string16* error) {
179 if (!dict.GetString(key, &tmp)) {
180 SetError(kErrMsgNoMandatoryKey, key, error);
184 bool result = ConvertValue(tmp, value);
186 SetError(kErrMsgInvalidKeyValue, key, error);
190 // Retrieves an optional value from specified dictionary and specified key.
191 // If the value is found, the function returns true and fills value
192 // parameter. If the value is not found, the function returns true and fills
193 // value parameter with default value. If an error occurs, it returns false
194 // and fills error parameter if it is set.
195 template <typename ValueType>
196 bool GetOptionalValue(const base::DictionaryValue& dict,
197 const std::string& key, ValueType default_value, ValueType* value,
198 base::string16* error) {
202 if (!dict.GetString(key, &tmp)) {
203 *value = default_value;
207 bool result = ConvertValue(tmp, value);
209 SetError(kErrMsgInvalidKeyValue, key, error);
213 // Helper function for ParseEach. Do not use directly.
214 template <typename ParseSingleType, typename DataContainerType>
215 bool ParseEachInternal(const base::Value& value, const std::string& key,
216 ParseSingleType parse_single, DataContainerType* data_container,
217 base::string16* error) {
218 DCHECK(data_container);
220 const base::DictionaryValue* inner_dict;
221 if (!value.GetAsDictionary(&inner_dict)) {
222 SetError(kErrMsgInvalidDictionary, key, error);
225 if (!parse_single(*inner_dict, key, data_container, error))
231 // Parsing helper function calling 'parse_single' for each dictionary contained
232 // in 'dict' under a 'key'. This helper function takes two template arguments:
233 // - a function with following prototype:
234 // bool ParseSingleExample(const base::Value& value, const std::string& key,
235 // DataContainerType* data_container, base::string16* error);
236 // - a DataContainerType object where the above function stores data
237 template <typename ParseSingleType, typename DataContainerType>
238 bool ParseEach(const base::DictionaryValue& dict, const std::string& key,
239 bool mandatory, ParseSingleType parse_single,
240 DataContainerType* data_container, base::string16* error) {
241 DCHECK(data_container);
243 const base::Value* value = nullptr;
244 if (!dict.Get(key, &value) || !value) {
246 SetError(kErrMsgNoMandatoryKey, key, error);
252 if (value->IsType(base::Value::TYPE_DICTIONARY)) {
253 if (!ParseEachInternal(*value, key, parse_single, data_container, error))
255 } else if (value->IsType(base::Value::TYPE_LIST)) {
256 const base::ListValue* list;
257 if (!value->GetAsList(&list)) {
258 SetError(kErrMsgInvalidList, key, error);
261 for (const base::Value* value : *list)
262 if (!ParseEachInternal(*value, key, parse_single, data_container, error))
269 // Verifies whether specified dictionary represents an element in specified
270 // namespace. Returns true, if the namespace is set and equal to the specified
271 // one or false otherwise. If the error parameter is specified, it is also
272 // filled with proper message.
273 bool VerifyElementNamespace(const base::DictionaryValue& dict,
274 const std::string& key, const std::string& desired_namespace_value,
275 base::string16* error) {
276 std::string namespace_value;
277 if (!GetMandatoryValue(dict, keys::kNamespaceKey,
278 &namespace_value, nullptr)) {
279 SetError(kErrMsgNoNamespace, key, error);
282 if (namespace_value != desired_namespace_value) {
283 SetError(kErrMsgInvalidNamespace, key, error);
289 // Parses box-label part
290 bool ParseLabel(const base::DictionaryValue& dict,
291 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
294 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
298 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxLabelLangKey,
299 std::string(), &lang, error))
303 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxLabelTextKey,
308 // Note: Tizen 2.2 WRT Core Spec does not determine how many times the value
309 // without lang attribute can appear in one app-widget, so overwrite.
310 app_widget->label.default_value = text;
312 // Note: Tizen 2.2 WRT Core Spec does not determine how many times the value
313 // with specific lang attribute can appear in one app-widget, so overwrite.
314 app_widget->label.lang_value_map[lang] = text;
320 // Parses box-icon part
321 bool ParseIcon(const base::DictionaryValue& dict,
322 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
325 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
328 if (!app_widget->icon_src.empty()) {
329 SetError(kErrMsgMultipleKeys, key, error);
332 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxIconSrcKey,
333 &app_widget->icon_src, error))
339 // Converts size type from text to enum representation
340 bool StringToSizeType(const std::string& str_type,
341 TizenAppWidgetSizeType* enum_type) {
343 if (str_type == "1x1") {
344 *enum_type = TizenAppWidgetSizeType::k1x1;
347 if (str_type == "2x1") {
348 *enum_type = TizenAppWidgetSizeType::k2x1;
351 if (str_type == "2x2") {
352 *enum_type = TizenAppWidgetSizeType::k2x2;
358 // Parses box-size part
359 bool ParseContentSizes(const base::DictionaryValue& dict,
360 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
363 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
366 TizenAppWidgetSize size;
368 std::string str_type;
369 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentSizeTextKey,
373 TizenAppWidgetSizeType type;
374 if (!StringToSizeType(str_type, &type)) {
375 SetError(kErrMsgInvalidKeyValue,
376 keys::kTizenAppWidgetBoxContentSizeTextKey, error);
381 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentSizePreviewKey,
382 std::string(), &size.preview, error))
385 if (!GetOptionalValue(dict,
386 keys::kTizenAppWidgetBoxContentSizeUseDecorationKey,
387 true, &size.use_decoration, error))
390 app_widget->content_size.push_back(size);
396 bool ParseContentDropView(const base::DictionaryValue& dict,
397 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
400 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
403 if (!app_widget->content_drop_view.empty()) {
404 SetError(kErrMsgMultipleKeys, key, error);
408 TizenAppWidgetDropView drop_view;
410 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentDropViewSrcKey,
411 &drop_view.src, error))
414 if (!GetMandatoryValue(dict,
415 keys::kTizenAppWidgetBoxContentDropViewWidthKey,
416 &drop_view.width, error))
419 if (!GetMandatoryValue(dict,
420 keys::kTizenAppWidgetBoxContentDropViewHeightKey,
421 &drop_view.height, error))
424 app_widget->content_drop_view.push_back(drop_view);
429 // Parses box-content part
430 bool ParseContent(const base::DictionaryValue& dict,
431 const std::string& key, TizenAppWidget* app_widget, base::string16* error) {
434 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
437 if (!app_widget->content_src.empty()) {
438 SetError(kErrMsgMultipleKeys, key, error);
441 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetBoxContentSrcKey,
442 &app_widget->content_src, error))
445 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentMouseEventKey,
446 false, &app_widget->content_mouse_event, error))
449 if (!GetOptionalValue(dict, keys::kTizenAppWidgetBoxContentTouchEffectKey,
450 true, &app_widget->content_touch_effect, error))
453 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentSizeKey,
454 true, ParseContentSizes, app_widget, error))
457 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentDropViewKey,
458 false, ParseContentDropView, app_widget, error))
464 // Parses app-widget part
465 bool ParseAppWidget(const base::DictionaryValue& dict,
466 const std::string& key, TizenAppWidgetVector* app_widgets,
467 base::string16* error) {
470 if (!VerifyElementNamespace(dict, key, keys::kTizenNamespacePrefix, error))
473 TizenAppWidget app_widget;
475 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetIdKey,
476 &app_widget.id, error))
479 if (!GetMandatoryValue(dict, keys::kTizenAppWidgetPrimaryKey,
480 &app_widget.primary, error))
483 double update_period;
484 double no_update_period = std::numeric_limits<double>::min();
485 if (!GetOptionalValue(dict, keys::kTizenAppWidgetUpdatePeriodKey,
486 no_update_period, &update_period, error))
488 if (update_period != no_update_period)
489 app_widget.update_period.push_back(update_period);
491 if (!GetOptionalValue(dict, keys::kTizenAppWidgetAutoLaunchKey,
492 false, &app_widget.auto_launch, error))
495 if (!ParseEach(dict, keys::kTizenAppWidgetBoxLabelKey,
496 true, ParseLabel, &app_widget, error))
499 if (!ParseEach(dict, keys::kTizenAppWidgetBoxIconKey,
500 false, ParseIcon, &app_widget, error))
503 if (!ParseEach(dict, keys::kTizenAppWidgetBoxContentKey,
504 true, ParseContent, &app_widget, error))
507 app_widgets->push_back(app_widget);
512 // Validates all app-widget ids
513 bool ValidateEachId(const TizenAppWidgetVector& app_widgets,
514 const std::string& app_id, std::string* error) {
515 std::set<std::string> unique_values;
517 for (const TizenAppWidget& app_widget : app_widgets) {
518 if (!unique_values.insert(app_widget.id).second) {
519 SetError(kErrMsgDuplicatedAppWidgetId, app_widget.id, error);
523 const size_t app_id_len = app_id.length();
525 if (app_widget.id.find(app_id) != 0) {
526 SetError(kErrMsgInvalidAppWidgetIdBeginning, app_widget.id, error);
530 const char kStringPattern[] = "[.][0-9a-zA-Z]+";
531 if (!RE2::FullMatch(app_widget.id.substr(app_id_len), kStringPattern)) {
532 SetError(kErrMsgInvalidAppWidgetIdFormat, app_widget.id, error);
540 // Tests if specified string represents valid remote url
541 bool IsValidUrl(const std::string& value) {
542 // TODO(tweglarski): implement me (it's not crucial atm)
546 // Tests if specified string represents valid path
547 bool IsValidPath(const std::string& value) {
548 // TODO(tweglarski): implement me (it's not crucial atm)
552 // Tests if specified string represents valid path or remote url
553 bool IsValidPathOrUrl(const std::string& value) {
554 return IsValidPath(value) || IsValidUrl(value);
557 // Validates all content sizes in an app-widget
558 bool ValidateContentSize(const TizenAppWidgetSizeVector& content_size,
559 std::string* error) {
560 bool mandatory_1x1_found = false;
562 for (const TizenAppWidgetSize& size : content_size) {
563 mandatory_1x1_found |= size.type == TizenAppWidgetSizeType::k1x1;
565 if (!size.preview.empty() && !IsValidPath(size.preview)) {
566 SetError(kErrMsgInvalidContentSizePreview, size.preview, error);
571 if (!mandatory_1x1_found) {
572 SetError(kErrMsgNoMandatoryContentSize1x1, error);
581 TizenAppWidgetInfo::TizenAppWidgetInfo(const TizenAppWidgetVector& app_widgets)
582 : app_widgets_(app_widgets) {
585 TizenAppWidgetInfo::~TizenAppWidgetInfo() {
588 TizenAppWidgetHandler::TizenAppWidgetHandler() {
591 TizenAppWidgetHandler::~TizenAppWidgetHandler() {
594 bool TizenAppWidgetHandler::Parse(scoped_refptr<ApplicationData> application,
595 base::string16* error) {
596 const Manifest* manifest = application->GetManifest();
599 const base::DictionaryValue* dict = nullptr;
600 if (!GetMandatoryDictionary(*manifest, keys::kTizenWidgetKey, &dict, error))
603 TizenAppWidgetVector app_widgets;
605 if (!ParseEach(*dict, keys::kTizenAppWidgetKey,
606 false, ParseAppWidget, &app_widgets, error))
609 scoped_ptr<TizenAppWidgetInfo> info(new TizenAppWidgetInfo(app_widgets));
610 application->SetManifestData(keys::kTizenAppWidgetFullKey, info.release());
615 bool TizenAppWidgetHandler::Validate(
616 scoped_refptr<const ApplicationData> application,
617 std::string* error) const {
618 const TizenAppWidgetInfo* app_widget_info =
619 static_cast<const TizenAppWidgetInfo*>(
620 application->GetManifestData(keys::kTizenAppWidgetFullKey));
621 const TizenApplicationInfo* app_info =
622 static_cast<const TizenApplicationInfo*>(
623 application->GetManifestData(keys::kTizenApplicationKey));
625 if (!app_widget_info) {
626 SetError(kErrMsgAppWidgetInfoNotFound, error);
630 SetError(kErrMsgApplicationInfoNotFound, error);
634 const TizenAppWidgetVector& app_widgets = app_widget_info->app_widgets();
636 if (!ValidateEachId(app_widgets, app_info->id(), error))
639 for (const TizenAppWidget& app_widget : app_widgets) {
640 if (!app_widget.update_period.empty()
641 && app_widget.update_period.front() < 1800) {
642 SetError(kErrMsgUpdatePeriodOutOfDomain,
643 base::DoubleToString(app_widget.update_period.front()), error);
647 if (app_widget.label.default_value.empty()
648 && app_widget.label.lang_value_map.empty()) {
649 SetError(kErrMsgNoLabel, error);
653 if (!app_widget.icon_src.empty()
654 && !IsValidPathOrUrl(app_widget.icon_src)) {
655 SetError(kErrMsgInvalidIconSrc, app_widget.icon_src, error);
659 if (!IsValidPathOrUrl(app_widget.content_src)) {
660 SetError(kErrMsgInvalidContentSrc, app_widget.content_src, error);
664 if (!ValidateContentSize(app_widget.content_size, error))
667 if (!app_widget.content_drop_view.empty()) {
668 const TizenAppWidgetDropView& drop_view
669 = app_widget.content_drop_view.front();
671 if (!IsValidPathOrUrl(drop_view.src)) {
672 SetError(kErrMsgInvalidContentDropViewSrc, drop_view.src, error);
676 if (drop_view.height < 1 || drop_view.height > 380) {
677 SetError(kErrMsgContentDropViewHeightOutOfDomain,
678 base::IntToString(drop_view.height), error);
687 std::vector<std::string> TizenAppWidgetHandler::Keys() const {
688 return std::vector<std::string>(1, keys::kTizenAppWidgetFullKey);
691 } // namespace application