summaryrefslogtreecommitdiff
path: root/chromium/components/policy/core
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2013-12-11 21:33:03 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-12-13 12:34:07 +0100
commitf2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch)
tree0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/components/policy/core
parent5362912cdb5eea702b68ebe23702468d17c3017a (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.cc15
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.h21
-rw-r--r--chromium/components/policy/core/common/policy_schema.cc244
-rw-r--r--chromium/components/policy/core/common/policy_schema.h70
-rw-r--r--chromium/components/policy/core/common/policy_schema_unittest.cc193
-rw-r--r--chromium/components/policy/core/common/schema.cc276
-rw-r--r--chromium/components/policy/core/common/schema.h150
-rw-r--r--chromium/components/policy/core/common/schema_internal.h45
-rw-r--r--chromium/components/policy/core/common/schema_unittest.cc338
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