import { Bluetooth, Speculation } from '../../../protocol/chromium-bidi.js';
import { UnknownErrorException, UnsupportedOperationException, } from '../../../protocol/protocol.js';
import { Deferred } from '../../../utils/Deferred.js';
import { LogType } from '../../../utils/log.js';
import { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import { LogManager } from '../log/LogManager.js';
import { MAX_TOTAL_COLLECTED_SIZE, } from '../network/NetworkStorage.js';
export class CdpTarget {
    #id;
    userContext;
    #cdpClient;
    #browserCdpClient;
    #parentCdpClient;
    #realmStorage;
    #eventManager;
    #preloadScriptStorage;
    #browsingContextStorage;
    #networkStorage;
    contextConfigStorage;
    #unblocked = new Deferred();
    // Default user agent for the target. Required, as emulating client hints without user
    // agent is not possible. Cache it to avoid round trips to the browser for every target override.
    #defaultUserAgent;
    #logger;
    /**
     * Target's window id. Is filled when the CDP target is created and do not reflect
     * moving targets from one window to another. The actual values
     * will be set during `#unblock`.
     * */
    #windowId;
    #deviceAccessEnabled = false;
    #cacheDisableState = false;
    #preloadEnabled = false;
    #fetchDomainStages = {
        request: false,
        response: false,
        auth: false,
    };
    static create(targetId, cdpClient, browserCdpClient, parentCdpClient, realmStorage, eventManager, preloadScriptStorage, browsingContextStorage, networkStorage, configStorage, userContext, defaultUserAgent, logger) {
        const cdpTarget = new CdpTarget(targetId, cdpClient, browserCdpClient, parentCdpClient, eventManager, realmStorage, preloadScriptStorage, browsingContextStorage, configStorage, networkStorage, userContext, defaultUserAgent, logger);
        LogManager.create(cdpTarget, realmStorage, eventManager, logger);
        cdpTarget.#setEventListeners();
        // No need to await.
        // Deferred will be resolved when the target is unblocked.
        void cdpTarget.#unblock();
        return cdpTarget;
    }
    constructor(targetId, cdpClient, browserCdpClient, parentCdpClient, eventManager, realmStorage, preloadScriptStorage, browsingContextStorage, configStorage, networkStorage, userContext, defaultUserAgent, logger) {
        this.#defaultUserAgent = defaultUserAgent;
        this.userContext = userContext;
        this.#id = targetId;
        this.#cdpClient = cdpClient;
        this.#browserCdpClient = browserCdpClient;
        this.#parentCdpClient = parentCdpClient;
        this.#eventManager = eventManager;
        this.#realmStorage = realmStorage;
        this.#preloadScriptStorage = preloadScriptStorage;
        this.#networkStorage = networkStorage;
        this.#browsingContextStorage = browsingContextStorage;
        this.contextConfigStorage = configStorage;
        this.#logger = logger;
    }
    /** Returns a deferred that resolves when the target is unblocked. */
    get unblocked() {
        return this.#unblocked;
    }
    get id() {
        return this.#id;
    }
    get cdpClient() {
        return this.#cdpClient;
    }
    get parentCdpClient() {
        return this.#parentCdpClient;
    }
    get browserCdpClient() {
        return this.#browserCdpClient;
    }
    /** Needed for CDP escape path. */
    get cdpSessionId() {
        // SAFETY we got the client by it's id for creating
        return this.#cdpClient.sessionId;
    }
    /**
     * Window id the target belongs to. If not known, returns 0.
     */
    get windowId() {
        if (this.#windowId === undefined) {
            this.#logger?.(LogType.debugError, 'Getting windowId before it was set, returning 0');
        }
        return this.#windowId ?? 0;
    }
    /**
     * Enables all the required CDP domains and unblocks the target.
     */
    async #unblock() {
        const config = this.contextConfigStorage.getActiveConfig(this.topLevelId, this.userContext);
        const results = await Promise.allSettled([
            this.#cdpClient.sendCommand('Page.enable', {
                enableFileChooserOpenedEvent: true,
            }),
            ...(this.#ignoreFileDialog()
                ? []
                : [
                    this.#cdpClient.sendCommand('Page.setInterceptFileChooserDialog', {
                        enabled: true,
                        // The intercepted dialog should be canceled.
                        cancel: true,
                    }),
                ]),
            // There can be some existing frames in the target, if reconnecting to an
            // existing browser instance, e.g. via Puppeteer. Need to restore the browsing
            // contexts for the frames to correctly handle further events, like
            // `Runtime.executionContextCreated`.
            // It's important to schedule this task together with enabling domains commands to
            // prepare the tree before the events (e.g. Runtime.executionContextCreated) start
            // coming.
            // https://github.com/GoogleChromeLabs/chromium-bidi/issues/2282
            this.#cdpClient
                .sendCommand('Page.getFrameTree')
                .then((frameTree) => this.#restoreFrameTreeState(frameTree.frameTree)),
            this.#cdpClient.sendCommand('Runtime.enable'),
            this.#cdpClient.sendCommand('Page.setLifecycleEventsEnabled', {
                enabled: true,
            }),
            // Enabling CDP Network domain is required for navigation detection:
            // https://github.com/GoogleChromeLabs/chromium-bidi/issues/2856.
            this.#cdpClient
                .sendCommand('Network.enable', {
                // If `googDisableNetworkDurableMessages` flag is set, do not enable durable
                // messages.
                enableDurableMessages: config.disableNetworkDurableMessages !== true,
                maxTotalBufferSize: MAX_TOTAL_COLLECTED_SIZE,
            })
                .then(() => this.toggleNetworkIfNeeded()),
            this.#cdpClient.sendCommand('Target.setAutoAttach', {
                autoAttach: true,
                waitForDebuggerOnStart: true,
                flatten: true,
            }),
            this.#updateWindowId(),
            this.#setUserContextConfig(config),
            this.#initAndEvaluatePreloadScripts(),
            this.#cdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
            // Resume tab execution as well if it was paused by the debugger.
            this.#parentCdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
            this.toggleDeviceAccessIfNeeded(),
            this.togglePreloadIfNeeded(),
        ]);
        for (const result of results) {
            if (result instanceof Error) {
                // Ignore errors during configuring targets, just log them.
                this.#logger?.(LogType.debugError, 'Error happened when configuring a new target', result);
            }
        }
        this.#unblocked.resolve({
            kind: 'success',
            value: undefined,
        });
    }
    #restoreFrameTreeState(frameTree) {
        const frame = frameTree.frame;
        const maybeContext = this.#browsingContextStorage.findContext(frame.id);
        if (maybeContext !== undefined) {
            // Restoring parent of already known browsing context. This means the target is
            // OOPiF and the BiDi session was connected to already existing browser instance.
            if (maybeContext.parentId === null &&
                frame.parentId !== null &&
                frame.parentId !== undefined) {
                maybeContext.parentId = frame.parentId;
            }
        }
        if (maybeContext === undefined && frame.parentId !== undefined) {
            // Restore not yet known nested frames. The top-level frame is created when the
            // target is attached.
            const parentBrowsingContext = this.#browsingContextStorage.getContext(frame.parentId);
            BrowsingContextImpl.create(frame.id, frame.parentId, this.userContext, parentBrowsingContext.cdpTarget, this.#eventManager, this.#browsingContextStorage, this.#realmStorage, this.contextConfigStorage, frame.url, undefined, this.#logger);
        }
        frameTree.childFrames?.map((frameTree) => this.#restoreFrameTreeState(frameTree));
    }
    async toggleFetchIfNeeded() {
        const stages = this.#networkStorage.getInterceptionStages(this.topLevelId);
        if (this.#fetchDomainStages.request === stages.request &&
            this.#fetchDomainStages.response === stages.response &&
            this.#fetchDomainStages.auth === stages.auth) {
            return;
        }
        const patterns = [];
        this.#fetchDomainStages = stages;
        if (stages.request || stages.auth) {
            // CDP quirk we need request interception when we intercept auth
            patterns.push({
                urlPattern: '*',
                requestStage: 'Request',
            });
        }
        if (stages.response) {
            patterns.push({
                urlPattern: '*',
                requestStage: 'Response',
            });
        }
        if (patterns.length) {
            await this.#cdpClient.sendCommand('Fetch.enable', {
                patterns,
                handleAuthRequests: stages.auth,
            });
        }
        else {
            const blockedRequest = this.#networkStorage
                .getRequestsByTarget(this)
                .filter((request) => request.interceptPhase);
            void Promise.allSettled(blockedRequest.map((request) => request.waitNextPhase))
                .then(async () => {
                const blockedRequest = this.#networkStorage
                    .getRequestsByTarget(this)
                    .filter((request) => request.interceptPhase);
                if (blockedRequest.length) {
                    return await this.toggleFetchIfNeeded();
                }
                return await this.#cdpClient.sendCommand('Fetch.disable');
            })
                .catch((error) => {
                this.#logger?.(LogType.bidi, 'Disable failed', error);
            });
        }
    }
    /**
     * Toggles CDP "Fetch" domain and enable/disable network cache.
     */
    async toggleNetworkIfNeeded() {
        // Although the Network domain remains active, Fetch domain activation and caching
        // settings should be managed dynamically.
        try {
            await Promise.all([
                this.toggleSetCacheDisabled(),
                this.toggleFetchIfNeeded(),
            ]);
        }
        catch (err) {
            this.#logger?.(LogType.debugError, err);
            if (!this.#isExpectedError(err)) {
                throw err;
            }
        }
    }
    async toggleSetCacheDisabled(disable) {
        const defaultCacheDisabled = this.#networkStorage.defaultCacheBehavior === 'bypass';
        const cacheDisabled = disable ?? defaultCacheDisabled;
        if (this.#cacheDisableState === cacheDisabled) {
            return;
        }
        this.#cacheDisableState = cacheDisabled;
        try {
            await this.#cdpClient.sendCommand('Network.setCacheDisabled', {
                cacheDisabled,
            });
        }
        catch (err) {
            this.#logger?.(LogType.debugError, err);
            this.#cacheDisableState = !cacheDisabled;
            if (!this.#isExpectedError(err)) {
                throw err;
            }
        }
    }
    async toggleDeviceAccessIfNeeded() {
        const enabled = this.isSubscribedTo(Bluetooth.EventNames.RequestDevicePromptUpdated);
        if (this.#deviceAccessEnabled === enabled) {
            return;
        }
        this.#deviceAccessEnabled = enabled;
        try {
            await this.#cdpClient.sendCommand(enabled ? 'DeviceAccess.enable' : 'DeviceAccess.disable');
        }
        catch (err) {
            this.#logger?.(LogType.debugError, err);
            this.#deviceAccessEnabled = !enabled;
            if (!this.#isExpectedError(err)) {
                throw err;
            }
        }
    }
    async togglePreloadIfNeeded() {
        const enabled = this.isSubscribedTo(Speculation.EventNames.PrefetchStatusUpdated);
        if (this.#preloadEnabled === enabled) {
            return;
        }
        this.#preloadEnabled = enabled;
        try {
            await this.#cdpClient.sendCommand(enabled ? 'Preload.enable' : 'Preload.disable');
        }
        catch (err) {
            this.#logger?.(LogType.debugError, err);
            this.#preloadEnabled = !enabled;
            if (!this.#isExpectedError(err)) {
                throw err;
            }
        }
    }
    /**
     * Heuristic checking if the error is due to the session being closed. If so, ignore the
     * error.
     */
    #isExpectedError(err) {
        const error = err;
        return ((error.code === -32001 &&
            error.message === 'Session with given id not found.') ||
            this.#cdpClient.isCloseError(err));
    }
    #setEventListeners() {
        this.#cdpClient.on('*', (event, params) => {
            // We may encounter uses for EventEmitter other than CDP events,
            // which we want to skip.
            if (typeof event !== 'string') {
                return;
            }
            this.#eventManager.registerEvent({
                type: 'event',
                method: `goog:cdp.${event}`,
                params: {
                    event,
                    params,
                    session: this.cdpSessionId,
                },
            }, this.id);
        });
    }
    async #enableFetch(stages) {
        const patterns = [];
        if (stages.request || stages.auth) {
            // CDP quirk we need request interception when we intercept auth
            patterns.push({
                urlPattern: '*',
                requestStage: 'Request',
            });
        }
        if (stages.response) {
            patterns.push({
                urlPattern: '*',
                requestStage: 'Response',
            });
        }
        if (patterns.length) {
            const oldStages = this.#fetchDomainStages;
            this.#fetchDomainStages = stages;
            try {
                await this.#cdpClient.sendCommand('Fetch.enable', {
                    patterns,
                    handleAuthRequests: stages.auth,
                });
            }
            catch {
                this.#fetchDomainStages = oldStages;
            }
        }
    }
    async #disableFetch() {
        const blockedRequest = this.#networkStorage
            .getRequestsByTarget(this)
            .filter((request) => request.interceptPhase);
        if (blockedRequest.length === 0) {
            this.#fetchDomainStages = {
                request: false,
                response: false,
                auth: false,
            };
            await this.#cdpClient.sendCommand('Fetch.disable');
        }
    }
    async toggleNetwork() {
        // TODO: respect the data collectors once CDP Network domain is enabled on-demand:
        // const networkEnable = this.#networkStorage.getCollectorsForBrowsingContext(this.topLevelId).length > 0;
        const stages = this.#networkStorage.getInterceptionStages(this.topLevelId);
        const fetchEnable = Object.values(stages).some((value) => value);
        const fetchChanged = this.#fetchDomainStages.request !== stages.request ||
            this.#fetchDomainStages.response !== stages.response ||
            this.#fetchDomainStages.auth !== stages.auth;
        this.#logger?.(LogType.debugInfo, 'Toggle Network', `Fetch (${fetchEnable}) ${fetchChanged}`);
        if (fetchEnable && fetchChanged) {
            await this.#enableFetch(stages);
        }
        if (!fetchEnable && fetchChanged) {
            await this.#disableFetch();
        }
    }
    /**
     * All the ProxyChannels from all the preload scripts of the given
     * BrowsingContext.
     */
    getChannels() {
        return this.#preloadScriptStorage
            .find()
            .flatMap((script) => script.channels);
    }
    async #updateWindowId() {
        const { windowId } = await this.#browserCdpClient.sendCommand('Browser.getWindowForTarget', { targetId: this.id });
        this.#windowId = windowId;
    }
    /** Loads all top-level preload scripts. */
    async #initAndEvaluatePreloadScripts() {
        await Promise.all(this.#preloadScriptStorage
            .find({
            // Needed for OOPIF
            targetId: this.topLevelId,
        })
            .map((script) => {
            return script.initInTarget(this, true);
        }));
    }
    async setDeviceMetricsOverride(viewport, devicePixelRatio, screenOrientation, screenArea) {
        if (viewport === null &&
            devicePixelRatio === null &&
            screenOrientation === null &&
            screenArea === null) {
            await this.cdpClient.sendCommand('Emulation.clearDeviceMetricsOverride');
            return;
        }
        const metricsOverride = {
            width: viewport?.width ?? 0,
            height: viewport?.height ?? 0,
            deviceScaleFactor: devicePixelRatio ?? 0,
            screenOrientation: this.#toCdpScreenOrientationAngle(screenOrientation) ?? undefined,
            mobile: false,
            screenWidth: screenArea?.width,
            screenHeight: screenArea?.height,
        };
        await this.cdpClient.sendCommand('Emulation.setDeviceMetricsOverride', metricsOverride);
    }
    /**
     * Immediately schedules all the required commands to configure user context
     * configuration and waits for them to finish. It's important to schedule them
     * in parallel, so that they are enqueued before any page's scripts.
     */
    async #setUserContextConfig(config) {
        const promises = [];
        promises.push(this.#cdpClient
            .sendCommand('Page.setPrerenderingAllowed', {
            isAllowed: !config.prerenderingDisabled,
        })
            .catch(() => {
            // Ignore CDP errors, as the command is not supported by iframe targets or
            // prerendered pages. Generic catch, as the error can vary between CdpClient
            // implementations: Tab vs Puppeteer.
        }));
        if (config.viewport !== undefined ||
            config.devicePixelRatio !== undefined ||
            config.screenOrientation !== undefined ||
            config.screenArea !== undefined) {
            promises.push(this.setDeviceMetricsOverride(config.viewport ?? null, config.devicePixelRatio ?? null, config.screenOrientation ?? null, config.screenArea ?? null).catch(() => {
                // Ignore CDP errors, as the command is not supported by iframe targets. Generic
                // catch, as the error can vary between CdpClient implementations: Tab vs
                // Puppeteer.
            }));
        }
        if (config.geolocation !== undefined && config.geolocation !== null) {
            promises.push(this.setGeolocationOverride(config.geolocation));
        }
        if (config.locale !== undefined) {
            promises.push(this.setLocaleOverride(config.locale));
        }
        if (config.timezone !== undefined) {
            promises.push(this.setTimezoneOverride(config.timezone));
        }
        if (config.extraHeaders !== undefined) {
            promises.push(this.setExtraHeaders(config.extraHeaders));
        }
        if (config.userAgent !== undefined ||
            config.locale !== undefined ||
            config.clientHints !== undefined) {
            promises.push(this.setUserAgentAndAcceptLanguage(config.userAgent, config.locale, config.clientHints));
        }
        if (config.scriptingEnabled !== undefined) {
            promises.push(this.setScriptingEnabled(config.scriptingEnabled));
        }
        if (config.acceptInsecureCerts !== undefined) {
            promises.push(this.cdpClient.sendCommand('Security.setIgnoreCertificateErrors', {
                ignore: config.acceptInsecureCerts,
            }));
        }
        if (config.emulatedNetworkConditions !== undefined) {
            promises.push(this.setEmulatedNetworkConditions(config.emulatedNetworkConditions));
        }
        if (config.maxTouchPoints !== undefined) {
            promises.push(this.setTouchOverride(config.maxTouchPoints));
        }
        await Promise.all(promises);
    }
    get topLevelId() {
        return (this.#browsingContextStorage.findTopLevelContextId(this.id) ?? this.id);
    }
    isSubscribedTo(moduleOrEvent) {
        return this.#eventManager.subscriptionManager.isSubscribedTo(moduleOrEvent, this.topLevelId);
    }
    #ignoreFileDialog() {
        const config = this.contextConfigStorage.getActiveConfig(this.topLevelId, this.userContext);
        return ((config.userPromptHandler?.file ??
            config.userPromptHandler?.default ??
            "ignore" /* Session.UserPromptHandlerType.Ignore */) ===
            "ignore" /* Session.UserPromptHandlerType.Ignore */);
    }
    async setGeolocationOverride(geolocation) {
        if (geolocation === null) {
            await this.cdpClient.sendCommand('Emulation.clearGeolocationOverride');
        }
        else if ('type' in geolocation) {
            if (geolocation.type !== 'positionUnavailable') {
                // Unreachable. Handled by params parser.
                throw new UnknownErrorException(`Unknown geolocation error ${geolocation.type}`);
            }
            // Omitting latitude, longitude or accuracy emulates position unavailable.
            await this.cdpClient.sendCommand('Emulation.setGeolocationOverride', {});
        }
        else if ('latitude' in geolocation) {
            await this.cdpClient.sendCommand('Emulation.setGeolocationOverride', {
                latitude: geolocation.latitude,
                longitude: geolocation.longitude,
                accuracy: geolocation.accuracy ?? 1,
                // `null` value is treated as "missing".
                altitude: geolocation.altitude ?? undefined,
                altitudeAccuracy: geolocation.altitudeAccuracy ?? undefined,
                heading: geolocation.heading ?? undefined,
                speed: geolocation.speed ?? undefined,
            });
        }
        else {
            // Unreachable. Handled by params parser.
            throw new UnknownErrorException('Unexpected geolocation coordinates value');
        }
    }
    async setTouchOverride(maxTouchPoints) {
        const touchEmulationParams = {
            enabled: maxTouchPoints !== null,
        };
        if (maxTouchPoints !== null) {
            touchEmulationParams.maxTouchPoints = maxTouchPoints;
        }
        await this.cdpClient.sendCommand('Emulation.setTouchEmulationEnabled', touchEmulationParams);
    }
    #toCdpScreenOrientationAngle(orientation) {
        if (orientation === null) {
            return null;
        }
        // https://w3c.github.io/screen-orientation/#the-current-screen-orientation-type-and-angle
        if (orientation.natural === "portrait" /* Emulation.ScreenOrientationNatural.Portrait */) {
            switch (orientation.type) {
                case 'portrait-primary':
                    return {
                        angle: 0,
                        type: 'portraitPrimary',
                    };
                case 'landscape-primary':
                    return {
                        angle: 90,
                        type: 'landscapePrimary',
                    };
                case 'portrait-secondary':
                    return {
                        angle: 180,
                        type: 'portraitSecondary',
                    };
                case 'landscape-secondary':
                    return {
                        angle: 270,
                        type: 'landscapeSecondary',
                    };
                default:
                    // Unreachable.
                    throw new UnknownErrorException(`Unexpected screen orientation type ${orientation.type}`);
            }
        }
        if (orientation.natural === "landscape" /* Emulation.ScreenOrientationNatural.Landscape */) {
            switch (orientation.type) {
                case 'landscape-primary':
                    return {
                        angle: 0,
                        type: 'landscapePrimary',
                    };
                case 'portrait-primary':
                    return {
                        angle: 90,
                        type: 'portraitPrimary',
                    };
                case 'landscape-secondary':
                    return {
                        angle: 180,
                        type: 'landscapeSecondary',
                    };
                case 'portrait-secondary':
                    return {
                        angle: 270,
                        type: 'portraitSecondary',
                    };
                default:
                    // Unreachable.
                    throw new UnknownErrorException(`Unexpected screen orientation type ${orientation.type}`);
            }
        }
        // Unreachable.
        throw new UnknownErrorException(`Unexpected orientation natural ${orientation.natural}`);
    }
    async setLocaleOverride(locale) {
        if (locale === null) {
            await this.cdpClient.sendCommand('Emulation.setLocaleOverride', {});
        }
        else {
            await this.cdpClient.sendCommand('Emulation.setLocaleOverride', {
                locale,
            });
        }
    }
    async setScriptingEnabled(scriptingEnabled) {
        await this.cdpClient.sendCommand('Emulation.setScriptExecutionDisabled', {
            value: scriptingEnabled === false,
        });
    }
    async setTimezoneOverride(timezone) {
        if (timezone === null) {
            await this.cdpClient.sendCommand('Emulation.setTimezoneOverride', {
                // If empty, disables the override and restores default host system timezone.
                timezoneId: '',
            });
        }
        else {
            await this.cdpClient.sendCommand('Emulation.setTimezoneOverride', {
                timezoneId: timezone,
            });
        }
    }
    async setExtraHeaders(headers) {
        await this.cdpClient.sendCommand('Network.setExtraHTTPHeaders', {
            headers,
        });
    }
    async setUserAgentAndAcceptLanguage(userAgent, acceptLanguage, clientHints) {
        const userAgentMetadata = clientHints
            ? {
                brands: clientHints.brands?.map((b) => ({
                    brand: b.brand,
                    version: b.version,
                })),
                fullVersionList: clientHints.fullVersionList,
                platform: clientHints.platform ?? '',
                platformVersion: clientHints.platformVersion ?? '',
                architecture: clientHints.architecture ?? '',
                model: clientHints.model ?? '',
                mobile: clientHints.mobile ?? false,
                bitness: clientHints.bitness ?? undefined,
                wow64: clientHints.wow64 ?? undefined,
                formFactors: clientHints.formFactors ?? undefined,
            }
            : undefined;
        await this.cdpClient.sendCommand('Emulation.setUserAgentOverride', {
            // `userAgent` is required if `userAgentMetadata` is provided.
            userAgent: userAgent || (userAgentMetadata ? this.#defaultUserAgent : ''),
            acceptLanguage: acceptLanguage ?? undefined,
            // We need to provide the platform to enable platform emulation.
            // Note that the value might be different from the one expected by the
            // legacy `navigator.platform` (e.g. `Win32` vs `Windows`).
            // https://github.com/w3c/webdriver-bidi/issues/1065
            platform: clientHints?.platform ?? undefined,
            userAgentMetadata,
        });
    }
    async setEmulatedNetworkConditions(networkConditions) {
        if (networkConditions !== null && networkConditions.type !== 'offline') {
            throw new UnsupportedOperationException(`Unsupported network conditions ${networkConditions.type}`);
        }
        await Promise.all([
            this.cdpClient.sendCommand('Network.emulateNetworkConditionsByRule', {
                offline: networkConditions?.type === 'offline',
                matchedNetworkConditions: [
                    {
                        urlPattern: '',
                        latency: 0,
                        downloadThroughput: -1,
                        uploadThroughput: -1,
                    },
                ],
            }),
            this.cdpClient.sendCommand('Network.overrideNetworkState', {
                offline: networkConditions?.type === 'offline',
                // TODO: restore the original `latency` value when emulation is removed.
                latency: 0,
                downloadThroughput: -1,
                uploadThroughput: -1,
            }),
        ]);
    }
}
//# sourceMappingURL=CdpTarget.js.map