@@ -29,6 +29,8 @@ export class FreshFirefox implements Interceptor {
2929
3030 constructor ( private config : HtkConfig ) { }
3131
32+ private readonly firefoxProfilePath = path . join ( this . config . configPath , 'firefox-profile' ) ;
33+
3234 isActive ( proxyPort : number | string ) {
3335 return browsers [ proxyPort ] != null && ! ! browsers [ proxyPort ] . pid ;
3436 }
@@ -42,91 +44,125 @@ export class FreshFirefox implements Interceptor {
4244
4345 }
4446
45- async activate ( proxyPort : number ) {
46- if ( this . isActive ( proxyPort ) ) return ;
47+ async startFirefox (
48+ certCheckServer : CertCheckServer ,
49+ proxyPort ?: number ,
50+ existingPrefs = { }
51+ ) {
52+ const browser = await launchBrowser ( certCheckServer . checkCertUrl , {
53+ browser : 'firefox' ,
54+ profile : this . firefoxProfilePath ,
55+ proxy : proxyPort ? `127.0.0.1:${ proxyPort } ` : undefined ,
56+ // Don't intercept our cert testing requests
57+ noProxy : proxyPort ? certCheckServer . host : undefined ,
58+ prefs : _ . assign (
59+ existingPrefs ,
60+ proxyPort ? {
61+ // By default browser-launcher only configures HTTP, so we need to add HTTPS:
62+ 'network.proxy.ssl' : '"127.0.0.1"' ,
63+ 'network.proxy.ssl_port' : proxyPort ,
64+
65+ // The above browser-launcher proxy/noProxy settings should do this, but don't seem to
66+ // reliably overwrite existing values, so we set them explicitly.
67+ 'network.proxy.http' : '"127.0.0.1"' ,
68+ 'network.proxy.http_port' : proxyPort ,
69+ 'network.proxy.http.network.proxy.http.no_proxies_on' : "\"" + certCheckServer . host + "\"" ,
70+
71+ // Send localhost reqs via the proxy too
72+ 'network.proxy.allow_hijacking_localhost' : true ,
73+ } : { } ,
74+ {
75+ // Disable the noisy captive portal check requests
76+ 'network.captive-portal-service.enabled' : false ,
77+
78+ // Disable some annoying tip messages
79+ 'browser.chrome.toolbar_tips' : false ,
80+
81+ // Ignore available updates:
82+ "app.update.auto" : false ,
83+ "browser.startup.homepage_override.mstone" : "\"ignore\"" ,
84+
85+ // Disable exit warnings:
86+ "browser.showQuitWarning" : false ,
87+ "browser.tabs.warnOnClose" : false ,
88+ "browser.tabs.warnOnCloseOtherTabs" : false ,
89+
90+ // Disable various first-run things:
91+ "browser.uitour.enabled" : false ,
92+ 'browser.usedOnWindows10' : true ,
93+ "browser.usedOnWindows10.introURL" : "\"\"" ,
94+ 'datareporting.healthreport.service.firstRun' : false ,
95+ 'toolkit.telemetry.reportingpolicy.firstRun' : false ,
96+ 'browser.reader.detectedFirstArticle' : false ,
97+ "datareporting.policy.dataSubmissionEnabled" : false ,
98+ "datareporting.policy.dataSubmissionPolicyAccepted" : false ,
99+ "datareporting.policy.dataSubmissionPolicyBypassNotification" : true ,
100+ "trailhead.firstrun.didSeeAboutWelcome" : true
101+ }
102+ )
103+ } , this . config . configPath ) ;
47104
105+ if ( browser . process . stdout ) browser . process . stdout . pipe ( process . stdout ) ;
106+ if ( browser . process . stderr ) browser . process . stderr . pipe ( process . stderr ) ;
107+
108+ return browser ;
109+ }
110+
111+ async setupFirefoxProfile ( ) {
48112 const certCheckServer = new CertCheckServer ( this . config ) ;
49- await certCheckServer . start ( 'https://amiusing.httptoolkit.tech' ) ;
113+ await certCheckServer . start ( ) ;
114+ const certInstalled = certCheckServer . waitForSuccess ( ) . catch ( reportError ) ;
115+
116+ const browser = await this . startFirefox ( certCheckServer ) ;
117+ browser . process . once ( 'exit' , ( ) => certCheckServer . stop ( ) ) ;
118+ await certInstalled ;
119+ await delay ( 100 ) ; // Tiny delay, so firefox can do initial setup tasks
120+ browser . stop ( ) ;
121+ }
122+
123+ async activate ( proxyPort : number ) {
124+ if ( this . isActive ( proxyPort ) ) return ;
50125
51126 const firefoxProfile = path . join ( this . config . configPath , 'firefox-profile' ) ;
52127 const firefoxPrefsFile = path . join ( firefoxProfile , 'prefs.js' ) ;
53128
54129 let existingPrefs : _ . Dictionary < any > = { } ;
55130
56- if ( await canAccess ( firefoxPrefsFile ) ) {
57- // If the profile exists, then we've run this before and not deleted the profile,
58- // so it probably worked. If so, reuse the existing preferences to avoid issues
59- // where on pref setup firefox behaves badly (opening a 2nd window) on OSX.
60- const prefContents = await readFile ( firefoxPrefsFile , {
61- encoding : 'utf8'
62- } ) ;
63-
64- existingPrefs = _ ( prefContents )
65- . split ( '\n' )
66- . reduce ( ( prefs : _ . Dictionary < any > , line ) => {
67- const match = FIREFOX_PREF_REGEX . exec ( line ) ;
68- if ( match ) {
69- prefs [ match [ 1 ] ] = match [ 2 ] ;
70- }
71- return prefs
72- } , { } ) ;
131+ if ( await canAccess ( firefoxPrefsFile ) === false ) {
132+ /*
133+ First time, we do a separate pre-usage startup & stop, without the proxy, for certificate setup.
134+ This helps avoid initial Firefox profile setup request noise, and tidies up some awkward UX where
135+ firefox likes to open extra welcome windows/tabs on first run, especially on OSX.
136+ */
137+ await this . setupFirefoxProfile ( ) ;
73138 }
74139
75- const browser = await launchBrowser ( certCheckServer . checkCertUrl , {
76- browser : 'firefox' ,
77- profile : firefoxProfile ,
78- proxy : `127.0.0.1:${ proxyPort } ` ,
79- // Don't intercept our cert testing requests
80- noProxy : certCheckServer . host ,
81- prefs : _ . assign ( existingPrefs , {
82- // By default browser-launcher only configures HTTP, so we need to add HTTPS:
83- 'network.proxy.ssl' : '"127.0.0.1"' ,
84- 'network.proxy.ssl_port' : proxyPort ,
85-
86- // The above browser-launcher proxy/noProxy settings should do this, but don't seem to
87- // reliably overwrite existing values, so we set them explicitly.
88- 'network.proxy.http' : '"127.0.0.1"' ,
89- 'network.proxy.http_port' : proxyPort ,
90- 'network.proxy.http.network.proxy.http.no_proxies_on' : certCheckServer . host ,
91-
92- // Send localhost reqs via the proxy too
93- 'network.proxy.allow_hijacking_localhost' : true ,
94-
95- // Disable the noisy captive portal check requests
96- 'network.captive-portal-service.enabled' : false ,
97-
98- // Disable some annoying tip messages
99- 'browser.chrome.toolbar_tips' : false ,
100-
101- // Ignore available updates:
102- "app.update.auto" : false ,
103- "browser.startup.homepage_override.mstone" : "ignore" ,
104-
105- // Disable exit warnings:
106- "browser.showQuitWarning" : false ,
107- "browser.tabs.warnOnClose" : false ,
108- "browser.tabs.warnOnCloseOtherTabs" : false ,
109-
110- // Disable various first-run things:
111- "browser.uitour.enabled" : false ,
112- 'browser.usedOnWindows10' : true ,
113- "browser.usedOnWindows10.introURL" : "" ,
114- 'datareporting.healthreport.service.firstRun' : false ,
115- 'toolkit.telemetry.reportingpolicy.firstRun' : false ,
116- 'browser.reader.detectedFirstArticle' : false ,
117- "datareporting.policy.dataSubmissionEnabled" : false ,
118- "datareporting.policy.dataSubmissionPolicyAccepted" : false ,
119- "datareporting.policy.dataSubmissionPolicyBypassNotification" : true
120- } )
121- } , this . config . configPath ) ;
140+ const certCheckServer = new CertCheckServer ( this . config ) ;
141+ await certCheckServer . start ( 'https://amiusing.httptoolkit.tech' ) ;
122142
123- if ( browser . process . stdout ) browser . process . stdout . pipe ( process . stdout ) ;
124- if ( browser . process . stderr ) browser . process . stderr . pipe ( process . stderr ) ;
143+ // At this stage, we've run firefox at least once, at it's working nicely.
144+ // We need to preserve & reuse any existing preferences, to avoid issues
145+ // where on pref setup firefox behaves badly (opening a 2nd window) on OSX.
146+ const prefContents = await readFile ( firefoxPrefsFile , {
147+ encoding : 'utf8'
148+ } ) ;
149+
150+ existingPrefs = _ ( prefContents )
151+ . split ( '\n' )
152+ . reduce ( ( prefs : _ . Dictionary < any > , line ) => {
153+ const match = FIREFOX_PREF_REGEX . exec ( line ) ;
154+ if ( match ) {
155+ prefs [ match [ 1 ] ] = match [ 2 ] ;
156+ }
157+ return prefs
158+ } , { } ) ;
159+
160+ const browser = await this . startFirefox ( certCheckServer , proxyPort , existingPrefs ) ;
125161
126162 let success = false ;
127163 certCheckServer . waitForSuccess ( ) . then ( ( ) => {
128164 success = true ;
129- } ) . catch ( console . warn ) ;
165+ } ) . catch ( reportError ) ;
130166
131167 browsers [ proxyPort ] = browser ;
132168 browser . process . once ( 'exit' , ( ) => {
0 commit comments