Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support cosmetic filterlists #466

Merged
merged 85 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
73b7274
easylist experiment
muodov Apr 9, 2024
4063d9a
update easylist
muodov Apr 9, 2024
781b60c
Tweak easylist hack
muodov Apr 11, 2024
7440877
delay easylist detection
muodov Apr 17, 2024
6268d6f
increase the easylist timeout
muodov Apr 18, 2024
55f4cb7
Merge branch 'main' into cosmetic-easylist
muodov May 30, 2024
7016c1b
update easylist
muodov May 30, 2024
323cafc
Use fanboy-cookiemonster in the test extension
muodov Jul 15, 2024
a27664a
Initialize and apply cosmetic filters together with prehide rules
muodov Jul 15, 2024
e86edf4
Fall back to filterist when no pop-up is found
muodov Jul 15, 2024
4ee88f3
Remove easylist rule
muodov Jul 15, 2024
73d42ab
WIP
muodov Jul 16, 2024
08844a2
WIP: add perf metrics
muodov Jul 17, 2024
4a17530
add performance metrics
muodov Jul 23, 2024
709c3a8
Allow disabling autoconsent per site (in the test extension)
muodov Jul 31, 2024
d3c171e
Use uBO version of easylist cookie
muodov Aug 5, 2024
1e908bb
remove the perf metrics code
muodov Aug 5, 2024
e161c12
Use constructed stylesheets for cosmetic styles
muodov Aug 5, 2024
0f48f16
minify the extension content script
muodov Aug 5, 2024
559fe91
filterlist experiment
muodov Aug 5, 2024
4b92532
Merge remote-tracking branch 'origin/main' into filterlist-experiment
muodov Aug 8, 2024
28c308d
Add filterlist overrides
muodov Aug 8, 2024
6e502a3
Update bundled filterlist
muodov Aug 9, 2024
ea1dbe9
minor filterlist changes
muodov Aug 9, 2024
2362fe5
Logging tweaks
muodov Aug 9, 2024
e13c166
Tweak logs
muodov Aug 9, 2024
9389cc3
Add a rule for wise.com
muodov Aug 9, 2024
cb21548
add a rule for nike
muodov Aug 12, 2024
c1131cc
Add a rule for dan.com
muodov Aug 12, 2024
07501cf
Remove generic-cosmetic rule (too many false positives)
muodov Aug 12, 2024
c6f6d47
add rule for medium.com
muodov Aug 12, 2024
26934b0
Add a rule for abc.net.au
muodov Aug 12, 2024
4d7446b
Lint fix
muodov Aug 12, 2024
1f31386
removed unused file
muodov Aug 12, 2024
e2d8569
support mobile aliexpress
muodov Aug 12, 2024
5f174b3
Update rule for temu
muodov Aug 12, 2024
1eccaed
Add rule for american airlines
muodov Aug 13, 2024
f867092
Add rule for tesla
muodov Aug 13, 2024
377f777
Wrap filterlist code in try catch
muodov Aug 14, 2024
497974f
Add a rule for admiral GDPR popups
muodov Aug 14, 2024
1e6f72c
Tweak ensighten rule for britishairways.com
muodov Aug 16, 2024
1a6383b
Merge branch 'wise' into filterlist-experiment
muodov Aug 16, 2024
dd13b60
Merge branch 'nike' into filterlist-experiment
muodov Aug 16, 2024
5623bea
Merge branch 'dan-com' into filterlist-experiment
muodov Aug 16, 2024
005a47d
Merge branch 'medium' into filterlist-experiment
muodov Aug 16, 2024
1f9d022
Merge branch 'abc' into filterlist-experiment
muodov Aug 16, 2024
3eb50dc
Merge branch 'aliexpress' into filterlist-experiment
muodov Aug 16, 2024
1de2641
Merge branch 'aa' into filterlist-experiment
muodov Aug 16, 2024
1196ae1
Merge branch 'tesla' into filterlist-experiment
muodov Aug 16, 2024
2c51cef
Merge branch 'admiral' into filterlist-experiment
muodov Aug 16, 2024
01d56d3
Merge branch 'ens-tweak' into filterlist-experiment
muodov Aug 16, 2024
b01adf2
Address minor PR comments
muodov Aug 16, 2024
42daae1
comment overrides
muodov Aug 19, 2024
3806f50
Merge branch 'filterlist-experiment' into filterlist-with-fixes
muodov Aug 19, 2024
1bfe229
Merge branch 'main' into cosmetic-easylist
muodov Aug 28, 2024
feb2c02
Merge some parts of 'filterlist-with-fixes' into cosmetic-easylist
muodov Aug 28, 2024
4ebdb92
update the adblocker library and use the provided style override
muodov Aug 29, 2024
cd92df9
Bundle filterlist in JS
muodov Oct 7, 2024
6c0f6c8
Update filterlist on every release
muodov Oct 7, 2024
ddc1da0
Tweak watch command
muodov Oct 7, 2024
d80a1f5
Remove outdated comments
muodov Oct 7, 2024
5da316a
Disable filterlist by default
muodov Oct 7, 2024
3729136
Move log to a more appropriate place
muodov Oct 7, 2024
c560b38
remove unused ignore
muodov Oct 7, 2024
d2b77b6
Merge remote-tracking branch 'origin/main' into cosmetic-easylist
muodov Oct 14, 2024
a4b0db8
Lint fix in auto-generated file
muodov Oct 14, 2024
a49e906
Merge remote-tracking branch 'origin/main' into cosmetic-easylist
muodov Oct 15, 2024
2f71b9d
Update filterlist
muodov Oct 15, 2024
3d25afd
Remove autogenerated filterlist from git index
muodov Oct 16, 2024
cb7bf56
Do not commit changes to filterlist
muodov Oct 16, 2024
5a9db3b
Update the build scripts to update filterlist when necessary
muodov Oct 16, 2024
7c252ae
Regenerate package-lock (see https://github.com/npm/cli/issues/4828)
muodov Oct 17, 2024
5140329
remove performance marks
muodov Oct 17, 2024
53ae421
Produce separate builds with and without filterlist
muodov Oct 17, 2024
ce6cc1a
Avoid generating filterlist twice in ci
muodov Oct 17, 2024
e24840a
Export json rules
muodov Oct 17, 2024
b16d952
Do not commit filterlist file during release
muodov Oct 18, 2024
4adf868
Build filterlist from easylist source
muodov Oct 22, 2024
70e141a
Update readme
muodov Oct 22, 2024
bb93e6b
add link to DDG download page
muodov Oct 22, 2024
05141c4
Merge branch 'main' into cosmetic-easylist
muodov Oct 24, 2024
564ade7
cliqz/adblocker is renamed to ghostery/adblocker
muodov Nov 6, 2024
0cf0332
Address minor PR comments
muodov Nov 6, 2024
12e735f
Track the resulting filterlist in git
muodov Nov 6, 2024
4ef5ed3
Add a ci job to update EasyList
muodov Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ jobs:
with:
node-version: 18.x

- name: Install dependencies
run: npm ci

- name: Compile filterlist
run: |
npm run compile-filterlist

- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm ci
npm run release
39 changes: 39 additions & 0 deletions .github/workflows/update-filterlist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Update filterlist

on:
workflow_dispatch:

schedule:
- cron: '0 3 * * SUN' # run every Sunday at 3:00

jobs:
update_filterlist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x

- name: Install dependencies
run: npm ci

- name: Update EasyList
run: |
npm run update-easylist

- name: Read EasyList revision
id: read-revision
run: echo "revision=`cat rules/filterlists/easylist_revision.txt`"" >> "$GITHUB_OUTPUT"

- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
title: Update filterlist to ${{ steps.read-revision.outputs.revision }}
commit-message: Update filterlist to ${{ steps.read-revision.outputs.revision }}
labels: |
minor
dependencies
reviewers: muodov,sammacbeth
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ addon/*.js
.vscode/
.env
.DS_Store
rules/filterlists/easylist_*.txt
lib/filterlist-engine.ts
3 changes: 1 addition & 2 deletions addon/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import { initConfig, isEnabledForDomain, showOptOutStatus } from "./utils";
const openDevToolsPanels = new Map<number, chrome.runtime.Port>();

async function loadRules() {
const res = await fetch("./rules.json");
storageSet({
rules: await res.json(),
rules: await (await fetch("./rules.json")).json(),
});
}

Expand Down
4 changes: 4 additions & 0 deletions addon/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@
</div>
<button id="reload">Reload rules</button>
<button id="reset">Reset settings</button>
<div>
<input type="checkbox" id="filterlist" name="filterlist" checked>
<label for="filterlist">Enable cosmetic filterlist</label>
</div>
</fieldset>
<script src="popup.bundle.js"></script>
</body>
Expand Down
7 changes: 7 additions & 0 deletions addon/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ async function init() {
const logsMessagesCheckbox = document.querySelector('input#logs-messages') as HTMLInputElement;
const ruleReloadButton = document.querySelector('#reload') as HTMLButtonElement;
const resetButton = document.querySelector('#reset') as HTMLButtonElement;
const filterlistCheckbox = document.querySelector('input#filterlist') as HTMLInputElement;

// enable proceed button when necessary

Expand Down Expand Up @@ -60,6 +61,7 @@ async function init() {
logsErrorsCheckbox.checked = autoconsentConfig.logs.errors;
logsMessagesCheckbox.checked = autoconsentConfig.logs.messages;
retriesInput.value = autoconsentConfig.detectRetries.toString();
filterlistCheckbox.checked = autoconsentConfig.enableFilterList;
if (autoconsentConfig.autoAction === 'optIn') {
optInRadio.checked = true;
} else if (autoconsentConfig.autoAction === 'optOut') {
Expand Down Expand Up @@ -146,6 +148,11 @@ async function init() {
updateLogsConfig();
});

filterlistCheckbox.addEventListener('change', () => {
autoconsentConfig.enableFilterList = filterlistCheckbox.checked;
storageSet({ config: autoconsentConfig });
});

ruleReloadButton.addEventListener('click', async () => {
const res = await fetch("./rules.json");
storageSet({
Expand Down
16 changes: 9 additions & 7 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ set -ex

ESBUILD="node_modules/.bin/esbuild --bundle"

$ESBUILD --format=iife --target=es2021 --minify playwright/content.ts --outfile=dist/autoconsent.playwright.js
$ESBUILD --format=esm --target=es2021 lib/web.ts --outfile=dist/autoconsent.esm.js
$ESBUILD --format=cjs --target=es2021 --platform=node lib/web.ts --outfile=dist/autoconsent.cjs.js
$ESBUILD --format=iife --define:BUNDLE_FILTERLIST=true --target=es2021 playwright/content.ts --outfile=dist/autoconsent.playwright.js
$ESBUILD --format=esm --define:BUNDLE_FILTERLIST=true --target=es2021 lib/web.ts --outfile=dist/autoconsent.extra.esm.js
$ESBUILD --format=cjs --define:BUNDLE_FILTERLIST=true --target=es2021 --platform=node lib/web.ts --outfile=dist/autoconsent.extra.cjs.js
$ESBUILD --format=esm --define:BUNDLE_FILTERLIST=false --target=es2021 lib/web.ts --outfile=dist/autoconsent.esm.js
$ESBUILD --format=cjs --define:BUNDLE_FILTERLIST=false --target=es2021 --platform=node lib/web.ts --outfile=dist/autoconsent.cjs.js

# Extension
$ESBUILD addon/background.ts --outfile=dist/addon-mv3/background.bundle.js
$ESBUILD addon/content.ts --outfile=dist/addon-mv3/content.bundle.js
$ESBUILD addon/popup.ts --outfile=dist/addon-mv3/popup.bundle.js
$ESBUILD addon/devtools/panel.ts --outfile=dist/addon-mv3/devtools/panel.js
$ESBUILD addon/background.ts --define:BUNDLE_FILTERLIST=true --outfile=dist/addon-mv3/background.bundle.js
$ESBUILD addon/content.ts --define:BUNDLE_FILTERLIST=true --outfile=dist/addon-mv3/content.bundle.js
$ESBUILD addon/popup.ts --define:BUNDLE_FILTERLIST=true --outfile=dist/addon-mv3/popup.bundle.js
$ESBUILD addon/devtools/panel.ts --define:BUNDLE_FILTERLIST=true --outfile=dist/addon-mv3/devtools/panel.js

## Copy extension files into place
mkdir -p dist/addon-firefox
Expand Down
2 changes: 1 addition & 1 deletion lib/cmps/airbnb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { waitFor } from "../utils";
import AutoConsentCMPBase from "./base";

export default class Airbnb extends AutoConsentCMPBase {
name: "airbnb";
name = "airbnb";

runContext: RunContext = {
urlPattern: '^https://(www\\.)?airbnb\\.[^/]+/'
Expand Down
16 changes: 16 additions & 0 deletions lib/dom-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ export class DomActions implements DomActionsProvider {
return !!existingElement;
}

async createOrUpdateStyleSheet(cssText: string, styleSheet?: CSSStyleSheet) {
if (!styleSheet) {
styleSheet = new CSSStyleSheet();
}
styleSheet = await styleSheet.replace(cssText);
return styleSheet;
}

removeStyleSheet(styleSheet?: CSSStyleSheet): boolean {
if (styleSheet) {
styleSheet.replace('');
return true;
}
return false;
}

querySingleReplySelector(selector: string, parent: any = document): HTMLElement[] {
if (selector.startsWith('aria/')) {
return []
Expand Down
45 changes: 45 additions & 0 deletions lib/filterlist-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { FiltersEngine } from '@ghostery/adblocker';
import { extractFeaturesFromDOM } from '@ghostery/adblocker-content';
import { parse as tldtsParse } from 'tldts-experimental';
import { getHidingStyle } from './utils';

export function deserializeFilterList(serializedEngine: Uint8Array) {
return FiltersEngine.deserialize(serializedEngine)
}

export function getCosmeticStylesheet(engine: FiltersEngine): string {
try {
const parsed = tldtsParse(location.href);
const hostname = parsed.hostname || '';
const domain = parsed.domain || '';

const cosmetics = engine.getCosmeticsFilters({
url: location.href,
hostname,
domain,

// this extracts current ids, classes and attributes (depends on the current DOM state)
...extractFeaturesFromDOM([document.documentElement]),

getBaseRules: true,
getInjectionRules: false, // we don't inject scripts atm
getExtendedRules: true,
getRulesFromDOM: true,
getRulesFromHostname: true,

hidingStyle: getHidingStyle('opacity'),
});
return cosmetics.styles;
} catch (e) {
console.error('Error getting cosmetic rules', e);
return '';
}
}

export function getFilterlistSelectors(styles: string): string {
if (styles) {
const selectorsOnly = styles.replace(/\s*{[^\\}]*}\s*/g, ',').replace(/,$/, '');
return selectorsOnly;
}
return '';
}
5 changes: 4 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface DomActionsProvider {

export type RuleBundle = {
autoconsent: AutoConsentCMPRule[];
consentomatic: { [name: string]: ConsentOMaticConfig };
consentomatic?: { [name: string]: ConsentOMaticConfig };
};

export type AutoAction = 'optOut' | 'optIn' | null;
Expand All @@ -52,6 +52,7 @@ export type Config = {
detectRetries: number;
isMainWorld: boolean;
prehideTimeout: number;
enableFilterList: boolean;
logs: {
lifecycle: boolean;
rulesteps: boolean;
Expand All @@ -66,6 +67,7 @@ export type LifecycleState = 'loading' |
'waitingForInitResponse' |
'started' |
'nothingDetected' |
'cosmeticFiltersDetected' |
'cmpDetected' |
'openPopupDetected' |
'runningOptOut' |
Expand All @@ -77,6 +79,7 @@ export type LifecycleState = 'loading' |
'done';

export type ConsentState = {
cosmeticFiltersOn: boolean; // true if cosmetic filter rules are currently applied.
lifecycle: LifecycleState; // What point in the autoconsent lifecycle this script is at.
prehideOn: boolean; // If the script is currently hiding preHide elements.
findCmpAttempts: number; // Number of times we tried to find CMPs in this frame.
Expand Down
9 changes: 7 additions & 2 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ export function getStyleElement(styleOverrideElementId = "autoconsent-css-rules"
}
}

export function getHidingStyle(method: HideMethod) {
const hidingSnippet = method === "opacity" ? `opacity: 0` : `display: none`; // use display by default
return `${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important;`
}

// hide elements with a CSS rule
export function hideElements(
styleEl: HTMLStyleElement,
selector: string,
method: HideMethod = 'display',
): boolean {
const hidingSnippet = method === "opacity" ? `opacity: 0` : `display: none`; // use display by default
const rule = `${selector} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
const rule = `${selector} { ${getHidingStyle(method)} } `;

if (styleEl instanceof HTMLStyleElement) {
styleEl.innerText += rule;
Expand Down Expand Up @@ -79,6 +83,7 @@ export function normalizeConfig(providedConfig: any): Config {
detectRetries: 20,
isMainWorld: false,
prehideTimeout: 2000,
enableFilterList: false,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filterlist should be explicitly enabled by the browser

logs: {
lifecycle: false,
rulesteps: false,
Expand Down
Loading
Loading