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 Cache
, 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
, Race
12 from operator
import itemgetter
13 from path_util
import Join
14 from platform_util
import PlatformToExtensionType
15 from schema_processor
import SchemaProcessor
, SchemaProcessorFactory
16 from third_party
.json_schema_compiler
.json_schema
import DeleteNodes
17 from third_party
.json_schema_compiler
.model
import Namespace
, UnixName
20 def GetNodeCategories():
21 '''Returns a tuple of the possible categories a node may belong to.
23 return ('types', 'functions', 'events', 'properties')
26 class ContentScriptAPI(object):
27 '''Represents an API available to content scripts.
29 |name| is the name of the API or API node this object represents.
30 |restrictedTo| is a list of dictionaries representing the nodes
31 of this API that are available to content scripts, or None if the
32 entire API is available to content scripts.
34 def __init__(self
, name
):
36 self
.restrictedTo
= None
39 return self
.name
== o
.name
and self
.restrictedTo
== o
.restrictedTo
42 return not (self
== o
)
45 return ('<ContentScriptAPI name=%s, restrictedTo=%s>' %
46 (self
.name
, self
.restrictedTo
))
52 class APIModels(object):
53 '''Tracks APIs and their Models.
62 schema_processor_factory
):
63 self
._features
_bundle
= features_bundle
64 self
._platform
= PlatformToExtensionType(platform
)
65 self
._model
_cache
= compiled_fs_factory
.Create(
66 file_system
, self
._CreateAPIModel
, APIModels
, category
=self
._platform
)
67 self
._object
_store
= object_store_creator
.Create(APIModels
)
68 self
._schema
_processor
= Future(callback
=lambda:
69 schema_processor_factory
.Create(False))
74 def _CreateAPIModel(self
, path
, data
):
75 def does_not_include_platform(node
):
76 return ('extension_types' in node
and
77 node
['extension_types'] != 'all' and
78 self
._platform
not in node
['extension_types'])
80 schema
= self
._schema
_processor
.Get().Process(path
, data
)[0]
82 raise ValueError('No schema for %s' % path
)
83 return Namespace(DeleteNodes(
84 schema
, matcher
=does_not_include_platform
), path
)
87 # API names appear alongside some of their methods/events/etc in the
88 # features file. APIs are those which either implicitly or explicitly have
89 # no parent feature (e.g. app, app.window, and devtools.inspectedWindow are
90 # APIs; runtime.onConnectNative is not).
91 api_features
= self
._features
_bundle
.GetAPIFeatures().Get()
92 return [name
for name
, feature
in api_features
.iteritems()
93 if not HasParent(name
, feature
, api_features
)]
95 def _GetPotentialPathsForModel(self
, api_name
):
96 '''Returns the list of file system paths that the model for |api_name|
99 # By default |api_name| is assumed to be given without a path or extension,
100 # so combinations of known paths and extension types will be searched.
101 api_extensions
= ('.json', '.idl')
102 api_paths
= API_PATHS
104 # Callers sometimes include a file extension and/or prefix path with the
105 # |api_name| argument. We believe them and narrow the search space
107 name
, ext
= posixpath
.splitext(api_name
)
108 if ext
in api_extensions
:
109 api_extensions
= (ext
,)
111 for api_path
in api_paths
:
112 if api_name
.startswith(api_path
):
113 api_name
= api_name
[len(api_path
):]
114 api_paths
= (api_path
,)
117 # API names are given as declarativeContent and app.window but file names
118 # will be declarative_content and app_window.
119 file_name
= UnixName(api_name
).replace('.', '_')
120 # Devtools APIs are in API/devtools/ not API/, and have their
121 # "devtools" names removed from the file names.
122 basename
= posixpath
.basename(file_name
)
123 if 'devtools_' in basename
:
124 file_name
= posixpath
.join(
125 'devtools', file_name
.replace(basename
,
126 basename
.replace('devtools_' , '')))
128 return [Join(path
, file_name
+ ext
) for ext
in api_extensions
129 for path
in api_paths
]
131 def GetModel(self
, api_name
):
132 futures
= [self
._model
_cache
.GetFromFile(path
)
133 for path
in self
._GetPotentialPathsForModel
(api_name
)]
134 return Race(futures
, except_pass
=(FileNotFoundError
, ValueError))
136 def GetContentScriptAPIs(self
):
137 '''Creates a dict of APIs and nodes supported by content scripts in
141 'extension': '<ContentScriptAPI name='extension',
142 restrictedTo=[{'node': 'onRequest'}]>',
146 content_script_apis_future
= self
._object
_store
.Get('content_script_apis')
147 api_features_future
= self
._features
_bundle
.GetAPIFeatures()
149 content_script_apis
= content_script_apis_future
.Get()
150 if content_script_apis
is not None:
151 return content_script_apis
153 api_features
= api_features_future
.Get()
154 content_script_apis
= {}
155 for name
, feature
in api_features
.iteritems():
156 if 'content_script' not in feature
.get('contexts', ()):
158 parent
= GetParentName(name
, feature
, api_features
)
160 content_script_apis
[name
] = ContentScriptAPI(name
)
162 # Creates a dict for the individual node.
163 node
= {'node': name
[len(parent
) + 1:]}
164 if parent
not in content_script_apis
:
165 content_script_apis
[parent
] = ContentScriptAPI(parent
)
166 if content_script_apis
[parent
].restrictedTo
:
167 content_script_apis
[parent
].restrictedTo
.append(node
)
169 content_script_apis
[parent
].restrictedTo
= [node
]
171 self
._object
_store
.Set('content_script_apis', content_script_apis
)
172 return content_script_apis
173 return Future(callback
=resolve
)
176 futures
= [self
.GetModel(name
) for name
in self
.GetNames()]
177 return All(futures
, except_pass
=(FileNotFoundError
, ValueError))
179 def IterModels(self
):
180 future_models
= [(name
, self
.GetModel(name
)) for name
in self
.GetNames()]
181 for name
, future_model
in future_models
:
183 model
= future_model
.Get()
184 except FileNotFoundError
: