15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from copy import copy
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import logging
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import posixpath
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from api_models import GetNodeCategories
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from api_schema_graph import APINodeCursor
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from docs_server_utils import MarkFirstAndLast
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from extensions_paths import JSON_TEMPLATES, PRIVATE_TEMPLATES
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from operator import itemgetter
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from platform_util import PlatformToExtensionType
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import third_party.json_schema_compiler.model as model
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def GetEventByNameFromEvents(events):
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''Parses the dictionary |events| to find the definitions of members of the
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  type Event.  Returns a dictionary mapping the name of a member to that
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  member's definition.
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  assert 'types' in events, \
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'The dictionary |events| must contain the key "types".'
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  event_list = [t for t in events['types'] if t.get('name') == 'Event']
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  assert len(event_list) == 1, 'Exactly one type must be called "Event".'
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return _GetByNameDict(event_list[0])
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def _GetByNameDict(namespace):
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''Returns a dictionary mapping names to named items from |namespace|.
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  This lets us render specific API entities rather than the whole thing at once,
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for example {{apis.manifestTypes.byName.ExternallyConnectable}}.
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  Includes items from namespace['types'], namespace['functions'],
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  namespace['events'], and namespace['properties'].
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  by_name = {}
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for item_type in GetNodeCategories():
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if item_type in namespace:
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      old_size = len(by_name)
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      by_name.update(
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          (item['name'], item) for item in namespace[item_type])
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      assert len(by_name) == old_size + len(namespace[item_type]), (
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          'Duplicate name in %r' % namespace)
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return by_name
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def _CreateId(node, prefix):
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if node.parent is not None and not isinstance(node.parent, model.Namespace):
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return '-'.join([prefix, node.parent.simple_name, node.simple_name])
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return '-'.join([prefix, node.simple_name])
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def _FormatValue(value):
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''Inserts commas every three digits for integer values. It is magic.
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  s = str(value)
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class JSCView(object):
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''Uses a Model from the JSON Schema Compiler and generates a dict that
6503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  a Motemplate template can use for a data source.
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  '''
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def __init__(self,
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               content_script_apis,
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               jsc_model,
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               availability_finder,
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               json_cache,
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               template_cache,
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               features_bundle,
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               event_byname_future,
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               platform):
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._content_script_apis = content_script_apis
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._availability = availability_finder.GetAPIAvailability(jsc_model.name)
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._current_node = APINodeCursor(availability_finder, jsc_model.name)
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._api_availabilities = json_cache.GetFromFile(
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        posixpath.join(JSON_TEMPLATES, 'api_availabilities.json'))
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._intro_tables = json_cache.GetFromFile(
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        posixpath.join(JSON_TEMPLATES, 'intro_tables.json'))
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._api_features = features_bundle.GetAPIFeatures()
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._template_cache = template_cache
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._event_byname_future = event_byname_future
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._jsc_model = jsc_model
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._platform = platform
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetLink(self, link):
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ref = link if '.' in link else (self._jsc_model.name + '.' + link)
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return { 'ref': ref, 'text': link, 'name': link }
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def ToDict(self):
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of |self._jsc_model|, which
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    is a Namespace object from JSON Schema Compiler.
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    assert self._jsc_model is not None
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    chrome_dot_name = 'chrome.%s' % self._jsc_model.name
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    as_dict = {
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'name': self._jsc_model.name,
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'namespace': self._jsc_model.documentation_options.get('namespace',
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                             chrome_dot_name),
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'title': self._jsc_model.documentation_options.get('title',
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                         chrome_dot_name),
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'documentationOptions': self._jsc_model.documentation_options,
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'types': self._GenerateTypes(self._jsc_model.types.values()),
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'functions': self._GenerateFunctions(self._jsc_model.functions),
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'events': self._GenerateEvents(self._jsc_model.events),
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'domEvents': self._GenerateDomEvents(self._jsc_model.events),
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'properties': self._GenerateProperties(self._jsc_model.properties),
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'introList': self._GetIntroTableList(),
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'channelWarning': self._GetChannelWarning(),
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self._jsc_model.deprecated:
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      as_dict['deprecated'] = self._jsc_model.deprecated
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    as_dict['byName'] = _GetByNameDict(as_dict)
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return as_dict
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _IsExperimental(self):
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self._jsc_model.name.startswith('experimental')
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetChannelWarning(self):
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not self._IsExperimental():
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return {
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._availability.channel_info.channel: True
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return None
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateCallback(self, callback):
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of a callback suitable
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for consumption by templates.
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not callback:
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return None
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    callback_dict = {
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'name': callback.simple_name,
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'simple_type': {'simple_type': 'function'},
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'optional': callback.optional,
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'parameters': []
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('parameters',
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    callback.simple_name,
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    'parameters'):
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for param in callback.params:
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        callback_dict['parameters'].append(self._GenerateProperty(param))
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (len(callback_dict['parameters']) > 0):
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      callback_dict['parameters'][-1]['last'] = True
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return callback_dict
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateCallbackProperty(self, callback, callback_dict):
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of a callback property
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    suitable for consumption by templates.
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    property_dict = {
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'name': callback.simple_name,
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'description': callback.description,
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'optional': callback.optional,
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'isCallback': True,
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'asFunction': callback_dict,
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'id': _CreateId(callback, 'property'),
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'simple_type': 'function',
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (callback.parent is not None and
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        not isinstance(callback.parent, model.Namespace)):
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      property_dict['parentName'] = callback.parent.simple_name
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return property_dict
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateTypes(self, types):
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a list of dictionaries representing this Model's types.
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('types'):
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [self._GenerateType(t) for t in types]
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateType(self, type_):
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of a type from JSON Schema Compiler.
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(type_.simple_name):
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      type_dict = {
1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'name': type_.simple_name,
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'description': type_.description,
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'properties': self._GenerateProperties(type_.properties),
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'functions': self._GenerateFunctions(type_.functions),
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'events': self._GenerateEvents(type_.events),
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'id': _CreateId(type_, 'type'),
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'availability': self._GetAvailabilityTemplate()
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._RenderTypeInformation(type_, type_dict)
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return type_dict
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateFunctions(self, functions):
1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a list of dictionaries representing this Model's functions.
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
1965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('functions'):
1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [self._GenerateFunction(f) for f in functions.values()]
1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateFunction(self, function):
2005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of a function from
2015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    JSON Schema Compiler.
2025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
2035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # When ignoring types, properties must be ignored as well.
2045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(function.simple_name,
2055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    ignore=('types', 'properties')):
2065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function_dict = {
2075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'name': function.simple_name,
2085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'description': function.description,
2095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'callback': self._GenerateCallback(function.callback),
2105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'parameters': [],
2115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'returns': None,
2125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'id': _CreateId(function, 'method'),
2135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'availability': self._GetAvailabilityTemplate()
2145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
2155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._AddCommonProperties(function_dict, function)
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if function.returns:
2175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        function_dict['returns'] = self._GenerateType(function.returns)
2185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(function.simple_name, 'parameters'):
2205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for param in function.params:
2215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        function_dict['parameters'].append(self._GenerateProperty(param))
2225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if function.callback is not None:
2235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      # Show the callback as an extra parameter.
2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function_dict['parameters'].append(
2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self._GenerateCallbackProperty(function.callback,
2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                         function_dict['callback']))
2275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if len(function_dict['parameters']) > 0:
2285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function_dict['parameters'][-1]['last'] = True
2295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return function_dict
2305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateEvents(self, events):
2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a list of dictionaries representing this Model's events.
2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('events'):
2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [self._GenerateEvent(e) for e in events.values()
2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              if not e.supports_dom]
2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateDomEvents(self, events):
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a list of dictionaries representing this Model's DOM events.
2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
2415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('events'):
2425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [self._GenerateEvent(e) for e in events.values()
2435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              if e.supports_dom]
2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateEvent(self, event):
2465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of an event from
2475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    JSON Schema Compiler. Note that although events are modeled as functions
2485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    in JSON Schema Compiler, we model them differently for the templates.
2495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
2505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(event.simple_name, ignore=('properties',)):
2515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      event_dict = {
2525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'name': event.simple_name,
2535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'description': event.description,
2545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'filters': [self._GenerateProperty(f) for f in event.filters],
2555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'conditions': [self._GetLink(condition)
2565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       for condition in event.conditions],
2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'actions': [self._GetLink(action) for action in event.actions],
2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'supportsRules': event.supports_rules,
2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'supportsListeners': event.supports_listeners,
2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'properties': [],
2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'id': _CreateId(event, 'event'),
2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'byName': {},
2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'availability': self._GetAvailabilityTemplate()
2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._AddCommonProperties(event_dict, event)
2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Add the Event members to each event in this object.
2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self._event_byname_future:
2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      event_dict['byName'].update(self._event_byname_future.Get())
2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # We need to create the method description for addListener based on the
2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # information stored in |event|.
2715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if event.supports_listeners:
2725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      callback_object = model.Function(parent=event,
2735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       name='callback',
2745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       json={},
2755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       namespace=event.parent,
2765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       origin='')
2775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      callback_object.params = event.params
2785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if event.callback:
2795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        callback_object.callback = event.callback
2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      with self._current_node.Descend(event.simple_name):
2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        callback = self._GenerateFunction(callback_object)
2835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      callback_parameter = self._GenerateCallbackProperty(callback_object,
2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                          callback)
2855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      callback_parameter['last'] = True
2865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      event_dict['byName']['addListener'] = {
2875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'name': 'addListener',
2885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'callback': callback,
2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'parameters': [callback_parameter]
2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
2915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if event.supports_dom:
2925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      # Treat params as properties of the custom Event object associated with
2935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      # this DOM Event.
2945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      with self._current_node.Descend(event.simple_name,
2955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                      ignore=('properties',)):
2965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        event_dict['properties'] += [self._GenerateProperty(param)
2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                     for param in event.params]
2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return event_dict
2995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateProperties(self, properties):
3015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a list of dictionaries representing this Model's properites.
3025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
3035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend('properties'):
3045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return [self._GenerateProperty(v) for v in properties.values()]
3055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GenerateProperty(self, property_):
3075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns a dictionary representation of a property from
3085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    JSON Schema Compiler.
3095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
3105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not hasattr(property_, 'type_'):
3115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for d in dir(property_):
3125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if not d.startswith('_'):
3135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          print ('%s -> %s' % (d, getattr(property_, d)))
3145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    type_ = property_.type_
3155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Make sure we generate property info for arrays, too.
3175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # TODO(kalman): what about choices?
3185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if type_.property_type == model.PropertyType.ARRAY:
3195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      properties = type_.item_type.properties
3205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    else:
3215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      properties = type_.properties
3225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(property_.simple_name):
3245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      property_dict = {
3255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'name': property_.simple_name,
3265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'optional': property_.optional,
3275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'description': property_.description,
3285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'properties': self._GenerateProperties(type_.properties),
3295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'functions': self._GenerateFunctions(type_.functions),
3305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'parameters': [],
3315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'returns': None,
3325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'id': _CreateId(property_, 'property'),
3335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'availability': self._GetAvailabilityTemplate()
3345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
3355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._AddCommonProperties(property_dict, property_)
3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if type_.property_type == model.PropertyType.FUNCTION:
3385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        function = type_.function
3395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        with self._current_node.Descend('parameters'):
3405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          for param in function.params:
3415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            property_dict['parameters'].append(self._GenerateProperty(param))
3425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if function.returns:
3435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          with self._current_node.Descend(ignore=('types', 'properties')):
3445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            property_dict['returns'] = self._GenerateType(function.returns)
3455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    value = property_.value
3475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if value is not None:
3485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if isinstance(value, int):
3495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        property_dict['value'] = _FormatValue(value)
3505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      else:
3515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        property_dict['value'] = value
3525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    else:
3535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._RenderTypeInformation(type_, property_dict)
3545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return property_dict
3565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _AddCommonProperties(self, target, src):
3585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if src.deprecated is not None:
3595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      target['deprecated'] = src.deprecated
3605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (src.parent is not None and
3615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        not isinstance(src.parent, model.Namespace)):
3625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      target['parentName'] = src.parent.simple_name
3635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _RenderTypeInformation(self, type_, dst_dict):
3655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    with self._current_node.Descend(ignore=('types', 'properties')):
3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT
3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if type_.property_type == model.PropertyType.CHOICES:
3685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['choices'] = self._GenerateTypes(type_.choices)
3695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # We keep track of which == last for knowing when to add "or" between
3705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # choices in templates.
3715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if len(dst_dict['choices']) > 0:
3725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          dst_dict['choices'][-1]['last'] = True
3735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif type_.property_type == model.PropertyType.REF:
3745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['link'] = self._GetLink(type_.ref_type)
3755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif type_.property_type == model.PropertyType.ARRAY:
3765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['array'] = self._GenerateType(type_.item_type)
3775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif type_.property_type == model.PropertyType.ENUM:
3785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['enum_values'] = [
3795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            {'name': value.name, 'description': value.description}
3805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            for value in type_.enum_values]
3815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if len(dst_dict['enum_values']) > 0:
3825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          dst_dict['enum_values'][-1]['last'] = True
3835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif type_.instance_of is not None:
3845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['simple_type'] = type_.instance_of
3855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      else:
3865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        dst_dict['simple_type'] = type_.property_type.name
3875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _CreateAvailabilityTemplate(self, status, scheduled, version):
3895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Returns an object suitable for use in templates to display availability
3905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    information.
3915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
3921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    # TODO(rockot): Temporary hack. Remove this very soon.
3931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if status == 'master':
3941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      status = 'trunk'
3955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return {
3965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'partial': self._template_cache.GetFromFile(
3975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          '%sintro_tables/%s_message.html' % (PRIVATE_TEMPLATES, status)).Get(),
3985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'scheduled': scheduled,
3995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'version': version
4005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
4015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetAvailabilityTemplate(self):
4035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Gets availability for the current node and returns an appropriate
4045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    template object.
4055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
4065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Displaying deprecated status takes precedence over when the API
4075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # became stable.
4085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    availability_info = self._current_node.GetDeprecated()
4095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if availability_info is not None:
4105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      status = 'deprecated'
4115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    else:
4125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      availability_info = self._current_node.GetAvailability()
4135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if availability_info is None:
4145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        return None
4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      status = availability_info.channel_info.channel
4165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self._CreateAvailabilityTemplate(
4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        status,
4185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        availability_info.scheduled,
4195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        availability_info.channel_info.version)
4205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetIntroTableList(self):
4225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Create a generic data structure that can be traversed by the templates
4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    to create an API intro table.
4245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
4255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    intro_rows = [
4265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._GetIntroDescriptionRow(),
4275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._GetIntroAvailabilityRow()
4285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ] + self._GetIntroDependencyRows() + self._GetIntroContentScriptRow()
4295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Add rows using data from intro_tables.json, overriding any existing rows
4315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # if they share the same 'title' attribute.
4325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    row_titles = [row['title'] for row in intro_rows]
4335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for misc_row in self._GetMiscIntroRows():
4345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if misc_row['title'] in row_titles:
4355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        intro_rows[row_titles.index(misc_row['title'])] = misc_row
4365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      else:
4375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        intro_rows.append(misc_row)
4385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return intro_rows
4405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetIntroContentScriptRow(self):
4425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''Generates the 'Content Script' row data for an API intro table.
4435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
4445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    content_script_support = self._content_script_apis.get(self._jsc_model.name)
4455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if content_script_support is None:
4465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return []
4475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if content_script_support.restrictedTo:
4485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      content_script_support.restrictedTo.sort(key=itemgetter('node'))
4495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      MarkFirstAndLast(content_script_support.restrictedTo)
4505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return [{
4515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'title': 'Content Scripts',
4525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'content': [{
4535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'partial': self._template_cache.GetFromFile(
4545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            posixpath.join(PRIVATE_TEMPLATES,
4555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                           'intro_tables',
4565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                           'content_scripts.html')).Get(),
4575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'contentScriptSupport': content_script_support.__dict__
4585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }]
4595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }]
4605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetIntroDescriptionRow(self):
4625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ''' Generates the 'Description' row data for an API intro table.
4635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
4645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return {
4655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'title': 'Description',
4665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'content': [
4675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        { 'text': self._jsc_model.description }
4685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ]
4695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
4705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetIntroAvailabilityRow(self):
4725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ''' Generates the 'Availability' row data for an API intro table.
4735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
4745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if self._IsExperimental():
4755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      status = 'experimental'
4765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      scheduled = None
4775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      version = None
4785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    else:
4795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      status = self._availability.channel_info.channel
4805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      scheduled = self._availability.scheduled
4815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      version = self._availability.channel_info.version
4825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return {
4835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'title': 'Availability',
4845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      'content': [
4855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._CreateAvailabilityTemplate(status, scheduled, version)
4865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ]
4875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
4885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetIntroDependencyRows(self):
4905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Devtools aren't in _api_features. If we're dealing with devtools, bail.
4915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if 'devtools' in self._jsc_model.name:
4925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return []
4935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    api_feature = self._api_features.Get().get(self._jsc_model.name)
4955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if not api_feature:
4965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      logging.error('"%s" not found in _api_features.json' %
4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                    self._jsc_model.name)
4985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return []
4995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    permissions_content = []
5015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    manifest_content = []
5025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    def categorize_dependency(dependency):
5045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      def make_code_node(text):
5055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        return { 'class': 'code', 'text': text }
5065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      context, name = dependency.split(':', 1)
5085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if context == 'permission':
5095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        permissions_content.append(make_code_node('"%s"' % name))
5105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif context == 'manifest':
5115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        manifest_content.append(make_code_node('"%s": {...}' % name))
5125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif context == 'api':
5135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        transitive_dependencies = (
5145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self._api_features.Get().get(name, {}).get('dependencies', []))
5155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        for transitive_dependency in transitive_dependencies:
5165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          categorize_dependency(transitive_dependency)
5175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      else:
5185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        logging.error('Unrecognized dependency for %s: %s' %
5195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      (self._jsc_model.name, context))
5205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for dependency in api_feature.get('dependencies', ()):
5225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      categorize_dependency(dependency)
5235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    dependency_rows = []
5255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if permissions_content:
5265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      dependency_rows.append({
5275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'title': 'Permissions',
5285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'content': permissions_content
5295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      })
5305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if manifest_content:
5315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      dependency_rows.append({
5325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'title': 'Manifest',
5335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        'content': manifest_content
5345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      })
5355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return dependency_rows
5365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetMiscIntroRows(self):
5385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ''' Generates miscellaneous intro table row data, such as 'Permissions',
5395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    'Samples', and 'Learn More', using intro_tables.json.
5405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    '''
5415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    misc_rows = []
5425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Look up the API name in intro_tables.json, which is structured
5435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # similarly to the data structure being created. If the name is found, loop
5445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # through the attributes and add them to this structure.
5455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    table_info = self._intro_tables.Get().get(self._jsc_model.name)
5465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if table_info is None:
5475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return misc_rows
5485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for category in table_info.iterkeys():
5505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      content = []
5515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for node in table_info[category]:
5525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        ext_type = PlatformToExtensionType(self._platform)
5535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Don't display nodes restricted to a different platform.
5545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if ext_type not in node.get('extension_types', (ext_type,)):
5555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          continue
5565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # If there is a 'partial' argument and it hasn't already been
55703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        # converted to a Motemplate object, transform it to a template.
5585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if 'partial' in node:
5595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # Note: it's enough to copy() not deepcopy() because only a single
5605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # top-level key is being modified.
5615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          node = copy(node)
5625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          node['partial'] = self._template_cache.GetFromFile(
5635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              posixpath.join(PRIVATE_TEMPLATES, node['partial'])).Get()
5645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        content.append(node)
5655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      misc_rows.append({ 'title': category, 'content': content })
5665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return misc_rows
567