availability_finder.py revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch# Copyright 2013 The Chromium Authors. All rights reserved.
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch# Use of this source code is governed by a BSD-style license that can be
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch# found in the LICENSE file.
4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from collections import Mapping
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import posixpath
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)from api_schema_graph import APISchemaGraph
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfrom branch_utility import BranchUtility
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from extensions_paths import (
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    API, CHROME_API, JSON_TEMPLATES, API_FEATURES, MANIFEST_FEATURES,
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    PERMISSION_FEATURES)
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from file_system import FileNotFoundError
14424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)from third_party.json_schema_compiler.model import UnixName
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)_EXTENSION_API = 'extension_api.json'
1868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# The version where api_features.json is first available.
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_API_FEATURES_MIN_VERSION = 28
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# The version where permission_ and manifest_features.json are available and
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# presented in the current format.
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_ORIGINAL_FEATURES_MIN_VERSION = 20
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# API schemas are aggregated in extension_api.json up to this version.
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_EXTENSION_API_MAX_VERSION = 17
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# The earliest version for which we have SVN data.
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_SVN_MIN_VERSION = 5
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _GetChannelFromFeatures(api_name, json_fs, filename):
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  '''Finds API channel information from the features |filename| within the the
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  given |json_fs|. Returns None if channel information for the API cannot be
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  located.
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  '''
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  feature = json_fs.GetFromFile(filename).Get().get(api_name)
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if feature is None:
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return None
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if isinstance(feature, Mapping):
39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    # The channel information exists as a solitary dict.
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return feature.get('channel')
41ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  # The channel information dict is nested within a list for whitelisting
42ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  # purposes. Take the newest channel out of all of the entries.
43ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  return BranchUtility.NewestChannel(entry.get('channel') for entry in feature)
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
45424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)def _GetChannelFromApiFeatures(api_name, json_fs):
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return _GetChannelFromFeatures(api_name, json_fs, API_FEATURES)
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)def _GetChannelFromManifestFeatures(api_name, json_fs):
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  # _manifest_features.json uses unix_style API names.
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  api_name = UnixName(api_name)
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return _GetChannelFromFeatures(api_name, json_fs, MANIFEST_FEATURES)
54424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
55424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)def _GetChannelFromPermissionFeatures(api_name, json_fs):
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return _GetChannelFromFeatures(api_name, json_fs, PERMISSION_FEATURES)
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass AvailabilityFinder(object):
61424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  '''Generates availability information for APIs by looking at API schemas and
62424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  _features files over multiple release versions of Chrome.
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  '''
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def __init__(self,
6668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)               branch_utility,
671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)               compiled_fs_factory,
681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)               file_system_iterator,
691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)               host_file_system,
701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)               object_store_creator):
711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._branch_utility = branch_utility
721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._compiled_fs_factory = compiled_fs_factory
73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    self._file_system_iterator = file_system_iterator
741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self._host_file_system = host_file_system
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._object_store_creator = object_store_creator
7668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    def create_object_store(category):
7768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      return object_store_creator.Create(AvailabilityFinder, category=category)
7868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    self._top_level_object_store = create_object_store('top_level')
7968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    self._node_level_object_store = create_object_store('node_level')
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._json_fs = compiled_fs_factory.ForJson(self._host_file_system)
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def _GetPredeterminedAvailability(self, api_name):
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    '''Checks a configuration file for hardcoded (i.e. predetermined)
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    availability information for an API.
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    '''
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    api_info = self._json_fs.GetFromFile(
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        JSON_TEMPLATES + 'api_availabilities.json').Get().get(api_name)
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if api_info is None:
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return None
90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if api_info['channel'] == 'stable':
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return self._branch_utility.GetStableChannelInfo(api_info['version'])
92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    else:
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return self._branch_utility.GetChannelInfo(api_info['channel'])
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def _GetApiSchemaFilename(self, api_name, file_system, version):
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''Gets the name of the file which may contain the schema for |api_name| in
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    |file_system|, or None if the API is not found. Note that this may be the
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    single _EXTENSION_API file which all APIs share in older versions of Chrome,
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    in which case it is unknown whether the API actually exists there.
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if version == 'trunk' or version > _ORIGINAL_FEATURES_MIN_VERSION:
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      # API schema filenames switch format to unix_hacker_style.
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      api_name = UnixName(api_name)
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    futures = [(path, file_system.ReadSingle(path))
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               for path in (API, CHROME_API)]
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for path, future in futures:
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      try:
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        filenames = future.Get()
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        for ext in ('json', 'idl'):
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          filename = '%s.%s' % (api_name, ext)
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          if filename in filenames:
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return path + filename
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          if _EXTENSION_API in filenames:
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return path + _EXTENSION_API
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      except FileNotFoundError:
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        pass
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return None
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def _GetApiSchema(self, api_name, file_system, version):
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''Searches |file_system| for |api_name|'s API schema data, and processes
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    and returns it if found.
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    api_filename = self._GetApiSchemaFilename(api_name, file_system, version)
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if api_filename is None:
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      # No file for the API could be found in the given |file_system|.
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return None
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    schema_fs = self._compiled_fs_factory.ForApiSchema(file_system)
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    api_schemas = schema_fs.GetFromFile(api_filename).Get()
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    matching_schemas = [api for api in api_schemas
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        if api['namespace'] == api_name]
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    # There should only be a single matching schema per file, or zero in the
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    # case of no API data being found in _EXTENSION_API.
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    assert len(matching_schemas) <= 1
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return matching_schemas or None
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def _HasApiSchema(self, api_name, file_system, version):
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''Whether or not an API schema for |api_name|exists in the given
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    |file_system|.
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '''
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    filename = self._GetApiSchemaFilename(api_name, file_system, version)
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if filename is None:
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return False
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if filename.endswith(_EXTENSION_API):
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return self._GetApiSchema(api_name, file_system, version) is not None
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return True
148424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
149424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _CheckStableAvailability(self, api_name, file_system, version):
150424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''Checks for availability of an API, |api_name|, on the stable channel.
151424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    Considers several _features.json files, file system existence, and
152424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    extension_api.json depending on the given |version|.
153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if version < _SVN_MIN_VERSION:
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      # SVN data isn't available below this version.
156424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return False
157424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    available_channel = None
1581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    json_fs = self._compiled_fs_factory.ForJson(file_system)
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if version >= _API_FEATURES_MIN_VERSION:
160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # The _api_features.json file first appears in version 28 and should be
161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # the most reliable for finding API availability.
1621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      available_channel = _GetChannelFromApiFeatures(api_name, json_fs)
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if version >= _ORIGINAL_FEATURES_MIN_VERSION:
164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # The _permission_features.json and _manifest_features.json files are
165424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # present in Chrome 20 and onwards. Use these if no information could be
166424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # found using _api_features.json.
167424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      available_channel = available_channel or (
1681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          _GetChannelFromPermissionFeatures(api_name, json_fs)
1691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          or _GetChannelFromManifestFeatures(api_name, json_fs))
170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if available_channel is not None:
171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        return available_channel == 'stable'
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if version >= _SVN_MIN_VERSION:
17368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      # Fall back to a check for file system existence if the API is not
17468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      # stable in any of the _features.json files, or if the _features files
17568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      # do not exist (version 19 and earlier).
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return self._HasApiSchema(api_name, file_system, version)
177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  def _CheckChannelAvailability(self, api_name, file_system, channel_info):
1791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    '''Searches through the _features files in a given |file_system|, falling
1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    back to checking the file system for API schema existence, to determine
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    whether or not an API is available on the given channel, |channel_info|.
182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''
1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    json_fs = self._compiled_fs_factory.ForJson(file_system)
1841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    available_channel = (_GetChannelFromApiFeatures(api_name, json_fs)
1851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        or _GetChannelFromPermissionFeatures(api_name, json_fs)
1861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        or _GetChannelFromManifestFeatures(api_name, json_fs))
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (available_channel is None and
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        self._HasApiSchema(api_name, file_system, channel_info.version)):
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      # If an API is not represented in any of the _features files, but exists
190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      # in the filesystem, then assume it is available in this version.
191a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      # The chrome.windows API is an example of this.
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      available_channel = channel_info.channel
193424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # If the channel we're checking is the same as or newer than the
194424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    # |available_channel| then the API is available at this channel.
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    newest = BranchUtility.NewestChannel((available_channel,
196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          channel_info.channel))
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return available_channel is not None and newest == channel_info.channel
198424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
199424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  def _CheckApiAvailability(self, api_name, file_system, channel_info):
200424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''Determines the availability for an API at a certain version of Chrome.
201424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    Two branches of logic are used depending on whether or not the API is
202424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    determined to be 'stable' at the given version.
203424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''
204424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if channel_info.channel == 'stable':
205424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      return self._CheckStableAvailability(api_name,
206424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                                           file_system,
207424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                                           channel_info.version)
208424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return self._CheckChannelAvailability(api_name,
209424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                                          file_system,
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          channel_info)
211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  def GetApiAvailability(self, api_name):
213424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    '''Performs a search for an API's top-level availability by using a
214424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    HostFileSystemIterator instance to traverse multiple version of the
215424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    SVN filesystem.
216eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    '''
21768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    availability = self._top_level_object_store.Get(api_name).Get()
218eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if availability is not None:
219eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return availability
220eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # Check for predetermined availability and cache this information if found.
222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    availability = self._GetPredeterminedAvailability(api_name)
223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if availability is not None:
224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      self._top_level_object_store.Set(api_name, availability)
225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return availability
226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
227424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    def check_api_availability(file_system, channel_info):
228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return self._CheckApiAvailability(api_name, file_system, channel_info)
229eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
230424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    availability = self._file_system_iterator.Descending(
231424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        self._branch_utility.GetChannelInfo('dev'),
232424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        check_api_availability)
2333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if availability is None:
234424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # The API wasn't available on 'dev', so it must be a 'trunk'-only API.
235424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      availability = self._branch_utility.GetChannelInfo('trunk')
23668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    self._top_level_object_store.Set(api_name, availability)
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return availability
23868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
23968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  def GetApiNodeAvailability(self, api_name):
24068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    '''Returns an APISchemaGraph annotated with each node's availability (the
24168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    ChannelInfo at the oldest channel it's available in).
24268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    '''
24368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    availability_graph = self._node_level_object_store.Get(api_name).Get()
24468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if availability_graph is not None:
24568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      return availability_graph
24668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    def assert_not_none(value):
248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      assert value is not None
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return value
2504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
25168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    availability_graph = APISchemaGraph()
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    host_fs = self._host_file_system
254a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    trunk_stat = assert_not_none(host_fs.Stat(self._GetApiSchemaFilename(
255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        api_name, host_fs, 'trunk')))
256f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # Weird object thing here because nonlocal is Python 3.
258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    previous = type('previous', (object,), {'stat': None, 'graph': None})
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
26068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    def update_availability_graph(file_system, channel_info):
261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      version_filename = assert_not_none(self._GetApiSchemaFilename(
262a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          api_name, file_system, channel_info.version))
263a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      version_stat = assert_not_none(file_system.Stat(version_filename))
264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      # Important optimisation: only re-parse the graph if the file changed in
266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      # the last revision. Parsing the same schema and forming a graph on every
267f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      # iteration is really expensive.
268f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if version_stat == previous.stat:
269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        version_graph = previous.graph
270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      else:
271f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        # Keep track of any new schema elements from this version by adding
272f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        # them to |availability_graph|.
273f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        #
274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        # Calling |availability_graph|.Lookup() on the nodes being updated
275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        # will return the |annotation| object -- the current |channel_info|.
276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        version_graph = APISchemaGraph(self._GetApiSchema(
277a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            api_name, file_system, channel_info.version))
278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        availability_graph.Update(version_graph.Subtract(availability_graph),
279f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  annotation=channel_info)
280f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
281f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      previous.stat = version_stat
282f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      previous.graph = version_graph
28368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
28468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      # Continue looping until there are no longer differences between this
28568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      # version and trunk.
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return version_stat != trunk_stat
28768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
2884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    self._file_system_iterator.Ascending(self.GetApiAvailability(api_name),
2894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                         update_availability_graph)
29068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
29168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    self._node_level_object_store.Set(api_name, availability_graph)
29268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    return availability_graph
293