• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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