1# Copyright 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 5from telemetry.internal.actions import action_runner 6from telemetry.internal.browser import web_contents 7from telemetry.internal.image_processing import video 8 9DEFAULT_TAB_TIMEOUT = 60 10 11 12class Tab(web_contents.WebContents): 13 """Represents a tab in the browser 14 15 The important parts of the Tab object are in the runtime and page objects. 16 E.g.: 17 # Navigates the tab to a given url. 18 tab.Navigate('http://www.google.com/') 19 20 # Evaluates 1+1 in the tab's JavaScript context. 21 tab.Evaluate('1+1') 22 """ 23 def __init__(self, inspector_backend, tab_list_backend, browser): 24 super(Tab, self).__init__(inspector_backend) 25 self._tab_list_backend = tab_list_backend 26 self._browser = browser 27 self._action_runner = action_runner.ActionRunner(self) 28 29 @property 30 def browser(self): 31 """The browser in which this tab resides.""" 32 return self._browser 33 34 @property 35 def action_runner(self): 36 return self._action_runner 37 38 @property 39 def url(self): 40 """Returns the URL of the tab, as reported by devtools. 41 42 Raises: 43 devtools_http.DevToolsClientConnectionError 44 """ 45 return self._inspector_backend.url 46 47 @property 48 def dom_stats(self): 49 """A dictionary populated with measured DOM statistics. 50 51 Currently this dictionary contains: 52 { 53 'document_count': integer, 54 'node_count': integer, 55 'event_listener_count': integer 56 } 57 58 Raises: 59 inspector_memory.InspectorMemoryException 60 exceptions.TimeoutException 61 exceptions.DevtoolsTargetCrashException 62 """ 63 dom_counters = self._inspector_backend.GetDOMStats( 64 timeout=DEFAULT_TAB_TIMEOUT) 65 assert (len(dom_counters) == 3 and 66 all([x in dom_counters for x in ['document_count', 'node_count', 67 'event_listener_count']])) 68 return dom_counters 69 70 def Activate(self): 71 """Brings this tab to the foreground asynchronously. 72 73 Not all browsers or browser versions support this method. 74 Be sure to check browser.supports_tab_control. 75 76 Please note: this is asynchronous. There is a delay between this call 77 and the page's documentVisibilityState becoming 'visible', and yet more 78 delay until the actual tab is visible to the user. None of these delays 79 are included in this call. 80 81 Raises: 82 devtools_http.DevToolsClientConnectionError 83 devtools_client_backend.TabNotFoundError 84 tab_list_backend.TabUnexpectedResponseException 85 """ 86 self._tab_list_backend.ActivateTab(self.id) 87 88 def Close(self): 89 """Closes this tab. 90 91 Not all browsers or browser versions support this method. 92 Be sure to check browser.supports_tab_control. 93 94 Raises: 95 devtools_http.DevToolsClientConnectionError 96 devtools_client_backend.TabNotFoundError 97 tab_list_backend.TabUnexpectedResponseException 98 exceptions.TimeoutException 99 """ 100 self._tab_list_backend.CloseTab(self.id) 101 102 @property 103 def screenshot_supported(self): 104 """True if the browser instance is capable of capturing screenshots.""" 105 return self._inspector_backend.screenshot_supported 106 107 def Screenshot(self, timeout=DEFAULT_TAB_TIMEOUT): 108 """Capture a screenshot of the tab's contents. 109 110 Returns: 111 A telemetry.core.Bitmap. 112 Raises: 113 exceptions.WebSocketDisconnected 114 exceptions.TimeoutException 115 exceptions.DevtoolsTargetCrashException 116 """ 117 return self._inspector_backend.Screenshot(timeout) 118 119 @property 120 def video_capture_supported(self): 121 """True if the browser instance is capable of capturing video.""" 122 return self.browser.platform.CanCaptureVideo() 123 124 def Highlight(self, color): 125 """Synchronously highlights entire tab contents with the given RgbaColor. 126 127 TODO(tonyg): It is possible that the z-index hack here might not work for 128 all pages. If this happens, DevTools also provides a method for this. 129 130 Raises: 131 exceptions.EvaluateException 132 exceptions.WebSocketDisconnected 133 exceptions.TimeoutException 134 exceptions.DevtoolsTargetCrashException 135 """ 136 screen_save = 'window.__telemetry_screen_%d' % int(color) 137 self.ExecuteJavaScript(""" 138 (function() { 139 var screen = document.createElement('div'); 140 screen.style.background = {{ color }}; 141 screen.style.position = 'fixed'; 142 screen.style.top = '0'; 143 screen.style.left = '0'; 144 screen.style.width = '100%'; 145 screen.style.height = '100%'; 146 screen.style.zIndex = '2147483638'; 147 document.body.appendChild(screen); 148 requestAnimationFrame(function() { 149 requestAnimationFrame(function() { 150 {{ @screen_save }} = screen; 151 }); 152 }); 153 })(); 154 """, 155 color='rgba(%d, %d, %d, %d)' % (color.r, color.g, color.b, color.a), 156 screen_save=screen_save) 157 self.WaitForJavaScriptCondition( 158 '!!{{ @screen_save }}', screen_save=screen_save, timeout=5) 159 160 def ClearHighlight(self, color): 161 """Clears a highlight of the given bitmap.RgbaColor. 162 163 Raises: 164 exceptions.EvaluateException 165 exceptions.WebSocketDisconnected 166 exceptions.TimeoutException 167 exceptions.DevtoolsTargetCrashException 168 """ 169 screen_save = 'window.__telemetry_screen_%d' % int(color) 170 self.ExecuteJavaScript(""" 171 (function() { 172 document.body.removeChild({{ @screen_save }}); 173 requestAnimationFrame(function() { 174 requestAnimationFrame(function() { 175 {{ @screen_save }} = null; 176 console.time('__ClearHighlight.video_capture_start'); 177 console.timeEnd('__ClearHighlight.video_capture_start'); 178 }); 179 }); 180 })(); 181 """, screen_save=screen_save) 182 self.WaitForJavaScriptCondition( 183 '!{{ @screen_save }}', screen_save=screen_save, timeout=5) 184 185 def StartVideoCapture(self, min_bitrate_mbps, 186 highlight_bitmap=video.HIGHLIGHT_ORANGE_FRAME): 187 """Starts capturing video of the tab's contents. 188 189 This works by flashing the entire tab contents to a arbitrary color and then 190 starting video recording. When the frames are processed, we can look for 191 that flash as the content bounds. 192 193 Args: 194 min_bitrate_mbps: The minimum caputre bitrate in MegaBits Per Second. 195 The platform is free to deliver a higher bitrate if it can do so 196 without increasing overhead. 197 198 Raises: 199 exceptions.EvaluateException 200 exceptions.WebSocketDisconnected 201 exceptions.TimeoutException 202 exceptions.DevtoolsTargetCrashException 203 ValueError: If the required |min_bitrate_mbps| can't be achieved. 204 """ 205 self.Highlight(highlight_bitmap) 206 self.browser.platform.StartVideoCapture(min_bitrate_mbps) 207 self.ClearHighlight(highlight_bitmap) 208 209 @property 210 def is_video_capture_running(self): 211 return self.browser.platform.is_video_capture_running 212 213 def StopVideoCapture(self): 214 """Stops recording video of the tab's contents. 215 216 This looks for the initial color flash in the first frame to establish the 217 tab content boundaries and then omits all frames displaying the flash. 218 219 Returns: 220 video: A video object which is a telemetry.core.Video 221 """ 222 return self.browser.platform.StopVideoCapture() 223 224 def GetCookieByName(self, name, timeout=DEFAULT_TAB_TIMEOUT): 225 """Returns the value of the cookie by the given |name|. 226 227 Raises: 228 exceptions.WebSocketDisconnected 229 exceptions.TimeoutException 230 exceptions.DevtoolsTargetCrashException 231 """ 232 return self._inspector_backend.GetCookieByName(name, timeout) 233 234 def CollectGarbage(self): 235 """Forces a garbage collection. 236 237 Raises: 238 exceptions.WebSocketDisconnected 239 exceptions.TimeoutException 240 exceptions.DevtoolsTargetCrashException 241 """ 242 self._inspector_backend.CollectGarbage() 243 244 def ClearCache(self, force): 245 """Clears the browser's networking related disk, memory and other caches. 246 247 Args: 248 force: Iff true, navigates to about:blank which destroys the previous 249 renderer, ensuring that even "live" resources in the memory cache are 250 cleared. 251 252 Raises: 253 exceptions.EvaluateException 254 exceptions.WebSocketDisconnected 255 exceptions.TimeoutException 256 exceptions.DevtoolsTargetCrashException 257 errors.DeviceUnresponsiveError 258 """ 259 self.browser.platform.FlushDnsCache() 260 self.ExecuteJavaScript(""" 261 if (window.chrome && chrome.benchmarking && 262 chrome.benchmarking.clearCache) { 263 chrome.benchmarking.clearCache(); 264 chrome.benchmarking.clearPredictorCache(); 265 chrome.benchmarking.clearHostResolverCache(); 266 } 267 """) 268 if force: 269 self.Navigate('about:blank') 270