From 42488565b94a1e039d7fabae65b6f480e4225908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Luba=C5=84ski?= Date: Fri, 13 Sep 2024 08:54:20 +0200 Subject: [PATCH] fix: move trackers preview to main package (#1880) --- .github/workflows/test.yml | 10 +- extension-manifest-v3/package.json | 8 +- extension-manifest-v3/scripts/build.js | 14 +- .../index.js => download-engines.js} | 2 +- ...mfilter.js => download-wtm-bloomfilter.js} | 0 .../scripts/download-wtm-stats.js | 13 +- .../scripts/xcode-version.js | 11 + extension-manifest-v3/src/background/serp.js | 51 +++-- .../src/content_scripts/trackers-preview.css | 0 .../src/content_scripts/trackers-preview.js | 183 ++++++++++++++++- .../src/pages/panel/views/home.js | 2 + .../pages/settings/views/website-details.js | 34 ++-- .../assets/disable-preview.svg | 0 .../trackers-preview/components/layout.js | 33 ++- .../src/pages/trackers-preview/index.js | 49 +++-- extension-manifest-v3/src/utils/wtm-stats.js | 35 ++++ package-lock.json | 15 -- packages/trackers-preview/.eslintignore | 2 - packages/trackers-preview/.gitignore | 1 - packages/trackers-preview/package.json | 23 --- .../trackers-preview/src/background/index.js | 70 ------- .../src/content_scripts/index.js | 192 ------------------ .../src/page_scripts/index.js | 45 ---- packages/ui/package.json | 1 - .../ui/src/modules/panel/components/stats.js | 38 ++-- 25 files changed, 365 insertions(+), 467 deletions(-) rename extension-manifest-v3/scripts/{download-engines/index.js => download-engines.js} (99%) rename extension-manifest-v3/scripts/{download-whotracksme-bloomfilter.js => download-wtm-bloomfilter.js} (100%) rename packages/trackers-preview/scripts/download.js => extension-manifest-v3/scripts/download-wtm-stats.js (55%) rename packages/trackers-preview/src/content_scripts/styles.css => extension-manifest-v3/src/content_scripts/trackers-preview.css (100%) rename {packages/ui/src/modules => extension-manifest-v3/src/pages}/trackers-preview/assets/disable-preview.svg (100%) rename packages/ui/src/modules/trackers-preview/index.js => extension-manifest-v3/src/pages/trackers-preview/components/layout.js (85%) create mode 100644 extension-manifest-v3/src/utils/wtm-stats.js delete mode 100644 packages/trackers-preview/.eslintignore delete mode 100644 packages/trackers-preview/.gitignore delete mode 100644 packages/trackers-preview/package.json delete mode 100644 packages/trackers-preview/src/background/index.js delete mode 100644 packages/trackers-preview/src/content_scripts/index.js delete mode 100644 packages/trackers-preview/src/page_scripts/index.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba5f0fa32..3d7cb0cd6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,18 +21,14 @@ jobs: - name: Install dependencies run: npm ci - - name: packages/trackers-preview - run: npm test - working-directory: packages/trackers-preview - - - name: packages/ui + - name: UI run: npm test working-directory: packages/ui - - name: MV3 - Test + - name: Source working-directory: extension-manifest-v3 run: npm test - - name: MV3 - Build + - name: Build working-directory: extension-manifest-v3 run: npm run build diff --git a/extension-manifest-v3/package.json b/extension-manifest-v3/package.json index 1585c7649..a88e56181 100644 --- a/extension-manifest-v3/package.json +++ b/extension-manifest-v3/package.json @@ -4,8 +4,9 @@ "version": "10.4.5", "type": "module", "scripts": { - "download-engines": "node scripts/download-engines/index.js", - "download-whotracksme-bloomfilter": "node scripts/download-whotracksme-bloomfilter.js", + "download-engines": "node scripts/download-engines.js", + "download-wtm-bloomfilter": "node scripts/download-wtm-bloomfilter.js", + "download-wtm-stats": "node scripts/download-wtm-stats.js", "build": "node scripts/build.js", "start": "npm run build -- --watch", "licenses": "license-report --config=../.license-report-config.json > dist/licenses.html", @@ -37,11 +38,10 @@ "@cliqz/adblocker-webextension": "^1.33.2", "@cliqz/adblocker-webextension-cosmetics": "^1.33.2", "@duckduckgo/autoconsent": "^10.15.0", - "@ghostery/trackers-preview": "^1.0.0", "@ghostery/ui": "^1.0.0", "@github/relative-time-element": "^4.3.0", - "@whotracksme/reporting": "^5.1.21", "@sentry/browser": "^8.26.0", + "@whotracksme/reporting": "^5.1.21", "hybrids": "^9.1.1", "idb": "^7.1.1", "jwt-decode": "^4.0.0", diff --git a/extension-manifest-v3/scripts/build.js b/extension-manifest-v3/scripts/build.js index b343cd216..aac5f6f5a 100644 --- a/extension-manifest-v3/scripts/build.js +++ b/extension-manifest-v3/scripts/build.js @@ -1,3 +1,14 @@ +/** + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2017-present Ghostery GmbH. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + import { resolve, dirname, join } from 'path'; import { readFileSync, writeFileSync, readdirSync } from 'fs'; import { exec, execSync } from 'child_process'; @@ -54,7 +65,8 @@ if (argv.staging) { execSync('npm run download-engines', { stdio: 'inherit' }); } -execSync('npm run download-whotracksme-bloomfilter', { stdio: 'inherit' }); +execSync('npm run download-wtm-bloomfilter', { stdio: 'inherit' }); +execSync('npm run download-wtm-stats', { stdio: 'inherit' }); const config = { logLevel: argv.silent ? 'silent' : undefined, diff --git a/extension-manifest-v3/scripts/download-engines/index.js b/extension-manifest-v3/scripts/download-engines.js similarity index 99% rename from extension-manifest-v3/scripts/download-engines/index.js rename to extension-manifest-v3/scripts/download-engines.js index 628950661..64e355fe1 100644 --- a/extension-manifest-v3/scripts/download-engines/index.js +++ b/extension-manifest-v3/scripts/download-engines.js @@ -15,7 +15,7 @@ import { resolve } from 'path'; import shelljs from 'shelljs'; import { ENGINE_VERSION, FiltersEngine } from '@cliqz/adblocker'; -import REGIONS from '../../src/utils/regions.js'; +import REGIONS from '../src/utils/regions.js'; function createChecksum(content) { return createHash('sha256').update(content).digest('hex'); diff --git a/extension-manifest-v3/scripts/download-whotracksme-bloomfilter.js b/extension-manifest-v3/scripts/download-wtm-bloomfilter.js similarity index 100% rename from extension-manifest-v3/scripts/download-whotracksme-bloomfilter.js rename to extension-manifest-v3/scripts/download-wtm-bloomfilter.js diff --git a/packages/trackers-preview/scripts/download.js b/extension-manifest-v3/scripts/download-wtm-stats.js similarity index 55% rename from packages/trackers-preview/scripts/download.js rename to extension-manifest-v3/scripts/download-wtm-stats.js index ea61c2fa6..0e5f2a12f 100644 --- a/packages/trackers-preview/scripts/download.js +++ b/extension-manifest-v3/scripts/download-wtm-stats.js @@ -1,9 +1,20 @@ +/** + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2017-present Ghostery GmbH. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + import { writeFileSync } from 'fs'; import { URL, fileURLToPath } from 'url'; const DATA_URL = 'https://whotracks.me/data/trackers-preview.json'; const OUTPUT_FILE = fileURLToPath( - new URL('../src/background/trackers-preview-generated.js', import.meta.url), + new URL('../src/rule_resources/wtm-stats.js', import.meta.url), ); const data = await fetch(DATA_URL).then((res) => { diff --git a/extension-manifest-v3/scripts/xcode-version.js b/extension-manifest-v3/scripts/xcode-version.js index 2d5103273..ab5a87d47 100644 --- a/extension-manifest-v3/scripts/xcode-version.js +++ b/extension-manifest-v3/scripts/xcode-version.js @@ -1,3 +1,14 @@ +/** + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2017-present Ghostery GmbH. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + import { resolve } from 'path'; import { readFileSync, writeFileSync } from 'fs'; diff --git a/extension-manifest-v3/src/background/serp.js b/extension-manifest-v3/src/background/serp.js index 1c5b50430..59e484fb2 100644 --- a/extension-manifest-v3/src/background/serp.js +++ b/extension-manifest-v3/src/background/serp.js @@ -10,13 +10,32 @@ */ import { store } from 'hybrids'; -import { - tryWTMReportOnMessageHandler, - isDisableWTMReportMessage, -} from '@ghostery/trackers-preview/background'; -import css from '@ghostery/trackers-preview/content_scripts/styles.css?raw'; +import { parse } from 'tldts-experimental'; import Options, { isPaused } from '/store/options.js'; +import trackersPreviewCSS from '/content_scripts/trackers-preview.css?raw'; +import { getWTMStats } from '/utils/wtm-stats'; + +chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { + if (msg.action === 'getWTMReport') { + sendResponse({ + wtmStats: msg.links.map((url) => { + const { domain } = parse(url); + + return { + stats: getWTMStats(domain), + domain, + }; + }), + }); + } + + if (msg.action === 'disableWTMReport') { + store.set(Options, { wtmSerpReport: false }); + } + + return false; +}); const SERP_URL_REGEXP = /^https:[/][/][^/]*[.]google[.][a-z]+([.][a-z]+)?[/]search/; @@ -29,7 +48,7 @@ chrome.webNavigation.onCommitted.addListener((details) => { target: { tabId: details.tabId, }, - css, + css: trackersPreviewCSS, }); } @@ -60,23 +79,3 @@ chrome.webNavigation.onCommitted.addListener((details) => { }); } }); - -chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { - const options = store.get(Options); - - if (!store.ready(options)) { - return false; - } - - if (options.wtmSerpReport) { - if (tryWTMReportOnMessageHandler(msg, sender, sendResponse)) { - return false; - } - - if (isDisableWTMReportMessage(msg)) { - store.set(options, { wtmSerpReport: false }); - } - } - - return false; -}); diff --git a/packages/trackers-preview/src/content_scripts/styles.css b/extension-manifest-v3/src/content_scripts/trackers-preview.css similarity index 100% rename from packages/trackers-preview/src/content_scripts/styles.css rename to extension-manifest-v3/src/content_scripts/trackers-preview.css diff --git a/extension-manifest-v3/src/content_scripts/trackers-preview.js b/extension-manifest-v3/src/content_scripts/trackers-preview.js index 3aa59ceef..6e10481a1 100644 --- a/extension-manifest-v3/src/content_scripts/trackers-preview.js +++ b/extension-manifest-v3/src/content_scripts/trackers-preview.js @@ -9,7 +9,188 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import setupTrackersPreview from '@ghostery/trackers-preview/content_scripts'; +import { drawWheel } from '@ghostery/ui/wheel'; + +const WRAPPER_CLASS = 'wtm-popup-iframe-wrapper'; + +const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); + +function closePopups() { + [...document.querySelectorAll(`.${WRAPPER_CLASS}`)].forEach((popup) => { + popup.parentElement.removeChild(popup); + }); +} + +function resizePopup(height) { + [...document.querySelectorAll(`.${WRAPPER_CLASS}`)].forEach((popup) => { + popup.style.height = `${height}px`; + }); +} + +const getTop = (el) => + el.offsetTop + (el.offsetParent && getTop(el.offsetParent)); + +function renderPopup(container, stats, popupUrl) { + closePopups(); + + const wrapper = document.createElement('div'); + wrapper.classList.add(WRAPPER_CLASS); + if (isMobile) { + wrapper.style.width = window.innerWidth - 20 + 'px'; + wrapper.style.left = '10px'; + } else { + const left = container.getBoundingClientRect().left - 350 / 2 + 12; + wrapper.style.left = (left < 20 ? 20 : left) + 'px'; + } + wrapper.style.top = getTop(container) + 25 + 'px'; + + const iframe = document.createElement('iframe'); + iframe.setAttribute('src', `${popupUrl}?domain=${stats.domain}`); + + wrapper.appendChild(iframe); + document.body.appendChild(wrapper); +} + +function getWheelElement(stats, popupUrl) { + const count = stats.stats.length; + + if (count === 0) { + return null; + } + + const container = document.createElement('div'); + container.classList.add('wtm-tracker-wheel-container'); + + const label = document.createElement('label'); + label.innerText = count; + + const canvas = document.createElement('canvas'); + canvas.classList.add('wtm-tracker-wheel'); + + const ctx = canvas.getContext('2d'); + drawWheel(ctx, 16, stats.stats); + + container.appendChild(canvas); + container.appendChild(label); + + container.addEventListener('click', (ev) => { + ev.preventDefault(); + ev.stopImmediatePropagation(); + + renderPopup(container, stats, popupUrl); + }); + + return container; +} + +function setupTrackersPreview(popupUrl) { + const elements = [ + ...window.document.querySelectorAll( + '[data-hveid] div.yuRUbf > div > span > a, [data-hveid] div.yuRUbf > div > a, [data-hveid] div.xpd a.cz3goc, [data-hveid] > .xpd > div.kCrYT:first-child > a', + ), + ].filter((el) => !el.dataset.wtm); + + if (elements.length) { + const links = elements.map((el) => { + if (el.hostname === window.location.hostname) { + const url = new URL(el.href); + return url.searchParams.get('url') || url.searchParams.get('q'); + } + return el.href; + }); + + chrome.runtime.sendMessage( + { action: 'getWTMReport', links }, + (response) => { + if (chrome.runtime.lastError) { + console.error( + 'Could not retrieve WTM information on URLs', + chrome.runtime.lastError, + ); + return; + } + + elements.forEach((anchor, i) => { + const stats = response.wtmStats[i]; + if (stats) { + try { + const wheelEl = getWheelElement(stats, popupUrl); + if (!wheelEl) return; + + const container = + // Desktop + anchor.parentElement.querySelector('.B6fmyf') || + anchor.parentElement.parentElement.querySelector('.B6fmyf') || + // Mobile + anchor.querySelector('span.yIn8Od') || + anchor.querySelector('div[role="link"]') || + anchor.querySelector('div.UPmit.AP7Wnd'); + + if (!container) return; + + let tempEl = container.firstElementChild; + if (tempEl && tempEl.textContent.includes(stats.domain)) { + container.insertBefore(wheelEl, tempEl.nextElementSibling); + } else { + container.appendChild(wheelEl); + } + + anchor.dataset.wtm = 1; + } catch (e) { + console.warn( + 'Unexpected error while rendering the Tracker Preview wheel', + e, + ); + } + } + }); + }, + ); + + const observer = new MutationObserver((mutations) => { + if (mutations.some((m) => m.addedNodes.length)) { + observer.disconnect(); + setTimeout(() => setupTrackersPreview(popupUrl), 500); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + }); + } +} + +window.addEventListener('message', (message) => { + if ( + message.origin + '/' !== chrome.runtime.getURL('/').toLowerCase() && + typeof message.data == 'string' + ) { + return; + } + + if (message.data === 'WTMReportClosePopups') { + closePopups(); + } else if (message.data === 'WTMReportDisable') { + closePopups(); + + // Remove the wheel from the elements + [...document.querySelectorAll('[data-wtm]')].forEach((el) => { + delete el.dataset.wtm; + }); + + [...document.querySelectorAll('.wtm-tracker-wheel-container')].forEach( + (el) => { + el.parentElement.removeChild(el); + }, + ); + + chrome.runtime.sendMessage({ action: 'disableWTMReport' }); + } else if (message.data?.startsWith('WTMReportResize')) { + const height = message.data.split(':')[1]; + resizePopup(height); + } +}); document.addEventListener('DOMContentLoaded', () => { setupTrackersPreview( diff --git a/extension-manifest-v3/src/pages/panel/views/home.js b/extension-manifest-v3/src/pages/panel/views/home.js index e5645da0c..a2263fe77 100644 --- a/extension-manifest-v3/src/pages/panel/views/home.js +++ b/extension-manifest-v3/src/pages/panel/views/home.js @@ -12,6 +12,7 @@ import { html, store, router, msg } from 'hybrids'; import { openTabWithUrl } from '/utils/tabs.js'; +import { hasWTMStats } from '/utils/wtm-stats.js'; import Options, { GLOBAL_PAUSE_ID } from '/store/options.js'; import TabStats from '/store/tab-stats.js'; @@ -208,6 +209,7 @@ export default { ontypechange="${setStatsType}" layout="margin:1:1.5" layout@390px="margin:1.5:1.5:2" + wtm-link="${hasWTMStats(stats.hostname)}" > ${!paused && diff --git a/extension-manifest-v3/src/pages/settings/views/website-details.js b/extension-manifest-v3/src/pages/settings/views/website-details.js index 545ac9bf2..47b92e129 100644 --- a/extension-manifest-v3/src/pages/settings/views/website-details.js +++ b/extension-manifest-v3/src/pages/settings/views/website-details.js @@ -11,13 +11,13 @@ import { html, router, store } from 'hybrids'; import * as labels from '@ghostery/ui/labels'; -import { getStats } from '@ghostery/trackers-preview/page_scripts'; import Options from '/store/options.js'; import TrackerException from '/store/tracker-exception.js'; import Tracker from '/store/tracker.js'; import { WTM_PAGE_URL } from '/utils/api.js'; +import { hasWTMStats } from '/utils/wtm-stats.js'; import TrackerDetails from './tracker-details.js'; @@ -197,26 +197,18 @@ export default { )} - ${html.resolve( - getStats(domain).then( - ({ stats }) => - !!stats.length && - html` -
- - - - WhoTracks.Me Statistical Report - - - -
- `, - ), - )} + ${hasWTMStats(domain) && + html` +
+ + + + WhoTracks.Me Statistical Report + + + +
+ `} `, diff --git a/packages/ui/src/modules/trackers-preview/assets/disable-preview.svg b/extension-manifest-v3/src/pages/trackers-preview/assets/disable-preview.svg similarity index 100% rename from packages/ui/src/modules/trackers-preview/assets/disable-preview.svg rename to extension-manifest-v3/src/pages/trackers-preview/assets/disable-preview.svg diff --git a/packages/ui/src/modules/trackers-preview/index.js b/extension-manifest-v3/src/pages/trackers-preview/components/layout.js similarity index 85% rename from packages/ui/src/modules/trackers-preview/index.js rename to extension-manifest-v3/src/pages/trackers-preview/components/layout.js index 891607a32..0669b05d7 100644 --- a/packages/ui/src/modules/trackers-preview/index.js +++ b/extension-manifest-v3/src/pages/trackers-preview/components/layout.js @@ -9,18 +9,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -import { html, define, dispatch } from 'hybrids'; +import { html, dispatch } from 'hybrids'; import { sortCategories } from '@ghostery/ui/categories'; -import DisablePreviewImg from './assets/disable-preview.svg'; - -// Panel UI -import '../panel/index.js'; +import DisablePreviewImg from '../assets/disable-preview.svg'; const sort = sortCategories(); -export default define({ - tag: 'ui-trackers-preview', +export default { confirmDisabled: false, stats: undefined, domain: '', @@ -77,18 +73,15 @@ export default define({
${stats && - html.resolve( - stats.then( - (data) => html` - - - `, - ), - )} + html` + + + `}