Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/policy/core/common/schema.h" | 5 #include "components/policy/core/common/schema.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <climits> | 8 #include <climits> |
| 9 #include <map> | 9 #include <map> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 }; | 75 }; |
| 76 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { | 76 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { |
| 77 if (kSchemaToValueTypeMap[i].schema_type == type_string) { | 77 if (kSchemaToValueTypeMap[i].schema_type == type_string) { |
| 78 *type = kSchemaToValueTypeMap[i].value_type; | 78 *type = kSchemaToValueTypeMap[i].value_type; |
| 79 return true; | 79 return true; |
| 80 } | 80 } |
| 81 } | 81 } |
| 82 return false; | 82 return false; |
| 83 } | 83 } |
| 84 | 84 |
| 85 bool StrategyAllowInvalidOnTopLevel(SchemaOnErrorStrategy strategy) { | |
| 86 return strategy == SCHEMA_ALLOW_INVALID || | |
| 87 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL || | |
| 88 strategy == SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN; | |
| 89 } | |
| 90 | |
| 91 bool StrategyAllowUnknownOnTopLevel(SchemaOnErrorStrategy strategy) { | |
| 92 return strategy != SCHEMA_STRICT; | |
| 93 } | |
| 94 | |
| 95 SchemaOnErrorStrategy StrategyForNextLevel(SchemaOnErrorStrategy strategy) { | |
| 96 static SchemaOnErrorStrategy next_level_strategy[] = { | |
| 97 SCHEMA_STRICT, // SCHEMA_STRICT | |
| 98 SCHEMA_STRICT, // SCHEMA_ALLOW_UNKNOWN_TOPLEVEL | |
| 99 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_UNKNOWN | |
| 100 SCHEMA_STRICT, // SCHEMA_ALLOW_INVALID_TOPLEVEL | |
| 101 SCHEMA_ALLOW_UNKNOWN, // SCHEMA_ALLOW_INVALID_TOPLEVEL_AND_ALLOW_UNKNOWN | |
| 102 SCHEMA_ALLOW_INVALID, // SCHEMA_ALLOW_INVALID | |
| 103 }; | |
| 104 return next_level_strategy[(int)strategy]; | |
| 105 } | |
| 106 | |
| 85 } // namespace | 107 } // namespace |
| 86 | 108 |
| 87 // Contains the internal data representation of a Schema. This can either wrap | 109 // Contains the internal data representation of a Schema. This can either wrap |
| 88 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which | 110 // a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which |
| 89 // is generated at compile time), or it can own its own SchemaData. | 111 // is generated at compile time), or it can own its own SchemaData. |
| 90 class Schema::InternalStorage | 112 class Schema::InternalStorage |
| 91 : public base::RefCountedThreadSafe<InternalStorage> { | 113 : public base::RefCountedThreadSafe<InternalStorage> { |
| 92 public: | 114 public: |
| 93 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); | 115 static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data); |
| 94 | 116 |
| (...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 602 node_ = schema.node_; | 624 node_ = schema.node_; |
| 603 return *this; | 625 return *this; |
| 604 } | 626 } |
| 605 | 627 |
| 606 // static | 628 // static |
| 607 Schema Schema::Wrap(const SchemaData* data) { | 629 Schema Schema::Wrap(const SchemaData* data) { |
| 608 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); | 630 scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data); |
| 609 return Schema(storage, storage->root_node()); | 631 return Schema(storage, storage->root_node()); |
| 610 } | 632 } |
| 611 | 633 |
| 612 bool Schema::Validate(const base::Value& value) const { | 634 bool Schema::Validate(const base::Value& value, |
| 635 SchemaOnErrorStrategy strategy, | |
| 636 std::string *error) const { | |
|
Joao da Silva
2014/01/23 15:52:36
std::string* error
The style guide says that the
binjin
2014/01/23 17:20:13
Done.
| |
| 613 if (!valid()) { | 637 if (!valid()) { |
| 614 // Schema not found, invalid entry. | 638 *error = "The schema is invalid."; |
| 615 return false; | 639 return false; |
| 616 } | 640 } |
| 617 | 641 |
| 618 if (!value.IsType(type())) | 642 if (!value.IsType(type())) { |
| 643 *error = "The value type doesn't match the schema type."; | |
| 619 return false; | 644 return false; |
| 645 } | |
| 620 | 646 |
| 621 const base::DictionaryValue* dict = NULL; | 647 const base::DictionaryValue* dict = NULL; |
| 622 const base::ListValue* list = NULL; | 648 const base::ListValue* list = NULL; |
| 623 int int_value; | 649 int int_value; |
| 624 std::string str_value; | 650 std::string str_value; |
| 625 if (value.GetAsDictionary(&dict)) { | 651 if (value.GetAsDictionary(&dict)) { |
| 626 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | 652 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); |
| 627 it.Advance()) { | 653 it.Advance()) { |
| 628 if (!GetProperty(it.key()).Validate(it.value())) | 654 Schema subschema = GetProperty(it.key()); |
| 629 return false; | 655 if (!subschema.valid()) { |
| 656 // Unknown property was detected. | |
| 657 if (!StrategyAllowUnknownOnTopLevel(strategy)) { | |
| 658 *error = "Unknown property: " + it.key(); | |
| 659 return false; | |
| 660 } | |
| 661 } else { | |
| 662 std::string sub_error; | |
| 663 if (!subschema.Validate(it.value(), | |
| 664 StrategyForNextLevel(strategy), &sub_error)) { | |
|
Joao da Silva
2014/01/23 15:52:36
The style guide says that arguments should be alig
binjin
2014/01/23 17:20:13
Done.
| |
| 665 // Invalid property was detected. | |
| 666 if (!StrategyAllowInvalidOnTopLevel(strategy)) { | |
| 667 *error = sub_error; | |
| 668 return false; | |
| 669 } | |
| 670 } | |
| 671 } | |
| 630 } | 672 } |
| 631 } else if (value.GetAsList(&list)) { | 673 } else if (value.GetAsList(&list)) { |
| 632 for (base::ListValue::const_iterator it = list->begin(); | 674 for (base::ListValue::const_iterator it = list->begin(); |
| 633 it != list->end(); ++it) { | 675 it != list->end(); ++it) { |
| 634 if (!*it || !GetItems().Validate(**it)) | 676 if (!*it || !GetItems().Validate(**it, |
| 635 return false; | 677 StrategyForNextLevel(strategy), error)) { |
|
Joao da Silva
2014/01/23 15:52:36
Run clang-format on this line too
binjin
2014/01/23 17:20:13
Done.
| |
| 678 // Invalid list item was detected. | |
| 679 if (!StrategyAllowInvalidOnTopLevel(strategy)) | |
| 680 return false; | |
| 681 } | |
| 636 } | 682 } |
| 637 } else if (value.GetAsInteger(&int_value)) { | 683 } else if (value.GetAsInteger(&int_value)) { |
| 638 return node_->extra == kInvalid || | 684 if (node_->extra != kInvalid && |
| 639 ValidateIntegerRestriction(node_->extra, int_value); | 685 !ValidateIntegerRestriction(node_->extra, int_value)) { |
| 686 *error = "Invalid value for integer"; | |
| 687 return false; | |
| 688 } | |
| 640 } else if (value.GetAsString(&str_value)) { | 689 } else if (value.GetAsString(&str_value)) { |
| 641 return node_->extra == kInvalid || | 690 if (node_->extra != kInvalid && |
| 642 ValidateStringRestriction(node_->extra, str_value.c_str()); | 691 !ValidateStringRestriction(node_->extra, str_value.c_str())) { |
| 692 *error = "Invalid value for string"; | |
| 693 return false; | |
| 694 } | |
| 643 } | 695 } |
| 644 | 696 |
| 645 return true; | 697 return true; |
| 646 } | 698 } |
| 647 | 699 |
| 700 bool Schema::Normalize(base::Value* value, | |
| 701 SchemaOnErrorStrategy strategy, | |
| 702 std::string *error) const { | |
| 703 if (!valid()) { | |
| 704 *error = "The schema is invalid."; | |
| 705 return false; | |
| 706 } | |
| 707 | |
| 708 if (!value->IsType(type())) { | |
|
Joao da Silva
2014/01/23 15:52:36
We could handle INTEGER to DOUBLE promotion here,
binjin
2014/01/23 17:20:13
I added special check to allow Integer to Double p
| |
| 709 *error = "The value type doesn't match the schema type."; | |
| 710 return false; | |
| 711 } | |
| 712 | |
| 713 base::DictionaryValue* dict = NULL; | |
| 714 base::ListValue* list = NULL; | |
| 715 if (value->GetAsDictionary(&dict)) { | |
| 716 std::vector<std::string> drop_list; // Contains the keys to drop. | |
| 717 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); | |
| 718 it.Advance()) { | |
| 719 Schema subschema = GetProperty(it.key()); | |
| 720 if (!subschema.valid()) { | |
| 721 // Unknown property was detected. | |
| 722 if (StrategyAllowUnknownOnTopLevel(strategy)) { | |
| 723 drop_list.push_back(it.key()); | |
| 724 } else { | |
| 725 *error = "Unknown property: " + it.key(); | |
| 726 return false; | |
| 727 } | |
| 728 } else { | |
| 729 base::Value* sub_value = NULL; | |
| 730 dict->GetWithoutPathExpansion(it.key(), &sub_value); | |
| 731 std::string sub_error; | |
| 732 if (!subschema.Normalize(sub_value, | |
| 733 StrategyForNextLevel(strategy), &sub_error)) { | |
|
Joao da Silva
2014/01/23 15:52:36
Run clang-format on this line
binjin
2014/01/23 17:20:13
Done.
| |
| 734 // Invalid property was detected. | |
| 735 if (StrategyAllowInvalidOnTopLevel(strategy)) { | |
| 736 drop_list.push_back(it.key()); | |
| 737 } else { | |
| 738 *error = sub_error; | |
| 739 return false; | |
| 740 } | |
| 741 } | |
| 742 } | |
| 743 } | |
| 744 for (std::vector<std::string>::const_iterator it = drop_list.begin(); | |
| 745 it != drop_list.end(); ++it) { | |
| 746 dict->Remove(*it, NULL); | |
|
Joao da Silva
2014/01/23 15:52:36
RemoveWithoutPathExpansion
binjin
2014/01/23 17:20:13
Done.
| |
| 747 } | |
| 748 return true; | |
| 749 } else if (value->GetAsList(&list)) { | |
| 750 std::vector<size_t> drop_list; // Contains the indexes to drop. | |
| 751 for (size_t index = 0; index < list->GetSize(); index++) { | |
| 752 base::Value* sub_value = NULL; | |
| 753 std::string sub_error; | |
| 754 list->Get(index, &sub_value); | |
| 755 if (!sub_value || !GetItems().Normalize(sub_value, | |
| 756 StrategyForNextLevel(strategy), &sub_error)) { | |
|
Joao da Silva
2014/01/23 15:52:36
Run clang-format on this line
binjin
2014/01/23 17:20:13
Done.
| |
| 757 // Invalid list item was detected. | |
| 758 if (StrategyAllowInvalidOnTopLevel(strategy)) { | |
| 759 drop_list.push_back(index); | |
| 760 } else { | |
| 761 *error = sub_error; | |
| 762 return false; | |
| 763 } | |
| 764 } | |
| 765 } | |
| 766 for (std::vector<size_t>::reverse_iterator it = drop_list.rbegin(); | |
| 767 it != drop_list.rend(); ++it) { | |
| 768 list->Remove(*it, NULL); | |
|
Joao da Silva
2014/01/23 15:52:36
This is expensive, because it will search for the
binjin
2014/01/23 17:20:13
The type of *it is size_t, and I actually called L
Joao da Silva
2014/01/23 20:18:47
I was confused by the drop_list of the dictionary,
| |
| 769 } | |
| 770 return true; | |
| 771 } | |
| 772 | |
| 773 return Validate(*value, strategy, error); | |
| 774 } | |
| 775 | |
| 648 // static | 776 // static |
| 649 Schema Schema::Parse(const std::string& content, std::string* error) { | 777 Schema Schema::Parse(const std::string& content, std::string* error) { |
| 650 // Validate as a generic JSON schema, and ignore unknown attributes; they | 778 // Validate as a generic JSON schema, and ignore unknown attributes; they |
| 651 // may become used in a future version of the schema format. | 779 // may become used in a future version of the schema format. |
| 652 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( | 780 scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema( |
| 653 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); | 781 content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error); |
| 654 if (!dict) | 782 if (!dict) |
| 655 return Schema(); | 783 return Schema(); |
| 656 | 784 |
| 657 // Validate the main type. | 785 // Validate the main type. |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 751 const RestrictionNode* rnode = storage_->restriction(index); | 879 const RestrictionNode* rnode = storage_->restriction(index); |
| 752 for (int i = rnode->enumeration_restriction.offset_begin; | 880 for (int i = rnode->enumeration_restriction.offset_begin; |
| 753 i < rnode->enumeration_restriction.offset_end; i++) { | 881 i < rnode->enumeration_restriction.offset_end; i++) { |
| 754 if (strcmp(*storage_->string_enums(i), str) == 0) | 882 if (strcmp(*storage_->string_enums(i), str) == 0) |
| 755 return true; | 883 return true; |
| 756 } | 884 } |
| 757 return false; | 885 return false; |
| 758 } | 886 } |
| 759 | 887 |
| 760 } // namespace policy | 888 } // namespace policy |
| OLD | NEW |