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

Side by Side Diff: chrome/test/pyautolib/remote_inspector_client.py

Issue 14058002: Keep remote_inspector_client in sync with the latest changes in Blink. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments addressed Created 7 years, 8 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Chrome remote inspector utility for pyauto tests. 6 """Chrome remote inspector utility for pyauto tests.
7 7
8 This script provides a python interface that acts as a front-end for Chrome's 8 This script provides a python interface that acts as a front-end for Chrome's
9 remote inspector module, communicating via sockets to interact with Chrome in 9 remote inspector module, communicating via sockets to interact with Chrome in
10 the same way that the Developer Tools does. This -- in theory -- should allow 10 the same way that the Developer Tools does. This -- in theory -- should allow
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 class _RemoteInspectorThread(threading.Thread): 274 class _RemoteInspectorThread(threading.Thread):
275 """Manages communication using Chrome's remote inspector protocol. 275 """Manages communication using Chrome's remote inspector protocol.
276 276
277 This class works in conjunction with the _DevToolsSocketClient class to 277 This class works in conjunction with the _DevToolsSocketClient class to
278 communicate with a remote Chrome instance following the remote inspector 278 communicate with a remote Chrome instance following the remote inspector
279 communication protocol in WebKit. This class performs the higher-level work 279 communication protocol in WebKit. This class performs the higher-level work
280 of managing request and reply messages, whereas _DevToolsSocketClient handles 280 of managing request and reply messages, whereas _DevToolsSocketClient handles
281 the lower-level work of socket communication. 281 the lower-level work of socket communication.
282 """ 282 """
283 283
284 def __init__(self, url, tab_index, tab_filter, verbose, show_socket_messages): 284 def __init__(self, url, tab_index, tab_filter, verbose, show_socket_messages,
285 agent_name):
285 """Initialize. 286 """Initialize.
286 287
287 Args: 288 Args:
288 url: The base URL to connent to. 289 url: The base URL to connent to.
289 tab_index: The integer index of the tab in the remote Chrome instance to 290 tab_index: The integer index of the tab in the remote Chrome instance to
290 use for snapshotting. 291 use for snapshotting.
291 tab_filter: When specified, is run over tabs of the remote Chrome 292 tab_filter: When specified, is run over tabs of the remote Chrome
292 instances to choose which one to connect to. 293 instances to choose which one to connect to.
293 verbose: A boolean indicating whether or not to use verbose logging. 294 verbose: A boolean indicating whether or not to use verbose logging.
294 show_socket_messages: A boolean indicating whether or not to show the 295 show_socket_messages: A boolean indicating whether or not to show the
295 socket messages sent/received when communicating with the remote 296 socket messages sent/received when communicating with the remote
296 Chrome instance. 297 Chrome instance.
297 """ 298 """
298 threading.Thread.__init__(self) 299 threading.Thread.__init__(self)
299 self._logger = logging.getLogger('_RemoteInspectorThread') 300 self._logger = logging.getLogger('_RemoteInspectorThread')
300 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) 301 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose])
301 302
302 self._killed = False 303 self._killed = False
303 self._requests = [] 304 self._requests = []
304 self._action_queue = [] 305 self._action_queue = []
305 self._action_queue_condition = threading.Condition() 306 self._action_queue_condition = threading.Condition()
306 self._action_specific_callback = None # Callback only for current action. 307 self._action_specific_callback = None # Callback only for current action.
307 self._action_specific_callback_lock = threading.Lock() 308 self._action_specific_callback_lock = threading.Lock()
308 self._general_callbacks = [] # General callbacks that can be long-lived. 309 self._general_callbacks = [] # General callbacks that can be long-lived.
309 self._general_callbacks_lock = threading.Lock() 310 self._general_callbacks_lock = threading.Lock()
310 self._condition_to_wait = None 311 self._condition_to_wait = None
312 self._agent_name = agent_name
311 313
312 # Create a DevToolsSocket client and wait for it to complete the remote 314 # Create a DevToolsSocket client and wait for it to complete the remote
313 # debugging protocol handshake with the remote Chrome instance. 315 # debugging protocol handshake with the remote Chrome instance.
314 result = self._IdentifyDevToolsSocketConnectionInfo( 316 result = self._IdentifyDevToolsSocketConnectionInfo(
315 url, tab_index, tab_filter) 317 url, tab_index, tab_filter)
316 self._client = _DevToolsSocketClient( 318 self._client = _DevToolsSocketClient(
317 verbose, show_socket_messages, result['host'], result['port'], 319 verbose, show_socket_messages, result['host'], result['port'],
318 result['path']) 320 result['path'])
319 self._client.inspector_thread = self 321 self._client.inspector_thread = self
320 while asyncore.socket_map: 322 while asyncore.socket_map:
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 return request 506 return request
505 return None 507 return None
506 508
507 def _FillInParams(self, request): 509 def _FillInParams(self, request):
508 """Fills in parameters for requests as necessary before the request is sent. 510 """Fills in parameters for requests as necessary before the request is sent.
509 511
510 Args: 512 Args:
511 request: The _DevToolsSocketRequest object associated with a request 513 request: The _DevToolsSocketRequest object associated with a request
512 message that is about to be sent. 514 message that is about to be sent.
513 """ 515 """
514 if request.method == 'Profiler.takeHeapSnapshot': 516 if request.method == self._agent_name +'.takeHeapSnapshot':
515 # We always want detailed v8 heap snapshot information. 517 # We always want detailed v8 heap snapshot information.
516 request.params = {'detailed': True} 518 request.params = {'detailed': True}
517 elif request.method == 'Profiler.getHeapSnapshot': 519 elif request.method == self._agent_name + '.getHeapSnapshot':
518 # To actually request the snapshot data from a previously-taken snapshot, 520 # To actually request the snapshot data from a previously-taken snapshot,
519 # we need to specify the unique uid of the snapshot we want. 521 # we need to specify the unique uid of the snapshot we want.
520 # The relevant uid should be contained in the last 522 # The relevant uid should be contained in the last
521 # 'Profiler.takeHeapSnapshot' request object. 523 # 'Profiler.takeHeapSnapshot' request object.
522 last_req = self._GetLatestRequestOfType(request, 524 last_req = self._GetLatestRequestOfType(request,
523 'Profiler.takeHeapSnapshot') 525 self._agent_name + '.takeHeapSnapshot')
524 if last_req and 'uid' in last_req.results: 526 if last_req and 'uid' in last_req.results:
525 request.params = {'uid': last_req.results['uid']} 527 request.params = {'uid': last_req.results['uid']}
526 elif request.method == 'Profiler.getProfile': 528 elif request.method == self._agent_name + '.getProfile':
527 # TODO(eustas): Remove this case after M27 is released. 529 # TODO(eustas): Remove this case after M27 is released.
528 last_req = self._GetLatestRequestOfType(request, 530 last_req = self._GetLatestRequestOfType(request,
529 'Profiler.takeHeapSnapshot') 531 self._agent_name + '.takeHeapSnapshot')
530 if last_req and 'uid' in last_req.results: 532 if last_req and 'uid' in last_req.results:
531 request.params = {'type': 'HEAP', 'uid': last_req.results['uid']} 533 request.params = {'type': 'HEAP', 'uid': last_req.results['uid']}
532 534
533 @staticmethod 535 @staticmethod
534 def _IdentifyDevToolsSocketConnectionInfo(url, tab_index, tab_filter): 536 def _IdentifyDevToolsSocketConnectionInfo(url, tab_index, tab_filter):
535 """Identifies DevToolsSocket connection info from a remote Chrome instance. 537 """Identifies DevToolsSocket connection info from a remote Chrome instance.
536 538
537 Args: 539 Args:
538 url: The base URL to connent to. 540 url: The base URL to connent to.
539 tab_index: The integer index of the tab in the remote Chrome instance to 541 tab_index: The integer index of the tab in the remote Chrome instance to
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 851
850 # Creating _RemoteInspectorThread might raise an exception. This prevents an 852 # Creating _RemoteInspectorThread might raise an exception. This prevents an
851 # AttributeError in the destructor. 853 # AttributeError in the destructor.
852 self._remote_inspector_thread = None 854 self._remote_inspector_thread = None
853 self._remote_inspector_driver_thread = None 855 self._remote_inspector_driver_thread = None
854 856
855 # TODO(dennisjeffrey): Do not assume port 9222. The port should be passed 857 # TODO(dennisjeffrey): Do not assume port 9222. The port should be passed
856 # as input to this function. 858 # as input to this function.
857 url = 'http://localhost:9222' 859 url = 'http://localhost:9222'
858 860
859 self._webkit_version = self._GetWebkitVersion(url) 861 self._version = self._GetVersion(url)
862
863 # TODO(loislo): Remove this hack after M28 is released.
864 self._agent_name = 'Profiler'
865 if self._IsBrowserDayNumberMoreThan(1470):
866 self._agent_name = 'HeapProfiler'
860 867
861 # Start up a thread for long-term communication with the remote inspector. 868 # Start up a thread for long-term communication with the remote inspector.
862 self._remote_inspector_thread = _RemoteInspectorThread( 869 self._remote_inspector_thread = _RemoteInspectorThread(
863 url, tab_index, tab_filter, verbose, show_socket_messages) 870 url, tab_index, tab_filter, verbose, show_socket_messages,
871 self._agent_name)
864 self._remote_inspector_thread.start() 872 self._remote_inspector_thread.start()
865 # At this point, a connection has already been made to the remote inspector. 873 # At this point, a connection has already been made to the remote inspector.
866 874
867 # This thread calls asyncore.loop, which activates the channel service. 875 # This thread calls asyncore.loop, which activates the channel service.
868 self._remote_inspector_driver_thread = _RemoteInspectorDriverThread() 876 self._remote_inspector_driver_thread = _RemoteInspectorDriverThread()
869 self._remote_inspector_driver_thread.start() 877 self._remote_inspector_driver_thread.start()
870 878
871 def __del__(self): 879 def __del__(self):
872 """Called on destruction of this object.""" 880 """Called on destruction of this object."""
873 self.Stop() 881 self.Stop()
(...skipping 17 matching lines...) Expand all
891 { 899 {
892 'url': string, # URL of the webpage that was snapshotted. 900 'url': string, # URL of the webpage that was snapshotted.
893 'raw_data': string, # The raw data as JSON string. 901 'raw_data': string, # The raw data as JSON string.
894 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. 902 'total_v8_node_count': integer, # Total number of nodes in the v8 heap.
895 # Only if |include_summary| is True. 903 # Only if |include_summary| is True.
896 'total_heap_size': integer, # Total v8 heap size (number of bytes). 904 'total_heap_size': integer, # Total v8 heap size (number of bytes).
897 # Only if |include_summary| is True. 905 # Only if |include_summary| is True.
898 } 906 }
899 """ 907 """
900 # TODO(eustas): Remove this hack after M27 is released. 908 # TODO(eustas): Remove this hack after M27 is released.
901 if self._IsWebkitVersionNotOlderThan(537, 27): 909 if self._IsPlatformVersionNotOlderThan(537, 27):
902 get_heap_snapshot_method = 'Profiler.getHeapSnapshot' 910 get_heap_snapshot_method = '.getHeapSnapshot'
903 else: 911 else:
904 get_heap_snapshot_method = 'Profiler.getProfile' 912 get_heap_snapshot_method = '.getProfile'
905 913
906 HEAP_SNAPSHOT_MESSAGES = [ 914 HEAP_SNAPSHOT_MESSAGES = [
907 ('Page.getResourceTree', {}), 915 ('Page.getResourceTree', {}),
908 ('Debugger.enable', {}), 916 ('Debugger.enable', {}),
909 ('Profiler.clearProfiles', {}), 917 (self._agent_name + '.clearProfiles', {}),
910 ('Profiler.takeHeapSnapshot', {}), 918 (self._agent_name + '.takeHeapSnapshot', {}),
911 (get_heap_snapshot_method, {}), 919 (self._agent_name + get_heap_snapshot_method, {}),
912 ] 920 ]
913 921
914 self._current_heap_snapshot = [] 922 self._current_heap_snapshot = []
915 self._url = '' 923 self._url = ''
916 self._collected_heap_snapshot_data = {} 924 self._collected_heap_snapshot_data = {}
917 925
918 done_condition = threading.Condition() 926 done_condition = threading.Condition()
919 927
920 def HandleReply(reply_dict): 928 def HandleReply(reply_dict):
921 """Processes a reply message received from the remote Chrome instance. 929 """Processes a reply message received from the remote Chrome instance.
922 930
923 Args: 931 Args:
924 reply_dict: A dictionary object representing the reply message received 932 reply_dict: A dictionary object representing the reply message received
925 from the remote inspector. 933 from the remote inspector.
926 """ 934 """
927 if 'result' in reply_dict: 935 if 'result' in reply_dict:
928 # This is the result message associated with a previously-sent request. 936 # This is the result message associated with a previously-sent request.
929 request = self._remote_inspector_thread.GetRequestWithId( 937 request = self._remote_inspector_thread.GetRequestWithId(
930 reply_dict['id']) 938 reply_dict['id'])
931 if 'frameTree' in reply_dict['result']: 939 if 'frameTree' in reply_dict['result']:
932 self._url = reply_dict['result']['frameTree']['frame']['url'] 940 self._url = reply_dict['result']['frameTree']['frame']['url']
933 elif 'method' in reply_dict: 941 elif 'method' in reply_dict:
934 # This is an auxiliary message sent from the remote Chrome instance. 942 # This is an auxiliary message sent from the remote Chrome instance.
935 if reply_dict['method'] == 'Profiler.addProfileHeader': 943 if reply_dict['method'] == self._agent_name + '.addProfileHeader':
936 snapshot_req = ( 944 snapshot_req = (
937 self._remote_inspector_thread.GetFirstUnfulfilledRequest( 945 self._remote_inspector_thread.GetFirstUnfulfilledRequest(
938 'Profiler.takeHeapSnapshot')) 946 self._agent_name + '.takeHeapSnapshot'))
939 if snapshot_req: 947 if snapshot_req:
940 snapshot_req.results['uid'] = reply_dict['params']['header']['uid'] 948 snapshot_req.results['uid'] = reply_dict['params']['header']['uid']
941 elif reply_dict['method'] == 'Profiler.addHeapSnapshotChunk': 949 elif reply_dict['method'] == self._agent_name + '.addHeapSnapshotChunk':
942 self._current_heap_snapshot.append(reply_dict['params']['chunk']) 950 self._current_heap_snapshot.append(reply_dict['params']['chunk'])
943 elif reply_dict['method'] == 'Profiler.finishHeapSnapshot': 951 elif reply_dict['method'] == self._agent_name + '.finishHeapSnapshot':
944 # A heap snapshot has been completed. Analyze and output the data. 952 # A heap snapshot has been completed. Analyze and output the data.
945 self._logger.debug('Heap snapshot taken: %s', self._url) 953 self._logger.debug('Heap snapshot taken: %s', self._url)
946 # TODO(dennisjeffrey): Parse the heap snapshot on-the-fly as the data 954 # TODO(dennisjeffrey): Parse the heap snapshot on-the-fly as the data
947 # is coming in over the wire, so we can avoid storing the entire 955 # is coming in over the wire, so we can avoid storing the entire
948 # snapshot string in memory. 956 # snapshot string in memory.
949 raw_snapshot_data = ''.join(self._current_heap_snapshot) 957 raw_snapshot_data = ''.join(self._current_heap_snapshot)
950 self._collected_heap_snapshot_data = { 958 self._collected_heap_snapshot_data = {
951 'url': self._url, 959 'url': self._url,
952 'raw_data': raw_snapshot_data} 960 'raw_data': raw_snapshot_data}
953 if include_summary: 961 if include_summary:
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
1274 A human-readable string representation of the given number of bytes. 1282 A human-readable string representation of the given number of bytes.
1275 """ 1283 """
1276 if num_bytes < 1024: 1284 if num_bytes < 1024:
1277 return '%d B' % num_bytes 1285 return '%d B' % num_bytes
1278 elif num_bytes < 1048576: 1286 elif num_bytes < 1048576:
1279 return '%.2f KB' % (num_bytes / 1024.0) 1287 return '%.2f KB' % (num_bytes / 1024.0)
1280 else: 1288 else:
1281 return '%.2f MB' % (num_bytes / 1048576.0) 1289 return '%.2f MB' % (num_bytes / 1048576.0)
1282 1290
1283 @staticmethod 1291 @staticmethod
1284 def _GetWebkitVersion(endpoint): 1292 def _GetVersion(endpoint):
1285 """Fetches Webkit version information from a remote Chrome instance. 1293 """Fetches version information from a remote Chrome instance.
1286 1294
1287 Args: 1295 Args:
1288 endpoint: The base URL to connent to. 1296 endpoint: The base URL to connent to.
1289 1297
1290 Returns: 1298 Returns:
1291 A dictionary containing Webkit version information: 1299 A dictionary containing Brownser and Content version information:
dennis_jeffrey 2013/04/10 17:21:34 Brownser --> Browser
1292 { 1300 {
1293 'major': integer, 1301 'Browser': {
1294 'minor': integer, 1302 'major': integer,
1303 'minor': integer,
1304 'fix': integer,
1305 'day': integer
1306 },
1307 'Content': {
1308 'name': string,
1309 'major': integer,
1310 'minor': integer
1311 }
1295 } 1312 }
1296 1313
1297 Raises: 1314 Raises:
1298 RuntimeError: When Webkit version info can't be fetched or parsed. 1315 RuntimeError: When Browser version info can't be fetched or parsed.
1299 """ 1316 """
1300 try: 1317 try:
1301 f = urllib2.urlopen(endpoint + '/json/version') 1318 f = urllib2.urlopen(endpoint + '/json/version')
1302 result = f.read(); 1319 result = f.read();
1303 result = simplejson.loads(result) 1320 result = simplejson.loads(result)
1304 except urllib2.URLError, e: 1321 except urllib2.URLError, e:
1305 raise RuntimeError( 1322 raise RuntimeError(
1306 'Error accessing Chrome instance debugging port: ' + str(e)) 1323 'Error accessing Chrome instance debugging port: ' + str(e))
1307 1324
1325 if 'Browser' not in result:
1326 raise RuntimeError('Browser version is not specified.')
1327
1328 parsed = re.search('^Chrome\/(\d+).(\d+).(\d+).(\d+)', result['Browser'])
1329 if parsed is None:
1330 raise RuntimeError('Browser-Version cannot be parsed.')
1331 try:
1332 day = int(parsed.group(3))
1333 browser_info = {
1334 'major': int(parsed.group(1)),
1335 'minor': int(parsed.group(2)),
1336 'day': day,
1337 'fix': int(parsed.group(4)),
1338 }
1339 except ValueError:
1340 raise RuntimeError('Browser-Version cannot be parsed.')
1341
1308 if 'WebKit-Version' not in result: 1342 if 'WebKit-Version' not in result:
1309 raise RuntimeError('WebKit-Version is not specified.') 1343 raise RuntimeError('Content-Version is not specified.')
1310 1344
1311 parsed = re.search('^(\d+)\.(\d+)', result['WebKit-Version']) 1345 parsed = re.search('^(\d+)\.(\d+)', result['WebKit-Version'])
1312 if parsed is None: 1346 if parsed is None:
1313 raise RuntimeError('WebKit-Version cannot be parsed.') 1347 raise RuntimeError('Content-Version cannot be parsed.')
1314 1348
1315 try: 1349 try:
1316 info = { 1350 platform_info = {
1351 'name': 'Blink' if day > 1464 else 'WebKit',
1317 'major': int(parsed.group(1)), 1352 'major': int(parsed.group(1)),
1318 'minor': int(parsed.group(2)), 1353 'minor': int(parsed.group(2)),
1319 } 1354 }
1320 except ValueError: 1355 except ValueError:
1321 raise RuntimeError('WebKit-Version cannot be parsed.') 1356 raise RuntimeError('WebKit-Version cannot be parsed.')
1322 1357
1323 return info 1358 return {
1359 'browser': browser_info,
1360 'platform': platform_info
1361 }
1324 1362
1325 def _IsWebkitVersionNotOlderThan(self, major, minor): 1363 def _IsContentVersionNotOlderThan(self, major, minor):
1326 """Compares remote Webkit version with specified one. 1364 """Compares remote Browser Content version with specified one.
1327 1365
1328 Args: 1366 Args:
1329 major: Major Webkit version. 1367 major: Major Webkit version.
1330 minor: Minor Webkit version. 1368 minor: Minor Webkit version.
1331 1369
1332 Returns: 1370 Returns:
1333 True if remote Webkit version is same or newer than specified, 1371 True if remote Content version is same or newer than specified,
1334 False otherwise. 1372 False otherwise.
1335 1373
1336 Raises: 1374 Raises:
1337 RuntimeError: If remote Webkit version hasn't been fetched yet. 1375 RuntimeError: If remote Content version hasn't been fetched yet.
1338 """ 1376 """
1339 if not hasattr(self, '_webkit_version'): 1377 if not hasattr(self, '_version'):
1340 raise RuntimeError('WebKit version has not been fetched yet.') 1378 raise RuntimeError('Browser version has not been fetched yet.')
1341 version = self._webkit_version 1379 version = self._version['platform']
1342 1380
1343 if version['major'] < major: 1381 if version['major'] < major:
1344 return False 1382 return False
1345 elif version['major'] == major and version['minor'] < minor: 1383 elif version['major'] == major and version['minor'] < minor:
1346 return False 1384 return False
1347 else: 1385 else:
1348 return True 1386 return True
1387
1388 def _IsBrowserDayNumberMoreThan(self, day_number):
1389 """Compares remote Chromium day number with specified one.
1390
1391 Args:
1392 day_number: Forth part of the chromium version.
1393
1394 Returns:
1395 True if remote Chromium day number is same or newer than specified,
1396 False otherwise.
1397
1398 Raises:
1399 RuntimeError: If remote Chromium version hasn't been fetched yet.
1400 """
1401 if not hasattr(self, '_version'):
1402 raise RuntimeError('Browser revision has not been fetched yet.')
1403 version = self._version['browser']
1404
1405 return version['day'] > day_number
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698