146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# Copyright 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import os
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
71e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)from telemetry.core import util
81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
98bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)DEFAULT_WEB_CONTENTS_TIMEOUT = 90
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# TODO(achuith, dtu, nduca): Add unit tests specifically for WebContents,
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# independent of Tab.
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class WebContents(object):
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Represents web contents in the browser"""
15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  def __init__(self, inspector_backend, backend_list):
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._inspector_backend = inspector_backend
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    self._backend_list = backend_list
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    with open(os.path.join(os.path.dirname(__file__),
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'network_quiescence.js')) as f:
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      self._quiescence_js = f.read()
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
23f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  @property
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  def id(self):
25f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    """Return the unique id string for this tab object."""
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return self._inspector_backend.id
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def WaitForDocumentReadyStateToBeComplete(self,
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.WaitForJavaScriptExpression(
311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        'document.readyState == "complete"', timeout)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def WaitForDocumentReadyStateToBeInteractiveOrBetter(self,
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.WaitForJavaScriptExpression(
361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        'document.readyState == "interactive" || '
371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        'document.readyState == "complete"', timeout)
381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def WaitForJavaScriptExpression(self, expr, timeout):
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    """Waits for the given JavaScript expression to be True.
411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    This method is robust against any given Evaluation timing out.
431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    """
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    def IsJavaScriptExpressionTrue():
451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      try:
461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return bool(self.EvaluateJavaScript(expr))
471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      except util.TimeoutException:
481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        # If the main thread is busy for longer than Evaluate's timeout, we
491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        # may time out here early. Instead, we want to wait for the full
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        # timeout of this method.
511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        return False
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    try:
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      util.WaitFor(IsJavaScriptExpressionTrue, timeout)
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    except util.TimeoutException as e:
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      # Try to make timeouts a little more actionable by dumping |this|.
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      raise util.TimeoutException(e.message + self.EvaluateJavaScript("""
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        (function() {
586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          var error = '\\n\\nJavaScript |this|:\\n';
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          for (name in this) {
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            try {
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)              error += '\\t' + name + ': ' + this[name] + '\\n';
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            } catch (e) {
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)              error += '\\t' + name + ': ???\\n';
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            }
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          }
666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          if (window && window.document) {
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            error += '\\n\\nJavaScript window.document:\\n';
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            for (name in window.document) {
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)              try {
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                error += '\\t' + name + ': ' + window.document[name] + '\\n';
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)              } catch (e) {
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                error += '\\t' + name + ': ???\\n';
736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)              }
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            }
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          }
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          return error;
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        })();
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      """))
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def HasReachedQuiescence(self):
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """Determine whether the page has reached quiescence after loading.
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Returns:
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      True if 2 seconds have passed since last resource received, false
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      otherwise."""
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # Inclusion of the script that provides
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # window.__telemetry_testHasReachedNetworkQuiescence()
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # is idempotent, it's run on every call because WebContents doesn't track
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # page loads and we need to execute anew for every newly loaded page.
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    has_reached_quiescence = (
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        self.EvaluateJavaScript(self._quiescence_js +
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            "window.__telemetry_testHasReachedNetworkQuiescence()"))
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return has_reached_quiescence
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  def ExecuteJavaScript(self, statement, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    """Executes statement in JavaScript. Does not return the result.
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    If the statement failed to evaluate, EvaluateException will be raised.
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self.ExecuteJavaScriptInContext(
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        statement, context_id=None, timeout=timeout)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def EvaluateJavaScript(self, expr, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Evalutes expr in JavaScript and returns the JSONized result.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Consider using ExecuteJavaScript for cases where the result of the
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    expression is not needed.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    If evaluation throws in JavaScript, a Python EvaluateException will
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    be raised.
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    If the result of the evaluation cannot be JSONized, then an
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EvaluationException will be raised.
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self.EvaluateJavaScriptInContext(
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        expr, context_id=None, timeout=timeout)
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def ExecuteJavaScriptInContext(self, expr, context_id,
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    The main page has context_id=1, the first iframe context_id=2, etc.
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self._inspector_backend.ExecuteJavaScript(
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        expr, context_id=context_id, timeout=timeout)
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def EvaluateJavaScriptInContext(self, expr, context_id,
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                  timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    The main page has context_id=1, the first iframe context_id=2, etc.
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    """
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self._inspector_backend.EvaluateJavaScript(
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        expr, context_id=context_id, timeout=timeout)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  def EnableAllContexts(self):
136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    """Enable all contexts in a page. Returns the number of available contexts.
137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    """
138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return self._inspector_backend.EnableAllContexts()
139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  @property
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def message_output_stream(self):
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._inspector_backend.message_output_stream
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  @message_output_stream.setter
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def message_output_stream(self, stream):
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._inspector_backend.message_output_stream = stream
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  @property
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def timeline_model(self):
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._inspector_backend.timeline_model
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def StartTimelineRecording(self, options=None):
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    self._inspector_backend.StartTimelineRecording(options)
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  @property
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def is_timeline_recording_running(self):
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return self._inspector_backend.is_timeline_recording_running
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def StopTimelineRecording(self):
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._inspector_backend.StopTimelineRecording()
16158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
16258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def TakeJSHeapSnapshot(self, timeout=120):
16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return self._inspector_backend.TakeJSHeapSnapshot(timeout)
164