"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FrameManager = void 0;
const frame_js_1 = require("./frame.js");
class FrameManager {
    constructor(page, frameTree) {
        this.frames = new Map();
        this.page = page;
        const { target } = page;
        this.mainFrame = this.registerFrame(frameTree.frame.id);
        this.registerFrameTree(frameTree);
        target.on('Page.frameAttached', ev => this.onFrameAttached(ev.frameId, ev.parentFrameId));
        target.on('Page.frameNavigated', ev => this.onFrameNavigated(ev.frame));
        target.on('Page.navigatedWithinDocument', ev => this.onNavigatedWithinDocument(ev.frameId, ev.url));
        target.on('Page.frameDetached', ev => this.onFrameDetached(ev.frameId, ev.reason));
        target.on('Page.frameStoppedLoading', ev => this.onFrameStoppedLoading(ev.frameId));
        target.on('Runtime.executionContextCreated', ev => this.onExecutionContextCreated(ev.context));
        target.on('Runtime.executionContextDestroyed', ev => this.onExecutionContextDestroyed(ev.executionContextId));
        target.on('Runtime.executionContextsCleared', () => this.onExecutionContextsCleared());
    }
    getFrameById(id) {
        return this.frames.get(id);
    }
    registerFrameTree(frameTree) {
        const { frame, childFrames } = frameTree;
        if (frame.parentId) {
            this.onFrameAttached(frame.id, frame.parentId);
        }
        this.onFrameNavigated(frame);
        if (childFrames) {
            for (const child of childFrames) {
                this.registerFrameTree(child);
            }
        }
    }
    registerFrame(frameId, parentFrameId) {
        if (this.frames.has(frameId)) {
            return this.frames.get(frameId);
        }
        const parentFrame = parentFrameId ? this.getFrameById(parentFrameId) : undefined;
        const frame = new frame_js_1.Frame(this.page, frameId, parentFrame);
        this.frames.set(frameId, frame);
        if (!parentFrameId) {
            this.mainFrame = frame;
        }
        return frame;
    }
    unregisterFrame(frame) {
        this.frames.delete(frame.frameId);
        if (frame.parentFrame) {
            frame.parentFrame.childFrames.delete(frame);
        }
        for (const child of frame.childFrames) {
            this.unregisterFrame(child);
        }
    }
    onFrameAttached(frameId, parentFrameId) {
        this.registerFrame(frameId, parentFrameId);
    }
    onFrameNavigated(cdpFrame) {
        const { id, parentId } = cdpFrame;
        const existingFrame = this.getFrameById(id);
        if (existingFrame) {
            for (const child of existingFrame.childFrames) {
                this.unregisterFrame(child);
            }
            if (existingFrame.isMainFrame()) {
                existingFrame.frameId = id;
                this.frames.delete(id);
                this.frames.set(id, existingFrame);
            }
            existingFrame.onNavigated(cdpFrame);
        }
        else {
            const newFrame = this.registerFrame(id, parentId);
            newFrame.onNavigated(cdpFrame);
        }
    }
    onNavigatedWithinDocument(frameId, url) {
        const frame = this.getFrameById(frameId);
        if (frame) {
            frame.url = url;
        }
    }
    onFrameDetached(frameId, reason) {
        const frame = this.getFrameById(frameId);
        if (frame) {
            if (reason === 'swap') {
                this.page.logger.debug('Frame swapped', { frameId });
            }
            this.unregisterFrame(frame);
        }
    }
    onFrameStoppedLoading(frameId) {
        const frame = this.getFrameById(frameId);
        if (frame) {
            frame.onStoppedLoading();
        }
    }
    onExecutionContextCreated(context) {
        const frameId = (context.auxData || {}).frameId || '';
        const frame = this.getFrameById(frameId);
        if (frame) {
            frame.onExecutionContextCreated(context);
        }
    }
    onExecutionContextDestroyed(executionContextId) {
        for (const frame of this.frames.values()) {
            frame.onExecutionContextDestroyed(executionContextId);
        }
    }
    onExecutionContextsCleared() {
        for (const frame of this.frames.values()) {
            frame.onExecutionContextsCleared();
        }
    }
}
exports.FrameManager = FrameManager;
//# sourceMappingURL=frame-manager.js.map