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

1,360 lines (1,341 loc) 51.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. 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 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } /* eslint-disable no-restricted-syntax,no-await-in-loop,no-underscore-dangle */ function waitFor(predicate, maxTimes, interval) { return __awaiter(this, void 0, void 0, function* () { let result = false; try { result = yield predicate(); } catch (e) { console.warn('error in waitFor predicate', e); } if (!result && maxTimes > 0) { return new Promise((resolve) => { setTimeout(() => __awaiter(this, void 0, void 0, function* () { resolve(waitFor(predicate, maxTimes - 1, interval)); }), interval); }); } return Promise.resolve(result); }); } class AutoConsentBase { constructor(name) { this.hasSelfTest = true; this.name = name; } detectCmp(tab) { throw new Error('Not Implemented'); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { return false; }); } detectFrame(tab, frame) { return false; } optOut(tab) { throw new Error('Not Implemented'); } optIn(tab) { throw new Error('Not Implemented'); } openCmp(tab) { throw new Error('Not Implemented'); } test(tab) { return __awaiter(this, void 0, void 0, function* () { // try IAB by default yield tab.eval('__cmp(\'getVendorConsents\', undefined, r => window.__rcsResult = r)'); return tab.eval('Object.values(window.__rcsResult.purposeConsents).every(c => !c)'); }); } } function evaluateRule(rule, tab) { return __awaiter(this, void 0, void 0, function* () { if (rule.frame && !tab.frame) { yield waitFor(() => Promise.resolve(!!tab.frame), 10, 500); } const frameId = rule.frame ? tab.frame.id : undefined; const results = []; if (rule.exists) { results.push(tab.elementExists(rule.exists, frameId)); } if (rule.visible) { results.push(tab.elementsAreVisible(rule.visible, rule.check, frameId)); } if (rule.eval) { results.push(new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { // catch eval error silently try { resolve(yield tab.eval(rule.eval, frameId)); } catch (e) { resolve(false); } }))); } if (rule.waitFor) { results.push(tab.waitForElement(rule.waitFor, rule.timeout || 10000, frameId)); } if (rule.click) { if (rule.all === true) { results.push(tab.clickElements(rule.click, frameId)); } else { results.push(tab.clickElement(rule.click, frameId)); } } if (rule.waitForThenClick) { results.push(tab.waitForElement(rule.waitForThenClick, rule.timeout || 10000, frameId) .then(() => tab.clickElement(rule.waitForThenClick, frameId))); } if (rule.wait) { results.push(tab.wait(rule.wait)); } if (rule.url) { results.push(Promise.resolve(tab.url.startsWith(rule.url))); } if (rule.goto) { results.push(tab.goto(rule.goto)); } if (rule.hide) { results.push(tab.hideElements(rule.hide, frameId)); } // boolean and of results return (yield Promise.all(results)).reduce((a, b) => a && b, true); }); } class AutoConsent extends AutoConsentBase { constructor(config) { super(config.name); this.config = config; } _runRulesParallel(tab, rules) { return __awaiter(this, void 0, void 0, function* () { const detections = yield Promise.all(rules.map(rule => evaluateRule(rule, tab))); return detections.some(r => !!r); }); } _runRulesSequentially(tab, rules) { return __awaiter(this, void 0, void 0, function* () { for (const rule of rules) { const result = yield evaluateRule(rule, tab); if (!result && !rule.optional) { return false; } } return true; }); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { if (this.config.detectCmp) { return this._runRulesParallel(tab, this.config.detectCmp); } return false; }); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { if (this.config.detectPopup) { return this._runRulesParallel(tab, this.config.detectPopup); } return false; }); } detectFrame(tab, frame) { if (this.config.frame) { return frame.url.startsWith(this.config.frame); } return false; } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { if (this.config.optOut) { return this._runRulesSequentially(tab, this.config.optOut); } return false; }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (this.config.optIn) { return this._runRulesSequentially(tab, this.config.optIn); } return false; }); } openCmp(tab) { return __awaiter(this, void 0, void 0, function* () { if (this.config.openCmp) { return this._runRulesSequentially(tab, this.config.openCmp); } return false; }); } test(tab) { const _super = Object.create(null, { test: { get: () => super.test } }); return __awaiter(this, void 0, void 0, function* () { if (this.config.test) { return this._runRulesSequentially(tab, this.config.test); } return _super.test.call(this, tab); }); } } class TabActions { constructor(tabId, url, frame, sendContentMessage, browser) { this.url = url; this.frame = frame; this.sendContentMessage = sendContentMessage; this.browser = browser; this.id = tabId; } elementExists(selector, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { // console.log(`check for ${selector} in tab ${this.id}, frame ${frameId}`); return this.sendContentMessage(this.id, { type: "elemExists", selector }, { frameId }); }); } clickElement(selector, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { // console.log(`click element ${selector} in tab ${this.id}`); return this.sendContentMessage(this.id, { type: "click", selector }, { frameId }); }); } clickElements(selector, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { // console.log(`click elements ${selector} in tab ${this.id}`); return this.sendContentMessage(this.id, { type: "click", all: true, selector }, { frameId }); }); } elementsAreVisible(selector, check, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { return this.sendContentMessage(this.id, { type: "elemVisible", selector, check }, { frameId }); }); } getAttribute(selector, attribute, frameId) { return __awaiter(this, void 0, void 0, function* () { return this.sendContentMessage(this.id, { type: "getAttribute", selector, attribute }, { frameId }); }); } eval(script, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { // console.log(`run ${script} in tab ${this.id}`); return yield this.sendContentMessage(this.id, { type: "eval", script }, { frameId }); }); } waitForElement(selector, timeout, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { const interval = 200; const times = Math.ceil(timeout / interval); return waitFor(() => this.elementExists(selector, frameId), times, interval); }); } waitForThenClick(selector, timeout, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { yield this.waitForElement(selector, timeout, frameId); yield this.clickElement(selector, frameId); return true; }); } hideElements(selectors, frameId = 0) { return __awaiter(this, void 0, void 0, function* () { return this.sendContentMessage(this.id, { type: "hide", selectors }, { frameId }); }); } getBrowserTab() { return __awaiter(this, void 0, void 0, function* () { return this.browser.tabs.get(this.id); }); } goto(url) { return __awaiter(this, void 0, void 0, function* () { return this.browser.tabs.update(this.id, { url }); }); } wait(ms) { return new Promise(resolve => { setTimeout(() => resolve(true), ms); }); } matches(matcherConfig) { return this.sendContentMessage(this.id, { type: "matches", config: matcherConfig }, { frameId: 0 }); } executeAction(config, param) { return this.sendContentMessage(this.id, { type: "executeAction", config, param }, { frameId: 0 }); } } /** * This code is in most parts copied from https://github.com/cavi-au/Consent-O-Matic/blob/master/Extension/Tools.js * which is licened under the MIT. */ class Tools { static setBase(base) { Tools.base = base; } static findElement(options, parent = null, multiple = false) { let possibleTargets = null; if (parent != null) { possibleTargets = Array.from(parent.querySelectorAll(options.selector)); } else { if (Tools.base != null) { possibleTargets = Array.from(Tools.base.querySelectorAll(options.selector)); } else { possibleTargets = Array.from(document.querySelectorAll(options.selector)); } } if (options.textFilter != null) { possibleTargets = possibleTargets.filter(possibleTarget => { let textContent = possibleTarget.textContent.toLowerCase(); if (Array.isArray(options.textFilter)) { let foundText = false; for (let text of options.textFilter) { if (textContent.indexOf(text.toLowerCase()) !== -1) { foundText = true; break; } } return foundText; } else if (options.textFilter != null) { return textContent.indexOf(options.textFilter.toLowerCase()) !== -1; } }); } if (options.styleFilters != null) { possibleTargets = possibleTargets.filter(possibleTarget => { let styles = window.getComputedStyle(possibleTarget); let keep = true; for (let styleFilter of options.styleFilters) { let option = styles[styleFilter.option]; if (styleFilter.negated) { keep = keep && option !== styleFilter.value; } else { keep = keep && option === styleFilter.value; } } return keep; }); } if (options.displayFilter != null) { possibleTargets = possibleTargets.filter(possibleTarget => { if (options.displayFilter) { //We should be displayed return possibleTarget.offsetHeight !== 0; } else { //We should not be displayed return possibleTarget.offsetHeight === 0; } }); } if (options.iframeFilter != null) { possibleTargets = possibleTargets.filter(possibleTarget => { if (options.iframeFilter) { //We should be inside an iframe return window.location !== window.parent.location; } else { //We should not be inside an iframe return window.location === window.parent.location; } }); } if (options.childFilter != null) { possibleTargets = possibleTargets.filter(possibleTarget => { let oldBase = Tools.base; Tools.setBase(possibleTarget); let childResults = Tools.find(options.childFilter); Tools.setBase(oldBase); return childResults.target != null; }); } if (multiple) { return possibleTargets; } else { if (possibleTargets.length > 1) { console.warn("Multiple possible targets: ", possibleTargets, options, parent); } return possibleTargets[0]; } } static find(options, multiple = false) { let results = []; if (options.parent != null) { let parent = Tools.findElement(options.parent, null, multiple); if (parent != null) { if (parent instanceof Array) { parent.forEach(p => { let targets = Tools.findElement(options.target, p, multiple); if (targets instanceof Array) { targets.forEach(target => { results.push({ parent: p, target: target }); }); } else { results.push({ parent: p, target: targets }); } }); return results; } else { let targets = Tools.findElement(options.target, parent, multiple); if (targets instanceof Array) { targets.forEach(target => { results.push({ parent: parent, target: target }); }); } else { results.push({ parent: parent, target: targets }); } } } } else { let targets = Tools.findElement(options.target, null, multiple); if (targets instanceof Array) { targets.forEach(target => { results.push({ parent: null, target: target }); }); } else { results.push({ parent: null, target: targets }); } } if (results.length === 0) { results.push({ parent: null, target: null }); } if (multiple) { return results; } else { if (results.length !== 1) { console.warn("Multiple results found, even though multiple false", results); } return results[0]; } } } Tools.base = null; function matches(config) { const result = Tools.find(config); if (config.type === "css") { return !!result.target; } else if (config.type === "checkbox") { return !!result.target && result.target.checked; } } function executeAction(config, param) { return __awaiter(this, void 0, void 0, function* () { switch (config.type) { case "click": return clickAction(config); case "list": return listAction(config, param); case "consent": return consentAction(config, param); case "ifcss": return ifCssAction(config, param); case "waitcss": return waitCssAction(config); case "foreach": return forEachAction(config, param); case "hide": return hideAction(config); case "slide": return slideAction(config); case "close": return closeAction(); case "wait": return waitAction(config); case "eval": return evalAction(config); default: throw "Unknown action type: " + config.type; } }); } const STEP_TIMEOUT = 0; function waitTimeout(timeout) { return new Promise(resolve => { setTimeout(() => { resolve(); }, timeout); }); } function clickAction(config) { return __awaiter(this, void 0, void 0, function* () { const result = Tools.find(config); if (result.target != null) { result.target.click(); } return waitTimeout(STEP_TIMEOUT); }); } function listAction(config, param) { return __awaiter(this, void 0, void 0, function* () { for (let action of config.actions) { yield executeAction(action, param); } }); } function consentAction(config, consentTypes) { return __awaiter(this, void 0, void 0, function* () { for (const consentConfig of config.consents) { const shouldEnable = consentTypes.indexOf(consentConfig.type) !== -1; if (consentConfig.matcher && consentConfig.toggleAction) { const isEnabled = matches(consentConfig.matcher); if (isEnabled !== shouldEnable) { yield executeAction(consentConfig.toggleAction); } } else { if (shouldEnable) { yield executeAction(consentConfig.trueAction); } else { yield executeAction(consentConfig.falseAction); } } } }); } function ifCssAction(config, param) { return __awaiter(this, void 0, void 0, function* () { const result = Tools.find(config); if (!result.target) { if (config.trueAction) { yield executeAction(config.trueAction, param); } } else { if (config.falseAction) { yield executeAction(config.falseAction, param); } } }); } function waitCssAction(config) { return __awaiter(this, void 0, void 0, function* () { yield new Promise(resolve => { let numRetries = config.retries || 10; const waitTime = config.waitTime || 250; const checkCss = () => { const result = Tools.find(config); if ((config.negated && result.target) || (!config.negated && !result.target)) { if (numRetries > 0) { numRetries -= 1; setTimeout(checkCss, waitTime); } else { resolve(); } } else { resolve(); } }; checkCss(); }); }); } function forEachAction(config, param) { return __awaiter(this, void 0, void 0, function* () { const results = Tools.find(config, true); const oldBase = Tools.base; for (const result of results) { if (result.target) { Tools.setBase(result.target); yield executeAction(config.action, param); } } Tools.setBase(oldBase); }); } function hideAction(config) { return __awaiter(this, void 0, void 0, function* () { const result = Tools.find(config); if (result.target) { result.target.classList.add("Autoconsent-Hidden"); // result.target.setAttribute("style", "display: none;"); } }); } function slideAction(config) { return __awaiter(this, void 0, void 0, function* () { const result = Tools.find(config); const dragResult = Tools.find(config.dragTarget); if (result.target) { let targetBounds = result.target.getBoundingClientRect(); let dragTargetBounds = dragResult.target.getBoundingClientRect(); let yDiff = dragTargetBounds.top - targetBounds.top; let xDiff = dragTargetBounds.left - targetBounds.left; if (this.config.axis.toLowerCase() === "y") { xDiff = 0; } if (this.config.axis.toLowerCase() === "x") { yDiff = 0; } let screenX = window.screenX + targetBounds.left + targetBounds.width / 2.0; let screenY = window.screenY + targetBounds.top + targetBounds.height / 2.0; let clientX = targetBounds.left + targetBounds.width / 2.0; let clientY = targetBounds.top + targetBounds.height / 2.0; let mouseDown = document.createEvent("MouseEvents"); mouseDown.initMouseEvent("mousedown", true, true, window, 0, screenX, screenY, clientX, clientY, false, false, false, false, 0, result.target); let mouseMove = document.createEvent("MouseEvents"); mouseMove.initMouseEvent("mousemove", true, true, window, 0, screenX + xDiff, screenY + yDiff, clientX + xDiff, clientY + yDiff, false, false, false, false, 0, result.target); let mouseUp = document.createEvent("MouseEvents"); mouseUp.initMouseEvent("mouseup", true, true, window, 0, screenX + xDiff, screenY + yDiff, clientX + xDiff, clientY + yDiff, false, false, false, false, 0, result.target); result.target.dispatchEvent(mouseDown); yield this.waitTimeout(10); result.target.dispatchEvent(mouseMove); yield this.waitTimeout(10); result.target.dispatchEvent(mouseUp); } }); } function waitAction(config) { return __awaiter(this, void 0, void 0, function* () { yield waitTimeout(config.waitTime); }); } function closeAction(config) { return __awaiter(this, void 0, void 0, function* () { window.close(); }); } function evalAction(config) { return __awaiter(this, void 0, void 0, function* () { console.log("eval!", config.code); return new Promise(resolve => { try { if (config.async) { window.eval(config.code); setTimeout(() => { resolve(window.eval("window.__consentCheckResult")); }, config.timeout || 250); } else { resolve(window.eval(config.code)); } } catch (e) { console.warn("eval error", e, config.code); resolve(false); } }); }); } function handleMessage(message, debug = false) { if (message.type === "click") { const elem = document.querySelectorAll(message.selector); debug && console.log("[click]", message.selector, elem); if (elem.length > 0) { if (message.all === true) { elem.forEach(e => e.click()); } else { elem[0].click(); } } return Promise.resolve(elem.length > 0); } else if (message.type === "elemExists") { const exists = document.querySelector(message.selector) !== null; debug && console.log("[exists?]", message.selector, exists); return Promise.resolve(exists); } else if (message.type === "elemVisible") { const elem = document.querySelectorAll(message.selector); const results = new Array(elem.length); elem.forEach((e, i) => { results[i] = e.offsetParent !== null; }); if (results.length === 0) { return Promise.resolve(false); } else if (message.check === "any") { return Promise.resolve(results.some(r => r)); } else if (message.check === "none") { return Promise.resolve(results.every(r => !r)); } // all return Promise.resolve(results.every(r => r)); } else if (message.type === "getAttribute") { const elem = document.querySelector(message.selector); if (!elem) { return Promise.resolve(false); } return Promise.resolve(elem.getAttribute(message.attribute)); } else if (message.type === "eval") { // TODO: chrome support try { const result = window.eval(message.script); // eslint-disable-line no-eval debug && console.log("[eval]", message.script, result); return Promise.resolve(result); } catch (e) { return Promise.reject(e); } } else if (message.type === "hide") { const parent = document.head || document.getElementsByTagName("head")[0] || document.documentElement; const hidden = message.selectors.filter(selector => { const matching = document.querySelectorAll(selector); return matching.length > 0; }, []); const rule = `${hidden.join(",")} { display: none !important; }`; const css = document.createElement("style"); css.type = "text/css"; css.id = "re-consent-css-rules"; css.appendChild(document.createTextNode(rule)); parent.appendChild(css); return Promise.resolve(hidden); } else if (message.type === "matches") { const matched = matches(message.config); return Promise.resolve(matched); } else if (message.type === "executeAction") { return executeAction(message.config, message.param).then(result => { return result !== undefined ? result : true; }); } return Promise.resolve(null); } class TagCommander extends AutoConsentBase { constructor() { super('tagcommander'); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { try { return yield tab.eval('window.tC && window.tC.privacyCenter !== undefined'); } catch (e) { return false; } }); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { return (yield tab.elementExists('#dnt-banner')) || (yield tab.elementExists('#privacy-iframe')) || (yield tab.elementExists('#footer_tc_privacy')) || (yield tab.elementExists('#header_tc_privacy')); }); } detectFrame(tab, frame) { return frame.url.startsWith('http://cdn.tagcommander.com/privacy') || frame.url.startsWith('https://cdn.tagcommander.com/privacy'); } openFrame(tab) { return __awaiter(this, void 0, void 0, function* () { if ((yield tab.elementExists('#footer_tc_privacy')) || (yield tab.elementExists('#footer_tc_privacy_privacy_center')) || (yield tab.elementExists('#header_tc_privacy'))) { yield this.openCmp(tab); } }); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { if (!(yield tab.elementExists('#privacy-iframe'))) { yield this.openFrame(tab); yield waitFor(() => !!tab.frame, 10, 200); } if (!tab.frame) { return false; } yield tab.wait(500); yield tab.waitForElement('#privacy-cat-modal', 20000, tab.frame.id); yield tab.wait(500); yield tab.clickElements('.btn-yes', tab.frame.id); yield tab.wait(200); yield tab.clickElement('.modal-footer > button', tab.frame.id); return true; }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (!(yield tab.elementExists('#privacy-iframe'))) { yield this.openCmp(tab); yield waitFor(() => !!tab.frame, 10, 200); } if (!tab.frame) { return false; } yield new Promise(resolve => setTimeout(resolve, 500)); yield tab.waitForElement('#privacy-cat-modal', 20000, tab.frame.id); yield tab.clickElements('.btn-no', tab.frame.id); yield tab.clickElement('.modal-footer > button', tab.frame.id); return true; }); } openCmp(tab) { return __awaiter(this, void 0, void 0, function* () { yield tab.eval('tC.privacyCenter.showPrivacyCenter();'); if (yield tab.waitForElement('#privacy-iframe', 10000)) { yield new Promise(resolve => setTimeout(resolve, 500)); return true; } return false; }); } test(tab) { return __awaiter(this, void 0, void 0, function* () { return tab.eval('tC.privacy.categories[0] === "-1"'); }); } } class TrustArc extends AutoConsentBase { constructor() { super('TrustArc'); } detectFrame(_, frame) { return frame.url.startsWith('https://consent-pref.trustarc.com/?'); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { if (tab.frame && tab.frame.url.startsWith('https://consent-pref.trustarc.com/?')) { return true; } return tab.elementExists('#truste-show-consent'); }); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { if (tab.frame) { return tab.waitForElement('#defaultpreferencemanager', 5000, tab.frame.id); } return tab.elementExists('#truste-show-consent'); }); } openFrame(tab) { return __awaiter(this, void 0, void 0, function* () { if (yield tab.elementExists('#truste-show-consent')) { yield tab.clickElement('#truste-show-consent'); } }); } navigateToSettings(tab, frameId) { return __awaiter(this, void 0, void 0, function* () { // wait for it to load yield waitFor(() => __awaiter(this, void 0, void 0, function* () { return (yield tab.elementExists('.shp', frameId)) || (yield tab.elementsAreVisible('.advance', 'any', frameId)) || tab.elementExists('.switch span:first-child', frameId); }), 10, 500); // splash screen -> hit more information if (yield tab.elementExists('.shp', frameId)) { yield tab.clickElement('.shp', frameId); } // wait for consent options if (!(yield tab.waitForElement('.switch span:first-child', 2000, frameId))) { return false; } // go to advanced settings if not yet shown if (yield tab.elementsAreVisible('.advance', 'any', frameId)) { yield tab.clickElement('.advance', frameId); } // takes a while to load the opt-in/opt-out buttons return yield waitFor(() => tab.elementsAreVisible('.switch span:first-child', 'any', frameId), 5, 1000); }); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { yield tab.hideElements(['.truste_overlay', '.truste_box_overlay', '.trustarc-banner', '.truste-banner']); return true; }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (!tab.frame) { yield this.openFrame(tab); yield waitFor(() => !!tab.frame, 10, 200); } const frameId = tab.frame.id; yield this.navigateToSettings(tab, frameId); yield tab.clickElements('.switch span:nth-child(2)', frameId); yield tab.clickElement('.submit', frameId); yield waitFor(() => tab.elementExists('#gwt-debug-close_id', frameId), 300, 1000); yield tab.clickElement('#gwt-debug-close_id', frameId); return true; }); } openCmp(tab) { return __awaiter(this, void 0, void 0, function* () { yield tab.eval('truste.eu.clickListener()'); return true; }); } test() { return __awaiter(this, void 0, void 0, function* () { // TODO: find out how to test TrustArc return true; }); } } class Cookiebot extends AutoConsentBase { constructor() { super('Cybotcookiebot'); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { try { return yield tab.eval('typeof window.CookieConsent === "object"'); } catch (e) { return false; } }); } detectPopup(tab) { return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner'); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { if (yield tab.elementExists('#dtcookie-container')) { return tab.clickElement('.h-dtcookie-decline'); } if (yield tab.elementExists('.cookiebot__button--settings')) { yield tab.clickElement('.cookiebot__button--settings'); } if (yield tab.elementsAreVisible('#CybotCookiebotDialogBodyButtonDecline', 'all')) { return yield tab.clickElement('#CybotCookiebotDialogBodyButtonDecline'); } yield tab.clickElements('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled'); if (yield tab.elementExists('#CybotCookiebotDialogBodyButtonDecline')) { yield tab.clickElement('#CybotCookiebotDialogBodyButtonDecline'); } yield tab.clickElements('#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept'); // await tab.clickElement('#CybotCookiebotDialogBodyButtonAccept'); return true; }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (yield tab.elementExists('#dtcookie-container')) { return tab.clickElement('.h-dtcookie-accept'); } yield tab.clickElements('.CybotCookiebotDialogBodyLevelButton:not(:checked):enabled'); yield tab.clickElement('#CybotCookiebotDialogBodyLevelButtonAccept'); yield tab.clickElement('#CybotCookiebotDialogBodyButtonAccept'); return true; }); } openCmp(tab) { return __awaiter(this, void 0, void 0, function* () { yield tab.eval('CookieConsent.renew() || true'); return tab.waitForElement('#CybotCookiebotDialog', 10000); }); } test(tab) { return __awaiter(this, void 0, void 0, function* () { return tab.eval('CookieConsent.declined === true'); }); } } function clickFirstMatching(tab, elements) { return __awaiter(this, void 0, void 0, function* () { const exists = yield Promise.all(elements.map(e => tab.elementExists(e))); const first = exists.findIndex(e => !!e); if (first >= 0) { yield tab.clickElement(elements[first]); return; } throw new Error('cannot open CMP'); }); } const popupContainers = [ 'div[class*="popup_popup-"]', 'div[class*="banner_banner-"]', 'div[class*="main_banner-"]', '.prism-modal-banner', 'div[class*="popupFooter_content-"]' ]; const moreInfoElements = [ 'a[class^="banner_learnMore-"]', 'button[class*="intro_rejectAll-"],span[class*="intro_rejectAll-"]', '.prism-modal-banner .ad-settings', // link to open options has no class or id, so this might be flaky 'div[class^="banner_message-"] > span:first-child > a', 'div[class^="banner_copy-"] > a', 'div[class^="main_buttons-"] > div > button:first-child', 'div[class^="banner_option-"]:nth-child(3) > a,a[class^="banner_option-"]:nth-child(3)', ]; class AppGdpr extends AutoConsentBase { constructor() { super('app_gdpr'); } detectCmp(tab) { return tab.elementExists('div[class^="app_gdpr-"]'); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { return !(yield tab.elementExists('div[class*="banner_hidden-"]')) && (yield tab.elementsAreVisible(popupContainers.join(','), 'any')) && (yield tab.elementExists(moreInfoElements.join(','))) && this.detectCmp(tab); }); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { yield clickFirstMatching(tab, moreInfoElements); if (yield tab.elementExists('button[class*="details_btnDeactivate-"],button[class*="introV2_rejectAll-"')) { yield tab.waitForThenClick('button[class*="details_btnDeactivate-"],button[class*="introV2_rejectAll-"'); return tab.clickElement('button[class*="details_btnSave-"],button[class*="details_save-"]'); } else if (yield tab.elementExists('div[class*="purposes_purposeItem-"]')) { let i = 1; while (yield tab.elementExists(`div[class*="purposes_purposeItem-"]:nth-child(${i})`)) { yield tab.clickElement(`div[class*="purposes_purposeItem-"]:nth-child(${i})`); yield tab.clickElements('span[class*="switch_isSelected-"]'); i += 1; } } else if (yield tab.elementExists('span[class*="switch_isSelected-"]')) { // streamlined UI with categories yield tab.clickElements('span[class*="switch_isSelected-"]'); } else { // we have to turn off vendors for all categories... let i = 1; while (yield tab.elementExists(`div[class*="summary_purposeItem-"]:nth-child(${i}) > a`)) { yield tab.clickElement(`div[class*="summary_purposeItem-"]:nth-child(${i}) > a`); yield tab.clickElements('span[class*="switch_isSelected-"]'); yield tab.clickElement('button[class*="details_back"]'); i += 1; } } return yield tab.clickElement('button[class*="details_save"]'); }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (yield tab.elementExists('a[class^="banner_continue-"]')) { yield tab.clickElement('a[class^="banner_continue-"]'); } if (yield tab.elementExists('span[class^="banner_consent-"')) { yield tab.clickElement('span[class^="banner_consent-"'); } return true; }); } } const domains = [ 'techradar.com', 'cyclingnews.com', 'livescience.com', 'gamesradar.com', 'itproportal.com', ]; class Future extends AutoConsentBase { constructor() { super('future'); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { return !!((yield tab.elementExists('#cmp-container-id')) && tab.frame); }); } detectFrame(tab, frame) { return domains.some(d => frame.url.startsWith(`https://consent.cmp.${d}/`)); } detectPopup(tab) { return tab.elementExists('#cmp-container-id'); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { if (!(yield waitFor(() => !!tab.frame, 10, 500))) { throw new Error('no frame'); } yield waitFor(() => !!tab.frame, 10, 500); yield tab.waitForElement('#mainMoreInfo', 10000, tab.frame.id); yield tab.clickElement('#mainMoreInfo', tab.frame.id); yield tab.clickElement('.cmp-btn-acceptall', tab.frame.id); yield tab.clickElement('.cmp-btn-acceptall', tab.frame.id); return true; }); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { if (!(yield waitFor(() => !!tab.frame, 10, 500))) { throw new Error('no frame'); } yield tab.waitForElement('#mainMoreInfo', 10000, tab.frame.id); yield tab.clickElement('#mainMoreInfo', tab.frame.id); yield tab.clickElement('.cmp-btn-rejectall', tab.frame.id); yield tab.clickElement('#confirmLeave', tab.frame.id); return true; }); } } const rules = [ new TagCommander(), new TrustArc(), new Cookiebot(), new AppGdpr(), new Future(), ]; function createAutoCMP(config) { return new AutoConsent(config); } const rules$1 = rules; class ConsentOMaticCMP { constructor(name, config) { this.name = name; this.config = config; this.methods = new Map(); config.methods.forEach(methodConfig => { if (methodConfig.action) { this.methods.set(methodConfig.name, methodConfig.action); } }); this.hasSelfTest = this.methods.has("TEST_CONSENT"); } detectCmp(tab) { return __awaiter(this, void 0, void 0, function* () { return (yield Promise.all(this.config.detectors.map(detectorConfig => tab.matches(detectorConfig.presentMatcher)))).some(matched => matched); }); } detectPopup(tab) { return __awaiter(this, void 0, void 0, function* () { return (yield Promise.all(this.config.detectors.map(detectorConfig => tab.matches(detectorConfig.showingMatcher)))).some(matched => matched); }); } executeAction(tab, method, param) { return __awaiter(this, void 0, void 0, function* () { if (this.methods.has(method)) { return tab.executeAction(this.methods.get(method), param); } return true; }); } optOut(tab) { return __awaiter(this, void 0, void 0, function* () { yield this.executeAction(tab, "HIDE_CMP"); yield this.executeAction(tab, "OPEN_OPTIONS"); yield this.executeAction(tab, "HIDE_CMP"); yield this.executeAction(tab, "DO_CONSENT", []); yield this.executeAction(tab, "SAVE_CONSENT"); return true; }); } optIn(tab) { return __awaiter(this, void 0, void 0, function* () { yield this.executeAction(tab, "HIDE_CMP"); yield this.executeAction(tab, "OPEN_OPTIONS"); yield this.executeAction(tab, "HIDE_CMP"); yield this.executeAction(tab, "DO_CONSENT", ['D', 'A', 'B', 'E', 'F', 'X']); yield this.executeAction(tab, "SAVE_CONSENT"); return true; }); } openCmp(tab) { return __awaiter(this, void 0, void 0, function* () { yield this.executeAction(tab, "HIDE_CMP"); yield this.executeAction(tab, "OPEN_OPTIONS"); return true; }); } test(tab) { return this.executeAction(tab, "TEST_CONSENT"); } detectFrame(tab, frame) { return false; } } class TabConsent { constructor(tab, url, ruleCheckPromise) { this.tab = tab; this.url = url; this.checked = ruleCheckPromise; ruleCheckPromise.then(rule => this.rule = rule); } getCMPName() { if (this.rule) { return this.rule.name; } return null; } isPopupOpen() { return __awaiter(this, void 0, void 0, function* () { return (yield this.rule.detectPopup(this.tab)) || (yield new Promise((resolve) => { setTimeout(() => __awaiter(this, void 0, void 0, function* () { return resolve(yield this.rule.detectPopup(this.tab)); }), 1000); })); }); } doOptOut() { return __awaiter(this, void 0, void 0, function* () { return this.rule.optOut(this.tab); }); } doOptIn() { return __awaiter(this, void 0, void 0, function* () { return this.rule.optIn(this.tab); }); } hasTest() { return !!this.rule.hasSelfTest; } testOptOutWorked() { return __awaiter(this, void 0, void 0, function* () { return this.rule.test(this.tab); }); } applyCosmetics(selectors) { return __awaiter(this, void 0, void 0, function* () { const hidden = yield this.tab.hideElements(selectors); return hidden; }); } } class AutoConsent$1 { constructor(browser, sendContentMessage) { this.browser = browser; this.sendContentMessage = sendContentMessage; this.consentFrames = new Map(); this.tabCmps = new Map(); this.sendContentMessage = sendContentMessage; this.rules = [...rules$1]; } addCMP(config) { this.rules.push(createAutoCMP(config)); } addConsentomaticCMP(name, config) { this.rules.push(new ConsentOMaticCMP(`com_${name}`, config)); } createTab(tabId, url) { return new TabActions(tabId, url, this.consentFrames.get(tabId), this.sendContentMessage, this.browser); } checkTab(tabId) { return __awaiter(this, void 0, void 0, function* () { const tabInfo = yield this.browser.tabs.get(tabId); const pageUrl = new URL(tabInfo.url); if (!this.tabCmps.has(tabId) || this.tabCmps.get(tabId).url.href !== pageUrl.href) { const tab = this.createTab(tabId, pageUrl.href); const consent = new TabConsent(tab, pageUrl, this.detectDialog(tab, 5)); this.tabCmps.set(tabId, consent); // check tabs consent.checked.then((rule) => { if (this.consentFrames.has(tabId)) { const frame = this.consentFrames.get(tabId); if (frame.type === rule.name) { consent.tab.frame = frame; } } }); } return this.tabCmps.get(tabId); }); } removeTab(tabId) { this.tabCmps.delete(tabId); } onFrame({ tabId, url, frameId }) { // ignore main frames if (frameId === 0) { return; } try { const frame = { id: frameId, url: