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.
7 from compiled_file_system import SingleFile, Unicode
8 from extensions_paths import API_PATHS
9 from features_bundle import HasParent, GetParentName
10 from file_system import FileNotFoundError
11 from future import All, Future
12 from operator import itemgetter
13 from platform_util import PlatformToExtensionType
14 from schema_util import ProcessSchema
15 from third_party.json_schema_compiler.json_schema import DeleteNodes
16 from third_party.json_schema_compiler.model import Namespace, UnixName
19 def GetNodeCategories():
20 '''Returns a tuple of the possible categories a node may belong to.
22 return ('types', 'functions', 'events', 'properties')
25 class ContentScriptAPI(object):
26 '''Represents an API available to content scripts.
28 |name| is the name of the API or API node this object represents.
29 |restrictedTo| is a list of dictionaries representing the nodes
30 of this API that are available to content scripts, or None if the
31 entire API is available to content scripts.
33 def __init__(self, name):
35 self.restrictedTo = None
38 return self.name == o.name and self.restrictedTo == o.restrictedTo
41 return not (self == o)
44 return '<ContentScriptAPI name=%s, restrictedTo=%s>' % (name, restrictedTo)
50 class APIModels(object):
51 '''Tracks APIs and their Models.
60 self._features_bundle = features_bundle
61 self._platform = PlatformToExtensionType(platform)
62 self._model_cache = compiled_fs_factory.Create(
63 file_system, self._CreateAPIModel, APIModels, category=self._platform)
64 self._object_store = object_store_creator.Create(APIModels)
68 def _CreateAPIModel(self, path, data):
69 def does_not_include_platform(node):
70 return ('extension_types' in node and
71 node['extension_types'] != 'all' and
72 self._platform not in node['extension_types'])
74 schema = ProcessSchema(path, data)[0]
76 raise ValueError('No schema for %s' % path)
77 return Namespace(DeleteNodes(
78 schema, matcher=does_not_include_platform), schema['namespace'])
81 # API names appear alongside some of their methods/events/etc in the
82 # features file. APIs are those which either implicitly or explicitly have
83 # no parent feature (e.g. app, app.window, and devtools.inspectedWindow are
84 # APIs; runtime.onConnectNative is not).
85 api_features = self._features_bundle.GetAPIFeatures().Get()
86 return [name for name, feature in api_features.iteritems()
87 if not HasParent(name, feature, api_features)]
89 def GetModel(self, api_name):
90 # By default |api_name| is assumed to be given without a path or extension,
91 # so combinations of known paths and extension types will be searched.
92 api_extensions = ('.json', '.idl')
95 # Callers sometimes include a file extension and/or prefix path with the
96 # |api_name| argument. We believe them and narrow the search space
98 name, ext = posixpath.splitext(api_name)
99 if ext in api_extensions:
100 api_extensions = (ext,)
102 for api_path in api_paths:
103 if api_name.startswith(api_path):
104 api_name = api_name[len(api_path):]
105 api_paths = (api_path,)
108 # API names are given as declarativeContent and app.window but file names
109 # will be declarative_content and app_window.
110 file_name = UnixName(api_name).replace('.', '_')
111 # Devtools APIs are in API/devtools/ not API/, and have their
112 # "devtools" names removed from the file names.
113 basename = posixpath.basename(file_name)
114 if 'devtools_' in basename:
115 file_name = posixpath.join(
116 'devtools', file_name.replace(basename,
117 basename.replace('devtools_' , '')))
119 futures = [self._model_cache.GetFromFile(
120 posixpath.join(path, '%s%s' % (file_name, ext)))
121 for ext in api_extensions
122 for path in api_paths]
124 for future in futures:
127 # Either the file wasn't found or there was no schema for the file
128 except (FileNotFoundError, ValueError): pass
129 # Propagate the first error if neither were found.
131 return Future(callback=resolve)
133 def GetContentScriptAPIs(self):
134 '''Creates a dict of APIs and nodes supported by content scripts in
138 'extension': '<ContentScriptAPI name='extension',
139 restrictedTo=[{'node': 'onRequest'}]>',
143 content_script_apis_future = self._object_store.Get('content_script_apis')
144 api_features_future = self._features_bundle.GetAPIFeatures()
146 content_script_apis = content_script_apis_future.Get()
147 if content_script_apis is not None:
148 return content_script_apis
150 api_features = api_features_future.Get()
151 content_script_apis = {}
152 for name, feature in api_features.iteritems():
153 if 'content_script' not in feature.get('contexts', ()):
155 parent = GetParentName(name, feature, api_features)
157 content_script_apis[name] = ContentScriptAPI(name)
159 # Creates a dict for the individual node.
160 node = {'node': name[len(parent) + 1:]}
161 if parent not in content_script_apis:
162 content_script_apis[parent] = ContentScriptAPI(parent)
163 if content_script_apis[parent].restrictedTo:
164 content_script_apis[parent].restrictedTo.append(node)
166 content_script_apis[parent].restrictedTo = [node]
168 self._object_store.Set('content_script_apis', content_script_apis)
169 return content_script_apis
170 return Future(callback=resolve)
173 futures = [self.GetModel(name) for name in self.GetNames()]
174 return All(futures, except_pass=(FileNotFoundError, ValueError))
176 def IterModels(self):
177 future_models = [(name, self.GetModel(name)) for name in self.GetNames()]
178 for name, future_model in future_models:
180 model = future_model.Get()
181 except FileNotFoundError: