1d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// found in the LICENSE file.
4d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
5d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/policy/core/common/schema.h"
6d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
7d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include <algorithm>
8a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <map>
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <utility>
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include <vector>
11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/compiler_specific.h"
13d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/logging.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/memory/scoped_vector.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/json_schema/json_schema_constants.h"
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/json_schema/json_schema_validator.h"
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/policy/core/common/schema_internal.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace schema = json_schema_constants;
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace policy {
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using internal::PropertiesNode;
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using internal::PropertyNode;
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using internal::SchemaData;
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using internal::SchemaNode;
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
28d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace {
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Maps schema "id" attributes to the corresponding SchemaNode index.
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)typedef std::map<std::string, int> IdMap;
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// List of pairs of references to be assigned later. The string is the "id"
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// whose corresponding index should be stored in the pointer, once all the IDs
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// are available.
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)typedef std::vector<std::pair<std::string, int*> > ReferenceList;
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Sizes for the storage arrays. These are calculated in advance so that the
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// arrays don't have to be resized during parsing, which would invalidate
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// pointers into their contents (i.e. string's c_str() and address of indices
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// for "$ref" attributes).
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)struct StorageSizes {
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  StorageSizes()
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      : strings(0), schema_nodes(0), property_nodes(0), properties_nodes(0) {}
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  size_t strings;
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  size_t schema_nodes;
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  size_t property_nodes;
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  size_t properties_nodes;
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)};
50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// An invalid index, indicating that a node is not present; similar to a NULL
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// pointer.
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kInvalid = -1;
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
55d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool SchemaTypeToValueType(const std::string& type_string,
56d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                           base::Value::Type* type) {
57d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Note: "any" is not an accepted type.
58d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  static const struct {
59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const char* schema_type;
60d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    base::Value::Type value_type;
61d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  } kSchemaToValueTypeMap[] = {
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kArray,        base::Value::TYPE_LIST       },
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kBoolean,      base::Value::TYPE_BOOLEAN    },
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kInteger,      base::Value::TYPE_INTEGER    },
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kNull,         base::Value::TYPE_NULL       },
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kNumber,       base::Value::TYPE_DOUBLE     },
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kObject,       base::Value::TYPE_DICTIONARY },
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    { schema::kString,       base::Value::TYPE_STRING     },
69d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  };
70d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
71d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (kSchemaToValueTypeMap[i].schema_type == type_string) {
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      *type = kSchemaToValueTypeMap[i].value_type;
73d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      return true;
74d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
75d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return false;
77d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
79d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}  // namespace
80d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Contains the internal data representation of a Schema. This can either wrap
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// a SchemaData owned elsewhere (currently used to wrap the Chrome schema, which
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// is generated at compile time), or it can own its own SchemaData.
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class Schema::InternalStorage
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : public base::RefCountedThreadSafe<InternalStorage> {
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public:
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  static scoped_refptr<const InternalStorage> Wrap(const SchemaData* data);
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  static scoped_refptr<const InternalStorage> ParseSchema(
90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      const base::DictionaryValue& schema,
91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      std::string* error);
92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const SchemaData* data() const { return &schema_data_; }
94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const SchemaNode* root_node() const {
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return schema(0);
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const SchemaNode* schema(int index) const {
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return schema_data_.schema_nodes + index;
101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertiesNode* properties(int index) const {
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return schema_data_.properties_nodes + index;
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertyNode* property(int index) const {
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return schema_data_.property_nodes + index;
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private:
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  friend class base::RefCountedThreadSafe<InternalStorage>;
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  InternalStorage();
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ~InternalStorage();
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Determines the expected |sizes| of the storage for the representation
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // of |schema|.
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static void DetermineStorageSizes(const base::DictionaryValue& schema,
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   StorageSizes* sizes);
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Parses the JSON schema in |schema|.
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // If |schema| has a "$ref" attribute then a pending reference is appended
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // to the |reference_list|, and nothing else is done.
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Otherwise, |index| gets assigned the index of the corresponding SchemaNode
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // in |schema_nodes_|. If the |schema| contains an "id" then that ID is mapped
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // to the |index| in the |id_map|.
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // If |schema| is invalid then |error| gets the error reason and false is
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // returned. Otherwise returns true.
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool Parse(const base::DictionaryValue& schema,
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             int* index,
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             IdMap* id_map,
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             ReferenceList* reference_list,
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             std::string* error);
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Helper for Parse() that gets an already assigned |schema_node| instead of
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // an |index| pointer.
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool ParseDictionary(const base::DictionaryValue& schema,
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       SchemaNode* schema_node,
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       IdMap* id_map,
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       ReferenceList* reference_list,
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                       std::string* error);
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Helper for Parse() that gets an already assigned |schema_node| instead of
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // an |index| pointer.
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool ParseList(const base::DictionaryValue& schema,
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 SchemaNode* schema_node,
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 IdMap* id_map,
152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 ReferenceList* reference_list,
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 std::string* error);
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Assigns the IDs in |id_map| to the pending references in the
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // |reference_list|. If an ID is missing then |error| is set and false is
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // returned; otherwise returns true.
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static bool ResolveReferences(const IdMap& id_map,
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                const ReferenceList& reference_list,
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                std::string* error);
161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SchemaData schema_data_;
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::vector<std::string> strings_;
164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::vector<SchemaNode> schema_nodes_;
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::vector<PropertyNode> property_nodes_;
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::vector<PropertiesNode> properties_nodes_;
167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(InternalStorage);
169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)};
170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::InternalStorage::InternalStorage() {}
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::InternalStorage::~InternalStorage() {}
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)scoped_refptr<const Schema::InternalStorage> Schema::InternalStorage::Wrap(
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const SchemaData* data) {
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  InternalStorage* storage = new InternalStorage();
179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  storage->schema_data_.schema_nodes = data->schema_nodes;
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  storage->schema_data_.property_nodes = data->property_nodes;
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  storage->schema_data_.properties_nodes = data->properties_nodes;
182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return storage;
183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)scoped_refptr<const Schema::InternalStorage>
187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::InternalStorage::ParseSchema(const base::DictionaryValue& schema,
188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                     std::string* error) {
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Determine the sizes of the storage arrays and reserve the capacity before
190a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // starting to append nodes and strings. This is important to prevent the
191a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // arrays from being reallocated, which would invalidate the c_str() pointers
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // and the addresses of indices to fix.
193a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  StorageSizes sizes;
194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DetermineStorageSizes(schema, &sizes);
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  scoped_refptr<InternalStorage> storage = new InternalStorage();
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  storage->strings_.reserve(sizes.strings);
198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  storage->schema_nodes_.reserve(sizes.schema_nodes);
199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  storage->property_nodes_.reserve(sizes.property_nodes);
200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  storage->properties_nodes_.reserve(sizes.properties_nodes);
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int root_index = kInvalid;
203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  IdMap id_map;
204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ReferenceList reference_list;
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!storage->Parse(schema, &root_index, &id_map, &reference_list, error))
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return NULL;
207a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (root_index == kInvalid) {
209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = "The main schema can't have a $ref";
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return NULL;
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // None of this should ever happen without having been already detected.
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // But, if it does happen, then it will lead to corrupted memory; drop
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // everything in that case.
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (root_index != 0 ||
217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      sizes.strings != storage->strings_.size() ||
218a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      sizes.schema_nodes != storage->schema_nodes_.size() ||
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      sizes.property_nodes != storage->property_nodes_.size() ||
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      sizes.properties_nodes != storage->properties_nodes_.size()) {
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *error = "Failed to parse the schema due to a Chrome bug. Please file a "
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             "new issue at http://crbug.com";
223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return NULL;
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
225a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
226a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!ResolveReferences(id_map, reference_list, error))
227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return NULL;
228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SchemaData* data = &storage->schema_data_;
230f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  data->schema_nodes = vector_as_array(&storage->schema_nodes_);
231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  data->property_nodes = vector_as_array(&storage->property_nodes_);
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  data->properties_nodes = vector_as_array(&storage->properties_nodes_);
233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return storage;
234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
236a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// static
237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void Schema::InternalStorage::DetermineStorageSizes(
238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::DictionaryValue& schema,
239a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    StorageSizes* sizes) {
240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string ref_string;
241a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (schema.GetString(schema::kRef, &ref_string)) {
242a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Schemas with a "$ref" attribute don't take additional storage.
243a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
244a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
245a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
246a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string type_string;
247a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::Value::Type type = base::Value::TYPE_NULL;
248a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!schema.GetString(schema::kType, &type_string) ||
249a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      !SchemaTypeToValueType(type_string, &type)) {
250a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // This schema is invalid.
251a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
252a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
254a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  sizes->schema_nodes++;
255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
256a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (type == base::Value::TYPE_LIST) {
257a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::DictionaryValue* items = NULL;
258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (schema.GetDictionary(schema::kItems, &items))
259a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DetermineStorageSizes(*items, sizes);
260a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else if (type == base::Value::TYPE_DICTIONARY) {
261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    sizes->properties_nodes++;
262a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
263a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::DictionaryValue* dict = NULL;
264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (schema.GetDictionary(schema::kAdditionalProperties, &dict))
265a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DetermineStorageSizes(*dict, sizes);
266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
267a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::DictionaryValue* properties = NULL;
268a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (schema.GetDictionary(schema::kProperties, &properties)) {
269a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      for (base::DictionaryValue::Iterator it(*properties);
270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)           !it.IsAtEnd(); it.Advance()) {
271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // This should have been verified by the JSONSchemaValidator.
272a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        CHECK(it.value().GetAsDictionary(&dict));
273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        DetermineStorageSizes(*dict, sizes);
274a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        sizes->strings++;
275a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        sizes->property_nodes++;
276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
277a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
279a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
280a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
281a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool Schema::InternalStorage::Parse(const base::DictionaryValue& schema,
282a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    int* index,
283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    IdMap* id_map,
284a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    ReferenceList* reference_list,
285a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                    std::string* error) {
286a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string ref_string;
287a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (schema.GetString(schema::kRef, &ref_string)) {
288a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::string id_string;
289a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (schema.GetString(schema::kId, &id_string)) {
290a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      *error = "Schemas with a $ref can't have an id";
291a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
292a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
293a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    reference_list->push_back(std::make_pair(ref_string, index));
294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return true;
295a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
296a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  std::string type_string;
298a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!schema.GetString(schema::kType, &type_string)) {
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    *error = "The schema type must be declared.";
300a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
301f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
302f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
303f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::Value::Type type = base::Value::TYPE_NULL;
304f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!SchemaTypeToValueType(type_string, &type)) {
305f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    *error = "Type not supported: " + type_string;
306a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
307f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
308f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
309a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  *index = static_cast<int>(schema_nodes_.size());
310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  schema_nodes_.push_back(SchemaNode());
311a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  SchemaNode* schema_node = &schema_nodes_.back();
312a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  schema_node->type = type;
313a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  schema_node->extra = kInvalid;
314a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
315a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (type == base::Value::TYPE_DICTIONARY) {
316a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!ParseDictionary(schema, schema_node, id_map, reference_list, error))
317a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
318a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else if (type == base::Value::TYPE_LIST) {
319a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!ParseList(schema, schema_node, id_map, reference_list, error))
320a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
321a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
322a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
323a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string id_string;
324a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (schema.GetString(schema::kId, &id_string)) {
325a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (ContainsKey(*id_map, id_string)) {
326a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      *error = "Duplicated id: " + id_string;
327a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
328a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
329a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    (*id_map)[id_string] = *index;
330a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
331a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
332a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
333f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
334f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
335a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool Schema::InternalStorage::ParseDictionary(
336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const base::DictionaryValue& schema,
337a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    SchemaNode* schema_node,
338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    IdMap* id_map,
339a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    ReferenceList* reference_list,
340f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    std::string* error) {
341f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int extra = static_cast<int>(properties_nodes_.size());
342f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  properties_nodes_.push_back(PropertiesNode());
343f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  properties_nodes_[extra].begin = kInvalid;
344f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  properties_nodes_[extra].end = kInvalid;
345f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  properties_nodes_[extra].additional = kInvalid;
346a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  schema_node->extra = extra;
347f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::DictionaryValue* dict = NULL;
349a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (schema.GetDictionary(schema::kAdditionalProperties, &dict)) {
350a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!Parse(*dict, &properties_nodes_[extra].additional,
351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)               id_map, reference_list, error)) {
352a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
354f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
356f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::DictionaryValue* properties = NULL;
357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (schema.GetDictionary(schema::kProperties, &properties)) {
358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int base_index = static_cast<int>(property_nodes_.size());
359f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // This reserves nodes for all of the |properties|, and makes sure they
360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // are contiguous. Recursive calls to Parse() will append after these
361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // elements.
362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    property_nodes_.resize(base_index + properties->size());
363f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int index = base_index;
365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for (base::DictionaryValue::Iterator it(*properties);
366f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)         !it.IsAtEnd(); it.Advance(), ++index) {
367f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // This should have been verified by the JSONSchemaValidator.
368f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      CHECK(it.value().GetAsDictionary(&dict));
369a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      strings_.push_back(it.key());
370a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      property_nodes_[index].key = strings_.back().c_str();
371a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (!Parse(*dict, &property_nodes_[index].schema,
372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 id_map, reference_list, error)) {
373a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return false;
374a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
376f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    CHECK_EQ(static_cast<int>(properties->size()), index - base_index);
377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    properties_nodes_[extra].begin = base_index;
378f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    properties_nodes_[extra].end = index;
379f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
381a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
384a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool Schema::InternalStorage::ParseList(const base::DictionaryValue& schema,
385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                        SchemaNode* schema_node,
386a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                        IdMap* id_map,
387a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                        ReferenceList* reference_list,
388a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                        std::string* error) {
389f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::DictionaryValue* dict = NULL;
390a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!schema.GetDictionary(schema::kItems, &dict)) {
391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    *error = "Arrays must declare a single schema for their items.";
392a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return false;
393f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
394a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return Parse(*dict, &schema_node->extra, id_map, reference_list, error);
395a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
396a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
397a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// static
398a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool Schema::InternalStorage::ResolveReferences(
399a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const IdMap& id_map,
400a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const ReferenceList& reference_list,
401a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::string* error) {
402a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (ReferenceList::const_iterator ref = reference_list.begin();
403a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)       ref != reference_list.end(); ++ref) {
404a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    IdMap::const_iterator id = id_map.find(ref->first);
405a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (id == id_map.end()) {
406a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      *error = "Invalid $ref: " + ref->first;
407a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
408a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    *ref->second = id->second;
410a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
411a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return true;
412f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
413f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
414f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::Iterator::Iterator(const scoped_refptr<const InternalStorage>& storage,
415f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                           const PropertiesNode* node)
416f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : storage_(storage),
417f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      it_(storage->property(node->begin)),
418f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      end_(storage->property(node->end)) {}
419d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
420d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Schema::Iterator::Iterator(const Iterator& iterator)
421f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : storage_(iterator.storage_),
422f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      it_(iterator.it_),
423d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      end_(iterator.end_) {}
424d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
425d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Schema::Iterator::~Iterator() {}
426d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
427d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
428f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  storage_ = iterator.storage_;
429d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  it_ = iterator.it_;
430d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  end_ = iterator.end_;
431d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return *this;
432d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
433d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
434d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool Schema::Iterator::IsAtEnd() const {
435d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return it_ == end_;
436d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
437d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
438d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void Schema::Iterator::Advance() {
439d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  ++it_;
440d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
441d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
442d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)const char* Schema::Iterator::key() const {
443d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return it_->key;
444d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
445d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
446d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Schema Schema::Iterator::schema() const {
447f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema(storage_, storage_->schema(it_->schema));
448d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
449d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
450f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::Schema() : node_(NULL) {}
451f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
452f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::Schema(const scoped_refptr<const InternalStorage>& storage,
453f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               const SchemaNode* node)
454f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : storage_(storage), node_(node) {}
45568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
456f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::Schema(const Schema& schema)
457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : storage_(schema.storage_), node_(schema.node_) {}
458d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
459f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::~Schema() {}
460d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
461d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Schema& Schema::operator=(const Schema& schema) {
462f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  storage_ = schema.storage_;
463f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  node_ = schema.node_;
464d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return *this;
465d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
466d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
467f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
468f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::Wrap(const SchemaData* data) {
469f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  scoped_refptr<const InternalStorage> storage = InternalStorage::Wrap(data);
470f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema(storage, storage->root_node());
471d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
472d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
473f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool Schema::Validate(const base::Value& value) const {
474f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!valid()) {
475f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // Schema not found, invalid entry.
476f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return false;
477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
478d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
479f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!value.IsType(type()))
480f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return false;
481d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
482f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::DictionaryValue* dict = NULL;
483f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const base::ListValue* list = NULL;
484f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (value.GetAsDictionary(&dict)) {
485f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd();
486f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)         it.Advance()) {
487f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (!GetProperty(it.key()).Validate(it.value()))
488f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return false;
489f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
490f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else if (value.GetAsList(&list)) {
491f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for (base::ListValue::const_iterator it = list->begin();
492f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)         it != list->end(); ++it) {
493f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (!*it || !GetItems().Validate(**it))
494f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return false;
495f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
496f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
497d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
498f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return true;
499d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
500d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
501d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// static
502f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::Parse(const std::string& content, std::string* error) {
503a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Validate as a generic JSON schema, and ignore unknown attributes; they
504a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // may become used in a future version of the schema format.
505a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> dict = JSONSchemaValidator::IsValidSchema(
506a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      content, JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES, error);
507d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (!dict)
508f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
509d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
510d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Validate the main type.
511d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::string string_value;
512a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!dict->GetString(schema::kType, &string_value) ||
513a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      string_value != schema::kObject) {
514d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    *error =
515d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        "The main schema must have a type attribute with \"object\" value.";
516f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
517d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
518d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
519d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Checks for invalid attributes at the top-level.
520a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (dict->HasKey(schema::kAdditionalProperties) ||
521a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      dict->HasKey(schema::kPatternProperties)) {
522d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    *error = "\"additionalProperties\" and \"patternProperties\" are not "
523d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)             "supported at the main schema.";
524f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
525d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
526d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
527f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  scoped_refptr<const InternalStorage> storage =
528f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      InternalStorage::ParseSchema(*dict, error);
529f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!storage)
530f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
531f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema(storage, storage->root_node());
532d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
533d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
534f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)base::Value::Type Schema::type() const {
535f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(valid());
536f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return node_->type;
537f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
538d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
539f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema::Iterator Schema::GetPropertiesIterator() const {
540f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(valid());
541f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
542f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Iterator(storage_, storage_->properties(node_->extra));
543d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
544d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
545f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace {
546d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
547f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool CompareKeys(const PropertyNode& node, const std::string& key) {
548f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return node.key < key;
549f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
550d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
551f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace
552d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
553f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::GetKnownProperty(const std::string& key) const {
554f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(valid());
555f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
556f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertiesNode* node = storage_->properties(node_->extra);
557f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertyNode* begin = storage_->property(node->begin);
558f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertyNode* end = storage_->property(node->end);
559f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertyNode* it = std::lower_bound(begin, end, key, CompareKeys);
560f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (it != end && it->key == key)
561f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema(storage_, storage_->schema(it->schema));
562f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema();
563f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
564d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
565f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::GetAdditionalProperties() const {
566f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(valid());
567f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
568f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const PropertiesNode* node = storage_->properties(node_->extra);
569f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (node->additional == kInvalid)
570f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
571f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema(storage_, storage_->schema(node->additional));
572d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
573d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
574f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::GetProperty(const std::string& key) const {
575f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  Schema schema = GetKnownProperty(key);
576f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return schema.valid() ? schema : GetAdditionalProperties();
577f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
578d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
579f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Schema Schema::GetItems() const {
580f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(valid());
581f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK_EQ(base::Value::TYPE_LIST, type());
582f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (node_->extra == kInvalid)
583f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Schema();
584f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Schema(storage_, storage_->schema(node_->extra));
585d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
586d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
587d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}  // namespace policy
588