/*
 * Copyright 2023 Google LLC.
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
import { uuidv4 } from '../../../utils/uuid.js';
import { ChannelProxy } from './ChannelProxy.js';
/**
 * BiDi IDs are generated by the server and are unique within contexts.
 *
 * CDP preload script IDs are generated by the client and are unique
 * within sessions.
 *
 * The mapping between BiDi and CDP preload script IDs is 1:many.
 * BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
 * in the client.
 */
export class PreloadScript {
    /** BiDi ID, an automatically generated UUID. */
    #id = uuidv4();
    /** CDP preload scripts. */
    #cdpPreloadScripts = [];
    /** The script itself, in a format expected by the spec i.e. a function. */
    #functionDeclaration;
    /** Targets, in which the preload script is initialized. */
    #targetIds = new Set();
    /** Channels to be added as arguments to functionDeclaration. */
    #channels;
    /** The script sandbox / world name. */
    #sandbox;
    /** The browsing contexts to execute the preload scripts in, if any. */
    #contexts;
    get id() {
        return this.#id;
    }
    get targetIds() {
        return this.#targetIds;
    }
    constructor(params, logger) {
        this.#channels =
            params.arguments?.map((a) => new ChannelProxy(a.value, logger)) ?? [];
        this.#functionDeclaration = params.functionDeclaration;
        this.#sandbox = params.sandbox;
        this.#contexts = params.contexts;
    }
    /** Channels of the preload script. */
    get channels() {
        return this.#channels;
    }
    /** Contexts of the preload script, if any */
    get contexts() {
        return this.#contexts;
    }
    /**
     * String to be evaluated. Wraps user-provided function so that the following
     * steps are run:
     * 1. Create channels.
     * 2. Store the created channels in window.
     * 3. Call the user-provided function with channels as arguments.
     */
    #getEvaluateString() {
        const channelsArgStr = `[${this.channels
            .map((c) => c.getEvalInWindowStr())
            .join(', ')}]`;
        return `(()=>{(${this.#functionDeclaration})(...${channelsArgStr})})()`;
    }
    /**
     * Adds the script to the given CDP targets by calling the
     * `Page.addScriptToEvaluateOnNewDocument` command.
     */
    async initInTargets(cdpTargets, runImmediately) {
        await Promise.all(Array.from(cdpTargets).map((cdpTarget) => this.initInTarget(cdpTarget, runImmediately)));
    }
    /**
     * Adds the script to the given CDP target by calling the
     * `Page.addScriptToEvaluateOnNewDocument` command.
     */
    async initInTarget(cdpTarget, runImmediately) {
        const addCdpPreloadScriptResult = await cdpTarget.cdpClient.sendCommand('Page.addScriptToEvaluateOnNewDocument', {
            source: this.#getEvaluateString(),
            worldName: this.#sandbox,
            runImmediately,
        });
        this.#cdpPreloadScripts.push({
            target: cdpTarget,
            preloadScriptId: addCdpPreloadScriptResult.identifier,
        });
        this.#targetIds.add(cdpTarget.id);
    }
    /**
     * Removes this script from all CDP targets.
     */
    async remove() {
        await Promise.all([
            this.#cdpPreloadScripts.map(async (cdpPreloadScript) => {
                const cdpTarget = cdpPreloadScript.target;
                const cdpPreloadScriptId = cdpPreloadScript.preloadScriptId;
                return await cdpTarget.cdpClient.sendCommand('Page.removeScriptToEvaluateOnNewDocument', {
                    identifier: cdpPreloadScriptId,
                });
            }),
        ]);
    }
    /** Removes the provided cdp target from the list of cdp preload scripts. */
    dispose(cdpTargetId) {
        this.#cdpPreloadScripts = this.#cdpPreloadScripts.filter((cdpPreloadScript) => cdpPreloadScript.target?.id !== cdpTargetId);
        this.#targetIds.delete(cdpTargetId);
    }
}
//# sourceMappingURL=PreloadScript.js.map