15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import copy
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
90f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)from environment import IsPreviewServer
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from extensions_paths import (
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    API, API_FEATURES, JSON_TEMPLATES, PRIVATE_TEMPLATES)
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import third_party.json_schema_compiler.json_parse as json_parse
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import third_party.json_schema_compiler.model as model
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from environment import IsPreviewServer
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)from third_party.json_schema_compiler.memoize import memoize
16424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _CreateId(node, prefix):
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if node.parent is not None and not isinstance(node.parent, model.Namespace):
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return '-'.join([prefix, node.parent.simple_name, node.simple_name])
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return '-'.join([prefix, node.simple_name])
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _FormatValue(value):
25424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''Inserts commas every three digits for integer values. It is magic.
26424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  s = str(value)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
310f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)def _GetByNameDict(namespace):
320f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  '''Returns a dictionary mapping names to named items from |namespace|.
330f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
340f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  This lets us render specific API entities rather than the whole thing at once,
350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  for example {{apis.manifestTypes.byName.ExternallyConnectable}}.
360f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
370f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  Includes items from namespace['types'], namespace['functions'],
380f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  namespace['events'], and namespace['properties'].
390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  '''
400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  by_name = {}
410f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  for item_type in ('types', 'functions', 'events', 'properties'):
420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if item_type in namespace:
430f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      old_size = len(by_name)
440f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      by_name.update(
450f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          (item['name'], item) for item in namespace[item_type])
460f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      assert len(by_name) == old_size + len(namespace[item_type]), (
470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          'Duplicate name in %r' % namespace)
480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  return by_name
490f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)def _GetEventByNameFromEvents(events):
520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  '''Parses the dictionary |events| to find the definitions of members of the
530f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  type Event.  Returns a dictionary mapping the name of a member to that
540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  member's definition.
55424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''
563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  assert 'types' in events, \
573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      'The dictionary |events| must contain the key "types".'
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  event_list = [t for t in events['types'] if t.get('name') == 'Event']
593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  assert len(event_list) == 1, 'Exactly one type must be called "Event".'
600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  return _GetByNameDict(event_list[0])
613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
62424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class _JSCModel(object):
64424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''Uses a Model from the JSON Schema Compiler and generates a dict that
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  a Handlebar template can use for a data source.
66424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''
67424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
68ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def __init__(self,
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               api_name,
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               api_models,
71ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               ref_resolver,
72ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               disable_refs,
73ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch               availability_finder,
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               json_cache,
750f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)               template_cache,
76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               event_byname_function):
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._ref_resolver = ref_resolver
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._disable_refs = disable_refs
79ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    self._availability_finder = availability_finder
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._api_availabilities = json_cache.GetFromFile(
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        '%s/api_availabilities.json' % JSON_TEMPLATES)
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._intro_tables = json_cache.GetFromFile(
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        '%s/intro_tables.json' % JSON_TEMPLATES)
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._api_features = json_cache.GetFromFile(API_FEATURES)
850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    self._template_cache = template_cache
860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    self._event_byname_function = event_byname_function
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._namespace = api_models.GetModel(api_name).Get()
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FormatDescription(self, description):
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._disable_refs:
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return description
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._ref_resolver.ResolveAllLinks(description,
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                              namespace=self._namespace.name)
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GetLink(self, link):
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._disable_refs:
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      type_name = link.split('.', 1)[-1]
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return { 'href': '#type-%s' % type_name, 'text': link, 'name': link }
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._ref_resolver.SafeGetLink(link, namespace=self._namespace.name)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ToDict(self):
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self._namespace is None:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return {}
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    chrome_dot_name = 'chrome.%s' % self._namespace.name
1053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    as_dict = {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': self._namespace.name,
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      'namespace': self._namespace.documentation_options.get('namespace',
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                             chrome_dot_name),
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      'title': self._namespace.documentation_options.get('title',
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                         chrome_dot_name),
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      'documentationOptions': self._namespace.documentation_options,
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'types': self._GenerateTypes(self._namespace.types.values()),
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'functions': self._GenerateFunctions(self._namespace.functions),
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'events': self._GenerateEvents(self._namespace.events),
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      'domEvents': self._GenerateDomEvents(self._namespace.events),
116ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      'properties': self._GenerateProperties(self._namespace.properties),
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # Rendering the intro list is really expensive and there's no point doing it
1190f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # unless we're rending the page - and disable_refs=True implies we're not.
1200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if not self._disable_refs:
1210f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      as_dict.update({
1220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        'introList': self._GetIntroTableList(),
1230f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        'channelWarning': self._GetChannelWarning(),
1240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      })
1250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    as_dict['byName'] = _GetByNameDict(as_dict)
1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return as_dict
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
128ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _GetApiAvailability(self):
129ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return self._availability_finder.GetApiAvailability(self._namespace.name)
130ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
131ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _GetChannelWarning(self):
132ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if not self._IsExperimental():
133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      return { self._GetApiAvailability().channel: True }
134ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    return None
135ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
136ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  def _IsExperimental(self):
1373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return self._namespace.name.startswith('experimental')
138ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateTypes(self, types):
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return [self._GenerateType(t) for t in types]
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateType(self, type_):
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    type_dict = {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': type_.simple_name,
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(type_.description),
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'properties': self._GenerateProperties(type_.properties),
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'functions': self._GenerateFunctions(type_.functions),
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'events': self._GenerateEvents(type_.events),
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(type_, 'type')
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._RenderTypeInformation(type_, type_dict)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return type_dict
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunctions(self, functions):
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [self._GenerateFunction(f) for f in functions.values()]
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateFunction(self, function):
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    function_dict = {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': function.simple_name,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(function.description),
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'callback': self._GenerateCallback(function.callback),
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': [],
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'returns': None,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(function, 'method')
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._AddCommonProperties(function_dict, function)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if function.returns:
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function_dict['returns'] = self._GenerateType(function.returns)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for param in function.params:
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function_dict['parameters'].append(self._GenerateProperty(param))
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if function.callback is not None:
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Show the callback as an extra parameter.
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function_dict['parameters'].append(
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          self._GenerateCallbackProperty(function.callback))
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(function_dict['parameters']) > 0:
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      function_dict['parameters'][-1]['last'] = True
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return function_dict
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateEvents(self, events):
1804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return [self._GenerateEvent(e) for e in events.values()
1814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            if not e.supports_dom]
1824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  def _GenerateDomEvents(self, events):
1844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return [self._GenerateEvent(e) for e in events.values()
1854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            if e.supports_dom]
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateEvent(self, event):
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    event_dict = {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': event.simple_name,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(event.description),
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'filters': [self._GenerateProperty(f) for f in event.filters],
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'conditions': [self._GetLink(condition)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     for condition in event.conditions],
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'actions': [self._GetLink(action) for action in event.actions],
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'supportsRules': event.supports_rules,
1963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      'supportsListeners': event.supports_listeners,
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      'properties': [],
1980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'id': _CreateId(event, 'event'),
1990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'byName': {},
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._AddCommonProperties(event_dict, event)
2020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # Add the Event members to each event in this object.
2030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # If refs are disabled then don't worry about this, since it's only needed
2040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # for rendering, and disable_refs=True implies we're not rendering.
2050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if self._event_byname_function and not self._disable_refs:
2060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      event_dict['byName'].update(self._event_byname_function())
2073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    # We need to create the method description for addListener based on the
2083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    # information stored in |event|.
2093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if event.supports_listeners:
2103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      callback_object = model.Function(parent=event,
2113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                       name='callback',
2123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                       json={},
2133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                       namespace=event.parent,
2143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                       origin='')
2153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      callback_object.params = event.params
2163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      if event.callback:
2173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        callback_object.callback = event.callback
2183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      callback_parameters = self._GenerateCallbackProperty(callback_object)
2193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      callback_parameters['last'] = True
2200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      event_dict['byName']['addListener'] = {
2213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'name': 'addListener',
2223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'callback': self._GenerateFunction(callback_object),
2233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        'parameters': [callback_parameters]
2243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      }
2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if event.supports_dom:
2264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # Treat params as properties of the custom Event object associated with
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      # this DOM Event.
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      event_dict['properties'] += [self._GenerateProperty(param)
2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                   for param in event.params]
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return event_dict
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateCallback(self, callback):
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not callback:
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return None
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    callback_dict = {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': callback.simple_name,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'simple_type': {'simple_type': 'function'},
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'optional': callback.optional,
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': []
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for param in callback.params:
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_dict['parameters'].append(self._GenerateProperty(param))
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (len(callback_dict['parameters']) > 0):
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_dict['parameters'][-1]['last'] = True
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return callback_dict
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateProperties(self, properties):
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return [self._GenerateProperty(v) for v in properties.values()]
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GenerateProperty(self, property_):
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not hasattr(property_, 'type_'):
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for d in dir(property_):
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if not d.startswith('_'):
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          print ('%s -> %s' % (d, getattr(property_, d)))
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    type_ = property_.type_
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Make sure we generate property info for arrays, too.
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # TODO(kalman): what about choices?
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.ARRAY:
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      properties = type_.item_type.properties
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      properties = type_.properties
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    property_dict = {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'name': property_.simple_name,
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'optional': property_.optional,
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'description': self._FormatDescription(property_.description),
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'properties': self._GenerateProperties(type_.properties),
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'functions': self._GenerateFunctions(type_.functions),
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'parameters': [],
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'returns': None,
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'id': _CreateId(property_, 'property')
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._AddCommonProperties(property_dict, property_)
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.FUNCTION:
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      function = type_.function
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for param in function.params:
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['parameters'].append(self._GenerateProperty(param))
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if function.returns:
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['returns'] = self._GenerateType(function.returns)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    value = property_.value
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if value is not None:
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if isinstance(value, int):
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['value'] = _FormatValue(value)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_dict['value'] = value
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._RenderTypeInformation(type_, property_dict)
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return property_dict
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _GenerateCallbackProperty(self, callback):
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    property_dict = {
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'name': callback.simple_name,
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'description': self._FormatDescription(callback.description),
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'optional': callback.optional,
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'id': _CreateId(callback, 'property'),
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      'simple_type': 'function',
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (callback.parent is not None and
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        not isinstance(callback.parent, model.Namespace)):
304bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      property_dict['parentName'] = callback.parent.simple_name
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return property_dict
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _RenderTypeInformation(self, type_, dst_dict):
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    dst_dict['is_object'] = type_.property_type == model.PropertyType.OBJECT
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if type_.property_type == model.PropertyType.CHOICES:
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['choices'] = self._GenerateTypes(type_.choices)
311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      # We keep track of which == last for knowing when to add "or" between
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # choices in templates.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(dst_dict['choices']) > 0:
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dst_dict['choices'][-1]['last'] = True
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.REF:
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['link'] = self._GetLink(type_.ref_type)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.ARRAY:
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['array'] = self._GenerateType(type_.item_type)
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.property_type == model.PropertyType.ENUM:
3201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      dst_dict['enum_values'] = [
3211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          {'name': value.name, 'description': value.description}
3221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          for value in type_.enum_values]
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if len(dst_dict['enum_values']) > 0:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        dst_dict['enum_values'][-1]['last'] = True
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    elif type_.instance_of is not None:
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['simple_type'] = type_.instance_of.lower()
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dst_dict['simple_type'] = type_.property_type.name.lower()
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
330424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _GetIntroTableList(self):
331424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''Create a generic data structure that can be traversed by the templates
332424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    to create an API intro table.
333424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''
334424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    intro_rows = [
335424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      self._GetIntroDescriptionRow(),
336424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      self._GetIntroAvailabilityRow()
337424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ] + self._GetIntroDependencyRows()
338424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
339424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # Add rows using data from intro_tables.json, overriding any existing rows
340424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # if they share the same 'title' attribute.
341424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    row_titles = [row['title'] for row in intro_rows]
342424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    for misc_row in self._GetMiscIntroRows():
343424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if misc_row['title'] in row_titles:
344424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        intro_rows[row_titles.index(misc_row['title'])] = misc_row
345424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      else:
346424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        intro_rows.append(misc_row)
347424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
348424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return intro_rows
349424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
350424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _GetIntroDescriptionRow(self):
351424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ''' Generates the 'Description' row data for an API intro table.
352424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''
353424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return {
354424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'title': 'Description',
355424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'content': [
356424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        { 'text': self._FormatDescription(self._namespace.description) }
357424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      ]
358424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    }
359424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
360424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _GetIntroAvailabilityRow(self):
361424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ''' Generates the 'Availability' row data for an API intro table.
362424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''
363424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if self._IsExperimental():
364424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      status = 'experimental'
365424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      version = None
366424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    else:
367424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      availability = self._GetApiAvailability()
368424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      status = availability.channel
369424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      version = availability.version
370424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return {
371424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'title': 'Availability',
372424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      'content': [{
3730f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        'partial': self._template_cache.GetFromFile(
3740f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                       '%s/intro_tables/%s_message.html' %
375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                           (PRIVATE_TEMPLATES, status)).Get(),
376424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'version': version
377424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      }]
378424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    }
379424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
380424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _GetIntroDependencyRows(self):
381424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # Devtools aren't in _api_features. If we're dealing with devtools, bail.
382424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if 'devtools' in self._namespace.name:
383424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return []
3844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    feature = self._api_features.Get().get(self._namespace.name)
385424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    assert feature, ('"%s" not found in _api_features.json.'
386424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                     % self._namespace.name)
387424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
388424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    dependencies = feature.get('dependencies')
389424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if dependencies is None:
390424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return []
391424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
392424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    def make_code_node(text):
393424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return { 'class': 'code', 'text': text }
394424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
395424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    permissions_content = []
396424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    manifest_content = []
397424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
398424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    def categorize_dependency(dependency):
399424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      context, name = dependency.split(':', 1)
400424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if context == 'permission':
401424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        permissions_content.append(make_code_node('"%s"' % name))
402424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      elif context == 'manifest':
403424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        manifest_content.append(make_code_node('"%s": {...}' % name))
404424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      elif context == 'api':
405424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        transitive_dependencies = (
4064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            self._api_features.Get().get(name, {}).get('dependencies', []))
407424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        for transitive_dependency in transitive_dependencies:
408424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          categorize_dependency(transitive_dependency)
409424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      else:
410424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        raise ValueError('Unrecognized dependency for %s: %s' % (
411424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)            self._namespace.name, context))
412424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
413424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    for dependency in dependencies:
414424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      categorize_dependency(dependency)
415424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
416424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    dependency_rows = []
417424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if permissions_content:
418424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      dependency_rows.append({
419424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'title': 'Permissions',
420424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'content': permissions_content
421424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      })
422424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if manifest_content:
423424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      dependency_rows.append({
424424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'title': 'Manifest',
425424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        'content': manifest_content
426424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      })
427424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return dependency_rows
428424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
429424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _GetMiscIntroRows(self):
430424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ''' Generates miscellaneous intro table row data, such as 'Permissions',
431424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    'Samples', and 'Learn More', using intro_tables.json.
432424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''
433424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    misc_rows = []
434424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # Look up the API name in intro_tables.json, which is structured
435424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # similarly to the data structure being created. If the name is found, loop
436424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # through the attributes and add them to this structure.
4374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    table_info = self._intro_tables.Get().get(self._namespace.name)
438424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if table_info is None:
439424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return misc_rows
440424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
441424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    for category in table_info.keys():
442424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      content = copy.deepcopy(table_info[category])
443424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      for node in content:
444424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        # If there is a 'partial' argument and it hasn't already been
445424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        # converted to a Handlebar object, transform it to a template.
446424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        if 'partial' in node:
4470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          node['partial'] = self._template_cache.GetFromFile('%s/%s' %
448f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              (PRIVATE_TEMPLATES, node['partial'])).Get()
449424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      misc_rows.append({ 'title': category, 'content': content })
450424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return misc_rows
451424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
452f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _AddCommonProperties(self, target, src):
453f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if src.deprecated is not None:
454f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      target['deprecated'] = self._FormatDescription(
455f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          src.deprecated)
456f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (src.parent is not None and
457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        not isinstance(src.parent, model.Namespace)):
458f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      target['parentName'] = src.parent.simple_name
459f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
460424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class _LazySamplesGetter(object):
462424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''This class is needed so that an extensions API page does not have to fetch
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the apps samples page and vice versa.
464424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''
465424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, api_name, samples):
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._api_name = api_name
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._samples = samples
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def get(self, key):
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._samples.FilterSamples(key, self._api_name)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
473424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class APIDataSource(object):
475424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''This class fetches and loads JSON APIs from the FileSystem passed in with
476c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  |compiled_fs_factory|, so the APIs can be plugged into templates.
477424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''
478424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class Factory(object):
480ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    def __init__(self,
481ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                 compiled_fs_factory,
4824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 file_system,
483424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                 availability_finder,
484f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 api_models,
485f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 object_store_creator):
486f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._json_cache = compiled_fs_factory.ForJson(file_system)
4870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      self._template_cache = compiled_fs_factory.ForTemplates(file_system)
488f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._availability_finder = availability_finder
489f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._api_models = api_models
490f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._model_cache_refs = object_store_creator.Create(
491f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          APIDataSource, 'model-cache-refs')
492f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._model_cache_no_refs = object_store_creator.Create(
493f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          APIDataSource, 'model-cache-no-refs')
4940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # These must be set later via the SetFooDataSourceFactory methods.
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._ref_resolver_factory = None
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._samples_data_source_factory = None
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      # This caches the result of _LoadEventByName.
5000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      self._event_byname = None
5013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def SetSamplesDataSourceFactory(self, samples_data_source_factory):
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._samples_data_source_factory = samples_data_source_factory
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    def SetReferenceResolverFactory(self, ref_resolver_factory):
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._ref_resolver_factory = ref_resolver_factory
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5080f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    def Create(self, request):
5090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      '''Creates an APIDataSource.
510424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      '''
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self._samples_data_source_factory is None:
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Only error if there is a request, which means this APIDataSource is
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # actually being used to render a page.
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if request is not None:
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          logging.error('SamplesDataSource.Factory was never set in '
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        'APIDataSource.Factory.')
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        samples = None
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        samples = self._samples_data_source_factory.Create(request)
520f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return APIDataSource(self._GetSchemaModel, samples)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5220f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    def _LoadEventByName(self):
523f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      '''All events have some members in common. We source their description
5240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      from Event in events.json.
525f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      '''
5260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      if self._event_byname is None:
527f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        self._event_byname = _GetEventByNameFromEvents(
528f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)            self._GetSchemaModel('events', True))
5290f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      return self._event_byname
5303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
531f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    def _GetModelCache(self, disable_refs):
532f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if disable_refs:
533f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return self._model_cache_no_refs
534f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return self._model_cache_refs
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
536f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    def _GetSchemaModel(self, api_name, disable_refs):
537f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      jsc_model = self._GetModelCache(disable_refs).Get(api_name).Get()
538f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if jsc_model is not None:
539f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return jsc_model
540f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
541f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      jsc_model = _JSCModel(
542f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          api_name,
543f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          self._api_models,
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._ref_resolver_factory.Create() if not disable_refs else None,
54590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          disable_refs,
546ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch          self._availability_finder,
547f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          self._json_cache,
5480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          self._template_cache,
549f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          self._LoadEventByName).ToDict()
550c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
551f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._GetModelCache(disable_refs).Set(api_name, jsc_model)
552f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return jsc_model
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
554f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def __init__(self, get_schema_model, samples):
555f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._get_schema_model = get_schema_model
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._samples = samples
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  def _GenerateHandlebarContext(self, handlebar_dict):
5590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # Parsing samples on the preview server takes seconds and doesn't add
5600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    # anything. Don't do it.
5610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if not IsPreviewServer():
5620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      handlebar_dict['samples'] = _LazySamplesGetter(
5630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          handlebar_dict['name'],
5640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          self._samples)
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return handlebar_dict
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
567f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def get(self, api_name, disable_refs=False):
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._GenerateHandlebarContext(
569f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        self._get_schema_model(api_name, disable_refs))
570