Chromium Code Reviews| 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): |
|
nduca
2013/01/15 05:31:06
I think we should leave TabController alone and cr
| |
| 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): | |
|
nduca
2013/01/15 05:31:06
I'd like to avoid this.
| |
| 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 | |
|
nduca
2013/01/15 05:31:06
Should ExtensionToLoad() should have an id? The us
| |
| 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 |