/**
 * Copyright 2019 Google Inc. All rights reserved.
 *
 * 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.
 */ import { assert } from './assert.js' ; import { helper , debugError } from './helper.js' ; import { ExecutionContext } from './ExecutionContext.js' ; import { Page , ScreenshotOptions } from './Page.js' ; import { CDPSession } from './Connection.js' ; import { KeyInput } from './USKeyboardLayout.js' ; import { FrameManager , Frame } from './FrameManager.js' ; import { getQueryHandlerAndSelector } from './QueryHandler.js' ; import { Protocol } from 'devtools-protocol' ; import { EvaluateFn , SerializableOrJSHandle , EvaluateFnReturnType , EvaluateHandleFn , WrapElementHandle , UnwrapPromiseLike , } from './EvalTypes.js' ; import { isNode } from '../environment.js' ; public export interface BoxModel { content : Array < { x : number ; y : number ; } > ; padding : Array < { x : number ; y : number ; } > ; border : Array < { x : number ; y : number ; } > ; margin : Array < { x : number ; y : number ; } > ; width : number ; height : number ; } public export interface BoundingBox { /**
     * the x coordinate of the element in pixels.
     */ x : number ; /**
     * the y coordinate of the element in pixels.
     */ y : number ; /**
     * the width of the element in pixels.
     */ width : number ; /**
     * the height of the element in pixels.
     */ height : number ; } internal export function createJSHandle ( context , remoteObject ) { const frame = context . frame ( ) ; if ( remoteObject . subtype === 'node' && frame ) { const frameManager = frame . _frameManager ; return new ElementHandle ( context , context . _client , remoteObject , frame , frameManager . page ( ) , frameManager ) ; } return new JSHandle ( context , context . _client , remoteObject ) ; } /**
 * Represents an in-page JavaScript object. JSHandles can be created with the
 *  Page . evaluateHandle method.
 *
 *  example ```js
 * const windowHandle = await page.evaluateHandle(() => window);
 * ```
 *
 * JSHandle prevents the referenced JavaScript object from being garbage-collected
 * unless the handle is  JSHandle . dispose . JSHandles are auto-
 * disposed when their origin frame gets navigated or the parent context gets destroyed.
 *
 * JSHandle instances can be used as arguments for  Page . $eval ,
 *  Page . evaluate , and  Page . evaluateHandle .
 *
 *  public export class JSHandle < HandleObjectType = unknown > { internal _context : ExecutionContext ; internal _client : CDPSession ; internal _remoteObject : Protocol . Runtime . RemoteObject ; internal _disposed = false ; internal constructor ( context , client , remoteObject ) { this . _context = context ; this . _client = client ; this . _remoteObject = remoteObject ; } /** Returns the execution context the handle belongs to.
     */ executionContext ( ) { return this . _context ; } example async evaluate < T extends EvaluateFn < HandleObjectType > > ( pageFunction , ... args ) { return await this . executionContext ( ) . evaluate < UnwrapPromiseLike < EvaluateFnReturnType < T > > > ( pageFunction , this , ... args ) ; } remarks The only difference between `jsHandle.evaluate` and
     * `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle`
     * returns an in-page object (JSHandle).
     *
     * If the function passed to `jsHandle.evaluateHandle` returns a Promise,
     * then `evaluateHandle.evaluateHandle` waits for the promise to resolve and
     * returns its value.
     *
     * See  Page . evaluateHandle for more details.
      async evaluateHandle < HandleType extends JSHandle = JSHandle > ( pageFunction , ... args ) { return await this . executionContext ( ) . evaluateHandle ( pageFunction , this , ... args ) ; } /** Fetches a single property from the referenced object.
     */ 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 ) ; assert ( result instanceof JSHandle ) ; await objectHandle . dispose ( ) ; return result ; } example async getProperties ( ) { const response = await this . _client . send ( 'Runtime.getProperties' , { objectId : this . _remoteObject . objectId , ownProperties : true , } ) ; const result = new Map < string , JSHandle > ( ) ; for ( const property of response . result ) { if ( ! property . enumerable ) continue ; result . set ( property . name , createJSHandle ( this . _context , property . value ) ) ; } return result ; } returns remarks The JSON is generated by running  https * on the object in page and consequent  https in puppeteer.
     * **NOTE** The method throws if the referenced object is not stringifiable.
      async jsonValue < T = unknown > ( ) { if ( this . _remoteObject . objectId ) { const response = await this . _client . send ( 'Runtime.callFunctionOn' , { functionDeclaration : 'function() { return this; }' , objectId : this . _remoteObject . objectId , returnByValue : true , awaitPromise : true , } ) ; return helper . valueFromRemoteObject ( response . result ) as T ; } return helper . valueFromRemoteObject ( this . _remoteObject ) as T ; } returns Either `null` or the object handle itself, if the object
     * handle is an instance of  ElementHandle .
      asElement ( ) { return null ; } /**
     * Stops referencing the element handle, and resolves when the object handle is
     * successfully disposed of.
     */ async dispose ( ) { if ( this . _disposed ) return ; this . _disposed = true ; await helper . releaseObject ( this . _client , this . _remoteObject ) ; } remarks toString ( ) { if ( this . _remoteObject . objectId ) { const type = this . _remoteObject . subtype || this . _remoteObject . type ; return 'JSHandle@' + type ; } return 'JSHandle:' + helper . valueFromRemoteObject ( this . _remoteObject ) ; } } remarks ElementHandles can be created with the  Page . $ method.
 *
 * ```js
 * const puppeteer = require('puppeteer');
 *
 * (async () => {
 *  const browser = await puppeteer.launch();
 *  const page = await browser.newPage();
 *  await page.goto('https://example.com');
 *  const hrefElement = await page.$('a');
 *  await hrefElement.click();
 *  // ...
 * })();
 * ```
 *
 * ElementHandle prevents the DOM element from being garbage-collected unless the
 * handle is  JSHandle . dispose . ElementHandles are auto-disposed
 * when their origin frame gets navigated.
 *
 * ElementHandle instances can be used as arguments in  Page . $eval and
 *  Page . evaluate methods.
 *
 * If you're using TypeScript, ElementHandle takes a generic argument that
 * denotes the type of element the handle is holding within. For example, if you
 * have a handle to a `<select>` element, you can type it as
 * `ElementHandle<HTMLSelectElement>` and you get some nicer type checks.
 *
 *  public export class ElementHandle < ElementType extends Element = Element > extends JSHandle < ElementType > { private _frame : Frame ; private _page : Page ; private _frameManager : FrameManager ; internal constructor ( context , client , remoteObject , frame , page , frameManager ) { super ( context , client , remoteObject ) ; this . _client = client ; this . _remoteObject = remoteObject ; this . _frame = frame ; this . _page = page ; this . _frameManager = frameManager ; } param selector - A
     *  https * of an element to wait for
     *  param options returns remarks The optional parameters in `options` are:
     *
     * - `visible`: wait for the selected element to be present in DOM and to be
     * visible, i.e. to not have `display: none` or `visibility: hidden` CSS
     * properties. Defaults to `false`.
     *
     * - `hidden`: wait for the selected element to not be found in the DOM or to be hidden,
     * i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to
     * `false`.
     *
     * - `timeout`: maximum time to wait in milliseconds. Defaults to `30000`
     * (30 seconds). Pass `0` to disable timeout. The default value can be changed
     * by using the  Page . setDefaultTimeout method.
      async waitForSelector ( selector , options = { } ) { const frame = this . _context . frame ( ) ; const secondaryContext = await frame . _secondaryWorld . executionContext ( ) ; const adoptedRoot = await secondaryContext . _adoptElementHandle ( this ) ; const handle = await frame . _secondaryWorld . waitForSelector ( selector , { ... options , root : adoptedRoot , } ) ; await adoptedRoot . dispose ( ) ; if ( ! handle ) return null ; const mainExecutionContext = await frame . _mainWorld . executionContext ( ) ; const result = await mainExecutionContext . _adoptElementHandle ( handle ) ; await handle . dispose ( ) ; return result ; } asElement ( ) { return this ; } /**
     * Resolves to the content frame for element handles referencing
     * iframe nodes, or null otherwise
     */ async contentFrame ( ) { const nodeInfo = await this . _client . send ( 'DOM.describeNode' , { objectId : this . _remoteObject . objectId , } ) ; if ( typeof nodeInfo . node . frameId !== 'string' ) return null ; return this . _frameManager . frame ( nodeInfo . node . frameId ) ; } private async _scrollIntoViewIfNeeded ( ) { const error = await this . evaluate < ( element : Element , pageJavascriptEnabled : boolean ) => Promise < string | false > > ( async ( element , pageJavascriptEnabled ) => { if ( ! element . isConnected ) return 'Node is detached from document' ; if ( element . nodeType !== Node . ELEMENT_NODE ) return 'Node is not of type HTMLElement' ; if ( ! pageJavascriptEnabled ) { element . scrollIntoView ( { block : 'center' , inline : 'center' , behavior : 'instant' , } ) ; return false ; } const visibleRatio = await new Promise ( ( resolve ) => { const observer = new IntersectionObserver ( ( entries ) => { resolve ( entries [ 0 ] . intersectionRatio ) ; observer . disconnect ( ) ; } ) ; observer . observe ( element ) ; } ) ; if ( visibleRatio !== 1.0 ) { element . scrollIntoView ( { block : 'center' , inline : 'center' , behavior : 'instant' , } ) ; } return false ; } , this . _page . isJavaScriptEnabled ( ) ) ; if ( error ) throw new Error ( error ) ; } /**
     * Returns the middle point within an element unless a specific offset is provided.
     */ async clickablePoint ( offset ? ) { const [ result , layoutMetrics ] = await Promise . all ( [ this . _client . send ( 'DOM.getContentQuads' , { objectId : this . _remoteObject . objectId , } ) . catch ( debugError ) , this . _page . client ( ) . send ( 'Page.getLayoutMetrics' ) , ] ) ; if ( ! result || ! result . quads . length ) throw new Error ( 'Node is either not clickable or not an HTMLElement' ) ; const { clientWidth , clientHeight } = layoutMetrics . cssLayoutViewport || layoutMetrics . layoutViewport ; let offsetX = 0 ; let offsetY = 0 ; let frame = this . _frame ; while ( frame . parentFrame ( ) ) { const parent = frame . parentFrame ( ) ; if ( ! frame . isOOPFrame ( ) ) { frame = parent ; continue ; } const { backendNodeId } = await parent . _client . send ( 'DOM.getFrameOwner' , { frameId : frame . _id , } ) ; const { quads } = await parent . _client . send ( 'DOM.getContentQuads' , { backendNodeId : backendNodeId , } ) ; if ( ! quads || ! quads . length ) { break ; } const protocolQuads = quads . map ( ( quad ) => this . _fromProtocolQuad ( quad ) ) ; const topLeftCorner = protocolQuads [ 0 ] [ 0 ] ; offsetX += topLeftCorner . x ; offsetY += topLeftCorner . y ; frame = parent ; } const quads = result . quads . map ( ( quad ) => this . _fromProtocolQuad ( quad ) ) . map ( ( quad ) => quad . map ( ( part ) => ( { x : part . x + offsetX , y : part . y + offsetY } ) ) ) . map ( ( quad ) => this . _intersectQuadWithViewport ( quad , clientWidth , clientHeight ) ) . filter ( ( quad ) => computeQuadArea ( quad ) > 1 ) ; if ( ! quads . length ) throw new Error ( 'Node is either not clickable or not an HTMLElement' ) ; const quad = quads [ 0 ] ; if ( offset ) { let minX = Number . MAX_SAFE_INTEGER ; let minY = Number . MAX_SAFE_INTEGER ; for ( const point of quad ) { if ( point . x < minX ) { minX = point . x ; } if ( point . y < minY ) { minY = point . y ; } } if ( minX !== Number . MAX_SAFE_INTEGER && minY !== Number . MAX_SAFE_INTEGER ) { return { x : minX + offset . x , y : minY + offset . y , } ; } } let x = 0 ; let y = 0 ; for ( const point of quad ) { x += point . x ; y += point . y ; } return { x : x / 4 , y : y / 4 , } ; } private _getBoxModel ( ) { const params = { objectId : this . _remoteObject . objectId , } ; return this . _client . send ( 'DOM.getBoxModel' , params ) . catch ( ( error ) => debugError ( error ) ) ; } private _fromProtocolQuad ( quad ) { return [ { x : quad [ 0 ] , y : quad [ 1 ] } , { x : quad [ 2 ] , y : quad [ 3 ] } , { x : quad [ 4 ] , y : quad [ 5 ] } , { x : quad [ 6 ] , y : quad [ 7 ] } , ] ; } private _intersectQuadWithViewport ( quad , width , height ) { return quad . map ( ( point ) => ( { x : Math . min ( Math . max ( point . x , 0 ) , width ) , y : Math . min ( Math . max ( point . y , 0 ) , height ) , } ) ) ; } /**
     * This method scrolls element into view if needed, and then
     * uses  Page . mouse to hover over the center of the element.
     * If the element is detached from DOM, the method throws an error.
      async hover ( ) { await this . _scrollIntoViewIfNeeded ( ) ; const { x , y } = await this . clickablePoint ( ) ; await this . _page . mouse . move ( x , y ) ; } /**
     * This method scrolls element into view if needed, and then
     * uses  Page . mouse to click in the center of the element.
     * If the element is detached from DOM, the method throws an error.
      async click ( options = { } ) { await this . _scrollIntoViewIfNeeded ( ) ; const { x , y } = await this . clickablePoint ( options . offset ) ; await this . _page . mouse . click ( x , y , options ) ; } /**
     * This method creates and captures a dragevent from the element.
     */ async drag ( target ) { assert ( this . _page . isDragInterceptionEnabled ( ) , 'Drag Interception is not enabled!' ) ; await this . _scrollIntoViewIfNeeded ( ) ; const start = await this . clickablePoint ( ) ; return await this . _page . mouse . drag ( start , target ) ; } /**
     * This method creates a `dragenter` event on the element.
     */ async dragEnter ( data = { items : [ ] , dragOperationsMask : 1 } ) { await this . _scrollIntoViewIfNeeded ( ) ; const target = await this . clickablePoint ( ) ; await this . _page . mouse . dragEnter ( target , data ) ; } /**
     * This method creates a `dragover` event on the element.
     */ async dragOver ( data = { items : [ ] , dragOperationsMask : 1 } ) { await this . _scrollIntoViewIfNeeded ( ) ; const target = await this . clickablePoint ( ) ; await this . _page . mouse . dragOver ( target , data ) ; } /**
     * This method triggers a drop on the element.
     */ async drop ( data = { items : [ ] , dragOperationsMask : 1 } ) { await this . _scrollIntoViewIfNeeded ( ) ; const destination = await this . clickablePoint ( ) ; await this . _page . mouse . drop ( destination , data ) ; } /**
     * This method triggers a dragenter, dragover, and drop on the element.
     */ async dragAndDrop ( target , options ? ) { await this . _scrollIntoViewIfNeeded ( ) ; const startPoint = await this . clickablePoint ( ) ; const targetPoint = await target . clickablePoint ( ) ; await this . _page . mouse . dragAndDrop ( startPoint , targetPoint , options ) ; } example param values async select ( ... values ) { for ( const value of values ) assert ( helper . isString ( value ) , 'Values must be strings. Found value "' + value + '" of type "' + typeof value + '"' ) ; return this . evaluate < ( element : Element , values : string [ ] ) => string [ ] > ( ( element , values ) => { if ( ! ( element instanceof HTMLSelectElement ) ) throw new Error ( 'Element is not a <select> element.' ) ; const options = Array . from ( element . options ) ; element . value = undefined ; for ( const option of options ) { option . selected = values . includes ( option . value ) ; if ( option . selected && ! element . multiple ) break ; } element . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ; element . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) ; return options . filter ( ( option ) => option . selected ) . map ( ( option ) => option . value ) ; } , values ) ; } /**
     * This method expects `elementHandle` to point to an
     *  https .
     *  param filePaths - Sets the value of the file input to these paths.
     *    If some of the  `filePaths` are relative paths, then they are resolved
     *    relative to the  https async uploadFile ( ... filePaths ) { const isMultiple = await this . evaluate < ( element : Element ) => boolean > ( ( element ) => { if ( ! ( element instanceof HTMLInputElement ) ) { throw new Error ( 'uploadFile can only be called on an input element.' ) ; } return element . multiple ; } ) ; assert ( filePaths . length <= 1 || isMultiple , 'Multiple file uploads only work with <input type=file multiple>' ) ; if ( ! isNode ) { throw new Error ( `JSHandle#uploadFile can only be used in Node environments.` ) ; } const path = await import ( 'path' ) ; const fs = await helper . importFSModule ( ) ; const files = await Promise . all ( filePaths . map ( async ( filePath ) => { const resolvedPath = path . resolve ( filePath ) ; try { await fs . promises . access ( resolvedPath , fs . constants . R_OK ) ; } catch ( error ) { if ( error . code === 'ENOENT' ) throw new Error ( `${ filePath } does not exist or is not readable` ) ; } return resolvedPath ; } ) ) ; const { objectId } = this . _remoteObject ; const { node } = await this . _client . send ( 'DOM.describeNode' , { objectId } ) ; const { backendNodeId } = node ; if ( files . length === 0 ) { await ( this as ElementHandle < HTMLInputElement > ) . evaluate ( ( element ) => { element . files = new DataTransfer ( ) . files ; element . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ; element . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) ; } ) ; } else { await this . _client . send ( 'DOM.setFileInputFiles' , { objectId , files , backendNodeId , } ) ; } } /**
     * This method scrolls element into view if needed, and then uses
     *  Touchscreen . tap to tap in the center of the element.
     * If the element is detached from DOM, the method throws an error.
      async tap ( ) { await this . _scrollIntoViewIfNeeded ( ) ; const { x , y } = await this . clickablePoint ( ) ; await this . _page . touchscreen . tap ( x , y ) ; } /**
     * Calls  https on the element.
      async focus ( ) { await ( this as ElementHandle < HTMLElement > ) . evaluate ( ( element ) => element . focus ( ) ) ; } /**
     * Focuses the element, and then sends a `keydown`, `keypress`/`input`, and
     * `keyup` event for each character in the text.
     *
     * To press a special key, like `Control` or `ArrowDown`,
     * use  ElementHandle . press .
     *
     *  example example async type ( text , options ? ) { await this . focus ( ) ; await this . _page . keyboard . type ( text , options ) ; } /**
     * Focuses the element, and then uses  Keyboard . down and  Keyboard . up .
     *
     *  remarks param key - Name of key to press, such as `ArrowLeft`.
     *    See  KeyInput for a list of all key names.
      async press ( key , options ? ) { await this . focus ( ) ; await this . _page . keyboard . press ( key , options ) ; } /**
     * This method returns the bounding box of the element (relative to the main frame),
     * or `null` if the element is not visible.
     */ async boundingBox ( ) { const result = await this . _getBoxModel ( ) ; if ( ! result ) return null ; const quad = result . model . border ; const x = Math . min ( quad [ 0 ] , quad [ 2 ] , quad [ 4 ] , quad [ 6 ] ) ; const y = Math . min ( quad [ 1 ] , quad [ 3 ] , quad [ 5 ] , quad [ 7 ] ) ; const width = Math . max ( quad [ 0 ] , quad [ 2 ] , quad [ 4 ] , quad [ 6 ] ) - x ; const height = Math . max ( quad [ 1 ] , quad [ 3 ] , quad [ 5 ] , quad [ 7 ] ) - y ; return { x , y , width , height } ; } remarks async boxModel ( ) { const result = await this . _getBoxModel ( ) ; if ( ! result ) return null ; const { content , padding , border , margin , width , height } = result . model ; return { content : this . _fromProtocolQuad ( content ) , padding : this . _fromProtocolQuad ( padding ) , border : this . _fromProtocolQuad ( border ) , margin : this . _fromProtocolQuad ( margin ) , width , height , } ; } /**
     * This method scrolls element into view if needed, and then uses
     *  Page . screenshot to take a screenshot of the element.
     * If the element is detached from DOM, the method throws an error.
      async screenshot ( options = { } ) { let needsViewportReset = false ; let boundingBox = await this . boundingBox ( ) ; assert ( boundingBox , 'Node is either not visible or not an HTMLElement' ) ; const viewport = this . _page . viewport ( ) ; if ( viewport && ( boundingBox . width > viewport . width || boundingBox . height > viewport . height ) ) { const newViewport = { width : Math . max ( viewport . width , Math . ceil ( boundingBox . width ) ) , height : Math . max ( viewport . height , Math . ceil ( boundingBox . height ) ) , } ; await this . _page . setViewport ( Object . assign ( { } , viewport , newViewport ) ) ; needsViewportReset = true ; } await this . _scrollIntoViewIfNeeded ( ) ; boundingBox = await this . boundingBox ( ) ; assert ( boundingBox , 'Node is either not visible or not an HTMLElement' ) ; assert ( boundingBox . width !== 0 , 'Node has 0 width.' ) ; assert ( boundingBox . height !== 0 , 'Node has 0 height.' ) ; const layoutMetrics = await this . _client . send ( 'Page.getLayoutMetrics' ) ; const { pageX , pageY } = layoutMetrics . cssLayoutViewport || layoutMetrics . layoutViewport ; const clip = Object . assign ( { } , boundingBox ) ; clip . x += pageX ; clip . y += pageY ; const imageData = await this . _page . screenshot ( Object . assign ( { } , { clip , } , options ) ) ; if ( needsViewportReset ) await this . _page . setViewport ( viewport ) ; return imageData ; } /**
     * Runs `element.querySelector` within the page. If no element matches the selector,
     * the return value resolves to `null`.
     */ async $ < T extends Element = Element > ( selector ) { const { updatedSelector , queryHandler } = getQueryHandlerAndSelector ( selector ) ; return queryHandler . queryOne ( this , updatedSelector ) ; } /**
     * Runs `element.querySelectorAll` within the page. If no elements match the selector,
     * the return value resolves to `[]`.
     */ async $$ < T extends Element = Element > ( selector ) { const { updatedSelector , queryHandler } = getQueryHandlerAndSelector ( selector ) ; return queryHandler . queryAll ( this , updatedSelector ) ; } example async $eval < ReturnType > ( selector , pageFunction , ... args ) { const elementHandle = await this . $ ( selector ) ; if ( ! elementHandle ) throw new Error ( `Error: failed to find element matching selector "${ selector }"` ) ; const result = await elementHandle . evaluate < ( element : Element , ... args : SerializableOrJSHandle [ ] ) => ReturnType | Promise < ReturnType > > ( pageFunction , ... args ) ; await elementHandle . dispose ( ) ; /**
         * This `as` is a little unfortunate but helps TS understand the behavior of
         * `elementHandle.evaluate`. If evaluate returns an element it will return an
         * ElementHandle instance, rather than the plain object. All the
         * WrapElementHandle type does is wrap ReturnType into
         * ElementHandle<ReturnType> if it is an ElementHandle, or leave it alone as
         * ReturnType if it isn't.
         */ return result as WrapElementHandle < ReturnType > ; } example example async $$eval < ReturnType > ( selector , pageFunction , ... args ) { const { updatedSelector , queryHandler } = getQueryHandlerAndSelector ( selector ) ; const arrayHandle = await queryHandler . queryAllArray ( this , updatedSelector ) ; const result = await arrayHandle . evaluate < ( elements : Element [ ] , ... args : unknown [ ] ) => ReturnType | Promise < ReturnType > > ( pageFunction , ... args ) ; await arrayHandle . dispose ( ) ; return result as WrapElementHandle < ReturnType > ; } param expression - Expression to  https async $x ( expression ) { const arrayHandle = await this . evaluateHandle ( ( element , expression ) => { const document = element . ownerDocument || element ; const iterator = document . evaluate ( expression , element , null , XPathResult . ORDERED_NODE_ITERATOR_TYPE ) ; const array = [ ] ; let item ; while ( ( item = iterator . iterateNext ( ) ) ) array . push ( item ) ; return array ; } , expression ) ; const properties = await arrayHandle . getProperties ( ) ; await arrayHandle . dispose ( ) ; const result = [ ] ; for ( const property of properties . values ( ) ) { const elementHandle = property . asElement ( ) ; if ( elementHandle ) result . push ( elementHandle ) ; } return result ; } /**
     * Resolves to true if the element is visible in the current viewport.
     */ async isIntersectingViewport ( options ? ) { const { threshold = 0 } = options || { } ; return await this . evaluate ( async ( element , threshold ) => { const visibleRatio = await new Promise < number > ( ( resolve ) => { const observer = new IntersectionObserver ( ( entries ) => { resolve ( entries [ 0 ] . intersectionRatio ) ; observer . disconnect ( ) ; } ) ; observer . observe ( element ) ; } ) ; return threshold === 1 ? visibleRatio === 1 : visibleRatio > threshold ; } , threshold ) ; } } public export interface Offset { /**
     * x-offset for the clickable point relative to the top-left corder of the border box.
     */ x : number ; /**
     * y-offset for the clickable point relative to the top-left corder of the border box.
     */ y : number ; } public export interface ClickOptions { defaultValue delay ? : number ; defaultValue button ? : 'left' | 'right' | 'middle' ; defaultValue clickCount ? : number ; /**
     * Offset for the clickable point relative to the top-left corder of the border box.
     */ offset ? : Offset ; } public export interface PressOptions { /**
     * Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
     */ delay ? : number ; /**
     * If specified, generates an input event with this text.
     */ text ? : string ; } public export interface Point { x : number ; y : number ; } function computeQuadArea ( quad ) { let area = 0 ; for ( let i = 0 ; i < quad . length ; ++ i ) { const p1 = quad [ i ] ; const p2 = quad [ ( i + 1 ) % quad . length ] ; area += ( p1 . x * p2 . y - p2 . x * p1 . y ) / 2 ; } return Math . abs ( area ) ; }