/** * 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. */ import * as types from '../types'; type Predicate = () => T; export type InjectedResult = (T extends undefined ? { status: 'success', value?: T} : { status: 'success', value: T }) | { status: 'notconnected' } | { status: 'timeout' } | { status: 'error', error: string }; export class Injected { isVisible(element: Element): boolean { // Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises. if (!element.ownerDocument || !element.ownerDocument.defaultView) return true; const style = element.ownerDocument.defaultView.getComputedStyle(element); if (!style || style.visibility === 'hidden') return false; const rect = element.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } private _pollMutation(predicate: Predicate, timeout: number): Promise { let timedOut = false; if (timeout) setTimeout(() => timedOut = true, timeout); const success = predicate(); if (success) return Promise.resolve(success); let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); const observer = new MutationObserver(() => { if (timedOut) { observer.disconnect(); fulfill(); return; } const success = predicate(); if (success) { observer.disconnect(); fulfill(success); } }); observer.observe(document, { childList: true, subtree: true, attributes: true }); return result; } private _pollRaf(predicate: Predicate, timeout: number): Promise { let timedOut = false; if (timeout) setTimeout(() => timedOut = true, timeout); let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); const onRaf = () => { if (timedOut) { fulfill(); return; } const success = predicate(); if (success) fulfill(success); else requestAnimationFrame(onRaf); }; onRaf(); return result; } private _pollInterval(pollInterval: number, predicate: Predicate, timeout: number): Promise { let timedOut = false; if (timeout) setTimeout(() => timedOut = true, timeout); let fulfill: (result?: any) => void; const result = new Promise(x => fulfill = x); const onTimeout = () => { if (timedOut) { fulfill(); return; } const success = predicate(); if (success) fulfill(success); else setTimeout(onTimeout, pollInterval); }; onTimeout(); return result; } poll(polling: 'raf' | 'mutation' | number, timeout: number, predicate: Predicate): Promise { if (polling === 'raf') return this._pollRaf(predicate, timeout); if (polling === 'mutation') return this._pollMutation(predicate, timeout); return this._pollInterval(polling, predicate, timeout); } getElementBorderWidth(node: Node): { left: number; top: number; } { if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView) return { left: 0, top: 0 }; const style = node.ownerDocument.defaultView.getComputedStyle(node as Element); return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) }; } selectOptions(node: Node, optionsToSelect: (Node | types.SelectOption)[]): InjectedResult { if (node.nodeName.toLowerCase() !== 'select') return { status: 'error', error: 'Element is not a ,