Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / docs / server2 / api_models.py
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
5 import posixpath
6
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
17
18
19 def GetNodeCategories():
20   '''Returns a tuple of the possible categories a node may belong to.
21   '''
22   return ('types', 'functions', 'events', 'properties')
23
24
25 class ContentScriptAPI(object):
26   '''Represents an API available to content scripts.
27
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.
32   '''
33   def __init__(self, name):
34     self.name = name
35     self.restrictedTo = None
36
37   def __eq__(self, o):
38     return self.name == o.name and self.restrictedTo == o.restrictedTo
39
40   def __ne__(self, o):
41     return not (self == o)
42
43   def __repr__(self):
44     return '<ContentScriptAPI name=%s, restrictedTo=%s>' % (name, restrictedTo)
45
46   def __str__(self):
47     return repr(self)
48
49
50 class APIModels(object):
51   '''Tracks APIs and their Models.
52   '''
53
54   def __init__(self,
55                features_bundle,
56                compiled_fs_factory,
57                file_system,
58                object_store_creator,
59                platform):
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)
65
66   @SingleFile
67   @Unicode
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'])
73
74     schema = ProcessSchema(path, data)[0]
75     if not schema:
76       raise ValueError('No schema for %s' % path)
77     return Namespace(DeleteNodes(
78         schema, matcher=does_not_include_platform), schema['namespace'])
79
80   def GetNames(self):
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)]
88
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')
93     api_paths = API_PATHS
94
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
97     # accordingly.
98     name, ext = posixpath.splitext(api_name)
99     if ext in api_extensions:
100       api_extensions = (ext,)
101       api_name = name
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,)
106         break
107
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_' , '')))
118
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]
123     def resolve():
124       for future in futures:
125         try:
126           return future.Get()
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.
130       futures[0].Get()
131     return Future(callback=resolve)
132
133   def GetContentScriptAPIs(self):
134     '''Creates a dict of APIs and nodes supported by content scripts in
135     this format:
136
137       {
138         'extension': '<ContentScriptAPI name='extension',
139                                         restrictedTo=[{'node': 'onRequest'}]>',
140         ...
141       }
142     '''
143     content_script_apis_future = self._object_store.Get('content_script_apis')
144     api_features_future = self._features_bundle.GetAPIFeatures()
145     def resolve():
146       content_script_apis = content_script_apis_future.Get()
147       if content_script_apis is not None:
148         return content_script_apis
149
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', ()):
154           continue
155         parent = GetParentName(name, feature, api_features)
156         if parent is None:
157           content_script_apis[name] = ContentScriptAPI(name)
158         else:
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)
165           else:
166             content_script_apis[parent].restrictedTo = [node]
167
168       self._object_store.Set('content_script_apis', content_script_apis)
169       return content_script_apis
170     return Future(callback=resolve)
171
172   def Cron(self):
173     futures = [self.GetModel(name) for name in self.GetNames()]
174     return All(futures, except_pass=(FileNotFoundError, ValueError))
175
176   def IterModels(self):
177     future_models = [(name, self.GetModel(name)) for name in self.GetNames()]
178     for name, future_model in future_models:
179       try:
180         model = future_model.Get()
181       except FileNotFoundError:
182         continue
183       if model:
184         yield name, model