Index: tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py |
diff --git a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py |
index 9e039ed32ace774bbbc3af19b87cfe48dcd0fc3f..db12c17cc65ac03122cb310408f0e053cca72040 100644 |
--- a/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py |
+++ b/tools/telemetry/telemetry/core/backends/chrome_inspector/inspector_websocket.py |
@@ -11,30 +11,54 @@ |
from telemetry.core.backends.chrome_inspector import websocket |
+class DispatchNotificationsUntilDoneTimeoutException(Exception): |
+ """Exception that can be thrown from DispatchNotificationsUntilDone to |
+ indicate timeout exception of the function. |
+ """ |
+ |
+ def __init__(self, elapsed_time): |
+ super(DispatchNotificationsUntilDoneTimeoutException, self).__init__() |
+ self.elapsed_time = elapsed_time |
+ |
+ |
class InspectorWebsocket(object): |
- def __init__(self): |
- """Create a websocket handler for communicating with Inspectors.""" |
+ def __init__(self, error_handler=None): |
+ """Create a websocket handler for communicating with Inspectors. |
+ |
+ Args: |
+ error_handler: A callback for errors in communicating with the Inspector. |
+ Must accept a single numeric parameter indicated the time elapsed before |
+ the error. |
+ """ |
self._socket = None |
self._cur_socket_timeout = 0 |
self._next_request_id = 0 |
+ self._error_handler = error_handler |
+ self._all_data_received = False |
self._domain_handlers = {} |
def RegisterDomain(self, domain_name, notification_handler): |
"""Registers a given domain for handling notification methods. |
+ When used as handler for DispatchNotificationsUntilDone, |
+ notification handler should return a boolean, where True indicates |
+ that we should stop listening for more notifications. |
+ |
For example, given inspector_backend: |
def OnConsoleNotification(msg): |
if msg['method'] == 'Console.messageAdded': |
print msg['params']['message'] |
+ return True |
inspector_backend.RegisterDomain('Console', OnConsoleNotification) |
Args: |
domain_name: The devtools domain name. E.g., 'Tracing', 'Memory', 'Page'. |
notification_handler: Handler for devtools notification. Will be |
called if a devtools notification with matching domain is received |
- via DispatchNotifications. The handler accepts a single paramater: |
- the JSON object representing the notification. |
+ (via DispatchNotifications and DispatchNotificationsUntilDone). |
+ The handler accepts a single paramater: the JSON object representing |
+ the notification. |
""" |
assert domain_name not in self._domain_handlers |
self._domain_handlers[domain_name] = notification_handler |
@@ -75,6 +99,38 @@ |
def DispatchNotifications(self, timeout=10): |
self._Receive(timeout) |
+ def DispatchNotificationsUntilDone(self, timeout): |
+ """Dispatch notifications until notification_handler return True. |
+ |
+ Args: |
+ timeout: a number that respresents the timeout in seconds. |
+ |
+ Raises: |
+ DispatchNotificationsUntilDoneTimeoutException if more than |timeout| has |
+ seconds has passed since the last time any data is received or since this |
+ function is called, whichever happens later, to when the next attempt to |
+ receive data fails due to some WebSocketException. |
+ """ |
+ self._all_data_received = False |
+ if timeout < self._cur_socket_timeout: |
+ self._SetTimeout(timeout) |
+ timeout_start_time = time.time() |
+ while self._socket: |
+ try: |
+ if self._Receive(timeout): |
+ timeout_start_time = time.time() |
+ if self._all_data_received: |
+ break |
+ except websocket.WebSocketTimeoutException: |
+ # TODO(chrishenry): Since we always call settimeout in |
+ # _Receive, we should be able to rip manual logic of tracking |
+ # elapsed time and simply throw |
+ # DispatchNotificationsUntilDoneTimeoutException from here. |
+ pass |
+ elapsed_time = time.time() - timeout_start_time |
+ if elapsed_time > timeout: |
+ raise DispatchNotificationsUntilDoneTimeoutException(elapsed_time) |
+ |
def _SetTimeout(self, timeout): |
if self._cur_socket_timeout != timeout: |
self._socket.settimeout(timeout) |
@@ -82,22 +138,29 @@ |
def _Receive(self, timeout=10): |
self._SetTimeout(timeout) |
- if not self._socket: |
- return None |
- data = self._socket.recv() |
- result = json.loads(data) |
- if logging.getLogger().isEnabledFor(logging.DEBUG): |
- logging.debug( |
- 'got [%s]', json.dumps(result, indent=2, sort_keys=True)) |
- if 'method' in result: |
- self._HandleNotification(result) |
- return result |
+ start_time = time.time() |
+ try: |
+ if self._socket: |
+ self._all_data_received = False |
+ data = self._socket.recv() |
+ result = json.loads(data) |
+ if logging.getLogger().isEnabledFor(logging.DEBUG): |
+ logging.debug( |
+ 'got [%s]', json.dumps(result, indent=2, sort_keys=True)) |
+ if 'method' in result and self._HandleNotification(result): |
+ self._all_data_received = True |
+ return None |
+ return result |
+ except (socket.error, websocket.WebSocketException): |
+ elapsed_time = time.time() - start_time |
+ self._error_handler(elapsed_time) |
def _HandleNotification(self, result): |
mname = result['method'] |
dot_pos = mname.find('.') |
domain_name = mname[:dot_pos] |
if domain_name in self._domain_handlers: |
- self._domain_handlers[domain_name](result) |
+ return self._domain_handlers[domain_name](result) |
logging.warn('Unhandled inspector message: %s', result) |
+ return False |