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.core import video
6from telemetry.core import web_contents
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, backend_list):
23    super(Tab, self).__init__(inspector_backend, backend_list)
24
25  @property
26  def browser(self):
27    """The browser in which this tab resides."""
28    return self._inspector_backend.browser
29
30  @property
31  def url(self):
32    return self._inspector_backend.url
33
34  @property
35  def dom_stats(self):
36    """A dictionary populated with measured DOM statistics.
37
38    Currently this dictionary contains:
39    {
40      'document_count': integer,
41      'node_count': integer,
42      'event_listener_count': integer
43    }
44    """
45    dom_counters = self._inspector_backend.GetDOMStats(
46        timeout=DEFAULT_TAB_TIMEOUT)
47    assert (len(dom_counters) == 3 and
48            all([x in dom_counters for x in ['document_count', 'node_count',
49                                             'event_listener_count']]))
50    return dom_counters
51
52  def Activate(self):
53    """Brings this tab to the foreground asynchronously.
54
55    Not all browsers or browser versions support this method.
56    Be sure to check browser.supports_tab_control.
57
58    Please note: this is asynchronous. There is a delay between this call
59    and the page's documentVisibilityState becoming 'visible', and yet more
60    delay until the actual tab is visible to the user. None of these delays
61    are included in this call."""
62    self._backend_list.ActivateTab(self._inspector_backend.debugger_url)
63
64  def Close(self):
65    """Closes this tab.
66
67    Not all browsers or browser versions support this method.
68    Be sure to check browser.supports_tab_control."""
69    self._backend_list.CloseTab(self._inspector_backend.debugger_url)
70
71  @property
72  def screenshot_supported(self):
73    """True if the browser instance is capable of capturing screenshots."""
74    return self._inspector_backend.screenshot_supported
75
76  def Screenshot(self, timeout=DEFAULT_TAB_TIMEOUT):
77    """Capture a screenshot of the tab's contents.
78
79    Returns:
80      A telemetry.core.Bitmap.
81    """
82    return self._inspector_backend.Screenshot(timeout)
83
84  @property
85  def video_capture_supported(self):
86    """True if the browser instance is capable of capturing video."""
87    return self.browser.platform.CanCaptureVideo()
88
89  def Highlight(self, color):
90    """Synchronously highlights entire tab contents with the given RgbaColor.
91
92    TODO(tonyg): It is possible that the z-index hack here might not work for
93    all pages. If this happens, DevTools also provides a method for this.
94    """
95    self.ExecuteJavaScript("""
96      (function() {
97        var screen = document.createElement('div');
98        screen.style.background = 'rgba(%d, %d, %d, %d)';
99        screen.style.position = 'fixed';
100        screen.style.top = '0';
101        screen.style.left = '0';
102        screen.style.width = '100%%';
103        screen.style.height = '100%%';
104        screen.style.zIndex = '2147483638';
105        document.body.appendChild(screen);
106        requestAnimationFrame(function() {
107          requestAnimationFrame(function() {
108            window.__telemetry_screen_%d = screen;
109          });
110        });
111      })();
112    """ % (color.r, color.g, color.b, color.a, int(color)))
113    self.WaitForJavaScriptExpression(
114        '!!window.__telemetry_screen_%d' % int(color), 5)
115
116  def ClearHighlight(self, color):
117    """Clears a highlight of the given bitmap.RgbaColor."""
118    self.ExecuteJavaScript("""
119      (function() {
120        document.body.removeChild(window.__telemetry_screen_%d);
121        requestAnimationFrame(function() {
122          requestAnimationFrame(function() {
123            window.__telemetry_screen_%d = null;
124            console.time('__ClearHighlight.video_capture_start');
125            console.timeEnd('__ClearHighlight.video_capture_start');
126          });
127        });
128      })();
129    """ % (int(color), int(color)))
130    self.WaitForJavaScriptExpression(
131        '!window.__telemetry_screen_%d' % int(color), 5)
132
133  def StartVideoCapture(self, min_bitrate_mbps,
134                        highlight_bitmap=video.HIGHLIGHT_ORANGE_FRAME):
135    """Starts capturing video of the tab's contents.
136
137    This works by flashing the entire tab contents to a arbitrary color and then
138    starting video recording. When the frames are processed, we can look for
139    that flash as the content bounds.
140
141    Args:
142      min_bitrate_mbps: The minimum caputre bitrate in MegaBits Per Second.
143          The platform is free to deliver a higher bitrate if it can do so
144          without increasing overhead.
145    """
146    self.Highlight(highlight_bitmap)
147    self.browser.platform.StartVideoCapture(min_bitrate_mbps)
148    self.ClearHighlight(highlight_bitmap)
149
150  @property
151  def is_video_capture_running(self):
152    return self.browser.platform.is_video_capture_running
153
154  def StopVideoCapture(self):
155    """Stops recording video of the tab's contents.
156
157    This looks for the initial color flash in the first frame to establish the
158    tab content boundaries and then omits all frames displaying the flash.
159
160    Returns:
161      video: A video object which is a telemetry.core.Video
162    """
163    return self.browser.platform.StopVideoCapture()
164
165  def WaitForNavigate(self, timeout=DEFAULT_TAB_TIMEOUT):
166    """Waits for the navigation to complete.
167
168    The current page is expect to be in a navigation.
169    This function returns when the navigation is complete or when
170    the timeout has been exceeded.
171    """
172    self._inspector_backend.WaitForNavigate(timeout)
173
174  def Navigate(self, url, script_to_evaluate_on_commit=None,
175               timeout=DEFAULT_TAB_TIMEOUT):
176    """Navigates to url.
177
178    If |script_to_evaluate_on_commit| is given, the script source string will be
179    evaluated when the navigation is committed. This is after the context of
180    the page exists, but before any script on the page itself has executed.
181    """
182    self._inspector_backend.Navigate(url, script_to_evaluate_on_commit, timeout)
183
184  def GetCookieByName(self, name, timeout=DEFAULT_TAB_TIMEOUT):
185    """Returns the value of the cookie by the given |name|."""
186    return self._inspector_backend.GetCookieByName(name, timeout)
187
188  def CollectGarbage(self):
189    self._inspector_backend.CollectGarbage()
190
191  def ClearCache(self, force):
192    """Clears the browser's networking related disk, memory and other caches.
193
194    Args:
195      force: Iff true, navigates to about:blank which destroys the previous
196          renderer, ensuring that even "live" resources in the memory cache are
197          cleared.
198    """
199    self.browser.platform.FlushDnsCache()
200    self.ExecuteJavaScript("""
201        if (window.chrome && chrome.benchmarking &&
202            chrome.benchmarking.clearCache) {
203          chrome.benchmarking.clearCache();
204          chrome.benchmarking.clearPredictorCache();
205          chrome.benchmarking.clearHostResolverCache();
206        }
207    """)
208    if force:
209      self.Navigate('about:blank')
210