<commit_before>/**
 * 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.
 */
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';

import { BrowserFetcher } from './BrowserFetcher.js';
import { Browser } from '../common/Browser.js';
import { BrowserRunner } from './BrowserRunner.js';
import { promisify } from 'util';

const mkdtempAsync = promisify(fs.mkdtemp);
const writeFileAsync = promisify(fs.writeFile);

import { ChromeArgOptions, LaunchOptions } from './LaunchOptions.js';
import { BrowserOptions } from '../common/BrowserConnector.js';
import { Product } from '../common/Product.js';

/**
 * Describes a launcher - a class that is able to create and launch a browser instance.
 * @public
 */
export interface ProductLauncher {
  launch(object);
  executablePath: () => string;
  defaultArgs(object);
  product: Product;
}

/**
 * @internal
 */
class ChromeLauncher implements ProductLauncher {
  _projectRoot: string;
  _preferredRevision: string;
  _isPuppeteerCore: boolean;

  constructor(
    projectRoot: string,
    preferredRevision: string,
    isPuppeteerCore: boolean
  ) {
    this._projectRoot = projectRoot;
    this._preferredRevision = preferredRevision;
    this._isPuppeteerCore = isPuppeteerCore;
  }

  async launch(
    options: LaunchOptions & ChromeArgOptions & BrowserOptions = {}
  ): Promise<Browser> {
    const {
      ignoreDefaultArgs = false,
      args = [],
      dumpio = false,
      executablePath = null,
      pipe = false,
      env = process.env,
      handleSIGINT = true,
      handleSIGTERM = true,
      handleSIGHUP = true,
      ignoreHTTPSErrors = false,
      defaultViewport = { width: 800, height: 600 },
      slowMo = 0,
      timeout = 30000,
    } = options;

    const profilePath = path.join(os.tmpdir(), 'puppeteer_dev_chrome_profile-');
    const chromeArguments = [];
    if (!ignoreDefaultArgs) chromeArguments.push(...this.defaultArgs(options));
    else if (Array.isArray(ignoreDefaultArgs))
      chromeArguments.push(
        ...this.defaultArgs(options).filter(
          (arg) => !ignoreDefaultArgs.includes(arg)
        )
      );
    else chromeArguments.push(...args);

    let temporaryUserDataDir = null;

    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'))) {
      temporaryUserDataDir = await mkdtempAsync(profilePath);
      chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
    }

    let chromeExecutable = executablePath;
    if (os.arch() === 'arm64') {
      chromeExecutable = '/usr/bin/chromium-browser';
    } else if (!executablePath) {
      const { missingText, executablePath } = resolveExecutablePath(this);
      if (missingText) throw new Error(missingText);
      chromeExecutable = executablePath;
    }

    const usePipe = chromeArguments.includes('--remote-debugging-pipe');
    const runner = new BrowserRunner(
      chromeExecutable,
      chromeArguments,
      temporaryUserDataDir
    );
    runner.start({
      handleSIGHUP,
      handleSIGTERM,
      handleSIGINT,
      dumpio,
      env,
      pipe: usePipe,
    });

    try {
      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');
      return browser;
    } catch (error) {
      runner.kill();
      throw error;
    }
  }

  /**
   * @param {!Launcher.ChromeArgOptions=} options
   * @returns {!Array<string>}
   */
  defaultArgs(options: ChromeArgOptions = {}): string[] {
    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',
      '--disable-features=TranslateUI',
      '--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=<PASSWORD>',
      '--use-mock-keychain',
      // TODO(sadym): remove '--enable-blink-features=IdleDetection'
      // once IdleDetection is turned on by default.
      '--enable-blink-features=IdleDetection',
    ];
    const {
      devtools = false,
      headless = !devtools,
      args = [],
      userDataDir = null,<commit_msg>fix(launcher): rename TranslateUI to Translate to match Chrome (#6692)<commit_after>/**
 * 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.
 */
import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';

import { BrowserFetcher } from './BrowserFetcher.js';
import { Browser } from '../common/Browser.js';
import { BrowserRunner } from './BrowserRunner.js';
import { promisify } from 'util';

const mkdtempAsync = promisify(fs.mkdtemp);
const writeFileAsync = promisify(fs.writeFile);

import { ChromeArgOptions, LaunchOptions } from './LaunchOptions.js';
import { BrowserOptions } from '../common/BrowserConnector.js';
import { Product } from '../common/Product.js';

/**
 * Describes a launcher - a class that is able to create and launch a browser instance.
 * @public
 */
export interface ProductLauncher {
  launch(object);
  executablePath: () => string;
  defaultArgs(object);
  product: Product;
}

/**
 * @internal
 */
class ChromeLauncher implements ProductLauncher {
  _projectRoot: string;
  _preferredRevision: string;
  _isPuppeteerCore: boolean;

  constructor(
    projectRoot: string,
    preferredRevision: string,
    isPuppeteerCore: boolean
  ) {
    this._projectRoot = projectRoot;
    this._preferredRevision = preferredRevision;
    this._isPuppeteerCore = isPuppeteerCore;
  }

  async launch(
    options: LaunchOptions & ChromeArgOptions & BrowserOptions = {}
  ): Promise<Browser> {
    const {
      ignoreDefaultArgs = false,
      args = [],
      dumpio = false,
      executablePath = null,
      pipe = false,
      env = process.env,
      handleSIGINT = true,
      handleSIGTERM = true,
      handleSIGHUP = true,
      ignoreHTTPSErrors = false,
      defaultViewport = { width: 800, height: 600 },
      slowMo = 0,
      timeout = 30000,
    } = options;

    const profilePath = path.join(os.tmpdir(), 'puppeteer_dev_chrome_profile-');
    const chromeArguments = [];
    if (!ignoreDefaultArgs) chromeArguments.push(...this.defaultArgs(options));
    else if (Array.isArray(ignoreDefaultArgs))
      chromeArguments.push(
        ...this.defaultArgs(options).filter(
          (arg) => !ignoreDefaultArgs.includes(arg)
        )
      );
    else chromeArguments.push(...args);

    let temporaryUserDataDir = null;

    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'))) {
      temporaryUserDataDir = await mkdtempAsync(profilePath);
      chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
    }

    let chromeExecutable = executablePath;
    if (os.arch() === 'arm64') {
      chromeExecutable = '/usr/bin/chromium-browser';
    } else if (!executablePath) {
      const { missingText, executablePath } = resolveExecutablePath(this);
      if (missingText) throw new Error(missingText);
      chromeExecutable = executablePath;
    }

    const usePipe = chromeArguments.includes('--remote-debugging-pipe');
    const runner = new BrowserRunner(
      chromeExecutable,
      chromeArguments,
      temporaryUserDataDir
    );
    runner.start({
      handleSIGHUP,
      handleSIGTERM,
      handleSIGINT,
      dumpio,
      env,
      pipe: usePipe,
    });

    try {
      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');
      return browser;
    } catch (error) {
      runner.kill();
      throw error;
    }
  }

  /**
   * @param {!Launcher.ChromeArgOptions=} options
   * @returns {!Array<string>}
   */
  defaultArgs(options: ChromeArgOptions = {}): string[] {
    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',
      '--disable-features=Translate',
      '--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=<PASSWORD>',
      '--use-mock-keychain',
      // TODO(sadym): remove '--enable-blink-features=IdleDetection'
      // once IdleDetection is turned on by default.
      '--enable-blink-features=IdleDetection',
    ];
    const {
      devtools = false,
      headless = !devtools,
      args = [],
      userDataDir = null,
{'fraction_non_alphanumeric': 0.1295672454003628, 'fraction_numerical': 0.004318908180012093, 'mean_word_length': 2.814827018121911, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '312807', 'n_tokens_mistral': 3481, 'n_tokens_neox': 3380, 'n_words': 765}