From 7644a1363f67f72c45fa817902d3546e39702864 Mon Sep 17 00:00:00 2001 From: jebbs Date: Wed, 15 Jan 2020 17:53:23 +0800 Subject: [PATCH] Extractor.watch() --- src/background/caches.ts | 9 ++++- src/background/extractor.ts | 15 ++++++-- src/background/messaging.ts | 47 ++++++++++++++++++------ src/background/task.ts | 72 ++++++++++++++++++++++++++++--------- src/common.ts | 6 +++- src/content/index.ts | 4 +++ 6 files changed, 122 insertions(+), 31 deletions(-) diff --git a/src/background/caches.ts b/src/background/caches.ts index b99f5fb..1ee79a1 100644 --- a/src/background/caches.ts +++ b/src/background/caches.ts @@ -1,8 +1,15 @@ import { logger } from "./common"; +import { Actions } from "../common"; +import { messageSubscribers } from "./messaging"; export class Caches { private _state: string = ""; - constructor() { } + constructor() { + messageSubscribers.addListener(Actions.UPLOAD_STATE, (request, sender, sendResponse) => { + sendResponse('recieved!'); + this.setState(request.fileName, request.state) + }); + } get state(): string { let s = this._state; this._state = ""; diff --git a/src/background/extractor.ts b/src/background/extractor.ts index cd72c96..59f2391 100644 --- a/src/background/extractor.ts +++ b/src/background/extractor.ts @@ -54,11 +54,22 @@ export class Extractor { start() { return this._startTasks(0); } - stop() { + stop(id?: number) { + if (id !== undefined) { + id = this._checkTaskId(id); + if (id < 0) return; + this._tasks[id].stop(); + return; + } for (let i = 0; i < this._tasks.length; i++) { this._tasks[i].stop(); } } + watch(id: number) { + id = this._checkTaskId(id); + if (id < 0) return; + this._tasks[id].watch(); + } /** * restart from specified task, but don't restart the previous tasks. * @param {number} from where to restart the tasks, begins with 0 @@ -138,7 +149,7 @@ ${exResults.toString(50) || "- Empty -"} saveFile(exResults.toString(), "text/csv"); } } - _checkTaskId(id: number, defaultId: number) { + _checkTaskId(id: number, defaultId?: number) { if (!this._tasks.length) { logger.info("No task found."); return -1; diff --git a/src/background/messaging.ts b/src/background/messaging.ts index 5cec0f0..59ac0ca 100644 --- a/src/background/messaging.ts +++ b/src/background/messaging.ts @@ -1,6 +1,6 @@ import { Request, Actions } from "../common"; import { getTabByID } from "./actions"; -import { caches, logger } from "./common"; +import { logger } from "./common"; /** * Sending a message to target tab repeatedly until the response is not undefined. @@ -78,14 +78,41 @@ export function sendMessage( }); } -chrome.runtime.onMessage.addListener(function (request: Request, sender, sendResponse) { - switch (request.action) { - case Actions.UPLOAD_STATE: - sendResponse('recieved!'); - caches.setState(request.fileName, request.state) - break; - default: - sendResponse("Request not supported."); - break; +export type ActionSubscriber = (request: Request, sender: chrome.runtime.MessageSender, sendResponse: (response?: any) => void) => void | Promise; +class MessageSubscribers { + private listeners: { [key: number]: ActionSubscriber[] } = {}; + addListener(action: Actions, subscriber: ActionSubscriber) { + this.listeners[action] || (this.listeners[action] = []); + this.listeners[action].push(subscriber); } + removeListener(action: Actions, subscriber: ActionSubscriber) { + this.listeners[action] || (this.listeners[action] = []); + for (let i = 0; i < this.listeners[action].length; i++) { + if (this.listeners[action][i] == subscriber) { + this.listeners[action].splice(i, 1); + i--; + } + } + logger.debug(`${this.listeners[action].length} subscriber(s) remained for action ${Actions[action]}`); + } + getListeners(action: Actions): ActionSubscriber[] { + return this.listeners[action] + } +} +export const messageSubscribers = new MessageSubscribers(); + +chrome.runtime.onMessage.addListener(function (request: Request, sender, sendResponse) { + let subscribers = messageSubscribers.getListeners(request.action); + if (!subscribers || !subscribers.length) { + sendResponse("Request not supported."); + return; + } + let promises: Promise[] = []; + for (let subscriber of subscribers) { + let p = subscriber(request, sender, sendResponse); + if (p instanceof Promise) promises.push(p); + } + if (promises.length) + return Promise.all(promises); + return; }); diff --git a/src/background/task.ts b/src/background/task.ts index b99f91d..d77a37f 100644 --- a/src/background/task.ts +++ b/src/background/task.ts @@ -2,6 +2,9 @@ import { parseUrls } from "./tools"; import { queryUrl, redirectTab, scrollToBottom, extractTabData } from "./actions"; import { testArgs, signitures } from "./signiture"; import { ExtractResult } from "./result"; +import { messageSubscribers, ActionSubscriber } from "./messaging"; +import { Actions } from "../common"; +import { logger } from "./common"; export class Task { private _data: { [key: string]: string[][] } = {}; @@ -46,12 +49,22 @@ export class Task { return this._fieldSelectors; } clean(): Task { + this.stop(); this._data = {}; this._data_keys = []; return this; } stop() { this._running = false; + messageSubscribers.removeListener(Actions.REPORT_NEW_PAGE, this.listener); + } + watch() { + if (this._running) { + logger.info("The task is running. Please wait..."); + return; + } + this._running = true; + messageSubscribers.addListener(Actions.REPORT_NEW_PAGE, this.listener); } async execute(tab: chrome.tabs.Tab, upstreamData?: ExtractResult): Promise { if (!tab) return Promise.reject("No tab to execute the task."); @@ -65,35 +78,26 @@ export class Task { urls = [await queryUrl(tab)]; } } - let saveResult = (results, key) => { - this._data[key] = results; - this._data_keys.push(key); - } - let runningCheck = (fn: () => Promise): Promise => { - if (!this._running) return Promise.reject("The task is stopped by user."); - return fn(); - } return urls.reduce((p, url, i) => p.then( results => { if (i > 0 && results instanceof Array) { let lastURL = urls[i - 1]; - saveResult(results, lastURL); + this.saveResult(results, lastURL); } if (this._data[url]) return; - let pms: Promise = runningCheck(() => redirectTab(tab, url)); - if (this._options["scrollToBottom"]) { - pms = pms.then(() => runningCheck(() => scrollToBottom(tab))); - } - return pms.then( - () => runningCheck(() => extractTabData(tab, this._itemsSelector, this._fieldSelectors)) - ); + let pms: Promise = this.runningCheck(() => redirectTab(tab, url)); + return pms + .then(() => this.makeOptionalTasks(tab)) + .then( + () => this.runningCheck(() => extractTabData(tab, this._itemsSelector, this._fieldSelectors)) + ); } ), Promise.resolve(null)).then( results => { if (results && results.length) { let lastURL = urls[urls.length - 1]; - saveResult(results, lastURL); + this.saveResult(results, lastURL); this._running = false; } } @@ -104,4 +108,38 @@ export class Task { } ); } + private listener: ActionSubscriber = (request, sender, sendResponse) => { + sendResponse('recieved!'); + let pm = this.makeOptionalTasks(sender.tab); + return pm.then( + () => extractTabData(sender.tab, this._itemsSelector, this._fieldSelectors) + ).then( + results => { + if (results && results.length) { + this.saveResult(results, sender.tab.url); + } + } + ).catch( + e => logger.error(e) + ) + } + private makeOptionalTasks(tab: chrome.tabs.Tab): Promise { + let pm: Promise; + if (this._options["scrollToBottom"]) { + pm = this.runningCheck(() => scrollToBottom(tab)); + } + return pm; + } + private runningCheck(fn: () => Promise): Promise { + if (!this._running) return Promise.reject("The task is stopped by user."); + return fn(); + } + private saveResult(results, key) { + if (this._data[key] === undefined) { + // do not add keys again + this._data_keys.push(key); + } + this._data[key] = results; + logger.info(`${results.length} items found.`) + } } \ No newline at end of file diff --git a/src/common.ts b/src/common.ts index 421f040..58f5e7a 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,12 +1,16 @@ export enum Actions { + // from background to content script EXTRACT = 1, GOTO_URL, PING, QUERY_URL, SCROLL_BOTTOM, - UPLOAD_STATE, SLEEP, WAKEUP, + // from popup to background script + UPLOAD_STATE, + // from content to background script + REPORT_NEW_PAGE, } export interface Request { diff --git a/src/content/index.ts b/src/content/index.ts index 0d00a28..cb0d491 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -16,6 +16,10 @@ chrome.runtime.onMessage.addListener( } ); +chrome.runtime.sendMessage({ + action: Actions.REPORT_NEW_PAGE, +}); + async function doAction(request: Request, sender: chrome.runtime.MessageSender) { switch (request.action) { case Actions.EXTRACT: