OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import urllib2 | 5 import urllib2 |
6 import httplib | 6 import httplib |
7 import socket | 7 import socket |
8 import json | 8 import json |
9 import re | 9 import re |
10 import sys | 10 import sys |
11 import weakref | 11 import weakref |
12 | 12 |
13 from telemetry import browser_gone_exception | 13 from telemetry import browser_gone_exception |
| 14 from telemetry import extension_page |
14 from telemetry import tab | 15 from telemetry import tab |
15 from telemetry import tracing_backend | 16 from telemetry import tracing_backend |
16 from telemetry import user_agent | 17 from telemetry import user_agent |
17 from telemetry import util | 18 from telemetry import util |
18 from telemetry import wpr_modes | 19 from telemetry import wpr_modes |
19 from telemetry import wpr_server | 20 from telemetry import wpr_server |
20 | 21 |
21 | 22 |
22 class BrowserConnectionGoneException( | 23 class BrowserConnectionGoneException( |
23 browser_gone_exception.BrowserGoneException): | 24 browser_gone_exception.BrowserGoneException): |
24 pass | 25 pass |
25 | 26 |
26 | 27 class ContextControler(object): |
27 class TabController(object): | |
28 def __init__(self, browser, browser_backend): | 28 def __init__(self, browser, browser_backend): |
29 self._browser = browser | 29 self._browser = browser |
30 self._browser_backend = browser_backend | 30 self._browser_backend = browser_backend |
31 | |
32 # Stores web socket debugger URLs in iteration order. | 31 # Stores web socket debugger URLs in iteration order. |
33 self._tab_list = [] | 32 self._context_list = [] |
34 # Maps debugger URLs to Tab objects. | 33 # Maps debugger URLs to Tab objects. |
35 self._tab_dict = weakref.WeakValueDictionary() | 34 self._context_dict = weakref.WeakValueDictionary() |
36 | 35 self._UpdateContextList() |
37 self._UpdateTabList() | |
38 | |
39 def New(self, timeout=None): | |
40 self._browser_backend.Request('new', timeout=timeout) | |
41 return self[-1] | |
42 | 36 |
43 def DoesDebuggerUrlExist(self, url): | 37 def DoesDebuggerUrlExist(self, url): |
44 self._UpdateTabList() | 38 self._UpdateContextList() |
45 return url in self._tab_list | 39 return url in self._context_list |
46 | 40 |
47 def CloseTab(self, debugger_url, timeout=None): | 41 def CloseTab(self, debugger_url, timeout=None): |
48 # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms. | 42 # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms. |
49 # For now, just create a new tab before closing the last tab. | 43 # For now, just create a new tab before closing the last tab. |
50 if len(self) <= 1: | 44 if len(self) <= 1: |
51 self.New() | 45 self.New() |
52 | 46 |
53 tab_id = debugger_url.split('/')[-1] | 47 tab_id = debugger_url.split('/')[-1] |
54 try: | 48 try: |
55 response = self._browser_backend.Request('close/%s' % tab_id, | 49 response = self._browser_backend.Request('close/%s' % tab_id, |
(...skipping 18 matching lines...) Expand all Loading... |
74 def GetTabUrl(self, debugger_url): | 68 def GetTabUrl(self, debugger_url): |
75 tab_info = self._FindTabInfo(debugger_url) | 69 tab_info = self._FindTabInfo(debugger_url) |
76 # TODO(hartmanng): crbug.com/166886 (uncomment the following assert and | 70 # TODO(hartmanng): crbug.com/166886 (uncomment the following assert and |
77 # remove the extra None check when _ListTabs is fixed): | 71 # remove the extra None check when _ListTabs is fixed): |
78 # assert tab_info is not None | 72 # assert tab_info is not None |
79 if tab_info is None: | 73 if tab_info is None: |
80 return None | 74 return None |
81 return tab_info['url'] | 75 return tab_info['url'] |
82 | 76 |
83 def __iter__(self): | 77 def __iter__(self): |
84 self._UpdateTabList() | 78 self._UpdateContextList() |
85 return self._tab_list.__iter__() | 79 return self._context_list.__iter__() |
86 | 80 |
87 def __len__(self): | 81 def __len__(self): |
88 self._UpdateTabList() | 82 self._UpdateContextList() |
89 return len(self._tab_list) | 83 return len(self._context_list) |
90 | 84 |
91 def __getitem__(self, index): | 85 def __getitem__(self, index): |
92 self._UpdateTabList() | 86 self._UpdateContextList() |
93 # This dereference will propagate IndexErrors. | 87 # This dereference will propagate IndexErrors. |
94 debugger_url = self._tab_list[index] | 88 debugger_url = self._context_list[index] |
| 89 return self._GetContextObjectForDebugger(debugger_url) |
| 90 |
| 91 def FindByUrl(self, url): |
| 92 for tab_info in self._ListContexts(): |
| 93 if tab_info['url'] == url: |
| 94 return self._GetContextObjectForDebugger( |
| 95 tab_info.get('webSocketDebuggerUrl')) |
| 96 return None |
| 97 |
| 98 def GetAllForUrl(self, url): |
| 99 return [self._GetContextObjectForDebugger( |
| 100 tab_info.get('webSocketDebuggerUrl')) |
| 101 for tab_info in self._ListContexts() |
| 102 if tab_info['url'] == url] |
| 103 |
| 104 def _GetContextObjectForDebugger(self, debugger_url): |
95 # Lazily get/create a Tab object. | 105 # Lazily get/create a Tab object. |
96 tab_object = self._tab_dict.get(debugger_url) | 106 tab_object = self._context_dict.get(debugger_url) |
97 if not tab_object: | 107 if not tab_object: |
98 tab_object = tab.Tab(self._browser, self, debugger_url) | 108 tab_object = self._CreateContextObject(debugger_url) |
99 self._tab_dict[debugger_url] = tab_object | 109 self._context_dict[debugger_url] = tab_object |
100 return tab_object | 110 return tab_object |
101 | 111 |
102 def _ListTabs(self, timeout=None): | 112 def _ListContexts(self, timeout=None): |
103 try: | 113 try: |
104 data = self._browser_backend.Request('', timeout=timeout) | 114 data = self._browser_backend.Request('', timeout=timeout) |
105 all_contexts = json.loads(data) | 115 all_contexts = json.loads(data) |
106 tabs = [ctx for ctx in all_contexts | 116 contexts = self._FilterContexts(all_contexts) |
107 if not ctx['url'].startswith('chrome-extension://')] | 117 return contexts |
108 return tabs | |
109 except (socket.error, httplib.BadStatusLine, urllib2.URLError): | 118 except (socket.error, httplib.BadStatusLine, urllib2.URLError): |
110 if not self._browser_backend.IsBrowserRunning(): | 119 if not self._browser_backend.IsBrowserRunning(): |
111 raise browser_gone_exception.BrowserGoneException() | 120 raise browser_gone_exception.BrowserGoneException() |
112 raise BrowserConnectionGoneException() | 121 raise BrowserConnectionGoneException() |
113 | 122 |
114 def _UpdateTabList(self): | 123 def _UpdateContextList(self): |
115 def GetDebuggerUrl(tab_info): | 124 def GetDebuggerUrl(tab_info): |
116 if 'webSocketDebuggerUrl' not in tab_info: | 125 if 'webSocketDebuggerUrl' not in tab_info: |
117 return None | 126 return None |
118 return tab_info['webSocketDebuggerUrl'] | 127 return tab_info['webSocketDebuggerUrl'] |
119 new_tab_list = map(GetDebuggerUrl, self._ListTabs()) | 128 new_context_list = map(GetDebuggerUrl, self._ListContexts()) |
120 self._tab_list = [t for t in self._tab_list if t in new_tab_list] | 129 self._context_list = [t for t in self._context_list |
121 self._tab_list += [t for t in new_tab_list if t not in self._tab_list] | 130 if t in new_context_list] |
| 131 self._context_list += [t for t in new_context_list |
| 132 if t not in self._context_list] |
122 | 133 |
123 def _FindTabInfo(self, debugger_url): | 134 def _FindContextInfo(self, debugger_url): |
124 for tab_info in self._ListTabs(): | 135 for tab_info in self._ListContexts(): |
125 if tab_info.get('webSocketDebuggerUrl') == debugger_url: | 136 if tab_info.get('webSocketDebuggerUrl') == debugger_url: |
126 return tab_info | 137 return tab_info |
127 return None | 138 return None |
128 | 139 |
129 | 140 |
| 141 class TabController(ContextControler): |
| 142 def __init__(self, browser, browser_backend): |
| 143 super(TabController, self).__init__(browser, browser_backend) |
| 144 |
| 145 def _CreateContextObject(self, debugger_url): |
| 146 return tab.Tab(self._browser, self, debugger_url) |
| 147 |
| 148 def _FilterContexts(self, all_contexts): |
| 149 return [ctx for ctx in all_contexts |
| 150 if not ctx['url'].startswith('chrome-extension://')] |
| 151 |
| 152 def New(self, timeout=None): |
| 153 self._browser_backend.Request('new', timeout=timeout) |
| 154 return self[-1] |
| 155 |
| 156 def CloseTab(self, debugger_url, timeout=None): |
| 157 # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms. |
| 158 # For now, just create a new tab before closing the last tab. |
| 159 if len(self) <= 1: |
| 160 self.New() |
| 161 |
| 162 tab_id = debugger_url.split('/')[-1] |
| 163 try: |
| 164 response = self._browser_backend.Request('close/%s' % tab_id, |
| 165 timeout=timeout) |
| 166 except urllib2.HTTPError: |
| 167 raise Exception('Unable to close tab, tab id not found: %s' % tab_id) |
| 168 assert response == 'Target is closing' |
| 169 |
| 170 util.WaitFor(lambda: not self._FindContextInfo(debugger_url), timeout=5) |
| 171 self._UpdateContextList() |
| 172 |
| 173 def GetTabUrl(self, debugger_url): |
| 174 tab_info = self._FindContextInfo(debugger_url) |
| 175 assert tab_info is not None |
| 176 return tab_info['url'] |
| 177 |
| 178 class ExtensionController(ContextControler): |
| 179 def __init__(self, browser, browser_backend): |
| 180 super(ExtensionController, self).__init__(browser, browser_backend) |
| 181 |
| 182 def _CreateContextObject(self, debugger_url): |
| 183 return extension_page.ExtensionPage( |
| 184 tab.Tab(self._browser, self, debugger_url)) |
| 185 |
| 186 def _FilterContexts(self, all_contexts): |
| 187 return [ctx for ctx in all_contexts |
| 188 if ctx['url'].startswith('chrome-extension://')] |
| 189 |
130 class BrowserBackend(object): | 190 class BrowserBackend(object): |
131 """A base class for browser backends. Provides basic functionality | 191 """A base class for browser backends. Provides basic functionality |
132 once a remote-debugger port has been established.""" | 192 once a remote-debugger port has been established.""" |
133 | 193 |
134 WEBPAGEREPLAY_HOST = '127.0.0.1' | 194 WEBPAGEREPLAY_HOST = '127.0.0.1' |
135 WEBPAGEREPLAY_HTTP_PORT = 8080 | 195 WEBPAGEREPLAY_HTTP_PORT = 8080 |
136 WEBPAGEREPLAY_HTTPS_PORT = 8413 | 196 WEBPAGEREPLAY_HTTPS_PORT = 8413 |
137 | 197 |
138 def __init__(self, is_content_shell, options): | 198 def __init__(self, is_content_shell, options): |
139 self.tabs = None | 199 self.tabs = None |
| 200 self.extensions = None |
140 self.browser_type = options.browser_type | 201 self.browser_type = options.browser_type |
141 self.is_content_shell = is_content_shell | 202 self.is_content_shell = is_content_shell |
142 self.options = options | 203 self.options = options |
143 self._port = None | 204 self._port = None |
144 | 205 |
145 self._inspector_protocol_version = 0 | 206 self._inspector_protocol_version = 0 |
146 self._chrome_branch_number = 0 | 207 self._chrome_branch_number = 0 |
147 self._webkit_base_revision = 0 | 208 self._webkit_base_revision = 0 |
148 self._tracing_backend = None | 209 self._tracing_backend = None |
149 | 210 |
150 if options.dont_override_profile: | 211 if options.dont_override_profile: |
151 sys.stderr.write('Warning: Not overriding profile. This can cause ' | 212 sys.stderr.write('Warning: Not overriding profile. This can cause ' |
152 'unexpected effects due to profile-specific settings, ' | 213 'unexpected effects due to profile-specific settings, ' |
153 'such as about:flags settings, cookies, and ' | 214 'such as about:flags settings, cookies, and ' |
154 'extensions.\n') | 215 'extensions.\n') |
155 | 216 |
156 def SetBrowser(self, browser): | 217 def SetBrowser(self, browser): |
157 self.tabs = TabController(browser, self) | 218 self.tabs = TabController(browser, self) |
| 219 if not self.is_content_shell: |
| 220 self.extensions = ExtensionController(browser, self) |
158 | 221 |
159 def GetBrowserStartupArgs(self): | 222 def GetBrowserStartupArgs(self): |
160 args = [] | 223 args = [] |
161 args.extend(self.options.extra_browser_args) | 224 args.extend(self.options.extra_browser_args) |
162 args.append('--disable-background-networking') | 225 args.append('--disable-background-networking') |
163 args.append('--metrics-recording-only') | 226 args.append('--metrics-recording-only') |
164 args.append('--no-first-run') | 227 args.append('--no-first-run') |
165 if self.options.wpr_mode != wpr_modes.WPR_OFF: | 228 if self.options.wpr_mode != wpr_modes.WPR_OFF: |
166 args.extend(wpr_server.GetChromeFlags(self.WEBPAGEREPLAY_HOST, | 229 args.extend(wpr_server.GetChromeFlags(self.WEBPAGEREPLAY_HOST, |
167 self.WEBPAGEREPLAY_HTTP_PORT, | 230 self.WEBPAGEREPLAY_HTTP_PORT, |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 self._tracing_backend = None | 304 self._tracing_backend = None |
242 | 305 |
243 def CreateForwarder(self, *port_pairs): | 306 def CreateForwarder(self, *port_pairs): |
244 raise NotImplementedError() | 307 raise NotImplementedError() |
245 | 308 |
246 def IsBrowserRunning(self): | 309 def IsBrowserRunning(self): |
247 raise NotImplementedError() | 310 raise NotImplementedError() |
248 | 311 |
249 def GetStandardOutput(self): | 312 def GetStandardOutput(self): |
250 raise NotImplementedError() | 313 raise NotImplementedError() |
OLD | NEW |