"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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoteElement = void 0;
const exception_js_1 = require("../exception.js");
const util = __importStar(require("./cdp-util.js"));
const remote_object_js_1 = require("./remote-object.js");
class RemoteElement extends remote_object_js_1.RemoteObject {
    constructor(executionContext, cdpRemoteObject) {
        super(executionContext, cdpRemoteObject);
        this.objectId = cdpRemoteObject.objectId;
        this.description = (cdpRemoteObject.description || '').toLowerCase();
    }
    isDocument() {
        return this.description.startsWith('#document');
    }
    isOption() {
        return this.description.startsWith('option');
    }
    isFrame() {
        return this.description.startsWith('frame') || this.description.startsWith('iframe');
    }
    async querySelectorAll(selector) {
        return await this.executionContext.evaluateElementList((el, sel) => {
            return el.querySelectorAll(sel);
        }, this, selector);
    }
    async querySelector(selector) {
        return await this.executionContext.evaluateElement((el, sel) => {
            return el.querySelector(sel);
        }, this, selector);
    }
    async queryXPathAll(expression) {
        return await this.executionContext.evaluateElementList((el, expr) => {
            const document = el.ownerDocument || window.document;
            const xpathResult = document.evaluate(expr, el, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
            const eList = [];
            while (true) {
                const element = xpathResult.iterateNext();
                if (element == null) {
                    break;
                }
                if (element instanceof HTMLElement) {
                    eList.push(element);
                }
            }
            return eList;
        }, this, expression);
    }
    async queryXPathOne(expression) {
        return await this.executionContext.evaluateElement((el, expr) => {
            const document = el.ownerDocument || window.document;
            const xpathResult = document.evaluate(expr, el, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
            const element = xpathResult.iterateNext();
            if (!(element instanceof HTMLElement)) {
                return null;
            }
            return element;
        }, this, expression);
    }
    async matches(selector) {
        return await this.executionContext.evaluateJson((el, sel) => {
            return el.matches(sel);
        }, this, selector);
    }
    async parent() {
        return this.executionContext.evaluateElement(el => {
            return el.parentElement;
        }, this);
    }
    async closest(selector) {
        return await this.executionContext.evaluateElement((el, sel) => {
            return el.closest(sel);
        }, this, selector);
    }
    async children() {
        return await this.executionContext.evaluateElementList(el => {
            return el.children;
        }, this);
    }
    async previousSibling() {
        return this.executionContext.evaluateElement(el => {
            return el.previousElementSibling;
        }, this);
    }
    async nextSibling() {
        return this.executionContext.evaluateElement(el => {
            return el.nextElementSibling;
        }, this);
    }
    async contains(otherEl) {
        return this.executionContext.evaluateJson((el, otherEl) => {
            return el.contains(otherEl);
        }, this, otherEl);
    }
    async isEqualTo(otherEl) {
        return this.executionContext.evaluateJson((el, otherEl) => {
            return el === otherEl;
        }, this, otherEl);
    }
    async click(options = {}) {
        const { waitForStable = true, hideOverlays = true, ctrlKey = false, altKey = false, metaKey = false, shiftKey = false, button = 'left', clickCount = 1, delay = 0, } = options;
        const modifiers = this.page.inputManager.getModifiers({ altKey, ctrlKey, metaKey, shiftKey });
        if (this.isOption()) {
            return await this.selectOption();
        }
        const point = waitForStable ? await this.getStablePoint() : await this.getClickablePoint();
        if (hideOverlays) {
            await this.stashOverlays();
        }
        await this.page.inputManager.mouseMove(point, { modifiers });
        if (button !== 'none') {
            await this.page.inputManager.click(point, {
                modifiers,
                button,
                clickCount,
                delay,
            });
        }
        if (hideOverlays) {
            this.unstashAll().catch(() => { });
        }
    }
    async typeText(text, options = {}) {
        const { click = true, clickOptions = {}, focus = true, clear = true, enter = false, blur = true, parallel = true, delay = 0, } = options;
        if (click) {
            await this.click(clickOptions);
        }
        else if (focus) {
            await this.focus();
        }
        if (clear) {
            await this.selectText();
            await this.page.inputManager.print('\b');
        }
        await this.page.inputManager.print(text, {
            delay,
            parallel,
        });
        if (enter) {
            await this.page.inputManager.print('\n');
        }
        if (blur) {
            await this.blur();
        }
    }
    async hover(options = {}) {
        const { waitForStable = true, ctrlKey = false, altKey = false, metaKey = false, shiftKey = false } = options;
        const modifiers = this.page.inputManager.getModifiers({ altKey, ctrlKey, metaKey, shiftKey });
        if (this.isOption()) {
            const selectEl = await this.getClickTarget();
            return selectEl.hover(options);
        }
        const point = waitForStable ? await this.getStablePoint() : await this.getClickablePoint();
        await this.page.inputManager.mouseMove(point, { modifiers });
    }
    async selectOption() {
        const err = await this.executionContext.evaluateJson(optionEl => {
            if (optionEl.nodeName !== 'OPTION') {
                return { code: 'DomManipulationError', message: 'Element is not an <option>' };
            }
            const selectEl = optionEl.closest('select');
            if (!selectEl) {
                return { code: 'DomManipulationError', message: 'Cannot find <select> for given <option>' };
            }
            const options = Array.from(selectEl.options);
            const index = options.indexOf(optionEl);
            if (selectEl.selectedIndex !== index) {
                selectEl.selectedIndex = index;
                const config = {
                    bubbles: true,
                    cancelable: false,
                    view: window,
                };
                selectEl.dispatchEvent(new Event('input', config));
                selectEl.dispatchEvent(new Event('change', config));
            }
        }, this);
        if (err) {
            throw new exception_js_1.Exception({
                name: err.code,
                message: err.message,
                retry: true,
            });
        }
    }
    async describeNode() {
        const { objectId } = this;
        const { node } = await this.page.send('DOM.describeNode', { objectId });
        return node;
    }
    async isConnected() {
        return await this.executionContext.evaluateJson(el => {
            return el.isConnected;
        }, this);
    }
    async checkConnected() {
        const isConnected = await this.isConnected();
        if (!isConnected) {
            throw new exception_js_1.Exception({
                name: 'ElementNotConnected',
                message: 'Element is not connected to DOM',
                retry: true,
            });
        }
    }
    async scrollIntoViewIfNeeded() {
        await this.executionContext.evaluateJson(el => {
            const targetEl = el.nodeName === 'OPTION' ? el.closest('select') : el;
            targetEl.scrollIntoViewIfNeeded(true);
        }, this);
    }
    async scrollIntoView() {
        await this.executionContext.evaluateJson(el => {
            const targetEl = el.nodeName === 'OPTION' ? el.closest('select') : el;
            targetEl.scrollIntoView({ block: 'center', inline: 'center' });
        }, this);
    }
    async getInfo() {
        return await this.evaluateJson((el, toolkitBinding) => {
            return window[toolkitBinding].getElementInfo(el);
        }, this.page.toolkitBinding);
    }
    async isVisible() {
        const quads = await this.getContentQuads();
        if (quads.length === 0) {
            return false;
        }
        const visibility = await this.evaluateJson(el => {
            return window.getComputedStyle(el).visibility;
        });
        return visibility !== 'hidden';
    }
    async checkVisible() {
        const visible = await this.isVisible();
        if (!visible) {
            throw new exception_js_1.Exception({
                name: 'ElementNotVisible',
                message: 'Element is not visible',
                retry: true,
            });
        }
    }
    async getStablePoint() {
        const timeout = this.page.browser.config.stableBoxTimeout;
        const timeoutAt = Date.now() + timeout;
        let lastPoint = await this.getClickablePoint();
        while (Date.now() < timeoutAt) {
            await this.page.waitForAnimationFrame();
            const newPoint = await this.getClickablePoint();
            if (util.arePointsEqual(lastPoint, newPoint)) {
                return newPoint;
            }
            lastPoint = newPoint;
        }
        throw new exception_js_1.Exception({
            name: 'ElementNotStable',
            message: 'Cannot obtain a stable element box (does it scroll randomly?)',
            retry: true,
        });
    }
    async getClickablePoint(forceScroll = false) {
        if (this.isOption()) {
            const selectEl = await this.getClickTarget();
            return selectEl.getClickablePoint();
        }
        if (forceScroll) {
            await this.scrollIntoView();
        }
        else {
            await this.scrollIntoViewIfNeeded();
        }
        const quads = await this.getContentQuads();
        if (!quads.length) {
            throw new exception_js_1.Exception({
                name: 'ElementNotVisible',
                message: 'Element is not visible',
                retry: true,
            });
        }
        const { visualViewport: { clientWidth, clientHeight, zoom = 1 }, } = await this.page.getLayoutMetrics();
        const visibleQuad = util.findVisibleQuad(quads, clientWidth / zoom, clientHeight / zoom);
        if (visibleQuad) {
            const { x, y } = util.quadCenterPoint(visibleQuad);
            return { x, y, zoom };
        }
        else if (!forceScroll) {
            return await this.getClickablePoint(true);
        }
        throw new exception_js_1.Exception({
            name: 'ElementNotVisible',
            message: 'Could not find a clickable point in viewport',
            retry: true,
        });
    }
    async getContentQuads() {
        const { objectId } = this;
        const res = await this.page.send('DOM.getContentQuads', { objectId }).catch(() => {
            return { quads: [] };
        });
        let quads = res.quads.map((cdpQuad) => util.fromCdpQuad(cdpQuad));
        if (quads.length === 0) {
            const rects = await this.getClientRects();
            quads = rects.map(rect => util.rectToQuad(rect));
        }
        return quads.filter((quad) => util.computeQuadArea(quad) > 1);
    }
    async getClientRects() {
        return await this.evaluateJson(el => {
            const rects = [].slice.call(el.getClientRects());
            return rects.map((rect) => {
                const { x, y, left, right, top, bottom, width, height } = rect;
                return { x, y, left, right, top, bottom, width, height };
            });
        });
    }
    async getBoundingClientRect() {
        return await this.evaluateJson(el => {
            const { x, y, left, right, top, bottom, width, height } = el.getBoundingClientRect();
            return { x, y, left, right, top, bottom, width, height };
        });
    }
    async getBoxModel() {
        const { objectId } = this;
        const { model } = await this.page.send('DOM.getBoxModel', { objectId });
        return {
            content: util.fromCdpQuad(model.content),
            padding: util.fromCdpQuad(model.padding),
            border: util.fromCdpQuad(model.border),
            margin: util.fromCdpQuad(model.margin),
            width: model.width,
            height: model.height,
        };
    }
    async stashOverlays() {
        await this.evaluate((el, toolkitBinding) => {
            window[toolkitBinding].stashObscuringElements(el);
        }, this.page.toolkitBinding);
    }
    async unstashAll() {
        await this.evaluate((el, toolkitBinding) => {
            window[toolkitBinding].unstashAll();
        }, this.page.toolkitBinding);
    }
    async selectText() {
        await this.executionContext.evaluate(el => el.select(), this);
    }
    async focus() {
        await this.executionContext.evaluate(el => el.focus(), this);
    }
    async blur() {
        await this.executionContext.evaluate(el => el.blur(), this);
    }
    async getClickTarget() {
        if (this.isOption()) {
            const selectEl = await this.closest('select');
            if (!selectEl) {
                throw new exception_js_1.Exception({
                    name: 'DomManipulationError',
                    message: 'Cannot find <select> for given <option>',
                    retry: true,
                });
            }
            return selectEl;
        }
        return this;
    }
    async resolveFrame() {
        const { nodeName, frameId } = await this.describeNode();
        if (!frameId) {
            throw new exception_js_1.Exception({
                name: 'DomManipulationError',
                message: `Cannot access contentDocument of ${nodeName} (reason: frameId not found on Node)`,
                retry: true,
            });
        }
        return await this.page.resolveFrameById(frameId);
    }
    async contentDocument() {
        const frame = this.isFrame() ? await this.resolveFrame() : null;
        if (!frame) {
            throw new exception_js_1.Exception({
                name: 'DomManipulationError',
                message: 'Cannot access iframe document (reason: frame not found by frameId)',
                retry: true,
            });
        }
        return await frame.document();
    }
    async ownerDocument() {
        const doc = await this.evaluateElement(el => (el instanceof Document ? el : el.ownerDocument));
        if (!doc) {
            throw new exception_js_1.Exception({
                name: 'DomManipulationError',
                message: `Cannot obtain owner document of ${this.description}`,
                retry: true,
            });
        }
        return doc;
    }
    async shadowRoot() {
        const el = await this.evaluateElement(el => el.shadowRoot);
        if (!el) {
            throw new exception_js_1.Exception({
                name: 'DomManipulationError',
                message: `Cannot obtain shadow root of ${this.description}`,
                retry: true,
            });
        }
        return el;
    }
    async createSelector(scopeEl, unique = false) {
        return await this.executionContext.evaluateJson((scopeEl, el, unique, toolkitBinding) => {
            return window[toolkitBinding].createSelector(scopeEl, el, unique);
        }, scopeEl, this, unique, this.page.toolkitBinding);
    }
    async sendPost(url, postParams) {
        await this.executionContext.evaluateJson((url, postParams, toolkitBinding) => {
            window[toolkitBinding].sendPost(url, postParams);
        }, url, postParams, this.page.toolkitBinding);
    }
    highlight() {
        this.page.domManager.highlightElement(this);
    }
    hideHighlight() {
        this.page.domManager.hideHighlight();
    }
}
exports.RemoteElement = RemoteElement;
//# sourceMappingURL=remote-element.js.map