1# Copyright 2014 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 collections 6import logging 7 8from telemetry.core.backends.chrome import inspector_backend 9 10 11def DebuggerUrlToId(debugger_url): 12 return debugger_url.split('/')[-1] 13 14 15class InspectorBackendList(collections.Sequence): 16 """A dynamic sequence of active InspectorBackends.""" 17 18 def __init__(self, browser_backend, backend_wrapper): 19 """Constructor. 20 21 Args: 22 browser_backend: The BrowserBackend instance to query for 23 InspectorBackends. 24 backend_wrapper: A public interface for wrapping each 25 InspectorBackend. It must accept an argument of the 26 InspectorBackend to wrap, and an argument of the 27 InspectorBackendList, and may expose whatever methods 28 are desired on top of that backend. 29 """ 30 self._browser_backend = browser_backend 31 # A ordered mapping of context IDs to inspectable contexts. 32 self._inspectable_contexts_dict = collections.OrderedDict() 33 # A cache of inspector backends, by context ID. 34 self._inspector_backend_dict = {} 35 # A wrapper class for InspectorBackends. 36 self._backend_wrapper = backend_wrapper 37 38 def GetContextInfo(self, context_id): 39 return self._inspectable_contexts_dict[context_id] 40 41 def ShouldIncludeContext(self, _context): 42 """Override this method to control which contexts are included.""" 43 return True 44 45 #TODO(nednguyen): Remove this method and turn inspector_backend_list API to 46 # dictionary-like API (crbug.com/398467) 47 def __getitem__(self, index): 48 self._Update() 49 if index >= len(self._inspectable_contexts_dict.keys()): 50 logging.error('About to explode: _inspectable_contexts_dict.keys() = %s', 51 repr({ 52 "index": index, 53 "keys": self._inspectable_contexts_dict.keys() 54 })) 55 context_id = self._inspectable_contexts_dict.keys()[index] 56 return self.GetBackendFromContextId(context_id) 57 58 def GetBackendFromContextId(self, context_id): 59 self._Update() 60 if context_id not in self._inspectable_contexts_dict: 61 raise KeyError('Cannot find a context with id=%s' % context_id) 62 if context_id not in self._inspector_backend_dict: 63 backend = inspector_backend.InspectorBackend( 64 self._browser_backend, 65 self._inspectable_contexts_dict[context_id]) 66 backend = self._backend_wrapper(backend, self) 67 self._inspector_backend_dict[context_id] = backend 68 return self._inspector_backend_dict[context_id] 69 70 def __iter__(self): 71 self._Update() 72 return self._inspectable_contexts_dict.keys().__iter__() 73 74 def __len__(self): 75 self._Update() 76 return len(self._inspectable_contexts_dict) 77 78 def _Update(self): 79 contexts = self._browser_backend.ListInspectableContexts() 80 context_ids = [context['id'] for context in contexts] 81 82 # Append all new inspectable contexts to the dict. 83 for context in contexts: 84 if not self.ShouldIncludeContext(context): 85 continue 86 if context['id'] in self._inspectable_contexts_dict: 87 continue 88 self._inspectable_contexts_dict[context['id']] = context 89 90 # Remove all inspectable contexts that have gone away from the dict. 91 for context_id in self._inspectable_contexts_dict.keys(): 92 if context_id not in context_ids: 93 del self._inspectable_contexts_dict[context_id] 94 else: 95 # Also remove inspectable contexts that have no websocketDebuggerUrls. 96 context = next(context for context in contexts 97 if context['id'] == context_id) 98 if (context_id not in self._inspector_backend_dict.keys() and 99 'webSocketDebuggerUrl' not in context): 100 logging.debug('webSocketDebuggerUrl missing, removing %s' 101 % context_id) 102 del self._inspectable_contexts_dict[context_id] 103 104 # Clean up any backends for contexts that have gone away. 105 for context_id in self._inspector_backend_dict.keys(): 106 if context_id not in self._inspectable_contexts_dict: 107 del self._inspector_backend_dict[context_id] 108