• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import posixpath
6
7from compiled_file_system import SingleFile, Unicode
8from extensions_paths import API_PATHS
9from file_system import FileNotFoundError
10from future import Collect, Future
11from schema_util import ProcessSchema
12from third_party.json_schema_compiler.model import Namespace, UnixName
13
14
15@SingleFile
16@Unicode
17def _CreateAPIModel(path, data):
18  schema = ProcessSchema(path, data)[0]
19  if not schema:
20    raise FileNotFoundError('No schema for %s' % path)
21  return Namespace(schema, schema['namespace'])
22
23
24class APIModels(object):
25  '''Tracks APIs and their Models.
26  '''
27
28  def __init__(self, features_bundle, compiled_fs_factory, file_system):
29    self._features_bundle = features_bundle
30    self._identity = file_system.GetIdentity()
31    self._model_cache = compiled_fs_factory.Create(
32        file_system, _CreateAPIModel, APIModels)
33
34  def GetNames(self):
35    # API names appear alongside some of their methods/events/etc in the
36    # features file. APIs are those which either implicitly or explicitly have
37    # no parent feature (e.g. app, app.window, and devtools.inspectedWindow are
38    # APIs; runtime.onConnectNative is not).
39    api_features = self._features_bundle.GetAPIFeatures().Get()
40    return [name for name, feature in api_features.iteritems()
41            if ('.' not in name or
42                name.rsplit('.', 1)[0] not in api_features or
43                feature.get('noparent'))]
44
45  def GetModel(self, api_name):
46    # By default |api_name| is assumed to be given without a path or extension,
47    # so combinations of known paths and extension types will be searched.
48    api_extensions = ('.json', '.idl')
49    api_paths = API_PATHS
50
51    # Callers sometimes include a file extension and/or prefix path with the
52    # |api_name| argument. We believe them and narrow the search space
53    # accordingly.
54    name, ext = posixpath.splitext(api_name)
55    if ext in api_extensions:
56      api_extensions = (ext,)
57      api_name = name
58    for api_path in api_paths:
59      if api_name.startswith(api_path):
60        api_name = api_name[len(api_path):]
61        api_paths = (api_path,)
62        break
63
64    # API names are given as declarativeContent and app.window but file names
65    # will be declarative_content and app_window.
66    file_name = UnixName(api_name).replace('.', '_')
67    # Devtools APIs are in API/devtools/ not API/, and have their
68    # "devtools" names removed from the file names.
69    basename = posixpath.basename(file_name)
70    if 'devtools_' in basename:
71      file_name = posixpath.join(
72          'devtools', file_name.replace(basename,
73                                        basename.replace('devtools_' , '')))
74
75    futures = [self._model_cache.GetFromFile(
76                   posixpath.join(path, '%s%s' % (file_name, ext)))
77               for ext in api_extensions
78               for path in api_paths]
79    def resolve():
80      for future in futures:
81        try:
82          return future.Get()
83        except FileNotFoundError: pass
84      # Propagate the first FileNotFoundError if neither were found.
85      futures[0].Get()
86    return Future(callback=resolve)
87
88  def Cron(self):
89    futures = [self.GetModel(name) for name in self.GetNames()]
90    return Collect(futures, except_pass=FileNotFoundError)
91
92  def IterModels(self):
93    future_models = [(name, self.GetModel(name)) for name in self.GetNames()]
94    for name, future_model in future_models:
95      try:
96        model = future_model.Get()
97      except FileNotFoundError:
98        continue
99      if model:
100        yield name, model
101
102  def GetIdentity(self):
103    return self._identity
104