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 + '\n\nJavaScript |this|:\n' +
     57                                   self.EvaluateJavaScript("""
     58         (function() {
     59           var error = '';
     60           for (name in this) {
     61             try {
     62               error += '\\t' + name + ': ' + this[name] + '\\n';
     63             } catch (e) {
     64               error += '\\t' + name + ': ???\\n';
     65             }
     66           }
     67           return error;
     68         })();
     69       """))
     70 
     71   def HasReachedQuiescence(self):
     72     """Determine whether the page has reached quiescence after loading.
     73 
     74     Returns:
     75       True if 2 seconds have passed since last resource received, false
     76       otherwise."""
     77 
     78     # Inclusion of the script that provides
     79     # window.__telemetry_testHasReachedNetworkQuiescence()
     80     # is idempotent, it's run on every call because WebContents doesn't track
     81     # page loads and we need to execute anew for every newly loaded page.
     82     has_reached_quiescence = (
     83         self.EvaluateJavaScript(self._quiescence_js +
     84             "window.__telemetry_testHasReachedNetworkQuiescence()"))
     85     return has_reached_quiescence
     86 
     87   def ExecuteJavaScript(self, statement, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
     88     """Executes statement in JavaScript. Does not return the result.
     89 
     90     If the statement failed to evaluate, EvaluateException will be raised.
     91     """
     92     return self.ExecuteJavaScriptInContext(
     93         statement, context_id=None, timeout=timeout)
     94 
     95   def EvaluateJavaScript(self, expr, timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
     96     """Evalutes expr in JavaScript and returns the JSONized result.
     97 
     98     Consider using ExecuteJavaScript for cases where the result of the
     99     expression is not needed.
    100 
    101     If evaluation throws in JavaScript, a Python EvaluateException will
    102     be raised.
    103 
    104     If the result of the evaluation cannot be JSONized, then an
    105     EvaluationException will be raised.
    106     """
    107     return self.EvaluateJavaScriptInContext(
    108         expr, context_id=None, timeout=timeout)
    109 
    110   def ExecuteJavaScriptInContext(self, expr, context_id,
    111                                  timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
    112     """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
    113     The main page has context_id=1, the first iframe context_id=2, etc.
    114     """
    115     return self._inspector_backend.ExecuteJavaScript(
    116         expr, context_id=context_id, timeout=timeout)
    117 
    118   def EvaluateJavaScriptInContext(self, expr, context_id,
    119                                   timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
    120     """Similar to ExecuteJavaScript, except context_id can refer to an iframe.
    121     The main page has context_id=1, the first iframe context_id=2, etc.
    122     """
    123     return self._inspector_backend.EvaluateJavaScript(
    124         expr, context_id=context_id, timeout=timeout)
    125 
    126   def EnableAllContexts(self):
    127     """Enable all contexts in a page. Returns the number of available contexts.
    128     """
    129     return self._inspector_backend.EnableAllContexts()
    130 
    131   @property
    132   def message_output_stream(self):
    133     return self._inspector_backend.message_output_stream
    134 
    135   @message_output_stream.setter
    136   def message_output_stream(self, stream):
    137     self._inspector_backend.message_output_stream = stream
    138 
    139   @property
    140   def timeline_model(self):
    141     return self._inspector_backend.timeline_model
    142 
    143   def StartTimelineRecording(self, options=None):
    144     self._inspector_backend.StartTimelineRecording(options)
    145 
    146   @property
    147   def is_timeline_recording_running(self):
    148     return self._inspector_backend.is_timeline_recording_running
    149 
    150   def StopTimelineRecording(self):
    151     self._inspector_backend.StopTimelineRecording()
    152 
    153   def TakeJSHeapSnapshot(self, timeout=120):
    154     return self._inspector_backend.TakeJSHeapSnapshot(timeout)
    155