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

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: Make unit tests pass and merge. 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
« no previous file with comments | « tools/telemetry/telemetry/tab.py ('k') | tools/telemetry/telemetry/tab_test_case.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..1d61a688ff7dde4899923184cbda2eb006263848
--- /dev/null
+++ b/tools/telemetry/telemetry/tab_backend.py
@@ -0,0 +1,298 @@
+# 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, browser_backend, debugger_url):
+ assert debugger_url
+ self._browser = browser
+ self._browser_backend = browser_backend
+ 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
+
+ def __del__(self):
+ self._Disconnect()
+
+ 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 = {}
+
+ if self._socket:
+ self._socket.close()
+ self._socket = None
+
+ self.__console = None
+ self.__page = None
+ self.__runtime = None
+ self.__timeline = None
+
+ @property
+ def _console(self):
+ self._Connect()
+ return self.__console
+
+ @property
+ def _page(self):
+ self._Connect()
+ return self.__page
+
+ @property
+ def _runtime(self):
+ self._Connect()
+ return self.__runtime
+
+ @property
+ def _timeline(self):
+ self._Connect()
+ return self.__timeline
+
+ # General public methods.
+
+ @property
+ def browser(self):
+ return self._browser
+
+ @property
+ def url(self):
+ self._Disconnect()
+ return self._browser_backend.tabs.GetTabUrl(self._debugger_url)
+
+ def Activate(self):
+ self._Connect()
+ self._browser_backend.tabs.ActivateTab(self._debugger_url)
+
+ def Close(self):
+ self._Disconnect()
+ self._browser_backend.tabs.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)
+
+ # Runtime public methods.
+
+ def ExecuteJavascript(self, expr, timeout):
+ self.__runtime.Execute(expr, timeout)
+
+ def EvaluateJavascript(self, expr, timeout):
+ return self.__runtime.Evaluate(expr, timeout)
+
+ # Timeline public methods.
+
+ @property
+ def timeline_events(self):
+ return self.__timeline.timeline_events
+
+ def StartTimelineRecording(self):
+ self.__timeline.Start()
+
+ def StopTimelineRecording(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._browser_backend.tabs.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._browser_backend.tabs.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)
« no previous file with comments | « tools/telemetry/telemetry/tab.py ('k') | tools/telemetry/telemetry/tab_test_case.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698