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