"use strict";
/**
 * 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.parseUnserializableValue = exports.evaluateExpression = exports.evaluate = exports.JSHandle = exports.ExecutionContext = void 0;
const utilityScriptSource = require("../generated/utilityScriptSource");
const sourceMap = require("../utils/sourceMap");
const utilityScriptSerializers_1 = require("./common/utilityScriptSerializers");
class ExecutionContext {
    constructor(delegate) {
        this._delegate = delegate;
    }
    adoptIfNeeded(handle) {
        return null;
    }
    utilityScript() {
        if (!this._utilityScriptPromise) {
            const source = `
      (() => {
        ${utilityScriptSource.source}
        return new pwExport();
      })();`;
            this._utilityScriptPromise = this._delegate.rawEvaluate(source).then(objectId => new JSHandle(this, 'object', objectId));
        }
        return this._utilityScriptPromise;
    }
    createHandle(remoteObject) {
        return this._delegate.createHandle(this, remoteObject);
    }
    async doSlowMo() {
        // overrided in FrameExecutionContext
    }
}
exports.ExecutionContext = ExecutionContext;
class JSHandle {
    constructor(context, type, objectId, value) {
        this._disposed = false;
        this._context = context;
        this._objectId = objectId;
        this._value = value;
        this._objectType = type;
        if (this._objectId)
            this._value = 'JSHandle@' + this._objectType;
        this._preview = 'JSHandle@' + String(this._objectId ? this._objectType : this._value);
    }
    async evaluate(pageFunction, arg) {
        return evaluate(this._context, true /* returnByValue */, pageFunction, this, arg);
    }
    async evaluateHandle(pageFunction, arg) {
        return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
    }
    async _evaluateExpression(expression, isFunction, returnByValue, arg) {
        const value = await evaluateExpression(this._context, returnByValue, expression, isFunction, this, arg);
        await this._context.doSlowMo();
        return value;
    }
    async getProperty(propertyName) {
        const objectHandle = await this.evaluateHandle((object, propertyName) => {
            const result = { __proto__: null };
            result[propertyName] = object[propertyName];
            return result;
        }, propertyName);
        const properties = await objectHandle.getProperties();
        const result = properties.get(propertyName);
        objectHandle.dispose();
        return result;
    }
    getProperties() {
        return this._context._delegate.getProperties(this);
    }
    async jsonValue() {
        if (!this._objectId)
            return this._value;
        const utilityScript = await this._context.utilityScript();
        const script = `(utilityScript, ...args) => utilityScript.jsonValue(...args)`;
        return this._context._delegate.evaluateWithArguments(script, true, utilityScript, [true], [this._objectId]);
    }
    asElement() {
        return null;
    }
    async dispose() {
        if (this._disposed)
            return;
        this._disposed = true;
        await this._context._delegate.releaseHandle(this);
    }
    toString() {
        return this._preview;
    }
    _setPreviewCallback(callback) {
        this._previewCallback = callback;
    }
    _setPreview(preview) {
        this._preview = preview;
        if (this._previewCallback)
            this._previewCallback(preview);
    }
}
exports.JSHandle = JSHandle;
async function evaluate(context, returnByValue, pageFunction, ...args) {
    return evaluateExpression(context, returnByValue, String(pageFunction), typeof pageFunction === 'function', ...args);
}
exports.evaluate = evaluate;
async function evaluateExpression(context, returnByValue, expression, isFunction, ...args) {
    const utilityScript = await context.utilityScript();
    if (!isFunction) {
        const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`;
        return context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, [returnByValue, expression], []);
    }
    let functionText = expression;
    try {
        new Function('(' + functionText + ')');
    }
    catch (e1) {
        // This means we might have a function shorthand. Try another
        // time prefixing 'function '.
        if (functionText.startsWith('async '))
            functionText = 'async function ' + functionText.substring('async '.length);
        else
            functionText = 'function ' + functionText;
        try {
            new Function('(' + functionText + ')');
        }
        catch (e2) {
            // We tried hard to serialize, but there's a weird beast here.
            throw new Error('Passed function is not well-serializable!');
        }
    }
    const handles = [];
    const toDispose = [];
    const pushHandle = (handle) => {
        handles.push(handle);
        return handles.length - 1;
    };
    args = args.map(arg => utilityScriptSerializers_1.serializeAsCallArgument(arg, handle => {
        if (handle instanceof JSHandle) {
            if (!handle._objectId)
                return { fallThrough: handle._value };
            if (handle._disposed)
                throw new Error('JSHandle is disposed!');
            const adopted = context.adoptIfNeeded(handle);
            if (adopted === null)
                return { h: pushHandle(Promise.resolve(handle)) };
            toDispose.push(adopted);
            return { h: pushHandle(adopted) };
        }
        return { fallThrough: handle };
    }));
    const utilityScriptObjectIds = [];
    for (const handle of await Promise.all(handles)) {
        if (handle._context !== context)
            throw new Error('JSHandles can be evaluated only in the context they were created!');
        utilityScriptObjectIds.push(handle._objectId);
    }
    functionText += await sourceMap.generateSourceMapUrl(expression, functionText);
    // See UtilityScript for arguments.
    const utilityScriptValues = [returnByValue, functionText, args.length, ...args];
    const script = `(utilityScript, ...args) => utilityScript.callFunction(...args)`;
    try {
        return await context._delegate.evaluateWithArguments(script, returnByValue, utilityScript, utilityScriptValues, utilityScriptObjectIds);
    }
    finally {
        toDispose.map(handlePromise => handlePromise.then(handle => handle.dispose()));
    }
}
exports.evaluateExpression = evaluateExpression;
function parseUnserializableValue(unserializableValue) {
    if (unserializableValue === 'NaN')
        return NaN;
    if (unserializableValue === 'Infinity')
        return Infinity;
    if (unserializableValue === '-Infinity')
        return -Infinity;
    if (unserializableValue === '-0')
        return -0;
}
exports.parseUnserializableValue = parseUnserializableValue;
//# sourceMappingURL=javascript.js.map