// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chai';
import sinon from 'sinon';
import * as Bindings from '../../models/bindings/bindings.js';
import * as Persistence from '../../models/persistence/persistence.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';
import * as Workspace from '../../models/workspace/workspace.js';
import { setupLocaleHooks } from '../../testing/LocaleHelpers.js';
import { MockCDPConnection } from '../../testing/MockCDPConnection.js';
import { createWorkspaceProject } from '../../testing/OverridesHelpers.js';
import { setupRuntimeHooks } from '../../testing/RuntimeHelpers.js';
import { setupSettingsHooks } from '../../testing/SettingsHelpers.js';
import { TestUniverse } from '../../testing/TestUniverse.js';
import * as Common from '../common/common.js';
import * as Platform from '../platform/platform.js';
import * as SDK from './sdk.js';
const { urlString } = Platform.DevToolsPath;
const LONG_URL_PART = 'LoremIpsumDolorSitAmetConsecteturAdipiscingElitPhasellusVitaeOrciInAugueCondimentumTinciduntUtEgetDolorQuisqueEfficiturUltricesTinciduntVivamusVelitPurusCommodoQuisErosSitAmetTemporMalesuadaNislNullamTtempusVulputateAugueEgetScelerisqueLacusVestibulumNon/index.html';
describe('NetworkManager', () => {
    setupLocaleHooks();
    setupSettingsHooks();
    setupRuntimeHooks();
    describe('request post data', () => {
        async function createPostRequestWithHeaders(headers, universe, connection) {
            const networkManager = new SDK.NetworkManager.NetworkManager(universe.createTarget({ connection }));
            const requestStartedPromise = networkManager.once(SDK.NetworkManager.Events.RequestStarted);
            networkManager.dispatcher.requestWillBeSent({
                requestId: 'mock-request-id',
                loaderId: 'mock-loader-id',
                documentURL: 'https://example.test/',
                request: {
                    url: 'https://example.test/',
                    method: 'POST',
                    headers,
                    hasPostData: true,
                    postData: 'garbled-inline-post-data',
                },
                timestamp: 1,
                wallTime: 1,
                initiator: { type: "other" /* Protocol.Network.InitiatorType.Other */ },
            });
            const { request } = await requestStartedPromise;
            return request;
        }
        it('decodes gzip-compressed request form data', async () => {
            const universe = new TestUniverse();
            const connection = new MockCDPConnection();
            SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager });
            const expectedPostData = 'a=1&b=hello+world';
            const compressedPostData = await Common.Gzip.compress(expectedPostData);
            const encodedPostData = btoa(String.fromCharCode(...new Uint8Array(compressedPostData)));
            connection.setSuccessHandler('Network.getRequestPostData', () => ({
                postData: encodedPostData,
                base64Encoded: true,
            }));
            const request = await createPostRequestWithHeaders({
                'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
                'Content-Encoding': 'gzip',
            }, universe, connection);
            const requestFormData = await request.requestFormData();
            assert.strictEqual(requestFormData, expectedPostData);
        });
    });
    describe('Direct TCP socket handling', () => {
        it('on CDP created event creates request ', () => {
            const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
            const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
            const startedRequests = [];
            networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                startedRequests.push(event.data.request);
            });
            networkDispatcher.directTCPSocketCreated({
                identifier: 'mockId',
                remoteAddr: 'example.com',
                remotePort: 1001,
                options: {
                    noDelay: true,
                    keepAliveDelay: 1002,
                    sendBufferSize: 1003,
                    receiveBufferSize: 1004,
                    dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                },
                timestamp: 1000,
            });
            assert.lengthOf(startedRequests, 1);
            const req = startedRequests[0];
            assert.strictEqual(req.requestId(), 'mockId');
            assert.strictEqual(req.remoteAddress(), 'example.com:1001');
            assert.strictEqual(req.url(), urlString `example.com:1001`);
            assert.isTrue(req.hasNetworkData);
            assert.strictEqual(req.protocol, 'tcp');
            assert.strictEqual(req.statusText, 'Opening');
            assert.deepEqual(req.directSocketInfo, {
                type: SDK.NetworkRequest.DirectSocketType.TCP,
                status: SDK.NetworkRequest.DirectSocketStatus.OPENING,
                createOptions: {
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    noDelay: true,
                    keepAliveDelay: 1002,
                    sendBufferSize: 1003,
                    receiveBufferSize: 1004,
                    dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                }
            });
            assert.strictEqual(req.resourceType(), Common.ResourceType.resourceTypes.DirectSocket);
            assert.strictEqual(req.issueTime(), 1000);
            assert.strictEqual(req.startTime, 1000);
        });
        describe('on CDP opened event', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketOpened({
                    identifier: 'mockId',
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    timestamp: 1000,
                });
                assert.lengthOf(updatedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockId', url: 'example.com', timestamp: 1000 });
                networkDispatcher.directTCPSocketOpened({
                    identifier: 'mockId',
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    timestamp: 1000,
                });
                assert.lengthOf(updatedRequests, 0);
            });
            it('updates request successfully', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketCreated({
                    identifier: 'mockId',
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    options: {
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                    timestamp: 1000,
                });
                assert.lengthOf(updatedRequests, 0);
                // update the request and check all fields are filled as necessary
                networkDispatcher.directTCPSocketOpened({
                    identifier: 'mockId',
                    remoteAddr: '192.81.29.1',
                    remotePort: 1010,
                    timestamp: 2000,
                    localAddr: '127.0.0.1',
                    localPort: 8000,
                });
                assert.lengthOf(updatedRequests, 1);
                const req = updatedRequests[0];
                assert.deepEqual(req.directSocketInfo, {
                    type: SDK.NetworkRequest.DirectSocketType.TCP,
                    status: SDK.NetworkRequest.DirectSocketStatus.OPEN,
                    createOptions: {
                        remoteAddr: 'example.com',
                        remotePort: 1001,
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                    openInfo: {
                        remoteAddr: '192.81.29.1',
                        remotePort: 1010,
                        localAddr: '127.0.0.1',
                        localPort: 8000,
                    }
                });
            });
        });
        describe('on CDP event aborted', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketAborted({
                    identifier: 'mockId',
                    errorMessage: 'mock error message',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockId', url: 'example.com', timestamp: 1000 });
                networkDispatcher.directTCPSocketAborted({
                    identifier: 'mockId',
                    errorMessage: 'mock error message',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('updates request successfully', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketCreated({
                    identifier: 'mockId',
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    options: {
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
                // update the request and check all fields are filled as necessary
                networkDispatcher.directTCPSocketAborted({
                    identifier: 'mockId',
                    errorMessage: 'mock error message',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 1);
                const req = finishedRequests[0];
                assert.strictEqual(req.statusText, 'Aborted');
                assert.isTrue(req.failed);
                assert.isTrue(req.finished);
                assert.deepEqual(req.directSocketInfo, {
                    type: SDK.NetworkRequest.DirectSocketType.TCP,
                    status: SDK.NetworkRequest.DirectSocketStatus.ABORTED,
                    errorMessage: 'mock error message',
                    createOptions: {
                        remoteAddr: 'example.com',
                        remotePort: 1001,
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                });
            });
        });
        describe('on CDP event closed', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketClosed({
                    identifier: 'mockId',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockId', url: 'example.com', timestamp: 1000 });
                networkDispatcher.directTCPSocketClosed({
                    identifier: 'mockId',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('updates request successfully', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketCreated({
                    identifier: 'mockId',
                    remoteAddr: 'example.com',
                    remotePort: 1001,
                    options: {
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 0);
                networkDispatcher.directTCPSocketClosed({
                    identifier: 'mockId',
                    timestamp: 1000,
                });
                assert.lengthOf(finishedRequests, 1);
                const req = finishedRequests[0];
                assert.strictEqual(req.statusText, 'Closed');
                assert.notExists(req.failed);
                assert.isTrue(req.finished);
                assert.deepEqual(req.directSocketInfo, {
                    type: SDK.NetworkRequest.DirectSocketType.TCP,
                    status: SDK.NetworkRequest.DirectSocketStatus.CLOSED,
                    createOptions: {
                        remoteAddr: 'example.com',
                        remotePort: 1001,
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                });
            });
        });
        describe('on CDP chunk event', () => {
            let networkManager;
            let networkDispatcher;
            let updatedRequests;
            let targetRequest;
            const mockIdentifier = 'mockParamChunkId';
            const baseTimestamp = 1000;
            beforeEach(() => {
                networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                updatedRequests = [];
                targetRequest = null;
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    if (event.data.request.requestId() === mockIdentifier) {
                        targetRequest = event.data.request;
                    }
                });
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directTCPSocketCreated({
                    identifier: mockIdentifier,
                    remoteAddr: 'example-param.com',
                    remotePort: 1005,
                    options: {
                        noDelay: true,
                        keepAliveDelay: 1002,
                        sendBufferSize: 1003,
                        receiveBufferSize: 1004,
                        dnsQueryType: "ipv4" /* Protocol.Network.DirectSocketDnsQueryType.Ipv4 */,
                    },
                    timestamp: baseTimestamp,
                });
            });
            const testCases = [
                {
                    description: 'adds SENT chunk to request successfully',
                    eventPayload: { data: 'c2VudCBkYXRh', timestamp: 3000 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.SEND,
                        data: 'c2VudCBkYXRh',
                        timestamp: 3000,
                    }
                },
                {
                    description: 'adds RECEIVED chunk to request successfully',
                    eventPayload: { data: 'cmVjZWl2ZWQgZGF0YQ==', timestamp: 4000 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.RECEIVE,
                        data: 'cmVjZWl2ZWQgZGF0YQ==',
                        timestamp: 4000,
                    }
                },
            ];
            testCases.forEach(testCase => {
                it(testCase.description, () => {
                    assert.exists(targetRequest, 'Target request should be created in beforeEach');
                    assert.lengthOf(updatedRequests, 0, 'No updates should occur during initial setup');
                    switch (testCase.expectedChunk.type) {
                        case SDK.NetworkRequest.DirectSocketChunkType.SEND:
                            networkDispatcher.directTCPSocketChunkSent({
                                identifier: mockIdentifier,
                                data: testCase.eventPayload.data,
                                timestamp: testCase.eventPayload.timestamp,
                            });
                            break;
                        case SDK.NetworkRequest.DirectSocketChunkType.RECEIVE:
                            networkDispatcher.directTCPSocketChunkReceived({
                                identifier: mockIdentifier,
                                data: testCase.eventPayload.data,
                                timestamp: testCase.eventPayload.timestamp,
                            });
                            break;
                    }
                    assert.lengthOf(updatedRequests, 1, 'Should trigger exactly one update');
                    const req = updatedRequests[0];
                    assert.strictEqual(req.requestId(), mockIdentifier);
                    assert.strictEqual(req.responseReceivedTime, testCase.eventPayload.timestamp, 'responseReceivedTime should be updated');
                    const chunks = req.directSocketChunks();
                    assert.lengthOf(chunks, 1, 'Should have exactly one chunk added');
                    assert.deepEqual(chunks[0], testCase.expectedChunk, 'Chunk details should match expected');
                });
            });
        });
    });
    describe('Direct UDP socket handling', () => {
        describe('on CDP created event', () => {
            it('creates request for connected UDP', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const startedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    startedRequests.push(event.data.request);
                });
                // remoteAddress/remotePort and localAddress/localPort
                // pairs cannot be specified together.
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId1',
                    options: {
                        remoteAddr: 'example.com',
                        remotePort: 2001,
                        sendBufferSize: 2003,
                        receiveBufferSize: 2004,
                        dnsQueryType: "ipv6" /* Protocol.Network.DirectSocketDnsQueryType.Ipv6 */,
                    },
                    timestamp: 2000,
                });
                assert.lengthOf(startedRequests, 1);
                const req = startedRequests[0];
                assert.strictEqual(req.requestId(), 'mockUdpId1');
                assert.strictEqual(req.remoteAddress(), 'example.com:2001');
                assert.strictEqual(req.url(), urlString `example.com:2001`);
                assert.isTrue(req.hasNetworkData);
                assert.strictEqual(req.protocol, 'udp');
                assert.strictEqual(req.statusText, 'Opening');
                assert.deepEqual(req.directSocketInfo, {
                    type: SDK.NetworkRequest.DirectSocketType.UDP_CONNECTED,
                    status: SDK.NetworkRequest.DirectSocketStatus.OPENING,
                    createOptions: {
                        remoteAddr: 'example.com',
                        remotePort: 2001,
                        localAddr: undefined,
                        localPort: undefined,
                        sendBufferSize: 2003,
                        receiveBufferSize: 2004,
                        dnsQueryType: "ipv6" /* Protocol.Network.DirectSocketDnsQueryType.Ipv6 */,
                        multicastLoopback: undefined,
                        multicastTimeToLive: undefined,
                        multicastAllowAddressSharing: undefined,
                    },
                    joinedMulticastGroups: new Set(),
                });
                assert.strictEqual(req.resourceType(), Common.ResourceType.resourceTypes.DirectSocket);
                assert.strictEqual(req.issueTime(), 2000);
                assert.strictEqual(req.startTime, 2000);
            });
            it('creates request for bound UDP', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const startedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    startedRequests.push(event.data.request);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId2',
                    options: {
                        localAddr: '192.168.0.1',
                        localPort: 2005,
                        sendBufferSize: 2006,
                        receiveBufferSize: 2007,
                        multicastLoopback: undefined,
                        multicastTimeToLive: undefined,
                        multicastAllowAddressSharing: undefined,
                    },
                    timestamp: 2100,
                });
                assert.lengthOf(startedRequests, 1);
                const req = startedRequests[0];
                assert.strictEqual(req.requestId(), 'mockUdpId2');
                // No remote address for bound UDP initially
                assert.strictEqual(req.remoteAddress(), '');
                // URL uses localAddr if remoteAddr is not present
                assert.strictEqual(req.url(), urlString `192.168.0.1:2005`);
                assert.isTrue(req.hasNetworkData);
                assert.strictEqual(req.protocol, 'udp');
                assert.strictEqual(req.statusText, 'Opening');
                assert.deepEqual(req.directSocketInfo, {
                    type: SDK.NetworkRequest.DirectSocketType.UDP_BOUND,
                    status: SDK.NetworkRequest.DirectSocketStatus.OPENING,
                    createOptions: {
                        remoteAddr: undefined,
                        remotePort: undefined,
                        localAddr: '192.168.0.1',
                        localPort: 2005,
                        sendBufferSize: 2006,
                        receiveBufferSize: 2007,
                        dnsQueryType: undefined,
                        multicastLoopback: undefined,
                        multicastTimeToLive: undefined,
                        multicastAllowAddressSharing: undefined,
                    },
                    joinedMulticastGroups: new Set(),
                });
                assert.strictEqual(req.resourceType(), Common.ResourceType.resourceTypes.DirectSocket);
                assert.strictEqual(req.issueTime(), 2100);
                assert.strictEqual(req.startTime, 2100);
            });
            it('does nothing if no localAddr for bound UDP', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const startedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    startedRequests.push(event.data.request);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId3',
                    options: {
                    // Skip request if remoteAddr and localAddr are both absent.
                    },
                    timestamp: 2200,
                });
                assert.lengthOf(startedRequests, 0);
            });
        });
        describe('on CDP opened event', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketOpened({
                    identifier: 'mockUdpId',
                    localAddr: '127.0.0.1',
                    localPort: 2002,
                    timestamp: 2000,
                });
                assert.lengthOf(updatedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockUdpId', url: 'example.com', timestamp: 2000 });
                networkDispatcher.directUDPSocketOpened({
                    identifier: 'mockUdpId',
                    localAddr: '127.0.0.1',
                    localPort: 2002,
                    timestamp: 2000,
                });
                assert.lengthOf(updatedRequests, 0);
            });
            it('updates request successfully for connected UDP', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId',
                    options: {
                        remoteAddr: 'example.com',
                        remotePort: 2001,
                    },
                    timestamp: 2000,
                });
                assert.lengthOf(updatedRequests, 0);
                networkDispatcher.directUDPSocketOpened({
                    identifier: 'mockUdpId',
                    localAddr: '127.0.0.1',
                    localPort: 8001,
                    remoteAddr: '192.168.1.1',
                    remotePort: 2010,
                    timestamp: 2050,
                });
                assert.lengthOf(updatedRequests, 1);
                const req = updatedRequests[0];
                assert.strictEqual(req.remoteAddress(), '192.168.1.1:2010');
                assert.strictEqual(req.url(), urlString `192.168.1.1:2010`);
                assert.strictEqual(req.statusText, 'Open');
                assert.deepEqual(req.directSocketInfo?.status, SDK.NetworkRequest.DirectSocketStatus.OPEN);
                assert.deepEqual(req.directSocketInfo?.openInfo, {
                    remoteAddr: '192.168.1.1',
                    remotePort: 2010,
                    localAddr: '127.0.0.1',
                    localPort: 8001,
                });
                assert.strictEqual(req.responseReceivedTime, 2050);
            });
            it('updates request successfully for bound UDP', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const updatedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpBoundId',
                    options: {
                        localAddr: '0.0.0.0',
                        localPort: 7000,
                    },
                    timestamp: 2100,
                });
                assert.lengthOf(updatedRequests, 0);
                networkDispatcher.directUDPSocketOpened({
                    identifier: 'mockUdpBoundId',
                    localAddr: '192.168.0.5',
                    localPort: 7000,
                    // No remoteAddr/remotePort for bound socket open event
                    timestamp: 2150,
                });
                assert.lengthOf(updatedRequests, 1);
                const req = updatedRequests[0];
                assert.strictEqual(req.remoteAddress(), ''); // Still no remote address
                assert.strictEqual(req.url(), urlString `192.168.0.5:7000`); // URL updated with resolved local address
                assert.strictEqual(req.statusText, 'Open');
                assert.deepEqual(req.directSocketInfo?.status, SDK.NetworkRequest.DirectSocketStatus.OPEN);
                assert.deepEqual(req.directSocketInfo?.openInfo, {
                    remoteAddr: undefined,
                    remotePort: undefined,
                    localAddr: '192.168.0.5',
                    localPort: 7000,
                });
                assert.strictEqual(req.responseReceivedTime, 2150);
            });
        });
        describe('on CDP event aborted', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketAborted({
                    identifier: 'mockUdpId',
                    errorMessage: 'mock udp error',
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockUdpId', url: 'example.com', timestamp: 2000 });
                networkDispatcher.directUDPSocketAborted({
                    identifier: 'mockUdpId',
                    errorMessage: 'mock udp error',
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('updates request successfully', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId',
                    options: {
                        remoteAddr: 'example.com',
                        remotePort: 2001,
                    },
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
                networkDispatcher.directUDPSocketAborted({
                    identifier: 'mockUdpId',
                    errorMessage: 'UDP aborted by peer',
                    timestamp: 2050,
                });
                assert.lengthOf(finishedRequests, 1);
                const req = finishedRequests[0];
                assert.strictEqual(req.statusText, 'Aborted');
                assert.isTrue(req.failed);
                assert.isTrue(req.finished);
                assert.strictEqual(req.endTime, 2050);
                assert.deepEqual(req.directSocketInfo?.status, SDK.NetworkRequest.DirectSocketStatus.ABORTED);
                assert.strictEqual(req.directSocketInfo?.errorMessage, 'UDP aborted by peer');
            });
        });
        describe('on CDP event closed', () => {
            it('does nothing if no request exists', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketClosed({
                    identifier: 'mockUdpId',
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('does nothing if the request has no direct socket info', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.webTransportCreated({ transportId: 'mockUdpId', url: 'example.com', timestamp: 2000 });
                networkDispatcher.directUDPSocketClosed({
                    identifier: 'mockUdpId',
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
            });
            it('updates request successfully', () => {
                const networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                const finishedRequests = [];
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, event => {
                    finishedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: 'mockUdpId',
                    options: {
                        remoteAddr: 'example.com',
                        remotePort: 2001,
                    },
                    timestamp: 2000,
                });
                assert.lengthOf(finishedRequests, 0);
                networkDispatcher.directUDPSocketClosed({
                    identifier: 'mockUdpId',
                    timestamp: 2050,
                });
                assert.lengthOf(finishedRequests, 1);
                const req = finishedRequests[0];
                assert.strictEqual(req.statusText, 'Closed');
                assert.notExists(req.failed);
                assert.isTrue(req.finished);
                assert.strictEqual(req.endTime, 2050);
                assert.deepEqual(req.directSocketInfo?.status, SDK.NetworkRequest.DirectSocketStatus.CLOSED);
            });
        });
        describe('on CDP chunk event', () => {
            let networkManager;
            let networkDispatcher;
            let updatedRequests;
            let targetRequest;
            const mockIdentifier = 'mockUdpChunkId';
            const baseTimestamp = 3000;
            beforeEach(() => {
                networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                updatedRequests = [];
                targetRequest = null;
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    if (event.data.request.requestId() === mockIdentifier) {
                        targetRequest = event.data.request;
                    }
                });
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: mockIdentifier,
                    options: {
                        remoteAddr: 'udp-example.com',
                        remotePort: 3005,
                    },
                    timestamp: baseTimestamp,
                });
            });
            const testCases = [
                {
                    description: 'adds SENT chunk to request successfully (connected)',
                    eventPayload: {
                        // No remoteAddr/Port for connected.
                        message: { data: 'c2VudCBkYXRh==' },
                        timestamp: 3100
                    },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.SEND,
                        data: 'c2VudCBkYXRh==',
                        timestamp: 3100,
                        remoteAddress: undefined,
                        remotePort: undefined
                    }
                },
                {
                    description: 'adds SENT chunk to request successfully (bound with address)',
                    eventPayload: { message: { data: 'Ym91bmQgc2VudA==', remoteAddr: '10.0.0.1', remotePort: 4000 }, timestamp: 3150 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.SEND,
                        data: 'Ym91bmQgc2VudA==',
                        timestamp: 3150,
                        remoteAddress: '10.0.0.1',
                        remotePort: 4000
                    }
                },
                {
                    description: 'adds SENT chunk to request successfully (bound with address, no port)',
                    eventPayload: { message: { data: 'Ym91bmQgc2VudCBwbA==', remoteAddr: '10.0.0.2' }, timestamp: 3160 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.SEND,
                        data: 'Ym91bmQgc2VudCBwbA==',
                        timestamp: 3160,
                        remoteAddress: '10.0.0.2',
                        remotePort: undefined
                    }
                },
                {
                    description: 'adds RECEIVED chunk to request successfully (connected)',
                    eventPayload: { message: { data: 'cmVjZWl2ZWQgZGF0YQ==' }, timestamp: 3200 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.RECEIVE,
                        data: 'cmVjZWl2ZWQgZGF0YQ==',
                        timestamp: 3200,
                        remoteAddress: undefined,
                        remotePort: undefined
                    }
                },
                {
                    description: 'adds RECEIVED chunk to request successfully (bound with address)',
                    eventPayload: { message: { data: 'Ym91bmQgcmVjZWl2ZWQ=', remoteAddr: '10.0.0.3', remotePort: 4001 }, timestamp: 3250 },
                    expectedChunk: {
                        type: SDK.NetworkRequest.DirectSocketChunkType.RECEIVE,
                        data: 'Ym91bmQgcmVjZWl2ZWQ=',
                        timestamp: 3250,
                        remoteAddress: '10.0.0.3',
                        remotePort: 4001
                    }
                },
            ];
            testCases.forEach(testCase => {
                it(testCase.description, () => {
                    assert.exists(targetRequest, 'Target request should be created in beforeEach');
                    updatedRequests.length = 0;
                    switch (testCase.expectedChunk.type) {
                        case SDK.NetworkRequest.DirectSocketChunkType.SEND:
                            networkDispatcher.directUDPSocketChunkSent({
                                identifier: mockIdentifier,
                                message: testCase.eventPayload.message,
                                timestamp: testCase.eventPayload.timestamp,
                            });
                            break;
                        case SDK.NetworkRequest.DirectSocketChunkType.RECEIVE:
                            networkDispatcher.directUDPSocketChunkReceived({
                                identifier: mockIdentifier,
                                message: testCase.eventPayload.message,
                                timestamp: testCase.eventPayload.timestamp,
                            });
                            break;
                    }
                    assert.lengthOf(updatedRequests, 1, 'Should trigger exactly one update');
                    const req = updatedRequests[0];
                    assert.strictEqual(req.requestId(), mockIdentifier);
                    assert.strictEqual(req.responseReceivedTime, testCase.eventPayload.timestamp, 'responseReceivedTime should be updated');
                    const chunks = req.directSocketChunks();
                    assert.lengthOf(chunks, 1, 'Should have exactly one chunk added');
                    assert.deepEqual(chunks[0], testCase.expectedChunk, 'Chunk details should match expected');
                });
            });
        });
        describe('on Join Multicast Group events', () => {
            let networkManager;
            let networkDispatcher;
            let updatedRequests;
            let request;
            const mockIdentifier = 'mockUdpMulticastId';
            beforeEach(() => {
                networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                updatedRequests = [];
                request = null;
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    if (event.data.request.requestId() === mockIdentifier) {
                        request = event.data.request;
                        // Reset state for the new request instance for each test
                        if (request?.directSocketInfo) {
                            request.directSocketInfo.joinedMulticastGroups = new Set();
                        }
                    }
                });
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: mockIdentifier,
                    options: { localAddr: '0.0.0.0', localPort: 8080 },
                    timestamp: 3000,
                });
            });
            it('directUDPSocketJoinedMulticastGroup adds the first group', () => {
                assert.strictEqual(request.directSocketInfo?.joinedMulticastGroups?.size, 0);
                networkDispatcher.directUDPSocketJoinedMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.17',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set(['237.132.100.17']));
                assert.lengthOf(updatedRequests, 1);
                assert.strictEqual(updatedRequests[0], request);
            });
            it('directUDPSocketJoinedMulticastGroup adds subsequent groups', () => {
                request.directSocketInfo.joinedMulticastGroups = new Set(['237.132.100.17']);
                networkDispatcher.directUDPSocketJoinedMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.18',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set(['237.132.100.17', '237.132.100.18']));
                assert.lengthOf(updatedRequests, 1);
                assert.strictEqual(updatedRequests[0], request);
            });
            it('directUDPSocketJoinedMulticastGroup does not add a duplicate group', () => {
                request.directSocketInfo.joinedMulticastGroups = new Set(['237.132.100.17']);
                networkDispatcher.directUDPSocketJoinedMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.17',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set(['237.132.100.17']));
                assert.lengthOf(updatedRequests, 0);
            });
        });
        describe('on Leave Multicast Group events', () => {
            let networkManager;
            let networkDispatcher;
            let updatedRequests;
            let request;
            const mockIdentifier = 'mockUdpMulticastId';
            beforeEach(() => {
                networkManager = new SDK.NetworkManager.NetworkManager(new TestUniverse().createTarget({}));
                networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
                updatedRequests = [];
                request = null;
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                    if (event.data.request.requestId() === mockIdentifier) {
                        request = event.data.request;
                        // Reset state for the new request instance for each test
                        if (request?.directSocketInfo) {
                            request.directSocketInfo.joinedMulticastGroups = new Set();
                        }
                    }
                });
                networkManager.addEventListener(SDK.NetworkManager.Events.RequestUpdated, event => {
                    updatedRequests.push(event.data);
                });
                networkDispatcher.directUDPSocketCreated({
                    identifier: mockIdentifier,
                    options: { localAddr: '0.0.0.0', localPort: 8080 },
                    timestamp: 3000,
                });
            });
            it('directUDPSocketLeftMulticastGroup removes an existing group', () => {
                request.directSocketInfo.joinedMulticastGroups = new Set(['237.132.100.17', '237.132.100.18']);
                updatedRequests.length = 0;
                networkDispatcher.directUDPSocketLeftMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.17',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set(['237.132.100.18']));
                assert.lengthOf(updatedRequests, 1);
            });
            it('directUDPSocketLeftMulticastGroup does not remove a non-existing group', () => {
                request.directSocketInfo.joinedMulticastGroups = new Set(['237.132.100.17']);
                updatedRequests.length = 0;
                networkDispatcher.directUDPSocketLeftMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.18',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set(['237.132.100.17']));
                assert.lengthOf(updatedRequests, 0);
            });
            it('directUDPSocketLeftMulticastGroup handles empty array', () => {
                request.directSocketInfo.joinedMulticastGroups = new Set();
                updatedRequests.length = 0;
                networkDispatcher.directUDPSocketLeftMulticastGroup({
                    identifier: mockIdentifier,
                    IPAddress: '237.132.100.17',
                });
                assert.deepEqual(request.directSocketInfo?.joinedMulticastGroups, new Set());
                assert.lengthOf(updatedRequests, 0);
            });
        });
    });
});
describe('MultitargetNetworkManager', () => {
    let universe;
    beforeEach(() => {
        universe = new TestUniverse();
        SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager });
    });
    describe('Trust Token done event', () => {
        it('is not lost when arriving before the corresponding requestWillBeSent event', () => {
            // 1) Setup a NetworkManager and listen to "RequestStarted" events.
            const networkManager = new Common.ObjectWrapper.ObjectWrapper();
            const startedRequests = [];
            networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, event => {
                startedRequests.push(event.data.request);
            });
            const networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
            // 2) Fire a trust token event, followed by a requestWillBeSent event.
            const mockEvent = { requestId: 'mockId' };
            networkDispatcher.trustTokenOperationDone(mockEvent);
            networkDispatcher.requestWillBeSent({ requestId: 'mockId', request: { url: 'example.com' } });
            // 3) Check that the resulting NetworkRequest has the Trust Token Event data associated with it.
            assert.lengthOf(startedRequests, 1);
            assert.strictEqual(startedRequests[0].trustTokenOperationDoneEvent(), mockEvent);
        });
    });
    it('handles worker requests originating from the frame target', async () => {
        const target = universe.createTarget({});
        const workerTarget = universe.createTarget({ type: SDK.Target.Type.Worker });
        const multiTargetNetworkManager = SDK.NetworkManager.MultitargetNetworkManager.instance();
        const initialNetworkManager = target.model(SDK.NetworkManager.NetworkManager);
        assert.strictEqual(multiTargetNetworkManager.inflightMainResourceRequests.size, 0);
        const requestId = 'mockId';
        const requestPromise = initialNetworkManager.once(SDK.NetworkManager.Events.RequestStarted);
        initialNetworkManager.dispatcher.requestWillBeSent({ requestId, loaderId: '', request: { url: 'example.com' } });
        const { request } = await requestPromise;
        assert.strictEqual(SDK.NetworkManager.NetworkManager.forRequest(request), initialNetworkManager);
        assert.isOk(multiTargetNetworkManager.inflightMainResourceRequests.has(requestId));
        const workerNetworkManager = workerTarget.model(SDK.NetworkManager.NetworkManager);
        workerNetworkManager.dispatcher.loadingFinished({ requestId });
        assert.strictEqual(SDK.NetworkManager.NetworkManager.forRequest(request), workerNetworkManager);
        assert.isNotOk(multiTargetNetworkManager.inflightMainResourceRequests.has(requestId));
    });
    it('uses main frame to get certificate', () => {
        const targetManager = universe.targetManager;
        const tabTarget = universe.createTarget({ type: SDK.Target.Type.TAB });
        const mainFrameTarget = universe.createTarget({ parentTarget: tabTarget });
        const prerenderTarget = universe.createTarget({ parentTarget: tabTarget, subtype: 'prerender' });
        const subframeTarget = universe.createTarget({ parentTarget: mainFrameTarget, subtype: '' });
        const unexpectedCalls = [tabTarget, prerenderTarget, subframeTarget].map(t => sinon.spy(t.networkAgent(), 'invoke_getCertificate'));
        const expectedCall = sinon.spy(mainFrameTarget.networkAgent(), 'invoke_getCertificate');
        void (new SDK.NetworkManager.MultitargetNetworkManager(targetManager)).getCertificate('https://example.com');
        for (const unexpectedCall of unexpectedCalls) {
            sinon.assert.notCalled(unexpectedCall);
        }
        assert.isTrue(expectedCall.calledOnceWith({ origin: 'https://example.com' }));
    });
    it('blocking settings are consistent after change', async () => {
        const multitargetNetworkManager = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true });
        let eventCounter = 0;
        multitargetNetworkManager.addEventListener("BlockedPatternsChanged" /* SDK.NetworkManager.MultitargetNetworkManager.Events.BLOCKED_PATTERNS_CHANGED */, () => eventCounter++);
        const blockingEnabledSetting = Common.Settings.Settings.instance().moduleSetting('request-blocking-enabled');
        // Change blocking setting via Common.Settings.Settings.
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isFalse(multitargetNetworkManager.requestConditions.conditionsEnabled);
        blockingEnabledSetting.set(true);
        assert.strictEqual(eventCounter, 1);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'example.com', enabled: true }));
        assert.strictEqual(eventCounter, 2);
        assert.isTrue(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.clear();
        assert.strictEqual(eventCounter, 3);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        blockingEnabledSetting.set(false);
        assert.strictEqual(eventCounter, 4);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isFalse(multitargetNetworkManager.requestConditions.conditionsEnabled);
        // Change blocking setting via MultitargetNetworkManager.
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isFalse(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.conditionsEnabled = (true);
        assert.strictEqual(eventCounter, 5);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'example.com', enabled: true }));
        assert.strictEqual(eventCounter, 6);
        assert.isTrue(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.clear();
        assert.strictEqual(eventCounter, 7);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isTrue(multitargetNetworkManager.requestConditions.conditionsEnabled);
        multitargetNetworkManager.requestConditions.conditionsEnabled = (false);
        assert.strictEqual(eventCounter, 8);
        assert.isFalse(multitargetNetworkManager.isBlocking());
        assert.isFalse(multitargetNetworkManager.requestConditions.conditionsEnabled);
    });
    it('blocking settings allow deleting an item in the middle of the list', () => {
        const conditions = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true }).requestConditions;
        const condition1 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url1', enabled: true });
        const condition2 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url2', enabled: true });
        const condition3 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url3', enabled: true });
        conditions.add(condition1, condition2, condition3);
        assert.deepEqual(conditions.conditions.toArray(), [condition1, condition2, condition3]);
        conditions.delete(condition2);
        assert.deepEqual(conditions.conditions.toArray(), [condition1, condition3]);
    });
    it('applies global conditions if request conditions are disabled', () => {
        const connection = new MockCDPConnection();
        universe.createTarget({ connection });
        const manager = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager });
        const rules = [];
        connection.setSuccessHandler('Network.emulateNetworkConditionsByRule', request => {
            rules.push(request);
            return { ruleIds: [] };
        });
        manager.setNetworkConditions(SDK.NetworkManager.Slow4GConditions);
        assert.lengthOf(rules, 1);
        sinon.assert.match(rules[0], sinon.match({
            offline: false,
            matchedNetworkConditions: [sinon.match({
                    downloadThroughput: SDK.NetworkManager.Slow4GConditions.download,
                    latency: SDK.NetworkManager.Slow4GConditions.latency,
                    uploadThroughput: SDK.NetworkManager.Slow4GConditions.upload,
                })],
        }));
    });
    it('calls the request conditions model for global throttling if individual request throttling is enabled', () => {
        const manager = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager });
        manager.setNetworkConditions(SDK.NetworkManager.Slow4GConditions);
        const targetManager = new SDK.NetworkManager.NetworkManager(universe.createTarget({}));
        const emulateNetworkConditions = sinon.stub(targetManager.target().networkAgent(), 'invoke_emulateNetworkConditions');
        const stub = sinon.stub(manager.requestConditions, 'applyConditions');
        manager.modelAdded(targetManager);
        sinon.assert.calledOnce(stub);
        assert.deepEqual(stub.args[0], [false, SDK.NetworkManager.Slow4GConditions, targetManager.target().networkAgent()]);
        manager.setNetworkConditions(SDK.NetworkManager.Slow3GConditions);
        sinon.assert.calledTwice(stub);
        assert.deepEqual(stub.args[1], [false, SDK.NetworkManager.Slow3GConditions, targetManager.target().networkAgent()]);
        sinon.assert.notCalled(emulateNetworkConditions);
    });
    it('can reorder the conditions', () => {
        const requestConditions = new SDK.NetworkManager.RequestConditions(universe.settings);
        const condition1 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url1', enabled: true });
        const condition2 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url2', enabled: true });
        const condition3 = SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'url3', enabled: true });
        requestConditions.add(condition1, condition2, condition3);
        const changedEventStub = sinon.stub();
        requestConditions.addEventListener("request-conditions-changed" /* SDK.NetworkManager.RequestConditions.Events.REQUEST_CONDITIONS_CHANGED */, changedEventStub);
        // Can't move the first condition up and the last condition down
        requestConditions.increasePriority(condition1);
        requestConditions.decreasePriority(condition3);
        sinon.assert.notCalled(changedEventStub);
        requestConditions.increasePriority(condition2);
        sinon.assert.calledOnce(changedEventStub);
        assert.deepEqual(requestConditions.conditions.toArray(), [condition2, condition1, condition3]);
        requestConditions.decreasePriority(condition1);
        sinon.assert.calledTwice(changedEventStub);
        assert.deepEqual(requestConditions.conditions.toArray(), [condition2, condition3, condition1]);
    });
});
describe('RequestURLPattern', () => {
    it('successfully upgrades url block patterns from wildcards', () => {
        const testPattern = (pattern, expectation) => {
            const urlPattern = SDK.NetworkManager.RequestURLPattern.upgradeFromWildcard(pattern);
            const keys = [
                'protocol',
                'username',
                'password',
                'hostname',
                'port',
                'pathname',
                'search',
                'hash',
            ];
            const relevantProperties = urlPattern?.pattern &&
                Object.assign({}, ...keys.map(key => key in urlPattern.pattern && urlPattern.pattern[key] !== '*' ?
                    { [key]: urlPattern.pattern[key] } :
                    {}));
            assert.deepEqual(relevantProperties, expectation, pattern);
        };
        testPattern('http://example.com/foo/bar', { port: '', protocol: 'http', hostname: 'example.com', pathname: '/foo/bar' });
        testPattern('http://example.com', { port: '', protocol: 'http', hostname: 'example.com' });
        testPattern('http://example.com/', { port: '', protocol: 'http', hostname: 'example.com', pathname: '/' });
        testPattern('*://example.com', { port: '', hostname: 'example.com' });
        testPattern('example.com', { port: '', hostname: 'example.com*' });
        testPattern('example.com/foo/bar', { port: '', hostname: 'example.com', pathname: '/foo/bar*' });
        testPattern('http://*.com/foo', { port: '', protocol: 'http', hostname: '*.com', pathname: '/foo' });
        testPattern('http://localhost:*/', { protocol: 'http', hostname: 'localhost', pathname: '/' });
        testPattern('http://localhost:1234', { port: '1234', protocol: 'http', hostname: 'localhost' });
        testPattern('localhost:1234', { port: '1234', hostname: 'localhost' });
        testPattern('http://example.com*', { port: '', protocol: 'http', hostname: 'example.com*' });
        testPattern('e*m', { port: '', hostname: 'e*m*' });
        testPattern('ht tp://*', undefined);
        testPattern('http://*/(:id)', undefined);
    });
    it('correctly reports pattern constructor string validity', () => {
        assert.strictEqual(SDK.NetworkManager.RequestURLPattern.isValidPattern('ht tp://*'), "failed-to-parse" /* SDK.NetworkManager.RequestURLPatternValidity.FAILED_TO_PARSE */);
        assert.strictEqual(SDK.NetworkManager.RequestURLPattern.isValidPattern('http://*/(:id)'), "has-regexp-groups" /* SDK.NetworkManager.RequestURLPatternValidity.HAS_REGEXP_GROUPS */);
        assert.strictEqual(SDK.NetworkManager.RequestURLPattern.isValidPattern('http://*/*'), "valid" /* SDK.NetworkManager.RequestURLPatternValidity.VALID */);
    });
});
describe('RequestConditions', () => {
    setupLocaleHooks();
    setupSettingsHooks();
    setupRuntimeHooks();
    function getSetting(values) {
        Common.Settings.Settings.instance().clearAll();
        Common.Settings.Settings.instance().getRegistry().clear();
        return Common.Settings.Settings.instance().createSetting('network-blocked-patterns', values);
    }
    it('loads settings with url pattern', () => {
        getSetting([
            {
                enabled: true,
                urlPattern: '*://example.com',
                conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */,
            },
        ]);
        const conditions = new SDK.NetworkManager.RequestConditions(Common.Settings.Settings.instance());
        const condition = conditions.conditions.next().value;
        assert.exists(condition);
        assert.isUndefined(condition.wildcardURL);
        assert.strictEqual(condition.constructorString, '*://example.com');
        assert.exists(condition.originalOrUpgradedURLPattern);
    });
    it('loads settings with url', () => {
        getSetting([{ enabled: true, url: 'foo' }]);
        const conditions = new SDK.NetworkManager.RequestConditions(Common.Settings.Settings.instance());
        const condition = conditions.conditions.next().value;
        assert.exists(condition);
        assert.strictEqual(condition.wildcardURL, 'foo');
        assert.strictEqual(condition.constructorString, '*://foo*');
    });
    it('stores settings correctly', () => {
        const setting = getSetting([]);
        const conditions = new SDK.NetworkManager.RequestConditions(Common.Settings.Settings.instance());
        const patternCondition = SDK.NetworkManager.RequestCondition.createFromSetting({
            enabled: true,
            urlPattern: '*://example.com',
            conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */,
        });
        assert.strictEqual(patternCondition.conditions, SDK.NetworkManager.NoThrottlingConditions);
        conditions.add(patternCondition);
        const wildcardCondition = SDK.NetworkManager.RequestCondition.createFromSetting({
            enabled: true,
            url: 'foo',
        });
        conditions.add(wildcardCondition);
        assert.strictEqual(wildcardCondition.conditions, SDK.NetworkManager.BlockingConditions);
        assert.deepEqual(setting.get()[0], {
            enabled: true,
            urlPattern: '*://example.com',
            conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */
        });
        assert.deepEqual(setting.get()[1], { enabled: true, url: 'foo' });
    });
    it('upgrades url to url pattern', () => {
        const setting = getSetting([]);
        const conditions = new SDK.NetworkManager.RequestConditions(Common.Settings.Settings.instance());
        const condition = SDK.NetworkManager.RequestCondition.createFromSetting({
            enabled: true,
            url: 'foo',
        });
        conditions.add(condition);
        condition.conditions = SDK.NetworkManager.NoThrottlingConditions;
        assert.deepEqual(setting.get()[0], {
            enabled: true,
            urlPattern: '*://foo*',
            conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */,
        });
    });
    describe('applyConditions', () => {
        function stubAgent() {
            const universe = new TestUniverse();
            const target = universe.createTarget({ url: urlString `http://example.com` });
            const agent = target.networkAgent();
            const setBlockedURLs = sinon.stub(agent, 'invoke_setBlockedURLs');
            const emulateNetworkConditions = sinon.stub(agent, 'invoke_emulateNetworkConditions');
            const emulateNetworkConditionsByRule = sinon.stub(agent, 'invoke_emulateNetworkConditionsByRule');
            emulateNetworkConditionsByRule.resolves({ ruleIds: [], getError: () => undefined });
            return { universe, agent, setBlockedURLs, emulateNetworkConditions, emulateNetworkConditionsByRule };
        }
        it('applies blocking, global, and local throttling if individual request throttling is enabled', () => {
            const { agent, setBlockedURLs, emulateNetworkConditions, emulateNetworkConditionsByRule } = stubAgent();
            const conditions = new SDK.NetworkManager.RequestConditions(Common.Settings.Settings.instance());
            conditions.conditionsEnabled = true;
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'foo', enabled: true }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'bar', enabled: false }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://nothrottle:*',
                enabled: true,
                conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */
            }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://block:*',
                enabled: true,
                conditions: "BLOCKING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.BLOCKING */
            }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://throttle:*',
                enabled: true,
                conditions: "SPEED_3G" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.SPEED_3G */
            }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://disabled_nothrottle:*',
                enabled: false,
                conditions: "NO_THROTTLING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.NO_THROTTLING */
            }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://disabled_block:*',
                enabled: false,
                conditions: "BLOCKING" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.BLOCKING */
            }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://disabled_throttle:*',
                enabled: false,
                conditions: "SPEED_3G" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.SPEED_3G */
            }));
            conditions.applyConditions(false, null, agent);
            sinon.assert.notCalled(emulateNetworkConditions);
            sinon.assert.calledOnce(emulateNetworkConditionsByRule);
            assert.deepEqual(emulateNetworkConditionsByRule.args[0][0], {
                offline: false,
                matchedNetworkConditions: [{
                        urlPattern: '*://throttle:*',
                        latency: 2000,
                        downloadThroughput: 50000,
                        uploadThroughput: 50000,
                        packetLoss: undefined,
                        packetQueueLength: undefined,
                        packetReordering: undefined,
                        connectionType: "cellular3g" /* Protocol.Network.ConnectionType.Cellular3g */,
                    }]
            });
            sinon.assert.calledOnceWithExactly(setBlockedURLs, {
                urlPatterns: [
                    { urlPattern: '*://foo*', block: true },
                    { urlPattern: '*://block:*', block: true },
                    { urlPattern: '*://throttle:*', block: false },
                ]
            });
            setBlockedURLs.resetHistory();
            emulateNetworkConditions.resetHistory();
            emulateNetworkConditionsByRule.resetHistory();
            conditions.applyConditions(true, SDK.NetworkManager.Slow4GConditions, agent);
            sinon.assert.notCalled(emulateNetworkConditions);
            sinon.assert.calledOnce(emulateNetworkConditionsByRule);
            assert.deepEqual(emulateNetworkConditionsByRule.args[0][0], {
                offline: true,
                matchedNetworkConditions: [
                    {
                        urlPattern: '*://throttle:*',
                        latency: 2000,
                        downloadThroughput: 50000,
                        uploadThroughput: 50000,
                        packetLoss: undefined,
                        packetQueueLength: undefined,
                        packetReordering: undefined,
                        connectionType: "cellular3g" /* Protocol.Network.ConnectionType.Cellular3g */,
                    },
                    {
                        urlPattern: '',
                        latency: 562.5,
                        downloadThroughput: 180000,
                        uploadThroughput: 84375,
                        packetLoss: undefined,
                        packetQueueLength: undefined,
                        packetReordering: undefined,
                        connectionType: "cellular4g" /* Protocol.Network.ConnectionType.Cellular4g */,
                    }
                ]
            });
            sinon.assert.calledOnceWithExactly(setBlockedURLs, {
                urlPatterns: [
                    { urlPattern: '*://foo*', block: true },
                    { urlPattern: '*://block:*', block: true },
                    { urlPattern: '*://throttle:*', block: false },
                ]
            });
        });
        it('disables throttling and blocking when the effect gets disabled globally', () => {
            const { universe, setBlockedURLs, emulateNetworkConditions, emulateNetworkConditionsByRule } = stubAgent();
            const conditions = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager })
                .requestConditions;
            conditions.conditionsEnabled = true;
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({ url: 'foo', enabled: true }));
            conditions.add(SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://throttle:*',
                enabled: true,
                conditions: "SPEED_3G" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.SPEED_3G */
            }));
            emulateNetworkConditions.resetHistory();
            emulateNetworkConditionsByRule.resetHistory();
            setBlockedURLs.resetHistory();
            conditions.conditionsEnabled = false;
            sinon.assert.notCalled(emulateNetworkConditions);
            sinon.assert.calledOnceWithExactly(emulateNetworkConditionsByRule, { offline: false, matchedNetworkConditions: [] });
            sinon.assert.calledOnceWithExactly(setBlockedURLs, { urlPatterns: [] });
            emulateNetworkConditions.resetHistory();
            emulateNetworkConditionsByRule.resetHistory();
            setBlockedURLs.resetHistory();
            conditions.conditionsEnabled = true;
            sinon.assert.notCalled(emulateNetworkConditions);
            sinon.assert.calledOnceWithExactly(emulateNetworkConditionsByRule, {
                offline: false,
                matchedNetworkConditions: [{
                        urlPattern: '*://throttle:*',
                        latency: 2000,
                        downloadThroughput: 50000,
                        uploadThroughput: 50000,
                        packetLoss: undefined,
                        packetQueueLength: undefined,
                        packetReordering: undefined,
                        connectionType: "cellular3g" /* Protocol.Network.ConnectionType.Cellular3g */,
                    }]
            });
            sinon.assert.calledOnceWithExactly(setBlockedURLs, {
                urlPatterns: [
                    { urlPattern: '*://foo*', block: true },
                    { urlPattern: '*://throttle:*', block: false },
                ]
            });
        });
        it('correctly maps ruleIds to conditions', async () => {
            const { universe, agent, emulateNetworkConditionsByRule } = stubAgent();
            const multitargetNetworkManager = SDK.NetworkManager.MultitargetNetworkManager.instance({ forceNew: true, targetManager: universe.targetManager });
            const requestConditions = multitargetNetworkManager.requestConditions;
            const ruleId = 'mock-rule-id-1';
            emulateNetworkConditionsByRule.resolves({ ruleIds: [ruleId], getError: () => undefined });
            const throttlingCondition = SDK.NetworkManager.RequestCondition.createFromSetting({
                urlPattern: '*://example.com:*',
                enabled: true,
                conditions: "SPEED_3G" /* SDK.NetworkManager.PredefinedThrottlingConditionKey.SPEED_3G */,
            });
            requestConditions.add(throttlingCondition);
            requestConditions.conditionsEnabled = true;
            await requestConditions.applyConditions(false, null, agent);
            const conditions = requestConditions.conditionsForId(ruleId);
            assert.strictEqual(conditions?.conditions, SDK.NetworkManager.Slow3GConditions);
            assert.strictEqual(conditions?.urlPattern, '*://example.com:*');
            const request = SDK.NetworkRequest.NetworkRequest.create('mockRequestId', urlString `http://example.com`, urlString `http://example.com`, null, null, null);
            request.addExtraRequestInfo({
                blockedRequestCookies: [],
                includedRequestCookies: [],
                requestHeaders: [],
                connectTiming: { requestTime: 0 },
                appliedNetworkConditionsId: ruleId,
            });
            const appliedConditions = multitargetNetworkManager.appliedRequestConditions(request);
            assert.strictEqual(appliedConditions?.conditions, SDK.NetworkManager.Slow3GConditions);
            assert.strictEqual(appliedConditions?.urlPattern, '*://example.com:*');
        });
    });
});
describe('NetworkDispatcher', () => {
    const requestWillBeSentEvent = { requestId: 'mockId', request: { url: 'example.com' } };
    const responseReceivedEvent = {
        requestId: 'mockId',
        loaderId: 'mockLoaderId',
        frameId: 'mockFrameId',
        timestamp: 581734.083213,
        type: "Document" /* Protocol.Network.ResourceType.Document */,
        response: {
            url: 'example.com',
            status: 200,
            statusText: '',
            mimeType: 'text/html',
            connectionReused: true,
            connectionId: 12345,
            encodedDataLength: 100,
            securityState: 'secure',
        },
        hasExtraInfo: true,
    };
    const loadingFinishedEvent = { requestId: 'mockId', timestamp: 42, encodedDataLength: 42 };
    describe('request', () => {
        let networkDispatcher;
        beforeEach(() => {
            const networkManager = new Common.ObjectWrapper.ObjectWrapper();
            networkManager.target = () => ({
                model: () => null,
                targetManager: () => ({
                    settings: Common.Settings.Settings.instance(),
                }),
            });
            networkDispatcher = new SDK.NetworkManager.NetworkDispatcher(networkManager);
        });
        it('is preserved after loadingFinished', () => {
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.loadingFinished(loadingFinishedEvent);
            assert.exists(networkDispatcher.requestForId('mockId'));
        });
        it('clears finished requests on clearRequests()', () => {
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.loadingFinished(loadingFinishedEvent);
            const unfinishedRequestWillBeSentEvent = { requestId: 'unfinishedRequestId', request: { url: 'example.com' } };
            networkDispatcher.requestWillBeSent(unfinishedRequestWillBeSentEvent);
            networkDispatcher.clearRequests();
            assert.notExists(networkDispatcher.requestForId('mockId'));
            assert.exists(networkDispatcher.requestForId('unfinishedRequestId'));
        });
        it('preserves extra info for unfinished clearRequests()', () => {
            const requestWillBeSentExtraInfoEvent = {
                requestId: 'mockId',
                associatedCookies: [],
                headers: { 'Header-From-Extra-Info': 'foo' },
                connectTiming: { requestTime: 0 },
            };
            networkDispatcher.requestWillBeSentExtraInfo(requestWillBeSentExtraInfoEvent);
            networkDispatcher.clearRequests();
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.responseReceived(responseReceivedEvent);
            assert.exists(networkDispatcher.requestForId('mockId'));
            assert.deepEqual(networkDispatcher.requestForId('mockId')?.requestHeaders(), [{ name: 'Header-From-Extra-Info', value: 'foo' }]);
        });
        it('handles redirect chains with mixed presence of raw headers', () => {
            const hop1RequestWillBeSent = { requestId: 'mockId', request: { url: 'http://example.com' } };
            const hop2RequestWillBeSent = {
                requestId: 'mockId',
                request: { url: 'https://example.com' },
                redirectHasExtraInfo: false,
                redirectResponse: {
                    url: 'http://example.com',
                    status: 307,
                    statusText: 'Temporary redirect',
                    headers: {
                        Location: 'https://example.com',
                    },
                },
            };
            const responseExtraInfo = {
                requestId: 'mockId',
                blockedCookies: [],
                headers: {},
                resourceIPAddressSpace: "Public" /* Protocol.Network.IPAddressSpace.Public */,
                statusCode: 200,
                headersText: 'HTTP/1.1 200 OK\r\n'
            };
            networkDispatcher.requestWillBeSent(hop1RequestWillBeSent);
            networkDispatcher.requestWillBeSent(hop2RequestWillBeSent);
            networkDispatcher.responseReceived(responseReceivedEvent);
            networkDispatcher.responseReceivedExtraInfo(responseExtraInfo);
            const originalResqest = networkDispatcher.requestForURL(urlString `http://example.com`);
            assert.exists(originalResqest);
            assert.strictEqual(originalResqest.statusCode, 307);
            assert.strictEqual(originalResqest.statusText, 'Temporary redirect');
            const redirectedRequest = networkDispatcher.requestForURL(urlString `https://example.com`);
            assert.exists(redirectedRequest);
            assert.strictEqual(redirectedRequest.statusCode, 200);
            assert.strictEqual(redirectedRequest.statusText, 'OK');
        });
        it('raw headers are processed when response fails', () => {
            networkDispatcher.requestWillBeSent({ requestId: 'mockId', request: { url: 'http://example.com' } });
            const responseExtraInfo = {
                requestId: 'mockId',
                blockedCookies: [],
                headers: {},
                resourceIPAddressSpace: "Public" /* Protocol.Network.IPAddressSpace.Public */,
                statusCode: 200,
                headersText: 'HTTP/1.1 200 OK\r\n'
            };
            networkDispatcher.responseReceivedExtraInfo(responseExtraInfo);
            const request = networkDispatcher.requestForId('mockId');
            assert.exists(request);
            assert.strictEqual(request.statusCode, 0);
            networkDispatcher.loadingFailed({
                requestId: 'mockId',
                timestamp: 2345,
                type: "Document" /* Protocol.Network.ResourceType.Document */,
                errorText: 'net::ERR_FAILED',
                canceled: false,
                corsErrorStatus: { corsError: "MissingAllowOriginHeader" /* Protocol.Network.CorsError.MissingAllowOriginHeader */, failedParameter: '' },
            });
            assert.strictEqual(request.statusCode, 200);
            assert.strictEqual(request.statusText, 'OK');
        });
        it('response headers are overwritten by request interception', () => {
            const responseReceivedExtraInfoEvent = {
                requestId: 'mockId',
                blockedCookies: [],
                headers: {
                    'test-header': 'first',
                },
                resourceIPAddressSpace: "Public" /* Protocol.Network.IPAddressSpace.Public */,
                statusCode: 200,
            };
            const mockResponseReceivedEventWithHeaders = (headers) => {
                const event = structuredClone(responseReceivedEvent);
                event.response.headers = headers;
                return event;
            };
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.responseReceivedExtraInfo(responseReceivedExtraInfoEvent);
            // ResponseReceived does not overwrite response headers.
            networkDispatcher.responseReceived(mockResponseReceivedEventWithHeaders({ 'test-header': 'second' }));
            assert.deepEqual(networkDispatcher.requestForId('mockId')?.responseHeaders, [{ name: 'test-header', value: 'first' }]);
            // ResponseReceived does overwrite response headers if request is marked as intercepted.
            SDK.NetworkManager.MultitargetNetworkManager.instance().dispatchEventToListeners("RequestIntercepted" /* SDK.NetworkManager.MultitargetNetworkManager.Events.REQUEST_INTERCEPTED */, 'mockId');
            networkDispatcher.responseReceived(mockResponseReceivedEventWithHeaders({ 'test-header': 'third' }));
            assert.deepEqual(networkDispatcher.requestForId('mockId')?.responseHeaders, [{ name: 'test-header', value: 'third' }]);
        });
        it('has populated \'originalHeaders\' after receiving \'responseReceivedExtraInfo\'', () => {
            const responseReceivedExtraInfoEvent = {
                requestId: 'mockId',
                blockedCookies: [],
                headers: {
                    'test-header': 'first',
                    'set-cookie': 'foo=bar\ncolor=green',
                },
                resourceIPAddressSpace: "Public" /* Protocol.Network.IPAddressSpace.Public */,
                statusCode: 200,
            };
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.responseReceivedExtraInfo(responseReceivedExtraInfoEvent);
            networkDispatcher.responseReceived(responseReceivedEvent);
            assert.deepEqual(networkDispatcher.requestForId('mockId')?.responseHeaders, [
                { name: 'test-header', value: 'first' },
                { name: 'set-cookie', value: 'foo=bar' },
                { name: 'set-cookie', value: 'color=green' },
            ]);
        });
        it('Correctly set early hints properties on receivedResponse event', () => {
            const responseReceivedEvent = {
                requestId: 'mockId',
                loaderId: 'mockLoaderId',
                frameId: 'mockFrameId',
                timestamp: 581734.083213,
                type: "Document" /* Protocol.Network.ResourceType.Document */,
                response: {
                    url: 'example.com',
                    status: 200,
                    statusText: '',
                    headers: {
                        'test-header': 'first',
                    },
                    mimeType: 'text/html',
                    connectionReused: true,
                    connectionId: 12345,
                    encodedDataLength: 100,
                    securityState: 'secure',
                    fromEarlyHints: true,
                },
            };
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.responseReceived(responseReceivedEvent);
            assert.isTrue(networkDispatcher.requestForId('mockId')?.fromEarlyHints());
        });
        it('has populated early hints headers after receiving \'repsonseReceivedEarlyHints\'', () => {
            const earlyHintsEvent = {
                requestId: 'mockId',
                headers: {
                    link: '</style.css>; as=style;',
                },
            };
            networkDispatcher.requestWillBeSent(requestWillBeSentEvent);
            networkDispatcher.loadingFinished(loadingFinishedEvent);
            networkDispatcher.responseReceivedEarlyHints(earlyHintsEvent);
            assert.deepEqual(networkDispatcher.requestForId('mockId')?.earlyHintsHeaders, [
                { name: 'link', value: '</style.css>; as=style;' },
            ]);
        });
    });
});
describe('InterceptedRequest', () => {
    setupLocaleHooks();
    setupSettingsHooks();
    setupRuntimeHooks();
    let target;
    let fulfillRequestSpy;
    async function checkRequestOverride(target, request, requestId, responseStatusCode, responseHeaders, responseBody, expectedOverriddenResponse, expectedSetCookieHeaders = []) {
        const multitargetNetworkManager = SDK.NetworkManager.MultitargetNetworkManager.instance();
        const fetchAgent = target.fetchAgent();
        const fulfilledRequest = new Promise(resolve => {
            multitargetNetworkManager.addEventListener("RequestFulfilled" /* SDK.NetworkManager.MultitargetNetworkManager.Events.REQUEST_FULFILLED */, resolve);
        });
        const networkRequest = SDK.NetworkRequest.NetworkRequest.create(requestId, urlString `${request.url}`, urlString `${request.url}`, null, null, null);
        networkRequest.originalResponseHeaders = responseHeaders;
        // The response headers passed to 'interceptedRequest' do not contain any
        // 'set-cookie' headers, because they originate from CDP's 'Fetch.requestPaused'
        // which receives its header information via mojo which in turn filters out
        // 'set-cookie' headers.
        const filteredResponseHeaders = responseHeaders.filter(header => header.name !== 'set-cookie');
        const interceptedRequest = new SDK.NetworkManager.InterceptedRequest(fetchAgent, request, "Document" /* Protocol.Network.ResourceType.Document */, requestId, networkRequest, responseStatusCode, filteredResponseHeaders);
        interceptedRequest.responseBody = async () => {
            return new TextUtils.ContentData.ContentData(responseBody, false, 'text/html');
        };
        sinon.assert.notCalled(fulfillRequestSpy);
        await multitargetNetworkManager.requestIntercepted(interceptedRequest);
        await fulfilledRequest;
        sinon.assert.calledOnceWithExactly(fulfillRequestSpy, expectedOverriddenResponse);
        assert.deepEqual(networkRequest.setCookieHeaders, expectedSetCookieHeaders);
        fulfillRequestSpy.resetHistory();
    }
    async function checkSetCookieOverride(url, headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders) {
        const responseCode = 200;
        const requestId = 'request_id_for_cookies';
        const responseBody = 'interceptedRequest content';
        const networkRequest = {
            method: 'GET',
            url,
        };
        await checkRequestOverride(target, networkRequest, requestId, responseCode, headersFromServer, responseBody, {
            requestId,
            responseCode,
            body: btoa(responseBody),
            responseHeaders: expectedOverriddenHeaders,
        }, expectedPersistedSetCookieHeaders);
    }
    let universe;
    let networkPersistenceManager;
    beforeEach(async () => {
        universe = new TestUniverse();
        const { targetManager, workspace, settings } = universe;
        sinon.stub(Workspace.Workspace.WorkspaceImpl, 'instance').returns(workspace);
        sinon.stub(SDK.TargetManager.TargetManager, 'instance').returns(targetManager);
        sinon.stub(Common.Settings.Settings, 'instance').returns(settings);
        sinon.stub(SDK.NetworkManager.MultitargetNetworkManager, 'instance').returns(universe.multitargetNetworkManager);
        settings.moduleSetting('persistence-network-overrides-enabled').set(true);
        target = universe.createTarget({});
        networkPersistenceManager = await createWorkspaceProject(urlString `file:///path/to/overrides`, [
            {
                name: '.headers',
                path: 'www.example.com/',
                content: `[
            {
              "applyTo": "index.html",
              "headers": [{
                "name": "index-only",
                "value": "only added to index.html"
              }]
            },
            {
              "applyTo": "*.css",
              "headers": [{
                "name": "css-only",
                "value": "only added to css files"
              }]
            },
            {
              "applyTo": "path/to/*.js",
              "headers": [{
                "name": "another-header",
                "value": "only added to specific path"
              }]
            },
            {
              "applyTo": "withCookie.html",
              "headers": [{
                "name": "set-cookie",
                "value": "userId=12345"
              }]
            },
            {
              "applyTo": "withCookie2.html",
              "headers": [
                {
                  "name": "set-cookie",
                  "value": "userName=DevTools"
                },
                {
                  "name": "set-cookie",
                  "value": "themeColour=dark"
                }
              ]
            },
            {
              "applyTo": "withCookie3.html",
              "headers": [
                {
                  "name": "set-cookie",
                  "value": "userName=DevTools"
                },
                {
                  "name": "set-cookie",
                  "value": "malformed_override"
                }
              ]
            },
            {
              "applyTo": "cookies/*",
              "headers": [
                {
                  "name": "set-cookie",
                  "value": "unique=value"
                },
                {
                  "name": "set-cookie",
                  "value": "override-me=first"
                }
              ]
            },
            {
              "applyTo": "cookies/mergeCookies.html",
              "headers": [
                {
                  "name": "set-cookie",
                  "value": "override-me=second"
                },
                {
                  "name": "set-cookie",
                  "value": "foo=bar"
                }
              ]
            }
          ]`,
            },
            {
                name: '.headers',
                path: '',
                content: `[
            {
              "applyTo": "*",
              "headers": [{
                "name": "age",
                "value": "overridden"
              }]
            }
          ]`,
            },
            { name: 'helloWorld.html', path: 'www.example.com/', content: 'Hello World!' },
            { name: 'utf16.html', path: 'www.example.com/', content: 'Overwritten with non-UTF16 (TODO: fix this!)' },
            { name: 'something.html', path: 'file:/usr/local/foo/content/', content: 'Override for something' },
            {
                name: '.headers',
                path: 'file:/usr/local/example/',
                content: `[
            {
              "applyTo": "*",
              "headers": [{
                "name": "test-file-urls",
                "value": "file url value"
              }]
            }
          ]`,
            },
            { name: 'index.html', path: 'file:/usr/local/example/', content: 'Overridden file content' },
            {
                name: '.headers',
                path: 'www.longurl.com/longurls/',
                content: `[
            {
              "applyTo": "index.html-${Platform.StringUtilities.hashCode('www.longurl.com/' + LONG_URL_PART).toString(16)}.html",
              "headers": [{
                "name": "long-url-header",
                "value": "long url header value"
              }]
            }
          ]`,
            },
            {
                name: `index.html-${Platform.StringUtilities.hashCode('www.longurl.com/' + LONG_URL_PART).toString(16)}.html`,
                path: 'www.longurl.com/longurls/',
                content: 'Overridden long URL file content',
            },
            {
                name: '.headers',
                path: 'file:/longurls/',
                content: `[
            {
              "applyTo": "index.html-${Platform.StringUtilities
                    .hashCode(Persistence.NetworkPersistenceManager.NetworkPersistenceManager.encodeEncodedPathToLocalPathParts('file:')[0] +
                    '/' + LONG_URL_PART)
                    .toString(16)}.html",
              "headers": [{
                "name": "long-file-url-header",
                "value": "long file url header value"
              }]
            }
          ]`,
            },
        ]);
        sinon.stub(target.fetchAgent(), 'invoke_enable');
        fulfillRequestSpy = sinon.spy(target.fetchAgent(), 'invoke_fulfillRequest');
        await networkPersistenceManager.updateInterceptionPatternsForTests();
    });
    it('can override headers-only for a status 200 request', async () => {
        const responseCode = 200;
        const requestId = 'request_id_1';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/styles.css',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode,
            body: btoa(responseBody),
            responseHeaders: [
                { name: 'css-only', value: 'only added to css files' },
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('does not intercept OPTIONS requests', async () => {
        const requestId = 'request_id_1';
        const request = {
            method: 'OPTIONS',
            url: 'https://www.example.com/styles.css',
        };
        const fetchAgent = target.fetchAgent();
        const continueRequestSpy = sinon.spy(fetchAgent, 'invoke_continueRequest');
        const networkRequest = SDK.NetworkRequest.NetworkRequest.create(requestId, urlString `${request.url}`, urlString `${request.url}`, null, null, null);
        const interceptedRequest = new SDK.NetworkManager.InterceptedRequest(fetchAgent, request, "Document" /* Protocol.Network.ResourceType.Document */, requestId, networkRequest);
        interceptedRequest.responseBody = async () => {
            return new TextUtils.ContentData.ContentData('interceptedRequest content', false, 'text/html');
        };
        sinon.assert.notCalled(continueRequestSpy);
        await SDK.NetworkManager.MultitargetNetworkManager.instance().requestIntercepted(interceptedRequest);
        sinon.assert.notCalled(fulfillRequestSpy);
        sinon.assert.calledOnce(continueRequestSpy);
    });
    it('can override headers and content for a status 200 request', async () => {
        const responseCode = 200;
        const requestId = 'request_id_2';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/helloWorld.html',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode,
            body: btoa('Hello World!'),
            responseHeaders: [
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    describe('NetworkPersistenceManager', () => {
        it('decodes the intercepted response body with the right charset', async () => {
            const requestId = 'request_id_utf_16';
            const request = {
                method: 'GET',
                url: 'https://www.example.com/utf16.html',
            };
            const fetchAgent = target.fetchAgent();
            sinon.spy(fetchAgent, 'invoke_continueRequest');
            const networkRequest = SDK.NetworkRequest.NetworkRequest.create(requestId, urlString `${request.url}`, urlString `${request.url}`, null, null, null);
            networkRequest.originalResponseHeaders = [{ name: 'content-type', value: 'text/html; charset-utf-16' }];
            // Create a quick'n dirty network UISourceCode for the request manually. We need to establish a binding to the
            // overridden file system UISourceCode.
            const networkProject = new Bindings.ContentProviderBasedProject.ContentProviderBasedProject(universe.workspace, 'testing-network', Workspace.Workspace.projectTypes.Network, 'Override network project', false);
            const uiSourceCode = networkProject.createUISourceCode(urlString `https://www.example.com/utf16.html`, Common.ResourceType.resourceTypes.Document);
            networkProject.addUISourceCode(uiSourceCode);
            const interceptedRequest = new SDK.NetworkManager.InterceptedRequest(fetchAgent, request, "Document" /* Protocol.Network.ResourceType.Document */, requestId, networkRequest, 200, [{ name: 'content-type', value: 'text/html; charset-utf-16' }]);
            interceptedRequest.responseBody = async () => {
                // Very simple HTML doc base64 encoded.
                return new TextUtils.ContentData.ContentData('//48ACEARABPAEMAVABZAFAARQAgAGgAdABtAGwAPgAKADwAcAA+AEkA8QB0AOsAcgBuAOIAdABpAPQAbgDgAGwAaQB6AOYAdABpAPgAbgADJjTYBt88AC8AcAA+AAoA', true, 'text/html', 'utf-16');
            };
            await universe.multitargetNetworkManager.requestIntercepted(interceptedRequest);
            const content = await networkPersistenceManager.originalContentForUISourceCode(uiSourceCode);
            assert.strictEqual(content, '<!DOCTYPE html>\n<p>Iñtërnâtiônàlizætiøn☃𝌆</p>\n');
        });
    });
    it('can override headers-only for a status 300 (redirect) request', async () => {
        const responseCode = 300;
        const requestId = 'request_id_3';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/path/to/foo.js',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode,
            body: '',
            responseHeaders: [
                { name: 'another-header', value: 'only added to specific path' },
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers and content for a status 300 (redirect) request', async () => {
        const responseCode = 300;
        const requestId = 'request_id_4';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/helloWorld.html',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode: 200,
            body: btoa('Hello World!'),
            responseHeaders: [
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers-only for a status 404 (not found) request', async () => {
        const responseCode = 404;
        const requestId = 'request_id_5';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/doesNotExist.html',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode,
            body: btoa(responseBody),
            responseHeaders: [
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers and content for a status 404 (not found) request', async () => {
        const responseCode = 404;
        const requestId = 'request_id_6';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'https://www.example.com/helloWorld.html',
        }, requestId, responseCode, [{ name: 'content-type', value: 'text/html; charset=utf-8' }], responseBody, {
            requestId,
            responseCode: 200,
            body: btoa('Hello World!'),
            responseHeaders: [
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers and content for a request with a \'file:/\'-URL', async () => {
        const responseCode = 200;
        const requestId = 'request_id_8';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'file:///usr/local/example/index.html',
        }, requestId, responseCode, [
            { name: 'content-type', value: 'text/html; charset=utf-8' },
            { name: 'age', value: 'original' },
        ], responseBody, {
            requestId,
            responseCode,
            body: btoa('Overridden file content'),
            responseHeaders: [
                { name: 'test-file-urls', value: 'file url value' },
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can apply global header overrides to a request with a \'file:/\'-URL', async () => {
        const responseCode = 200;
        const requestId = 'request_id_9';
        const responseBody = 'content of something/index.html';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'file:///usr/local/whatever/index.html',
        }, requestId, responseCode, [
            { name: 'content-type', value: 'text/html; charset=utf-8' },
            { name: 'age', value: 'original' },
        ], responseBody, {
            requestId,
            responseCode,
            body: btoa(responseBody),
            responseHeaders: [
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers and content for a request with a very long URL', async () => {
        const responseCode = 200;
        const requestId = 'request_id_10';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: `https://www.longurl.com/${LONG_URL_PART}`,
        }, requestId, responseCode, [
            { name: 'content-type', value: 'text/html; charset=utf-8' },
            { name: 'age', value: 'original' },
        ], responseBody, {
            requestId,
            responseCode,
            body: btoa('Overridden long URL file content'),
            responseHeaders: [
                { name: 'long-url-header', value: 'long url header value' },
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override headers for a request with a very long \'file:/\'-URL', async () => {
        const responseCode = 200;
        const requestId = 'request_id_11';
        const responseBody = 'interceptedRequest content';
        await checkRequestOverride(target, {
            method: 'GET',
            url: 'file:///' + LONG_URL_PART,
        }, requestId, responseCode, [
            { name: 'content-type', value: 'text/html; charset=utf-8' },
            { name: 'age', value: 'original' },
        ], responseBody, {
            requestId,
            responseCode,
            body: btoa(responseBody),
            responseHeaders: [
                { name: 'long-file-url-header', value: 'long file url header value' },
                { name: 'age', value: 'overridden' },
                { name: 'content-type', value: 'text/html; charset=utf-8' },
            ],
        });
    });
    it('can override \'set-cookie\' headers', async () => {
        const headersFromServer = [{ name: 'content-type', value: 'text/html; charset=utf-8' }];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'content-type', value: 'text/html; charset=utf-8' },
            { name: 'set-cookie', value: 'userId=12345' },
        ];
        const expectedPersistedSetCookieHeaders = [{ name: 'set-cookie', value: 'userId=12345' }];
        await checkSetCookieOverride('https://www.example.com/withCookie.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('marks both requests as overridden when there are 2 requests with the same URL', async () => {
        const responseCode = 200;
        const requestId1 = 'request_id_1';
        const requestId2 = 'request_id_2';
        const body = 'interceptedRequest content';
        const request = {
            method: 'GET',
            url: 'https://www.example.com/styles.css',
        };
        const originalResponseHeaders = [{ name: 'content-type', value: 'text/html; charset=utf-8' }];
        const responseHeaders = [
            { name: 'css-only', value: 'only added to css files' },
            { name: 'age', value: 'overridden' },
            { name: 'content-type', value: 'text/html; charset=utf-8' },
        ];
        const { dispatcher } = target.model(SDK.NetworkManager.NetworkManager);
        dispatcher.requestWillBeSent({ requestId: requestId1, request });
        dispatcher.requestWillBeSent({ requestId: requestId2, request });
        await checkRequestOverride(target, request, requestId1, responseCode, originalResponseHeaders, body, {
            requestId: requestId1,
            responseCode,
            body: btoa(body),
            responseHeaders,
        });
        await checkRequestOverride(target, request, requestId2, responseCode, originalResponseHeaders, body, {
            requestId: requestId2,
            responseCode,
            body: btoa(body),
            responseHeaders,
        });
        assert.isTrue(dispatcher.requestForId(requestId1)?.wasIntercepted());
        assert.isTrue(dispatcher.requestForId(requestId2)?.wasIntercepted());
    });
    it('stores \'set-cookie\' headers on the request', async () => {
        const headersFromServer = [{ name: 'set-cookie', value: 'foo=bar' }];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
        ];
        const expectedPersistedSetCookieHeaders = [{ name: 'set-cookie', value: 'foo=bar' }];
        await checkSetCookieOverride('https://www.example.com/noCookie.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('can override \'set-cookie\' headers when there server also sends \'set-cookie\' headers', async () => {
        const headersFromServer = [{ name: 'set-cookie', value: 'foo=bar' }];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'set-cookie', value: 'userId=12345' },
        ];
        const expectedPersistedSetCookieHeaders = [{ name: 'set-cookie', value: 'foo=bar' }, { name: 'set-cookie', value: 'userId=12345' }];
        await checkSetCookieOverride('https://www.example.com/withCookie.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('can overwrite a cookie value from server with a cookie value from overrides', async () => {
        const headersFromServer = [{ name: 'set-cookie', value: 'userId=999' }];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'set-cookie', value: 'userId=12345' },
        ];
        const expectedPersistedSetCookieHeaders = [{ name: 'set-cookie', value: 'userId=12345' }];
        await checkSetCookieOverride('https://www.example.com/withCookie.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('correctly merges cookies from server and from overrides', async () => {
        const headersFromServer = [
            { name: 'set-cookie', value: 'foo=bar' },
            { name: 'set-cookie', value: 'userName=server' },
        ];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'set-cookie', value: 'userName=DevTools' },
            { name: 'set-cookie', value: 'themeColour=dark' },
        ];
        const expectedPersistedSetCookieHeaders = [
            { name: 'set-cookie', value: 'foo=bar' },
            { name: 'set-cookie', value: 'userName=DevTools' },
            { name: 'set-cookie', value: 'themeColour=dark' },
        ];
        await checkSetCookieOverride('https://www.example.com/withCookie2.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('correctly merges malformed cookies from server and from overrides', async () => {
        const headersFromServer = [
            { name: 'set-cookie', value: 'malformed_original' },
            { name: 'set-cookie', value: 'userName=server' },
        ];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'set-cookie', value: 'userName=DevTools' },
            { name: 'set-cookie', value: 'malformed_override' },
        ];
        const expectedPersistedSetCookieHeaders = [
            { name: 'set-cookie', value: 'malformed_original' },
            { name: 'set-cookie', value: 'userName=DevTools' },
            { name: 'set-cookie', value: 'malformed_override' },
        ];
        await checkSetCookieOverride('https://www.example.com/withCookie3.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('correctly merges \'set-cookie\' headers from server with multiple defined overrides', async () => {
        const headersFromServer = [
            { name: 'set-cookie', value: 'userName=server' },
            { name: 'set-cookie', value: 'override-me=zero' },
        ];
        const expectedOverriddenHeaders = [
            { name: 'age', value: 'overridden' },
            { name: 'set-cookie', value: 'unique=value' },
            { name: 'set-cookie', value: 'override-me=second' },
            { name: 'set-cookie', value: 'foo=bar' },
        ];
        const expectedPersistedSetCookieHeaders = [
            { name: 'set-cookie', value: 'userName=server' },
            { name: 'set-cookie', value: 'override-me=second' },
            { name: 'set-cookie', value: 'unique=value' },
            { name: 'set-cookie', value: 'foo=bar' },
        ];
        await checkSetCookieOverride('https://www.example.com/cookies/mergeCookies.html', headersFromServer, expectedOverriddenHeaders, expectedPersistedSetCookieHeaders);
    });
    it('correctly merges \'set-cookie\' headers with duplicates', () => {
        const original = [
            { name: 'set-cookie', value: 'foo=original' },
            { name: 'set-cookie', value: 'bar=original' },
            { name: 'set-cookie', value: 'baz=original' },
            { name: 'set-cookie', value: 'duplicate=duplicate' },
            { name: 'set-cookie', value: 'duplicate=duplicate' },
            { name: 'set-cookie', value: 'duplicate2=duplicate2' },
            { name: 'set-cookie', value: 'duplicate2=duplicate2' },
            { name: 'set-cookie', value: 'duplicate3=duplicate3' },
            { name: 'set-cookie', value: 'duplicate3=duplicate3' },
            { name: 'set-cookie', value: 'malformed' },
            { name: 'set-cookie', value: 'both' },
            { name: 'set-cookie', value: 'double' },
            { name: 'set-cookie', value: 'double' },
            { name: 'set-cookie', value: 'original_duplicate' },
            { name: 'set-cookie', value: 'original_duplicate' },
            { name: 'set-cookie', value: 'override_duplicate' },
        ];
        const overrides = [
            { name: 'set-cookie', value: 'bar=overridden' },
            { name: 'set-cookie', value: 'baz=overridden1' },
            { name: 'set-cookie', value: 'baz=overridden2' },
            { name: 'set-cookie', value: 'duplicate2=overridden' },
            { name: 'set-cookie', value: 'duplicate3=overridden' },
            { name: 'set-cookie', value: 'duplicate3=overridden' },
            { name: 'set-cookie', value: 'malformed_override' },
            { name: 'set-cookie', value: 'both' },
            { name: 'set-cookie', value: 'original_duplicate' },
            { name: 'set-cookie', value: 'override_duplicate' },
            { name: 'set-cookie', value: 'override_duplicate' },
        ];
        const expected = [
            { name: 'set-cookie', value: 'foo=original' },
            { name: 'set-cookie', value: 'bar=overridden' },
            { name: 'set-cookie', value: 'baz=overridden1' },
            { name: 'set-cookie', value: 'baz=overridden2' },
            { name: 'set-cookie', value: 'duplicate=duplicate' },
            { name: 'set-cookie', value: 'duplicate=duplicate' },
            { name: 'set-cookie', value: 'duplicate2=overridden' },
            { name: 'set-cookie', value: 'duplicate3=overridden' },
            { name: 'set-cookie', value: 'duplicate3=overridden' },
            { name: 'set-cookie', value: 'malformed' },
            { name: 'set-cookie', value: 'both' },
            { name: 'set-cookie', value: 'double' },
            { name: 'set-cookie', value: 'double' },
            { name: 'set-cookie', value: 'original_duplicate' },
            { name: 'set-cookie', value: 'override_duplicate' },
            { name: 'set-cookie', value: 'override_duplicate' },
            { name: 'set-cookie', value: 'malformed_override' },
        ];
        assert.deepEqual(SDK.NetworkManager.InterceptedRequest.mergeSetCookieHeaders(original, overrides), expected);
    });
    describe('getRecommendedNetworkPreset', () => {
        it('should recommend "Slow 3G" for high RTT', () => {
            // RTT >= 276ms should be Slow 3G.
            const rtt = 500;
            const preset = SDK.NetworkManager.getRecommendedNetworkPreset(rtt);
            assert.isNotNull(preset);
            assert.strictEqual(preset?.key, SDK.NetworkManager.Slow3GConditions.key);
        });
        it('should recommend "Slow 4G" for medium RTT', () => {
            // RTT between 106ms and 275ms should be Slow 4G.
            const rtt = 200;
            const preset = SDK.NetworkManager.getRecommendedNetworkPreset(rtt);
            assert.isNotNull(preset);
            assert.strictEqual(preset?.key, SDK.NetworkManager.Slow4GConditions.key);
        });
        it('should recommend "Fast 4G" for low RTT', () => {
            // RTT between 60ms and 105ms should be Fast 4G.
            const rtt = 100;
            const preset = SDK.NetworkManager.getRecommendedNetworkPreset(rtt);
            assert.isNotNull(preset);
            assert.strictEqual(preset?.key, SDK.NetworkManager.Fast4GConditions.key);
        });
        it('should return null for very low RTT, suggesting no throttling', () => {
            // RTT below 60ms should suggest no throttling by returning null.
            const rtt = 20;
            const preset = SDK.NetworkManager.getRecommendedNetworkPreset(rtt);
            assert.isNull(preset);
        });
        it('should handle boundary conditions correctly', () => {
            // Crossover between Slow 3G and Slow 4G
            const slow3GPreset = SDK.NetworkManager.getRecommendedNetworkPreset(276);
            assert.isNotNull(slow3GPreset);
            assert.strictEqual(slow3GPreset.key, SDK.NetworkManager.Slow3GConditions.key, 'RTT 276 should be Slow 3G');
            const slow4GAt3GBoundary = SDK.NetworkManager.getRecommendedNetworkPreset(275);
            assert.isNotNull(slow4GAt3GBoundary);
            assert.strictEqual(slow4GAt3GBoundary.key, SDK.NetworkManager.Slow4GConditions.key, 'RTT 275 should be Slow 4G');
            // Crossover between Slow 4G and Fast 4G
            const slow4GAtFast4GBoundary = SDK.NetworkManager.getRecommendedNetworkPreset(106);
            assert.isNotNull(slow4GAtFast4GBoundary);
            assert.strictEqual(slow4GAtFast4GBoundary.key, SDK.NetworkManager.Slow4GConditions.key, 'RTT 106 should be Slow 4G');
            const fast4GAtSlow4GBoundary = SDK.NetworkManager.getRecommendedNetworkPreset(105);
            assert.isNotNull(fast4GAtSlow4GBoundary);
            assert.strictEqual(fast4GAtSlow4GBoundary.key, SDK.NetworkManager.Fast4GConditions.key, 'RTT 105 should be Fast 4G');
            // Boundary for Fast 4G and No Throttling
            const fast4GAtNoThrottlingBoundary = SDK.NetworkManager.getRecommendedNetworkPreset(60);
            assert.isNotNull(fast4GAtNoThrottlingBoundary);
            assert.strictEqual(fast4GAtNoThrottlingBoundary.key, SDK.NetworkManager.Fast4GConditions.key, 'RTT 60 should be Fast 4G');
            const noThrottlingPreset = SDK.NetworkManager.getRecommendedNetworkPreset(59);
            assert.isNull(noThrottlingPreset, 'RTT 59 should be null');
        });
    });
});
//# sourceMappingURL=NetworkManager.test.js.map