tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Tracing.ts (3634B)


      1 /**
      2 * @license
      3 * Copyright 2017 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 import type {CDPSession} from '../api/CDPSession.js';
      7 import {
      8  getReadableAsTypedArray,
      9  getReadableFromProtocolStream,
     10 } from '../common/util.js';
     11 import {assert} from '../util/assert.js';
     12 import {Deferred} from '../util/Deferred.js';
     13 import {isErrorLike} from '../util/ErrorLike.js';
     14 
     15 /**
     16 * @public
     17 */
     18 export interface TracingOptions {
     19  path?: string;
     20  screenshots?: boolean;
     21  categories?: string[];
     22 }
     23 
     24 /**
     25 * The Tracing class exposes the tracing audit interface.
     26 * @remarks
     27 * You can use `tracing.start` and `tracing.stop` to create a trace file
     28 * which can be opened in Chrome DevTools or {@link https://chromedevtools.github.io/timeline-viewer/ | timeline viewer}.
     29 *
     30 * @example
     31 *
     32 * ```ts
     33 * await page.tracing.start({path: 'trace.json'});
     34 * await page.goto('https://www.google.com');
     35 * await page.tracing.stop();
     36 * ```
     37 *
     38 * @public
     39 */
     40 export class Tracing {
     41  #client: CDPSession;
     42  #recording = false;
     43  #path?: string;
     44 
     45  /**
     46   * @internal
     47   */
     48  constructor(client: CDPSession) {
     49    this.#client = client;
     50  }
     51 
     52  /**
     53   * @internal
     54   */
     55  updateClient(client: CDPSession): void {
     56    this.#client = client;
     57  }
     58 
     59  /**
     60   * Starts a trace for the current page.
     61   * @remarks
     62   * Only one trace can be active at a time per browser.
     63   *
     64   * @param options - Optional `TracingOptions`.
     65   */
     66  async start(options: TracingOptions = {}): Promise<void> {
     67    assert(
     68      !this.#recording,
     69      'Cannot start recording trace while already recording trace.',
     70    );
     71 
     72    const defaultCategories = [
     73      '-*',
     74      'devtools.timeline',
     75      'v8.execute',
     76      'disabled-by-default-devtools.timeline',
     77      'disabled-by-default-devtools.timeline.frame',
     78      'toplevel',
     79      'blink.console',
     80      'blink.user_timing',
     81      'latencyInfo',
     82      'disabled-by-default-devtools.timeline.stack',
     83      'disabled-by-default-v8.cpu_profiler',
     84    ];
     85    const {path, screenshots = false, categories = defaultCategories} = options;
     86 
     87    if (screenshots) {
     88      categories.push('disabled-by-default-devtools.screenshot');
     89    }
     90 
     91    const excludedCategories = categories
     92      .filter(cat => {
     93        return cat.startsWith('-');
     94      })
     95      .map(cat => {
     96        return cat.slice(1);
     97      });
     98    const includedCategories = categories.filter(cat => {
     99      return !cat.startsWith('-');
    100    });
    101 
    102    this.#path = path;
    103    this.#recording = true;
    104    await this.#client.send('Tracing.start', {
    105      transferMode: 'ReturnAsStream',
    106      traceConfig: {
    107        excludedCategories,
    108        includedCategories,
    109      },
    110    });
    111  }
    112 
    113  /**
    114   * Stops a trace started with the `start` method.
    115   * @returns Promise which resolves to buffer with trace data.
    116   */
    117  async stop(): Promise<Uint8Array | undefined> {
    118    const contentDeferred = Deferred.create<Uint8Array | undefined>();
    119    this.#client.once('Tracing.tracingComplete', async event => {
    120      try {
    121        assert(event.stream, 'Missing "stream"');
    122        const readable = await getReadableFromProtocolStream(
    123          this.#client,
    124          event.stream,
    125        );
    126        const typedArray = await getReadableAsTypedArray(readable, this.#path);
    127        contentDeferred.resolve(typedArray ?? undefined);
    128      } catch (error) {
    129        if (isErrorLike(error)) {
    130          contentDeferred.reject(error);
    131        } else {
    132          contentDeferred.reject(new Error(`Unknown error: ${error}`));
    133        }
    134      }
    135    });
    136    await this.#client.send('Tracing.end');
    137    this.#recording = false;
    138    return await contentDeferred.valueOrThrow();
    139  }
    140 }