UNPKG

@cliqz/autoconsent

Version:

This is a library of rules for navigating through common consent popups on the web. These rules can be run in a Firefox webextension, or in a puppeteer orchestrated headless browser. Using these rules, opt-in and opt-out options can be selected automatica

173 lines (151 loc) 4.47 kB
import Tab from './web/tab'; import handleContentMessage from './web/content'; import { rules, createAutoCMP } from './index'; import { Browser, MessageSender, AutoCMP, TabActor } from './types'; import { ConsentOMaticCMP, ConsentOMaticConfig } from './consentomatic/index'; import { AutoConsentCMPRule } from './rules'; export * from './index'; export { Tab, handleContentMessage, } class TabConsent { checked: Promise<AutoCMP> rule: AutoCMP optOutStatus: boolean | Error = null constructor(public tab: TabActor, ruleCheckPromise: Promise<AutoCMP>) { this.checked = ruleCheckPromise; ruleCheckPromise.then(rule => this.rule = rule); } getCMPName() { if (this.rule) { return this.rule.name; } return null; } async isPopupOpen(retries = 1, interval = 1000) { const isOpen = await this.rule.detectPopup(this.tab); if (!isOpen && retries > 0) { return new Promise((resolve) => setTimeout(() => resolve(this.isPopupOpen(retries - 1, interval)), interval)); } return isOpen; } async doOptOut() { try { this.optOutStatus = await this.rule.optOut(this.tab); return this.optOutStatus; } catch (e) { this.optOutStatus = e; throw e; } } async doOptIn() { return this.rule.optIn(this.tab); } hasTest() { return !!this.rule.hasSelfTest } async testOptOutWorked() { return this.rule.test(this.tab); } async applyCosmetics(selectors: string[]) { const hidden = await this.tab.hideElements(selectors); return hidden; } } export default class AutoConsent { consentFrames: Map<number, any> = new Map() tabCmps: Map<number, TabConsent> = new Map() rules: AutoCMP[] constructor(protected browser: Browser, protected sendContentMessage: MessageSender) { this.sendContentMessage = sendContentMessage; this.rules = [...rules]; } addCMP(config: AutoConsentCMPRule) { this.rules.push(createAutoCMP(config)); } disableCMPs(cmpNames: String[]) { this.rules = this.rules.filter((cmp) => !cmpNames.includes(cmp.name)) } addConsentomaticCMP(name: string, config: ConsentOMaticConfig) { this.rules.push(new ConsentOMaticCMP(`com_${name}`, config)); } createTab(tabId: number) { return new Tab(tabId, this.consentFrames.get(tabId), this.sendContentMessage, this.browser); } async checkTab(tabId: number) { const tab = this.createTab(tabId); const consent = new TabConsent(tab, this.detectDialog(tab, 20)); this.tabCmps.set(tabId, consent); // check tabs consent.checked.then((rule) => { if (this.consentFrames.has(tabId) && rule) { const frame = this.consentFrames.get(tabId); if (frame.type === rule.name) { consent.tab.frame = frame; } } }); return this.tabCmps.get(tabId); } removeTab(tabId: number) { this.tabCmps.delete(tabId); this.consentFrames.delete(tabId); } onFrame({ tabId, url, frameId }: { tabId: number, url: string, frameId: number }) { // ignore main frames if (frameId === 0) { return; } try { const frame = { id: frameId, url: url, }; const tab = this.createTab(tabId); const frameMatch = this.rules.findIndex(r => r.detectFrame(tab, frame)); if (frameMatch > -1) { this.consentFrames.set(tabId, { type: this.rules[frameMatch].name, url, id: frameId, }); if (this.tabCmps.has(tabId)) { this.tabCmps.get(tabId).tab.frame = this.consentFrames.get(tabId); } } } catch (e) { console.error(e); } } async detectDialog(tab: TabActor, retries: number): Promise<AutoCMP> { const found: number = await new Promise(async (resolve) => { let earlyReturn = false; await Promise.all(this.rules.map(async (r, index) => { try { if (await r.detectCmp(tab)) { earlyReturn = true; resolve(index) } } catch (e) { console.warn('detectCMP error', r.name, e) } })); if (!earlyReturn) { resolve(-1) } }) if (found === -1 && retries > 0) { return new Promise((resolve) => { setTimeout(async () => { const result = this.detectDialog(tab, retries - 1); resolve(result); }, 500); }); } return found > -1 ? this.rules[found] : null; } }