summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/settings/settings_frontend.cc44
-rw-r--r--chrome/common/extensions/api/api.gyp1
-rw-r--r--chrome/common/extensions/api/storage.json12
-rw-r--r--chrome/common/extensions/api/windows.json6
-rw-r--r--chrome/common/extensions/docs/js/api_page_generator.js33
-rw-r--r--chrome/renderer/resources/extensions/event.js2
-rw-r--r--chrome/renderer/resources/extensions/json_schema.js4
-rw-r--r--chrome/renderer/resources/extensions/schema_generated_bindings.js19
-rw-r--r--chrome/renderer/resources/extensions/send_request.js4
-rw-r--r--tools/json_schema_compiler/cc_generator.py45
-rw-r--r--tools/json_schema_compiler/cpp_type_generator.py42
-rw-r--r--tools/json_schema_compiler/h_generator.py32
-rw-r--r--tools/json_schema_compiler/model.py147
-rwxr-xr-xtools/json_schema_compiler/model_test.py8
14 files changed, 239 insertions, 160 deletions
diff --git a/chrome/browser/extensions/settings/settings_frontend.cc b/chrome/browser/extensions/settings/settings_frontend.cc
index 0c2c51d..f20e58d 100644
--- a/chrome/browser/extensions/settings/settings_frontend.cc
+++ b/chrome/browser/extensions/settings/settings_frontend.cc
@@ -15,7 +15,7 @@
#include "chrome/browser/extensions/settings/settings_leveldb_storage.h"
#include "chrome/browser/extensions/settings/weak_unlimited_settings_storage.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/extensions/api/extension_api.h"
+#include "chrome/common/extensions/api/storage.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
@@ -88,36 +88,20 @@ void CallbackWithUnlimitedStorage(
callback.Run(&unlimited_storage);
}
-// Returns the integer at |path| in |dict| as a size_t, or a default value if
-// there's nothing found at that path.
-size_t GetStringAsInteger(
- const DictionaryValue& dict, const std::string& path, size_t default_size) {
- std::string as_string;
- if (!dict.GetString(path, &as_string))
- return default_size;
- size_t as_integer = default_size;
- CHECK(base::StringToSizeT(as_string, &as_integer));
- return as_integer;
+SettingsStorageQuotaEnforcer::Limits GetLocalLimits() {
+ SettingsStorageQuotaEnforcer::Limits limits = {
+ api::storage::local::QUOTA_BYTES,
+ UINT_MAX,
+ UINT_MAX
+ };
+ return limits;
}
-// Constructs a |Limits| configuration by looking up the QUOTA_BYTES,
-// QUOTA_BYTES_PER_ITEM, and MAX_ITEMS properties of a storage area defined
-// in chrome/common/extensions/api/storage.json (via ExtensionAPI).
-SettingsStorageQuotaEnforcer::Limits GetLimitsFromExtensionAPI(
- const std::string& storage_area_id) {
- const DictionaryValue* storage_schema =
- ExtensionAPI::GetSharedInstance()->GetSchema("storage");
- CHECK(storage_schema);
-
- DictionaryValue* properties = NULL;
- storage_schema->GetDictionary(
- "properties." + storage_area_id + ".properties", &properties);
- CHECK(properties);
-
+SettingsStorageQuotaEnforcer::Limits GetSyncLimits() {
SettingsStorageQuotaEnforcer::Limits limits = {
- GetStringAsInteger(*properties, "QUOTA_BYTES.value", UINT_MAX),
- GetStringAsInteger(*properties, "QUOTA_BYTES_PER_ITEM.value", UINT_MAX),
- GetStringAsInteger(*properties, "MAX_ITEMS.value", UINT_MAX),
+ api::storage::sync::QUOTA_BYTES,
+ api::storage::sync::QUOTA_BYTES_PER_ITEM,
+ api::storage::sync::MAX_ITEMS
};
return limits;
}
@@ -232,8 +216,8 @@ SettingsFrontend* SettingsFrontend::Create(
SettingsFrontend::SettingsFrontend(
const scoped_refptr<SettingsStorageFactory>& factory, Profile* profile)
- : local_quota_limit_(GetLimitsFromExtensionAPI("local")),
- sync_quota_limit_(GetLimitsFromExtensionAPI("sync")),
+ : local_quota_limit_(GetLocalLimits()),
+ sync_quota_limit_(GetSyncLimits()),
profile_(profile),
observers_(new SettingsObserverList()),
profile_observer_(new DefaultObserver(profile)) {
diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp
index 17e773a..7646399 100644
--- a/chrome/common/extensions/api/api.gyp
+++ b/chrome/common/extensions/api/api.gyp
@@ -21,6 +21,7 @@
'browserAction.json',
'experimental.declarative.json',
'permissions.json',
+ 'storage.json',
'tabs.json',
'windows.json',
],
diff --git a/chrome/common/extensions/api/storage.json b/chrome/common/extensions/api/storage.json
index 998ac6b..1b9b298 100644
--- a/chrome/common/extensions/api/storage.json
+++ b/chrome/common/extensions/api/storage.json
@@ -175,18 +175,15 @@
"value": [ "sync" ],
"properties": {
"QUOTA_BYTES": {
- "type": "integer",
- "value": "102400",
+ "value": 102400,
"description": "The maximum total amount (in bytes) of data that can be stored in sync storage."
},
"QUOTA_BYTES_PER_ITEM": {
- "type": "integer",
- "value": "2048",
+ "value": 2048,
"description": "The maximum size (in bytes) of each individual item in sync storage."
},
"MAX_ITEMS": {
- "type": "integer",
- "value": "512",
+ "value": 512,
"description": "The maximum number of items that can be stored in sync storage."
}
}
@@ -197,8 +194,7 @@
"value": [ "local" ],
"properties": {
"QUOTA_BYTES": {
- "type": "integer",
- "value": "5242880",
+ "value": 5242880,
"description": "The maximum amount (in bytes) of data that can be stored in local storage. This value will be ignored if the extension has the <code>unlimitedStorage</code> permission."
}
}
diff --git a/chrome/common/extensions/api/windows.json b/chrome/common/extensions/api/windows.json
index 573a93c..80fe9cf 100644
--- a/chrome/common/extensions/api/windows.json
+++ b/chrome/common/extensions/api/windows.json
@@ -35,13 +35,11 @@
],
"properties": {
"WINDOW_ID_NONE": {
- "type": "integer",
- "value": "-1",
+ "value": -1,
"description": "The windowId value that represents the absence of a chrome browser window."
},
"WINDOW_ID_CURRENT": {
- "type": "integer",
- "value": "-2",
+ "value": -2,
"description": "The windowId value that represents the <a href='windows.html#current-window'>current window</a>."
}
},
diff --git a/chrome/common/extensions/docs/js/api_page_generator.js b/chrome/common/extensions/docs/js/api_page_generator.js
index 1569f32..70d7545 100644
--- a/chrome/common/extensions/docs/js/api_page_generator.js
+++ b/chrome/common/extensions/docs/js/api_page_generator.js
@@ -645,25 +645,28 @@ function getTypeName(schema) {
}
function hasPrimitiveValue(schema) {
- return typeof(schema.value) === 'string';
+ var type = typeof(schema.value);
+ return type === 'string' || type === 'number';
}
function getPrimitiveValue(schema) {
- if (schema.type === 'string') {
- return '"' + schema.value + '"';
- } else if (schema.type === 'integer') {
- // Comma-separate large numbers (e.g. 5,000,000), easier to read.
- var value = String(schema.value);
- var groupsOfThree = [];
- while (value.length > 3) {
- groupsOfThree.unshift(value.slice(value.length - 3));
- value = value.slice(0, value.length - 3);
- }
- groupsOfThree.unshift(value);
- return groupsOfThree.join(',');
- } else {
- return schema.value;
+ switch (typeof(schema.value)) {
+ case 'string':
+ return '"' + schema.value + '"';
+
+ case 'number':
+ // Comma-separate large numbers (e.g. 5,000,000), easier to read.
+ var value = String(schema.value);
+ var groupsOfThree = [];
+ while (value.length > 3) {
+ groupsOfThree.unshift(value.slice(value.length - 3));
+ value = value.slice(0, value.length - 3);
+ }
+ groupsOfThree.unshift(value);
+ return groupsOfThree.join(',');
}
+
+ return undefined;
}
function getSignatureString(parameters) {
diff --git a/chrome/renderer/resources/extensions/event.js b/chrome/renderer/resources/extensions/event.js
index 63b7d7f..5db2a59 100644
--- a/chrome/renderer/resources/extensions/event.js
+++ b/chrome/renderer/resources/extensions/event.js
@@ -205,7 +205,7 @@
results.push(result);
} catch (e) {
console.error("Error in event handler for '" + this.eventName_ +
- "': " + e.stack);
+ "': " + e.message + ' ' + e.stack);
}
}
if (results.length)
diff --git a/chrome/renderer/resources/extensions/json_schema.js b/chrome/renderer/resources/extensions/json_schema.js
index bc51d97..ec4b48d 100644
--- a/chrome/renderer/resources/extensions/json_schema.js
+++ b/chrome/renderer/resources/extensions/json_schema.js
@@ -137,8 +137,8 @@ chromeHidden.JSONSchemaValidator.getType = function(value) {
*/
chromeHidden.JSONSchemaValidator.prototype.addTypes = function(typeOrTypeList) {
function addType(validator, type) {
- if(!type.id)
- throw "Attempt to addType with missing 'id' property";
+ if (!type.id)
+ throw new Error("Attempt to addType with missing 'id' property");
validator.types[type.id] = type;
}
diff --git a/chrome/renderer/resources/extensions/schema_generated_bindings.js b/chrome/renderer/resources/extensions/schema_generated_bindings.js
index 4da0dba..66c193a 100644
--- a/chrome/renderer/resources/extensions/schema_generated_bindings.js
+++ b/chrome/renderer/resources/extensions/schema_generated_bindings.js
@@ -522,9 +522,14 @@
var value = propertyDef.value;
if (value) {
- if (propertyDef.type === 'integer') {
+ // Values may just have raw types as defined in the JSON, such
+ // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
+ // TODO(kalman): enforce that things with a "value" property can't
+ // define their own types.
+ var type = propertyDef.type || typeof(value);
+ if (type === 'integer' || type === 'number') {
value = parseInt(value);
- } else if (propertyDef.type === 'boolean') {
+ } else if (type === 'boolean') {
value = value === "true";
} else if (propertyDef["$ref"]) {
var constructor = customTypes[propertyDef["$ref"]];
@@ -539,15 +544,13 @@
constructor.apply(value, args);
// Recursively add properties.
addProperties(value, propertyDef);
- } else if (propertyDef.type === 'object') {
+ } else if (type === 'object') {
// Recursively add properties.
addProperties(value, propertyDef);
- } else if (propertyDef.type !== 'string') {
- throw "NOT IMPLEMENTED (extension_api.json error): Cannot " +
- "parse values for type \"" + propertyDef.type + "\"";
+ } else if (type !== 'string') {
+ throw new Error("NOT IMPLEMENTED (extension_api.json error): " +
+ "Cannot parse values for type \"" + type + "\"");
}
- }
- if (value) {
m[propertyName] = value;
}
});
diff --git a/chrome/renderer/resources/extensions/send_request.js b/chrome/renderer/resources/extensions/send_request.js
index fff9c90..3e47f40 100644
--- a/chrome/renderer/resources/extensions/send_request.js
+++ b/chrome/renderer/resources/extensions/send_request.js
@@ -38,11 +38,11 @@ chromeHidden.handleResponse = function(requestId, name,
if (chromeHidden.validateCallbacks && !error) {
try {
if (!request.callbackSchema.parameters) {
- throw "No callback schemas defined";
+ throw new Error("No callback schemas defined");
}
if (request.callbackSchema.parameters.length > 1) {
- throw "Callbacks may only define one parameter";
+ throw new Error("Callbacks may only define one parameter");
}
chromeHidden.validate(callbackArgs,
diff --git a/tools/json_schema_compiler/cc_generator.py b/tools/json_schema_compiler/cc_generator.py
index 138f6bf..7f7cb56 100644
--- a/tools/json_schema_compiler/cc_generator.py
+++ b/tools/json_schema_compiler/cc_generator.py
@@ -2,9 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from code import Code
from model import PropertyType
import any_helper
-import code
import cpp_util
import model
import sys
@@ -23,9 +23,9 @@ class CCGenerator(object):
self._any_helper = any_helper.AnyHelper()
def Generate(self):
- """Generates a code.Code object with the .cc for a single namespace.
+ """Generates a Code object with the .cc for a single namespace.
"""
- c = code.Code()
+ c = Code()
(c.Append(cpp_util.CHROMIUM_LICENSE)
.Append()
.Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
@@ -50,6 +50,19 @@ class CCGenerator(object):
.Concat(self._cpp_type_generator.GetNamespaceStart())
.Append()
)
+ if self._namespace.properties:
+ (c.Append('//')
+ .Append('// Properties')
+ .Append('//')
+ .Append()
+ )
+ for property in self._namespace.properties.values():
+ property_code = self._cpp_type_generator.GeneratePropertyValues(
+ property,
+ 'const %(type)s %(name)s = %(value)s;',
+ nodoc=True)
+ if property_code:
+ c.Concat(property_code).Append()
if self._namespace.types:
(c.Append('//')
.Append('// Types')
@@ -82,7 +95,7 @@ class CCGenerator(object):
"""Generates the function definitions for a type.
"""
classname = cpp_util.Classname(type_.name)
- c = code.Code()
+ c = Code()
if type_.functions:
# Types with functions are not instantiable in C++ because they are
@@ -151,7 +164,7 @@ class CCGenerator(object):
else:
s = ''
s = s + ' {}'
- return code.Code().Append(s)
+ return Code().Append(s)
def _GenerateTypePopulate(self, cpp_namespace, type_):
"""Generates the function for populating a type given a pointer to it.
@@ -159,7 +172,7 @@ class CCGenerator(object):
E.g for type "Foo", generates Foo::Populate()
"""
classname = cpp_util.Classname(type_.name)
- c = code.Code()
+ c = Code()
(c.Append('// static')
.Sblock('bool %(namespace)s::Populate'
'(const Value& value, %(name)s* out) {')
@@ -194,7 +207,7 @@ class CCGenerator(object):
src: DictionaryValue*
dst: Type*
"""
- c = code.Code()
+ c = Code()
value_var = prop.unix_name + '_value'
c.Append('Value* %(value_var)s = NULL;')
if prop.optional:
@@ -221,7 +234,7 @@ class CCGenerator(object):
E.g. for type "Foo" generates Foo::ToValue()
"""
- c = code.Code()
+ c = Code()
(c.Sblock('scoped_ptr<DictionaryValue> %s::ToValue() const {' %
cpp_namespace)
.Append('scoped_ptr<DictionaryValue> value(new DictionaryValue());')
@@ -252,7 +265,7 @@ class CCGenerator(object):
def _GenerateFunction(self, cpp_namespace, function):
"""Generates the definitions for function structs.
"""
- c = code.Code()
+ c = Code()
# Params::Populate function
if function.params:
@@ -277,7 +290,7 @@ class CCGenerator(object):
"""Generates CreateEnumValue() that returns the |StringValue|
representation of an enum.
"""
- c = code.Code()
+ c = Code()
c.Append('// static')
c.Sblock('scoped_ptr<Value> %(cpp_namespace)s::CreateEnumValue(%(arg)s) {')
c.Sblock('switch (%s) {' % prop.unix_name)
@@ -353,7 +366,7 @@ class CCGenerator(object):
"""Generates a check for the correct number of arguments when creating
Params.
"""
- c = code.Code()
+ c = Code()
num_required = 0
for param in function.params:
if not param.optional:
@@ -379,7 +392,7 @@ class CCGenerator(object):
E.g for function "Bar", generate Bar::Params::Create()
"""
- c = code.Code()
+ c = Code()
(c.Append('// static')
.Sblock('scoped_ptr<%(cpp_namespace)s::Params> '
'%(cpp_namespace)s::Params::Create(const ListValue& args) {')
@@ -434,7 +447,7 @@ class CCGenerator(object):
|value_var|
check_type: if true, will check if |value_var| is the correct Value::Type
"""
- c = code.Code()
+ c = Code()
c.Sblock('{')
if check_type and prop.type_ not in (
@@ -544,7 +557,7 @@ class CCGenerator(object):
"""Generate the functions for structures generated by a property such as
CreateEnumValue for ENUMs and Populate/ToValue for Params/Result objects.
"""
- c = code.Code()
+ c = Code()
for param in params:
if param.type_ == PropertyType.OBJECT:
c.Concat(self._GenerateType(
@@ -564,7 +577,7 @@ class CCGenerator(object):
E.g for function "Bar", generate Bar::Result::Create
"""
- c = code.Code()
+ c = Code()
params = function.callback.params
if not params:
@@ -609,7 +622,7 @@ class CCGenerator(object):
dst: Type*
"""
- c = code.Code()
+ c = Code()
if prop.type_ in (PropertyType.ENUM, PropertyType.CHOICES):
if prop.optional:
prop_name = prop.unix_name
diff --git a/tools/json_schema_compiler/cpp_type_generator.py b/tools/json_schema_compiler/cpp_type_generator.py
index c73589e..a8719af 100644
--- a/tools/json_schema_compiler/cpp_type_generator.py
+++ b/tools/json_schema_compiler/cpp_type_generator.py
@@ -263,3 +263,45 @@ class CppTypeGenerator(object):
for p in prop.properties.values():
deps |= self._PropertyTypeDependencies(p)
return deps
+
+ def GeneratePropertyValues(self, property, line, nodoc=False):
+ """Generates the Code to display all value-containing properties.
+ """
+ c = Code()
+ if not nodoc:
+ c.Comment(property.description)
+
+ if property.has_value:
+ c.Append(line % {
+ "type": self._GetPrimitiveType(property.type_),
+ "name": property.name,
+ "value": property.value
+ })
+ else:
+ has_child_code = False
+ c.Sblock('namespace %s {' % property.name)
+ for child_property in property.properties.values():
+ child_code = self.GeneratePropertyValues(
+ child_property,
+ line,
+ nodoc=nodoc)
+ if child_code:
+ has_child_code = True
+ c.Concat(child_code)
+ c.Eblock('} // namespace %s' % property.name)
+ if not has_child_code:
+ c = None
+ return c
+
+ def _GetPrimitiveType(self, type_):
+ """Like |GetType| but only accepts and returns C++ primitive types.
+ """
+ if type_ == PropertyType.BOOLEAN:
+ return 'bool'
+ elif type_ == PropertyType.INTEGER:
+ return 'int'
+ elif type_ == PropertyType.DOUBLE:
+ return 'double'
+ elif type_ == PropertyType.STRING:
+ return 'char*'
+ raise Exception(type_ + ' is not primitive')
diff --git a/tools/json_schema_compiler/h_generator.py b/tools/json_schema_compiler/h_generator.py
index dd07f4f..35e8248 100644
--- a/tools/json_schema_compiler/h_generator.py
+++ b/tools/json_schema_compiler/h_generator.py
@@ -2,8 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from code import Code
from model import PropertyType
-import code
import cpp_util
import model
import os
@@ -18,9 +18,9 @@ class HGenerator(object):
self._cpp_type_generator.GetCppNamespaceName(self._namespace))
def Generate(self):
- """Generates a code.Code object with the .h for a single namespace.
+ """Generates a Code object with the .h for a single namespace.
"""
- c = code.Code()
+ c = Code()
(c.Append(cpp_util.CHROMIUM_LICENSE)
.Append()
.Append(cpp_util.GENERATED_FILE_MESSAGE % self._namespace.source_file)
@@ -59,6 +59,18 @@ class HGenerator(object):
c.Concat(self._cpp_type_generator.GetNamespaceStart())
c.Append()
+ if self._namespace.properties:
+ (c.Append('//')
+ .Append('// Properties')
+ .Append('//')
+ .Append()
+ )
+ for property in self._namespace.properties.values():
+ property_code = self._cpp_type_generator.GeneratePropertyValues(
+ property,
+ 'extern const %(type)s %(name)s;')
+ if property_code:
+ c.Concat(property_code).Append()
if self._namespace.types:
(c.Append('//')
.Append('// Types')
@@ -111,7 +123,7 @@ class HGenerator(object):
"""Generate the declaration of a C++ enum for the given property and
values.
"""
- c = code.Code()
+ c = Code()
c.Sblock('enum %s {' % enum_name)
if prop.optional:
c.Append(self._cpp_type_generator.GetEnumNoneValue(prop) + ',')
@@ -125,7 +137,7 @@ class HGenerator(object):
def _GenerateFields(self, props):
"""Generates the field declarations when declaring a type.
"""
- c = code.Code()
+ c = Code()
# Generate the enums needed for any fields with "choices"
for prop in props:
if prop.type_ == PropertyType.CHOICES:
@@ -145,7 +157,7 @@ class HGenerator(object):
"""Generates a struct for a type.
"""
classname = cpp_util.Classname(type_.name)
- c = code.Code()
+ c = Code()
if type_.functions:
# Types with functions are not instantiable in C++ because they are
@@ -203,7 +215,7 @@ class HGenerator(object):
def _GenerateFunction(self, function):
"""Generates the structs for a function.
"""
- c = code.Code()
+ c = Code()
(c.Sblock('namespace %s {' % cpp_util.Classname(function.name))
.Concat(self._GenerateFunctionParams(function))
.Append()
@@ -219,7 +231,7 @@ class HGenerator(object):
def _GenerateFunctionParams(self, function):
"""Generates the struct for passing parameters into a function.
"""
- c = code.Code()
+ c = Code()
if function.params:
(c.Sblock('struct Params {')
@@ -242,7 +254,7 @@ class HGenerator(object):
"""Generate the structures required by a property such as OBJECT classes
and enums.
"""
- c = code.Code()
+ c = Code()
for prop in props:
if prop.type_ == PropertyType.OBJECT:
c.Concat(self._GenerateType(prop))
@@ -266,7 +278,7 @@ class HGenerator(object):
def _GenerateFunctionResult(self, function):
"""Generates functions for passing a function's result back.
"""
- c = code.Code()
+ c = Code()
c.Sblock('namespace Result {')
params = function.callback.params
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index 9a5c55f..6dc20fe 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -6,6 +6,15 @@ import copy
import os.path
import re
+class ParseException(Exception):
+ """Thrown when data in the model is invalid.
+ """
+ def __init__(self, parent, message):
+ hierarchy = _GetModelHierarchy(parent)
+ hierarchy.append(message)
+ Exception.__init__(
+ self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
+
class Model(object):
"""Model of all namespaces that comprise an API.
@@ -33,24 +42,17 @@ class Namespace(object):
- |source_file_filename| the filename component of |source_file|
- |types| a map of type names to their model.Type
- |functions| a map of function names to their model.Function
+ - |properties| a map of property names to their model.Property
"""
def __init__(self, json, source_file):
self.name = json['namespace']
- self.unix_name = UnixName(self.name)
+ self.unix_name = _UnixName(self.name)
self.source_file = source_file
self.source_file_dir, self.source_file_filename = os.path.split(source_file)
- self.types = {}
- self.functions = {}
self.parent = None
- # TODO(calamity): Implement properties on namespaces for shared structures
- # or constants across a namespace (e.g Windows::WINDOW_ID_NONE).
- for property_json in json.get('properties', []):
- pass
- for type_json in json.get('types', []):
- type_ = Type(self, type_json['id'], type_json)
- self.types[type_.name] = type_
- for function_json in json.get('functions', []):
- self.functions[function_json['name']] = Function(self, function_json)
+ _AddTypes(self, json)
+ _AddFunctions(self, json)
+ _AddProperties(self, json)
class Type(object):
"""A Type defined in the json.
@@ -79,44 +81,24 @@ class Type(object):
'properties' in json or
'additionalProperties' in json or
'functions' in json):
- raise ParseException(name + " has no properties or functions")
+ raise ParseException(self, name + " has no properties or functions")
self.type_ = PropertyType.OBJECT
self.name = name
self.description = json.get('description')
self.from_json = True
self.from_client = True
- self.properties = {}
- self.functions = {}
self.parent = parent
- for function_json in json.get('functions', []):
- self.functions[function_json['name']] = Function(self, function_json)
- props = []
- for prop_name, prop_json in json.get('properties', {}).items():
- # TODO(calamity): support functions (callbacks) as properties. The model
- # doesn't support it yet because the h/cc generators don't -- this is
- # because we'd need to hook it into a base::Callback or something.
- #
- # However, pragmatically it's not necessary to support them anyway, since
- # the instances of functions-on-properties in the extension APIs are all
- # handled in pure Javascript on the render process (and .: never reach
- # C++ let alone the browser).
- if prop_json.get('type') == 'function':
- continue
- props.append(Property(self, prop_name, prop_json,
- from_json=True,
- from_client=True))
+ _AddFunctions(self, json)
+ _AddProperties(self, json, from_json=True, from_client=True)
- additional_properties = json.get('additionalProperties')
+ additional_properties_key = 'additionalProperties'
+ additional_properties = json.get(additional_properties_key)
if additional_properties:
- props.append(Property(self, 'additionalProperties', additional_properties,
- is_additional_properties=True))
-
- for prop in props:
- if prop.unix_name in self.properties:
- raise ParseException(
- self.properties[prop.unix_name].name + ' and ' + prop.name +
- ' are both named ' + prop.unix_name)
- self.properties[prop.unix_name] = prop
+ self.properties[additional_properties_key] = Property(
+ self,
+ additional_properties_key,
+ additional_properties,
+ is_additional_properties=True)
class Callback(object):
"""A callback parameter to a Function.
@@ -135,7 +117,9 @@ class Callback(object):
self.params.append(Property(self, param['name'], param,
from_client=True))
else:
- raise ParseException("Callbacks can have at most a single parameter")
+ raise ParseException(
+ self,
+ "Callbacks can have at most a single parameter")
class Function(object):
"""A Function defined in the API.
@@ -157,7 +141,7 @@ class Function(object):
for param in json['parameters']:
if param.get('type') == 'function':
if self.callback:
- raise ParseException(self.name + " has more than one callback")
+ raise ParseException(self, self.name + " has more than one callback")
self.callback = Callback(self, param)
else:
self.params.append(Property(self, param['name'], param,
@@ -191,11 +175,13 @@ class Property(object):
users of generated code, such as top-level types and function results
"""
self.name = name
- self._unix_name = UnixName(self.name)
+ self._unix_name = _UnixName(self.name)
self._unix_name_used = False
self.optional = json.get('optional', False)
+ self.has_value = False
self.description = json.get('description')
self.parent = parent
+ _AddProperties(self, json)
if is_additional_properties:
self.type_ = PropertyType.ADDITIONAL_PROPERTIES
elif '$ref' in json:
@@ -226,17 +212,17 @@ class Property(object):
elif json_type == 'object':
self.type_ = PropertyType.OBJECT
# These members are read when this OBJECT Property is used as a Type
- self.properties = {}
self.from_json = from_json
self.from_client = from_client
type_ = Type(self, self.name, json)
- self.properties = type_.properties
+ # self.properties will already have some value from |_AddProperties|.
+ self.properties.update(type_.properties)
self.functions = type_.functions
else:
raise ParseException(self, 'type ' + json_type + ' not recognized')
elif 'choices' in json:
if not json['choices']:
- raise ParseException('Choices has no choices')
+ raise ParseException(self, 'Choices has no choices')
self.choices = {}
self.type_ = PropertyType.CHOICES
for choice_json in json['choices']:
@@ -249,13 +235,23 @@ class Property(object):
# The existence of any single choice is optional
choice.optional = True
self.choices[choice.type_] = choice
+ elif 'value' in json:
+ self.has_value = True
+ self.value = json['value']
+ if type(self.value) == int:
+ self.type_ = PropertyType.INTEGER
+ else:
+ # TODO(kalman): support more types as necessary.
+ raise ParseException(
+ self, '"%s" is not a supported type' % type(self.value))
else:
- raise ParseException('Property has no type, $ref or choices')
+ raise ParseException(
+ self, 'Property has no type, $ref, choices, or value')
def GetUnixName(self):
"""Gets the property's unix_name. Raises AttributeError if not set.
"""
- if self._unix_name is None:
+ if not self._unix_name:
raise AttributeError('No unix_name set on %s' % self.name)
self._unix_name_used = True
return self._unix_name
@@ -306,7 +302,7 @@ class PropertyType(object):
ANY = _Info(False, "ANY")
ADDITIONAL_PROPERTIES = _Info(False, "ADDITIONAL_PROPERTIES")
-def UnixName(name):
+def _UnixName(name):
"""Returns the unix_style name for a given lowerCamelCase string.
"""
# First replace any lowerUpper patterns with lower_Upper.
@@ -316,15 +312,7 @@ def UnixName(name):
# Finally, replace any remaining periods, and make lowercase.
return s2.replace('.', '_').lower()
-class ParseException(Exception):
- """Thrown when data in the model is invalid."""
- def __init__(self, parent, message):
- hierarchy = GetModelHierarchy(parent)
- hierarchy.append(message)
- Exception.__init__(
- self, 'Model parse exception at:\n' + '\n'.join(hierarchy))
-
-def GetModelHierarchy(entity):
+def _GetModelHierarchy(entity):
"""Returns the hierarchy of the given model entity."""
hierarchy = []
while entity:
@@ -335,3 +323,42 @@ def GetModelHierarchy(entity):
entity = entity.parent
hierarchy.reverse()
return hierarchy
+
+def _AddTypes(model, json):
+ """Adds Type objects to |model| contained in the 'types' field of |json|.
+ """
+ model.types = {}
+ for type_json in json.get('types', []):
+ type_ = Type(model, type_json['id'], type_json)
+ model.types[type_.name] = type_
+
+def _AddFunctions(model, json):
+ """Adds Function objects to |model| contained in the 'types' field of |json|.
+ """
+ model.functions = {}
+ for function_json in json.get('functions', []):
+ function = Function(model, function_json)
+ model.functions[function.name] = function
+
+def _AddProperties(model, json, from_json=False, from_client=False):
+ """Adds model.Property objects to |model| contained in the 'properties' field
+ of |json|.
+ """
+ model.properties = {}
+ for name, property_json in json.get('properties', {}).items():
+ # TODO(calamity): support functions (callbacks) as properties. The model
+ # doesn't support it yet because the h/cc generators don't -- this is
+ # because we'd need to hook it into a base::Callback or something.
+ #
+ # However, pragmatically it's not necessary to support them anyway, since
+ # the instances of functions-on-properties in the extension APIs are all
+ # handled in pure Javascript on the render process (and .: never reach
+ # C++ let alone the browser).
+ if property_json.get('type') == 'function':
+ continue
+ model.properties[name] = Property(
+ model,
+ name,
+ property_json,
+ from_json=from_json,
+ from_client=from_client)
diff --git a/tools/json_schema_compiler/model_test.py b/tools/json_schema_compiler/model_test.py
index 10f8656..577b3ca 100755
--- a/tools/json_schema_compiler/model_test.py
+++ b/tools/json_schema_compiler/model_test.py
@@ -37,9 +37,9 @@ class ModelTest(unittest.TestCase):
self.assertEquals(['Window'], self.windows.types.keys())
def testHasProperties(self):
- self.assertEquals(["active", "fav_icon_url", "highlighted", "id",
+ self.assertEquals(["active", "favIconUrl", "highlighted", "id",
"incognito", "index", "pinned", "selected", "status", "title", "url",
- "window_id"],
+ "windowId"],
sorted(self.tabs.types['Tab'].properties.keys()))
def testProperties(self):
@@ -55,7 +55,7 @@ class ModelTest(unittest.TestCase):
self.assertEquals(model.PropertyType.OBJECT, object_prop.type_)
self.assertEquals(
["active", "highlighted", "pinned", "status", "title", "url",
- "window_id", "window_type"],
+ "windowId", "windowType"],
sorted(object_prop.properties.keys()))
def testChoices(self):
@@ -100,7 +100,7 @@ class ModelTest(unittest.TestCase):
'foo.barBAZ': 'foo_bar_baz'
}
for name in expectations:
- self.assertEquals(expectations[name], model.UnixName(name));
+ self.assertEquals(expectations[name], model._UnixName(name));
if __name__ == '__main__':
unittest.main()