| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 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 import httplib | |
| 5 import json | |
| 6 import socket | |
| 7 import urllib2 | |
| 8 import weakref | |
| 9 | |
| 10 from telemetry.core import util | |
| 11 from telemetry.core import exceptions | |
| 12 from telemetry.core import tab | |
| 13 from telemetry.core.chrome import inspector_backend | |
| 14 | |
| 15 class TabListBackend(object): | |
| 16 def __init__(self, browser_backend): | |
| 17 self._browser_backend = browser_backend | |
| 18 | |
| 19 # Stores web socket debugger URLs in iteration order. | |
| 20 self._tab_list = [] | |
| 21 # Maps debugger URLs to Tab objects. | |
| 22 self._tab_dict = weakref.WeakValueDictionary() | |
| 23 | |
| 24 def Init(self): | |
| 25 self._UpdateTabList() | |
| 26 | |
| 27 def Reset(self): | |
| 28 # TODO(hartmanng): Remove this method when crbug.com/166886 is fixed. | |
| 29 self.Init() | |
| 30 self._tab_list = [] | |
| 31 self._tab_dict = weakref.WeakValueDictionary() | |
| 32 | |
| 33 def New(self, timeout): | |
| 34 assert self._browser_backend.supports_tab_control | |
| 35 | |
| 36 self._browser_backend.Request('new', timeout=timeout) | |
| 37 return self[-1] | |
| 38 | |
| 39 def DoesDebuggerUrlExist(self, url): | |
| 40 self._UpdateTabList() | |
| 41 return url in self._tab_list | |
| 42 | |
| 43 def CloseTab(self, debugger_url, timeout=None): | |
| 44 assert self._browser_backend.supports_tab_control | |
| 45 | |
| 46 # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms. | |
| 47 # For now, just create a new tab before closing the last tab. | |
| 48 if len(self) <= 1: | |
| 49 self.New(timeout) | |
| 50 | |
| 51 tab_id = debugger_url.split('/')[-1] | |
| 52 try: | |
| 53 response = self._browser_backend.Request('close/%s' % tab_id, | |
| 54 timeout=timeout) | |
| 55 except urllib2.HTTPError: | |
| 56 raise Exception('Unable to close tab, tab id not found: %s' % tab_id) | |
| 57 assert response == 'Target is closing' | |
| 58 | |
| 59 util.WaitFor(lambda: not self._FindTabInfo(debugger_url), timeout=5) | |
| 60 | |
| 61 if debugger_url in self._tab_dict: | |
| 62 del self._tab_dict[debugger_url] | |
| 63 self._UpdateTabList() | |
| 64 | |
| 65 def ActivateTab(self, debugger_url, timeout=None): | |
| 66 assert self._browser_backend.supports_tab_control | |
| 67 | |
| 68 assert debugger_url in self._tab_dict | |
| 69 tab_id = debugger_url.split('/')[-1] | |
| 70 try: | |
| 71 response = self._browser_backend.Request('activate/%s' % tab_id, | |
| 72 timeout=timeout) | |
| 73 except urllib2.HTTPError: | |
| 74 raise Exception('Unable to activate tab, tab id not found: %s' % tab_id) | |
| 75 assert response == 'Target activated' | |
| 76 | |
| 77 def GetTabUrl(self, debugger_url): | |
| 78 tab_info = self._FindTabInfo(debugger_url) | |
| 79 # TODO(hartmanng): crbug.com/166886 (uncomment the following assert and | |
| 80 # remove the extra None check when _ListTabs is fixed): | |
| 81 # assert tab_info is not None | |
| 82 if tab_info is None: | |
| 83 return None | |
| 84 return tab_info['url'] | |
| 85 | |
| 86 def __iter__(self): | |
| 87 self._UpdateTabList() | |
| 88 return self._tab_list.__iter__() | |
| 89 | |
| 90 def __len__(self): | |
| 91 self._UpdateTabList() | |
| 92 return len(self._tab_list) | |
| 93 | |
| 94 def Get(self, index, ret): | |
| 95 """Returns self[index] if it exists, or ret if index is out of bounds. | |
| 96 """ | |
| 97 self._UpdateTabList() | |
| 98 if len(self._tab_list) <= index: | |
| 99 return ret | |
| 100 debugger_url = self._tab_list[index] | |
| 101 # Lazily get/create a Tab object. | |
| 102 tab_object = self._tab_dict.get(debugger_url) | |
| 103 if not tab_object: | |
| 104 backend = inspector_backend.InspectorBackend( | |
| 105 self._browser_backend.browser, | |
| 106 self._browser_backend, | |
| 107 debugger_url) | |
| 108 tab_object = tab.Tab(backend) | |
| 109 self._tab_dict[debugger_url] = tab_object | |
| 110 return tab_object | |
| 111 | |
| 112 def __getitem__(self, index): | |
| 113 tab_object = self.Get(index, None) | |
| 114 if tab_object is None: | |
| 115 raise IndexError('list index out of range') | |
| 116 return tab_object | |
| 117 | |
| 118 def _ListTabs(self, timeout=None): | |
| 119 try: | |
| 120 data = self._browser_backend.Request('', timeout=timeout) | |
| 121 all_contexts = json.loads(data) | |
| 122 tabs = [ctx for ctx in all_contexts | |
| 123 if not ctx['url'].startswith('chrome-extension://')] | |
| 124 return tabs | |
| 125 except (socket.error, httplib.BadStatusLine, urllib2.URLError): | |
| 126 if not self._browser_backend.IsBrowserRunning(): | |
| 127 raise exceptions.BrowserGoneException() | |
| 128 raise exceptions.BrowserConnectionGoneException() | |
| 129 | |
| 130 def _UpdateTabList(self): | |
| 131 def GetDebuggerUrl(tab_info): | |
| 132 if 'webSocketDebuggerUrl' not in tab_info: | |
| 133 return None | |
| 134 return tab_info['webSocketDebuggerUrl'] | |
| 135 new_tab_list = map(GetDebuggerUrl, self._ListTabs()) | |
| 136 self._tab_list = [t for t in self._tab_list | |
| 137 if t in self._tab_dict or t in new_tab_list] | |
| 138 self._tab_list += [t for t in new_tab_list | |
| 139 if t is not None and t not in self._tab_list] | |
| 140 | |
| 141 def _FindTabInfo(self, debugger_url): | |
| 142 for tab_info in self._ListTabs(): | |
| 143 if tab_info.get('webSocketDebuggerUrl') == debugger_url: | |
| 144 return tab_info | |
| 145 return None | |
| OLD | NEW |