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 import browser_gone_exception | |
11 from telemetry import tab | |
12 from telemetry import inspector_backend | |
13 from telemetry import util | |
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 browser_gone_exception.BrowserGoneException() | |
128 raise browser_gone_exception.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 |