2017-08-15 01:08:06 +00:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2020-04-30 12:45:58 +00:00
import * as os from 'os' ;
import * as path from 'path' ;
import * as fs from 'fs' ;
2020-07-13 09:22:26 +00:00
import { BrowserFetcher } from './BrowserFetcher.js' ;
import { Browser } from '../common/Browser.js' ;
import { BrowserRunner } from './BrowserRunner.js' ;
2020-06-25 10:54:00 +00:00
import { promisify } from 'util' ;
2017-08-15 01:08:06 +00:00
2020-06-25 10:54:00 +00:00
const mkdtempAsync = promisify ( fs . mkdtemp ) ;
const writeFileAsync = promisify ( fs . writeFile ) ;
2020-05-12 15:30:13 +00:00
2020-10-12 09:08:57 +00:00
import { ChromeArgOptions , LaunchOptions } from './LaunchOptions.js' ;
import { BrowserOptions } from '../common/BrowserConnector.js' ;
import { Product } from '../common/Product.js' ;
2020-04-30 12:45:58 +00:00
2020-07-02 11:15:39 +00:00
/ * *
* Describes a launcher - a class that is able to create and launch a browser instance .
* @public
* /
2020-04-30 12:45:58 +00:00
export interface ProductLauncher {
launch ( object ) ;
executablePath : ( ) = > string ;
defaultArgs ( object ) ;
2020-10-12 09:08:57 +00:00
product : Product ;
2020-04-30 12:45:58 +00:00
}
2020-07-02 11:15:39 +00:00
/ * *
* @internal
* /
2020-04-30 12:45:58 +00:00
class ChromeLauncher implements ProductLauncher {
_projectRoot : string ;
_preferredRevision : string ;
_isPuppeteerCore : boolean ;
2020-05-07 10:54:55 +00:00
constructor (
projectRoot : string ,
preferredRevision : string ,
isPuppeteerCore : boolean
) {
2018-09-06 19:33:41 +00:00
this . _projectRoot = projectRoot ;
this . _preferredRevision = preferredRevision ;
this . _isPuppeteerCore = isPuppeteerCore ;
}
2020-05-07 10:54:55 +00:00
async launch (
options : LaunchOptions & ChromeArgOptions & BrowserOptions = { }
) : Promise < Browser > {
2018-08-07 20:22:04 +00:00
const {
ignoreDefaultArgs = false ,
args = [ ] ,
dumpio = false ,
executablePath = null ,
pipe = false ,
env = process . env ,
handleSIGINT = true ,
handleSIGTERM = true ,
handleSIGHUP = true ,
ignoreHTTPSErrors = false ,
2020-05-07 10:54:55 +00:00
defaultViewport = { width : 800 , height : 600 } ,
2018-08-07 20:22:04 +00:00
slowMo = 0 ,
2020-05-07 10:54:55 +00:00
timeout = 30000 ,
2018-08-07 20:22:04 +00:00
} = options ;
2018-04-03 22:05:27 +00:00
2019-11-26 09:23:19 +00:00
const profilePath = path . join ( os . tmpdir ( ) , 'puppeteer_dev_chrome_profile-' ) ;
2018-08-09 02:10:10 +00:00
const chromeArguments = [ ] ;
2020-05-07 10:54:55 +00:00
if ( ! ignoreDefaultArgs ) chromeArguments . push ( . . . this . defaultArgs ( options ) ) ;
2018-08-09 02:10:10 +00:00
else if ( Array . isArray ( ignoreDefaultArgs ) )
2020-05-07 10:54:55 +00:00
chromeArguments . push (
. . . this . defaultArgs ( options ) . filter (
( arg ) = > ! ignoreDefaultArgs . includes ( arg )
)
) ;
else chromeArguments . push ( . . . args ) ;
2018-08-09 02:10:10 +00:00
2018-08-07 20:22:04 +00:00
let temporaryUserDataDir = null ;
2017-08-18 06:18:08 +00:00
2020-05-07 10:54:55 +00:00
if (
! chromeArguments . some ( ( argument ) = >
argument . startsWith ( '--remote-debugging-' )
)
)
chromeArguments . push (
pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0'
) ;
if ( ! chromeArguments . some ( ( arg ) = > arg . startsWith ( '--user-data-dir' ) ) ) {
2019-11-26 09:23:19 +00:00
temporaryUserDataDir = await mkdtempAsync ( profilePath ) ;
2018-08-07 20:22:04 +00:00
chromeArguments . push ( ` --user-data-dir= ${ temporaryUserDataDir } ` ) ;
2017-08-28 19:14:21 +00:00
}
2018-05-26 00:26:40 +00:00
2018-08-07 20:22:04 +00:00
let chromeExecutable = executablePath ;
2020-06-10 15:14:23 +00:00
if ( os . arch ( ) === 'arm64' ) {
chromeExecutable = '/usr/bin/chromium-browser' ;
} else if ( ! executablePath ) {
2020-05-07 10:54:55 +00:00
const { missingText , executablePath } = resolveExecutablePath ( this ) ;
if ( missingText ) throw new Error ( missingText ) ;
2018-09-05 21:59:14 +00:00
chromeExecutable = executablePath ;
2017-08-15 01:08:06 +00:00
}
2017-08-21 22:43:36 +00:00
2018-04-03 22:05:27 +00:00
const usePipe = chromeArguments . includes ( '--remote-debugging-pipe' ) ;
2020-05-07 10:54:55 +00:00
const runner = new BrowserRunner (
chromeExecutable ,
chromeArguments ,
temporaryUserDataDir
) ;
runner . start ( {
handleSIGHUP ,
handleSIGTERM ,
handleSIGINT ,
dumpio ,
env ,
pipe : usePipe ,
} ) ;
2018-02-23 03:06:13 +00:00
2017-08-21 22:43:36 +00:00
try {
2020-05-07 10:54:55 +00:00
const connection = await runner . setupConnection ( {
usePipe ,
timeout ,
slowMo ,
preferredRevision : this._preferredRevision ,
} ) ;
const browser = await Browser . create (
connection ,
[ ] ,
ignoreHTTPSErrors ,
defaultViewport ,
runner . proc ,
runner . close . bind ( runner )
) ;
await browser . waitForTarget ( ( t ) = > t . type ( ) === 'page' ) ;
2018-06-01 20:57:50 +00:00
return browser ;
2019-11-26 09:23:19 +00:00
} catch ( error ) {
runner . kill ( ) ;
throw error ;
2017-10-24 23:05:12 +00:00
}
2017-08-15 01:08:06 +00:00
}
2017-08-15 21:29:42 +00:00
2017-12-20 01:51:21 +00:00
/ * *
2018-11-12 20:59:21 +00:00
* @param { ! Launcher . ChromeArgOptions = } options
2020-06-12 10:38:24 +00:00
* @returns { ! Array < string > }
2017-12-20 01:51:21 +00:00
* /
2020-04-30 12:45:58 +00:00
defaultArgs ( options : ChromeArgOptions = { } ) : string [ ] {
2019-11-26 09:23:19 +00:00
const chromeArguments = [
'--disable-background-networking' ,
'--enable-features=NetworkService,NetworkServiceInProcess' ,
'--disable-background-timer-throttling' ,
'--disable-backgrounding-occluded-windows' ,
'--disable-breakpad' ,
'--disable-client-side-phishing-detection' ,
'--disable-component-extensions-with-background-pages' ,
'--disable-default-apps' ,
'--disable-dev-shm-usage' ,
'--disable-extensions' ,
2019-11-27 11:58:09 +00:00
'--disable-features=TranslateUI' ,
2019-11-26 09:23:19 +00:00
'--disable-hang-monitor' ,
'--disable-ipc-flooding-protection' ,
'--disable-popup-blocking' ,
'--disable-prompt-on-repost' ,
'--disable-renderer-backgrounding' ,
'--disable-sync' ,
'--force-color-profile=srgb' ,
'--metrics-recording-only' ,
'--no-first-run' ,
'--enable-automation' ,
'--password-store=basic' ,
'--use-mock-keychain' ,
2020-09-14 09:31:23 +00:00
// TODO(sadym): remove '--enable-blink-features=IdleDetection'
// once IdleDetection is turned on by default.
'--enable-blink-features=IdleDetection' ,
2019-11-26 09:23:19 +00:00
] ;
2018-08-07 20:22:04 +00:00
const {
devtools = false ,
headless = ! devtools ,
args = [ ] ,
2020-05-07 10:54:55 +00:00
userDataDir = null ,
2018-08-07 20:22:04 +00:00
} = options ;
2020-10-13 10:59:58 +00:00
if ( userDataDir )
chromeArguments . push ( ` --user-data-dir= ${ path . resolve ( userDataDir ) } ` ) ;
2020-05-07 10:54:55 +00:00
if ( devtools ) chromeArguments . push ( '--auto-open-devtools-for-tabs' ) ;
2018-08-07 20:22:04 +00:00
if ( headless ) {
2020-05-07 10:54:55 +00:00
chromeArguments . push ( '--headless' , '--hide-scrollbars' , '--mute-audio' ) ;
2018-08-07 20:22:04 +00:00
}
2020-05-07 10:54:55 +00:00
if ( args . every ( ( arg ) = > arg . startsWith ( '-' ) ) )
2018-08-07 20:22:04 +00:00
chromeArguments . push ( 'about:blank' ) ;
chromeArguments . push ( . . . args ) ;
return chromeArguments ;
2017-12-20 01:51:21 +00:00
}
2020-04-30 12:45:58 +00:00
executablePath ( ) : string {
2019-11-26 09:23:19 +00:00
return resolveExecutablePath ( this ) . executablePath ;
}
2020-10-12 09:08:57 +00:00
get product ( ) : Product {
2019-11-26 09:23:19 +00:00
return 'chrome' ;
2017-09-14 00:39:18 +00:00
}
2019-11-26 09:23:19 +00:00
}
2020-07-02 11:15:39 +00:00
/ * *
* @internal
* /
2020-04-30 12:45:58 +00:00
class FirefoxLauncher implements ProductLauncher {
_projectRoot : string ;
_preferredRevision : string ;
_isPuppeteerCore : boolean ;
2020-05-07 10:54:55 +00:00
constructor (
projectRoot : string ,
preferredRevision : string ,
isPuppeteerCore : boolean
) {
2019-11-26 09:23:19 +00:00
this . _projectRoot = projectRoot ;
this . _preferredRevision = preferredRevision ;
this . _isPuppeteerCore = isPuppeteerCore ;
}
2020-05-07 10:54:55 +00:00
async launch (
options : LaunchOptions &
ChromeArgOptions &
BrowserOptions & {
extraPrefsFirefox ? : { [ x : string ] : unknown } ;
} = { }
) : Promise < Browser > {
2019-11-26 09:23:19 +00:00
const {
ignoreDefaultArgs = false ,
args = [ ] ,
dumpio = false ,
executablePath = null ,
pipe = false ,
env = process . env ,
handleSIGINT = true ,
handleSIGTERM = true ,
handleSIGHUP = true ,
ignoreHTTPSErrors = false ,
2020-05-07 10:54:55 +00:00
defaultViewport = { width : 800 , height : 600 } ,
2019-11-26 09:23:19 +00:00
slowMo = 0 ,
timeout = 30000 ,
2020-05-07 10:54:55 +00:00
extraPrefsFirefox = { } ,
2019-11-26 09:23:19 +00:00
} = options ;
const firefoxArguments = [ ] ;
2020-05-07 10:54:55 +00:00
if ( ! ignoreDefaultArgs ) firefoxArguments . push ( . . . this . defaultArgs ( options ) ) ;
2019-11-26 09:23:19 +00:00
else if ( Array . isArray ( ignoreDefaultArgs ) )
2020-05-07 10:54:55 +00:00
firefoxArguments . push (
. . . this . defaultArgs ( options ) . filter (
( arg ) = > ! ignoreDefaultArgs . includes ( arg )
)
) ;
else firefoxArguments . push ( . . . args ) ;
2019-11-26 09:23:19 +00:00
2020-05-07 10:54:55 +00:00
if (
! firefoxArguments . some ( ( argument ) = >
argument . startsWith ( '--remote-debugging-' )
)
)
2020-02-20 12:57:15 +00:00
firefoxArguments . push ( '--remote-debugging-port=0' ) ;
2019-11-26 09:23:19 +00:00
let temporaryUserDataDir = null ;
2020-05-07 10:54:55 +00:00
if (
! firefoxArguments . includes ( '-profile' ) &&
! firefoxArguments . includes ( '--profile' )
) {
2019-11-26 09:23:19 +00:00
temporaryUserDataDir = await this . _createProfile ( extraPrefsFirefox ) ;
firefoxArguments . push ( '--profile' ) ;
firefoxArguments . push ( temporaryUserDataDir ) ;
2019-07-14 23:37:57 +00:00
}
2019-11-26 09:23:19 +00:00
2020-04-15 11:30:42 +00:00
await this . _updateRevision ( ) ;
2020-03-10 20:59:03 +00:00
let firefoxExecutable = executablePath ;
2019-11-26 09:23:19 +00:00
if ( ! executablePath ) {
2020-05-07 10:54:55 +00:00
const { missingText , executablePath } = resolveExecutablePath ( this ) ;
if ( missingText ) throw new Error ( missingText ) ;
2020-03-10 20:59:03 +00:00
firefoxExecutable = executablePath ;
2019-11-26 09:23:19 +00:00
}
2020-05-07 10:54:55 +00:00
const runner = new BrowserRunner (
firefoxExecutable ,
firefoxArguments ,
temporaryUserDataDir
) ;
runner . start ( {
handleSIGHUP ,
handleSIGTERM ,
handleSIGINT ,
dumpio ,
env ,
pipe ,
} ) ;
2019-11-26 09:23:19 +00:00
try {
2020-05-07 10:54:55 +00:00
const connection = await runner . setupConnection ( {
usePipe : pipe ,
timeout ,
slowMo ,
preferredRevision : this._preferredRevision ,
} ) ;
const browser = await Browser . create (
connection ,
[ ] ,
ignoreHTTPSErrors ,
defaultViewport ,
runner . proc ,
runner . close . bind ( runner )
) ;
await browser . waitForTarget ( ( t ) = > t . type ( ) === 'page' ) ;
2019-11-26 09:23:19 +00:00
return browser ;
} catch ( error ) {
runner . kill ( ) ;
throw error ;
}
}
2020-04-30 12:45:58 +00:00
executablePath ( ) : string {
2020-03-10 20:59:03 +00:00
return resolveExecutablePath ( this ) . executablePath ;
2019-11-26 09:23:19 +00:00
}
2020-04-30 12:45:58 +00:00
async _updateRevision ( ) : Promise < void > {
2020-04-15 11:30:42 +00:00
// replace 'latest' placeholder with actual downloaded revision
if ( this . _preferredRevision === 'latest' ) {
2020-05-07 10:54:55 +00:00
const browserFetcher = new BrowserFetcher ( this . _projectRoot , {
product : this.product ,
} ) ;
2020-04-15 11:30:42 +00:00
const localRevisions = await browserFetcher . localRevisions ( ) ;
2020-05-07 10:54:55 +00:00
if ( localRevisions [ 0 ] ) this . _preferredRevision = localRevisions [ 0 ] ;
2020-04-15 11:30:42 +00:00
}
}
2020-10-12 09:08:57 +00:00
get product ( ) : Product {
2019-11-26 09:23:19 +00:00
return 'firefox' ;
}
2020-04-30 12:45:58 +00:00
defaultArgs ( options : ChromeArgOptions = { } ) : string [ ] {
2020-05-07 10:54:55 +00:00
const firefoxArguments = [ '--no-remote' , '--foreground' ] ;
2020-08-10 08:23:17 +00:00
if ( os . platform ( ) . startsWith ( 'win' ) ) {
firefoxArguments . push ( '--wait-for-browser' ) ;
}
2019-11-26 09:23:19 +00:00
const {
devtools = false ,
headless = ! devtools ,
args = [ ] ,
2020-05-07 10:54:55 +00:00
userDataDir = null ,
2019-11-26 09:23:19 +00:00
} = options ;
if ( userDataDir ) {
firefoxArguments . push ( '--profile' ) ;
firefoxArguments . push ( userDataDir ) ;
2018-09-06 19:33:41 +00:00
}
2020-05-07 10:54:55 +00:00
if ( headless ) firefoxArguments . push ( '--headless' ) ;
if ( devtools ) firefoxArguments . push ( '--devtools' ) ;
if ( args . every ( ( arg ) = > arg . startsWith ( '-' ) ) )
2019-11-26 09:23:19 +00:00
firefoxArguments . push ( 'about:blank' ) ;
firefoxArguments . push ( . . . args ) ;
return firefoxArguments ;
2018-09-06 19:33:41 +00:00
}
2020-05-07 10:54:55 +00:00
async _createProfile ( extraPrefs : { [ x : string ] : unknown } ) : Promise < string > {
const profilePath = await mkdtempAsync (
path . join ( os . tmpdir ( ) , 'puppeteer_dev_firefox_profile-' )
) ;
2019-11-27 06:57:43 +00:00
const prefsJS = [ ] ;
const userJS = [ ] ;
const server = 'dummy.test' ;
const defaultPreferences = {
// Make sure Shield doesn't hit the network.
'app.normandy.api_url' : '' ,
// Disable Firefox old build background check
'app.update.checkInstallTime' : false ,
// Disable automatically upgrading Firefox
'app.update.disabledForTesting' : true ,
// Increase the APZ content response timeout to 1 minute
'apz.content_response_timeout' : 60000 ,
// Prevent various error message on the console
// jest-puppeteer asserts that no error message is emitted by the console
2020-05-07 10:54:55 +00:00
'browser.contentblocking.features.standard' :
'-tp,tpPrivate,cookieBehavior0,-cm,-fp' ,
2019-11-27 06:57:43 +00:00
// Enable the dump function: which sends messages to the system
// console
// https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
'browser.dom.window.dump.enabled' : true ,
// Disable topstories
2020-10-05 05:17:21 +00:00
'browser.newtabpage.activity-stream.feeds.system.topstories' : false ,
2019-11-27 06:57:43 +00:00
// Always display a blank page
'browser.newtabpage.enabled' : false ,
// Background thumbnails in particular cause grief: and disabling
// thumbnails in general cannot hurt
'browser.pagethumbnails.capturing_disabled' : true ,
// Disable safebrowsing components.
'browser.safebrowsing.blockedURIs.enabled' : false ,
'browser.safebrowsing.downloads.enabled' : false ,
'browser.safebrowsing.malware.enabled' : false ,
'browser.safebrowsing.passwords.enabled' : false ,
'browser.safebrowsing.phishing.enabled' : false ,
// Disable updates to search engines.
'browser.search.update' : false ,
// Do not restore the last open set of tabs if the browser has crashed
'browser.sessionstore.resume_from_crash' : false ,
// Skip check for default browser on startup
'browser.shell.checkDefaultBrowser' : false ,
// Disable newtabpage
'browser.startup.homepage' : 'about:blank' ,
// Do not redirect user when a milstone upgrade of Firefox is detected
'browser.startup.homepage_override.mstone' : 'ignore' ,
// Start with a blank page about:blank
'browser.startup.page' : 0 ,
// Do not allow background tabs to be zombified on Android: otherwise for
// tests that open additional tabs: the test harness tab itself might get
// unloaded
'browser.tabs.disableBackgroundZombification' : false ,
// Do not warn when closing all other open tabs
'browser.tabs.warnOnCloseOtherTabs' : false ,
// Do not warn when multiple tabs will be opened
'browser.tabs.warnOnOpen' : false ,
// Disable the UI tour.
'browser.uitour.enabled' : false ,
// Turn off search suggestions in the location bar so as not to trigger
// network connections.
'browser.urlbar.suggest.searches' : false ,
// Disable first run splash page on Windows 10
'browser.usedOnWindows10.introURL' : '' ,
// Do not warn on quitting Firefox
'browser.warnOnQuit' : false ,
2020-10-05 05:17:21 +00:00
// Defensively disable data reporting systems
2019-11-27 06:57:43 +00:00
'datareporting.healthreport.documentServerURI' : ` http:// ${ server } /dummy/healthreport/ ` ,
'datareporting.healthreport.logging.consoleEnabled' : false ,
'datareporting.healthreport.service.enabled' : false ,
'datareporting.healthreport.service.firstRun' : false ,
'datareporting.healthreport.uploadEnabled' : false ,
2020-10-05 05:17:21 +00:00
// Do not show datareporting policy notifications which can interfere with tests
2019-11-27 06:57:43 +00:00
'datareporting.policy.dataSubmissionEnabled' : false ,
'datareporting.policy.dataSubmissionPolicyBypassNotification' : true ,
// DevTools JSONViewer sometimes fails to load dependencies with its require.js.
// This doesn't affect Puppeteer but spams console (Bug 1424372)
'devtools.jsonview.enabled' : false ,
// Disable popup-blocker
'dom.disable_open_during_load' : false ,
// Enable the support for File object creation in the content process
// Required for |Page.setFileInputFiles| protocol method.
'dom.file.createInChild' : true ,
// Disable the ProcessHangMonitor
'dom.ipc.reportProcessHangs' : false ,
// Disable slow script dialogues
'dom.max_chrome_script_run_time' : 0 ,
'dom.max_script_run_time' : 0 ,
// Only load extensions from the application and user profile
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
'extensions.autoDisableScopes' : 0 ,
'extensions.enabledScopes' : 5 ,
// Disable metadata caching for installed add-ons by default
'extensions.getAddons.cache.enabled' : false ,
// Disable installing any distribution extensions or add-ons.
'extensions.installDistroAddons' : false ,
// Disabled screenshots extension
'extensions.screenshots.disabled' : true ,
// Turn off extension updates so they do not bother tests
'extensions.update.enabled' : false ,
// Turn off extension updates so they do not bother tests
'extensions.update.notifyUser' : false ,
// Make sure opening about:addons will not hit the network
'extensions.webservice.discoverURL' : ` http:// ${ server } /dummy/discoveryURL ` ,
// Allow the application to have focus even it runs in the background
'focusmanager.testmode' : true ,
// Disable useragent updates
'general.useragent.updates.enabled' : false ,
// Always use network provider for geolocation tests so we bypass the
// macOS dialog raised by the corelocation provider
'geo.provider.testing' : true ,
// Do not scan Wifi
'geo.wifi.scan' : false ,
// No hang monitor
'hangmonitor.timeout' : 0 ,
// Show chrome errors and warnings in the error console
'javascript.options.showInConsole' : true ,
// Disable download and usage of OpenH264: and Widevine plugins
'media.gmp-manager.updateEnabled' : false ,
// Prevent various error message on the console
// jest-puppeteer asserts that no error message is emitted by the console
'network.cookie.cookieBehavior' : 0 ,
// Do not prompt for temporary redirects
'network.http.prompt-temp-redirect' : false ,
// Disable speculative connections so they are not reported as leaking
// when they are hanging around
'network.http.speculative-parallel-limit' : 0 ,
// Do not automatically switch between offline and online
'network.manage-offline-status' : false ,
// Make sure SNTP requests do not hit the network
'network.sntp.pools' : server ,
// Disable Flash.
'plugin.state.flash' : 0 ,
'privacy.trackingprotection.enabled' : false ,
// Enable Remote Agent
2019-11-26 09:23:19 +00:00
// https://bugzilla.mozilla.org/show_bug.cgi?id=1544393
'remote.enabled' : true ,
2019-11-27 06:57:43 +00:00
// Don't do network connections for mitm priming
'security.certerrors.mitm.priming.enabled' : false ,
// Local documents have access to all other local documents,
// including directory listings
'security.fileuri.strict_origin_policy' : false ,
// Do not wait for the notification button security delay
'security.notification_enable_delay' : 0 ,
// Ensure blocklist updates do not hit the network
'services.settings.server' : ` http:// ${ server } /dummy/blocklist/ ` ,
// Do not automatically fill sign-in forms with known usernames and
// passwords
'signon.autofillForms' : false ,
// Disable password capture, so that tests that include forms are not
// influenced by the presence of the persistent doorhanger notification
'signon.rememberSignons' : false ,
// Disable first-run welcome page
'startup.homepage_welcome_url' : 'about:blank' ,
// Disable first-run welcome page
'startup.homepage_welcome_url.additional' : '' ,
// Disable browser animations (tabs, fullscreen, sliding alerts)
'toolkit.cosmeticAnimations.enabled' : false ,
// Prevent starting into safe mode after application crashes
'toolkit.startup.max_resumed_crashes' : - 1 ,
2019-11-26 09:23:19 +00:00
} ;
2019-11-27 06:57:43 +00:00
Object . assign ( defaultPreferences , extraPrefs ) ;
for ( const [ key , value ] of Object . entries ( defaultPreferences ) )
2020-05-07 10:54:55 +00:00
userJS . push (
` user_pref( ${ JSON . stringify ( key ) } , ${ JSON . stringify ( value ) } ); `
) ;
2019-11-27 06:57:43 +00:00
await writeFileAsync ( path . join ( profilePath , 'user.js' ) , userJS . join ( '\n' ) ) ;
2020-05-07 10:54:55 +00:00
await writeFileAsync (
path . join ( profilePath , 'prefs.js' ) ,
prefsJS . join ( '\n' )
) ;
2019-11-26 09:23:19 +00:00
return profilePath ;
}
2017-08-15 01:08:06 +00:00
}
2020-05-07 10:54:55 +00:00
function resolveExecutablePath (
launcher : ChromeLauncher | FirefoxLauncher
) : { executablePath : string ; missingText? : string } {
2020-08-10 08:37:31 +00:00
let downloadPath : string ;
2019-11-26 09:23:19 +00:00
// puppeteer-core doesn't take into account PUPPETEER_* env variables.
if ( ! launcher . _isPuppeteerCore ) {
2020-05-07 10:54:55 +00:00
const executablePath =
process . env . PUPPETEER_EXECUTABLE_PATH ||
process . env . npm_config_puppeteer_executable_path ||
process . env . npm_package_config_puppeteer_executable_path ;
2019-11-26 09:23:19 +00:00
if ( executablePath ) {
2020-05-07 10:54:55 +00:00
const missingText = ! fs . existsSync ( executablePath )
? 'Tried to use PUPPETEER_EXECUTABLE_PATH env variable to launch browser but did not find any executable at: ' +
executablePath
: null ;
return { executablePath , missingText } ;
2019-11-26 09:23:19 +00:00
}
2020-08-10 08:37:31 +00:00
downloadPath =
process . env . PUPPETEER_DOWNLOAD_PATH ||
process . env . npm_config_puppeteer_download_path ||
process . env . npm_package_config_puppeteer_download_path ;
2019-11-26 09:23:19 +00:00
}
2020-05-07 10:54:55 +00:00
const browserFetcher = new BrowserFetcher ( launcher . _projectRoot , {
product : launcher.product ,
2020-08-10 08:37:31 +00:00
path : downloadPath ,
2020-05-07 10:54:55 +00:00
} ) ;
2020-03-10 20:59:03 +00:00
if ( ! launcher . _isPuppeteerCore && launcher . product === 'chrome' ) {
2019-11-26 09:23:19 +00:00
const revision = process . env [ 'PUPPETEER_CHROMIUM_REVISION' ] ;
if ( revision ) {
const revisionInfo = browserFetcher . revisionInfo ( revision ) ;
2020-05-07 10:54:55 +00:00
const missingText = ! revisionInfo . local
? 'Tried to use PUPPETEER_CHROMIUM_REVISION env variable to launch browser but did not find executable at: ' +
revisionInfo . executablePath
: null ;
return { executablePath : revisionInfo.executablePath , missingText } ;
2019-11-26 09:23:19 +00:00
}
}
const revisionInfo = browserFetcher . revisionInfo ( launcher . _preferredRevision ) ;
2020-05-07 10:54:55 +00:00
const missingText = ! revisionInfo . local
2020-07-16 12:28:09 +00:00
? ` Could not find browser revision ${ launcher . _preferredRevision } . Run "PUPPETEER_PRODUCT=firefox npm install" or "PUPPETEER_PRODUCT=firefox yarn install" to download a supported Firefox browser binary. `
2020-05-07 10:54:55 +00:00
: null ;
return { executablePath : revisionInfo.executablePath , missingText } ;
2019-11-26 09:23:19 +00:00
}
2020-07-02 11:15:39 +00:00
/ * *
* @internal
* /
export default function Launcher (
2020-05-07 10:54:55 +00:00
projectRoot : string ,
preferredRevision : string ,
isPuppeteerCore : boolean ,
product? : string
) : ProductLauncher {
2019-11-26 09:23:19 +00:00
// puppeteer-core doesn't take into account PUPPETEER_* env variables.
if ( ! product && ! isPuppeteerCore )
2020-05-07 10:54:55 +00:00
product =
process . env . PUPPETEER_PRODUCT ||
process . env . npm_config_puppeteer_product ||
process . env . npm_package_config_puppeteer_product ;
2019-11-26 09:23:19 +00:00
switch ( product ) {
case 'firefox' :
2020-05-07 10:54:55 +00:00
return new FirefoxLauncher (
projectRoot ,
preferredRevision ,
isPuppeteerCore
) ;
2019-11-26 09:23:19 +00:00
case 'chrome' :
default :
2020-05-12 09:30:24 +00:00
if ( typeof product !== 'undefined' && product !== 'chrome' ) {
/ * T h e u s e r g a v e u s a n i n c o r r e c t p r o d u c t n a m e
* we ' ll default to launching Chrome , but log to the console
* to let the user know ( they ' ve probably typoed ) .
* /
console . warn (
` Warning: unknown product name ${ product } . Falling back to chrome. `
) ;
}
2020-05-07 10:54:55 +00:00
return new ChromeLauncher (
projectRoot ,
preferredRevision ,
isPuppeteerCore
) ;
2019-11-26 09:23:19 +00:00
}
}