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 5import os 6 7from telemetry.core import exceptions 8 9from py_trace_event import trace_event 10 11DEFAULT_WEB_CONTENTS_TIMEOUT = 90 12 13# TODO(achuith, dtu, nduca): Add unit tests specifically for WebContents, 14# independent of Tab. 15class WebContents(object): 16 17 __metaclass__ = trace_event.TracedMetaClass 18 19 """Represents web contents in the browser""" 20 def __init__(self, inspector_backend): 21 self._inspector_backend = inspector_backend 22 23 with open(os.path.join(os.path.dirname(__file__), 24 'network_quiescence.js')) as f: 25 self._quiescence_js = f.read() 26 27 with open(os.path.join(os.path.dirname(__file__), 28 'wait_for_frame.js')) as f: 29 self._wait_for_frame_js = f.read() 30 31 # An incrementing ID used to query frame timing javascript. Using a new id 32 # with each request ensures that previously timed-out wait for frame 33 # requests don't impact new requests. 34 self._wait_for_frame_id = 0 35 36 @property 37 def id(self): 38 """Return the unique id string for this tab object.""" 39 return self._inspector_backend.id 40 41 def GetUrl(self): 42 """Returns the URL to which the WebContents is connected. 43 44 Raises: 45 exceptions.Error: If there is an error in inspector backend connection. 46 """ 47 return self._inspector_backend.url 48 49 def GetWebviewContexts(self): 50 """Returns a list of webview contexts within the current inspector backend. 51 52 Returns: 53 A list of WebContents objects representing the webview contexts. 54 55 Raises: 56 exceptions.Error: If there is an error in inspector backend connection. 57 """ 58 webviews = [] 59 inspector_backends = self._inspector_backend.GetWebviewInspectorBackends() 60 for inspector_backend in inspector_backends: 61 webviews.append(WebContents(inspector_backend)) 62 return webviews 63 64 def WaitForDocumentReadyStateToBeComplete(self, 65 timeout=DEFAULT_WEB_CONTENTS_TIMEOUT): 66 """Waits for the document to finish loading. 67 68 Raises: 69 exceptions.Error: See WaitForJavaScriptCondition() for a detailed list 70 of possible exceptions. 71 """ 72 73 self.WaitForJavaScriptCondition( 74 'document.readyState == "complete"', timeout=timeout) 75 76 def WaitForDocumentReadyStateToBeInteractiveOrBetter(self, 77 timeout=DEFAULT_WEB_CONTENTS_TIMEOUT): 78 """Waits for the document to be interactive. 79 80 Raises: 81 exceptions.Error: See WaitForJavaScriptCondition() for a detailed list 82 of possible exceptions. 83 """ 84 self.WaitForJavaScriptCondition( 85 'document.readyState == "interactive" || ' 86 'document.readyState == "complete"', timeout=timeout) 87 88 def WaitForFrameToBeDisplayed(self, 89 timeout=DEFAULT_WEB_CONTENTS_TIMEOUT): 90 """Waits for a frame to be displayed before returning. 91 92 Raises: 93 exceptions.Error: See WaitForJavaScriptCondition() for a detailed list 94 of possible exceptions. 95 """ 96 # Generate a new id for each call of this function to ensure that we track 97 # each request to wait seperately. 98 self._wait_for_frame_id += 1 99 self.WaitForJavaScriptCondition( 100 '{{ @script }}; window.__telemetry_testHasFramePassed({{ frame_id }})', 101 script=self._wait_for_frame_js, 102 frame_id=str(self._wait_for_frame_id), # Place id as a str. 103 timeout=timeout) 104 105 def HasReachedQuiescence(self): 106 """Determine whether the page has reached quiescence after loading. 107 108 Returns: 109 True if 2 seconds have passed since last resource received, false 110 otherwise. 111 Raises: 112 exceptions.Error: See EvaluateJavaScript() for a detailed list of 113 possible exceptions. 114 """ 115 # Inclusion of the script that provides 116 # window.__telemetry_testHasReachedNetworkQuiescence() 117 # is idempotent, it's run on every call because WebContents doesn't track 118 # page loads and we need to execute anew for every newly loaded page. 119 return self.EvaluateJavaScript( 120 '{{ @script }}; window.__telemetry_testHasReachedNetworkQuiescence()', 121 script=self._quiescence_js) 122 123 def ExecuteJavaScript(self, *args, **kwargs): 124 """Executes a given JavaScript statement. Does not return the result. 125 126 Example: runner.ExecuteJavaScript('var foo = {{ value }};', value='hi'); 127 128 Args: 129 statement: The statement to execute (provided as a string). 130 131 Optional keyword args: 132 timeout: The number of seconds to wait for the statement to execute. 133 context_id: The id of an iframe where to execute the code; the main page 134 has context_id=1, the first iframe context_id=2, etc. 135 Additional keyword arguments provide values to be interpolated within 136 the statement. See telemetry.util.js_template for details. 137 138 Raises: 139 py_utils.TimeoutException 140 exceptions.EvaluationException 141 exceptions.WebSocketException 142 exceptions.DevtoolsTargetCrashException 143 """ 144 return self._inspector_backend.ExecuteJavaScript(*args, **kwargs) 145 146 def EvaluateJavaScript(self, *args, **kwargs): 147 """Returns the result of evaluating a given JavaScript expression. 148 149 Example: runner.ExecuteJavaScript('document.location.href'); 150 151 Args: 152 expression: The expression to execute (provided as a string). 153 154 Optional keyword args: 155 timeout: The number of seconds to wait for the expression to evaluate. 156 context_id: The id of an iframe where to execute the code; the main page 157 has context_id=1, the first iframe context_id=2, etc. 158 Additional keyword arguments provide values to be interpolated within 159 the expression. See telemetry.util.js_template for details. 160 161 Raises: 162 py_utils.TimeoutException 163 exceptions.EvaluationException 164 exceptions.WebSocketException 165 exceptions.DevtoolsTargetCrashException 166 """ 167 return self._inspector_backend.EvaluateJavaScript(*args, **kwargs) 168 169 def WaitForJavaScriptCondition(self, *args, **kwargs): 170 """Wait for a JavaScript condition to become true. 171 172 Example: runner.WaitForJavaScriptCondition('window.foo == 10'); 173 174 Args: 175 condition: The JavaScript condition (provided as string). 176 177 Optional keyword args: 178 timeout: The number in seconds to wait for the condition to become 179 True (default to 60). 180 context_id: The id of an iframe where to execute the code; the main page 181 has context_id=1, the first iframe context_id=2, etc. 182 Additional keyword arguments provide values to be interpolated within 183 the expression. See telemetry.util.js_template for details. 184 185 Raises: 186 py_utils.TimeoutException 187 exceptions.EvaluationException 188 exceptions.WebSocketException 189 exceptions.DevtoolsTargetCrashException 190 """ 191 return self._inspector_backend.WaitForJavaScriptCondition(*args, **kwargs) 192 193 def EnableAllContexts(self): 194 """Enable all contexts in a page. Returns the number of available contexts. 195 196 Raises: 197 exceptions.WebSocketDisconnected 198 py_utils.TimeoutException 199 exceptions.DevtoolsTargetCrashException 200 """ 201 return self._inspector_backend.EnableAllContexts() 202 203 def WaitForNavigate(self, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT): 204 """Waits for the navigation to complete. 205 206 The current page is expect to be in a navigation. 207 This function returns when the navigation is complete or when 208 the timeout has been exceeded. 209 210 Raises: 211 py_utils.TimeoutException 212 exceptions.DevtoolsTargetCrashException 213 """ 214 self._inspector_backend.WaitForNavigate(timeout) 215 216 def Navigate(self, url, script_to_evaluate_on_commit=None, 217 timeout=DEFAULT_WEB_CONTENTS_TIMEOUT): 218 """Navigates to url. 219 220 If |script_to_evaluate_on_commit| is given, the script source string will be 221 evaluated when the navigation is committed. This is after the context of 222 the page exists, but before any script on the page itself has executed. 223 224 Raises: 225 py_utils.TimeoutException 226 exceptions.DevtoolsTargetCrashException 227 """ 228 self._inspector_backend.Navigate(url, script_to_evaluate_on_commit, timeout) 229 230 def IsAlive(self): 231 """Whether the WebContents is still operating normally. 232 233 Since WebContents function asynchronously, this method does not guarantee 234 that the WebContents will still be alive at any point in the future. 235 236 Returns: 237 A boolean indicating whether the WebContents is opearting normally. 238 """ 239 return self._inspector_backend.IsInspectable() 240 241 def CloseConnections(self): 242 """Closes all TCP sockets held open by the browser. 243 244 Raises: 245 exceptions.DevtoolsTargetCrashException if the tab is not alive. 246 """ 247 if not self.IsAlive(): 248 raise exceptions.DevtoolsTargetCrashException 249 self.ExecuteJavaScript('window.chrome && chrome.benchmarking &&' 250 'chrome.benchmarking.closeConnections()') 251 252 def SynthesizeScrollGesture(self, x=100, y=800, xDistance=0, yDistance=-500, 253 xOverscroll=None, yOverscroll=None, 254 preventFling=None, speed=None, 255 gestureSourceType=None, repeatCount=None, 256 repeatDelayMs=None, interactionMarkerName=None, 257 timeout=60): 258 """Runs an inspector command that causes a repeatable browser driven scroll. 259 260 Args: 261 x: X coordinate of the start of the gesture in CSS pixels. 262 y: Y coordinate of the start of the gesture in CSS pixels. 263 xDistance: Distance to scroll along the X axis (positive to scroll left). 264 yDistance: Ddistance to scroll along the Y axis (positive to scroll up). 265 xOverscroll: Number of additional pixels to scroll back along the X axis. 266 xOverscroll: Number of additional pixels to scroll back along the Y axis. 267 preventFling: Prevents a fling gesture. 268 speed: Swipe speed in pixels per second. 269 gestureSourceType: Which type of input events to be generated. 270 repeatCount: Number of additional repeats beyond the first scroll. 271 repeatDelayMs: Number of milliseconds delay between each repeat. 272 interactionMarkerName: The name of the interaction markers to generate. 273 274 Raises: 275 py_utils.TimeoutException 276 exceptions.DevtoolsTargetCrashException 277 """ 278 return self._inspector_backend.SynthesizeScrollGesture( 279 x=x, y=y, xDistance=xDistance, yDistance=yDistance, 280 xOverscroll=xOverscroll, yOverscroll=yOverscroll, 281 preventFling=preventFling, speed=speed, 282 gestureSourceType=gestureSourceType, repeatCount=repeatCount, 283 repeatDelayMs=repeatDelayMs, 284 interactionMarkerName=interactionMarkerName, 285 timeout=timeout) 286 287 def DispatchKeyEvent(self, keyEventType='char', modifiers=None, 288 timestamp=None, text=None, unmodifiedText=None, 289 keyIdentifier=None, domCode=None, domKey=None, 290 windowsVirtualKeyCode=None, nativeVirtualKeyCode=None, 291 autoRepeat=None, isKeypad=None, isSystemKey=None, 292 timeout=60): 293 """Dispatches a key event to the page. 294 295 Args: 296 type: Type of the key event. Allowed values: 'keyDown', 'keyUp', 297 'rawKeyDown', 'char'. 298 modifiers: Bit field representing pressed modifier keys. Alt=1, Ctrl=2, 299 Meta/Command=4, Shift=8 (default: 0). 300 timestamp: Time at which the event occurred. Measured in UTC time in 301 seconds since January 1, 1970 (default: current time). 302 text: Text as generated by processing a virtual key code with a keyboard 303 layout. Not needed for for keyUp and rawKeyDown events (default: ''). 304 unmodifiedText: Text that would have been generated by the keyboard if no 305 modifiers were pressed (except for shift). Useful for shortcut 306 (accelerator) key handling (default: ""). 307 keyIdentifier: Unique key identifier (e.g., 'U+0041') (default: ''). 308 windowsVirtualKeyCode: Windows virtual key code (default: 0). 309 nativeVirtualKeyCode: Native virtual key code (default: 0). 310 autoRepeat: Whether the event was generated from auto repeat (default: 311 False). 312 isKeypad: Whether the event was generated from the keypad (default: 313 False). 314 isSystemKey: Whether the event was a system key event (default: False). 315 316 Raises: 317 py_utils.TimeoutException 318 exceptions.DevtoolsTargetCrashException 319 """ 320 return self._inspector_backend.DispatchKeyEvent( 321 keyEventType=keyEventType, modifiers=modifiers, timestamp=timestamp, 322 text=text, unmodifiedText=unmodifiedText, keyIdentifier=keyIdentifier, 323 domCode=domCode, domKey=domKey, 324 windowsVirtualKeyCode=windowsVirtualKeyCode, 325 nativeVirtualKeyCode=nativeVirtualKeyCode, autoRepeat=autoRepeat, 326 isKeypad=isKeypad, isSystemKey=isSystemKey, timeout=timeout) 327