"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Event_js_1 = __importDefault(require("../../event/Event.cjs"));
const PropertySymbol = __importStar(require("../../PropertySymbol.cjs"));
const HTMLElement_js_1 = __importDefault(require("../html-element/HTMLElement.cjs"));
const CrossOriginBrowserWindow_js_1 = __importDefault(require("../../window/CrossOriginBrowserWindow.cjs"));
const DOMTokenList_js_1 = __importDefault(require("../../dom/DOMTokenList.cjs"));
const BrowserFrameFactory_js_1 = __importDefault(require("../../browser/utilities/BrowserFrameFactory.cjs"));
const BrowserFrameURL_js_1 = __importDefault(require("../../browser/utilities/BrowserFrameURL.cjs"));
const WindowErrorUtility_js_1 = __importDefault(require("../../window/WindowErrorUtility.cjs"));
const DOMExceptionNameEnum_js_1 = __importDefault(require("../../exception/DOMExceptionNameEnum.cjs"));
const WindowBrowserContext_js_1 = __importDefault(require("../../window/WindowBrowserContext.cjs"));
const SANDBOX_FLAGS = [
    'allow-downloads',
    'allow-forms',
    'allow-modals',
    'allow-orientation-lock',
    'allow-pointer-lock',
    'allow-popups',
    'allow-popups-to-escape-sandbox',
    'allow-presentation',
    'allow-same-origin',
    'allow-scripts',
    'allow-top-navigation',
    'allow-top-navigation-by-user-activation',
    'allow-top-navigation-to-custom-protocols'
];
/**
 * HTML Iframe Element.
 *
 * Reference:
 * https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement.
 */
class HTMLIFrameElement extends HTMLElement_js_1.default {
    // Events
    onload = null;
    onerror = null;
    // Internal properties
    [PropertySymbol.sandbox] = null;
    // Private properties
    #contentWindowContainer = {
        window: null
    };
    #iframe;
    #loadedSrcdoc = null;
    /**
     * Returns source.
     *
     * @returns Source.
     */
    get src() {
        if (!this.hasAttribute('src')) {
            return '';
        }
        try {
            return new URL(this.getAttribute('src'), this[PropertySymbol.ownerDocument].location.href)
                .href;
        }
        catch (e) {
            return this.getAttribute('src');
        }
    }
    /**
     * Sets source.
     *
     * @param src Source.
     */
    set src(src) {
        this.setAttribute('src', src);
    }
    /**
     * Returns allow.
     *
     * @returns Allow.
     */
    get allow() {
        return this.getAttribute('allow') || '';
    }
    /**
     * Sets allow.
     *
     * @param allow Allow.
     */
    set allow(allow) {
        this.setAttribute('allow', allow);
    }
    /**
     * Returns height.
     *
     * @returns Height.
     */
    get height() {
        return this.getAttribute('height') || '';
    }
    /**
     * Sets height.
     *
     * @param height Height.
     */
    set height(height) {
        this.setAttribute('height', height);
    }
    /**
     * Returns width.
     *
     * @returns Width.
     */
    get width() {
        return this.getAttribute('width') || '';
    }
    /**
     * Sets width.
     *
     * @param width Width.
     */
    set width(width) {
        this.setAttribute('width', width);
    }
    /**
     * Returns name.
     *
     * @returns Name.
     */
    get name() {
        return this.getAttribute('name') || '';
    }
    /**
     * Sets name.
     *
     * @param name Name.
     */
    set name(name) {
        this.setAttribute('name', name);
    }
    /**
     * Returns sandbox.
     *
     * @returns Sandbox.
     */
    get sandbox() {
        if (!this[PropertySymbol.sandbox]) {
            this[PropertySymbol.sandbox] = new DOMTokenList_js_1.default(PropertySymbol.illegalConstructor, this, 'sandbox');
        }
        return this[PropertySymbol.sandbox];
    }
    /**
     * Sets sandbox.
     */
    set sandbox(sandbox) {
        this.setAttribute('sandbox', sandbox);
    }
    /**
     * Returns srcdoc.
     *
     * @returns Srcdoc.
     */
    get srcdoc() {
        return this.getAttribute('srcdoc') || '';
    }
    /**
     * Sets srcdoc.
     *
     * @param srcdoc Srcdoc.
     */
    set srcdoc(srcdoc) {
        this.setAttribute('srcdoc', srcdoc);
    }
    /**
     * Returns referrer policy.
     */
    get referrerPolicy() {
        return this.getAttribute('referrerpolicy') || '';
    }
    /**
     * Sets referrer policy.
     *
     * @param referrerPolicy Referrer policy.
     */
    set referrerPolicy(referrerPolicy) {
        this.setAttribute('referrerpolicy', referrerPolicy);
    }
    /**
     * Returns content document.
     *
     * @returns Content document.
     */
    get contentDocument() {
        return this.#contentWindowContainer.window?.document ?? null;
    }
    /**
     * Returns content window.
     *
     * @returns Content window.
     */
    get contentWindow() {
        return this.#contentWindowContainer.window;
    }
    /**
     * @override
     */
    [PropertySymbol.connectedToDocument]() {
        super[PropertySymbol.connectedToDocument]();
        this.#loadPage();
    }
    /**
     * Called when disconnected from document.
     * @param e
     */
    [PropertySymbol.disconnectedFromDocument]() {
        super[PropertySymbol.disconnectedFromDocument]();
        this.#unloadPage();
    }
    /**
     * @override
     */
    [PropertySymbol.cloneNode](deep = false) {
        return super[PropertySymbol.cloneNode](deep);
    }
    /**
     * @override
     */
    [PropertySymbol.onSetAttribute](attribute, replacedAttribute) {
        super[PropertySymbol.onSetAttribute](attribute, replacedAttribute);
        if (attribute[PropertySymbol.name] === 'srcdoc') {
            this.#loadPage();
        }
        if (attribute[PropertySymbol.name] === 'src' &&
            attribute[PropertySymbol.value] &&
            !this[PropertySymbol.attributes][PropertySymbol.namedItems].has('srcdoc') &&
            attribute[PropertySymbol.value] !== replacedAttribute?.[PropertySymbol.value]) {
            this.#loadPage();
        }
        if (attribute[PropertySymbol.name] === 'sandbox') {
            this.#validateSandboxFlags();
        }
    }
    /**
     * @override
     */
    [PropertySymbol.onRemoveAttribute](removedAttribute) {
        super[PropertySymbol.onRemoveAttribute](removedAttribute);
        if (removedAttribute[PropertySymbol.name] === 'srcdoc' ||
            removedAttribute[PropertySymbol.name] === 'src') {
            this.#loadPage();
        }
    }
    /**
     *
     * @param tokens
     * @param vconsole
     */
    #validateSandboxFlags() {
        const window = this[PropertySymbol.window];
        const invalidFlags = [];
        for (const token of this.sandbox) {
            if (!SANDBOX_FLAGS.includes(token)) {
                invalidFlags.push(token);
            }
        }
        if (invalidFlags.length === 1) {
            window.console.error(`Error while parsing the 'sandbox' attribute: '${invalidFlags[0]}' is an invalid sandbox flag.`);
        }
        else if (invalidFlags.length > 1) {
            window.console.error(`Error while parsing the 'sandbox' attribute: '${invalidFlags.join(`', '`)}' are invalid sandbox flags.`);
        }
    }
    /**
     * Loads an iframe page.
     */
    #loadPage() {
        if (!this[PropertySymbol.isConnected]) {
            this.#unloadPage();
            return;
        }
        const srcdoc = this.getAttribute('srcdoc');
        const window = this[PropertySymbol.window];
        const browserFrame = new WindowBrowserContext_js_1.default(window).getBrowserFrame();
        if (!browserFrame) {
            return;
        }
        if (srcdoc !== null) {
            if (this.#loadedSrcdoc === srcdoc) {
                return;
            }
            this.#unloadPage();
            this.#iframe = BrowserFrameFactory_js_1.default.createChildFrame(browserFrame);
            this.#iframe.url = 'about:srcdoc';
            this.#contentWindowContainer.window = this.#iframe.window;
            this.#iframe.window[PropertySymbol.top] = browserFrame.window.top;
            this.#iframe.window[PropertySymbol.parent] = browserFrame.window;
            this.#iframe.window.document.open();
            this.#iframe.window.document.write(srcdoc);
            this.#loadedSrcdoc = srcdoc;
            this[PropertySymbol.window].requestAnimationFrame(() => this.dispatchEvent(new Event_js_1.default('load')));
            return;
        }
        if (this.#loadedSrcdoc !== null) {
            this.#unloadPage();
        }
        const originURL = browserFrame.window.location;
        const targetURL = BrowserFrameURL_js_1.default.getRelativeURL(browserFrame, this.src);
        if (this.#iframe && this.#iframe.window.location.href === targetURL.href) {
            return;
        }
        if (browserFrame.page.context.browser.settings.disableIframePageLoading) {
            WindowErrorUtility_js_1.default.dispatchError(this, new window.DOMException(`Failed to load iframe page "${targetURL.href}". Iframe page loading is disabled.`, DOMExceptionNameEnum_js_1.default.notSupportedError));
            return;
        }
        // Iframes has a special rule for CORS and doesn't allow access between frames when the origin is different.
        const isSameOrigin = originURL.origin === targetURL.origin || targetURL.origin === 'null';
        const parentWindow = isSameOrigin ? window : new CrossOriginBrowserWindow_js_1.default(window);
        this.#iframe = this.#iframe ?? BrowserFrameFactory_js_1.default.createChildFrame(browserFrame);
        this.#iframe.window[PropertySymbol.top] = parentWindow;
        this.#iframe.window[PropertySymbol.parent] = parentWindow;
        this.#iframe
            .goto(targetURL.href, {
            referrer: originURL.origin,
            referrerPolicy: this.referrerPolicy
        })
            .then(() => this.dispatchEvent(new Event_js_1.default('load')))
            .catch((error) => WindowErrorUtility_js_1.default.dispatchError(this, error));
        this.#contentWindowContainer.window = isSameOrigin
            ? this.#iframe.window
            : new CrossOriginBrowserWindow_js_1.default(this.#iframe.window, window);
    }
    /**
     * Unloads an iframe page.
     */
    #unloadPage() {
        if (this.#iframe) {
            BrowserFrameFactory_js_1.default.destroyFrame(this.#iframe);
            this.#iframe = null;
        }
        this.#contentWindowContainer.window = null;
        this.#loadedSrcdoc = null;
    }
}
exports.default = HTMLIFrameElement;
//# sourceMappingURL=HTMLIFrameElement.cjs.map