11e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 21e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 31e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# found in the LICENSE file. 41e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 51e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)import posixpath 61e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifrom compiled_file_system import Cache, SingleFile, Unicode 8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom extensions_paths import API_PATHS 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from features_bundle import HasParent, GetParentName 101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from file_system import FileNotFoundError 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifrom future import All, Future, Race 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from operator import itemgetter 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifrom path_util import Join 14116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom platform_util import PlatformToExtensionType 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccifrom schema_processor import SchemaProcessor, SchemaProcessorFactory 16116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom third_party.json_schema_compiler.json_schema import DeleteNodes 171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from third_party.json_schema_compiler.model import Namespace, UnixName 181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def GetNodeCategories(): 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '''Returns a tuple of the possible categories a node may belong to. 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''' 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return ('types', 'functions', 'events', 'properties') 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class ContentScriptAPI(object): 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '''Represents an API available to content scripts. 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) |name| is the name of the API or API node this object represents. 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) |restrictedTo| is a list of dictionaries representing the nodes 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) of this API that are available to content scripts, or None if the 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) entire API is available to content scripts. 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''' 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, name): 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.name = name 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.restrictedTo = None 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __eq__(self, o): 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return self.name == o.name and self.restrictedTo == o.restrictedTo 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __ne__(self, o): 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return not (self == o) 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __repr__(self): 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return '<ContentScriptAPI name=%s, restrictedTo=%s>' % (name, restrictedTo) 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __str__(self): 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return repr(self) 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class APIModels(object): 521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) '''Tracks APIs and their Models. 531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) ''' 541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def __init__(self, 56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch features_bundle, 57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch compiled_fs_factory, 58116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch file_system, 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) object_store_creator, 601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci platform, 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci schema_processor_factory): 621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) self._features_bundle = features_bundle 63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._platform = PlatformToExtensionType(platform) 641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) self._model_cache = compiled_fs_factory.Create( 65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch file_system, self._CreateAPIModel, APIModels, category=self._platform) 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._object_store = object_store_creator.Create(APIModels) 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self._schema_processor = Future(callback=lambda: 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci schema_processor_factory.Create(False)) 69116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci @Cache 71116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @SingleFile 72116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch @Unicode 73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def _CreateAPIModel(self, path, data): 74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def does_not_include_platform(node): 75116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return ('extension_types' in node and 76116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch node['extension_types'] != 'all' and 77116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._platform not in node['extension_types']) 78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci schema = self._schema_processor.Get().Process(path, data)[0] 80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if not schema: 81116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch raise ValueError('No schema for %s' % path) 82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return Namespace(DeleteNodes( 831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci schema, matcher=does_not_include_platform), path) 841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) def GetNames(self): 860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # API names appear alongside some of their methods/events/etc in the 870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # features file. APIs are those which either implicitly or explicitly have 880f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # no parent feature (e.g. app, app.window, and devtools.inspectedWindow are 890f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) # APIs; runtime.onConnectNative is not). 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) api_features = self._features_bundle.GetAPIFeatures().Get() 910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return [name for name, feature in api_features.iteritems() 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not HasParent(name, feature, api_features)] 931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def _GetPotentialPathsForModel(self, api_name): 951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci '''Returns the list of file system paths that the model for |api_name| 961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci might be located at. 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ''' 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # By default |api_name| is assumed to be given without a path or extension, 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # so combinations of known paths and extension types will be searched. 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) api_extensions = ('.json', '.idl') 101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch api_paths = API_PATHS 1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # Callers sometimes include a file extension and/or prefix path with the 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # |api_name| argument. We believe them and narrow the search space 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # accordingly. 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) name, ext = posixpath.splitext(api_name) 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if ext in api_extensions: 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) api_extensions = (ext,) 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) api_name = name 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for api_path in api_paths: 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if api_name.startswith(api_path): 112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) api_name = api_name[len(api_path):] 113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) api_paths = (api_path,) 114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) break 1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) # API names are given as declarativeContent and app.window but file names 1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) # will be declarative_content and app_window. 1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) file_name = UnixName(api_name).replace('.', '_') 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Devtools APIs are in API/devtools/ not API/, and have their 1201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) # "devtools" names removed from the file names. 1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) basename = posixpath.basename(file_name) 122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if 'devtools_' in basename: 1231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) file_name = posixpath.join( 124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 'devtools', file_name.replace(basename, 125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) basename.replace('devtools_' , ''))) 1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return [Join(path, file_name + ext) for ext in api_extensions 1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for path in api_paths] 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def GetModel(self, api_name): 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci futures = [self._model_cache.GetFromFile(path) 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for path in self._GetPotentialPathsForModel(api_name)] 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return Race(futures, except_pass=(FileNotFoundError, ValueError)) 134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def GetContentScriptAPIs(self): 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '''Creates a dict of APIs and nodes supported by content scripts in 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) this format: 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) { 1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'extension': '<ContentScriptAPI name='extension', 1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) restrictedTo=[{'node': 'onRequest'}]>', 1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ... 1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''' 1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis_future = self._object_store.Get('content_script_apis') 1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) api_features_future = self._features_bundle.GetAPIFeatures() 1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def resolve(): 1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis = content_script_apis_future.Get() 1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if content_script_apis is not None: 1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return content_script_apis 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) api_features = api_features_future.Get() 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis = {} 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for name, feature in api_features.iteritems(): 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if 'content_script' not in feature.get('contexts', ()): 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) continue 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) parent = GetParentName(name, feature, api_features) 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if parent is None: 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis[name] = ContentScriptAPI(name) 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Creates a dict for the individual node. 1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) node = {'node': name[len(parent) + 1:]} 1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if parent not in content_script_apis: 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis[parent] = ContentScriptAPI(parent) 1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if content_script_apis[parent].restrictedTo: 1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis[parent].restrictedTo.append(node) 1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content_script_apis[parent].restrictedTo = [node] 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._object_store.Set('content_script_apis', content_script_apis) 1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return content_script_apis 1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return Future(callback=resolve) 1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def Refresh(self): 1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch futures = [self.GetModel(name) for name in self.GetNames()] 1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return All(futures, except_pass=(FileNotFoundError, ValueError)) 1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def IterModels(self): 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) future_models = [(name, self.GetModel(name)) for name in self.GetNames()] 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for name, future_model in future_models: 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) model = future_model.Get() 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except FileNotFoundError: 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) continue 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if model: 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) yield name, model 187