Skip to content

Commit 4832727

Browse files
authored
Merge branch 'master' into keyPress
2 parents 2a96477 + 2bfce8b commit 4832727

File tree

10 files changed

+230
-58
lines changed

10 files changed

+230
-58
lines changed

internal/chrome_desktop.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
'MediaRouter',
6969
'OfflinePagesPrefetching',
7070
'OptimizationHints',
71+
'SidePanelPinning',
7172
'Translate',
7273
# Disable noisy Edge features
7374
'msAutofillEdgeCoupons',

internal/devtools.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,7 +1362,35 @@ def type_text(self, string):
13621362
self.send_character(char)
13631363
except Exception:
13641364
logging.exception('Error running type command')
1365+
def mouse_press(self, command_options):
1366+
"""Press down the mouse"""
1367+
params = {
1368+
'type': 'mousePressed',
1369+
'x': command_options['x'],
1370+
'y': command_options['y'],
1371+
'button': command_options['button'],
1372+
'clickCount': command_options['clickCount']
1373+
}
1374+
self.send_command('Input.dispatchMouseEvent', params)
1375+
1376+
def mouse_release(self, command_options):
1377+
"""Let up the mouse"""
1378+
self.send_command('Input.dispatchMouseEvent', {
1379+
'type': 'mouseReleased',
1380+
'x': command_options['x'],
1381+
'y': command_options['y'],
1382+
'button': command_options['button'],
1383+
'clickCount': command_options['clickCount']
1384+
})
13651385

1386+
def mouse_click(self, params):
1387+
"""Simulate pressing the mouse"""
1388+
try:
1389+
self.mouse_press(params)
1390+
self.mouse_release(params)
1391+
except Exception:
1392+
logging.exception('Error running mouse click command')
1393+
13661394
def enable_target(self, target_id=None):
13671395
"""Hook up the necessary network (or other) events for the given target"""
13681396
try:

internal/devtools_browser.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,43 @@ def process_command(self, command):
777777
if keyModifier in KeyModifiers.keys():
778778
modifier = KeyModifiers[keyModifier]
779779
self.devtools.keypress(command['target'], modifier)
780+
elif command['command'] == 'mouseClick':
781+
if 'target' in command:
782+
target = command['target']
783+
separator = target.find('=')
784+
if separator == -1:
785+
separator = target.find("'")
786+
if separator >= 0:
787+
attribute = target[:separator]
788+
attr_value = target[separator + 1:]
789+
try:
790+
query = "JSON.stringify(document.querySelector('[{0}=\"{1}\"]').getBoundingClientRect())".format(
791+
attribute, attr_value)
792+
resp = self.devtools.execute_js(query, use_execution_context = True)
793+
resp_json = json.loads(resp)
794+
795+
value = command['value']
796+
button = 'left'
797+
clickCount = 1
798+
if value in ['left', 'right']:
799+
button = value
800+
elif value == 'double':
801+
clickCount = 2
802+
elif value is not None:
803+
logging.info("Click type is not defined.")
804+
805+
if 'x' in resp_json and 'y' in resp_json and 'width' in resp_json and 'height' in resp_json:
806+
x = int(float(resp_json['x'])) + int(float(resp_json['width']))/2
807+
y = int(float(resp_json['y'])) + int(float(resp_json['height']))/2
808+
command_options = {}
809+
command_options['x'] = x
810+
command_options['y'] = y
811+
command_options['button'] = button
812+
command_options['clickCount'] = clickCount
813+
self.devtools.mouse_click(command_options)
814+
except:
815+
self.task['error'] = 'Exception parsing mouseClick arguments.'
816+
logging.error(self.task['error'])
780817
elif command['command'] == 'waitfor':
781818
try:
782819
self.devtools.wait_for_script = command['target'] if command['target'] else None
@@ -865,7 +902,12 @@ def run_lighthouse_test(self, task):
865902
else:
866903
cpu_throttle = '{:.3f}'.format(self.job['throttle_cpu']) if 'throttle_cpu' in self.job else '1'
867904
if self.job['dtShaper']:
868-
command.extend(['--throttling-method', 'devtools', '--throttling.requestLatencyMs', '150', '--throttling.downloadThroughputKbps', '1600', '--throttling.uploadThroughputKbps', '768', '--throttling.cpuSlowdownMultiplier', cpu_throttle])
905+
if self.options.android or ('mobile' in self.job and self.job['mobile']):
906+
# 1.6Mbps down, 750Kbps up, 150ms RTT
907+
command.extend(['--throttling-method', 'devtools', '--throttling.requestLatencyMs', '150', '--throttling.downloadThroughputKbps', '1600', '--throttling.uploadThroughputKbps', '750', '--throttling.cpuSlowdownMultiplier', cpu_throttle])
908+
else:
909+
# 10Mbps, 40ms RTT
910+
command.extend(['--throttling-method', 'devtools', '--throttling.requestLatencyMs', '40', '--throttling.downloadThroughputKbps', '10240', '--throttling.uploadThroughputKbps', '10240', '--throttling.cpuSlowdownMultiplier', cpu_throttle])
869911
elif 'throttle_cpu_requested' in self.job and self.job['throttle_cpu_requested'] > 1:
870912
command.extend(['--throttling-method', 'devtools', '--throttling.requestLatencyMs', '0', '--throttling.downloadThroughputKbps', '0', '--throttling.uploadThroughputKbps', '0', '--throttling.cpuSlowdownMultiplier', cpu_throttle])
871913
else:

internal/firefox.py

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import json
3232
from .desktop_browser import DesktopBrowser
3333

34+
def _get_location_uri(accuracy, lat, lng) -> str:
35+
return f'data:application/json, {{ "status":"OK", "accuracy":{accuracy}, "location":{{ "lat":{lat}, "lng":{lng} }} }}'
3436

3537
class Firefox(DesktopBrowser):
3638
"""Firefox"""
@@ -140,26 +142,47 @@ def start_firefox(self, job, task):
140142
return
141143
from selenium import webdriver # pylint: disable=import-error
142144

143-
capabilities = webdriver.DesiredCapabilities.FIREFOX.copy()
144-
if 'ignoreSSL' in job and job['ignoreSSL']:
145-
capabilities['acceptInsecureCerts'] = True
146-
else:
147-
capabilities['acceptInsecureCerts'] = False
145+
if webdriver.__version__ >= "4.12":
146+
service_args = ["--marionette-port", "2828"]
147+
service = webdriver.FirefoxService(service_args=service_args, log_output=os.environ["MOZ_LOG_FILE"])
148148

149-
capabilities['moz:firefoxOptions'] = {
150-
'binary': self.path,
151-
'args': ['-profile', task['profile']],
152-
'prefs': self.prepare_prefs(),
153-
"log": {"level": "error"},
154-
'env': {
155-
"MOZ_LOG_FILE": os.environ["MOZ_LOG_FILE"],
156-
"MOZ_LOG": os.environ["MOZ_LOG"]
157-
}
158-
}
159-
service_args = ["--marionette-port", "2828"]
149+
options = webdriver.FirefoxOptions()
150+
options.binary_location = self.path
151+
options.add_argument('--profile')
152+
options.add_argument(f'{task["profile"]}')
153+
options.log.level = 'error'
154+
options.prefs = self.prepare_prefs()
160155

161-
self.driver = webdriver.Firefox(desired_capabilities=capabilities, service_args=service_args)
162-
logging.debug(self.driver.capabilities)
156+
capabilities = webdriver.DesiredCapabilities.FIREFOX.copy()
157+
if 'ignoreSSL' in job and job['ignoreSSL']:
158+
capabilities['acceptInsecureCerts'] = True
159+
else:
160+
capabilities['acceptInsecureCerts'] = False
161+
162+
for key, value in capabilities.items():
163+
options.set_capability(key, value)
164+
self.driver = webdriver.Firefox(options=options, service=service)
165+
elif webdriver.__version__ <= "4.9":
166+
capabilities = webdriver.DesiredCapabilities.FIREFOX.copy()
167+
if 'ignoreSSL' in job and job['ignoreSSL']:
168+
capabilities['acceptInsecureCerts'] = True
169+
else:
170+
capabilities['acceptInsecureCerts'] = False
171+
172+
capabilities['moz:firefoxOptions'] = {
173+
'binary': self.path,
174+
'args': ['-profile', task['profile']],
175+
'prefs': self.prepare_prefs(),
176+
"log": {"level": "error"},
177+
'env': {
178+
"MOZ_LOG_FILE": os.environ["MOZ_LOG_FILE"],
179+
"MOZ_LOG": os.environ["MOZ_LOG"]
180+
}
181+
}
182+
service_args = ["--marionette-port", "2828"]
183+
self.driver = webdriver.Firefox(desired_capabilities=capabilities, service_args=service_args)
184+
else:
185+
raise Exception("Unsupported selenium version %s", webdriver.__version__)
163186

164187
self.driver.set_page_load_timeout(task['time_limit'])
165188
if 'browserVersion' in self.driver.capabilities:
@@ -208,17 +231,13 @@ def launch(self, job, task):
208231
ua_string += ' ' + task['AppendUA']
209232
modified = True
210233
if modified:
211-
logging.debug(ua_string)
212234
self.driver_set_pref('general.useragent.override', ua_string)
213235
# Location
214236
if 'lat' in self.job and 'lng' in self.job:
215237
try:
216238
lat = float(str(self.job['lat']))
217239
lng = float(str(self.job['lng']))
218-
location_uri = 'data:application/json,{{'\
219-
'"status":"OK","accuracy":10.0,'\
220-
'"location":{{"lat":{0:f},"lng":{1:f}}}'\
221-
'}}'.format(lat, lng)
240+
location_uri = _get_location_uri(10, lat, lng)
222241
logging.debug('Setting location: %s', location_uri)
223242
self.driver_set_pref('geo.wifi.uri', location_uri)
224243
except Exception:
@@ -261,20 +280,12 @@ def driver_set_pref(self, key, value):
261280
"""Set a Firefox pref at runtime"""
262281
if self.driver is not None:
263282
try:
264-
script = 'const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");'
265-
script += 'Services.prefs.'
266-
if isinstance(value, bool):
267-
script += 'setBoolPref'
268-
elif isinstance(value, (str, unicode)):
269-
script += 'setStringPref'
270-
else:
271-
script += 'setIntPref'
272-
script += '({0}, {1});'.format(json.dumps(key), json.dumps(value))
273-
logging.debug(script)
283+
script = 'const { Preferences } = ChromeUtils.importESModule("resource://gre/modules/Preferences.sys.mjs");'
284+
script += f'Preferences.set({json.dumps(key)}, {json.dumps(value)});'
274285
self.driver.set_context(self.driver.CONTEXT_CHROME)
275286
self.driver.execute_script(script)
276-
except Exception:
277-
logging.exception("Error setting pref")
287+
except Exception as err:
288+
logging.exception("Error setting pref %s => %s: %s", key, value, err)
278289
finally:
279290
self.driver.set_context(self.driver.CONTEXT_CONTENT)
280291

@@ -300,6 +311,8 @@ def close_browser(self, job, task):
300311
if platform.system() == "Linux":
301312
subprocess.call(['killall', '-9', 'firefox'])
302313
subprocess.call(['killall', '-9', 'firefox-trunk'])
314+
subprocess.call(['killall', '-9', 'firefox-nightly'])
315+
subprocess.call(['killall', '-9', 'firefox-esr'])
303316
os.environ["MOZ_LOG_FILE"] = ''
304317
os.environ["MOZ_LOG"] = ''
305318

@@ -334,7 +347,7 @@ def run_axe(self, task):
334347
script += "'" + "', '".join(axe_cats) + "'"
335348
script += ']}).then(results=>{return results;});'
336349
except Exception as err:
337-
logging.exception("Exception running Axe: %s", err.__str__())
350+
logging.exception("Exception running Axe: %s", err)
338351
if self.must_exit_now:
339352
return
340353
completed = False
@@ -357,7 +370,7 @@ def run_axe(self, task):
357370
axe_info['incomplete'] = axe_results['incomplete']
358371
task['page_data']['axe'] = axe_info
359372
except Exception as err:
360-
logging.exception("Exception running Axe: %s", err.__str__())
373+
logging.exception("Exception running Axe: %s", err)
361374
if not completed:
362375
task['page_data']['axe_failed'] = 1
363376
self.axe_time = monotonic() - start
@@ -384,7 +397,7 @@ def run_task(self, task):
384397
logging.exception("Exception running task")
385398
if command['record']:
386399
self.wait_for_page_load()
387-
if not task['combine_steps'] or not len(task['script']):
400+
if not task['combine_steps'] or not task['script']:
388401
self.on_stop_capture(task)
389402
self.on_stop_recording(task)
390403
recording = False
@@ -405,10 +418,9 @@ def run_task(self, task):
405418
self.task = None
406419

407420
def alert_size(self, _alert_config, _task_dir, _prefix):
408-
'''Checks the agents file size and alert on certain percentage over avg byte size'''
421+
'''Checks the agents file size and alert on certain percentage over avg byte size'''
409422
self.alert_desktop_results(_alert_config, 'Firefox', _task_dir, _prefix)
410423

411-
412424
def wait_for_extension(self):
413425
"""Wait for the extension to send the started message"""
414426
if self.job['message_server'] is not None:
@@ -514,7 +526,7 @@ def run_js_file(self, file_name):
514526
script = None
515527
script_file_path = os.path.join(self.script_dir, file_name)
516528
if os.path.isfile(script_file_path):
517-
with open(script_file_path, 'r') as script_file:
529+
with open(script_file_path, 'r', encoding='utf-8') as script_file:
518530
script = script_file.read()
519531
if self.driver is not None and script is not None:
520532
try:
@@ -526,7 +538,7 @@ def run_js_file(self, file_name):
526538
logging.debug(ret)
527539
return ret
528540

529-
def get_sorted_requests_json(self, include_bodies):
541+
def get_sorted_requests_json(self, _include_bodies):
530542
return 'null'
531543

532544
def collect_browser_metrics(self, task):
@@ -970,10 +982,7 @@ def process_command(self, command):
970982
parts = command['target'].split(',')
971983
lat = float(parts[0])
972984
lng = float(parts[1])
973-
location_uri = 'data:application/json,{{'\
974-
'"status":"OK","accuracy":{2:d},'\
975-
'"location":{{"lat":{0:f},"lng":{1:f}}}'\
976-
'}}'.format(lat, lng, accuracy)
985+
location_uri = _get_location_uri(accuracy, lat, lng)
977986
logging.debug('Setting location: %s', location_uri)
978987
self.set_pref('geo.wifi.uri', location_uri)
979988
except Exception:

internal/optimization_checks.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ def __init__(self, job, task, requests):
304304
'.cdn.dnsv1.com.cn',
305305
'.dsa.dnsv1.com',
306306
'.dsa.dnsv1.com.cn'],
307+
'Transparent Edge': ['.edge2befaster.io',
308+
'.edge2befaster.net',
309+
'.edgetcdn.io',
310+
'.edgetcdn.net'],
307311
'TRBCDN': ['.trbcdn.net'],
308312
'Twitter': ['.twimg.com'],
309313
'UnicornCDN': ['.unicorncdn.net'],

internal/support/firefox_log_parser.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,14 @@ def socket_thread_http_entry(self, msg):
296296
socket = self.http['current_socket']
297297
self.http['connections'][connection] = {'socket': socket}
298298
del self.http['current_socket']
299+
elif msg['message'].startswith('TlsHandshaker::SetupSSL '):
300+
match = re.search(r'^TlsHandshaker::SetupSSL (?P<connection>[\w\d]+)',
301+
msg['message'])
302+
if match:
303+
connection = match.groupdict().get('connection')
304+
if connection in self.http['connections']:
305+
if 'ssl_start' not in self.http['connections'][connection]:
306+
self.http['connections'][connection]['ssl_start'] = msg['timestamp']
299307
elif msg['message'].startswith('nsHttpConnection::SetupSSL '):
300308
match = re.search(r'^nsHttpConnection::SetupSSL (?P<connection>[\w\d]+)',
301309
msg['message'])
@@ -332,6 +340,17 @@ def socket_thread_http_entry(self, msg):
332340
if byte_count > 0 and trans_id in self.http['requests'] and \
333341
'start' not in self.http['requests'][trans_id]:
334342
self.http['requests'][trans_id]['start'] = msg['timestamp']
343+
elif msg['message'].startswith('nsHttpTransaction::OnSocketStatus ') and \
344+
msg['message'].find(' status=4b0005 progress=') > -1:
345+
match = re.search(r'^nsHttpTransaction::OnSocketStatus '
346+
r'\[this=(?P<id>[\w\d]+) status=4b0005 progress=(?P<bytes>[\d+]+)',
347+
msg['message'])
348+
if match:
349+
trans_id = match.groupdict().get('id')
350+
byte_count = int(match.groupdict().get('bytes'))
351+
if byte_count > 0 and trans_id in self.http['requests'] and \
352+
'start' not in self.http['requests'][trans_id]:
353+
self.http['requests'][trans_id]['start'] = msg['timestamp']
335354
elif msg['message'].startswith('nsHttpTransaction::ProcessData '):
336355
match = re.search(r'^nsHttpTransaction::ProcessData \[this=(?P<id>[\w\d]+)',
337356
msg['message'])
@@ -446,14 +465,15 @@ def socket_transport_entry(self, msg):
446465
port = match.groupdict().get('port')
447466
self.http['sockets'][socket] = {'host': host, 'port': port}
448467
# nsSocketTransport::SendStatus [this=143f4000 status=804b0007]
468+
# nsSocketTransport::SendStatus [this=7fe074bd2a00 status=4B0007]
449469
elif msg['message'].startswith('nsSocketTransport::SendStatus '):
450470
match = re.search(r'^nsSocketTransport::SendStatus \['
451471
r'this=(?P<socket>[\w\d]+) '
452472
r'status=(?P<status>[\w\d]+)', msg['message'])
453473
if match:
454474
socket = match.groupdict().get('socket')
455475
status = match.groupdict().get('status')
456-
if status == '804b0007':
476+
if status in ['804b0007', '4b0007']:
457477
if socket not in self.http['sockets']:
458478
self.http['sockets'][socket] = {}
459479
if 'start' not in self.http['sockets'][socket]:

0 commit comments

Comments
 (0)