| Index: components/policy/core/common/schema.cc
|
| diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
|
| index d50bd8abc4d031cd8c445f32e5cb0626f5d8ad84..7359938f3d9acc9e17754cbe3b111e0c3ed1f9f1 100644
|
| --- a/components/policy/core/common/schema.cc
|
| +++ b/components/policy/core/common/schema.cc
|
| @@ -8,15 +8,17 @@
|
| #include <climits>
|
| #include <map>
|
| #include <utility>
|
| -#include <vector>
|
|
|
| #include "base/compiler_specific.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| #include "base/memory/scoped_vector.h"
|
| +#include "base/stl_util.h"
|
| #include "base/strings/stringprintf.h"
|
| #include "components/json_schema/json_schema_constants.h"
|
| #include "components/json_schema/json_schema_validator.h"
|
| #include "components/policy/core/common/schema_internal.h"
|
| +#include "third_party/re2/re2/re2.h"
|
|
|
| namespace schema = json_schema_constants;
|
|
|
| @@ -175,6 +177,10 @@ class Schema::InternalStorage
|
| return schema_data_.string_enums + index;
|
| }
|
|
|
| + // Compiles regular expression |pattern|. The result is cached and will be
|
| + // returned directly next time.
|
| + re2::RE2* CompileRegex(const std::string& pattern) const;
|
| +
|
| private:
|
| friend class base::RefCountedThreadSafe<InternalStorage>;
|
|
|
| @@ -228,6 +234,10 @@ class Schema::InternalStorage
|
| SchemaNode* schema_node,
|
| std::string* error);
|
|
|
| + bool ParseStringPattern(const base::DictionaryValue& schema,
|
| + SchemaNode* schema_node,
|
| + std::string* error);
|
| +
|
| // Assigns the IDs in |id_map| to the pending references in the
|
| // |reference_list|. If an ID is missing then |error| is set and false is
|
| // returned; otherwise returns true.
|
| @@ -235,6 +245,11 @@ class Schema::InternalStorage
|
| const ReferenceList& reference_list,
|
| std::string* error);
|
|
|
| + // Cache for CompileRegex(), will memorize return value of every call to
|
| + // CompileRegex() and return results directly next time.
|
| + mutable std::map<std::string, re2::RE2*> regex_cache_;
|
| + STLValueDeleter<std::map<std::string, re2::RE2*> > regex_cache_deleter_;
|
| +
|
| SchemaData schema_data_;
|
| std::vector<std::string> strings_;
|
| std::vector<SchemaNode> schema_nodes_;
|
| @@ -247,7 +262,8 @@ class Schema::InternalStorage
|
| DISALLOW_COPY_AND_ASSIGN(InternalStorage);
|
| };
|
|
|
| -Schema::InternalStorage::InternalStorage() {}
|
| +Schema::InternalStorage::InternalStorage()
|
| + : regex_cache_deleter_(®ex_cache_) {}
|
|
|
| Schema::InternalStorage::~InternalStorage() {}
|
|
|
| @@ -324,6 +340,17 @@ Schema::InternalStorage::ParseSchema(const base::DictionaryValue& schema,
|
| return storage;
|
| }
|
|
|
| +re2::RE2* Schema::InternalStorage::CompileRegex(
|
| + const std::string& pattern) const {
|
| + std::map<std::string, re2::RE2*>::iterator it = regex_cache_.find(pattern);
|
| + if (it == regex_cache_.end()) {
|
| + re2::RE2* compiled = new re2::RE2(pattern);
|
| + regex_cache_[pattern] = compiled;
|
| + return compiled;
|
| + }
|
| + return it->second;
|
| +}
|
| +
|
| // static
|
| void Schema::InternalStorage::DetermineStorageSizes(
|
| const base::DictionaryValue& schema,
|
| @@ -366,6 +393,17 @@ void Schema::InternalStorage::DetermineStorageSizes(
|
| sizes->property_nodes++;
|
| }
|
| }
|
| +
|
| + const base::DictionaryValue* pattern_properties = NULL;
|
| + if (schema.GetDictionary(schema::kPatternProperties, &pattern_properties)) {
|
| + for (base::DictionaryValue::Iterator it(*pattern_properties);
|
| + !it.IsAtEnd(); it.Advance()) {
|
| + CHECK(it.value().GetAsDictionary(&dict));
|
| + DetermineStorageSizes(*dict, sizes);
|
| + sizes->strings++;
|
| + sizes->property_nodes++;
|
| + }
|
| + }
|
| } else if (schema.HasKey(schema::kEnum)) {
|
| const base::ListValue* possible_values = NULL;
|
| if (schema.GetList(schema::kEnum, &possible_values)) {
|
| @@ -380,6 +418,12 @@ void Schema::InternalStorage::DetermineStorageSizes(
|
| } else if (type == base::Value::TYPE_INTEGER) {
|
| if (schema.HasKey(schema::kMinimum) || schema.HasKey(schema::kMaximum))
|
| sizes->restriction_nodes++;
|
| + } else if (type == base::Value::TYPE_STRING) {
|
| + if (schema.HasKey(schema::kPattern)) {
|
| + sizes->strings++;
|
| + sizes->string_enums++;
|
| + sizes->restriction_nodes++;
|
| + }
|
| }
|
| }
|
|
|
| @@ -426,6 +470,9 @@ bool Schema::InternalStorage::Parse(const base::DictionaryValue& schema,
|
| } else if (schema.HasKey(schema::kEnum)) {
|
| if (!ParseEnum(schema, type, schema_node, error))
|
| return false;
|
| + } else if (schema.HasKey(schema::kPattern)) {
|
| + if (!ParseStringPattern(schema, schema_node, error))
|
| + return false;
|
| } else if (schema.HasKey(schema::kMinimum) ||
|
| schema.HasKey(schema::kMaximum)) {
|
| if (type != base::Value::TYPE_INTEGER) {
|
| @@ -455,8 +502,6 @@ bool Schema::InternalStorage::ParseDictionary(
|
| std::string* error) {
|
| int extra = static_cast<int>(properties_nodes_.size());
|
| properties_nodes_.push_back(PropertiesNode());
|
| - properties_nodes_[extra].begin = kInvalid;
|
| - properties_nodes_[extra].end = kInvalid;
|
| properties_nodes_[extra].additional = kInvalid;
|
| schema_node->extra = extra;
|
|
|
| @@ -468,15 +513,29 @@ bool Schema::InternalStorage::ParseDictionary(
|
| }
|
| }
|
|
|
| + properties_nodes_[extra].begin = static_cast<int>(property_nodes_.size());
|
| +
|
| const base::DictionaryValue* properties = NULL;
|
| if (schema.GetDictionary(schema::kProperties, &properties)) {
|
| - int base_index = static_cast<int>(property_nodes_.size());
|
| - // This reserves nodes for all of the |properties|, and makes sure they
|
| - // are contiguous. Recursive calls to Parse() will append after these
|
| + // This and below reserves nodes for all of the |properties|, and makes sure
|
| + // they are contiguous. Recursive calls to Parse() will append after these
|
| // elements.
|
| - property_nodes_.resize(base_index + properties->size());
|
| + property_nodes_.resize(property_nodes_.size() + properties->size());
|
| + }
|
|
|
| + properties_nodes_[extra].end = static_cast<int>(property_nodes_.size());
|
| +
|
| + const base::DictionaryValue* pattern_properties = NULL;
|
| + if (schema.GetDictionary(schema::kPatternProperties, &pattern_properties))
|
| + property_nodes_.resize(property_nodes_.size() + pattern_properties->size());
|
| +
|
| + properties_nodes_[extra].pattern_end =
|
| + static_cast<int>(property_nodes_.size());
|
| +
|
| + if (properties != NULL) {
|
| + int base_index = properties_nodes_[extra].begin;
|
| int index = base_index;
|
| +
|
| for (base::DictionaryValue::Iterator it(*properties);
|
| !it.IsAtEnd(); it.Advance(), ++index) {
|
| // This should have been verified by the JSONSchemaValidator.
|
| @@ -489,8 +548,35 @@ bool Schema::InternalStorage::ParseDictionary(
|
| }
|
| }
|
| CHECK_EQ(static_cast<int>(properties->size()), index - base_index);
|
| - properties_nodes_[extra].begin = base_index;
|
| - properties_nodes_[extra].end = index;
|
| + }
|
| +
|
| + if (pattern_properties != NULL) {
|
| + int base_index = properties_nodes_[extra].end;
|
| + int index = base_index;
|
| +
|
| + for (base::DictionaryValue::Iterator it(*pattern_properties);
|
| + !it.IsAtEnd(); it.Advance(), ++index) {
|
| + CHECK(it.value().GetAsDictionary(&dict));
|
| + re2::RE2* compiled_regex = CompileRegex(it.key());
|
| + if (!compiled_regex->ok()) {
|
| + *error =
|
| + "/" + it.key() + "/ is a invalid regex: " + compiled_regex->error();
|
| + return false;
|
| + }
|
| + strings_.push_back(it.key());
|
| + property_nodes_[index].key = strings_.back().c_str();
|
| + if (!Parse(*dict, &property_nodes_[index].schema,
|
| + id_map, reference_list, error)) {
|
| + return false;
|
| + }
|
| + }
|
| + CHECK_EQ(static_cast<int>(pattern_properties->size()), index - base_index);
|
| + }
|
| +
|
| + if (properties_nodes_[extra].begin == properties_nodes_[extra].pattern_end) {
|
| + properties_nodes_[extra].begin = kInvalid;
|
| + properties_nodes_[extra].end = kInvalid;
|
| + properties_nodes_[extra].pattern_end = kInvalid;
|
| }
|
|
|
| return true;
|
| @@ -582,6 +668,31 @@ bool Schema::InternalStorage::ParseRangedInt(
|
| return true;
|
| }
|
|
|
| +bool Schema::InternalStorage::ParseStringPattern(
|
| + const base::DictionaryValue& schema,
|
| + SchemaNode* schema_node,
|
| + std::string* error) {
|
| + std::string pattern;
|
| + if (!schema.GetString(schema::kPattern, &pattern)) {
|
| + *error = "Schema pattern must be a string.";
|
| + return false;
|
| + }
|
| + re2::RE2* compiled_regex = CompileRegex(pattern);
|
| + if (!compiled_regex->ok()) {
|
| + *error = "/" + pattern + "/ is invalid regex: " + compiled_regex->error();
|
| + return false;
|
| + }
|
| + int index = static_cast<int>(string_enums_.size());
|
| + strings_.push_back(pattern);
|
| + string_enums_.push_back(strings_.back().c_str());
|
| + schema_node->extra = static_cast<int>(restriction_nodes_.size());
|
| + restriction_nodes_.push_back(RestrictionNode());
|
| + restriction_nodes_.back().string_pattern_restriction.pattern_index = index;
|
| + restriction_nodes_.back().string_pattern_restriction.pattern_index_backup =
|
| + index;
|
| + return true;
|
| +}
|
| +
|
| // static
|
| bool Schema::InternalStorage::ResolveReferences(
|
| const IdMap& id_map,
|
| @@ -687,20 +798,25 @@ bool Schema::Validate(const base::Value& value,
|
| if (value.GetAsDictionary(&dict)) {
|
| for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
|
| it.Advance()) {
|
| - Schema subschema = GetProperty(it.key());
|
| - if (!subschema.valid()) {
|
| + SchemaList schema_list = GetMatchingProperties(it.key());
|
| + if (schema_list.empty()) {
|
| // Unknown property was detected.
|
| SchemaErrorFound(error_path, error, "Unknown property: " + it.key());
|
| if (!StrategyAllowUnknownOnTopLevel(strategy))
|
| return false;
|
| - } else if (!subschema.Validate(it.value(),
|
| - StrategyForNextLevel(strategy),
|
| - error_path,
|
| - error)) {
|
| - // Invalid property was detected.
|
| - AddDictKeyPrefixToPath(it.key(), error_path);
|
| - if (!StrategyAllowInvalidOnTopLevel(strategy))
|
| - return false;
|
| + } else {
|
| + for (SchemaList::iterator subschema = schema_list.begin();
|
| + subschema != schema_list.end(); ++subschema) {
|
| + if (!subschema->Validate(it.value(),
|
| + StrategyForNextLevel(strategy),
|
| + error_path,
|
| + error)) {
|
| + // Invalid property was detected.
|
| + AddDictKeyPrefixToPath(it.key(), error_path);
|
| + if (!StrategyAllowInvalidOnTopLevel(strategy))
|
| + return false;
|
| + }
|
| + }
|
| }
|
| }
|
| } else if (value.GetAsList(&list)) {
|
| @@ -763,8 +879,8 @@ bool Schema::Normalize(base::Value* value,
|
| std::vector<std::string> drop_list; // Contains the keys to drop.
|
| for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
|
| it.Advance()) {
|
| - Schema subschema = GetProperty(it.key());
|
| - if (!subschema.valid()) {
|
| + SchemaList schema_list = GetMatchingProperties(it.key());
|
| + if (schema_list.empty()) {
|
| // Unknown property was detected.
|
| SchemaErrorFound(error_path, error, "Unknown property: " + it.key());
|
| if (StrategyAllowUnknownOnTopLevel(strategy))
|
| @@ -772,19 +888,24 @@ bool Schema::Normalize(base::Value* value,
|
| else
|
| return false;
|
| } else {
|
| - base::Value* sub_value = NULL;
|
| - dict->GetWithoutPathExpansion(it.key(), &sub_value);
|
| - if (!subschema.Normalize(sub_value,
|
| - StrategyForNextLevel(strategy),
|
| - error_path,
|
| - error,
|
| - changed)) {
|
| - // Invalid property was detected.
|
| - AddDictKeyPrefixToPath(it.key(), error_path);
|
| - if (StrategyAllowInvalidOnTopLevel(strategy))
|
| - drop_list.push_back(it.key());
|
| - else
|
| - return false;
|
| + for (SchemaList::iterator subschema = schema_list.begin();
|
| + subschema != schema_list.end(); ++subschema) {
|
| + base::Value* sub_value = NULL;
|
| + dict->GetWithoutPathExpansion(it.key(), &sub_value);
|
| + if (!subschema->Normalize(sub_value,
|
| + StrategyForNextLevel(strategy),
|
| + error_path,
|
| + error,
|
| + changed)) {
|
| + // Invalid property was detected.
|
| + AddDictKeyPrefixToPath(it.key(), error_path);
|
| + if (StrategyAllowInvalidOnTopLevel(strategy)) {
|
| + drop_list.push_back(it.key());
|
| + break;
|
| + } else {
|
| + return false;
|
| + }
|
| + }
|
| }
|
| }
|
| }
|
| @@ -899,9 +1020,47 @@ Schema Schema::GetAdditionalProperties() const {
|
| return Schema(storage_, storage_->schema(node->additional));
|
| }
|
|
|
| +SchemaList Schema::GetPatternProperties(const std::string& key) const {
|
| + CHECK(valid());
|
| + CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
|
| + const PropertiesNode* node = storage_->properties(node_->extra);
|
| + const PropertyNode* begin = storage_->property(node->end);
|
| + const PropertyNode* end = storage_->property(node->pattern_end);
|
| + SchemaList matching_properties;
|
| + for (const PropertyNode* it = begin; it != end; ++it) {
|
| + if (re2::RE2::PartialMatch(key, *storage_->CompileRegex(it->key))) {
|
| + matching_properties.push_back(
|
| + Schema(storage_, storage_->schema(it->schema)));
|
| + }
|
| + }
|
| + return matching_properties;
|
| +}
|
| +
|
| Schema Schema::GetProperty(const std::string& key) const {
|
| Schema schema = GetKnownProperty(key);
|
| - return schema.valid() ? schema : GetAdditionalProperties();
|
| + if (schema.valid())
|
| + return schema;
|
| + return GetAdditionalProperties();
|
| +}
|
| +
|
| +SchemaList Schema::GetMatchingProperties(const std::string& key) const {
|
| + SchemaList schema_list;
|
| +
|
| + Schema known_property = GetKnownProperty(key);
|
| + if (known_property.valid())
|
| + schema_list.push_back(known_property);
|
| +
|
| + SchemaList pattern_properties = GetPatternProperties(key);
|
| + schema_list.insert(
|
| + schema_list.end(), pattern_properties.begin(), pattern_properties.end());
|
| +
|
| + if (schema_list.empty()) {
|
| + Schema additional_property = GetAdditionalProperties();
|
| + if (additional_property.valid())
|
| + schema_list.push_back(additional_property);
|
| + }
|
| +
|
| + return schema_list;
|
| }
|
|
|
| Schema Schema::GetItems() const {
|
| @@ -920,7 +1079,7 @@ bool Schema::ValidateIntegerRestriction(int index, int value) const {
|
| rnode->ranged_restriction.max_value >= value;
|
| } else {
|
| for (int i = rnode->enumeration_restriction.offset_begin;
|
| - i < rnode->enumeration_restriction.offset_end; i++) {
|
| + i < rnode->enumeration_restriction.offset_end; ++i) {
|
| if (*storage_->int_enums(i) == value)
|
| return true;
|
| }
|
| @@ -930,12 +1089,20 @@ bool Schema::ValidateIntegerRestriction(int index, int value) const {
|
|
|
| bool Schema::ValidateStringRestriction(int index, const char* str) const {
|
| const RestrictionNode* rnode = storage_->restriction(index);
|
| - for (int i = rnode->enumeration_restriction.offset_begin;
|
| - i < rnode->enumeration_restriction.offset_end; i++) {
|
| - if (strcmp(*storage_->string_enums(i), str) == 0)
|
| - return true;
|
| + if (rnode->enumeration_restriction.offset_begin <
|
| + rnode->enumeration_restriction.offset_end) {
|
| + for (int i = rnode->enumeration_restriction.offset_begin;
|
| + i < rnode->enumeration_restriction.offset_end; ++i) {
|
| + if (strcmp(*storage_->string_enums(i), str) == 0)
|
| + return true;
|
| + }
|
| + return false;
|
| + } else {
|
| + int index = rnode->string_pattern_restriction.pattern_index;
|
| + DCHECK(index == rnode->string_pattern_restriction.pattern_index_backup);
|
| + re2::RE2* regex = storage_->CompileRegex(*storage_->string_enums(index));
|
| + return re2::RE2::PartialMatch(str, *regex);
|
| }
|
| - return false;
|
| }
|
|
|
| } // namespace policy
|
|
|