Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Unified Diff: tools/telemetry/telemetry/tab_backend.py

Issue 11819018: [Telemetry] Clean separation between tab (public API) and tab_backend (Chrome implementation). Flat… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/tab_backend.py
diff --git a/tools/telemetry/telemetry/tab_backend.py b/tools/telemetry/telemetry/tab_backend.py
new file mode 100644
index 0000000000000000000000000000000000000000..265edd0f5b74803be5e7c3a0ac2ed81fabdfa8ef
--- /dev/null
+++ b/tools/telemetry/telemetry/tab_backend.py
@@ -0,0 +1,279 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import logging
+import socket
+
+from telemetry import inspector_console
+from telemetry import inspector_page
+from telemetry import inspector_runtime
+from telemetry import inspector_timeline
+from telemetry import png_bitmap
+from telemetry import tab_crash_exception
+from telemetry import util
+from telemetry import websocket
+
+class InspectorException(Exception):
+ pass
+
+class TabBackend(object):
+ def __init__(self, browser, tab_controller, debugger_url):
+ self._browser = browser
+ self._tab_controller = tab_controller
+ self._debugger_url = debugger_url
+ self._socket = None
+ self._next_request_id = 0
+ self._domain_handlers = {}
+ self._cur_socket_timeout = 0
+
+ self._console = None
+ self._page = None
+ self._runtime = None
+ self._timeline = None
+
+ self._Connect()
+
+ def _Connect(self):
+ if self._socket:
+ return
+ self._socket = websocket.create_connection(self._debugger_url)
+
+ self._console = inspector_console.InspectorConsole(self)
+ self._page = inspector_page.InspectorPage(self)
+ self._runtime = inspector_runtime.InspectorRuntime(self)
+ self._timeline = inspector_timeline.InspectorTimeline(self)
+
+ def _Disconnect(self):
+ for _, handlers in self._domain_handlers.items():
+ _, will_close_handler = handlers
+ will_close_handler()
+ self._domain_handlers = {}
+
+ self._socket.close()
+ self._socket = None
+
+ self._browser = None
+ self._tab_controller = None
+ self._debugger_url = None
+
+ self._console = None
+ self._page = None
+ self._runtime = None
+ self._timeline = None
+
+ # General public methods.
+
+ @property
+ def browser(self):
+ return self._browser
+
+ @property
+ def url(self):
+ self._Disconnect()
+ return self._tab_controller.GetTabUrl(self._debugger_url)
+
+ def Activate(self):
+ self._Connect()
+ self._tab_controller.ActivateTab(self._debugger_url)
+
+ def Close(self):
+ self._Disconnect()
+ self._tab_controller.CloseTab(self._debugger_url)
+
+ # Public methods implemented in JavaScript.
+
+ def WaitForDocumentReadyStateToBeComplete(self, timeout):
+ util.WaitFor(
+ lambda: self._runtime.Evaluate('document.readyState') == 'complete',
+ timeout)
+
+ def WaitForDocumentReadyStateToBeInteractiveOrBetter(
+ self, timeout):
+ def IsReadyStateInteractiveOrBetter():
+ rs = self._runtime.Evaluate('document.readyState')
+ return rs == 'complete' or rs == 'interactive'
+ util.WaitFor(IsReadyStateInteractiveOrBetter, timeout)
+
+ @property
+ def screenshot_supported(self):
+ if self._runtime.Evaluate(
+ 'window.chrome.gpuBenchmarking === undefined'):
+ return False
+
+ if self._runtime.Evaluate(
+ 'window.chrome.gpuBenchmarking.windowSnapshotPNG === undefined'):
+ return False
+
+ return True
+
+ def Screenshot(self, timeout):
+ if self._runtime.Evaluate(
+ 'window.chrome.gpuBenchmarking === undefined'):
+ raise Exception("Browser was not started with --enable-gpu-benchmarking")
+
+ if self._runtime.Evaluate(
+ 'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
+ raise Exception("Browser does not support window snapshot API.")
+
+ self._runtime.Evaluate("""
+ if(!window.__telemetry) {
+ window.__telemetry = {}
+ }
+ window.__telemetry.snapshotComplete = false;
+ window.__telemetry.snapshotData = null;
+ window.chrome.gpuBenchmarking.beginWindowSnapshotPNG(
+ function(snapshot) {
+ window.__telemetry.snapshotData = snapshot;
+ window.__telemetry.snapshotComplete = true;
+ }
+ );
+ """)
+
+ def IsSnapshotComplete():
+ return self._runtime.Evaluate('window.__telemetry.snapshotComplete')
+
+ util.WaitFor(IsSnapshotComplete, timeout)
+
+ snap = self._runtime.Evaluate("""
+ (function() {
+ var data = window.__telemetry.snapshotData;
+ delete window.__telemetry.snapshotComplete;
+ delete window.__telemetry.snapshotData;
+ return data;
+ })()
+ """)
+ if snap:
+ return png_bitmap.PngBitmap(snap['data'])
+ return None
+
+ # Console public methods.
+
+ @property
+ def message_output_stream(self): # pylint: disable=E0202
+ return self._console.message_output_stream
+
+ @message_output_stream.setter
+ def message_output_stream(self, stream): # pylint: disable=E0202
+ self._console.message_output_stream = stream
+
+ # Page public methods.
+
+ def PerformActionAndWaitForNavigate(self, action_function, timeout):
+ self._page.PerformActionAndWaitForNavigate(action_function, timeout)
+
+ def Navigate(self, url, timeout):
+ self._page.Navigate(url, timeout)
+
+ def GetCookieByName(self, name, timeout):
+ return self._page.GetCookieByName(name, timeout)
+
+ # Console public methods.
+
+ def Execute(self, expr, timeout):
+ self._console.Execute(expr, timeout)
+
+ def Evaluate(self, expr, timeout):
+ return self._console.Evaluate(expr, timeout)
+
+ # Timeline public methods.
+
+ @property
+ def timeline_events(self):
+ return self._timeline.timeline_events
+
+ def StartRecording(self):
+ self._timeline.Start()
+
+ def StopRecording(self):
+ self._timeline.Stop()
+
+ # Methods used internally by other backends.
+
+ def DispatchNotifications(self, timeout=10):
+ self._SetTimeout(timeout)
+ try:
+ data = self._socket.recv()
+ except (socket.error, websocket.WebSocketException):
+ if self._tab_controller.DoesDebuggerUrlExist(self._debugger_url):
+ return
+ raise tab_crash_exception.TabCrashException()
+
+ res = json.loads(data)
+ logging.debug('got [%s]', data)
+ if 'method' in res:
+ self._HandleNotification(res)
+
+ def _HandleNotification(self, res):
+ mname = res['method']
+ dot_pos = mname.find('.')
+ domain_name = mname[:dot_pos]
+ if domain_name in self._domain_handlers:
+ try:
+ self._domain_handlers[domain_name][0](res)
+ except Exception:
+ import traceback
+ traceback.print_exc()
+ else:
+ logging.debug('Unhandled inspector message: %s', res)
+
+ def SendAndIgnoreResponse(self, req):
+ req['id'] = self._next_request_id
+ self._next_request_id += 1
+ data = json.dumps(req)
+ self._socket.send(data)
+ logging.debug('sent [%s]', data)
+
+ def _SetTimeout(self, timeout):
+ if self._cur_socket_timeout != timeout:
+ self._socket.settimeout(timeout)
+ self._cur_socket_timeout = timeout
+
+ def SyncRequest(self, req, timeout=10):
+ # TODO(nduca): Listen to the timeout argument
+ # pylint: disable=W0613
+ self._SetTimeout(timeout)
+ self.SendAndIgnoreResponse(req)
+
+ while True:
+ try:
+ data = self._socket.recv()
+ except (socket.error, websocket.WebSocketException):
+ if self._tab_controller.DoesDebuggerUrlExist(self._debugger_url):
+ raise util.TimeoutException(
+ 'Timed out waiting for reply. This is unusual.')
+ raise tab_crash_exception.TabCrashException()
+
+ res = json.loads(data)
+ logging.debug('got [%s]', data)
+ if 'method' in res:
+ self._HandleNotification(res)
+ continue
+
+ if res['id'] != req['id']:
+ logging.debug('Dropped reply: %s', json.dumps(res))
+ continue
+ return res
+
+ def RegisterDomain(self,
+ domain_name, notification_handler, will_close_handler):
+ """Registers a given domain for handling notification methods.
+
+ For example, given tab_backend:
+ def OnConsoleNotification(msg):
+ if msg['method'] == 'Console.messageAdded':
+ print msg['params']['message']
+ return
+ def OnConsoleClose(self):
+ pass
+ tab_backend.RegisterDomain('Console',
+ OnConsoleNotification, OnConsoleClose)
+ """
+ assert domain_name not in self._domain_handlers
+ self._domain_handlers[domain_name] = (notification_handler,
+ will_close_handler)
+
+ def UnregisterDomain(self, domain_name):
+ """Unregisters a previously registered domain."""
+ assert domain_name in self._domain_handlers
+ self._domain_handlers.pop(domain_name)

Powered by Google App Engine
This is Rietveld 408576698