import { Actions, Request } from "../common"; import { sendMessage, ResponseChecker } from "./messaging"; import { logger } from "./logger"; /** * redirect tab to url. * @param {any} tab target tab * @param {string} url target URL * @returns {Promise} a promise of target URL */ export function redirectTab(tab: chrome.tabs.Tab, url: string) { return queryUrl(tab).then(u => { if (url !== u) { let req: Request = { action: Actions.GOTO_URL, url: url } let checker: ResponseChecker = async (r, err, tryCount): Promise => { let queryErr: any; let newURL = await queryUrl(tab).catch(e => queryErr = e); if (queryErr) { throw queryErr; } if (newURL == url) return url; if ( tryCount % 1 == 0 && !confirm('Cannot navigate to target url. \nPress OK to continue, Cancel to stop.') ) { throw "Tasks stopped by user."; } return undefined; } return sendMessage(tab, req, `Goto url: ${url}`, checker); } }); } /** * extract data in from the target tab. * @param {any} tab target tab * @param {string} itemsSelector items selectors for selecting items (data rows) * @param {Array} fieldSelectors fields selectors for selecting fields (data columns) under each item * @returns {Promise} a promise of extracted data */ export function extractTabData(tab: chrome.tabs.Tab, itemsSelector: string, fieldSelectors: string[], expectedURL?: string, askOnfail?: boolean) { let req: Request = { action: Actions.EXTRACT, itemsSelector: itemsSelector, fieldSelectors: fieldSelectors, url: expectedURL, } let checker: ResponseChecker = (response, err, tryCount) => { if (response.error) throw response.error; let result = response.result; if (!result || !result.length) { if ( tryCount % 20 == 0 && ( !askOnfail || confirm('No data found in current page. \n\nContinue to next page?') ) ) { logger.warn(`Failed after ${tryCount} tries: ${tab.url}`) return []; } else { return undefined; } } return result; }; return sendMessage(tab, req, 'Extract data from the tab...', checker); } /** * ping target tab, usually used to detect if the content script is ready. * @param {any} tab target tab * @returns {Promise} a promise of boolean value indicates if ping success */ export async function ping(tab, count = 1) { let req = { action: Actions.PING } let checker: ResponseChecker = (r, e, c) => r.result == "pong" ? r.result : undefined; let pong = await sendMessage(tab, req, 'Check tab availability...', checker, 1000, count).catch(() => { }); return pong == "pong"; } /** * get the url of the target tab * @param {any} tab target tab * @returns {Promise} a promise of the url */ export function queryUrl(tab: chrome.tabs.Tab) { let req = { action: Actions.QUERY_URL } return sendMessage(tab, req); } /** * get the url of the target tab * @param {any} tab target tab * @param {string} expected if specified, queryUrl resolves only when tab url equals to expected * @returns {Promise} a promise of the url */ export function scrollToBottom(tab: chrome.tabs.Tab) { let req = { action: Actions.SCROLL_BOTTOM } return sendMessage(tab, req, 'Scroll to page bottom...'); } export async function createTab(url: string, active: boolean) { return new Promise((resolve, reject) => { findIncognitoWindow().then( incognitoWindow => { chrome.tabs.create({ 'url': url, 'active': active, // createTab to incognito window first 'windowId': incognitoWindow ? incognitoWindow.id : undefined }, function (tab) { resolve(tab); }) } ); }); } export async function findIncognitoWindow(): Promise { return new Promise((resolve, reject) => { chrome.windows.getAll( { windowTypes: ['normal'], }, (windows: chrome.windows.Window[]) => { for (let window of windows) { if (window.incognito) { resolve(window); return; } } resolve(undefined); } ); }); } export async function getCurrentWindow(): Promise { return new Promise((resolve, reject) => { chrome.windows.getCurrent( (windows: chrome.windows.Window) => { return resolve(windows); } ); }); } export async function getWindowByID(id: number) { return new Promise((resolve, reject) => { chrome.windows.get(id, function (window) { chrome.runtime.lastError; resolve(window); }) }) } export async function CreateIncognitoWindow() { return new Promise((resolve, reject) => { chrome.windows.create( { incognito: true, }, (window: chrome.windows.Window) => { resolve(window); } ); }); } export async function getActiveTab(currentWindow: boolean) { return new Promise((resolve, reject) => { chrome.tabs.query({ active: true, currentWindow: currentWindow }, function (tabs) { resolve(tabs[0]); }) }) } export async function getTabByID(id: number) { return new Promise((resolve, reject) => { chrome.tabs.get(id, function (tab) { chrome.runtime.lastError; resolve(tab); }) }) }