@@ -130,6 +130,15 @@ def configure(data_dir):
130
130
shutil .copy (test_file ('firefox_user.js' ), os .path .join (data_dir , 'user.js' ))
131
131
132
132
133
+ class SafariConfig :
134
+ default_flags = ('' , )
135
+ executable_name = 'Safari'
136
+
137
+ @staticmethod
138
+ def configure (data_dir ):
139
+ """ Safari has no special configuration step."""
140
+
141
+
133
142
# Special value for passing to assert_returncode which means we expect that program
134
143
# to fail with non-zero return code, but we don't care about specifically which one.
135
144
NON_ZERO = - 1
@@ -202,6 +211,10 @@ def is_firefox():
202
211
return EMTEST_BROWSER and 'firefox' in EMTEST_BROWSER .lower ()
203
212
204
213
214
+ def is_safari ():
215
+ return EMTEST_BROWSER and 'safari' in EMTEST_BROWSER .lower ()
216
+
217
+
205
218
def compiler_for (filename , force_c = False ):
206
219
if shared .suffix (filename ) in ('.cc' , '.cxx' , '.cpp' ) and not force_c :
207
220
return EMXX
@@ -2418,6 +2431,8 @@ def configure_test_browser():
2418
2431
config = ChromeConfig ()
2419
2432
elif is_firefox ():
2420
2433
config = FirefoxConfig ()
2434
+ elif is_safari ():
2435
+ config = SafariConfig ()
2421
2436
if config :
2422
2437
EMTEST_BROWSER += ' ' + ' ' .join (config .default_flags )
2423
2438
if EMTEST_HEADLESS == 1 :
@@ -2548,11 +2563,21 @@ def browser_restart(cls):
2548
2563
def browser_open (cls , url ):
2549
2564
assert has_browser ()
2550
2565
browser_args = EMTEST_BROWSER
2566
+ parallel_harness = worker_id is not None
2551
2567
2552
- if EMTEST_BROWSER_AUTO_CONFIG :
2568
+ config = None
2569
+ if is_chrome ():
2570
+ config = ChromeConfig ()
2571
+ elif is_firefox ():
2572
+ config = FirefoxConfig ()
2573
+ elif is_safari ():
2574
+ config = SafariConfig ()
2575
+
2576
+ # Prepare the browser data directory, if it uses one.
2577
+ if EMTEST_BROWSER_AUTO_CONFIG and config and hasattr (config , 'data_dir_flag' ):
2553
2578
logger .info ('Using default CI configuration.' )
2554
2579
browser_data_dir = DEFAULT_BROWSER_DATA_DIR
2555
- if worker_id is not None :
2580
+ if parallel_harness :
2556
2581
# Running in parallel mode, give each browser its own profile dir.
2557
2582
browser_data_dir += '-' + str (worker_id )
2558
2583
@@ -2567,52 +2592,65 @@ def browser_open(cls, url):
2567
2592
# Recreate the new data directory.
2568
2593
os .mkdir (browser_data_dir )
2569
2594
2570
- if is_chrome ():
2571
- config = ChromeConfig ()
2572
- elif is_firefox ():
2573
- config = FirefoxConfig ()
2574
- else :
2575
- exit_with_error (f'EMTEST_BROWSER_AUTO_CONFIG only currently works with firefox or chrome. EMTEST_BROWSER was "{ EMTEST_BROWSER } "' )
2595
+ if not config :
2596
+ exit_with_error (f'EMTEST_BROWSER_AUTO_CONFIG only currently works with firefox, chrome and safari. EMTEST_BROWSER was "{ EMTEST_BROWSER } "' )
2576
2597
if WINDOWS :
2577
2598
# Escape directory delimiter backslashes for shlex.split.
2578
2599
browser_data_dir = browser_data_dir .replace ('\\ ' , '\\ \\ ' )
2579
2600
config .configure (browser_data_dir )
2580
2601
browser_args += f' { config .data_dir_flag } "{ browser_data_dir } "'
2581
2602
2582
2603
browser_args = shlex .split (browser_args )
2604
+ if is_safari ():
2605
+ # For the macOS 'open' command, pass
2606
+ # --new: to make a new Safari app be launched, rather than add a tab to an existing Safari process/window
2607
+ # --fresh: do not restore old tabs (e.g. if user had old navigated windows open)
2608
+ # --background: Open the new Safari window behind the current Terminal window, to make following the test run more pleasing (this is for convenience only)
2609
+ # -a <exe_name>: The path to the executable to open, in this case Safari
2610
+ browser_args = ['open' , '--new' , '--fresh' , '--background' , '-a' ] + browser_args
2611
+
2583
2612
logger .info ('Launching browser: %s' , str (browser_args ))
2584
2613
2585
- if WINDOWS and is_firefox ():
2586
- cls .launch_browser_harness_windows_firefox ( worker_id , config , browser_args , url )
2614
+ if ( WINDOWS and is_firefox ()) or is_safari ():
2615
+ cls .launch_browser_harness_with_proc_snapshot_workaround ( parallel_harness , config , browser_args , url )
2587
2616
else :
2588
2617
cls .browser_procs = [subprocess .Popen (browser_args + [url ])]
2589
2618
2590
2619
@classmethod
2591
- def launch_browser_harness_windows_firefox (cls , worker_id , config , browser_args , url ):
2592
- ''' Dedicated function for launching browser harness on Firefox on Windows,
2593
- which requires extra care for window positioning and process tracking.'''
2620
+ def launch_browser_harness_with_proc_snapshot_workaround (cls , parallel_harness , config , browser_args , url ):
2621
+ ''' Dedicated function for launching browser harness in scenarios where
2622
+ we need to identify the launched browser processes via a before-after
2623
+ subprocess snapshotting delta workaround.'''
2594
2624
2625
+ # In order for this to work, each browser needs to be launched one at a time
2626
+ # so that we know which process belongs to which browser.
2595
2627
with FileLock (browser_spawn_lock_filename ) as count :
2596
- # Firefox is a multiprocess browser. On Windows, killing the spawned
2597
- # process will not bring down the whole browser, but only one browser tab.
2598
- # So take a delta snapshot before->after spawning the browser to find
2599
- # which subprocesses we launched.
2600
- if worker_id is not None :
2628
+ # Take a snapshot before spawning the browser to find which processes
2629
+ # existed before launching the browser.
2630
+ if parallel_harness or is_safari ():
2601
2631
procs_before = list_processes_by_name (config .executable_name )
2632
+
2633
+ # Browser launch
2602
2634
cls .browser_procs = [subprocess .Popen (browser_args + [url ])]
2603
- # Give Firefox time to spawn its subprocesses. Use an increasing timeout
2604
- # as a crude way to account for system load.
2605
- if worker_id is not None :
2635
+
2636
+ # Give the browser time to spawn its subprocesses. Use an increasing
2637
+ # timeout as a crude way to account for system load.
2638
+ if parallel_harness or is_safari ():
2606
2639
time .sleep (2 + count * 0.3 )
2607
2640
procs_after = list_processes_by_name (config .executable_name )
2641
+
2642
+ # Take a snapshot again to find which processes exist after launching
2643
+ # the browser. Then the newly launched browser processes are determined
2644
+ # by the delta before->after.
2645
+ cls .browser_procs = list (set (procs_after ).difference (set (procs_before )))
2646
+ if len (cls .browser_procs ) == 0 :
2647
+ logger .warning ('Could not detect the launched browser subprocesses. The test harness may not be able to close browser windows if a test hangs, and at harness exit.' )
2648
+
2649
+ # Firefox on Windows quirk:
2608
2650
# Make sure that each browser window is visible on the desktop. Otherwise
2609
2651
# browser might decide that the tab is backgrounded, and not load a test,
2610
2652
# or it might not tick rAF()s forward, causing tests to hang.
2611
- if worker_id is not None and not EMTEST_HEADLESS :
2612
- # On Firefox on Windows we needs to track subprocesses that got created
2613
- # by Firefox. Other setups can use 'browser_proc' directly to terminate
2614
- # the browser.
2615
- cls .browser_procs = list (set (procs_after ).difference (set (procs_before )))
2653
+ if WINDOWS and parallel_harness and not EMTEST_HEADLESS :
2616
2654
# Wrap window positions on a Full HD desktop area modulo primes.
2617
2655
for proc in cls .browser_procs :
2618
2656
move_browser_window (proc .pid , (300 + count * 47 ) % 1901 , (10 + count * 37 ) % 997 )
0 commit comments