diff options
author | Andras Becsi <andras.becsi@digia.com> | 2013-12-11 21:33:03 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@digia.com> | 2013-12-13 12:34:07 +0100 |
commit | f2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch) | |
tree | 0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/components/policy/core | |
parent | 5362912cdb5eea702b68ebe23702468d17c3017a (diff) | |
download | qtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz |
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f
Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/components/policy/core')
9 files changed, 1352 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/policy_pref_names.cc b/chromium/components/policy/core/common/policy_pref_names.cc new file mode 100644 index 00000000000..ca86fcd0870 --- /dev/null +++ b/chromium/components/policy/core/common/policy_pref_names.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/policy_pref_names.h" + +namespace policy { +namespace prefs { + +// 64-bit serialization of the time last policy usage statistics were collected +// by UMA_HISTOGRAM_ENUMERATION. +const char kLastPolicyStatisticsUpdate[] = "policy.last_statistics_update"; + +} // namespace prefs +} // namespace policy diff --git a/chromium/components/policy/core/common/policy_pref_names.h b/chromium/components/policy/core/common/policy_pref_names.h new file mode 100644 index 00000000000..0a65262fec0 --- /dev/null +++ b/chromium/components/policy/core/common/policy_pref_names.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_PREF_NAMES_H_ +#define COMPONENTS_POLICY_CORE_COMMON_PREF_NAMES_H_ + +#include "components/policy/policy_export.h" + +namespace policy { +namespace prefs { + +// Constants for the names of policy-related preferences. +// TODO(dconnelly): remove POLICY_EXPORT once the statistics collector moves +// to the policy component (crbug.com/271392). +POLICY_EXPORT extern const char kLastPolicyStatisticsUpdate[]; + +} // prefs +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_PREF_NAMES_H_ diff --git a/chromium/components/policy/core/common/policy_schema.cc b/chromium/components/policy/core/common/policy_schema.cc new file mode 100644 index 00000000000..8c3145ce43a --- /dev/null +++ b/chromium/components/policy/core/common/policy_schema.cc @@ -0,0 +1,244 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/policy_schema.h" + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/json_schema/json_schema_constants.h" +#include "components/json_schema/json_schema_validator.h" + +namespace policy { + +namespace { + +const char kJSONSchemaVersion[] = "http://json-schema.org/draft-03/schema#"; + +// Describes the properties of a TYPE_DICTIONARY policy schema. +class DictionaryPolicySchema : public PolicySchema { + public: + static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema, + std::string* error); + + virtual ~DictionaryPolicySchema(); + + virtual const PolicySchemaMap* GetProperties() const OVERRIDE; + virtual const PolicySchema* GetSchemaForAdditionalProperties() const OVERRIDE; + + private: + DictionaryPolicySchema(); + + PolicySchemaMap properties_; + scoped_ptr<PolicySchema> additional_properties_; + + DISALLOW_COPY_AND_ASSIGN(DictionaryPolicySchema); +}; + +// Describes the items of a TYPE_LIST policy schema. +class ListPolicySchema : public PolicySchema { + public: + static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema, + std::string* error); + + virtual ~ListPolicySchema(); + + virtual const PolicySchema* GetSchemaForItems() const OVERRIDE; + + private: + ListPolicySchema(); + + scoped_ptr<PolicySchema> items_schema_; + + DISALLOW_COPY_AND_ASSIGN(ListPolicySchema); +}; + +bool SchemaTypeToValueType(const std::string& type_string, + base::Value::Type* type) { + // Note: "any" is not an accepted type. + static const struct { + const char* schema_type; + base::Value::Type value_type; + } kSchemaToValueTypeMap[] = { + { json_schema_constants::kArray, base::Value::TYPE_LIST }, + { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN }, + { json_schema_constants::kInteger, base::Value::TYPE_INTEGER }, + { json_schema_constants::kNull, base::Value::TYPE_NULL }, + { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE }, + { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY }, + { json_schema_constants::kString, base::Value::TYPE_STRING }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { + if (kSchemaToValueTypeMap[i].schema_type == type_string) { + *type = kSchemaToValueTypeMap[i].value_type; + return true; + } + } + return false; +} + +scoped_ptr<PolicySchema> ParseSchema(const base::DictionaryValue& schema, + std::string* error) { + std::string type_string; + if (!schema.GetString(json_schema_constants::kType, &type_string)) { + *error = "The schema type must be declared."; + return scoped_ptr<PolicySchema>(); + } + + base::Value::Type type = base::Value::TYPE_NULL; + if (!SchemaTypeToValueType(type_string, &type)) { + *error = "The \"any\" type can't be used."; + return scoped_ptr<PolicySchema>(); + } + + switch (type) { + case base::Value::TYPE_DICTIONARY: + return DictionaryPolicySchema::Parse(schema, error); + case base::Value::TYPE_LIST: + return ListPolicySchema::Parse(schema, error); + default: + return make_scoped_ptr(new PolicySchema(type)); + } +} + +DictionaryPolicySchema::DictionaryPolicySchema() + : PolicySchema(base::Value::TYPE_DICTIONARY) {} + +DictionaryPolicySchema::~DictionaryPolicySchema() { + STLDeleteValues(&properties_); +} + +const PolicySchemaMap* DictionaryPolicySchema::GetProperties() const { + return &properties_; +} + +const PolicySchema* + DictionaryPolicySchema::GetSchemaForAdditionalProperties() const { + return additional_properties_.get(); +} + +// static +scoped_ptr<PolicySchema> DictionaryPolicySchema::Parse( + const base::DictionaryValue& schema, + std::string* error) { + scoped_ptr<DictionaryPolicySchema> dict_schema(new DictionaryPolicySchema()); + + const base::DictionaryValue* dict = NULL; + const base::DictionaryValue* properties = NULL; + if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) { + for (base::DictionaryValue::Iterator it(*properties); + !it.IsAtEnd(); it.Advance()) { + // This should have been verified by the JSONSchemaValidator. + CHECK(it.value().GetAsDictionary(&dict)); + scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error); + if (!sub_schema) + return scoped_ptr<PolicySchema>(); + dict_schema->properties_[it.key()] = sub_schema.release(); + } + } + + if (schema.GetDictionary(json_schema_constants::kAdditionalProperties, + &dict)) { + scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error); + if (!sub_schema) + return scoped_ptr<PolicySchema>(); + dict_schema->additional_properties_ = sub_schema.Pass(); + } + + return dict_schema.PassAs<PolicySchema>(); +} + +ListPolicySchema::ListPolicySchema() + : PolicySchema(base::Value::TYPE_LIST) {} + +ListPolicySchema::~ListPolicySchema() {} + +const PolicySchema* ListPolicySchema::GetSchemaForItems() const { + return items_schema_.get(); +} + +scoped_ptr<PolicySchema> ListPolicySchema::Parse( + const base::DictionaryValue& schema, + std::string* error) { + const base::DictionaryValue* dict = NULL; + if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) { + *error = "Arrays must declare a single schema for their items."; + return scoped_ptr<PolicySchema>(); + } + scoped_ptr<PolicySchema> items_schema = ParseSchema(*dict, error); + if (!items_schema) + return scoped_ptr<PolicySchema>(); + + scoped_ptr<ListPolicySchema> list_schema(new ListPolicySchema()); + list_schema->items_schema_ = items_schema.Pass(); + return list_schema.PassAs<PolicySchema>(); +} + +} // namespace + +PolicySchema::PolicySchema(base::Value::Type type) + : type_(type) {} + +PolicySchema::~PolicySchema() {} + +const PolicySchemaMap* PolicySchema::GetProperties() const { + NOTREACHED(); + return NULL; +} + +const PolicySchema* PolicySchema::GetSchemaForAdditionalProperties() const { + NOTREACHED(); + return NULL; +} + +const PolicySchema* PolicySchema::GetSchemaForProperty( + const std::string& key) const { + const PolicySchemaMap* properties = GetProperties(); + PolicySchemaMap::const_iterator it = properties->find(key); + return it == properties->end() ? GetSchemaForAdditionalProperties() + : it->second; +} + +const PolicySchema* PolicySchema::GetSchemaForItems() const { + NOTREACHED(); + return NULL; +} + +// static +scoped_ptr<PolicySchema> PolicySchema::Parse(const std::string& content, + std::string* error) { + // Validate as a generic JSON schema. + scoped_ptr<base::DictionaryValue> dict = + JSONSchemaValidator::IsValidSchema(content, error); + if (!dict) + return scoped_ptr<PolicySchema>(); + + // Validate the schema version. + std::string string_value; + if (!dict->GetString(json_schema_constants::kSchema, &string_value) || + string_value != kJSONSchemaVersion) { + *error = "Must declare JSON Schema v3 version in \"$schema\"."; + return scoped_ptr<PolicySchema>(); + } + + // Validate the main type. + if (!dict->GetString(json_schema_constants::kType, &string_value) || + string_value != json_schema_constants::kObject) { + *error = + "The main schema must have a type attribute with \"object\" value."; + return scoped_ptr<PolicySchema>(); + } + + // Checks for invalid attributes at the top-level. + if (dict->HasKey(json_schema_constants::kAdditionalProperties) || + dict->HasKey(json_schema_constants::kPatternProperties)) { + *error = "\"additionalProperties\" and \"patternProperties\" are not " + "supported at the main schema."; + return scoped_ptr<PolicySchema>(); + } + + return ParseSchema(*dict, error); +} + +} // namespace policy diff --git a/chromium/components/policy/core/common/policy_schema.h b/chromium/components/policy/core/common/policy_schema.h new file mode 100644 index 00000000000..c48ee6fc2b9 --- /dev/null +++ b/chromium/components/policy/core/common/policy_schema.h @@ -0,0 +1,70 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEMA_H_ +#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEMA_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "components/policy/policy_export.h" + +namespace policy { + +class PolicySchema; +typedef std::map<std::string, PolicySchema*> PolicySchemaMap; + +// Maps known policy keys to their expected types, and recursively describes +// the known keys within dictionary or list types. +class POLICY_EXPORT PolicySchema { + public: + + // Parses |schema| as a JSON v3 schema, and additionally verifies that: + // - the version is JSON schema v3; + // - the top-level entry is of type "object"; + // - the top-level object doesn't contain "additionalProperties" nor + // "patternProperties"; + // - each "property" maps to a schema with one "type"; + // - the type "any" is not used. + // If all the checks pass then the parsed PolicySchema is returned; otherwise + // returns NULL. + static scoped_ptr<PolicySchema> Parse(const std::string& schema, + std::string* error); + + explicit PolicySchema(base::Value::Type type); + virtual ~PolicySchema(); + + // Returns the expected type for this policy. At the top-level PolicySchema + // this is always TYPE_DICTIONARY. + base::Value::Type type() const { return type_; } + + // It is invalid to call these methods when type() is not TYPE_DICTIONARY. + // + // GetProperties() returns a map of the known property names to their schemas; + // the map is never NULL. + // GetSchemaForAdditionalProperties() returns the schema that should be used + // for keys not found in the map, and may be NULL. + // GetSchemaForProperty() is a utility method that combines both, returning + // the mapped schema if found in GetProperties(), otherwise returning + // GetSchemaForAdditionalProperties(). + virtual const PolicySchemaMap* GetProperties() const; + virtual const PolicySchema* GetSchemaForAdditionalProperties() const; + const PolicySchema* GetSchemaForProperty(const std::string& key) const; + + // It is invalid to call this method when type() is not TYPE_LIST. + // Returns the type of the entries of this "array", which is never NULL. + virtual const PolicySchema* GetSchemaForItems() const; + + private: + const base::Value::Type type_; + + DISALLOW_COPY_AND_ASSIGN(PolicySchema); +}; + +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEMA_H_ diff --git a/chromium/components/policy/core/common/policy_schema_unittest.cc b/chromium/components/policy/core/common/policy_schema_unittest.cc new file mode 100644 index 00000000000..bfbdd652159 --- /dev/null +++ b/chromium/components/policy/core/common/policy_schema_unittest.cc @@ -0,0 +1,193 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/policy_schema.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +namespace { + +#define SCHEMA_VERSION "\"$schema\":\"http://json-schema.org/draft-03/schema#\"" +#define OBJECT_TYPE "\"type\":\"object\"" + +bool ParseFails(const std::string& content) { + std::string error; + scoped_ptr<PolicySchema> schema = PolicySchema::Parse(content, &error); + EXPECT_TRUE(schema || !error.empty()); + return !schema; +} + +} // namespace + +TEST(PolicySchemaTest, MinimalSchema) { + EXPECT_FALSE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE + "}")); +} + +TEST(PolicySchemaTest, InvalidSchemas) { + EXPECT_TRUE(ParseFails("")); + EXPECT_TRUE(ParseFails("omg")); + EXPECT_TRUE(ParseFails("\"omg\"")); + EXPECT_TRUE(ParseFails("123")); + EXPECT_TRUE(ParseFails("[]")); + EXPECT_TRUE(ParseFails("null")); + EXPECT_TRUE(ParseFails("{}")); + EXPECT_TRUE(ParseFails("{" SCHEMA_VERSION "}")); + EXPECT_TRUE(ParseFails("{" OBJECT_TYPE "}")); + + EXPECT_TRUE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"additionalProperties\": { \"type\":\"object\" }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": \"bogus\" } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": \"any\" } }" + "}")); +} + +TEST(PolicySchemaTest, ValidSchema) { + std::string error; + scoped_ptr<PolicySchema> schema = PolicySchema::Parse( + "{" + SCHEMA_VERSION "," + OBJECT_TYPE "," + "\"properties\": {" + " \"Boolean\": { \"type\": \"boolean\" }," + " \"Integer\": { \"type\": \"integer\" }," + " \"Null\": { \"type\": \"null\" }," + " \"Number\": { \"type\": \"number\" }," + " \"String\": { \"type\": \"string\" }," + " \"Array\": {" + " \"type\": \"array\"," + " \"items\": { \"type\": \"string\" }" + " }," + " \"ArrayOfObjects\": {" + " \"type\": \"array\"," + " \"items\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"one\": { \"type\": \"string\" }," + " \"two\": { \"type\": \"integer\" }" + " }" + " }" + " }," + " \"ArrayOfArray\": {" + " \"type\": \"array\"," + " \"items\": {" + " \"type\": \"array\"," + " \"items\": { \"type\": \"string\" }" + " }" + " }," + " \"Object\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"one\": { \"type\": \"boolean\" }," + " \"two\": { \"type\": \"integer\" }" + " }," + " \"additionalProperties\": { \"type\": \"string\" }" + " }" + "}" + "}", &error); + ASSERT_TRUE(schema) << error; + + ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema->type()); + EXPECT_FALSE(schema->GetSchemaForProperty("invalid")); + + const PolicySchema* sub = schema->GetSchemaForProperty("Boolean"); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub->type()); + + sub = schema->GetSchemaForProperty("Integer"); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_INTEGER, sub->type()); + + sub = schema->GetSchemaForProperty("Null"); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_NULL, sub->type()); + + sub = schema->GetSchemaForProperty("Number"); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_DOUBLE, sub->type()); + sub = schema->GetSchemaForProperty("String"); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_STRING, sub->type()); + + sub = schema->GetSchemaForProperty("Array"); + ASSERT_TRUE(sub); + ASSERT_EQ(base::Value::TYPE_LIST, sub->type()); + sub = sub->GetSchemaForItems(); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_STRING, sub->type()); + + sub = schema->GetSchemaForProperty("ArrayOfObjects"); + ASSERT_TRUE(sub); + ASSERT_EQ(base::Value::TYPE_LIST, sub->type()); + sub = sub->GetSchemaForItems(); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub->type()); + const PolicySchema* subsub = sub->GetSchemaForProperty("one"); + ASSERT_TRUE(subsub); + EXPECT_EQ(base::Value::TYPE_STRING, subsub->type()); + subsub = sub->GetSchemaForProperty("two"); + ASSERT_TRUE(subsub); + EXPECT_EQ(base::Value::TYPE_INTEGER, subsub->type()); + subsub = sub->GetSchemaForProperty("invalid"); + EXPECT_FALSE(subsub); + + sub = schema->GetSchemaForProperty("ArrayOfArray"); + ASSERT_TRUE(sub); + ASSERT_EQ(base::Value::TYPE_LIST, sub->type()); + sub = sub->GetSchemaForItems(); + ASSERT_TRUE(sub); + ASSERT_EQ(base::Value::TYPE_LIST, sub->type()); + sub = sub->GetSchemaForItems(); + ASSERT_TRUE(sub); + EXPECT_EQ(base::Value::TYPE_STRING, sub->type()); + + sub = schema->GetSchemaForProperty("Object"); + ASSERT_TRUE(sub); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub->type()); + subsub = sub->GetSchemaForProperty("one"); + ASSERT_TRUE(subsub); + EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub->type()); + subsub = sub->GetSchemaForProperty("two"); + ASSERT_TRUE(subsub); + EXPECT_EQ(base::Value::TYPE_INTEGER, subsub->type()); + subsub = sub->GetSchemaForProperty("undeclared"); + ASSERT_TRUE(subsub); + EXPECT_EQ(base::Value::TYPE_STRING, subsub->type()); +} + +} // namespace policy diff --git a/chromium/components/policy/core/common/schema.cc b/chromium/components/policy/core/common/schema.cc new file mode 100644 index 00000000000..cbfde46faf3 --- /dev/null +++ b/chromium/components/policy/core/common/schema.cc @@ -0,0 +1,276 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/schema.h" + +#include <algorithm> + +#include "base/compiler_specific.h" +#include "base/logging.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" + +namespace policy { + +namespace { + +bool SchemaTypeToValueType(const std::string& type_string, + base::Value::Type* type) { + // Note: "any" is not an accepted type. + static const struct { + const char* schema_type; + base::Value::Type value_type; + } kSchemaToValueTypeMap[] = { + { json_schema_constants::kArray, base::Value::TYPE_LIST }, + { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN }, + { json_schema_constants::kInteger, base::Value::TYPE_INTEGER }, + { json_schema_constants::kNull, base::Value::TYPE_NULL }, + { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE }, + { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY }, + { json_schema_constants::kString, base::Value::TYPE_STRING }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { + if (kSchemaToValueTypeMap[i].schema_type == type_string) { + *type = kSchemaToValueTypeMap[i].value_type; + return true; + } + } + return false; +} + +} // namespace + +Schema::Iterator::Iterator(const internal::PropertiesNode* properties) + : it_(properties->begin), + end_(properties->end) {} + +Schema::Iterator::Iterator(const Iterator& iterator) + : it_(iterator.it_), + end_(iterator.end_) {} + +Schema::Iterator::~Iterator() {} + +Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) { + it_ = iterator.it_; + end_ = iterator.end_; + return *this; +} + +bool Schema::Iterator::IsAtEnd() const { + return it_ == end_; +} + +void Schema::Iterator::Advance() { + ++it_; +} + +const char* Schema::Iterator::key() const { + return it_->key; +} + +Schema Schema::Iterator::schema() const { + return Schema(it_->schema); +} + +Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {} + +Schema::Schema(const Schema& schema) : schema_(schema.schema_) {} + +Schema& Schema::operator=(const Schema& schema) { + schema_ = schema.schema_; + return *this; +} + +base::Value::Type Schema::type() const { + CHECK(valid()); + return schema_->type; +} + +Schema::Iterator Schema::GetPropertiesIterator() const { + CHECK(valid()); + CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); + return Iterator( + static_cast<const internal::PropertiesNode*>(schema_->extra)); +} + +namespace { + +bool CompareKeys(const internal::PropertyNode& node, const std::string& key) { + return node.key < key; +} + +} // namespace + +Schema Schema::GetKnownProperty(const std::string& key) const { + CHECK(valid()); + CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); + const internal::PropertiesNode* properties_node = + static_cast<const internal::PropertiesNode*>(schema_->extra); + const internal::PropertyNode* it = std::lower_bound( + properties_node->begin, properties_node->end, key, CompareKeys); + if (it != properties_node->end && it->key == key) + return Schema(it->schema); + return Schema(NULL); +} + +Schema Schema::GetAdditionalProperties() const { + CHECK(valid()); + CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); + return Schema( + static_cast<const internal::PropertiesNode*>(schema_->extra)->additional); +} + +Schema Schema::GetProperty(const std::string& key) const { + Schema schema = GetKnownProperty(key); + return schema.valid() ? schema : GetAdditionalProperties(); +} + +Schema Schema::GetItems() const { + CHECK(valid()); + CHECK_EQ(base::Value::TYPE_LIST, type()); + return Schema(static_cast<const internal::SchemaNode*>(schema_->extra)); +} + +SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {} + +SchemaOwner::~SchemaOwner() { + for (size_t i = 0; i < property_nodes_.size(); ++i) + delete[] property_nodes_[i]; +} + +// static +scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) { + return scoped_ptr<SchemaOwner>(new SchemaOwner(schema)); +} + +// static +scoped_ptr<SchemaOwner> SchemaOwner::Parse(const std::string& content, + std::string* error) { + // Validate as a generic JSON schema. + scoped_ptr<base::DictionaryValue> dict = + JSONSchemaValidator::IsValidSchema(content, error); + if (!dict) + return scoped_ptr<SchemaOwner>(); + + // Validate the main type. + std::string string_value; + if (!dict->GetString(json_schema_constants::kType, &string_value) || + string_value != json_schema_constants::kObject) { + *error = + "The main schema must have a type attribute with \"object\" value."; + return scoped_ptr<SchemaOwner>(); + } + + // Checks for invalid attributes at the top-level. + if (dict->HasKey(json_schema_constants::kAdditionalProperties) || + dict->HasKey(json_schema_constants::kPatternProperties)) { + *error = "\"additionalProperties\" and \"patternProperties\" are not " + "supported at the main schema."; + return scoped_ptr<SchemaOwner>(); + } + + scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL)); + impl->root_ = impl->Parse(*dict, error); + if (!impl->root_) + impl.reset(); + return impl.PassAs<SchemaOwner>(); +} + +const internal::SchemaNode* SchemaOwner::Parse( + const base::DictionaryValue& schema, + std::string* error) { + std::string type_string; + if (!schema.GetString(json_schema_constants::kType, &type_string)) { + *error = "The schema type must be declared."; + return NULL; + } + + base::Value::Type type = base::Value::TYPE_NULL; + if (!SchemaTypeToValueType(type_string, &type)) { + *error = "Type not supported: " + type_string; + return NULL; + } + + if (type == base::Value::TYPE_DICTIONARY) + return ParseDictionary(schema, error); + if (type == base::Value::TYPE_LIST) + return ParseList(schema, error); + + internal::SchemaNode* node = new internal::SchemaNode; + node->type = type; + node->extra = NULL; + schema_nodes_.push_back(node); + return node; +} + +const internal::SchemaNode* SchemaOwner::ParseDictionary( + const base::DictionaryValue& schema, + std::string* error) { + internal::PropertiesNode* properties_node = new internal::PropertiesNode; + properties_node->begin = NULL; + properties_node->end = NULL; + properties_node->additional = NULL; + properties_nodes_.push_back(properties_node); + + const base::DictionaryValue* dict = NULL; + const base::DictionaryValue* properties = NULL; + if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) { + internal::PropertyNode* property_nodes = + new internal::PropertyNode[properties->size()]; + property_nodes_.push_back(property_nodes); + + size_t index = 0; + for (base::DictionaryValue::Iterator it(*properties); + !it.IsAtEnd(); it.Advance(), ++index) { + // This should have been verified by the JSONSchemaValidator. + CHECK(it.value().GetAsDictionary(&dict)); + const internal::SchemaNode* sub_schema = Parse(*dict, error); + if (!sub_schema) + return NULL; + std::string* key = new std::string(it.key()); + keys_.push_back(key); + property_nodes[index].key = key->c_str(); + property_nodes[index].schema = sub_schema; + } + CHECK_EQ(properties->size(), index); + properties_node->begin = property_nodes; + properties_node->end = property_nodes + index; + } + + if (schema.GetDictionary(json_schema_constants::kAdditionalProperties, + &dict)) { + const internal::SchemaNode* sub_schema = Parse(*dict, error); + if (!sub_schema) + return NULL; + properties_node->additional = sub_schema; + } + + internal::SchemaNode* schema_node = new internal::SchemaNode; + schema_node->type = base::Value::TYPE_DICTIONARY; + schema_node->extra = properties_node; + schema_nodes_.push_back(schema_node); + return schema_node; +} + +const internal::SchemaNode* SchemaOwner::ParseList( + const base::DictionaryValue& schema, + std::string* error) { + const base::DictionaryValue* dict = NULL; + if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) { + *error = "Arrays must declare a single schema for their items."; + return NULL; + } + const internal::SchemaNode* items_schema_node = Parse(*dict, error); + if (!items_schema_node) + return NULL; + + internal::SchemaNode* schema_node = new internal::SchemaNode; + schema_node->type = base::Value::TYPE_LIST; + schema_node->extra = items_schema_node; + schema_nodes_.push_back(schema_node); + return schema_node; +} + +} // namespace policy diff --git a/chromium/components/policy/core/common/schema.h b/chromium/components/policy/core/common/schema.h new file mode 100644 index 00000000000..a81b7adae30 --- /dev/null +++ b/chromium/components/policy/core/common/schema.h @@ -0,0 +1,150 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_ +#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/values.h" +#include "components/policy/policy_export.h" + +namespace policy { +namespace internal { + +struct POLICY_EXPORT SchemaNode; +struct POLICY_EXPORT PropertyNode; +struct POLICY_EXPORT PropertiesNode; + +} // namespace internal + +// Describes the expected type of one policy. Also recursively describes the +// types of inner elements, for structured types. +// Objects of this class refer to external, immutable data and are cheap to +// copy. +// Use the SchemaOwner class to parse a schema and get Schema objects. +class POLICY_EXPORT Schema { + public: + explicit Schema(const internal::SchemaNode* schema); + Schema(const Schema& schema); + + Schema& operator=(const Schema& schema); + + // Returns true if this Schema is valid. Schemas returned by the methods below + // may be invalid, and in those cases the other methods must not be used. + bool valid() const { return schema_ != NULL; } + + base::Value::Type type() const; + + // Used to iterate over the known properties of TYPE_DICTIONARY schemas. + class POLICY_EXPORT Iterator { + public: + explicit Iterator(const internal::PropertiesNode* properties); + Iterator(const Iterator& iterator); + ~Iterator(); + + Iterator& operator=(const Iterator& iterator); + + // The other methods must not be called if the iterator is at the end. + bool IsAtEnd() const; + + // Advances the iterator to the next property. + void Advance(); + + // Returns the name of the current property. + const char* key() const; + + // Returns the Schema for the current property. This Schema is always valid. + Schema schema() const; + + private: + const internal::PropertyNode* it_; + const internal::PropertyNode* end_; + }; + + // These methods should be called only if type() == TYPE_DICTIONARY, + // otherwise invalid memory will be read. A CHECK is currently enforcing this. + + // Returns an iterator that goes over the named properties of this schema. + // The returned iterator is at the beginning. + Iterator GetPropertiesIterator() const; + + // Returns the Schema for the property named |key|. If |key| is not a known + // property name then the returned Schema is not valid. + Schema GetKnownProperty(const std::string& key) const; + + // Returns the Schema for additional properties. If additional properties are + // not allowed for this Schema then the Schema returned is not valid. + Schema GetAdditionalProperties() const; + + // Returns the Schema for |key| if it is a known property, otherwise returns + // the Schema for additional properties. + Schema GetProperty(const std::string& key) const; + + // Returns the Schema for items of an array. + // This method should be called only if type() == TYPE_LIST, + // otherwise invalid memory will be read. A CHECK is currently enforcing this. + Schema GetItems() const; + + private: + const internal::SchemaNode* schema_; +}; + +// Owns schemas for policies of a given component. +class POLICY_EXPORT SchemaOwner { + public: + ~SchemaOwner(); + + // The returned Schema is valid only during the lifetime of the SchemaOwner + // that created it. It may be obtained multiple times. + Schema schema() const { return Schema(root_); } + + // Returns a SchemaOwner that references static data. This can be used by + // the embedder to pass structures generated at compile time, which can then + // be quickly loaded at runtime. + // Note: PropertiesNodes must have their PropertyNodes sorted by key. + static scoped_ptr<SchemaOwner> Wrap(const internal::SchemaNode* schema); + + // Parses the JSON schema in |schema| and returns a SchemaOwner that owns + // the internal representation. If |schema| is invalid then NULL is returned + // and |error| contains a reason for the failure. + static scoped_ptr<SchemaOwner> Parse(const std::string& schema, + std::string* error); + + private: + explicit SchemaOwner(const internal::SchemaNode* root); + + // Parses the JSON schema in |schema| and returns the root SchemaNode if + // successful, otherwise returns NULL. Any intermediate objects built by + // this method are appended to the ScopedVectors. + const internal::SchemaNode* Parse(const base::DictionaryValue& schema, + std::string* error); + + // Helper for Parse(). + const internal::SchemaNode* ParseDictionary( + const base::DictionaryValue& schema, + std::string* error); + + // Helper for Parse(). + const internal::SchemaNode* ParseList(const base::DictionaryValue& schema, + std::string* error); + + const internal::SchemaNode* root_; + ScopedVector<internal::SchemaNode> schema_nodes_; + // Note: |property_nodes_| contains PropertyNode[] elements and must be + // cleared manually to properly use delete[]. + std::vector<internal::PropertyNode*> property_nodes_; + ScopedVector<internal::PropertiesNode> properties_nodes_; + ScopedVector<std::string> keys_; + + DISALLOW_COPY_AND_ASSIGN(SchemaOwner); +}; + +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_ diff --git a/chromium/components/policy/core/common/schema_internal.h b/chromium/components/policy/core/common/schema_internal.h new file mode 100644 index 00000000000..025a4373ee7 --- /dev/null +++ b/chromium/components/policy/core/common/schema_internal.h @@ -0,0 +1,45 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_ +#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_ + +#include "base/values.h" +#include "components/policy/policy_export.h" + +namespace policy { +namespace internal { + +// These types are used internally by the SchemaOwner parser, and by the +// compile-time code generator. They shouldn't be used directly. + +struct POLICY_EXPORT SchemaNode { + base::Value::Type type; + + // If |type| is TYPE_LIST then this is a SchemaNode* describing the + // element type. + // + // If |type| is TYPE_DICTIONARY then this is a PropertiesNode* that can + // contain any number of named properties and optionally a SchemaNode* for + // additional properties. + // + // This is NULL if |type| has any other values. + const void* extra; +}; + +struct POLICY_EXPORT PropertyNode { + const char* key; + const SchemaNode* schema; +}; + +struct POLICY_EXPORT PropertiesNode { + const PropertyNode* begin; + const PropertyNode* end; + const SchemaNode* additional; +}; + +} // namespace internal +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_ diff --git a/chromium/components/policy/core/common/schema_unittest.cc b/chromium/components/policy/core/common/schema_unittest.cc new file mode 100644 index 00000000000..06fb3bf34a3 --- /dev/null +++ b/chromium/components/policy/core/common/schema_unittest.cc @@ -0,0 +1,338 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/schema.h" + +#include "components/policy/core/common/schema_internal.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace policy { + +namespace { + +#define OBJECT_TYPE "\"type\":\"object\"" + +const internal::SchemaNode kTypeBoolean = { base::Value::TYPE_BOOLEAN, NULL, }; +const internal::SchemaNode kTypeInteger = { base::Value::TYPE_INTEGER, NULL, }; +const internal::SchemaNode kTypeNumber = { base::Value::TYPE_DOUBLE, NULL, }; +const internal::SchemaNode kTypeString = { base::Value::TYPE_STRING, NULL, }; + +bool ParseFails(const std::string& content) { + std::string error; + scoped_ptr<SchemaOwner> schema = SchemaOwner::Parse(content, &error); + if (schema) + EXPECT_TRUE(schema->schema().valid()); + else + EXPECT_FALSE(error.empty()); + return !schema; +} + +} // namespace + +TEST(SchemaTest, MinimalSchema) { + EXPECT_FALSE(ParseFails( + "{" + OBJECT_TYPE + "}")); +} + +TEST(SchemaTest, InvalidSchemas) { + EXPECT_TRUE(ParseFails("")); + EXPECT_TRUE(ParseFails("omg")); + EXPECT_TRUE(ParseFails("\"omg\"")); + EXPECT_TRUE(ParseFails("123")); + EXPECT_TRUE(ParseFails("[]")); + EXPECT_TRUE(ParseFails("null")); + EXPECT_TRUE(ParseFails("{}")); + + EXPECT_TRUE(ParseFails( + "{" + OBJECT_TYPE "," + "\"additionalProperties\": { \"type\":\"object\" }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + OBJECT_TYPE "," + "\"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": \"bogus\" } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }" + "}")); + + EXPECT_TRUE(ParseFails( + "{" + OBJECT_TYPE "," + "\"properties\": { \"Policy\": { \"type\": \"any\" } }" + "}")); +} + +TEST(SchemaTest, ValidSchema) { + std::string error; + scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse( + "{" + OBJECT_TYPE "," + "\"properties\": {" + " \"Boolean\": { \"type\": \"boolean\" }," + " \"Integer\": { \"type\": \"integer\" }," + " \"Null\": { \"type\": \"null\" }," + " \"Number\": { \"type\": \"number\" }," + " \"String\": { \"type\": \"string\" }," + " \"Array\": {" + " \"type\": \"array\"," + " \"items\": { \"type\": \"string\" }" + " }," + " \"ArrayOfObjects\": {" + " \"type\": \"array\"," + " \"items\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"one\": { \"type\": \"string\" }," + " \"two\": { \"type\": \"integer\" }" + " }" + " }" + " }," + " \"ArrayOfArray\": {" + " \"type\": \"array\"," + " \"items\": {" + " \"type\": \"array\"," + " \"items\": { \"type\": \"string\" }" + " }" + " }," + " \"Object\": {" + " \"type\": \"object\"," + " \"properties\": {" + " \"one\": { \"type\": \"boolean\" }," + " \"two\": { \"type\": \"integer\" }" + " }," + " \"additionalProperties\": { \"type\": \"string\" }" + " }" + "}" + "}", &error); + ASSERT_TRUE(policy_schema) << error; + ASSERT_TRUE(policy_schema->schema().valid()); + + Schema schema = policy_schema->schema(); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type()); + EXPECT_FALSE(schema.GetProperty("invalid").valid()); + + Schema sub = schema.GetProperty("Boolean"); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type()); + + sub = schema.GetProperty("Integer"); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type()); + + sub = schema.GetProperty("Null"); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_NULL, sub.type()); + + sub = schema.GetProperty("Number"); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type()); + + sub = schema.GetProperty("String"); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, sub.type()); + + sub = schema.GetProperty("Array"); + ASSERT_TRUE(sub.valid()); + ASSERT_EQ(base::Value::TYPE_LIST, sub.type()); + sub = sub.GetItems(); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, sub.type()); + + sub = schema.GetProperty("ArrayOfObjects"); + ASSERT_TRUE(sub.valid()); + ASSERT_EQ(base::Value::TYPE_LIST, sub.type()); + sub = sub.GetItems(); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type()); + Schema subsub = sub.GetProperty("one"); + ASSERT_TRUE(subsub.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, subsub.type()); + subsub = sub.GetProperty("two"); + ASSERT_TRUE(subsub.valid()); + EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type()); + subsub = sub.GetProperty("invalid"); + EXPECT_FALSE(subsub.valid()); + + sub = schema.GetProperty("ArrayOfArray"); + ASSERT_TRUE(sub.valid()); + ASSERT_EQ(base::Value::TYPE_LIST, sub.type()); + sub = sub.GetItems(); + ASSERT_TRUE(sub.valid()); + ASSERT_EQ(base::Value::TYPE_LIST, sub.type()); + sub = sub.GetItems(); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, sub.type()); + + sub = schema.GetProperty("Object"); + ASSERT_TRUE(sub.valid()); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type()); + subsub = sub.GetProperty("one"); + ASSERT_TRUE(subsub.valid()); + EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type()); + subsub = sub.GetProperty("two"); + ASSERT_TRUE(subsub.valid()); + EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type()); + subsub = sub.GetProperty("undeclared"); + ASSERT_TRUE(subsub.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, subsub.type()); + + struct { + const char* expected_key; + base::Value::Type expected_type; + } kExpectedProperties[] = { + { "Array", base::Value::TYPE_LIST }, + { "ArrayOfArray", base::Value::TYPE_LIST }, + { "ArrayOfObjects", base::Value::TYPE_LIST }, + { "Boolean", base::Value::TYPE_BOOLEAN }, + { "Integer", base::Value::TYPE_INTEGER }, + { "Null", base::Value::TYPE_NULL }, + { "Number", base::Value::TYPE_DOUBLE }, + { "Object", base::Value::TYPE_DICTIONARY }, + { "String", base::Value::TYPE_STRING }, + }; + Schema::Iterator it = schema.GetPropertiesIterator(); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) { + ASSERT_FALSE(it.IsAtEnd()); + EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key()); + ASSERT_TRUE(it.schema().valid()); + EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type()); + it.Advance(); + } + EXPECT_TRUE(it.IsAtEnd()); +} + +TEST(SchemaTest, Lookups) { + std::string error; + scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse( + "{" + OBJECT_TYPE + "}", &error); + ASSERT_TRUE(policy_schema) << error; + Schema schema = policy_schema->schema(); + ASSERT_TRUE(schema.valid()); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type()); + + // This empty schema should never find named properties. + EXPECT_FALSE(schema.GetKnownProperty("").valid()); + EXPECT_FALSE(schema.GetKnownProperty("xyz").valid()); + EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd()); + + policy_schema = SchemaOwner::Parse( + "{" + OBJECT_TYPE "," + "\"properties\": {" + " \"Boolean\": { \"type\": \"boolean\" }" + "}" + "}", &error); + ASSERT_TRUE(policy_schema) << error; + schema = policy_schema->schema(); + ASSERT_TRUE(schema.valid()); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type()); + + EXPECT_FALSE(schema.GetKnownProperty("").valid()); + EXPECT_FALSE(schema.GetKnownProperty("xyz").valid()); + EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid()); + + policy_schema = SchemaOwner::Parse( + "{" + OBJECT_TYPE "," + "\"properties\": {" + " \"bb\" : { \"type\": \"null\" }," + " \"aa\" : { \"type\": \"boolean\" }," + " \"abab\" : { \"type\": \"string\" }," + " \"ab\" : { \"type\": \"number\" }," + " \"aba\" : { \"type\": \"integer\" }" + "}" + "}", &error); + ASSERT_TRUE(policy_schema) << error; + schema = policy_schema->schema(); + ASSERT_TRUE(schema.valid()); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type()); + + EXPECT_FALSE(schema.GetKnownProperty("").valid()); + EXPECT_FALSE(schema.GetKnownProperty("xyz").valid()); + + struct { + const char* expected_key; + base::Value::Type expected_type; + } kExpectedKeys[] = { + { "aa", base::Value::TYPE_BOOLEAN }, + { "ab", base::Value::TYPE_DOUBLE }, + { "aba", base::Value::TYPE_INTEGER }, + { "abab", base::Value::TYPE_STRING }, + { "bb", base::Value::TYPE_NULL }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedKeys); ++i) { + Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type()); + } +} + +TEST(SchemaTest, WrapSimpleNode) { + scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&kTypeString); + ASSERT_TRUE(policy_schema); + Schema schema = policy_schema->schema(); + ASSERT_TRUE(schema.valid()); + EXPECT_EQ(base::Value::TYPE_STRING, schema.type()); +} + +TEST(SchemaTest, WrapDictionary) { + const internal::SchemaNode kList = { + base::Value::TYPE_LIST, + &kTypeString, + }; + + const internal::PropertyNode kPropertyNodes[] = { + { "Boolean", &kTypeBoolean }, + { "Integer", &kTypeInteger }, + { "List", &kList }, + { "Number", &kTypeNumber }, + { "String", &kTypeString }, + }; + + const internal::PropertiesNode kProperties = { + kPropertyNodes, + kPropertyNodes + arraysize(kPropertyNodes), + NULL, + }; + + const internal::SchemaNode root = { + base::Value::TYPE_DICTIONARY, + &kProperties, + }; + + scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&root); + ASSERT_TRUE(policy_schema); + Schema schema = policy_schema->schema(); + ASSERT_TRUE(schema.valid()); + EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type()); + + Schema::Iterator it = schema.GetPropertiesIterator(); + for (size_t i = 0; i < arraysize(kPropertyNodes); ++i) { + ASSERT_FALSE(it.IsAtEnd()); + EXPECT_STREQ(kPropertyNodes[i].key, it.key()); + Schema sub = it.schema(); + ASSERT_TRUE(sub.valid()); + EXPECT_EQ(kPropertyNodes[i].schema->type, sub.type()); + it.Advance(); + } + EXPECT_TRUE(it.IsAtEnd()); +} + +} // namespace policy |