chromium/chrome/test/chromedriver/embed_mobile_devices_in_cpp.py

#!/usr/bin/env python3
# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Embeds standalone JavaScript snippets in C++ code.

The script requires the Source/devtools/front_end/emulated_devices/module.json
file from Blink that lists the known mobile devices to be passed in as the only
argument.  The list of known devices will be written to a C-style string to be
parsed with JSONReader.
"""

import ast
import json
import optparse
import os
import sys

import chrome_paths
import cpp_source

_EMULATED_DEVICES_BEGIN = '// DEVICE-LIST-BEGIN'
_EMULATED_DEVICES_END = '// DEVICE-LIST-END'
_EMULATED_DEVICES_IF = '/* DEVICE-LIST-IF-JS */'
_EMULATED_DEVICES_ELSE = '/* DEVICE-LIST-ELSE'
_EMULATED_DEVICES_ENDIF = 'DEVICE-LIST-END-IF */'

def main():
  parser = optparse.OptionParser()
  parser.add_option(
      '', '--version-file', type='string',
      default=os.path.join(chrome_paths.GetSrc(), 'chrome', 'VERSION'),
      help='Path to Chrome version file')
  parser.add_option(
      '', '--directory', type='string', default='.',
      help='Path to directory where the cc/h files should be created')
  options, args = parser.parse_args()

  # The device userAgent string may contain '%s', which should be replaced with
  # current Chrome version. First we read the version file.
  version_parts = ['MAJOR', 'MINOR', 'BUILD', 'PATCH']
  version = []
  version_file = open(options.version_file, 'r', encoding='utf-8')
  for part in version_parts:
    # The version file should have 4 lines, with format like MAJOR=63
    components = version_file.readline().split('=')
    if len(components) != 2 or components[0].strip() != part:
      print('Bad version file')
      return 1
    version.append(components[1].strip())
  # Join parts of version together using '.' as separator
  version = '.'.join(version)

  devices = {}
  file_name = args[0]
  with open(file_name, 'r', encoding='utf-8') as f:
    data = f.read()

    # Extract the list from the source file.
    begin_position = data.find(_EMULATED_DEVICES_BEGIN)
    end_position = data.find(_EMULATED_DEVICES_END)
    if begin_position == -1 or end_position == -1:
      print('Could not find list of emulatedDevices in %s' % file_name)
      return 1
    begin_position += len(_EMULATED_DEVICES_BEGIN)
    list_string = '[' + data[begin_position:end_position] + ']'

    # Only used the non-localized strings in the list.
    if_position = list_string.find(_EMULATED_DEVICES_IF)
    while if_position != -1:
      else_position = list_string.find(_EMULATED_DEVICES_ELSE)
      if else_position == -1:
        print('Could not find list of emulatedDevices in %s' % file_name)
        return 1
      else_position += len(_EMULATED_DEVICES_ELSE)
      list_string = list_string[0:if_position] + list_string[else_position::]

      endif_position = list_string.find(_EMULATED_DEVICES_ENDIF)
      if endif_position == -1:
        print('Could not find list of emulatedDevices in %s' % file_name)
        return 1
      list_string = list_string[0:endif_position] + \
          list_string[endif_position + len(_EMULATED_DEVICES_ENDIF)::]

      if_position = list_string.find(_EMULATED_DEVICES_IF)

    # Do a bunch of substitutions to get something parseable by Python.
    list_string = list_string.replace('true', 'True')
    list_string = list_string.replace('false', 'False')
    emulated_devices = ast.literal_eval(list_string)
  for device in emulated_devices:
    title = device['title']
    titles = [title]
    # For 'iPhone 6/7/8', also add ['iPhone 6', 'iPhone 7', 'iPhone 8'] for
    # backward compatibility.
    if '/' in title:
      words = title.split()
      for i in range(len(words)):
        if '/' in words[i]:
          # Only support one word containing '/'
          break
      tokens = words[i].split('/')
      for token in tokens:
        words[i] = token
        titles.append(' '.join(words))
    for title in titles:
      mobile_emulation = {
        'userAgent': device['user-agent'],
        'deviceMetrics': {
          'width': device['screen']['vertical']['width'],
          'height': device['screen']['vertical']['height'],
          'deviceScaleFactor': device['screen']['device-pixel-ratio'],
          'touch': 'touch' in device['capabilities'],
          'mobile': 'mobile' in device['capabilities'],
        },
        'type': device['type']
      }
      if 'user-agent-metadata' in device:
        client_hints = device['user-agent-metadata']
        mobile_emulation['clientHints'] = {
            'architecture': client_hints['architecture'],
            'bitness': '',
            'platform': client_hints['platform'],
            'platformVersion': client_hints['platformVersion'],
            'model': client_hints['model'],
            'mobile': client_hints['mobile'],
            'wow64': False,
        }
      devices[title] = mobile_emulation

  output_dir = 'chrome/test/chromedriver/chrome'
  cpp_source.WriteSource('mobile_device_list',
                         output_dir,
                         options.directory,
                         {'kMobileDevices': json.dumps(devices)})

if __name__ == '__main__':
  sys.exit(main())