mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	chore: allow client operation w/o local utils (#34790)
This commit is contained in:
		
							parent
							
								
									90ec838318
								
							
						
					
					
						commit
						163aacf4b6
					
				@ -1,5 +1,4 @@
 | 
			
		||||
[*]
 | 
			
		||||
../common/
 | 
			
		||||
../protocol/
 | 
			
		||||
../utils/**
 | 
			
		||||
../utilsBundle.ts
 | 
			
		||||
../utils/isomorphic
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
 | 
			
		||||
import { isRegExp, isString } from '../utils/isomorphic/rtti';
 | 
			
		||||
import { monotonicTime } from '../utils/isomorphic/time';
 | 
			
		||||
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
 | 
			
		||||
import { connectOverWebSocket } from './webSocket';
 | 
			
		||||
 | 
			
		||||
import type { Page } from './page';
 | 
			
		||||
import type * as types from './types';
 | 
			
		||||
@ -69,9 +70,8 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
 | 
			
		||||
    return await this._wrapApiCall(async () => {
 | 
			
		||||
      const deadline = options.timeout ? monotonicTime() + options.timeout : 0;
 | 
			
		||||
      const headers = { 'x-playwright-browser': 'android', ...options.headers };
 | 
			
		||||
      const localUtils = this._connection.localUtils();
 | 
			
		||||
      const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout };
 | 
			
		||||
      const connection = await localUtils.connect(connectParams);
 | 
			
		||||
      const connection = await connectOverWebSocket(this._connection, connectParams);
 | 
			
		||||
 | 
			
		||||
      let device: AndroidDevice;
 | 
			
		||||
      connection.on('close', () => {
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
import { ChannelOwner } from './channelOwner';
 | 
			
		||||
import { Stream } from './stream';
 | 
			
		||||
import { mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
 | 
			
		||||
import type * as channels from '@protocol/channels';
 | 
			
		||||
import type { Readable } from 'stream';
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ import { CDPSession } from './cdpSession';
 | 
			
		||||
import { ChannelOwner } from './channelOwner';
 | 
			
		||||
import { isTargetClosedError } from './errors';
 | 
			
		||||
import { Events } from './events';
 | 
			
		||||
import { mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
 | 
			
		||||
import type { BrowserType } from './browserType';
 | 
			
		||||
import type { Page } from './page';
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ import { Waiter } from './waiter';
 | 
			
		||||
import { WebError } from './webError';
 | 
			
		||||
import { Worker } from './worker';
 | 
			
		||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
 | 
			
		||||
import { mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
 | 
			
		||||
import { urlMatchesEqual } from '../utils/isomorphic/urlMatch';
 | 
			
		||||
import { isRegExp, isString } from '../utils/isomorphic/rtti';
 | 
			
		||||
@ -361,11 +361,14 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full' } = {}): Promise<void> {
 | 
			
		||||
    const localUtils = this._connection.localUtils();
 | 
			
		||||
    if (!localUtils)
 | 
			
		||||
      throw new Error('Route from har is not supported in thin clients');
 | 
			
		||||
    if (options.update) {
 | 
			
		||||
      await this._recordIntoHAR(har, null, options);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
 | 
			
		||||
    const harRouter = await HarRouter.create(localUtils, har, options.notFound || 'abort', { urlMatch: options.url });
 | 
			
		||||
    this._harRouters.push(harRouter);
 | 
			
		||||
    await harRouter.addContextRoute(this);
 | 
			
		||||
  }
 | 
			
		||||
@ -484,8 +487,11 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
 | 
			
		||||
        const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip');
 | 
			
		||||
        const needCompressed = harParams.path.endsWith('.zip');
 | 
			
		||||
        if (isCompressed && !needCompressed) {
 | 
			
		||||
          const localUtils = this._connection.localUtils();
 | 
			
		||||
          if (!localUtils)
 | 
			
		||||
            throw new Error('Uncompressed har is not supported in thin clients');
 | 
			
		||||
          await artifact.saveAs(harParams.path + '.tmp');
 | 
			
		||||
          await this._connection.localUtils().harUnzip({ zipFile: harParams.path + '.tmp', harFile: harParams.path });
 | 
			
		||||
          await localUtils.harUnzip({ zipFile: harParams.path + '.tmp', harFile: harParams.path });
 | 
			
		||||
        } else {
 | 
			
		||||
          await artifact.saveAs(harParams.path);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
 | 
			
		||||
import { monotonicTime } from '../utils/isomorphic/time';
 | 
			
		||||
import { raceAgainstDeadline } from '../utils/isomorphic/timeoutRunner';
 | 
			
		||||
import { connectOverWebSocket } from './webSocket';
 | 
			
		||||
 | 
			
		||||
import type { Playwright } from './playwright';
 | 
			
		||||
import type { ConnectOptions, LaunchOptions, LaunchPersistentContextOptions, LaunchServerOptions, Logger } from './types';
 | 
			
		||||
@ -124,7 +125,6 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
 | 
			
		||||
    return await this._wrapApiCall(async () => {
 | 
			
		||||
      const deadline = params.timeout ? monotonicTime() + params.timeout : 0;
 | 
			
		||||
      const headers = { 'x-playwright-browser': this.name(), ...params.headers };
 | 
			
		||||
      const localUtils = this._connection.localUtils();
 | 
			
		||||
      const connectParams: channels.LocalUtilsConnectParams = {
 | 
			
		||||
        wsEndpoint: params.wsEndpoint,
 | 
			
		||||
        headers,
 | 
			
		||||
@ -134,7 +134,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
 | 
			
		||||
      };
 | 
			
		||||
      if ((params as any).__testHookRedirectPortForwarding)
 | 
			
		||||
        connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
 | 
			
		||||
      const connection = await localUtils.connect(connectParams);
 | 
			
		||||
      const connection = await connectOverWebSocket(this._connection, connectParams);
 | 
			
		||||
      let browser: Browser;
 | 
			
		||||
      connection.on('close', () => {
 | 
			
		||||
        // Emulate all pages, contexts and the browser closing upon disconnect.
 | 
			
		||||
 | 
			
		||||
@ -108,8 +108,8 @@ export class Connection extends EventEmitter {
 | 
			
		||||
    return this._rawBuffers;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  localUtils(): LocalUtils {
 | 
			
		||||
    return this._localUtils!;
 | 
			
		||||
  localUtils(): LocalUtils | undefined {
 | 
			
		||||
    return this._localUtils;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async initializePlaywright(): Promise<Playwright> {
 | 
			
		||||
 | 
			
		||||
@ -20,10 +20,10 @@ import { promisify } from 'util';
 | 
			
		||||
import { Frame } from './frame';
 | 
			
		||||
import { JSHandle, parseResult, serializeArgument } from './jsHandle';
 | 
			
		||||
import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { fileUploadSizeLimit, mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { fileUploadSizeLimit, mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
import { isString } from '../utils/isomorphic/rtti';
 | 
			
		||||
import { mime } from '../utilsBundle';
 | 
			
		||||
import { WritableStream } from './writableStream';
 | 
			
		||||
import { getMimeTypeForPath } from '../utils/isomorphic/mimeType';
 | 
			
		||||
 | 
			
		||||
import type { BrowserContext } from './browserContext';
 | 
			
		||||
import type { ChannelOwner } from './channelOwner';
 | 
			
		||||
@ -327,7 +327,7 @@ export async function convertInputFiles(platform: Platform, files: string | File
 | 
			
		||||
 | 
			
		||||
export function determineScreenshotType(options: { path?: string, type?: 'png' | 'jpeg' }): 'png' | 'jpeg' | undefined {
 | 
			
		||||
  if (options.path) {
 | 
			
		||||
    const mimeType = mime.getType(options.path);
 | 
			
		||||
    const mimeType = getMimeTypeForPath(options.path);
 | 
			
		||||
    if (mimeType === 'image/png')
 | 
			
		||||
      return 'png';
 | 
			
		||||
    else if (mimeType === 'image/jpeg')
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ import { TargetClosedError, isTargetClosedError } from './errors';
 | 
			
		||||
import { RawHeaders } from './network';
 | 
			
		||||
import { Tracing } from './tracing';
 | 
			
		||||
import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
 | 
			
		||||
import { isString } from '../utils/isomorphic/rtti';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,17 +14,12 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import type { Platform } from './platform';
 | 
			
		||||
import type { Platform } from '../common/platform';
 | 
			
		||||
 | 
			
		||||
// Keep in sync with the server.
 | 
			
		||||
export const fileUploadSizeLimit = 50 * 1024 * 1024;
 | 
			
		||||
 | 
			
		||||
export async function mkdirIfNeeded(platform: Platform, filePath: string) {
 | 
			
		||||
  // This will harmlessly throw on windows if the dirname is the root directory.
 | 
			
		||||
  await platform.fs().promises.mkdir(platform.path().dirname(filePath), { recursive: true }).catch(() => {});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function removeFolders(platform: Platform, dirs: string[]): Promise<Error[]> {
 | 
			
		||||
  return await Promise.all(dirs.map((dir: string) =>
 | 
			
		||||
    platform.fs().promises.rm(dir, { recursive: true, force: true, maxRetries: 10 }).catch(e => e)
 | 
			
		||||
  ));
 | 
			
		||||
}
 | 
			
		||||
@ -15,12 +15,8 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { ChannelOwner } from './channelOwner';
 | 
			
		||||
import { Connection } from './connection';
 | 
			
		||||
import * as localUtils from '../common/localUtils';
 | 
			
		||||
 | 
			
		||||
import type { HeadersArray, Size } from './types';
 | 
			
		||||
import type { HarBackend } from '../common/harBackend';
 | 
			
		||||
import type { Platform } from '../common/platform';
 | 
			
		||||
import type { Size } from './types';
 | 
			
		||||
import type * as channels from '@protocol/channels';
 | 
			
		||||
 | 
			
		||||
type DeviceDescriptor = {
 | 
			
		||||
@ -35,8 +31,6 @@ type Devices = { [name: string]: DeviceDescriptor };
 | 
			
		||||
 | 
			
		||||
export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
 | 
			
		||||
  readonly devices: Devices;
 | 
			
		||||
  private _harBackends = new Map<string, HarBackend>();
 | 
			
		||||
  private _stackSessions = new Map<string, localUtils.StackSession>();
 | 
			
		||||
 | 
			
		||||
  constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.LocalUtilsInitializer) {
 | 
			
		||||
    super(parent, type, guid, initializer);
 | 
			
		||||
@ -47,132 +41,34 @@ export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async zip(params: channels.LocalUtilsZipParams): Promise<void> {
 | 
			
		||||
    return await localUtils.zip(this._platform, this._stackSessions, params);
 | 
			
		||||
    return await this._channel.zip(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harOpen(params: channels.LocalUtilsHarOpenParams): Promise<channels.LocalUtilsHarOpenResult> {
 | 
			
		||||
    return await localUtils.harOpen(this._platform, this._harBackends, params);
 | 
			
		||||
    return await this._channel.harOpen(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harLookup(params: channels.LocalUtilsHarLookupParams): Promise<channels.LocalUtilsHarLookupResult> {
 | 
			
		||||
    return await localUtils.harLookup(this._harBackends, params);
 | 
			
		||||
    return await this._channel.harLookup(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harClose(params: channels.LocalUtilsHarCloseParams): Promise<void> {
 | 
			
		||||
    return await localUtils.harClose(this._harBackends, params);
 | 
			
		||||
    return await this._channel.harClose(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harUnzip(params: channels.LocalUtilsHarUnzipParams): Promise<void> {
 | 
			
		||||
    return await localUtils.harUnzip(params);
 | 
			
		||||
    return await this._channel.harUnzip(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async tracingStarted(params: channels.LocalUtilsTracingStartedParams): Promise<channels.LocalUtilsTracingStartedResult> {
 | 
			
		||||
    return await localUtils.tracingStarted(this._stackSessions, params);
 | 
			
		||||
    return await this._channel.tracingStarted(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async traceDiscarded(params: channels.LocalUtilsTraceDiscardedParams): Promise<void> {
 | 
			
		||||
    return await localUtils.traceDiscarded(this._platform, this._stackSessions, params);
 | 
			
		||||
    return await this._channel.traceDiscarded(params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addStackToTracingNoReply(params: channels.LocalUtilsAddStackToTracingNoReplyParams): Promise<void> {
 | 
			
		||||
    return await localUtils.addStackToTracingNoReply(this._stackSessions, params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async connect(params: channels.LocalUtilsConnectParams): Promise<Connection> {
 | 
			
		||||
    const transport = this._platform.ws ? new WebSocketTransport(this._platform) : new JsonPipeTransport(this);
 | 
			
		||||
    const connectHeaders = await transport.connect(params);
 | 
			
		||||
    const connection = new Connection(this, this._platform, this._instrumentation, connectHeaders);
 | 
			
		||||
    connection.markAsRemote();
 | 
			
		||||
    connection.on('close', () => transport.close());
 | 
			
		||||
 | 
			
		||||
    let closeError: string | undefined;
 | 
			
		||||
    const onTransportClosed = (reason?: string) => {
 | 
			
		||||
      connection.close(reason || closeError);
 | 
			
		||||
    };
 | 
			
		||||
    transport.onClose(reason => onTransportClosed(reason));
 | 
			
		||||
    connection.onmessage = message => transport.send(message).catch(() => onTransportClosed());
 | 
			
		||||
    transport.onMessage(message => {
 | 
			
		||||
      try {
 | 
			
		||||
        connection!.dispatch(message);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        closeError = String(e);
 | 
			
		||||
        transport.close();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return connection;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
interface Transport {
 | 
			
		||||
  connect(params: channels.LocalUtilsConnectParams): Promise<HeadersArray>;
 | 
			
		||||
  send(message: any): Promise<void>;
 | 
			
		||||
  onMessage(callback: (message: object) => void): void;
 | 
			
		||||
  onClose(callback: (reason?: string) => void): void;
 | 
			
		||||
  close(): Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class JsonPipeTransport implements Transport {
 | 
			
		||||
  private _pipe: channels.JsonPipeChannel | undefined;
 | 
			
		||||
  private _owner: ChannelOwner<channels.LocalUtilsChannel>;
 | 
			
		||||
 | 
			
		||||
  constructor(owner: ChannelOwner<channels.LocalUtilsChannel>) {
 | 
			
		||||
    this._owner = owner;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async connect(params: channels.LocalUtilsConnectParams) {
 | 
			
		||||
    const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => {
 | 
			
		||||
      return await this._owner._channel.connect(params);
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
    this._pipe = pipe;
 | 
			
		||||
    return connectHeaders;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async send(message: object) {
 | 
			
		||||
    this._owner._wrapApiCall(async () => {
 | 
			
		||||
      await this._pipe!.send({ message });
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMessage(callback: (message: object) => void) {
 | 
			
		||||
    this._pipe!.on('message', ({ message }) => callback(message));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClose(callback: (reason?: string) => void) {
 | 
			
		||||
    this._pipe!.on('closed', ({ reason }) => callback(reason));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async close() {
 | 
			
		||||
    await this._owner._wrapApiCall(async () => {
 | 
			
		||||
      await this._pipe!.close().catch(() => {});
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class WebSocketTransport implements Transport {
 | 
			
		||||
  private _platform: Platform;
 | 
			
		||||
  private _ws: WebSocket | undefined;
 | 
			
		||||
 | 
			
		||||
  constructor(platform: Platform) {
 | 
			
		||||
    this._platform = platform;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async connect(params: channels.LocalUtilsConnectParams) {
 | 
			
		||||
    this._ws = this._platform.ws!(params.wsEndpoint);
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async send(message: object) {
 | 
			
		||||
    this._ws!.send(JSON.stringify(message));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMessage(callback: (message: object) => void) {
 | 
			
		||||
    this._ws!.addEventListener('message', event => callback(JSON.parse(event.data)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClose(callback: (reason?: string) => void) {
 | 
			
		||||
    this._ws!.addEventListener('close', () => callback());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async close() {
 | 
			
		||||
    this._ws!.close();
 | 
			
		||||
    return await this._channel.addStackToTracingNoReply(params);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ import { LongStandingScope, ManualPromise } from '../utils/isomorphic/manualProm
 | 
			
		||||
import { MultiMap } from '../utils/isomorphic/multimap';
 | 
			
		||||
import { isRegExp, isString } from '../utils/isomorphic/rtti';
 | 
			
		||||
import { rewriteErrorMessage } from '../utils/isomorphic/stackTrace';
 | 
			
		||||
import { mime } from '../utilsBundle';
 | 
			
		||||
import { getMimeTypeForPath } from '../utils/isomorphic/mimeType';
 | 
			
		||||
 | 
			
		||||
import type { BrowserContext } from './browserContext';
 | 
			
		||||
import type { Page } from './page';
 | 
			
		||||
@ -413,7 +413,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
 | 
			
		||||
    else if (options.json)
 | 
			
		||||
      headers['content-type'] = 'application/json';
 | 
			
		||||
    else if (options.path)
 | 
			
		||||
      headers['content-type'] = mime.getType(options.path) || 'application/octet-stream';
 | 
			
		||||
      headers['content-type'] = getMimeTypeForPath(options.path) || 'application/octet-stream';
 | 
			
		||||
    if (length && !('content-length' in headers))
 | 
			
		||||
      headers['content-length'] = String(length);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ import { Waiter } from './waiter';
 | 
			
		||||
import { Worker } from './worker';
 | 
			
		||||
import { TimeoutSettings } from '../utils/isomorphic/timeoutSettings';
 | 
			
		||||
import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { mkdirIfNeeded } from '../common/fileUtils';
 | 
			
		||||
import { mkdirIfNeeded } from './fileUtils';
 | 
			
		||||
import { headersObjectToArray } from '../utils/isomorphic/headers';
 | 
			
		||||
import { trimStringWithEllipsis  } from '../utils/isomorphic/stringUtils';
 | 
			
		||||
import { urlMatches, urlMatchesEqual } from '../utils/isomorphic/urlMatch';
 | 
			
		||||
@ -525,11 +525,14 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async routeFromHAR(har: string, options: { url?: string | RegExp, notFound?: 'abort' | 'fallback', update?: boolean, updateContent?: 'attach' | 'embed', updateMode?: 'minimal' | 'full'} = {}): Promise<void> {
 | 
			
		||||
    const localUtils = this._connection.localUtils();
 | 
			
		||||
    if (!localUtils)
 | 
			
		||||
      throw new Error('Route from har is not supported in thin clients');
 | 
			
		||||
    if (options.update) {
 | 
			
		||||
      await this._browserContext._recordIntoHAR(har, this, options);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const harRouter = await HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', { urlMatch: options.url });
 | 
			
		||||
    const harRouter = await HarRouter.create(localUtils, har, options.notFound || 'abort', { urlMatch: options.url });
 | 
			
		||||
    this._harRouters.push(harRouter);
 | 
			
		||||
    await harRouter.addPageRoute(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -69,8 +69,8 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
 | 
			
		||||
      this._isTracing = true;
 | 
			
		||||
      this._connection.setIsTracing(true);
 | 
			
		||||
    }
 | 
			
		||||
    const result = await this._connection.localUtils().tracingStarted({ tracesDir: this._tracesDir, traceName });
 | 
			
		||||
    this._stacksId = result.stacksId;
 | 
			
		||||
    const result = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName });
 | 
			
		||||
    this._stacksId = result?.stacksId;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async stopChunk(options: { path?: string } = {}) {
 | 
			
		||||
@ -89,15 +89,19 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
 | 
			
		||||
      // Not interested in artifacts.
 | 
			
		||||
      await this._channel.tracingStopChunk({ mode: 'discard' });
 | 
			
		||||
      if (this._stacksId)
 | 
			
		||||
        await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId });
 | 
			
		||||
        await this._connection.localUtils()!.traceDiscarded({ stacksId: this._stacksId });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const localUtils = this._connection.localUtils();
 | 
			
		||||
    if (!localUtils)
 | 
			
		||||
      throw new Error('Cannot save trace in thin clients');
 | 
			
		||||
 | 
			
		||||
    const isLocal = !this._connection.isRemote();
 | 
			
		||||
 | 
			
		||||
    if (isLocal) {
 | 
			
		||||
      const result = await this._channel.tracingStopChunk({ mode: 'entries' });
 | 
			
		||||
      await this._connection.localUtils().zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources });
 | 
			
		||||
      await localUtils.zip({ zipFile: filePath, entries: result.entries!, mode: 'write', stacksId: this._stacksId, includeSources: this._includeSources });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -106,7 +110,7 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
 | 
			
		||||
    // The artifact may be missing if the browser closed while stopping tracing.
 | 
			
		||||
    if (!result.artifact) {
 | 
			
		||||
      if (this._stacksId)
 | 
			
		||||
        await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId });
 | 
			
		||||
        await localUtils.traceDiscarded({ stacksId: this._stacksId });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -115,7 +119,7 @@ export class Tracing extends ChannelOwner<channels.TracingChannel> implements ap
 | 
			
		||||
    await artifact.saveAs(filePath);
 | 
			
		||||
    await artifact.delete();
 | 
			
		||||
 | 
			
		||||
    await this._connection.localUtils().zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources });
 | 
			
		||||
    await localUtils.zip({ zipFile: filePath, entries: [], mode: 'append', stacksId: this._stacksId, includeSources: this._includeSources });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _resetStackCounter() {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										116
									
								
								packages/playwright-core/src/client/webSocket.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								packages/playwright-core/src/client/webSocket.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright (c) Microsoft Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 { ChannelOwner } from './channelOwner';
 | 
			
		||||
import { Connection } from './connection';
 | 
			
		||||
 | 
			
		||||
import type { HeadersArray } from './types';
 | 
			
		||||
import type * as channels from '@protocol/channels';
 | 
			
		||||
 | 
			
		||||
export async function connectOverWebSocket(parentConnection: Connection, params: channels.LocalUtilsConnectParams): Promise<Connection> {
 | 
			
		||||
  const localUtils = parentConnection.localUtils();
 | 
			
		||||
  const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
 | 
			
		||||
  const connectHeaders = await transport.connect(params);
 | 
			
		||||
  const connection = new Connection(localUtils, parentConnection.platform, parentConnection._instrumentation, connectHeaders);
 | 
			
		||||
  connection.markAsRemote();
 | 
			
		||||
  connection.on('close', () => transport.close());
 | 
			
		||||
 | 
			
		||||
  let closeError: string | undefined;
 | 
			
		||||
  const onTransportClosed = (reason?: string) => {
 | 
			
		||||
    connection.close(reason || closeError);
 | 
			
		||||
  };
 | 
			
		||||
  transport.onClose(reason => onTransportClosed(reason));
 | 
			
		||||
  connection.onmessage = message => transport.send(message).catch(() => onTransportClosed());
 | 
			
		||||
  transport.onMessage(message => {
 | 
			
		||||
    try {
 | 
			
		||||
      connection!.dispatch(message);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      closeError = String(e);
 | 
			
		||||
      transport.close();
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return connection;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Transport {
 | 
			
		||||
  connect(params: channels.LocalUtilsConnectParams): Promise<HeadersArray>;
 | 
			
		||||
  send(message: any): Promise<void>;
 | 
			
		||||
  onMessage(callback: (message: object) => void): void;
 | 
			
		||||
  onClose(callback: (reason?: string) => void): void;
 | 
			
		||||
  close(): Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class JsonPipeTransport implements Transport {
 | 
			
		||||
  private _pipe: channels.JsonPipeChannel | undefined;
 | 
			
		||||
  private _owner: ChannelOwner<channels.LocalUtilsChannel>;
 | 
			
		||||
 | 
			
		||||
  constructor(owner: ChannelOwner<channels.LocalUtilsChannel>) {
 | 
			
		||||
    this._owner = owner;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async connect(params: channels.LocalUtilsConnectParams) {
 | 
			
		||||
    const { pipe, headers: connectHeaders } = await this._owner._wrapApiCall(async () => {
 | 
			
		||||
      return await this._owner._channel.connect(params);
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
    this._pipe = pipe;
 | 
			
		||||
    return connectHeaders;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async send(message: object) {
 | 
			
		||||
    this._owner._wrapApiCall(async () => {
 | 
			
		||||
      await this._pipe!.send({ message });
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMessage(callback: (message: object) => void) {
 | 
			
		||||
    this._pipe!.on('message', ({ message }) => callback(message));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClose(callback: (reason?: string) => void) {
 | 
			
		||||
    this._pipe!.on('closed', ({ reason }) => callback(reason));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async close() {
 | 
			
		||||
    await this._owner._wrapApiCall(async () => {
 | 
			
		||||
      await this._pipe!.close().catch(() => {});
 | 
			
		||||
    }, /* isInternal */ true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class WebSocketTransport implements Transport {
 | 
			
		||||
  private _ws: WebSocket | undefined;
 | 
			
		||||
 | 
			
		||||
  async connect(params: channels.LocalUtilsConnectParams) {
 | 
			
		||||
    this._ws = new window.WebSocket(params.wsEndpoint);
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async send(message: object) {
 | 
			
		||||
    this._ws!.send(JSON.stringify(message));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMessage(callback: (message: object) => void) {
 | 
			
		||||
    this._ws!.addEventListener('message', event => callback(JSON.parse(event.data)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onClose(callback: (reason?: string) => void) {
 | 
			
		||||
    this._ws!.addEventListener('close', () => callback());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async close() {
 | 
			
		||||
    this._ws!.close();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,2 @@
 | 
			
		||||
[*]
 | 
			
		||||
../utils/
 | 
			
		||||
../utils/isomorphic/
 | 
			
		||||
../utilsBundle.ts
 | 
			
		||||
../zipBundle.ts
 | 
			
		||||
 | 
			
		||||
@ -14,12 +14,10 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import * as crypto from 'crypto';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
 | 
			
		||||
import { webColors, noColors } from '../utils/isomorphic/colors';
 | 
			
		||||
 | 
			
		||||
import type * as fs from 'fs';
 | 
			
		||||
import type * as path from 'path';
 | 
			
		||||
import type { Colors } from '../utils/isomorphic/colors';
 | 
			
		||||
 | 
			
		||||
export type Zone = {
 | 
			
		||||
@ -37,6 +35,8 @@ const noopZone: Zone = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Platform = {
 | 
			
		||||
  name: 'node' | 'web' | 'empty';
 | 
			
		||||
 | 
			
		||||
  calculateSha1(text: string): Promise<string>;
 | 
			
		||||
  colors: Colors;
 | 
			
		||||
  createGuid: () => string;
 | 
			
		||||
@ -46,21 +46,22 @@ export type Platform = {
 | 
			
		||||
  log(name: 'api' | 'channel', message: string | Error | object): void;
 | 
			
		||||
  path: () => typeof path;
 | 
			
		||||
  pathSeparator: string;
 | 
			
		||||
  ws?: (url: string) => WebSocket;
 | 
			
		||||
  zones: { empty: Zone, current: () => Zone; };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const webPlatform: Platform = {
 | 
			
		||||
  name: 'web',
 | 
			
		||||
 | 
			
		||||
  calculateSha1: async (text: string) => {
 | 
			
		||||
    const bytes = new TextEncoder().encode(text);
 | 
			
		||||
    const hashBuffer = await crypto.subtle.digest('SHA-1', bytes);
 | 
			
		||||
    const hashBuffer = await window.crypto.subtle.digest('SHA-1', bytes);
 | 
			
		||||
    return Array.from(new Uint8Array(hashBuffer), b => b.toString(16).padStart(2, '0')).join('');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  colors: webColors,
 | 
			
		||||
 | 
			
		||||
  createGuid: () => {
 | 
			
		||||
    return Array.from(crypto.getRandomValues(new Uint8Array(16)), b => b.toString(16).padStart(2, '0')).join('');
 | 
			
		||||
    return Array.from(window.crypto.getRandomValues(new Uint8Array(16)), b => b.toString(16).padStart(2, '0')).join('');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  fs: () => {
 | 
			
		||||
@ -82,12 +83,12 @@ export const webPlatform: Platform = {
 | 
			
		||||
 | 
			
		||||
  pathSeparator: '/',
 | 
			
		||||
 | 
			
		||||
  ws: (url: string) => new WebSocket(url),
 | 
			
		||||
 | 
			
		||||
  zones: { empty: noopZone, current: () => noopZone },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const emptyPlatform: Platform = {
 | 
			
		||||
  name: 'empty',
 | 
			
		||||
 | 
			
		||||
  calculateSha1: async () => {
 | 
			
		||||
    throw new Error('Not implemented');
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -1,23 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright (c) Microsoft Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export interface Progress {
 | 
			
		||||
  log(message: string): void;
 | 
			
		||||
  timeUntilDeadline(): number;
 | 
			
		||||
  isRunning(): boolean;
 | 
			
		||||
  cleanupWhenAborted(cleanup: () => any): void;
 | 
			
		||||
  throwIfAborted(): void;
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
[*]
 | 
			
		||||
../common/
 | 
			
		||||
../utils/
 | 
			
		||||
 | 
			
		||||
../utils/isomorphic
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { isUnderTest } from '../utils';
 | 
			
		||||
import { isUnderTest } from '../utils/isomorphic/debug';
 | 
			
		||||
 | 
			
		||||
export class ValidationError extends Error {}
 | 
			
		||||
export type Validator = (arg: any, path: string, context: ValidatorContext) => any;
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,7 @@
 | 
			
		||||
 | 
			
		||||
import { Dispatcher } from './dispatcher';
 | 
			
		||||
import { SdkObject } from '../../server/instrumentation';
 | 
			
		||||
import * as localUtils from '../../common/localUtils';
 | 
			
		||||
import { nodePlatform } from '../utils/nodePlatform';
 | 
			
		||||
import * as localUtils from '../localUtils';
 | 
			
		||||
import { getUserAgent } from '../utils/userAgent';
 | 
			
		||||
import { deviceDescriptors as descriptors }  from '../deviceDescriptors';
 | 
			
		||||
import { JsonPipeDispatcher } from '../dispatchers/jsonPipeDispatcher';
 | 
			
		||||
@ -26,7 +25,7 @@ import { SocksInterceptor } from '../socksInterceptor';
 | 
			
		||||
import { WebSocketTransport } from '../transport';
 | 
			
		||||
import { fetchData } from '../utils/network';
 | 
			
		||||
 | 
			
		||||
import type { HarBackend } from '../../common/harBackend';
 | 
			
		||||
import type { HarBackend } from '../harBackend';
 | 
			
		||||
import type { CallMetadata } from '../instrumentation';
 | 
			
		||||
import type { Playwright } from '../playwright';
 | 
			
		||||
import type { RootDispatcher } from './dispatcher';
 | 
			
		||||
@ -50,11 +49,11 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async zip(params: channels.LocalUtilsZipParams): Promise<void> {
 | 
			
		||||
    return await localUtils.zip(nodePlatform, this._stackSessions, params);
 | 
			
		||||
    return await localUtils.zip(this._stackSessions, params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harOpen(params: channels.LocalUtilsHarOpenParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarOpenResult> {
 | 
			
		||||
    return await localUtils.harOpen(nodePlatform, this._harBackends, params);
 | 
			
		||||
    return await localUtils.harOpen(this._harBackends, params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async harLookup(params: channels.LocalUtilsHarLookupParams, metadata: CallMetadata): Promise<channels.LocalUtilsHarLookupResult> {
 | 
			
		||||
@ -74,7 +73,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async traceDiscarded(params: channels.LocalUtilsTraceDiscardedParams, metadata?: CallMetadata | undefined): Promise<void> {
 | 
			
		||||
    return await localUtils.traceDiscarded(nodePlatform, this._stackSessions, params);
 | 
			
		||||
    return await localUtils.traceDiscarded(this._stackSessions, params);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async addStackToTracingNoReply(params: channels.LocalUtilsAddStackToTracingNoReplyParams, metadata?: CallMetadata | undefined): Promise<void> {
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,6 @@ import * as fs from 'fs';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
 | 
			
		||||
import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { fileUploadSizeLimit } from '../common/fileUtils';
 | 
			
		||||
import { mime } from '../utilsBundle';
 | 
			
		||||
 | 
			
		||||
import type { WritableStreamDispatcher } from './dispatchers/writableStreamDispatcher';
 | 
			
		||||
@ -27,6 +26,9 @@ import type { Frame } from './frames';
 | 
			
		||||
import type * as types from './types';
 | 
			
		||||
import type * as channels from '@protocol/channels';
 | 
			
		||||
 | 
			
		||||
// Keep in sync with the client.
 | 
			
		||||
export const fileUploadSizeLimit = 50 * 1024 * 1024;
 | 
			
		||||
 | 
			
		||||
async function filesExceedUploadLimit(files: string[]) {
 | 
			
		||||
  const sizes = await Promise.all(files.map(async file => (await fs.promises.stat(file)).size));
 | 
			
		||||
  return sizes.reduce((total, size) => total + size, 0) >= fileUploadSizeLimit;
 | 
			
		||||
 | 
			
		||||
@ -14,11 +14,14 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { ZipFile } from '../utils/zipFile';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
 | 
			
		||||
import type { HeadersArray } from './types';
 | 
			
		||||
import { createGuid } from './utils/crypto';
 | 
			
		||||
import { ZipFile } from './utils/zipFile';
 | 
			
		||||
 | 
			
		||||
import type { HeadersArray } from '../common/types';
 | 
			
		||||
import type * as har from '@trace/har';
 | 
			
		||||
import type { Platform } from './platform';
 | 
			
		||||
 | 
			
		||||
const redirectStatus = [301, 302, 303, 307, 308];
 | 
			
		||||
 | 
			
		||||
@ -27,11 +30,9 @@ export class HarBackend {
 | 
			
		||||
  private _harFile: har.HARFile;
 | 
			
		||||
  private _zipFile: ZipFile | null;
 | 
			
		||||
  private _baseDir: string | null;
 | 
			
		||||
  private _platform: Platform;
 | 
			
		||||
 | 
			
		||||
  constructor(platform: Platform, harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) {
 | 
			
		||||
    this._platform = platform;
 | 
			
		||||
    this.id = platform.createGuid();
 | 
			
		||||
  constructor(harFile: har.HARFile, baseDir: string | null, zipFile: ZipFile | null) {
 | 
			
		||||
    this.id = createGuid();
 | 
			
		||||
    this._harFile = harFile;
 | 
			
		||||
    this._baseDir = baseDir;
 | 
			
		||||
    this._zipFile = zipFile;
 | 
			
		||||
@ -79,7 +80,7 @@ export class HarBackend {
 | 
			
		||||
      if (this._zipFile)
 | 
			
		||||
        buffer = await this._zipFile.read(file);
 | 
			
		||||
      else
 | 
			
		||||
        buffer = await this._platform.fs().promises.readFile(this._platform.path().resolve(this._baseDir!, file));
 | 
			
		||||
        buffer = await fs.promises.readFile(path.resolve(this._baseDir!, file));
 | 
			
		||||
    } else {
 | 
			
		||||
      buffer = Buffer.from(content.text || '', content.encoding === 'base64' ? 'base64' : 'utf-8');
 | 
			
		||||
    }
 | 
			
		||||
@ -18,15 +18,15 @@ import * as fs from 'fs';
 | 
			
		||||
import * as os from 'os';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
 | 
			
		||||
import { removeFolders } from './fileUtils';
 | 
			
		||||
import { calculateSha1 } from './utils/crypto';
 | 
			
		||||
import { HarBackend } from './harBackend';
 | 
			
		||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
 | 
			
		||||
import { ZipFile } from '../utils/zipFile';
 | 
			
		||||
import { ZipFile } from './utils/zipFile';
 | 
			
		||||
import { yauzl, yazl } from '../zipBundle';
 | 
			
		||||
import { serializeClientSideCallMetadata } from '../utils/isomorphic/traceUtils';
 | 
			
		||||
import { assert } from '../utils/isomorphic/debug';
 | 
			
		||||
import { removeFolders } from './utils/fileUtils';
 | 
			
		||||
 | 
			
		||||
import type { Platform } from './platform';
 | 
			
		||||
import type * as channels from '@protocol/channels';
 | 
			
		||||
import type * as har from '@trace/har';
 | 
			
		||||
import type EventEmitter from 'events';
 | 
			
		||||
@ -39,7 +39,7 @@ export type StackSession = {
 | 
			
		||||
  callStacks: channels.ClientSideCallMetadata[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export async function zip(platform: Platform, stackSessions: Map<string, StackSession>, params: channels.LocalUtilsZipParams): Promise<void> {
 | 
			
		||||
export async function zip(stackSessions: Map<string, StackSession>, params: channels.LocalUtilsZipParams): Promise<void> {
 | 
			
		||||
  const promise = new ManualPromise<void>();
 | 
			
		||||
  const zipFile = new yazl.ZipFile();
 | 
			
		||||
  (zipFile as any as EventEmitter).on('error', error => promise.reject(error));
 | 
			
		||||
@ -77,7 +77,7 @@ export async function zip(platform: Platform, stackSessions: Map<string, StackSe
 | 
			
		||||
        sourceFiles.add(file);
 | 
			
		||||
    }
 | 
			
		||||
    for (const sourceFile of sourceFiles)
 | 
			
		||||
      addFile(sourceFile, 'resources/src@' + await platform.calculateSha1(sourceFile) + '.txt');
 | 
			
		||||
      addFile(sourceFile, 'resources/src@' + await calculateSha1(sourceFile) + '.txt');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (params.mode === 'write') {
 | 
			
		||||
@ -89,7 +89,7 @@ export async function zip(platform: Platform, stackSessions: Map<string, StackSe
 | 
			
		||||
          .on('error', error => promise.reject(error));
 | 
			
		||||
    });
 | 
			
		||||
    await promise;
 | 
			
		||||
    await deleteStackSession(platform, stackSessions, params.stacksId);
 | 
			
		||||
    await deleteStackSession(stackSessions, params.stacksId);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -124,20 +124,20 @@ export async function zip(platform: Platform, stackSessions: Map<string, StackSe
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  await promise;
 | 
			
		||||
  await deleteStackSession(platform, stackSessions, params.stacksId);
 | 
			
		||||
  await deleteStackSession(stackSessions, params.stacksId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function deleteStackSession(platform: Platform, stackSessions: Map<string, StackSession>, stacksId?: string) {
 | 
			
		||||
async function deleteStackSession(stackSessions: Map<string, StackSession>, stacksId?: string) {
 | 
			
		||||
  const session = stacksId ? stackSessions.get(stacksId) : undefined;
 | 
			
		||||
  if (!session)
 | 
			
		||||
    return;
 | 
			
		||||
  await session.writer;
 | 
			
		||||
  if (session.tmpDir)
 | 
			
		||||
    await removeFolders(platform, [session.tmpDir]);
 | 
			
		||||
    await removeFolders([session.tmpDir]);
 | 
			
		||||
  stackSessions.delete(stacksId!);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function harOpen(platform: Platform, harBackends: Map<string, HarBackend>, params: channels.LocalUtilsHarOpenParams): Promise<channels.LocalUtilsHarOpenResult> {
 | 
			
		||||
export async function harOpen(harBackends: Map<string, HarBackend>, params: channels.LocalUtilsHarOpenParams): Promise<channels.LocalUtilsHarOpenResult> {
 | 
			
		||||
  let harBackend: HarBackend;
 | 
			
		||||
  if (params.file.endsWith('.zip')) {
 | 
			
		||||
    const zipFile = new ZipFile(params.file);
 | 
			
		||||
@ -147,10 +147,10 @@ export async function harOpen(platform: Platform, harBackends: Map<string, HarBa
 | 
			
		||||
      return { error: 'Specified archive does not have a .har file' };
 | 
			
		||||
    const har = await zipFile.read(harEntryName);
 | 
			
		||||
    const harFile = JSON.parse(har.toString()) as har.HARFile;
 | 
			
		||||
    harBackend = new HarBackend(platform, harFile, null, zipFile);
 | 
			
		||||
    harBackend = new HarBackend(harFile, null, zipFile);
 | 
			
		||||
  } else {
 | 
			
		||||
    const harFile = JSON.parse(await fs.promises.readFile(params.file, 'utf-8')) as har.HARFile;
 | 
			
		||||
    harBackend = new HarBackend(platform, harFile, path.dirname(params.file), null);
 | 
			
		||||
    harBackend = new HarBackend(harFile, path.dirname(params.file), null);
 | 
			
		||||
  }
 | 
			
		||||
  harBackends.set(harBackend.id, harBackend);
 | 
			
		||||
  return { harId: harBackend.id };
 | 
			
		||||
@ -194,8 +194,8 @@ export async function tracingStarted(stackSessions: Map<string, StackSession>, p
 | 
			
		||||
  return { stacksId: traceStacksFile };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function traceDiscarded(platform: Platform, stackSessions: Map<string, StackSession>, params: channels.LocalUtilsTraceDiscardedParams): Promise<void> {
 | 
			
		||||
  await deleteStackSession(platform, stackSessions, params.stacksId);
 | 
			
		||||
export async function traceDiscarded(stackSessions: Map<string, StackSession>, params: channels.LocalUtilsTraceDiscardedParams): Promise<void> {
 | 
			
		||||
  await deleteStackSession(stackSessions, params.stacksId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function addStackToTracingNoReply(stackSessions: Map<string, StackSession>, params: channels.LocalUtilsAddStackToTracingNoReplyParams): Promise<void> {
 | 
			
		||||
@ -19,10 +19,14 @@ import { assert, monotonicTime } from '../utils';
 | 
			
		||||
import { ManualPromise } from '../utils/isomorphic/manualPromise';
 | 
			
		||||
 | 
			
		||||
import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation';
 | 
			
		||||
import type { Progress as CommonProgress } from '../common/progress';
 | 
			
		||||
import type { LogName } from './utils/debugLogger';
 | 
			
		||||
 | 
			
		||||
export interface Progress extends CommonProgress {
 | 
			
		||||
export interface Progress {
 | 
			
		||||
  log(message: string): void;
 | 
			
		||||
  timeUntilDeadline(): number;
 | 
			
		||||
  isRunning(): boolean;
 | 
			
		||||
  cleanupWhenAborted(cleanup: () => any): void;
 | 
			
		||||
  throwIfAborted(): void;
 | 
			
		||||
  metadata: CallMetadata;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -45,16 +45,14 @@ class NodeZone implements Zone {
 | 
			
		||||
    return this._zone.run(func);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  runIgnoreCurrent<R>(func: () => R): R {
 | 
			
		||||
    return emptyZone.run(func);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  data<T>(): T | undefined {
 | 
			
		||||
    return this._zone.data('apiZone');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const nodePlatform: Platform = {
 | 
			
		||||
  name: 'node',
 | 
			
		||||
 | 
			
		||||
  calculateSha1: (text: string) => {
 | 
			
		||||
    const sha1 = crypto.createHash('sha1');
 | 
			
		||||
    sha1.update(text);
 | 
			
		||||
 | 
			
		||||
@ -14,9 +14,9 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { yauzl } from '../zipBundle';
 | 
			
		||||
import { yauzl } from '../../zipBundle';
 | 
			
		||||
 | 
			
		||||
import type { Entry, UnzipFile } from '../zipBundle';
 | 
			
		||||
import type { Entry, UnzipFile } from '../../zipBundle';
 | 
			
		||||
 | 
			
		||||
export class ZipFile {
 | 
			
		||||
  private _fileName: string;
 | 
			
		||||
@ -29,7 +29,6 @@ export * from './utils/isomorphic/urlMatch';
 | 
			
		||||
export * from './utils/isomorphic/headers';
 | 
			
		||||
export * from './utils/isomorphic/semaphore';
 | 
			
		||||
export * from './utils/isomorphic/stackTrace';
 | 
			
		||||
export * from './utils/zipFile';
 | 
			
		||||
 | 
			
		||||
export * from './server/utils/ascii';
 | 
			
		||||
export * from './server/utils/comparators';
 | 
			
		||||
@ -50,6 +49,7 @@ export * from './server/utils/spawnAsync';
 | 
			
		||||
export * from './server/utils/task';
 | 
			
		||||
export * from './server/utils/userAgent';
 | 
			
		||||
export * from './server/utils/wsServer';
 | 
			
		||||
export * from './server/utils/zipFile';
 | 
			
		||||
export * from './server/utils/zones';
 | 
			
		||||
 | 
			
		||||
export { colors } from './utilsBundle';
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright (c) Microsoft Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * 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,
 | 
			
		||||
 * 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.
 | 
			
		||||
@ -21,3 +21,426 @@ export function isJsonMimeType(mimeType: string) {
 | 
			
		||||
export function isTextualMimeType(mimeType: string) {
 | 
			
		||||
  return !!mimeType.match(/^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$/);
 | 
			
		||||
}
 | 
			
		||||
export function getMimeTypeForPath(path: string): string | null {
 | 
			
		||||
  const dotIndex = path.lastIndexOf('.');
 | 
			
		||||
  if (dotIndex === -1)
 | 
			
		||||
    return null;
 | 
			
		||||
  const extension = path.substring(dotIndex + 1);
 | 
			
		||||
  return types.get(extension) || null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const types: Map<string, string> = new Map([
 | 
			
		||||
  ['ez', 'application/andrew-inset'],
 | 
			
		||||
  ['aw', 'application/applixware'],
 | 
			
		||||
  ['atom', 'application/atom+xml'],
 | 
			
		||||
  ['atomcat', 'application/atomcat+xml'],
 | 
			
		||||
  ['atomdeleted', 'application/atomdeleted+xml'],
 | 
			
		||||
  ['atomsvc', 'application/atomsvc+xml'],
 | 
			
		||||
  ['dwd', 'application/atsc-dwd+xml'],
 | 
			
		||||
  ['held', 'application/atsc-held+xml'],
 | 
			
		||||
  ['rsat', 'application/atsc-rsat+xml'],
 | 
			
		||||
  ['bdoc', 'application/bdoc'],
 | 
			
		||||
  ['xcs', 'application/calendar+xml'],
 | 
			
		||||
  ['ccxml', 'application/ccxml+xml'],
 | 
			
		||||
  ['cdfx', 'application/cdfx+xml'],
 | 
			
		||||
  ['cdmia', 'application/cdmi-capability'],
 | 
			
		||||
  ['cdmic', 'application/cdmi-container'],
 | 
			
		||||
  ['cdmid', 'application/cdmi-domain'],
 | 
			
		||||
  ['cdmio', 'application/cdmi-object'],
 | 
			
		||||
  ['cdmiq', 'application/cdmi-queue'],
 | 
			
		||||
  ['cu', 'application/cu-seeme'],
 | 
			
		||||
  ['mpd', 'application/dash+xml'],
 | 
			
		||||
  ['davmount', 'application/davmount+xml'],
 | 
			
		||||
  ['dbk', 'application/docbook+xml'],
 | 
			
		||||
  ['dssc', 'application/dssc+der'],
 | 
			
		||||
  ['xdssc', 'application/dssc+xml'],
 | 
			
		||||
  ['ecma', 'application/ecmascript'],
 | 
			
		||||
  ['es', 'application/ecmascript'],
 | 
			
		||||
  ['emma', 'application/emma+xml'],
 | 
			
		||||
  ['emotionml', 'application/emotionml+xml'],
 | 
			
		||||
  ['epub', 'application/epub+zip'],
 | 
			
		||||
  ['exi', 'application/exi'],
 | 
			
		||||
  ['exp', 'application/express'],
 | 
			
		||||
  ['fdt', 'application/fdt+xml'],
 | 
			
		||||
  ['pfr', 'application/font-tdpfr'],
 | 
			
		||||
  ['geojson', 'application/geo+json'],
 | 
			
		||||
  ['gml', 'application/gml+xml'],
 | 
			
		||||
  ['gpx', 'application/gpx+xml'],
 | 
			
		||||
  ['gxf', 'application/gxf'],
 | 
			
		||||
  ['gz', 'application/gzip'],
 | 
			
		||||
  ['hjson', 'application/hjson'],
 | 
			
		||||
  ['stk', 'application/hyperstudio'],
 | 
			
		||||
  ['ink', 'application/inkml+xml'],
 | 
			
		||||
  ['inkml', 'application/inkml+xml'],
 | 
			
		||||
  ['ipfix', 'application/ipfix'],
 | 
			
		||||
  ['its', 'application/its+xml'],
 | 
			
		||||
  ['ear', 'application/java-archive'],
 | 
			
		||||
  ['jar', 'application/java-archive'],
 | 
			
		||||
  ['war', 'application/java-archive'],
 | 
			
		||||
  ['ser', 'application/java-serialized-object'],
 | 
			
		||||
  ['class', 'application/java-vm'],
 | 
			
		||||
  ['js', 'application/javascript'],
 | 
			
		||||
  ['mjs', 'application/javascript'],
 | 
			
		||||
  ['json', 'application/json'],
 | 
			
		||||
  ['map', 'application/json'],
 | 
			
		||||
  ['json5', 'application/json5'],
 | 
			
		||||
  ['jsonml', 'application/jsonml+json'],
 | 
			
		||||
  ['jsonld', 'application/ld+json'],
 | 
			
		||||
  ['lgr', 'application/lgr+xml'],
 | 
			
		||||
  ['lostxml', 'application/lost+xml'],
 | 
			
		||||
  ['hqx', 'application/mac-binhex40'],
 | 
			
		||||
  ['cpt', 'application/mac-compactpro'],
 | 
			
		||||
  ['mads', 'application/mads+xml'],
 | 
			
		||||
  ['webmanifest', 'application/manifest+json'],
 | 
			
		||||
  ['mrc', 'application/marc'],
 | 
			
		||||
  ['mrcx', 'application/marcxml+xml'],
 | 
			
		||||
  ['ma', 'application/mathematica'],
 | 
			
		||||
  ['mb', 'application/mathematica'],
 | 
			
		||||
  ['nb', 'application/mathematica'],
 | 
			
		||||
  ['mathml', 'application/mathml+xml'],
 | 
			
		||||
  ['mbox', 'application/mbox'],
 | 
			
		||||
  ['mscml', 'application/mediaservercontrol+xml'],
 | 
			
		||||
  ['metalink', 'application/metalink+xml'],
 | 
			
		||||
  ['meta4', 'application/metalink4+xml'],
 | 
			
		||||
  ['mets', 'application/mets+xml'],
 | 
			
		||||
  ['maei', 'application/mmt-aei+xml'],
 | 
			
		||||
  ['musd', 'application/mmt-usd+xml'],
 | 
			
		||||
  ['mods', 'application/mods+xml'],
 | 
			
		||||
  ['m21', 'application/mp21'],
 | 
			
		||||
  ['mp21', 'application/mp21'],
 | 
			
		||||
  ['m4p', 'application/mp4'],
 | 
			
		||||
  ['mp4s', 'application/mp4'],
 | 
			
		||||
  ['doc', 'application/msword'],
 | 
			
		||||
  ['dot', 'application/msword'],
 | 
			
		||||
  ['mxf', 'application/mxf'],
 | 
			
		||||
  ['nq', 'application/n-quads'],
 | 
			
		||||
  ['nt', 'application/n-triples'],
 | 
			
		||||
  ['cjs', 'application/node'],
 | 
			
		||||
  ['bin', 'application/octet-stream'],
 | 
			
		||||
  ['bpk', 'application/octet-stream'],
 | 
			
		||||
  ['buffer', 'application/octet-stream'],
 | 
			
		||||
  ['deb', 'application/octet-stream'],
 | 
			
		||||
  ['deploy', 'application/octet-stream'],
 | 
			
		||||
  ['dist', 'application/octet-stream'],
 | 
			
		||||
  ['distz', 'application/octet-stream'],
 | 
			
		||||
  ['dll', 'application/octet-stream'],
 | 
			
		||||
  ['dmg', 'application/octet-stream'],
 | 
			
		||||
  ['dms', 'application/octet-stream'],
 | 
			
		||||
  ['dump', 'application/octet-stream'],
 | 
			
		||||
  ['elc', 'application/octet-stream'],
 | 
			
		||||
  ['exe', 'application/octet-stream'],
 | 
			
		||||
  ['img', 'application/octet-stream'],
 | 
			
		||||
  ['iso', 'application/octet-stream'],
 | 
			
		||||
  ['lrf', 'application/octet-stream'],
 | 
			
		||||
  ['mar', 'application/octet-stream'],
 | 
			
		||||
  ['msi', 'application/octet-stream'],
 | 
			
		||||
  ['msm', 'application/octet-stream'],
 | 
			
		||||
  ['msp', 'application/octet-stream'],
 | 
			
		||||
  ['pkg', 'application/octet-stream'],
 | 
			
		||||
  ['so', 'application/octet-stream'],
 | 
			
		||||
  ['oda', 'application/oda'],
 | 
			
		||||
  ['opf', 'application/oebps-package+xml'],
 | 
			
		||||
  ['ogx', 'application/ogg'],
 | 
			
		||||
  ['omdoc', 'application/omdoc+xml'],
 | 
			
		||||
  ['onepkg', 'application/onenote'],
 | 
			
		||||
  ['onetmp', 'application/onenote'],
 | 
			
		||||
  ['onetoc', 'application/onenote'],
 | 
			
		||||
  ['onetoc2', 'application/onenote'],
 | 
			
		||||
  ['oxps', 'application/oxps'],
 | 
			
		||||
  ['relo', 'application/p2p-overlay+xml'],
 | 
			
		||||
  ['xer', 'application/patch-ops-error+xml'],
 | 
			
		||||
  ['pdf', 'application/pdf'],
 | 
			
		||||
  ['pgp', 'application/pgp-encrypted'],
 | 
			
		||||
  ['asc', 'application/pgp-signature'],
 | 
			
		||||
  ['sig', 'application/pgp-signature'],
 | 
			
		||||
  ['prf', 'application/pics-rules'],
 | 
			
		||||
  ['p10', 'application/pkcs10'],
 | 
			
		||||
  ['p7c', 'application/pkcs7-mime'],
 | 
			
		||||
  ['p7m', 'application/pkcs7-mime'],
 | 
			
		||||
  ['p7s', 'application/pkcs7-signature'],
 | 
			
		||||
  ['p8', 'application/pkcs8'],
 | 
			
		||||
  ['ac', 'application/pkix-attr-cert'],
 | 
			
		||||
  ['cer', 'application/pkix-cert'],
 | 
			
		||||
  ['crl', 'application/pkix-crl'],
 | 
			
		||||
  ['pkipath', 'application/pkix-pkipath'],
 | 
			
		||||
  ['pki', 'application/pkixcmp'],
 | 
			
		||||
  ['pls', 'application/pls+xml'],
 | 
			
		||||
  ['ai', 'application/postscript'],
 | 
			
		||||
  ['eps', 'application/postscript'],
 | 
			
		||||
  ['ps', 'application/postscript'],
 | 
			
		||||
  ['provx', 'application/provenance+xml'],
 | 
			
		||||
  ['pskcxml', 'application/pskc+xml'],
 | 
			
		||||
  ['raml', 'application/raml+yaml'],
 | 
			
		||||
  ['owl', 'application/rdf+xml'],
 | 
			
		||||
  ['rdf', 'application/rdf+xml'],
 | 
			
		||||
  ['rif', 'application/reginfo+xml'],
 | 
			
		||||
  ['rnc', 'application/relax-ng-compact-syntax'],
 | 
			
		||||
  ['rl', 'application/resource-lists+xml'],
 | 
			
		||||
  ['rld', 'application/resource-lists-diff+xml'],
 | 
			
		||||
  ['rs', 'application/rls-services+xml'],
 | 
			
		||||
  ['rapd', 'application/route-apd+xml'],
 | 
			
		||||
  ['sls', 'application/route-s-tsid+xml'],
 | 
			
		||||
  ['rusd', 'application/route-usd+xml'],
 | 
			
		||||
  ['gbr', 'application/rpki-ghostbusters'],
 | 
			
		||||
  ['mft', 'application/rpki-manifest'],
 | 
			
		||||
  ['roa', 'application/rpki-roa'],
 | 
			
		||||
  ['rsd', 'application/rsd+xml'],
 | 
			
		||||
  ['rss', 'application/rss+xml'],
 | 
			
		||||
  ['rtf', 'application/rtf'],
 | 
			
		||||
  ['sbml', 'application/sbml+xml'],
 | 
			
		||||
  ['scq', 'application/scvp-cv-request'],
 | 
			
		||||
  ['scs', 'application/scvp-cv-response'],
 | 
			
		||||
  ['spq', 'application/scvp-vp-request'],
 | 
			
		||||
  ['spp', 'application/scvp-vp-response'],
 | 
			
		||||
  ['sdp', 'application/sdp'],
 | 
			
		||||
  ['senmlx', 'application/senml+xml'],
 | 
			
		||||
  ['sensmlx', 'application/sensml+xml'],
 | 
			
		||||
  ['setpay', 'application/set-payment-initiation'],
 | 
			
		||||
  ['setreg', 'application/set-registration-initiation'],
 | 
			
		||||
  ['shf', 'application/shf+xml'],
 | 
			
		||||
  ['sieve', 'application/sieve'],
 | 
			
		||||
  ['siv', 'application/sieve'],
 | 
			
		||||
  ['smi', 'application/smil+xml'],
 | 
			
		||||
  ['smil', 'application/smil+xml'],
 | 
			
		||||
  ['rq', 'application/sparql-query'],
 | 
			
		||||
  ['srx', 'application/sparql-results+xml'],
 | 
			
		||||
  ['gram', 'application/srgs'],
 | 
			
		||||
  ['grxml', 'application/srgs+xml'],
 | 
			
		||||
  ['sru', 'application/sru+xml'],
 | 
			
		||||
  ['ssdl', 'application/ssdl+xml'],
 | 
			
		||||
  ['ssml', 'application/ssml+xml'],
 | 
			
		||||
  ['swidtag', 'application/swid+xml'],
 | 
			
		||||
  ['tei', 'application/tei+xml'],
 | 
			
		||||
  ['teicorpus', 'application/tei+xml'],
 | 
			
		||||
  ['tfi', 'application/thraud+xml'],
 | 
			
		||||
  ['tsd', 'application/timestamped-data'],
 | 
			
		||||
  ['toml', 'application/toml'],
 | 
			
		||||
  ['trig', 'application/trig'],
 | 
			
		||||
  ['ttml', 'application/ttml+xml'],
 | 
			
		||||
  ['ubj', 'application/ubjson'],
 | 
			
		||||
  ['rsheet', 'application/urc-ressheet+xml'],
 | 
			
		||||
  ['td', 'application/urc-targetdesc+xml'],
 | 
			
		||||
  ['vxml', 'application/voicexml+xml'],
 | 
			
		||||
  ['wasm', 'application/wasm'],
 | 
			
		||||
  ['wgt', 'application/widget'],
 | 
			
		||||
  ['hlp', 'application/winhlp'],
 | 
			
		||||
  ['wsdl', 'application/wsdl+xml'],
 | 
			
		||||
  ['wspolicy', 'application/wspolicy+xml'],
 | 
			
		||||
  ['xaml', 'application/xaml+xml'],
 | 
			
		||||
  ['xav', 'application/xcap-att+xml'],
 | 
			
		||||
  ['xca', 'application/xcap-caps+xml'],
 | 
			
		||||
  ['xdf', 'application/xcap-diff+xml'],
 | 
			
		||||
  ['xel', 'application/xcap-el+xml'],
 | 
			
		||||
  ['xns', 'application/xcap-ns+xml'],
 | 
			
		||||
  ['xenc', 'application/xenc+xml'],
 | 
			
		||||
  ['xht', 'application/xhtml+xml'],
 | 
			
		||||
  ['xhtml', 'application/xhtml+xml'],
 | 
			
		||||
  ['xlf', 'application/xliff+xml'],
 | 
			
		||||
  ['rng', 'application/xml'],
 | 
			
		||||
  ['xml', 'application/xml'],
 | 
			
		||||
  ['xsd', 'application/xml'],
 | 
			
		||||
  ['xsl', 'application/xml'],
 | 
			
		||||
  ['dtd', 'application/xml-dtd'],
 | 
			
		||||
  ['xop', 'application/xop+xml'],
 | 
			
		||||
  ['xpl', 'application/xproc+xml'],
 | 
			
		||||
  ['*xsl', 'application/xslt+xml'],
 | 
			
		||||
  ['xslt', 'application/xslt+xml'],
 | 
			
		||||
  ['xspf', 'application/xspf+xml'],
 | 
			
		||||
  ['mxml', 'application/xv+xml'],
 | 
			
		||||
  ['xhvml', 'application/xv+xml'],
 | 
			
		||||
  ['xvm', 'application/xv+xml'],
 | 
			
		||||
  ['xvml', 'application/xv+xml'],
 | 
			
		||||
  ['yang', 'application/yang'],
 | 
			
		||||
  ['yin', 'application/yin+xml'],
 | 
			
		||||
  ['zip', 'application/zip'],
 | 
			
		||||
  ['*3gpp', 'audio/3gpp'],
 | 
			
		||||
  ['adp', 'audio/adpcm'],
 | 
			
		||||
  ['amr', 'audio/amr'],
 | 
			
		||||
  ['au', 'audio/basic'],
 | 
			
		||||
  ['snd', 'audio/basic'],
 | 
			
		||||
  ['kar', 'audio/midi'],
 | 
			
		||||
  ['mid', 'audio/midi'],
 | 
			
		||||
  ['midi', 'audio/midi'],
 | 
			
		||||
  ['rmi', 'audio/midi'],
 | 
			
		||||
  ['mxmf', 'audio/mobile-xmf'],
 | 
			
		||||
  ['*mp3', 'audio/mp3'],
 | 
			
		||||
  ['m4a', 'audio/mp4'],
 | 
			
		||||
  ['mp4a', 'audio/mp4'],
 | 
			
		||||
  ['m2a', 'audio/mpeg'],
 | 
			
		||||
  ['m3a', 'audio/mpeg'],
 | 
			
		||||
  ['mp2', 'audio/mpeg'],
 | 
			
		||||
  ['mp2a', 'audio/mpeg'],
 | 
			
		||||
  ['mp3', 'audio/mpeg'],
 | 
			
		||||
  ['mpga', 'audio/mpeg'],
 | 
			
		||||
  ['oga', 'audio/ogg'],
 | 
			
		||||
  ['ogg', 'audio/ogg'],
 | 
			
		||||
  ['opus', 'audio/ogg'],
 | 
			
		||||
  ['spx', 'audio/ogg'],
 | 
			
		||||
  ['s3m', 'audio/s3m'],
 | 
			
		||||
  ['sil', 'audio/silk'],
 | 
			
		||||
  ['wav', 'audio/wav'],
 | 
			
		||||
  ['*wav', 'audio/wave'],
 | 
			
		||||
  ['weba', 'audio/webm'],
 | 
			
		||||
  ['xm', 'audio/xm'],
 | 
			
		||||
  ['ttc', 'font/collection'],
 | 
			
		||||
  ['otf', 'font/otf'],
 | 
			
		||||
  ['ttf', 'font/ttf'],
 | 
			
		||||
  ['woff', 'font/woff'],
 | 
			
		||||
  ['woff2', 'font/woff2'],
 | 
			
		||||
  ['exr', 'image/aces'],
 | 
			
		||||
  ['apng', 'image/apng'],
 | 
			
		||||
  ['avif', 'image/avif'],
 | 
			
		||||
  ['bmp', 'image/bmp'],
 | 
			
		||||
  ['cgm', 'image/cgm'],
 | 
			
		||||
  ['drle', 'image/dicom-rle'],
 | 
			
		||||
  ['emf', 'image/emf'],
 | 
			
		||||
  ['fits', 'image/fits'],
 | 
			
		||||
  ['g3', 'image/g3fax'],
 | 
			
		||||
  ['gif', 'image/gif'],
 | 
			
		||||
  ['heic', 'image/heic'],
 | 
			
		||||
  ['heics', 'image/heic-sequence'],
 | 
			
		||||
  ['heif', 'image/heif'],
 | 
			
		||||
  ['heifs', 'image/heif-sequence'],
 | 
			
		||||
  ['hej2', 'image/hej2k'],
 | 
			
		||||
  ['hsj2', 'image/hsj2'],
 | 
			
		||||
  ['ief', 'image/ief'],
 | 
			
		||||
  ['jls', 'image/jls'],
 | 
			
		||||
  ['jp2', 'image/jp2'],
 | 
			
		||||
  ['jpg2', 'image/jp2'],
 | 
			
		||||
  ['jpe', 'image/jpeg'],
 | 
			
		||||
  ['jpeg', 'image/jpeg'],
 | 
			
		||||
  ['jpg', 'image/jpeg'],
 | 
			
		||||
  ['jph', 'image/jph'],
 | 
			
		||||
  ['jhc', 'image/jphc'],
 | 
			
		||||
  ['jpm', 'image/jpm'],
 | 
			
		||||
  ['jpf', 'image/jpx'],
 | 
			
		||||
  ['jpx', 'image/jpx'],
 | 
			
		||||
  ['jxr', 'image/jxr'],
 | 
			
		||||
  ['jxra', 'image/jxra'],
 | 
			
		||||
  ['jxrs', 'image/jxrs'],
 | 
			
		||||
  ['jxs', 'image/jxs'],
 | 
			
		||||
  ['jxsc', 'image/jxsc'],
 | 
			
		||||
  ['jxsi', 'image/jxsi'],
 | 
			
		||||
  ['jxss', 'image/jxss'],
 | 
			
		||||
  ['ktx', 'image/ktx'],
 | 
			
		||||
  ['ktx2', 'image/ktx2'],
 | 
			
		||||
  ['png', 'image/png'],
 | 
			
		||||
  ['sgi', 'image/sgi'],
 | 
			
		||||
  ['svg', 'image/svg+xml'],
 | 
			
		||||
  ['svgz', 'image/svg+xml'],
 | 
			
		||||
  ['t38', 'image/t38'],
 | 
			
		||||
  ['tif', 'image/tiff'],
 | 
			
		||||
  ['tiff', 'image/tiff'],
 | 
			
		||||
  ['tfx', 'image/tiff-fx'],
 | 
			
		||||
  ['webp', 'image/webp'],
 | 
			
		||||
  ['wmf', 'image/wmf'],
 | 
			
		||||
  ['disposition-notification', 'message/disposition-notification'],
 | 
			
		||||
  ['u8msg', 'message/global'],
 | 
			
		||||
  ['u8dsn', 'message/global-delivery-status'],
 | 
			
		||||
  ['u8mdn', 'message/global-disposition-notification'],
 | 
			
		||||
  ['u8hdr', 'message/global-headers'],
 | 
			
		||||
  ['eml', 'message/rfc822'],
 | 
			
		||||
  ['mime', 'message/rfc822'],
 | 
			
		||||
  ['3mf', 'model/3mf'],
 | 
			
		||||
  ['gltf', 'model/gltf+json'],
 | 
			
		||||
  ['glb', 'model/gltf-binary'],
 | 
			
		||||
  ['iges', 'model/iges'],
 | 
			
		||||
  ['igs', 'model/iges'],
 | 
			
		||||
  ['mesh', 'model/mesh'],
 | 
			
		||||
  ['msh', 'model/mesh'],
 | 
			
		||||
  ['silo', 'model/mesh'],
 | 
			
		||||
  ['mtl', 'model/mtl'],
 | 
			
		||||
  ['obj', 'model/obj'],
 | 
			
		||||
  ['stpx', 'model/step+xml'],
 | 
			
		||||
  ['stpz', 'model/step+zip'],
 | 
			
		||||
  ['stpxz', 'model/step-xml+zip'],
 | 
			
		||||
  ['stl', 'model/stl'],
 | 
			
		||||
  ['vrml', 'model/vrml'],
 | 
			
		||||
  ['wrl', 'model/vrml'],
 | 
			
		||||
  ['*x3db', 'model/x3d+binary'],
 | 
			
		||||
  ['x3dbz', 'model/x3d+binary'],
 | 
			
		||||
  ['x3db', 'model/x3d+fastinfoset'],
 | 
			
		||||
  ['*x3dv', 'model/x3d+vrml'],
 | 
			
		||||
  ['x3dvz', 'model/x3d+vrml'],
 | 
			
		||||
  ['x3d', 'model/x3d+xml'],
 | 
			
		||||
  ['x3dz', 'model/x3d+xml'],
 | 
			
		||||
  ['x3dv', 'model/x3d-vrml'],
 | 
			
		||||
  ['appcache', 'text/cache-manifest'],
 | 
			
		||||
  ['manifest', 'text/cache-manifest'],
 | 
			
		||||
  ['ics', 'text/calendar'],
 | 
			
		||||
  ['ifb', 'text/calendar'],
 | 
			
		||||
  ['coffee', 'text/coffeescript'],
 | 
			
		||||
  ['litcoffee', 'text/coffeescript'],
 | 
			
		||||
  ['css', 'text/css'],
 | 
			
		||||
  ['csv', 'text/csv'],
 | 
			
		||||
  ['htm', 'text/html'],
 | 
			
		||||
  ['html', 'text/html'],
 | 
			
		||||
  ['shtml', 'text/html'],
 | 
			
		||||
  ['jade', 'text/jade'],
 | 
			
		||||
  ['jsx', 'text/jsx'],
 | 
			
		||||
  ['less', 'text/less'],
 | 
			
		||||
  ['markdown', 'text/markdown'],
 | 
			
		||||
  ['md', 'text/markdown'],
 | 
			
		||||
  ['mml', 'text/mathml'],
 | 
			
		||||
  ['mdx', 'text/mdx'],
 | 
			
		||||
  ['n3', 'text/n3'],
 | 
			
		||||
  ['conf', 'text/plain'],
 | 
			
		||||
  ['def', 'text/plain'],
 | 
			
		||||
  ['in', 'text/plain'],
 | 
			
		||||
  ['ini', 'text/plain'],
 | 
			
		||||
  ['list', 'text/plain'],
 | 
			
		||||
  ['log', 'text/plain'],
 | 
			
		||||
  ['text', 'text/plain'],
 | 
			
		||||
  ['txt', 'text/plain'],
 | 
			
		||||
  ['rtx', 'text/richtext'],
 | 
			
		||||
  ['*rtf', 'text/rtf'],
 | 
			
		||||
  ['sgm', 'text/sgml'],
 | 
			
		||||
  ['sgml', 'text/sgml'],
 | 
			
		||||
  ['shex', 'text/shex'],
 | 
			
		||||
  ['slim', 'text/slim'],
 | 
			
		||||
  ['slm', 'text/slim'],
 | 
			
		||||
  ['spdx', 'text/spdx'],
 | 
			
		||||
  ['styl', 'text/stylus'],
 | 
			
		||||
  ['stylus', 'text/stylus'],
 | 
			
		||||
  ['tsv', 'text/tab-separated-values'],
 | 
			
		||||
  ['man', 'text/troff'],
 | 
			
		||||
  ['me', 'text/troff'],
 | 
			
		||||
  ['ms', 'text/troff'],
 | 
			
		||||
  ['roff', 'text/troff'],
 | 
			
		||||
  ['t', 'text/troff'],
 | 
			
		||||
  ['tr', 'text/troff'],
 | 
			
		||||
  ['ttl', 'text/turtle'],
 | 
			
		||||
  ['uri', 'text/uri-list'],
 | 
			
		||||
  ['uris', 'text/uri-list'],
 | 
			
		||||
  ['urls', 'text/uri-list'],
 | 
			
		||||
  ['vcard', 'text/vcard'],
 | 
			
		||||
  ['vtt', 'text/vtt'],
 | 
			
		||||
  ['*xml', 'text/xml'],
 | 
			
		||||
  ['yaml', 'text/yaml'],
 | 
			
		||||
  ['yml', 'text/yaml'],
 | 
			
		||||
  ['3gp', 'video/3gpp'],
 | 
			
		||||
  ['3gpp', 'video/3gpp'],
 | 
			
		||||
  ['3g2', 'video/3gpp2'],
 | 
			
		||||
  ['h261', 'video/h261'],
 | 
			
		||||
  ['h263', 'video/h263'],
 | 
			
		||||
  ['h264', 'video/h264'],
 | 
			
		||||
  ['m4s', 'video/iso.segment'],
 | 
			
		||||
  ['jpgv', 'video/jpeg'],
 | 
			
		||||
  ['jpm', 'video/jpm'],
 | 
			
		||||
  ['jpgm', 'video/jpm'],
 | 
			
		||||
  ['mj2', 'video/mj2'],
 | 
			
		||||
  ['mjp2', 'video/mj2'],
 | 
			
		||||
  ['ts', 'video/mp2t'],
 | 
			
		||||
  ['mp4', 'video/mp4'],
 | 
			
		||||
  ['mp4v', 'video/mp4'],
 | 
			
		||||
  ['mpg4', 'video/mp4'],
 | 
			
		||||
  ['m1v', 'video/mpeg'],
 | 
			
		||||
  ['m2v', 'video/mpeg'],
 | 
			
		||||
  ['mpe', 'video/mpeg'],
 | 
			
		||||
  ['mpeg', 'video/mpeg'],
 | 
			
		||||
  ['mpg', 'video/mpeg'],
 | 
			
		||||
  ['ogv', 'video/ogg'],
 | 
			
		||||
  ['mov', 'video/quicktime'],
 | 
			
		||||
  ['qt', 'video/quicktime'],
 | 
			
		||||
  ['webm', 'video/webm']
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import type { Frame, Page } from 'playwright-core';
 | 
			
		||||
import { ZipFile } from '../../packages/playwright-core/lib/utils/zipFile';
 | 
			
		||||
import { ZipFile } from '../../packages/playwright-core/lib/server/utils/zipFile';
 | 
			
		||||
import type { TraceModelBackend } from '../../packages/trace-viewer/src/sw/traceModel';
 | 
			
		||||
import type { StackFrame } from '../../packages/protocol/src/channels';
 | 
			
		||||
import { parseClientSideCallMetadata } from '../../packages/playwright-core/lib/utils/isomorphic/traceUtils';
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user