Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: components/policy/core/common/schema.cc

Issue 31273002: Generate the Chrome policy schema at compile time. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « components/policy/core/common/schema.h ('k') | components/policy/core/common/schema_internal.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/policy/core/common/schema.h" 5 #include "components/policy/core/common/schema.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector>
8 9
9 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_vector.h"
11 #include "components/json_schema/json_schema_constants.h" 13 #include "components/json_schema/json_schema_constants.h"
12 #include "components/json_schema/json_schema_validator.h" 14 #include "components/json_schema/json_schema_validator.h"
13 #include "components/policy/core/common/schema_internal.h" 15 #include "components/policy/core/common/schema_internal.h"
14 16
15 namespace policy { 17 namespace policy {
16 18
19 using internal::PropertiesNode;
20 using internal::PropertyNode;
21 using internal::SchemaData;
22 using internal::SchemaNode;
23
17 namespace { 24 namespace {
18 25
26 const int kInvalid = -1;
27
19 bool SchemaTypeToValueType(const std::string& type_string, 28 bool SchemaTypeToValueType(const std::string& type_string,
20 base::Value::Type* type) { 29 base::Value::Type* type) {
21 // Note: "any" is not an accepted type. 30 // Note: "any" is not an accepted type.
22 static const struct { 31 static const struct {
23 const char* schema_type; 32 const char* schema_type;
24 base::Value::Type value_type; 33 base::Value::Type value_type;
25 } kSchemaToValueTypeMap[] = { 34 } kSchemaToValueTypeMap[] = {
26 { json_schema_constants::kArray, base::Value::TYPE_LIST }, 35 { json_schema_constants::kArray, base::Value::TYPE_LIST },
27 { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN }, 36 { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN },
28 { json_schema_constants::kInteger, base::Value::TYPE_INTEGER }, 37 { json_schema_constants::kInteger, base::Value::TYPE_INTEGER },
29 { json_schema_constants::kNull, base::Value::TYPE_NULL }, 38 { json_schema_constants::kNull, base::Value::TYPE_NULL },
30 { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE }, 39 { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE },
31 { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY }, 40 { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY },
32 { json_schema_constants::kString, base::Value::TYPE_STRING }, 41 { json_schema_constants::kString, base::Value::TYPE_STRING },
33 }; 42 };
34 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) { 43 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
35 if (kSchemaToValueTypeMap[i].schema_type == type_string) { 44 if (kSchemaToValueTypeMap[i].schema_type == type_string) {
36 *type = kSchemaToValueTypeMap[i].value_type; 45 *type = kSchemaToValueTypeMap[i].value_type;
37 return true; 46 return true;
38 } 47 }
39 } 48 }
40 return false; 49 return false;
41 } 50 }
42 51
43 } // namespace 52 } // namespace
44 53
45 Schema::Iterator::Iterator(const internal::PropertiesNode* properties) 54 // A SchemaOwner can either Wrap() a SchemaData owned elsewhere (currently used
46 : it_(properties->begin), 55 // to wrap the Chrome schema, which is generated at compile time), or it can
47 end_(properties->end) {} 56 // own its own SchemaData. In that case, the InternalStorage class holds the
57 // data referenced by the SchemaData substructures.
58 class SchemaOwner::InternalStorage {
59 public:
60 ~InternalStorage() {}
61
62 static scoped_ptr<InternalStorage> ParseSchema(
63 const base::DictionaryValue& schema,
64 std::string* error);
65
66 const SchemaData* schema_data() const { return &schema_data_; }
67
68 private:
69 InternalStorage() {}
70
71 // Parses the JSON schema in |schema| and returns the index of the
72 // corresponding SchemaNode in |schema_nodes_|, which gets populated with any
73 // necessary intermediate nodes. If |schema| is invalid then -1 is returned
74 // and |error| is set to the error cause.
75 int Parse(const base::DictionaryValue& schema, std::string* error);
76
77 // Helper for Parse().
78 int ParseDictionary(const base::DictionaryValue& schema, std::string* error);
79
80 // Helper for Parse().
81 int ParseList(const base::DictionaryValue& schema, std::string* error);
82
83 SchemaData schema_data_;
84 // TODO: compute the sizes of these arrays before filling them up to avoid
85 // having to resize them.
86 ScopedVector<std::string> strings_;
87 std::vector<SchemaNode> schema_nodes_;
88 std::vector<PropertyNode> property_nodes_;
89 std::vector<PropertiesNode> properties_nodes_;
90
91 DISALLOW_COPY_AND_ASSIGN(InternalStorage);
92 };
93
94 Schema::Iterator::Iterator(const SchemaData* data, const PropertiesNode* node)
95 : data_(data),
96 it_(data->property_nodes + node->begin),
97 end_(data->property_nodes + node->end) {}
48 98
49 Schema::Iterator::Iterator(const Iterator& iterator) 99 Schema::Iterator::Iterator(const Iterator& iterator)
50 : it_(iterator.it_), 100 : data_(iterator.data_),
101 it_(iterator.it_),
51 end_(iterator.end_) {} 102 end_(iterator.end_) {}
52 103
53 Schema::Iterator::~Iterator() {} 104 Schema::Iterator::~Iterator() {}
54 105
55 Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) { 106 Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
107 data_ = iterator.data_;
56 it_ = iterator.it_; 108 it_ = iterator.it_;
57 end_ = iterator.end_; 109 end_ = iterator.end_;
58 return *this; 110 return *this;
59 } 111 }
60 112
61 bool Schema::Iterator::IsAtEnd() const { 113 bool Schema::Iterator::IsAtEnd() const {
62 return it_ == end_; 114 return it_ == end_;
63 } 115 }
64 116
65 void Schema::Iterator::Advance() { 117 void Schema::Iterator::Advance() {
66 ++it_; 118 ++it_;
67 } 119 }
68 120
69 const char* Schema::Iterator::key() const { 121 const char* Schema::Iterator::key() const {
70 return it_->key; 122 return it_->key;
71 } 123 }
72 124
73 Schema Schema::Iterator::schema() const { 125 Schema Schema::Iterator::schema() const {
74 return Schema(it_->schema); 126 return Schema(data_, data_->schema_nodes + it_->schema);
75 } 127 }
76 128
77 Schema::Schema() : schema_(NULL) {} 129 Schema::Schema() : data_(NULL), node_(NULL) {}
78 130
79 Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {} 131 Schema::Schema(const SchemaData* data, const SchemaNode* node)
132 : data_(data), node_(node) {}
80 133
81 Schema::Schema(const Schema& schema) : schema_(schema.schema_) {} 134 Schema::Schema(const Schema& schema)
135 : data_(schema.data_), node_(schema.node_) {}
82 136
83 Schema& Schema::operator=(const Schema& schema) { 137 Schema& Schema::operator=(const Schema& schema) {
84 schema_ = schema.schema_; 138 data_ = schema.data_;
139 node_ = schema.node_;
85 return *this; 140 return *this;
86 } 141 }
87 142
88 base::Value::Type Schema::type() const { 143 base::Value::Type Schema::type() const {
89 CHECK(valid()); 144 CHECK(valid());
90 return schema_->type; 145 return node_->type;
91 } 146 }
92 147
93 Schema::Iterator Schema::GetPropertiesIterator() const { 148 Schema::Iterator Schema::GetPropertiesIterator() const {
94 CHECK(valid()); 149 CHECK(valid());
95 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 150 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
96 return Iterator( 151 return Iterator(data_, data_->properties_nodes + node_->extra);
97 static_cast<const internal::PropertiesNode*>(schema_->extra));
98 } 152 }
99 153
100 namespace { 154 namespace {
101 155
102 bool CompareKeys(const internal::PropertyNode& node, const std::string& key) { 156 bool CompareKeys(const PropertyNode& node, const std::string& key) {
103 return node.key < key; 157 return node.key < key;
104 } 158 }
105 159
106 } // namespace 160 } // namespace
107 161
108 Schema Schema::GetKnownProperty(const std::string& key) const { 162 Schema Schema::GetKnownProperty(const std::string& key) const {
109 CHECK(valid()); 163 CHECK(valid());
110 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 164 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
111 const internal::PropertiesNode* properties_node = 165 const PropertiesNode* node = data_->properties_nodes + node_->extra;
112 static_cast<const internal::PropertiesNode*>(schema_->extra); 166 const PropertyNode* begin = data_->property_nodes + node->begin;
113 const internal::PropertyNode* it = std::lower_bound( 167 const PropertyNode* end = data_->property_nodes + node->end;
114 properties_node->begin, properties_node->end, key, CompareKeys); 168 const PropertyNode* it = std::lower_bound(begin, end, key, CompareKeys);
115 if (it != properties_node->end && it->key == key) 169 if (it != end && it->key == key)
116 return Schema(it->schema); 170 return Schema(data_, data_->schema_nodes + it->schema);
117 return Schema(); 171 return Schema();
118 } 172 }
119 173
120 Schema Schema::GetAdditionalProperties() const { 174 Schema Schema::GetAdditionalProperties() const {
121 CHECK(valid()); 175 CHECK(valid());
122 CHECK_EQ(base::Value::TYPE_DICTIONARY, type()); 176 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
123 return Schema( 177 const PropertiesNode* node = data_->properties_nodes + node_->extra;
124 static_cast<const internal::PropertiesNode*>(schema_->extra)->additional); 178 if (node->additional == kInvalid)
179 return Schema();
180 return Schema(data_, data_->schema_nodes + node->additional);
125 } 181 }
126 182
127 Schema Schema::GetProperty(const std::string& key) const { 183 Schema Schema::GetProperty(const std::string& key) const {
128 Schema schema = GetKnownProperty(key); 184 Schema schema = GetKnownProperty(key);
129 return schema.valid() ? schema : GetAdditionalProperties(); 185 return schema.valid() ? schema : GetAdditionalProperties();
130 } 186 }
131 187
132 Schema Schema::GetItems() const { 188 Schema Schema::GetItems() const {
133 CHECK(valid()); 189 CHECK(valid());
134 CHECK_EQ(base::Value::TYPE_LIST, type()); 190 CHECK_EQ(base::Value::TYPE_LIST, type());
135 return Schema(static_cast<const internal::SchemaNode*>(schema_->extra)); 191 if (node_->extra == kInvalid)
192 return Schema();
193 return Schema(data_, data_->schema_nodes + node_->extra);
136 } 194 }
137 195
138 SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {} 196 SchemaOwner::SchemaOwner(const SchemaData* data,
197 scoped_ptr<InternalStorage> storage)
198 : storage_(storage.Pass()), data_(data) {}
139 199
140 SchemaOwner::~SchemaOwner() { 200 SchemaOwner::~SchemaOwner() {}
141 for (size_t i = 0; i < property_nodes_.size(); ++i) 201
142 delete[] property_nodes_[i]; 202 Schema SchemaOwner::schema() const {
203 // data_->schema_nodes[0] is the root node.
204 return Schema(data_, data_->schema_nodes);
143 } 205 }
144 206
145 // static 207 // static
146 scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) { 208 scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const SchemaData* data) {
147 return scoped_ptr<SchemaOwner>(new SchemaOwner(schema)); 209 return make_scoped_ptr(new SchemaOwner(data, scoped_ptr<InternalStorage>()));
148 } 210 }
149 211
150 // static 212 // static
151 scoped_ptr<SchemaOwner> SchemaOwner::Parse(const std::string& content, 213 scoped_ptr<SchemaOwner> SchemaOwner::Parse(const std::string& content,
152 std::string* error) { 214 std::string* error) {
153 // Validate as a generic JSON schema. 215 // Validate as a generic JSON schema.
154 scoped_ptr<base::DictionaryValue> dict = 216 scoped_ptr<base::DictionaryValue> dict =
155 JSONSchemaValidator::IsValidSchema(content, error); 217 JSONSchemaValidator::IsValidSchema(content, error);
156 if (!dict) 218 if (!dict)
157 return scoped_ptr<SchemaOwner>(); 219 return scoped_ptr<SchemaOwner>();
158 220
159 // Validate the main type. 221 // Validate the main type.
160 std::string string_value; 222 std::string string_value;
161 if (!dict->GetString(json_schema_constants::kType, &string_value) || 223 if (!dict->GetString(json_schema_constants::kType, &string_value) ||
162 string_value != json_schema_constants::kObject) { 224 string_value != json_schema_constants::kObject) {
163 *error = 225 *error =
164 "The main schema must have a type attribute with \"object\" value."; 226 "The main schema must have a type attribute with \"object\" value.";
165 return scoped_ptr<SchemaOwner>(); 227 return scoped_ptr<SchemaOwner>();
166 } 228 }
167 229
168 // Checks for invalid attributes at the top-level. 230 // Checks for invalid attributes at the top-level.
169 if (dict->HasKey(json_schema_constants::kAdditionalProperties) || 231 if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
170 dict->HasKey(json_schema_constants::kPatternProperties)) { 232 dict->HasKey(json_schema_constants::kPatternProperties)) {
171 *error = "\"additionalProperties\" and \"patternProperties\" are not " 233 *error = "\"additionalProperties\" and \"patternProperties\" are not "
172 "supported at the main schema."; 234 "supported at the main schema.";
173 return scoped_ptr<SchemaOwner>(); 235 return scoped_ptr<SchemaOwner>();
174 } 236 }
175 237
176 scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL)); 238 scoped_ptr<InternalStorage> storage =
177 impl->root_ = impl->Parse(*dict, error); 239 InternalStorage::ParseSchema(*dict, error);
178 if (!impl->root_) 240 if (!storage)
179 impl.reset(); 241 return scoped_ptr<SchemaOwner>();
180 return impl.PassAs<SchemaOwner>(); 242 const SchemaData* data = storage->schema_data();
243 return make_scoped_ptr(new SchemaOwner(data, storage.Pass()));
181 } 244 }
182 245
183 const internal::SchemaNode* SchemaOwner::Parse( 246 // static
184 const base::DictionaryValue& schema, 247 scoped_ptr<SchemaOwner::InternalStorage>
185 std::string* error) { 248 SchemaOwner::InternalStorage::ParseSchema(const base::DictionaryValue& schema,
249 std::string* error) {
250 scoped_ptr<InternalStorage> storage(new InternalStorage);
251 if (storage->Parse(schema, error) == kInvalid)
252 return scoped_ptr<InternalStorage>();
253 SchemaData* data = &storage->schema_data_;
254 data->schema_nodes = vector_as_array(&storage->schema_nodes_);
255 data->property_nodes = vector_as_array(&storage->property_nodes_);
256 data->properties_nodes = vector_as_array(&storage->properties_nodes_);
257 return storage.Pass();
258 }
259
260 int SchemaOwner::InternalStorage::Parse(const base::DictionaryValue& schema,
261 std::string* error) {
186 std::string type_string; 262 std::string type_string;
187 if (!schema.GetString(json_schema_constants::kType, &type_string)) { 263 if (!schema.GetString(json_schema_constants::kType, &type_string)) {
188 *error = "The schema type must be declared."; 264 *error = "The schema type must be declared.";
189 return NULL; 265 return kInvalid;
190 } 266 }
191 267
192 base::Value::Type type = base::Value::TYPE_NULL; 268 base::Value::Type type = base::Value::TYPE_NULL;
193 if (!SchemaTypeToValueType(type_string, &type)) { 269 if (!SchemaTypeToValueType(type_string, &type)) {
194 *error = "Type not supported: " + type_string; 270 *error = "Type not supported: " + type_string;
195 return NULL; 271 return kInvalid;
196 } 272 }
197 273
198 if (type == base::Value::TYPE_DICTIONARY) 274 if (type == base::Value::TYPE_DICTIONARY)
199 return ParseDictionary(schema, error); 275 return ParseDictionary(schema, error);
200 if (type == base::Value::TYPE_LIST) 276 if (type == base::Value::TYPE_LIST)
201 return ParseList(schema, error); 277 return ParseList(schema, error);
202 278
203 internal::SchemaNode* node = new internal::SchemaNode; 279 int index = static_cast<int>(schema_nodes_.size());
204 node->type = type; 280 schema_nodes_.push_back(SchemaNode());
205 node->extra = NULL; 281 SchemaNode& node = schema_nodes_.back();
206 schema_nodes_.push_back(node); 282 node.type = type;
207 return node; 283 node.extra = kInvalid;
284 return index;
208 } 285 }
209 286
210 const internal::SchemaNode* SchemaOwner::ParseDictionary( 287 // static
288 int SchemaOwner::InternalStorage::ParseDictionary(
211 const base::DictionaryValue& schema, 289 const base::DictionaryValue& schema,
212 std::string* error) { 290 std::string* error) {
213 internal::PropertiesNode* properties_node = new internal::PropertiesNode; 291 // Note: recursive calls to Parse() invalidate iterators and references into
214 properties_node->begin = NULL; 292 // the vectors.
215 properties_node->end = NULL; 293
216 properties_node->additional = NULL; 294 // Reserve an index for this dictionary at the front, so that the root node
217 properties_nodes_.push_back(properties_node); 295 // is at index 0.
296 int schema_index = static_cast<int>(schema_nodes_.size());
297 schema_nodes_.push_back(SchemaNode());
298
299 int extra = static_cast<int>(properties_nodes_.size());
300 properties_nodes_.push_back(PropertiesNode());
301 properties_nodes_[extra].begin = kInvalid;
302 properties_nodes_[extra].end = kInvalid;
303 properties_nodes_[extra].additional = kInvalid;
218 304
219 const base::DictionaryValue* dict = NULL; 305 const base::DictionaryValue* dict = NULL;
306 if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
307 &dict)) {
308 int additional = Parse(*dict, error);
309 if (additional == kInvalid)
310 return kInvalid;
311 properties_nodes_[extra].additional = additional;
312 }
313
220 const base::DictionaryValue* properties = NULL; 314 const base::DictionaryValue* properties = NULL;
221 if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) { 315 if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
222 internal::PropertyNode* property_nodes = 316 int base_index = static_cast<int>(property_nodes_.size());
223 new internal::PropertyNode[properties->size()]; 317 // This reserves nodes for all of the |properties|, and makes sure they
224 property_nodes_.push_back(property_nodes); 318 // are contiguous. Recursive calls to Parse() will append after these
319 // elements.
320 property_nodes_.resize(base_index + properties->size());
225 321
226 size_t index = 0; 322 int index = base_index;
227 for (base::DictionaryValue::Iterator it(*properties); 323 for (base::DictionaryValue::Iterator it(*properties);
228 !it.IsAtEnd(); it.Advance(), ++index) { 324 !it.IsAtEnd(); it.Advance(), ++index) {
229 // This should have been verified by the JSONSchemaValidator. 325 // This should have been verified by the JSONSchemaValidator.
230 CHECK(it.value().GetAsDictionary(&dict)); 326 CHECK(it.value().GetAsDictionary(&dict));
231 const internal::SchemaNode* sub_schema = Parse(*dict, error); 327 int extra = Parse(*dict, error);
232 if (!sub_schema) 328 if (extra == kInvalid)
233 return NULL; 329 return kInvalid;
234 std::string* key = new std::string(it.key()); 330 strings_.push_back(new std::string(it.key()));
235 keys_.push_back(key); 331 property_nodes_[index].key = strings_.back()->c_str();
236 property_nodes[index].key = key->c_str(); 332 property_nodes_[index].schema = extra;
237 property_nodes[index].schema = sub_schema;
238 } 333 }
239 CHECK_EQ(properties->size(), index); 334 CHECK_EQ(static_cast<int>(properties->size()), index - base_index);
240 properties_node->begin = property_nodes; 335 properties_nodes_[extra].begin = base_index;
241 properties_node->end = property_nodes + index; 336 properties_nodes_[extra].end = index;
242 } 337 }
243 338
244 if (schema.GetDictionary(json_schema_constants::kAdditionalProperties, 339 schema_nodes_[schema_index].type = base::Value::TYPE_DICTIONARY;
245 &dict)) { 340 schema_nodes_[schema_index].extra = extra;
246 const internal::SchemaNode* sub_schema = Parse(*dict, error); 341 return schema_index;
247 if (!sub_schema)
248 return NULL;
249 properties_node->additional = sub_schema;
250 }
251
252 internal::SchemaNode* schema_node = new internal::SchemaNode;
253 schema_node->type = base::Value::TYPE_DICTIONARY;
254 schema_node->extra = properties_node;
255 schema_nodes_.push_back(schema_node);
256 return schema_node;
257 } 342 }
258 343
259 const internal::SchemaNode* SchemaOwner::ParseList( 344 // static
260 const base::DictionaryValue& schema, 345 int SchemaOwner::InternalStorage::ParseList(const base::DictionaryValue& schema,
261 std::string* error) { 346 std::string* error) {
262 const base::DictionaryValue* dict = NULL; 347 const base::DictionaryValue* dict = NULL;
263 if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) { 348 if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
264 *error = "Arrays must declare a single schema for their items."; 349 *error = "Arrays must declare a single schema for their items.";
265 return NULL; 350 return kInvalid;
266 } 351 }
267 const internal::SchemaNode* items_schema_node = Parse(*dict, error); 352 int extra = Parse(*dict, error);
268 if (!items_schema_node) 353 if (extra == kInvalid)
269 return NULL; 354 return kInvalid;
270 355 int index = static_cast<int>(schema_nodes_.size());
271 internal::SchemaNode* schema_node = new internal::SchemaNode; 356 schema_nodes_.push_back(SchemaNode());
272 schema_node->type = base::Value::TYPE_LIST; 357 schema_nodes_[index].type = base::Value::TYPE_LIST;
273 schema_node->extra = items_schema_node; 358 schema_nodes_[index].extra = extra;
274 schema_nodes_.push_back(schema_node); 359 return index;
275 return schema_node;
276 } 360 }
277 361
278 } // namespace policy 362 } // namespace policy
OLDNEW
« no previous file with comments | « components/policy/core/common/schema.h ('k') | components/policy/core/common/schema_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698