firefox.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /**
  2. * @license
  3. * Copyright 2023 Google Inc.
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. import fs from 'fs';
  7. import path from 'path';
  8. import { getJSON } from '../httpUtil.js';
  9. import { BrowserPlatform } from './types.js';
  10. function archiveNightly(platform, buildId) {
  11. switch (platform) {
  12. case BrowserPlatform.LINUX:
  13. return `firefox-${buildId}.en-US.${platform}-x86_64.tar.bz2`;
  14. case BrowserPlatform.MAC_ARM:
  15. case BrowserPlatform.MAC:
  16. return `firefox-${buildId}.en-US.mac.dmg`;
  17. case BrowserPlatform.WIN32:
  18. case BrowserPlatform.WIN64:
  19. return `firefox-${buildId}.en-US.${platform}.zip`;
  20. }
  21. }
  22. function archive(platform, buildId) {
  23. switch (platform) {
  24. case BrowserPlatform.LINUX:
  25. return `firefox-${buildId}.tar.bz2`;
  26. case BrowserPlatform.MAC_ARM:
  27. case BrowserPlatform.MAC:
  28. return `Firefox ${buildId}.dmg`;
  29. case BrowserPlatform.WIN32:
  30. case BrowserPlatform.WIN64:
  31. return `Firefox Setup ${buildId}.exe`;
  32. }
  33. }
  34. function platformName(platform) {
  35. switch (platform) {
  36. case BrowserPlatform.LINUX:
  37. return `linux-x86_64`;
  38. case BrowserPlatform.MAC_ARM:
  39. case BrowserPlatform.MAC:
  40. return `mac`;
  41. case BrowserPlatform.WIN32:
  42. case BrowserPlatform.WIN64:
  43. return platform;
  44. }
  45. }
  46. function parseBuildId(buildId) {
  47. for (const value of Object.values(FirefoxChannel)) {
  48. if (buildId.startsWith(value + '_')) {
  49. buildId = buildId.substring(value.length + 1);
  50. return [value, buildId];
  51. }
  52. }
  53. // Older versions do not have channel as the prefix.«
  54. return [FirefoxChannel.NIGHTLY, buildId];
  55. }
  56. export function resolveDownloadUrl(platform, buildId, baseUrl) {
  57. const [channel, resolvedBuildId] = parseBuildId(buildId);
  58. switch (channel) {
  59. case FirefoxChannel.NIGHTLY:
  60. baseUrl ??=
  61. 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central';
  62. break;
  63. case FirefoxChannel.DEVEDITION:
  64. baseUrl ??= 'https://archive.mozilla.org/pub/devedition/releases';
  65. break;
  66. case FirefoxChannel.BETA:
  67. case FirefoxChannel.STABLE:
  68. case FirefoxChannel.ESR:
  69. baseUrl ??= 'https://archive.mozilla.org/pub/firefox/releases';
  70. break;
  71. }
  72. switch (channel) {
  73. case FirefoxChannel.NIGHTLY:
  74. return `${baseUrl}/${resolveDownloadPath(platform, resolvedBuildId).join('/')}`;
  75. case FirefoxChannel.DEVEDITION:
  76. case FirefoxChannel.BETA:
  77. case FirefoxChannel.STABLE:
  78. case FirefoxChannel.ESR:
  79. return `${baseUrl}/${resolvedBuildId}/${platformName(platform)}/en-US/${archive(platform, resolvedBuildId)}`;
  80. }
  81. }
  82. export function resolveDownloadPath(platform, buildId) {
  83. return [archiveNightly(platform, buildId)];
  84. }
  85. export function relativeExecutablePath(platform, buildId) {
  86. const [channel] = parseBuildId(buildId);
  87. switch (channel) {
  88. case FirefoxChannel.NIGHTLY:
  89. switch (platform) {
  90. case BrowserPlatform.MAC_ARM:
  91. case BrowserPlatform.MAC:
  92. return path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox');
  93. case BrowserPlatform.LINUX:
  94. return path.join('firefox', 'firefox');
  95. case BrowserPlatform.WIN32:
  96. case BrowserPlatform.WIN64:
  97. return path.join('firefox', 'firefox.exe');
  98. }
  99. case FirefoxChannel.BETA:
  100. case FirefoxChannel.DEVEDITION:
  101. case FirefoxChannel.ESR:
  102. case FirefoxChannel.STABLE:
  103. switch (platform) {
  104. case BrowserPlatform.MAC_ARM:
  105. case BrowserPlatform.MAC:
  106. return path.join('Firefox.app', 'Contents', 'MacOS', 'firefox');
  107. case BrowserPlatform.LINUX:
  108. return path.join('firefox', 'firefox');
  109. case BrowserPlatform.WIN32:
  110. case BrowserPlatform.WIN64:
  111. return path.join('core', 'firefox.exe');
  112. }
  113. }
  114. }
  115. export var FirefoxChannel;
  116. (function (FirefoxChannel) {
  117. FirefoxChannel["STABLE"] = "stable";
  118. FirefoxChannel["ESR"] = "esr";
  119. FirefoxChannel["DEVEDITION"] = "devedition";
  120. FirefoxChannel["BETA"] = "beta";
  121. FirefoxChannel["NIGHTLY"] = "nightly";
  122. })(FirefoxChannel || (FirefoxChannel = {}));
  123. export async function resolveBuildId(channel = FirefoxChannel.NIGHTLY) {
  124. const channelToVersionKey = {
  125. [FirefoxChannel.ESR]: 'FIREFOX_ESR',
  126. [FirefoxChannel.STABLE]: 'LATEST_FIREFOX_VERSION',
  127. [FirefoxChannel.DEVEDITION]: 'FIREFOX_DEVEDITION',
  128. [FirefoxChannel.BETA]: 'FIREFOX_DEVEDITION',
  129. [FirefoxChannel.NIGHTLY]: 'FIREFOX_NIGHTLY',
  130. };
  131. const versions = (await getJSON(new URL('https://product-details.mozilla.org/1.0/firefox_versions.json')));
  132. const version = versions[channelToVersionKey[channel]];
  133. if (!version) {
  134. throw new Error(`Channel ${channel} is not found.`);
  135. }
  136. return channel + '_' + version;
  137. }
  138. export async function createProfile(options) {
  139. if (!fs.existsSync(options.path)) {
  140. await fs.promises.mkdir(options.path, {
  141. recursive: true,
  142. });
  143. }
  144. await writePreferences({
  145. preferences: {
  146. ...defaultProfilePreferences(options.preferences),
  147. ...options.preferences,
  148. },
  149. path: options.path,
  150. });
  151. }
  152. function defaultProfilePreferences(extraPrefs) {
  153. const server = 'dummy.test';
  154. const defaultPrefs = {
  155. // Make sure Shield doesn't hit the network.
  156. 'app.normandy.api_url': '',
  157. // Disable Firefox old build background check
  158. 'app.update.checkInstallTime': false,
  159. // Disable automatically upgrading Firefox
  160. 'app.update.disabledForTesting': true,
  161. // Increase the APZ content response timeout to 1 minute
  162. 'apz.content_response_timeout': 60000,
  163. // Prevent various error message on the console
  164. // jest-puppeteer asserts that no error message is emitted by the console
  165. 'browser.contentblocking.features.standard': '-tp,tpPrivate,cookieBehavior0,-cm,-fp',
  166. // Enable the dump function: which sends messages to the system
  167. // console
  168. // https://bugzilla.mozilla.org/show_bug.cgi?id=1543115
  169. 'browser.dom.window.dump.enabled': true,
  170. // Disable topstories
  171. 'browser.newtabpage.activity-stream.feeds.system.topstories': false,
  172. // Always display a blank page
  173. 'browser.newtabpage.enabled': false,
  174. // Background thumbnails in particular cause grief: and disabling
  175. // thumbnails in general cannot hurt
  176. 'browser.pagethumbnails.capturing_disabled': true,
  177. // Disable safebrowsing components.
  178. 'browser.safebrowsing.blockedURIs.enabled': false,
  179. 'browser.safebrowsing.downloads.enabled': false,
  180. 'browser.safebrowsing.malware.enabled': false,
  181. 'browser.safebrowsing.phishing.enabled': false,
  182. // Disable updates to search engines.
  183. 'browser.search.update': false,
  184. // Do not restore the last open set of tabs if the browser has crashed
  185. 'browser.sessionstore.resume_from_crash': false,
  186. // Skip check for default browser on startup
  187. 'browser.shell.checkDefaultBrowser': false,
  188. // Disable newtabpage
  189. 'browser.startup.homepage': 'about:blank',
  190. // Do not redirect user when a milstone upgrade of Firefox is detected
  191. 'browser.startup.homepage_override.mstone': 'ignore',
  192. // Start with a blank page about:blank
  193. 'browser.startup.page': 0,
  194. // Do not allow background tabs to be zombified on Android: otherwise for
  195. // tests that open additional tabs: the test harness tab itself might get
  196. // unloaded
  197. 'browser.tabs.disableBackgroundZombification': false,
  198. // Do not warn when closing all other open tabs
  199. 'browser.tabs.warnOnCloseOtherTabs': false,
  200. // Do not warn when multiple tabs will be opened
  201. 'browser.tabs.warnOnOpen': false,
  202. // Do not automatically offer translations, as tests do not expect this.
  203. 'browser.translations.automaticallyPopup': false,
  204. // Disable the UI tour.
  205. 'browser.uitour.enabled': false,
  206. // Turn off search suggestions in the location bar so as not to trigger
  207. // network connections.
  208. 'browser.urlbar.suggest.searches': false,
  209. // Disable first run splash page on Windows 10
  210. 'browser.usedOnWindows10.introURL': '',
  211. // Do not warn on quitting Firefox
  212. 'browser.warnOnQuit': false,
  213. // Defensively disable data reporting systems
  214. 'datareporting.healthreport.documentServerURI': `http://${server}/dummy/healthreport/`,
  215. 'datareporting.healthreport.logging.consoleEnabled': false,
  216. 'datareporting.healthreport.service.enabled': false,
  217. 'datareporting.healthreport.service.firstRun': false,
  218. 'datareporting.healthreport.uploadEnabled': false,
  219. // Do not show datareporting policy notifications which can interfere with tests
  220. 'datareporting.policy.dataSubmissionEnabled': false,
  221. 'datareporting.policy.dataSubmissionPolicyBypassNotification': true,
  222. // DevTools JSONViewer sometimes fails to load dependencies with its require.js.
  223. // This doesn't affect Puppeteer but spams console (Bug 1424372)
  224. 'devtools.jsonview.enabled': false,
  225. // Disable popup-blocker
  226. 'dom.disable_open_during_load': false,
  227. // Enable the support for File object creation in the content process
  228. // Required for |Page.setFileInputFiles| protocol method.
  229. 'dom.file.createInChild': true,
  230. // Disable the ProcessHangMonitor
  231. 'dom.ipc.reportProcessHangs': false,
  232. // Disable slow script dialogues
  233. 'dom.max_chrome_script_run_time': 0,
  234. 'dom.max_script_run_time': 0,
  235. // Only load extensions from the application and user profile
  236. // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
  237. 'extensions.autoDisableScopes': 0,
  238. 'extensions.enabledScopes': 5,
  239. // Disable metadata caching for installed add-ons by default
  240. 'extensions.getAddons.cache.enabled': false,
  241. // Disable installing any distribution extensions or add-ons.
  242. 'extensions.installDistroAddons': false,
  243. // Disabled screenshots extension
  244. 'extensions.screenshots.disabled': true,
  245. // Turn off extension updates so they do not bother tests
  246. 'extensions.update.enabled': false,
  247. // Turn off extension updates so they do not bother tests
  248. 'extensions.update.notifyUser': false,
  249. // Make sure opening about:addons will not hit the network
  250. 'extensions.webservice.discoverURL': `http://${server}/dummy/discoveryURL`,
  251. // Allow the application to have focus even it runs in the background
  252. 'focusmanager.testmode': true,
  253. // Disable useragent updates
  254. 'general.useragent.updates.enabled': false,
  255. // Always use network provider for geolocation tests so we bypass the
  256. // macOS dialog raised by the corelocation provider
  257. 'geo.provider.testing': true,
  258. // Do not scan Wifi
  259. 'geo.wifi.scan': false,
  260. // No hang monitor
  261. 'hangmonitor.timeout': 0,
  262. // Show chrome errors and warnings in the error console
  263. 'javascript.options.showInConsole': true,
  264. // Disable download and usage of OpenH264: and Widevine plugins
  265. 'media.gmp-manager.updateEnabled': false,
  266. // Disable the GFX sanity window
  267. 'media.sanity-test.disabled': true,
  268. // Disable experimental feature that is only available in Nightly
  269. 'network.cookie.sameSite.laxByDefault': false,
  270. // Do not prompt for temporary redirects
  271. 'network.http.prompt-temp-redirect': false,
  272. // Disable speculative connections so they are not reported as leaking
  273. // when they are hanging around
  274. 'network.http.speculative-parallel-limit': 0,
  275. // Do not automatically switch between offline and online
  276. 'network.manage-offline-status': false,
  277. // Make sure SNTP requests do not hit the network
  278. 'network.sntp.pools': server,
  279. // Disable Flash.
  280. 'plugin.state.flash': 0,
  281. 'privacy.trackingprotection.enabled': false,
  282. // Can be removed once Firefox 89 is no longer supported
  283. // https://bugzilla.mozilla.org/show_bug.cgi?id=1710839
  284. 'remote.enabled': true,
  285. // Don't do network connections for mitm priming
  286. 'security.certerrors.mitm.priming.enabled': false,
  287. // Local documents have access to all other local documents,
  288. // including directory listings
  289. 'security.fileuri.strict_origin_policy': false,
  290. // Do not wait for the notification button security delay
  291. 'security.notification_enable_delay': 0,
  292. // Ensure blocklist updates do not hit the network
  293. 'services.settings.server': `http://${server}/dummy/blocklist/`,
  294. // Do not automatically fill sign-in forms with known usernames and
  295. // passwords
  296. 'signon.autofillForms': false,
  297. // Disable password capture, so that tests that include forms are not
  298. // influenced by the presence of the persistent doorhanger notification
  299. 'signon.rememberSignons': false,
  300. // Disable first-run welcome page
  301. 'startup.homepage_welcome_url': 'about:blank',
  302. // Disable first-run welcome page
  303. 'startup.homepage_welcome_url.additional': '',
  304. // Disable browser animations (tabs, fullscreen, sliding alerts)
  305. 'toolkit.cosmeticAnimations.enabled': false,
  306. // Prevent starting into safe mode after application crashes
  307. 'toolkit.startup.max_resumed_crashes': -1,
  308. };
  309. return Object.assign(defaultPrefs, extraPrefs);
  310. }
  311. /**
  312. * Populates the user.js file with custom preferences as needed to allow
  313. * Firefox's CDP support to properly function. These preferences will be
  314. * automatically copied over to prefs.js during startup of Firefox. To be
  315. * able to restore the original values of preferences a backup of prefs.js
  316. * will be created.
  317. *
  318. * @param prefs - List of preferences to add.
  319. * @param profilePath - Firefox profile to write the preferences to.
  320. */
  321. async function writePreferences(options) {
  322. const lines = Object.entries(options.preferences).map(([key, value]) => {
  323. return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value)});`;
  324. });
  325. await fs.promises.writeFile(path.join(options.path, 'user.js'), lines.join('\n'));
  326. // Create a backup of the preferences file if it already exitsts.
  327. const prefsPath = path.join(options.path, 'prefs.js');
  328. if (fs.existsSync(prefsPath)) {
  329. const prefsBackupPath = path.join(options.path, 'prefs.js.puppeteer');
  330. await fs.promises.copyFile(prefsPath, prefsBackupPath);
  331. }
  332. }
  333. export function compareVersions(a, b) {
  334. // TODO: this is a not very reliable check.
  335. return parseInt(a.replace('.', ''), 16) - parseInt(b.replace('.', ''), 16);
  336. }
  337. //# sourceMappingURL=firefox.js.map