Home | History | Annotate | Download | only in core
      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 
      5 import os
      6 
      7 from telemetry.core import util
      8 
      9 DEFAULT_WEB_CONTENTS_TIMEOUT = 90
     10 
     11 # TODO(achuith, dtu, nduca): Add unit tests specifically for WebContents,
     12 # independent of Tab.
     13 class WebContents(object):
     14   """Represents web contents in the browser"""
     15   def __init__(self, inspector_backend, backend_list):
     16     self._inspector_backend = inspector_backend
     17     self._backend_list = backend_list
     18 
     19     with open(os.path.join(os.path.dirname(__file__),
     20         'network_quiescence.js')) as f:
     21       self._quiescence_js = f.read()
     22 
     23   @property
     24   def id(self):
     25     """Return the unique id string for this tab object."""
     26     return self._inspector_backend.id
     27 
     28   def WaitForDocumentReadyStateToBeComplete(self,
     29       timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
     30     self.WaitForJavaScriptExpression(
     31         'document.readyState == "complete"', timeout)
     32 
     33   def WaitForDocumentReadyStateToBeInteractiveOrBetter(self,
     34       timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
     35     self.WaitForJavaScriptExpression(
     36         'document.readyState == "interactive" || '
     37         'document.readyState == "complete"', timeout)
     38 
     39   def WaitForJavaScriptExpression(self, expr, timeout):
     40     """Waits for the given JavaScript expression to be True.
     41 
     42     This method is robust against any given Evaluation timing out.
     43     """
     44     def IsJavaScriptExpressionTrue():
     45       try:
     46         return bool(self.EvaluateJavaScript(expr))
     47       except util.TimeoutException:
     48         # If the main thread is busy for longer than Evaluate's timeout, we
     49         # may time out here early. Instead, we want to wait for the full
     50         # timeout of this method.
     51         return False
     52     try:
     53       util.WaitFor(IsJavaScriptExpressionTrue, timeout)
     54     except util.TimeoutException as e:
     55       # Try to make timeouts a little more actionable by dumping |this|.
     56       raise util.TimeoutException(e.message + self.EvaluateJavaScript("""
     57         (function() {
     58           var error = '\\n\\nJavaScript |this|:\\n';
     59           for (name in this) {
     60             try {
     61               error += '\\t' + name + ': ' + this[name] + '\\n';
     62             } catch (e) {
     63               error += '\\t' + name + ': ???\\n';
     64             }
     65           }
     66           if (window && window.document) {
     67             error += '\\n\\nJavaScript window.document:\\n';
     68             for (name in window.document) {
     69               try {
     70                 error += '\\t' + name + ': ' + window.document[name] + '\\n';
     71               } catch (e) {
     72                 error += '\\t' + name + ': ???\\n';
     73               }
     74             }
     75           }
     76           return error;
     77         })();
     78       """))
     79 
     80   def HasReachedQuiescence(self):
     81     """Determine whether the page has reached quiescence after loading.
     82 
     83     Returns:
     84       True if 2 seconds have passed since last resource received, false
     85       otherwise."""
     86 
     87     # Inclusion of the script that provides
     88     # window.__telemetry_testHasReachedNetworkQuiescence()
     89     # is idempotent, it's run on every call because WebContents doesn't track
     90     # page loads and we need to execute anew for every newly loaded page.
     91     has_reached_quiescence = (
     92         self.EvaluateJavaScript(self._quiescence_js +
     93             "window.__telemetry_testHasReachedNetworkQuiescence()"))
     94     return has_reached_quiescence
     95 
     96   def ExecuteJavaScript(self, statement, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
     97     """Executes statement in JavaScript. Does not return the result.
     98 
     99     If the statement failed to evaluate, EvaluateException will be raised.
    100     """
    101     return self.ExecuteJavaScriptInContext(
    102         statement, context_id=None, timeout=timeout)
    103 
    104   def EvaluateJavaScript(self, expr, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
    105     """Evalutes expr in JavaScript and returns the JSONized result.
    106 
    107     Consider using ExecuteJavaScript for cases where the result of the
    108     expression is not needed.
    109 
    110     If evaluation throws in JavaScript, a Python EvaluateException will
    111     be raised.
    112 
    113     If the result of the evaluation cannot be JSONized, then an
    114     EvaluationException will be raised.
    115     """
    116     return self.EvaluateJavaScriptInContext(
    117         expr, context_id=None, timeout=timeout)
    118 
    119   def ExecuteJavaScriptInContext(self, expr, context_id,
    120                                  timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
    121     """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
    122     The main page has context_id=1, the first iframe context_id=2, etc.
    123     """
    124     return self._inspector_backend.ExecuteJavaScript(
    125         expr, context_id=context_id, timeout=timeout)
    126 
    127   def EvaluateJavaScriptInContext(self, expr, context_id,
    128                                   timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
    129     """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
    130     The main page has context_id=1, the first iframe context_id=2, etc.
    131     """
    132     return self._inspector_backend.EvaluateJavaScript(
    133         expr, context_id=context_id, timeout=timeout)
    134 
    135   def EnableAllContexts(self):
    136     """Enable all contexts in a page. Returns the number of available contexts.
    137     """
    138     return self._inspector_backend.EnableAllContexts()
    139 
    140   @property
    141   def message_output_stream(self):
    142     return self._inspector_backend.message_output_stream
    143 
    144   @message_output_stream.setter
    145   def message_output_stream(self, stream):
    146     self._inspector_backend.message_output_stream = stream
    147 
    148   @property
    149   def timeline_model(self):
    150     return self._inspector_backend.timeline_model
    151 
    152   def StartTimelineRecording(self, options=None):
    153     self._inspector_backend.StartTimelineRecording(options)
    154 
    155   @property
    156   def is_timeline_recording_running(self):
    157     return self._inspector_backend.is_timeline_recording_running
    158 
    159   def StopTimelineRecording(self):
    160     self._inspector_backend.StopTimelineRecording()
    161 
    162   def TakeJSHeapSnapshot(self, timeout=120):
    163     return self._inspector_backend.TakeJSHeapSnapshot(timeout)
    164