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

Unified Diff: headless/lib/browser/devtools_api/client_api_generator.py

Issue 2473073003: [headless] Refactor headless devtools client API. (Closed)
Patch Set: Address nits Created 4 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 side-by-side diff with in-line comments
Download patch
Index: headless/lib/browser/devtools_api/client_api_generator.py
diff --git a/headless/lib/browser/client_api_generator.py b/headless/lib/browser/devtools_api/client_api_generator.py
similarity index 52%
rename from headless/lib/browser/client_api_generator.py
rename to headless/lib/browser/devtools_api/client_api_generator.py
index bb93ede97aa557fde61a8ead59480db0b88f9abd..366b37a2c2e15294c591db841404616e6e36c82e 100644
--- a/headless/lib/browser/client_api_generator.py
+++ b/headless/lib/browser/devtools_api/client_api_generator.py
@@ -3,9 +3,10 @@
# found in the LICENSE file.
import argparse
+import collections
import os.path
-import sys
import re
+import sys
try:
import json
except ImportError:
@@ -61,14 +62,14 @@ def CamelCaseToHackerStyle(name):
def SanitizeLiteral(literal):
return {
- # Rename null enumeration values to avoid a clash with the NULL macro.
- 'null': 'none',
- # Rename mathematical constants to avoid colliding with C macros.
- 'Infinity': 'InfinityValue',
- '-Infinity': 'NegativeInfinityValue',
- 'NaN': 'NaNValue',
- # Turn negative zero into a safe identifier.
- '-0': 'NegativeZeroValue',
+ # Rename null enumeration values to avoid a clash with the NULL macro.
+ 'null': 'none',
+ # Rename mathematical constants to avoid colliding with C macros.
+ 'Infinity': 'InfinityValue',
+ '-Infinity': 'NegativeInfinityValue',
+ 'NaN': 'NaNValue',
+ # Turn negative zero into a safe identifier.
+ '-0': 'NegativeZeroValue',
}.get(literal, literal)
@@ -82,10 +83,10 @@ def InitializeJinjaEnv(cache_dir):
lstrip_blocks=True, # So we can indent control flow tags.
trim_blocks=True)
jinja_env.filters.update({
- 'to_title_case': ToTitleCase,
- 'dash_to_camelcase': DashToCamelCase,
- 'camelcase_to_hacker_style': CamelCaseToHackerStyle,
- 'sanitize_literal': SanitizeLiteral,
+ 'to_title_case': ToTitleCase,
+ 'dash_to_camelcase': DashToCamelCase,
+ 'camelcase_to_hacker_style': CamelCaseToHackerStyle,
+ 'sanitize_literal': SanitizeLiteral,
})
jinja_env.add_extension('jinja2.ext.loopcontrols')
return jinja_env
@@ -113,108 +114,109 @@ def PatchFullQualifiedRefs(json_api):
def CreateUserTypeDefinition(domain, type):
namespace = CamelCaseToHackerStyle(domain['domain'])
return {
- 'return_type': 'std::unique_ptr<headless::%s::%s>' % (
- namespace, type['id']),
- 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
- 'to_raw_type': '*%s',
- 'to_raw_return_type': '%s.get()',
- 'to_pass_type': 'std::move(%s)',
- 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
- 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
- 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']),
- 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']),
+ 'return_type': 'std::unique_ptr<headless::%s::%s>' % (
+ namespace, type['id']),
+ 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (
+ namespace, type['id']),
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
+ 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']),
+ 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']),
}
def CreateEnumTypeDefinition(domain_name, type):
namespace = CamelCaseToHackerStyle(domain_name)
return {
- 'return_type': 'headless::%s::%s' % (namespace, type['id']),
- 'pass_type': 'headless::%s::%s' % (namespace, type['id']),
- 'to_raw_type': '%s',
- 'to_raw_return_type': '%s',
- 'to_pass_type': '%s',
- 'type': 'headless::%s::%s' % (namespace, type['id']),
- 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
- 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']),
- 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'return_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'pass_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'to_pass_type': '%s',
+ 'type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']),
+ 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']),
}
def CreateObjectTypeDefinition():
return {
- 'return_type': 'std::unique_ptr<base::DictionaryValue>',
- 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
- 'to_raw_type': '*%s',
- 'to_raw_return_type': '%s.get()',
- 'to_pass_type': 'std::move(%s)',
- 'type': 'std::unique_ptr<base::DictionaryValue>',
- 'raw_type': 'base::DictionaryValue',
- 'raw_pass_type': 'base::DictionaryValue*',
- 'raw_return_type': 'const base::DictionaryValue*',
+ 'return_type': 'std::unique_ptr<base::DictionaryValue>',
+ 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<base::DictionaryValue>',
+ 'raw_type': 'base::DictionaryValue',
+ 'raw_pass_type': 'base::DictionaryValue*',
+ 'raw_return_type': 'const base::DictionaryValue*',
}
def WrapObjectTypeDefinition(type):
id = type.get('id', 'base::Value')
return {
- 'return_type': 'std::unique_ptr<%s>' % id,
- 'pass_type': 'std::unique_ptr<%s>' % id,
- 'to_raw_type': '*%s',
- 'to_raw_return_type': '%s.get()',
- 'to_pass_type': 'std::move(%s)',
- 'type': 'std::unique_ptr<%s>' % id,
- 'raw_type': id,
- 'raw_pass_type': '%s*' % id,
- 'raw_return_type': 'const %s*' % id,
+ 'return_type': 'std::unique_ptr<%s>' % id,
+ 'pass_type': 'std::unique_ptr<%s>' % id,
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<%s>' % id,
+ 'raw_type': id,
+ 'raw_pass_type': '%s*' % id,
+ 'raw_return_type': 'const %s*' % id,
}
def CreateAnyTypeDefinition():
return {
- 'return_type': 'std::unique_ptr<base::Value>',
- 'pass_type': 'std::unique_ptr<base::Value>',
- 'to_raw_type': '*%s',
- 'to_raw_return_type': '%s.get()',
- 'to_pass_type': 'std::move(%s)',
- 'type': 'std::unique_ptr<base::Value>',
- 'raw_type': 'base::Value',
- 'raw_pass_type': 'base::Value*',
- 'raw_return_type': 'const base::Value*',
+ 'return_type': 'std::unique_ptr<base::Value>',
+ 'pass_type': 'std::unique_ptr<base::Value>',
+ 'to_raw_type': '*%s',
+ 'to_raw_return_type': '%s.get()',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::unique_ptr<base::Value>',
+ 'raw_type': 'base::Value',
+ 'raw_pass_type': 'base::Value*',
+ 'raw_return_type': 'const base::Value*',
}
def CreateStringTypeDefinition(domain):
return {
- 'return_type': 'std::string',
- 'pass_type': 'const std::string&',
- 'to_pass_type': '%s',
- 'to_raw_type': '%s',
- 'to_raw_return_type': '%s',
- 'type': 'std::string',
- 'raw_type': 'std::string',
- 'raw_pass_type': 'const std::string&',
- 'raw_return_type': 'std::string',
+ 'return_type': 'std::string',
+ 'pass_type': 'const std::string&',
+ 'to_pass_type': '%s',
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'type': 'std::string',
+ 'raw_type': 'std::string',
+ 'raw_pass_type': 'const std::string&',
+ 'raw_return_type': 'std::string',
}
def CreatePrimitiveTypeDefinition(type):
typedefs = {
- 'number': 'double',
- 'integer': 'int',
- 'boolean': 'bool',
- 'string': 'std::string',
+ 'number': 'double',
+ 'integer': 'int',
+ 'boolean': 'bool',
+ 'string': 'std::string',
}
return {
- 'return_type': typedefs[type],
- 'pass_type': typedefs[type],
- 'to_pass_type': '%s',
- 'to_raw_type': '%s',
- 'to_raw_return_type': '%s',
- 'type': typedefs[type],
- 'raw_type': typedefs[type],
- 'raw_pass_type': typedefs[type],
- 'raw_return_type': typedefs[type],
+ 'return_type': typedefs[type],
+ 'pass_type': typedefs[type],
+ 'to_pass_type': '%s',
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '%s',
+ 'type': typedefs[type],
+ 'raw_type': typedefs[type],
+ 'raw_pass_type': typedefs[type],
+ 'raw_return_type': typedefs[type],
}
@@ -229,15 +231,15 @@ type_definitions['any'] = CreateAnyTypeDefinition()
def WrapArrayDefinition(type):
return {
- 'return_type': 'std::vector<%s>' % type['type'],
- 'pass_type': 'std::vector<%s>' % type['type'],
- 'to_raw_type': '%s',
- 'to_raw_return_type': '&%s',
- 'to_pass_type': 'std::move(%s)',
- 'type': 'std::vector<%s>' % type['type'],
- 'raw_type': 'std::vector<%s>' % type['type'],
- 'raw_pass_type': 'std::vector<%s>*' % type['type'],
- 'raw_return_type': 'const std::vector<%s>*' % type['type'],
+ 'return_type': 'std::vector<%s>' % type['type'],
+ 'pass_type': 'std::vector<%s>' % type['type'],
+ 'to_raw_type': '%s',
+ 'to_raw_return_type': '&%s',
+ 'to_pass_type': 'std::move(%s)',
+ 'type': 'std::vector<%s>' % type['type'],
+ 'raw_type': 'std::vector<%s>' % type['type'],
+ 'raw_pass_type': 'std::vector<%s>*' % type['type'],
+ 'raw_return_type': 'const std::vector<%s>*' % type['type'],
}
@@ -301,7 +303,8 @@ def SynthesizeEnumType(domain, owner, type):
def SynthesizeCommandTypes(json_api):
"""Generate types for command parameters, return values and enum
- properties."""
+ properties.
+ """
for domain in json_api['domains']:
if not 'types' in domain:
domain['types'] = []
@@ -317,11 +320,11 @@ def SynthesizeCommandTypes(json_api):
if 'enum' in parameter and not '$ref' in parameter:
SynthesizeEnumType(domain, command['name'], parameter)
parameters_type = {
- 'id': ToTitleCase(command['name']) + 'Params',
- 'type': 'object',
- 'description': 'Parameters for the %s command.' % ToTitleCase(
- command['name']),
- 'properties': command['parameters']
+ 'id': ToTitleCase(command['name']) + 'Params',
+ 'type': 'object',
+ 'description': 'Parameters for the %s command.' % ToTitleCase(
+ command['name']),
+ 'properties': command['parameters']
}
domain['types'].append(parameters_type)
if 'returns' in command:
@@ -329,11 +332,11 @@ def SynthesizeCommandTypes(json_api):
if 'enum' in parameter and not '$ref' in parameter:
SynthesizeEnumType(domain, command['name'], parameter)
result_type = {
- 'id': ToTitleCase(command['name']) + 'Result',
- 'type': 'object',
- 'description': 'Result for the %s command.' % ToTitleCase(
- command['name']),
- 'properties': command['returns']
+ 'id': ToTitleCase(command['name']) + 'Result',
+ 'type': 'object',
+ 'description': 'Result for the %s command.' % ToTitleCase(
+ command['name']),
+ 'properties': command['returns']
}
domain['types'].append(result_type)
@@ -352,18 +355,57 @@ def SynthesizeEventTypes(json_api):
if 'enum' in parameter and not '$ref' in parameter:
SynthesizeEnumType(domain, event['name'], parameter)
event_type = {
- 'id': ToTitleCase(event['name']) + 'Params',
- 'type': 'object',
- 'description': 'Parameters for the %s event.' % ToTitleCase(
- event['name']),
- 'properties': event.get('parameters', [])
+ 'id': ToTitleCase(event['name']) + 'Params',
+ 'type': 'object',
+ 'description': 'Parameters for the %s event.' % ToTitleCase(
+ event['name']),
+ 'properties': event.get('parameters', [])
}
domain['types'].append(event_type)
+def InitializeDomainDependencies(json_api):
+ """For each domain create list of domains given domain depends on,
+ including itself."""
+
+ direct_deps = collections.defaultdict(set)
+
+ def GetDomainDepsFromRefs(domain_name, json):
+ if isinstance(json, list):
+ for value in json:
+ GetDomainDepsFromRefs(domain_name, value)
+ return
+
+ if not isinstance(json, dict):
+ return
+ for value in json.itervalues():
+ GetDomainDepsFromRefs(domain_name, value)
+
+ if '$ref' in json:
+ if '.' in json['$ref']:
+ dep = json['$ref'].split('.')[0]
+ direct_deps[domain_name].add(dep)
+
+ for domain in json_api['domains']:
+ direct_deps[domain['domain']] = set(domain.get('dependencies', []))
+ GetDomainDepsFromRefs(domain['domain'], domain)
+
+ def TraverseDependencies(domain, deps):
+ if domain in deps:
+ return
+ deps.add(domain)
+
+ for dep in direct_deps[domain]:
+ TraverseDependencies(dep, deps)
+
+ for domain in json_api['domains']:
+ domain_deps = set()
+ TraverseDependencies(domain['domain'], domain_deps)
+ domain['dependencies'] = sorted(domain_deps)
+
+
def PatchExperimentalCommandsAndEvents(json_api):
- """
- Mark all commands and events in experimental domains as experimental
+ """Mark all commands and events in experimental domains as experimental
and make sure experimental commands have at least empty parameters
and return values.
"""
@@ -375,10 +417,14 @@ def PatchExperimentalCommandsAndEvents(json_api):
event['experimental'] = True
+def EnsureDirectoryExists(path):
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+
def EnsureCommandsHaveParametersAndReturnTypes(json_api):
- """
- Make sure all commands have at least empty parameters and return values. This
- guarantees API compatibility if a previously experimental command is made
+ """Make sure all commands have at least empty parameters and return values.
+ This guarantees API compatibility if a previously experimental command is made
stable.
"""
for domain in json_api['domains']:
@@ -392,46 +438,98 @@ def EnsureCommandsHaveParametersAndReturnTypes(json_api):
event['parameters'] = []
-def Generate(jinja_env, output_dirname, json_api, class_name, file_types):
+def Generate(jinja_env, output_dirname, json_api,
+ class_name, file_types, file_name=None):
+ if file_name is None:
+ file_name = class_name
+ EnsureDirectoryExists(output_dirname)
template_context = {
- 'api': json_api,
- 'join_arrays': JoinArrays,
- 'resolve_type': ResolveType,
- 'type_definition': TypeDefinition,
+ 'api': json_api,
+ 'join_arrays': JoinArrays,
+ 'resolve_type': ResolveType,
+ 'type_definition': TypeDefinition,
}
for file_type in file_types:
template = jinja_env.get_template('/%s_%s.template' % (
class_name, file_type))
- output_file = '%s/%s.%s' % (output_dirname, class_name, file_type)
+ output_file = '%s/%s.%s' % (output_dirname, file_name, file_type)
with open(output_file, 'w') as f:
f.write(template.render(template_context))
-def GenerateDomains(jinja_env, output_dirname, json_api, class_name,
- file_types):
+def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name,
+ file_types, domain_name_to_file_name_func):
+ EnsureDirectoryExists(output_dirname)
for file_type in file_types:
template = jinja_env.get_template('/%s_%s.template' % (
class_name, file_type))
for domain in json_api['domains']:
template_context = {
- 'domain': domain,
- 'resolve_type': ResolveType,
+ 'domain': domain,
+ 'resolve_type': ResolveType,
}
domain_name = CamelCaseToHackerStyle(domain['domain'])
- output_file = '%s/%s.%s' % (output_dirname, domain_name, file_type)
+ output_file = '%s/%s.%s' % (output_dirname,
+ domain_name_to_file_name_func(domain_name),
+ file_type)
with open(output_file, 'w') as f:
f.write(template.render(template_context))
+def GenerateDomains(jinja_env, output_dirname, json_api):
+ GeneratePerDomain(
+ jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api,
+ 'domain', ['cc', 'h'],
+ lambda domain_name: domain_name)
+
+ # TODO(altimin): Remove this in 2017.
+ # Generate DOMAIN.h in the old directory for backwards compatibility.
+ GeneratePerDomain(
+ jinja_env, os.path.join(output_dirname, 'domains'), json_api,
+ 'deprecated_domain', ['h'], lambda domain_name: domain_name)
+
+
+def GenerateTypes(jinja_env, output_dirname, json_api):
+ # Generate forward declarations for types.
+ GeneratePerDomain(
+ jinja_env, os.path.join(output_dirname, 'devtools', 'internal'),
+ json_api, 'domain_types_forward_declaration', ['h'],
+ lambda domain_name: 'types_forward_declaration_%s' % (domain_name, ))
+ # Generate types on per-domain basis.
+ GeneratePerDomain(
+ jinja_env, os.path.join(output_dirname, 'devtools', 'domains'),
+ json_api, 'domain_types', ['h', 'cc'],
+ lambda domain_name: 'types_%s' % (domain_name, ))
+
+ # TODO(altimin): Remove this in 2017.
+ # Generate types.h for backwards compatibility.
+ Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api,
+ 'deprecated_types', ['h'], 'types')
+
+
+def GenerateTypeConversions(jinja_env, output_dirname, json_api):
+ # Generate type conversions on per-domain basis.
+ GeneratePerDomain(
+ jinja_env, os.path.join(output_dirname, 'devtools', 'internal'),
+ json_api, 'domain_type_conversions', ['h'],
+ lambda domain_name: 'type_conversions_%s' % (domain_name, ))
+
+ # TODO(altimin): Remove this in 2017.
+ # Generate type_conversions.h for backwards compatibility.
+ Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api,
+ 'deprecated_type_conversions', ['h'], 'type_conversions')
+
+
if __name__ == '__main__':
json_api, output_dirname = ParseArguments(sys.argv[1:])
jinja_env = InitializeJinjaEnv(output_dirname)
+ InitializeDomainDependencies(json_api)
PatchExperimentalCommandsAndEvents(json_api)
EnsureCommandsHaveParametersAndReturnTypes(json_api)
SynthesizeCommandTypes(json_api)
SynthesizeEventTypes(json_api)
PatchFullQualifiedRefs(json_api)
CreateTypeDefinitions(json_api)
- Generate(jinja_env, output_dirname, json_api, 'types', ['cc', 'h'])
- Generate(jinja_env, output_dirname, json_api, 'type_conversions', ['h'])
- GenerateDomains(jinja_env, output_dirname, json_api, 'domain', ['cc', 'h'])
+ GenerateDomains(jinja_env, output_dirname, json_api)
+ GenerateTypes(jinja_env, output_dirname, json_api)
+ GenerateTypeConversions(jinja_env, output_dirname, json_api)

Powered by Google App Engine
This is Rietveld 408576698