1# Copyright 2014 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 logging 6import time 7import urlparse 8 9from telemetry.internal.actions.drag import DragAction 10from telemetry.internal.actions.javascript_click import ClickElementAction 11from telemetry.internal.actions.load_media import LoadMediaAction 12from telemetry.internal.actions.loop import LoopAction 13from telemetry.internal.actions.mouse_click import MouseClickAction 14from telemetry.internal.actions.navigate import NavigateAction 15from telemetry.internal.actions.page_action import GESTURE_SOURCE_DEFAULT 16from telemetry.internal.actions.page_action import SUPPORTED_GESTURE_SOURCES 17from telemetry.internal.actions.pinch import PinchAction 18from telemetry.internal.actions.play import PlayAction 19from telemetry.internal.actions.repaint_continuously import ( 20 RepaintContinuouslyAction) 21from telemetry.internal.actions.repeatable_scroll import RepeatableScrollAction 22from telemetry.internal.actions.scroll import ScrollAction 23from telemetry.internal.actions.scroll_bounce import ScrollBounceAction 24from telemetry.internal.actions.seek import SeekAction 25from telemetry.internal.actions.swipe import SwipeAction 26from telemetry.internal.actions.tap import TapAction 27from telemetry.internal.actions.wait import WaitForElementAction 28from telemetry.web_perf import timeline_interaction_record 29 30 31class ActionRunner(object): 32 33 def __init__(self, tab, skip_waits=False): 34 self._tab = tab 35 self._skip_waits = skip_waits 36 37 @property 38 def tab(self): 39 """Returns the tab on which actions are performed.""" 40 return self._tab 41 42 def _RunAction(self, action): 43 action.WillRunAction(self._tab) 44 action.RunAction(self._tab) 45 46 def CreateInteraction(self, label, repeatable=False): 47 """ Create an action.Interaction object that issues interaction record. 48 49 An interaction record is a labeled time period containing 50 interaction that developers care about. Each set of metrics 51 specified in flags will be calculated for this time period. 52 53 To mark the start of interaction record, call Begin() method on the returned 54 object. To mark the finish of interaction record, call End() method on 55 it. Or better yet, use the with statement to create an 56 interaction record that covers the actions in the with block. 57 58 e.g: 59 with action_runner.CreateInteraction('Animation-1'): 60 action_runner.TapElement(...) 61 action_runner.WaitForJavaScriptCondition(...) 62 63 Args: 64 label: A label for this particular interaction. This can be any 65 user-defined string, but must not contain '/'. 66 repeatable: Whether other interactions may use the same logical name 67 as this interaction. All interactions with the same logical name must 68 have the same flags. 69 70 Returns: 71 An instance of action_runner.Interaction 72 """ 73 flags = [] 74 if repeatable: 75 flags.append(timeline_interaction_record.REPEATABLE) 76 77 return Interaction(self._tab, label, flags) 78 79 def CreateGestureInteraction(self, label, repeatable=False): 80 """ Create an action.Interaction object that issues gesture-based 81 interaction record. 82 83 This is similar to normal interaction record, but it will 84 auto-narrow the interaction time period to only include the 85 synthetic gesture event output by Chrome. This is typically use to 86 reduce noise in gesture-based analysis (e.g., analysis for a 87 swipe/scroll). 88 89 The interaction record label will be prepended with 'Gesture_'. 90 91 e.g: 92 with action_runner.CreateGestureInteraction('Scroll-1'): 93 action_runner.ScrollPage() 94 95 Args: 96 label: A label for this particular interaction. This can be any 97 user-defined string, but must not contain '/'. 98 repeatable: Whether other interactions may use the same logical name 99 as this interaction. All interactions with the same logical name must 100 have the same flags. 101 102 Returns: 103 An instance of action_runner.Interaction 104 """ 105 return self.CreateInteraction('Gesture_' + label, repeatable) 106 107 def Navigate(self, url, script_to_evaluate_on_commit=None, 108 timeout_in_seconds=60): 109 """Navigates to |url|. 110 111 If |script_to_evaluate_on_commit| is given, the script source string will be 112 evaluated when the navigation is committed. This is after the context of 113 the page exists, but before any script on the page itself has executed. 114 """ 115 if urlparse.urlparse(url).scheme == 'file': 116 url = self._tab.browser.platform.http_server.UrlOf(url[7:]) 117 118 self._RunAction(NavigateAction( 119 url=url, 120 script_to_evaluate_on_commit=script_to_evaluate_on_commit, 121 timeout_in_seconds=timeout_in_seconds)) 122 123 def WaitForNavigate(self, timeout_in_seconds_seconds=60): 124 start_time = time.time() 125 self._tab.WaitForNavigate(timeout_in_seconds_seconds) 126 127 time_left_in_seconds = (start_time + timeout_in_seconds_seconds 128 - time.time()) 129 time_left_in_seconds = max(0, time_left_in_seconds) 130 self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter( 131 time_left_in_seconds) 132 133 def ReloadPage(self): 134 """Reloads the page.""" 135 self._tab.ExecuteJavaScript('window.location.reload()') 136 self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() 137 138 def ExecuteJavaScript(self, statement): 139 """Executes a given JavaScript expression. Does not return the result. 140 141 Example: runner.ExecuteJavaScript('var foo = 1;'); 142 143 Args: 144 statement: The statement to execute (provided as string). 145 146 Raises: 147 EvaluationException: The statement failed to execute. 148 """ 149 self._tab.ExecuteJavaScript(statement) 150 151 def EvaluateJavaScript(self, expression): 152 """Returns the evaluation result of the given JavaScript expression. 153 154 The evaluation results must be convertible to JSON. If the result 155 is not needed, use ExecuteJavaScript instead. 156 157 Example: num = runner.EvaluateJavaScript('document.location.href') 158 159 Args: 160 expression: The expression to evaluate (provided as string). 161 162 Raises: 163 EvaluationException: The statement expression failed to execute 164 or the evaluation result can not be JSON-ized. 165 """ 166 return self._tab.EvaluateJavaScript(expression) 167 168 def Wait(self, seconds): 169 """Wait for the number of seconds specified. 170 171 Args: 172 seconds: The number of seconds to wait. 173 """ 174 if not self._skip_waits: 175 time.sleep(seconds) 176 177 def WaitForJavaScriptCondition(self, condition, timeout_in_seconds=60): 178 """Wait for a JavaScript condition to become true. 179 180 Example: runner.WaitForJavaScriptCondition('window.foo == 10'); 181 182 Args: 183 condition: The JavaScript condition (as string). 184 timeout_in_seconds: The timeout in seconds (default to 60). 185 """ 186 self._tab.WaitForJavaScriptExpression(condition, timeout_in_seconds) 187 188 def WaitForElement(self, selector=None, text=None, element_function=None, 189 timeout_in_seconds=60): 190 """Wait for an element to appear in the document. 191 192 The element may be selected via selector, text, or element_function. 193 Only one of these arguments must be specified. 194 195 Args: 196 selector: A CSS selector describing the element. 197 text: The element must contains this exact text. 198 element_function: A JavaScript function (as string) that is used 199 to retrieve the element. For example: 200 '(function() { return foo.element; })()'. 201 timeout_in_seconds: The timeout in seconds (default to 60). 202 """ 203 self._RunAction(WaitForElementAction( 204 selector=selector, text=text, element_function=element_function, 205 timeout_in_seconds=timeout_in_seconds)) 206 207 def TapElement(self, selector=None, text=None, element_function=None): 208 """Tap an element. 209 210 The element may be selected via selector, text, or element_function. 211 Only one of these arguments must be specified. 212 213 Args: 214 selector: A CSS selector describing the element. 215 text: The element must contains this exact text. 216 element_function: A JavaScript function (as string) that is used 217 to retrieve the element. For example: 218 '(function() { return foo.element; })()'. 219 """ 220 self._RunAction(TapAction( 221 selector=selector, text=text, element_function=element_function)) 222 223 def ClickElement(self, selector=None, text=None, element_function=None): 224 """Click an element. 225 226 The element may be selected via selector, text, or element_function. 227 Only one of these arguments must be specified. 228 229 Args: 230 selector: A CSS selector describing the element. 231 text: The element must contains this exact text. 232 element_function: A JavaScript function (as string) that is used 233 to retrieve the element. For example: 234 '(function() { return foo.element; })()'. 235 """ 236 self._RunAction(ClickElementAction( 237 selector=selector, text=text, element_function=element_function)) 238 239 def DragPage(self, left_start_ratio, top_start_ratio, left_end_ratio, 240 top_end_ratio, speed_in_pixels_per_second=800, use_touch=False, 241 selector=None, text=None, element_function=None): 242 """Perform a drag gesture on the page. 243 244 You should specify a start and an end point in ratios of page width and 245 height (see drag.js for full implementation). 246 247 Args: 248 left_start_ratio: The horizontal starting coordinate of the 249 gesture, as a ratio of the visible bounding rectangle for 250 document.body. 251 top_start_ratio: The vertical starting coordinate of the 252 gesture, as a ratio of the visible bounding rectangle for 253 document.body. 254 left_end_ratio: The horizontal ending coordinate of the 255 gesture, as a ratio of the visible bounding rectangle for 256 document.body. 257 top_end_ratio: The vertical ending coordinate of the 258 gesture, as a ratio of the visible bounding rectangle for 259 document.body. 260 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 261 use_touch: Whether dragging should be done with touch input. 262 """ 263 self._RunAction(DragAction( 264 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 265 left_end_ratio=left_end_ratio, top_end_ratio=top_end_ratio, 266 speed_in_pixels_per_second=speed_in_pixels_per_second, 267 use_touch=use_touch, selector=selector, text=text, 268 element_function=element_function)) 269 270 def PinchPage(self, left_anchor_ratio=0.5, top_anchor_ratio=0.5, 271 scale_factor=None, speed_in_pixels_per_second=800): 272 """Perform the pinch gesture on the page. 273 274 It computes the pinch gesture automatically based on the anchor 275 coordinate and the scale factor. The scale factor is the ratio of 276 of the final span and the initial span of the gesture. 277 278 Args: 279 left_anchor_ratio: The horizontal pinch anchor coordinate of the 280 gesture, as a ratio of the visible bounding rectangle for 281 document.body. 282 top_anchor_ratio: The vertical pinch anchor coordinate of the 283 gesture, as a ratio of the visible bounding rectangle for 284 document.body. 285 scale_factor: The ratio of the final span to the initial span. 286 The default scale factor is 287 3.0 / (window.outerWidth/window.innerWidth). 288 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 289 """ 290 self._RunAction(PinchAction( 291 left_anchor_ratio=left_anchor_ratio, top_anchor_ratio=top_anchor_ratio, 292 scale_factor=scale_factor, 293 speed_in_pixels_per_second=speed_in_pixels_per_second)) 294 295 def PinchElement(self, selector=None, text=None, element_function=None, 296 left_anchor_ratio=0.5, top_anchor_ratio=0.5, 297 scale_factor=None, speed_in_pixels_per_second=800): 298 """Perform the pinch gesture on an element. 299 300 It computes the pinch gesture automatically based on the anchor 301 coordinate and the scale factor. The scale factor is the ratio of 302 of the final span and the initial span of the gesture. 303 304 Args: 305 selector: A CSS selector describing the element. 306 text: The element must contains this exact text. 307 element_function: A JavaScript function (as string) that is used 308 to retrieve the element. For example: 309 'function() { return foo.element; }'. 310 left_anchor_ratio: The horizontal pinch anchor coordinate of the 311 gesture, as a ratio of the visible bounding rectangle for 312 the element. 313 top_anchor_ratio: The vertical pinch anchor coordinate of the 314 gesture, as a ratio of the visible bounding rectangle for 315 the element. 316 scale_factor: The ratio of the final span to the initial span. 317 The default scale factor is 318 3.0 / (window.outerWidth/window.innerWidth). 319 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 320 """ 321 self._RunAction(PinchAction( 322 selector=selector, text=text, element_function=element_function, 323 left_anchor_ratio=left_anchor_ratio, top_anchor_ratio=top_anchor_ratio, 324 scale_factor=scale_factor, 325 speed_in_pixels_per_second=speed_in_pixels_per_second)) 326 327 def ScrollPage(self, left_start_ratio=0.5, top_start_ratio=0.5, 328 direction='down', distance=None, distance_expr=None, 329 speed_in_pixels_per_second=800, use_touch=False, 330 synthetic_gesture_source=GESTURE_SOURCE_DEFAULT): 331 """Perform scroll gesture on the page. 332 333 You may specify distance or distance_expr, but not both. If 334 neither is specified, the default scroll distance is variable 335 depending on direction (see scroll.js for full implementation). 336 337 Args: 338 left_start_ratio: The horizontal starting coordinate of the 339 gesture, as a ratio of the visible bounding rectangle for 340 document.body. 341 top_start_ratio: The vertical starting coordinate of the 342 gesture, as a ratio of the visible bounding rectangle for 343 document.body. 344 direction: The direction of scroll, either 'left', 'right', 345 'up', 'down', 'upleft', 'upright', 'downleft', or 'downright' 346 distance: The distance to scroll (in pixel). 347 distance_expr: A JavaScript expression (as string) that can be 348 evaluated to compute scroll distance. Example: 349 'window.scrollTop' or '(function() { return crazyMath(); })()'. 350 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 351 use_touch: Whether scrolling should be done with touch input. 352 synthetic_gesture_source: the source input device type for the 353 synthetic gesture: 'DEFAULT', 'TOUCH' or 'MOUSE'. 354 """ 355 assert synthetic_gesture_source in SUPPORTED_GESTURE_SOURCES 356 self._RunAction(ScrollAction( 357 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 358 direction=direction, distance=distance, distance_expr=distance_expr, 359 speed_in_pixels_per_second=speed_in_pixels_per_second, 360 use_touch=use_touch, synthetic_gesture_source=synthetic_gesture_source)) 361 362 def RepeatableBrowserDrivenScroll(self, x_scroll_distance_ratio=0.0, 363 y_scroll_distance_ratio=0.5, 364 repeat_count=0, 365 repeat_delay_ms=250, 366 timeout=60): 367 """Perform a browser driven repeatable scroll gesture. 368 369 The scroll gesture is driven from the browser, this is useful because the 370 main thread often isn't resposive but the browser process usually is, so the 371 delay between the scroll gestures should be consistent. 372 373 Args: 374 x_scroll_distance_ratio: The horizontal length of the scroll as a fraction 375 of the screen width. 376 y_scroll_distance_ratio: The vertical length of the scroll as a fraction 377 of the screen height. 378 repeat_count: The number of additional times to repeat the gesture. 379 repeat_delay_ms: The delay in milliseconds between each scroll gesture. 380 """ 381 self._RunAction(RepeatableScrollAction( 382 x_scroll_distance_ratio=x_scroll_distance_ratio, 383 y_scroll_distance_ratio=y_scroll_distance_ratio, 384 repeat_count=repeat_count, 385 repeat_delay_ms=repeat_delay_ms, timeout=timeout)) 386 387 def ScrollElement(self, selector=None, text=None, element_function=None, 388 left_start_ratio=0.5, top_start_ratio=0.5, 389 direction='down', distance=None, distance_expr=None, 390 speed_in_pixels_per_second=800, use_touch=False, 391 synthetic_gesture_source=GESTURE_SOURCE_DEFAULT): 392 """Perform scroll gesture on the element. 393 394 The element may be selected via selector, text, or element_function. 395 Only one of these arguments must be specified. 396 397 You may specify distance or distance_expr, but not both. If 398 neither is specified, the default scroll distance is variable 399 depending on direction (see scroll.js for full implementation). 400 401 Args: 402 selector: A CSS selector describing the element. 403 text: The element must contains this exact text. 404 element_function: A JavaScript function (as string) that is used 405 to retrieve the element. For example: 406 'function() { return foo.element; }'. 407 left_start_ratio: The horizontal starting coordinate of the 408 gesture, as a ratio of the visible bounding rectangle for 409 the element. 410 top_start_ratio: The vertical starting coordinate of the 411 gesture, as a ratio of the visible bounding rectangle for 412 the element. 413 direction: The direction of scroll, either 'left', 'right', 414 'up', 'down', 'upleft', 'upright', 'downleft', or 'downright' 415 distance: The distance to scroll (in pixel). 416 distance_expr: A JavaScript expression (as string) that can be 417 evaluated to compute scroll distance. Example: 418 'window.scrollTop' or '(function() { return crazyMath(); })()'. 419 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 420 use_touch: Whether scrolling should be done with touch input. 421 synthetic_gesture_source: the source input device type for the 422 synthetic gesture: 'DEFAULT', 'TOUCH' or 'MOUSE'. 423 """ 424 assert synthetic_gesture_source in SUPPORTED_GESTURE_SOURCES 425 self._RunAction(ScrollAction( 426 selector=selector, text=text, element_function=element_function, 427 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 428 direction=direction, distance=distance, distance_expr=distance_expr, 429 speed_in_pixels_per_second=speed_in_pixels_per_second, 430 use_touch=use_touch, synthetic_gesture_source=synthetic_gesture_source)) 431 432 def ScrollBouncePage(self, left_start_ratio=0.5, top_start_ratio=0.5, 433 direction='down', distance=100, 434 overscroll=10, repeat_count=10, 435 speed_in_pixels_per_second=400): 436 """Perform scroll bounce gesture on the page. 437 438 This gesture scrolls the page by the number of pixels specified in 439 distance, in the given direction, followed by a scroll by 440 (distance + overscroll) pixels in the opposite direction. 441 The above gesture is repeated repeat_count times. 442 443 Args: 444 left_start_ratio: The horizontal starting coordinate of the 445 gesture, as a ratio of the visible bounding rectangle for 446 document.body. 447 top_start_ratio: The vertical starting coordinate of the 448 gesture, as a ratio of the visible bounding rectangle for 449 document.body. 450 direction: The direction of scroll, either 'left', 'right', 451 'up', 'down', 'upleft', 'upright', 'downleft', or 'downright' 452 distance: The distance to scroll (in pixel). 453 overscroll: The number of additional pixels to scroll back, in 454 addition to the givendistance. 455 repeat_count: How often we want to repeat the full gesture. 456 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 457 """ 458 self._RunAction(ScrollBounceAction( 459 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 460 direction=direction, distance=distance, 461 overscroll=overscroll, repeat_count=repeat_count, 462 speed_in_pixels_per_second=speed_in_pixels_per_second)) 463 464 def ScrollBounceElement( 465 self, selector=None, text=None, element_function=None, 466 left_start_ratio=0.5, top_start_ratio=0.5, 467 direction='down', distance=100, 468 overscroll=10, repeat_count=10, 469 speed_in_pixels_per_second=400): 470 """Perform scroll bounce gesture on the element. 471 472 This gesture scrolls on the element by the number of pixels specified in 473 distance, in the given direction, followed by a scroll by 474 (distance + overscroll) pixels in the opposite direction. 475 The above gesture is repeated repeat_count times. 476 477 Args: 478 selector: A CSS selector describing the element. 479 text: The element must contains this exact text. 480 element_function: A JavaScript function (as string) that is used 481 to retrieve the element. For example: 482 'function() { return foo.element; }'. 483 left_start_ratio: The horizontal starting coordinate of the 484 gesture, as a ratio of the visible bounding rectangle for 485 document.body. 486 top_start_ratio: The vertical starting coordinate of the 487 gesture, as a ratio of the visible bounding rectangle for 488 document.body. 489 direction: The direction of scroll, either 'left', 'right', 490 'up', 'down', 'upleft', 'upright', 'downleft', or 'downright' 491 distance: The distance to scroll (in pixel). 492 overscroll: The number of additional pixels to scroll back, in 493 addition to the given distance. 494 repeat_count: How often we want to repeat the full gesture. 495 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 496 """ 497 self._RunAction(ScrollBounceAction( 498 selector=selector, text=text, element_function=element_function, 499 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 500 direction=direction, distance=distance, 501 overscroll=overscroll, repeat_count=repeat_count, 502 speed_in_pixels_per_second=speed_in_pixels_per_second)) 503 504 def MouseClick(self, selector=None): 505 """Mouse click the given element. 506 507 Args: 508 selector: A CSS selector describing the element. 509 """ 510 self._RunAction(MouseClickAction(selector=selector)) 511 512 def SwipePage(self, left_start_ratio=0.5, top_start_ratio=0.5, 513 direction='left', distance=100, speed_in_pixels_per_second=800): 514 """Perform swipe gesture on the page. 515 516 Args: 517 left_start_ratio: The horizontal starting coordinate of the 518 gesture, as a ratio of the visible bounding rectangle for 519 document.body. 520 top_start_ratio: The vertical starting coordinate of the 521 gesture, as a ratio of the visible bounding rectangle for 522 document.body. 523 direction: The direction of swipe, either 'left', 'right', 524 'up', or 'down' 525 distance: The distance to swipe (in pixel). 526 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 527 """ 528 self._RunAction(SwipeAction( 529 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 530 direction=direction, distance=distance, 531 speed_in_pixels_per_second=speed_in_pixels_per_second)) 532 533 def SwipeElement(self, selector=None, text=None, element_function=None, 534 left_start_ratio=0.5, top_start_ratio=0.5, 535 direction='left', distance=100, 536 speed_in_pixels_per_second=800): 537 """Perform swipe gesture on the element. 538 539 The element may be selected via selector, text, or element_function. 540 Only one of these arguments must be specified. 541 542 Args: 543 selector: A CSS selector describing the element. 544 text: The element must contains this exact text. 545 element_function: A JavaScript function (as string) that is used 546 to retrieve the element. For example: 547 'function() { return foo.element; }'. 548 left_start_ratio: The horizontal starting coordinate of the 549 gesture, as a ratio of the visible bounding rectangle for 550 the element. 551 top_start_ratio: The vertical starting coordinate of the 552 gesture, as a ratio of the visible bounding rectangle for 553 the element. 554 direction: The direction of swipe, either 'left', 'right', 555 'up', or 'down' 556 distance: The distance to swipe (in pixel). 557 speed_in_pixels_per_second: The speed of the gesture (in pixels/s). 558 """ 559 self._RunAction(SwipeAction( 560 selector=selector, text=text, element_function=element_function, 561 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 562 direction=direction, distance=distance, 563 speed_in_pixels_per_second=speed_in_pixels_per_second)) 564 565 def LoadMedia(self, selector=None, event_timeout_in_seconds=0, 566 event_to_await='canplaythrough'): 567 """Invokes load() on media elements and awaits an event. 568 569 Args: 570 selector: A CSS selector describing the element. If none is 571 specified, play the first media element on the page. If the 572 selector matches more than 1 media element, all of them will 573 be played. 574 event_timeout_in_seconds: Maximum waiting time for the event to be fired. 575 0 means do not wait. 576 event_to_await: Which event to await. For example: 'canplaythrough' or 577 'loadedmetadata'. 578 579 Raises: 580 TimeoutException: If the maximum waiting time is exceeded. 581 """ 582 self._RunAction(LoadMediaAction( 583 selector=selector, timeout_in_seconds=event_timeout_in_seconds, 584 event_to_await=event_to_await)) 585 586 def PlayMedia(self, selector=None, 587 playing_event_timeout_in_seconds=0, 588 ended_event_timeout_in_seconds=0): 589 """Invokes the "play" action on media elements (such as video). 590 591 Args: 592 selector: A CSS selector describing the element. If none is 593 specified, play the first media element on the page. If the 594 selector matches more than 1 media element, all of them will 595 be played. 596 playing_event_timeout_in_seconds: Maximum waiting time for the "playing" 597 event (dispatched when the media begins to play) to be fired. 598 0 means do not wait. 599 ended_event_timeout_in_seconds: Maximum waiting time for the "ended" 600 event (dispatched when playback completes) to be fired. 601 0 means do not wait. 602 603 Raises: 604 TimeoutException: If the maximum waiting time is exceeded. 605 """ 606 self._RunAction(PlayAction( 607 selector=selector, 608 playing_event_timeout_in_seconds=playing_event_timeout_in_seconds, 609 ended_event_timeout_in_seconds=ended_event_timeout_in_seconds)) 610 611 def SeekMedia(self, seconds, selector=None, timeout_in_seconds=0, 612 log_time=True, label=''): 613 """Performs a seek action on media elements (such as video). 614 615 Args: 616 seconds: The media time to seek to. 617 selector: A CSS selector describing the element. If none is 618 specified, seek the first media element on the page. If the 619 selector matches more than 1 media element, all of them will 620 be seeked. 621 timeout_in_seconds: Maximum waiting time for the "seeked" event 622 (dispatched when the seeked operation completes) to be 623 fired. 0 means do not wait. 624 log_time: Whether to log the seek time for the perf 625 measurement. Useful when performing multiple seek. 626 label: A suffix string to name the seek perf measurement. 627 628 Raises: 629 TimeoutException: If the maximum waiting time is exceeded. 630 """ 631 self._RunAction(SeekAction( 632 seconds=seconds, selector=selector, 633 timeout_in_seconds=timeout_in_seconds, 634 log_time=log_time, label=label)) 635 636 def LoopMedia(self, loop_count, selector=None, timeout_in_seconds=None): 637 """Loops a media playback. 638 639 Args: 640 loop_count: The number of times to loop the playback. 641 selector: A CSS selector describing the element. If none is 642 specified, loop the first media element on the page. If the 643 selector matches more than 1 media element, all of them will 644 be looped. 645 timeout_in_seconds: Maximum waiting time for the looped playback to 646 complete. 0 means do not wait. None (the default) means to 647 wait loop_count * 60 seconds. 648 649 Raises: 650 TimeoutException: If the maximum waiting time is exceeded. 651 """ 652 self._RunAction(LoopAction( 653 loop_count=loop_count, selector=selector, 654 timeout_in_seconds=timeout_in_seconds)) 655 656 def ForceGarbageCollection(self): 657 """Forces JavaScript garbage collection on the page.""" 658 self._tab.CollectGarbage() 659 660 def SimulateMemoryPressureNotification(self, pressure_level): 661 """Simulate memory pressure notification. 662 663 Args: 664 pressure_level: 'moderate' or 'critical'. 665 """ 666 self._tab.browser.SimulateMemoryPressureNotification(pressure_level) 667 668 def PauseInteractive(self): 669 """Pause the page execution and wait for terminal interaction. 670 671 This is typically used for debugging. You can use this to pause 672 the page execution and inspect the browser state before 673 continuing. 674 """ 675 raw_input("Interacting... Press Enter to continue.") 676 677 def RepaintContinuously(self, seconds): 678 """Continuously repaints the visible content. 679 680 It does this by requesting animation frames until the given number 681 of seconds have elapsed AND at least three RAFs have been 682 fired. Times out after max(60, self.seconds), if less than three 683 RAFs were fired.""" 684 self._RunAction(RepaintContinuouslyAction( 685 seconds=0 if self._skip_waits else seconds)) 686 687 688class Interaction(object): 689 690 def __init__(self, action_runner, label, flags): 691 assert action_runner 692 assert label 693 assert isinstance(flags, list) 694 695 self._action_runner = action_runner 696 self._label = label 697 self._flags = flags 698 self._started = False 699 700 def __enter__(self): 701 self.Begin() 702 return self 703 704 def __exit__(self, exc_type, exc_value, traceback): 705 if exc_value is None: 706 self.End() 707 else: 708 logging.warning( 709 'Exception was raised in the with statement block, the end of ' 710 'interaction record is not marked.') 711 712 def Begin(self): 713 assert not self._started 714 self._started = True 715 self._action_runner.ExecuteJavaScript( 716 'console.time("%s");' % 717 timeline_interaction_record.GetJavaScriptMarker( 718 self._label, self._flags)) 719 720 def End(self): 721 assert self._started 722 self._started = False 723 self._action_runner.ExecuteJavaScript( 724 'console.timeEnd("%s");' % 725 timeline_interaction_record.GetJavaScriptMarker( 726 self._label, self._flags)) 727