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

Side by Side Diff: headless/lib/browser/devtools_api/client_api_generator.py

Issue 2902583002: Add some closureised JS bindings for DevTools for use by headless embedder (Closed)
Patch Set: Try and fix python test Created 3 years, 6 months 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
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 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 import argparse 5 import argparse
6 import collections 6 import collections
7 import functools
7 import os.path 8 import os.path
8 import re 9 import re
9 import sys 10 import sys
11
10 try: 12 try:
11 import json 13 import json
12 except ImportError: 14 except ImportError:
13 import simplejson as json 15 import simplejson as json
14 16
15 # Path handling for libraries and templates 17 # Path handling for libraries and templates
16 # Paths have to be normalized because Jinja uses the exact template path to 18 # Paths have to be normalized because Jinja uses the exact template path to
17 # determine the hash used in the cache filename, and we need a pre-caching step 19 # determine the hash used in the cache filename, and we need a pre-caching step
18 # to be concurrency-safe. Use absolute path because __file__ is absolute if 20 # to be concurrency-safe. Use absolute path because __file__ is absolute if
19 # module is imported, and relative if executed directly. 21 # module is imported, and relative if executed directly.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 56
55 57
56 def CamelCaseToHackerStyle(name): 58 def CamelCaseToHackerStyle(name):
57 # Do two passes to insert '_' chars to deal with overlapping matches (e.g., 59 # Do two passes to insert '_' chars to deal with overlapping matches (e.g.,
58 # 'LoLoLoL'). 60 # 'LoLoLoL').
59 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name) 61 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
60 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name) 62 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
61 return name.lower() 63 return name.lower()
62 64
63 65
66 def Shorten(js_name, domain_name):
67 short_name = domain_name + '.'
68 long_name = 'chromium.DevTools.' + short_name
69 return js_name.replace(long_name, short_name)
70
71
72 def ShortForm(domain, js_name):
73 if not 'js_dependencies' in domain:
74 return js_name
75
76 for dependency in domain['js_dependencies']:
77 js_name = Shorten(js_name, dependency)
78 js_name = Shorten(js_name, domain['domain'])
79 return js_name
80
81
64 def SanitizeLiteral(literal): 82 def SanitizeLiteral(literal):
65 return { 83 return {
66 # Rename null enumeration values to avoid a clash with the NULL macro. 84 # Rename null enumeration values to avoid a clash with the NULL macro.
67 'null': 'none', 85 'null': 'none',
68 # Rename literals that clash with Win32 defined macros. 86 # Rename literals that clash with Win32 defined macros.
69 'error': 'err', 87 'error': 'err',
70 'mouseMoved': 'mouse_ptr_moved', 88 'mouseMoved': 'mouse_ptr_moved',
71 'Strict': 'exact', 89 'Strict': 'exact',
72 'getCurrentTime': 'getCurrentAnimationTime', 90 'getCurrentTime': 'getCurrentAnimationTime',
73 # Rename mathematical constants to avoid colliding with C macros. 91 # Rename mathematical constants to avoid colliding with C macros.
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 if not '.' in json['$ref']: 131 if not '.' in json['$ref']:
114 json['$ref'] = domain_name + '.' + json['$ref'] 132 json['$ref'] = domain_name + '.' + json['$ref']
115 133
116 for domain in json_api['domains']: 134 for domain in json_api['domains']:
117 PatchFullQualifiedRefsInDomain(domain, domain['domain']) 135 PatchFullQualifiedRefsInDomain(domain, domain['domain'])
118 136
119 137
120 def CreateUserTypeDefinition(domain, type): 138 def CreateUserTypeDefinition(domain, type):
121 namespace = CamelCaseToHackerStyle(domain['domain']) 139 namespace = CamelCaseToHackerStyle(domain['domain'])
122 return { 140 return {
141 'js_type': '!chromium.DevTools.%s.%s' % (domain['domain'], type['id']),
123 'return_type': 'std::unique_ptr<headless::%s::%s>' % ( 142 'return_type': 'std::unique_ptr<headless::%s::%s>' % (
124 namespace, type['id']), 143 namespace, type['id']),
125 'pass_type': 'std::unique_ptr<headless::%s::%s>' % ( 144 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (
126 namespace, type['id']), 145 namespace, type['id']),
127 'to_raw_type': '*%s', 146 'to_raw_type': '*%s',
128 'to_raw_return_type': '%s.get()', 147 'to_raw_return_type': '%s.get()',
129 'to_pass_type': 'std::move(%s)', 148 'to_pass_type': 'std::move(%s)',
130 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']), 149 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
131 'raw_type': 'headless::%s::%s' % (namespace, type['id']), 150 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
132 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']), 151 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']),
133 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']), 152 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']),
134 } 153 }
135 154
136 155
137 def CreateEnumTypeDefinition(domain_name, type): 156 def CreateEnumTypeDefinition(domain_name, type):
138 namespace = CamelCaseToHackerStyle(domain_name) 157 namespace = CamelCaseToHackerStyle(domain_name)
139 return { 158 return {
159 'js_type': '!chromium.DevTools.%s.%s' % (domain_name, type['id']),
140 'return_type': 'headless::%s::%s' % (namespace, type['id']), 160 'return_type': 'headless::%s::%s' % (namespace, type['id']),
141 'pass_type': 'headless::%s::%s' % (namespace, type['id']), 161 'pass_type': 'headless::%s::%s' % (namespace, type['id']),
142 'to_raw_type': '%s', 162 'to_raw_type': '%s',
143 'to_raw_return_type': '%s', 163 'to_raw_return_type': '%s',
144 'to_pass_type': '%s', 164 'to_pass_type': '%s',
145 'type': 'headless::%s::%s' % (namespace, type['id']), 165 'type': 'headless::%s::%s' % (namespace, type['id']),
146 'raw_type': 'headless::%s::%s' % (namespace, type['id']), 166 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
147 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']), 167 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']),
148 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']), 168 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']),
149 } 169 }
150 170
151 171
152 def CreateObjectTypeDefinition(): 172 def CreateObjectTypeDefinition():
153 return { 173 return {
174 'js_type': 'Object',
154 'return_type': 'std::unique_ptr<base::DictionaryValue>', 175 'return_type': 'std::unique_ptr<base::DictionaryValue>',
155 'pass_type': 'std::unique_ptr<base::DictionaryValue>', 176 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
156 'to_raw_type': '*%s', 177 'to_raw_type': '*%s',
157 'to_raw_return_type': '%s.get()', 178 'to_raw_return_type': '%s.get()',
158 'to_pass_type': 'std::move(%s)', 179 'to_pass_type': 'std::move(%s)',
159 'type': 'std::unique_ptr<base::DictionaryValue>', 180 'type': 'std::unique_ptr<base::DictionaryValue>',
160 'raw_type': 'base::DictionaryValue', 181 'raw_type': 'base::DictionaryValue',
161 'raw_pass_type': 'base::DictionaryValue*', 182 'raw_pass_type': 'base::DictionaryValue*',
162 'raw_return_type': 'const base::DictionaryValue*', 183 'raw_return_type': 'const base::DictionaryValue*',
163 } 184 }
164 185
165 186
166 def WrapObjectTypeDefinition(type): 187 def WrapObjectTypeDefinition(type):
167 id = type.get('id', 'base::Value') 188 id = type.get('id', 'base::Value')
168 return { 189 return {
190 'js_type': '!Object',
169 'return_type': 'std::unique_ptr<%s>' % id, 191 'return_type': 'std::unique_ptr<%s>' % id,
170 'pass_type': 'std::unique_ptr<%s>' % id, 192 'pass_type': 'std::unique_ptr<%s>' % id,
171 'to_raw_type': '*%s', 193 'to_raw_type': '*%s',
172 'to_raw_return_type': '%s.get()', 194 'to_raw_return_type': '%s.get()',
173 'to_pass_type': 'std::move(%s)', 195 'to_pass_type': 'std::move(%s)',
174 'type': 'std::unique_ptr<%s>' % id, 196 'type': 'std::unique_ptr<%s>' % id,
175 'raw_type': id, 197 'raw_type': id,
176 'raw_pass_type': '%s*' % id, 198 'raw_pass_type': '%s*' % id,
177 'raw_return_type': 'const %s*' % id, 199 'raw_return_type': 'const %s*' % id,
178 } 200 }
179 201
180 202
181 def CreateAnyTypeDefinition(): 203 def CreateAnyTypeDefinition():
182 return { 204 return {
205 'js_type': '*',
183 'return_type': 'std::unique_ptr<base::Value>', 206 'return_type': 'std::unique_ptr<base::Value>',
184 'pass_type': 'std::unique_ptr<base::Value>', 207 'pass_type': 'std::unique_ptr<base::Value>',
185 'to_raw_type': '*%s', 208 'to_raw_type': '*%s',
186 'to_raw_return_type': '%s.get()', 209 'to_raw_return_type': '%s.get()',
187 'to_pass_type': 'std::move(%s)', 210 'to_pass_type': 'std::move(%s)',
188 'type': 'std::unique_ptr<base::Value>', 211 'type': 'std::unique_ptr<base::Value>',
189 'raw_type': 'base::Value', 212 'raw_type': 'base::Value',
190 'raw_pass_type': 'base::Value*', 213 'raw_pass_type': 'base::Value*',
191 'raw_return_type': 'const base::Value*', 214 'raw_return_type': 'const base::Value*',
192 } 215 }
193 216
194 217
195 def CreateStringTypeDefinition(): 218 def CreateStringTypeDefinition():
196 return { 219 return {
220 'js_type': 'string',
197 'return_type': 'std::string', 221 'return_type': 'std::string',
198 'pass_type': 'const std::string&', 222 'pass_type': 'const std::string&',
199 'to_pass_type': '%s', 223 'to_pass_type': '%s',
200 'to_raw_type': '%s', 224 'to_raw_type': '%s',
201 'to_raw_return_type': '%s', 225 'to_raw_return_type': '%s',
202 'type': 'std::string', 226 'type': 'std::string',
203 'raw_type': 'std::string', 227 'raw_type': 'std::string',
204 'raw_pass_type': 'const std::string&', 228 'raw_pass_type': 'const std::string&',
205 'raw_return_type': 'std::string', 229 'raw_return_type': 'std::string',
206 } 230 }
207 231
208 232
209 def CreatePrimitiveTypeDefinition(type): 233 def CreatePrimitiveTypeDefinition(type):
210 typedefs = { 234 typedefs = {
211 'number': 'double', 235 'number': 'double',
212 'integer': 'int', 236 'integer': 'int',
213 'boolean': 'bool', 237 'boolean': 'bool',
214 } 238 }
239 js_typedefs = {
240 'number': 'number',
241 'integer': 'number',
242 'boolean': 'boolean',
243 }
215 return { 244 return {
245 'js_type': js_typedefs[type],
216 'return_type': typedefs[type], 246 'return_type': typedefs[type],
217 'pass_type': typedefs[type], 247 'pass_type': typedefs[type],
218 'to_pass_type': '%s', 248 'to_pass_type': '%s',
219 'to_raw_type': '%s', 249 'to_raw_type': '%s',
220 'to_raw_return_type': '%s', 250 'to_raw_return_type': '%s',
221 'type': typedefs[type], 251 'type': typedefs[type],
222 'raw_type': typedefs[type], 252 'raw_type': typedefs[type],
223 'raw_pass_type': typedefs[type], 253 'raw_pass_type': typedefs[type],
224 'raw_return_type': typedefs[type], 254 'raw_return_type': typedefs[type],
225 } 255 }
226 256
227 257
228 type_definitions = {} 258 type_definitions = {}
229 type_definitions['number'] = CreatePrimitiveTypeDefinition('number') 259 type_definitions['number'] = CreatePrimitiveTypeDefinition('number')
230 type_definitions['integer'] = CreatePrimitiveTypeDefinition('integer') 260 type_definitions['integer'] = CreatePrimitiveTypeDefinition('integer')
231 type_definitions['boolean'] = CreatePrimitiveTypeDefinition('boolean') 261 type_definitions['boolean'] = CreatePrimitiveTypeDefinition('boolean')
232 type_definitions['string'] = CreateStringTypeDefinition() 262 type_definitions['string'] = CreateStringTypeDefinition()
233 type_definitions['object'] = CreateObjectTypeDefinition() 263 type_definitions['object'] = CreateObjectTypeDefinition()
234 type_definitions['any'] = CreateAnyTypeDefinition() 264 type_definitions['any'] = CreateAnyTypeDefinition()
235 265
236 266
237 def WrapArrayDefinition(type): 267 def WrapArrayDefinition(type):
238 return { 268 return {
269 'js_type': '!Array.<%s>' % type['js_type'],
239 'return_type': 'std::vector<%s>' % type['type'], 270 'return_type': 'std::vector<%s>' % type['type'],
240 'pass_type': 'std::vector<%s>' % type['type'], 271 'pass_type': 'std::vector<%s>' % type['type'],
241 'to_raw_type': '%s', 272 'to_raw_type': '%s',
242 'to_raw_return_type': '&%s', 273 'to_raw_return_type': '&%s',
243 'to_pass_type': 'std::move(%s)', 274 'to_pass_type': 'std::move(%s)',
244 'type': 'std::vector<%s>' % type['type'], 275 'type': 'std::vector<%s>' % type['type'],
245 'raw_type': 'std::vector<%s>' % type['type'], 276 'raw_type': 'std::vector<%s>' % type['type'],
246 'raw_pass_type': 'std::vector<%s>*' % type['type'], 277 'raw_pass_type': 'std::vector<%s>*' % type['type'],
247 'raw_return_type': 'const std::vector<%s>*' % type['type'], 278 'raw_return_type': 'const std::vector<%s>*' % type['type'],
248 } 279 }
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 'properties': event.get('parameters', []) 401 'properties': event.get('parameters', [])
371 } 402 }
372 domain['types'].append(event_type) 403 domain['types'].append(event_type)
373 404
374 405
375 def InitializeDomainDependencies(json_api): 406 def InitializeDomainDependencies(json_api):
376 """For each domain create list of domains given domain depends on, 407 """For each domain create list of domains given domain depends on,
377 including itself.""" 408 including itself."""
378 409
379 direct_deps = collections.defaultdict(set) 410 direct_deps = collections.defaultdict(set)
411 types_required = collections.defaultdict(set)
380 412
381 def GetDomainDepsFromRefs(domain_name, json): 413 def GetDomainDepsFromRefs(domain_name, json):
382 if isinstance(json, list): 414 if isinstance(json, list):
383 for value in json: 415 for value in json:
384 GetDomainDepsFromRefs(domain_name, value) 416 GetDomainDepsFromRefs(domain_name, value)
385 return 417 return
386 418
387 if not isinstance(json, dict): 419 if not isinstance(json, dict):
388 return 420 return
389 for value in json.itervalues(): 421 for value in json.itervalues():
390 GetDomainDepsFromRefs(domain_name, value) 422 GetDomainDepsFromRefs(domain_name, value)
391 423
392 if '$ref' in json: 424 if '$ref' in json:
393 if '.' in json['$ref']: 425 if '.' in json['$ref']:
394 dep = json['$ref'].split('.')[0] 426 dep = json['$ref'].split('.')[0]
395 direct_deps[domain_name].add(dep) 427 direct_deps[domain_name].add(dep)
428 types_required[domain_name].add(json['$ref'])
396 429
397 for domain in json_api['domains']: 430 for domain in json_api['domains']:
398 direct_deps[domain['domain']] = set(domain.get('dependencies', [])) 431 direct_deps[domain['domain']] = set(domain.get('dependencies', []))
432 types_required[domain['domain']] = set(domain.get('types_required', []))
399 GetDomainDepsFromRefs(domain['domain'], domain) 433 GetDomainDepsFromRefs(domain['domain'], domain)
400 434
401 def TraverseDependencies(domain, deps): 435 def TraverseDependencies(domain, deps):
402 if domain in deps: 436 if domain in deps:
403 return 437 return
404 deps.add(domain) 438 deps.add(domain)
405 439
406 for dep in direct_deps[domain]: 440 for dep in direct_deps[domain]:
407 TraverseDependencies(dep, deps) 441 TraverseDependencies(dep, deps)
408 442
409 for domain in json_api['domains']: 443 for domain in json_api['domains']:
410 domain_deps = set() 444 domain_deps = set()
411 TraverseDependencies(domain['domain'], domain_deps) 445 TraverseDependencies(domain['domain'], domain_deps)
446 if 'dependencies' in domain:
447 domain['js_dependencies'] = domain['dependencies']
448 else:
449 domain['js_dependencies'] = []
450
451 domain['js_forward_declarations'] = []
452 for type in types_required[domain['domain']]:
453 if not type.split('.')[0] in domain['js_dependencies']:
454 domain['js_forward_declarations'].append(type)
412 domain['dependencies'] = sorted(domain_deps) 455 domain['dependencies'] = sorted(domain_deps)
413 456
414 457
415 def PatchExperimentalCommandsAndEvents(json_api): 458 def PatchExperimentalCommandsAndEvents(json_api):
416 """Mark all commands and events in experimental domains as experimental 459 """Mark all commands and events in experimental domains as experimental
417 and make sure experimental commands have at least empty parameters 460 and make sure experimental commands have at least empty parameters
418 and return values. 461 and return values.
419 """ 462 """
420 for domain in json_api['domains']: 463 for domain in json_api['domains']:
421 if domain.get('experimental', False): 464 if domain.get('experimental', False):
(...skipping 27 matching lines...) Expand all
449 def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name, 492 def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name,
450 file_types, domain_name_to_file_name_func): 493 file_types, domain_name_to_file_name_func):
451 EnsureDirectoryExists(output_dirname) 494 EnsureDirectoryExists(output_dirname)
452 for file_type in file_types: 495 for file_type in file_types:
453 template = jinja_env.get_template('/%s_%s.template' % ( 496 template = jinja_env.get_template('/%s_%s.template' % (
454 class_name, file_type)) 497 class_name, file_type))
455 for domain in json_api['domains']: 498 for domain in json_api['domains']:
456 template_context = { 499 template_context = {
457 'domain': domain, 500 'domain': domain,
458 'resolve_type': ResolveType, 501 'resolve_type': ResolveType,
502 'short_form': functools.partial(ShortForm, domain),
459 } 503 }
460 domain_name = CamelCaseToHackerStyle(domain['domain']) 504 domain_name = CamelCaseToHackerStyle(domain['domain'])
461 output_file = '%s/%s.%s' % (output_dirname, 505 output_file = '%s/%s.%s' % (output_dirname,
462 domain_name_to_file_name_func(domain_name), 506 domain_name_to_file_name_func(domain_name),
463 file_type) 507 file_type)
464 with open(output_file, 'w') as f: 508 with open(output_file, 'w') as f:
465 f.write(template.render(template_context)) 509 f.write(template.render(template_context))
466 510
467 511
468 def GenerateDomains(jinja_env, output_dirname, json_api): 512 def GenerateDomains(jinja_env, output_dirname, json_api):
469 GeneratePerDomain( 513 GeneratePerDomain(
470 jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api, 514 jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api,
471 'domain', ['cc', 'h'], 515 'domain', ['cc', 'h'],
472 lambda domain_name: domain_name) 516 lambda domain_name: domain_name)
517 GeneratePerDomain(
518 jinja_env, os.path.join(output_dirname, 'devtools_js'), json_api,
519 'domain', ['js'],
520 lambda domain_name: domain_name)
473 521
474 522
475 def GenerateTypes(jinja_env, output_dirname, json_api): 523 def GenerateTypes(jinja_env, output_dirname, json_api):
476 # Generate forward declarations for types. 524 # Generate forward declarations for types.
477 GeneratePerDomain( 525 GeneratePerDomain(
478 jinja_env, os.path.join(output_dirname, 'devtools', 'internal'), 526 jinja_env, os.path.join(output_dirname, 'devtools', 'internal'),
479 json_api, 'domain_types_forward_declarations', ['h'], 527 json_api, 'domain_types_forward_declarations', ['h'],
480 lambda domain_name: 'types_forward_declarations_%s' % (domain_name, )) 528 lambda domain_name: 'types_forward_declarations_%s' % (domain_name, ))
481 # Generate types on per-domain basis. 529 # Generate types on per-domain basis.
482 GeneratePerDomain( 530 GeneratePerDomain(
(...skipping 16 matching lines...) Expand all
499 InitializeDomainDependencies(json_api) 547 InitializeDomainDependencies(json_api)
500 PatchExperimentalCommandsAndEvents(json_api) 548 PatchExperimentalCommandsAndEvents(json_api)
501 EnsureCommandsHaveParametersAndReturnTypes(json_api) 549 EnsureCommandsHaveParametersAndReturnTypes(json_api)
502 SynthesizeCommandTypes(json_api) 550 SynthesizeCommandTypes(json_api)
503 SynthesizeEventTypes(json_api) 551 SynthesizeEventTypes(json_api)
504 PatchFullQualifiedRefs(json_api) 552 PatchFullQualifiedRefs(json_api)
505 CreateTypeDefinitions(json_api) 553 CreateTypeDefinitions(json_api)
506 GenerateDomains(jinja_env, output_dirname, json_api) 554 GenerateDomains(jinja_env, output_dirname, json_api)
507 GenerateTypes(jinja_env, output_dirname, json_api) 555 GenerateTypes(jinja_env, output_dirname, json_api)
508 GenerateTypeConversions(jinja_env, output_dirname, json_api) 556 GenerateTypeConversions(jinja_env, output_dirname, json_api)
OLDNEW
« no previous file with comments | « headless/headless_browsertest_resources.grd ('k') | headless/lib/browser/devtools_api/devtools_connection.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698