"use strict";
/**
 * Copyright 2024 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.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.WindowRealm = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const Realm_js_1 = require("./Realm.js");
const SharedId_js_1 = require("./SharedId.js");
class WindowRealm extends Realm_js_1.Realm {
    #browsingContextId;
    #browsingContextStorage;
    sandbox;
    constructor(browsingContextId, browsingContextStorage, cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage, sandbox) {
        super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
        this.#browsingContextId = browsingContextId;
        this.#browsingContextStorage = browsingContextStorage;
        this.sandbox = sandbox;
        this.initialize();
    }
    #getBrowsingContextId(navigableId) {
        const maybeBrowsingContext = this.#browsingContextStorage
            .getAllContexts()
            .find((context) => context.navigableId === navigableId);
        return maybeBrowsingContext?.id ?? 'UNKNOWN';
    }
    get browsingContext() {
        return this.#browsingContextStorage.getContext(this.#browsingContextId);
    }
    get associatedBrowsingContexts() {
        return [this.browsingContext];
    }
    get realmType() {
        return 'window';
    }
    get realmInfo() {
        return {
            ...this.baseInfo,
            type: this.realmType,
            context: this.#browsingContextId,
            sandbox: this.sandbox,
        };
    }
    get source() {
        return {
            realm: this.realmId,
            context: this.browsingContext.id,
        };
    }
    serializeForBiDi(deepSerializedValue, internalIdMap) {
        const bidiValue = deepSerializedValue.value;
        if (deepSerializedValue.type === 'node' && bidiValue !== undefined) {
            if (Object.hasOwn(bidiValue, 'backendNodeId')) {
                let navigableId = this.browsingContext.navigableId ?? 'UNKNOWN';
                if (Object.hasOwn(bidiValue, 'loaderId')) {
                    // `loaderId` should be always there after ~2024-03-05, when
                    // https://crrev.com/c/5116240 reaches stable.
                    // TODO: remove the check after the date.
                    navigableId = bidiValue.loaderId;
                    delete bidiValue['loaderId'];
                }
                deepSerializedValue.sharedId =
                    (0, SharedId_js_1.getSharedId)(this.#getBrowsingContextId(navigableId), navigableId, bidiValue.backendNodeId);
                delete bidiValue['backendNodeId'];
            }
            if (Object.hasOwn(bidiValue, 'children')) {
                for (const i in bidiValue.children) {
                    bidiValue.children[i] = this.serializeForBiDi(bidiValue.children[i], internalIdMap);
                }
            }
            if (Object.hasOwn(bidiValue, 'shadowRoot') &&
                bidiValue.shadowRoot !== null) {
                bidiValue.shadowRoot = this.serializeForBiDi(bidiValue.shadowRoot, internalIdMap);
            }
            // `namespaceURI` can be is either `null` or non-empty string.
            if (bidiValue.namespaceURI === '') {
                bidiValue.namespaceURI = null;
            }
        }
        return super.serializeForBiDi(deepSerializedValue, internalIdMap);
    }
    async deserializeForCdp(localValue) {
        if ('sharedId' in localValue && localValue.sharedId) {
            const parsedSharedId = (0, SharedId_js_1.parseSharedId)(localValue.sharedId);
            if (parsedSharedId === null) {
                throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
            }
            const { documentId, backendNodeId } = parsedSharedId;
            // TODO: add proper validation if the element is accessible from the current realm.
            if (this.browsingContext.navigableId !== documentId) {
                throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" belongs to different document. Current document is ${this.browsingContext.navigableId}.`);
            }
            try {
                const { object } = await this.cdpClient.sendCommand('DOM.resolveNode', {
                    backendNodeId,
                    executionContextId: this.executionContextId,
                });
                // TODO(#375): Release `obj.object.objectId` after using.
                return { objectId: object.objectId };
            }
            catch (error) {
                // Heuristic to detect "no such node" exception. Based on the  specific
                // CDP implementation.
                if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
                    error.message === 'No node with given id found') {
                    throw new protocol_js_1.NoSuchNodeException(`SharedId "${localValue.sharedId}" was not found.`);
                }
                throw new protocol_js_1.UnknownErrorException(error.message, error.stack);
            }
        }
        return await super.deserializeForCdp(localValue);
    }
    async evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation) {
        await this.#browsingContextStorage
            .getContext(this.#browsingContextId)
            .targetUnblockedOrThrow();
        return await super.evaluate(expression, awaitPromise, resultOwnership, serializationOptions, userActivation);
    }
    async callFunction(functionDeclaration, awaitPromise, thisLocalValue, argumentsLocalValues, resultOwnership, serializationOptions, userActivation) {
        await this.#browsingContextStorage
            .getContext(this.#browsingContextId)
            .targetUnblockedOrThrow();
        return await super.callFunction(functionDeclaration, awaitPromise, thisLocalValue, argumentsLocalValues, resultOwnership, serializationOptions, userActivation);
    }
}
exports.WindowRealm = WindowRealm;
//# sourceMappingURL=WindowRealm.js.map