Skip to content

Commit

Permalink
feat: emit cosmetic rules on match
Browse files Browse the repository at this point in the history
  • Loading branch information
seia-soto committed Jun 9, 2024
1 parent 2c93101 commit ba99f8a
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 50 deletions.
26 changes: 22 additions & 4 deletions packages/adblocker-electron-example/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { app, BrowserWindow } from 'electron';
import fetch from 'cross-fetch';
import { app, BrowserWindow } from 'electron';
import { readFileSync, writeFileSync } from 'fs';

import { ElectronBlocker, fullLists, Request } from '@cliqz/adblocker-electron';
import {
CosmeticFilter,
ElectronBlocker,
fullLists,
NetworkFilter,
Request,
} from '@cliqz/adblocker-electron';
import { MatchingContext } from '@cliqz/adblocker';

function getUrlToLoad(): string {
let url = 'https://google.com';
Expand Down Expand Up @@ -41,6 +48,10 @@ async function createWindow() {

blocker.enableBlockingInSession(mainWindow.webContents.session);

blocker.on('request-allowed', (request: Request) => {
console.log('allow', request.tabId, request.url);
});

blocker.on('request-blocked', (request: Request) => {
console.log('blocked', request.tabId, request.url);
});
Expand All @@ -53,8 +64,8 @@ async function createWindow() {
console.log('whitelisted', request.tabId, request.url);
});

blocker.on('csp-injected', (request: Request) => {
console.log('csp', request.url);
blocker.on('csp-injected', (csps: string, request: Request) => {
console.log('csp', csps, request.url);
});

blocker.on('script-injected', (script: string, url: string) => {
Expand All @@ -65,6 +76,13 @@ async function createWindow() {
console.log('style', style.length, url);
});

blocker.on(
'filter-matched',
(filter: CosmeticFilter | NetworkFilter, context: MatchingContext) => {
console.log('filter-matched', filter, context);
},
);

mainWindow.loadURL(getUrlToLoad());
mainWindow.webContents.openDevTools();

Expand Down
12 changes: 12 additions & 0 deletions packages/adblocker-electron/adblocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ export class ElectronBlocker extends FiltersEngine {
getExtendedRules: true,
getRulesFromHostname: true,
getRulesFromDOM: false, // Only done on updates (see `onGetCosmeticFiltersUpdated`)

reference: {
frameId: event.frameId,
processId: event.processId,
},
});

if (active === false) {
Expand Down Expand Up @@ -233,6 +238,13 @@ export class ElectronBlocker extends FiltersEngine {

// This will be done every time we get information about DOM mutation
getRulesFromDOM: true,

reference: {
frameId: event.frameId,
processId: event.processId,

lifecycle: msg.lifecycle,
},
});

if (active === false) {
Expand Down
21 changes: 16 additions & 5 deletions packages/adblocker-playwright-example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import * as pw from 'playwright';

await blocker.enableBlockingInPage(page);

blocker.on('request-allowed', (request: Request) => {
console.log('allow', request.url);
});

blocker.on('request-blocked', (request: Request) => {
console.log('blocked', request.url);
});
Expand All @@ -29,19 +33,26 @@ import * as pw from 'playwright';
console.log('whitelisted', request.url);
});

blocker.on('csp-injected', (request: Request) => {
console.log('csp', request.url);
blocker.on('csp-injected', (csps: string, request: Request) => {
console.log('csp', request.url, csps);
});

blocker.on('script-injected', (script: string, url: string) => {
console.log('script', script.length, url);
console.log('script', url, script.length);
});

blocker.on('style-injected', (style: string, url: string) => {
console.log('style', style.length, url);
console.log('style', url, style.length);
});

await page.goto('https://www.mangareader.to/');
blocker.on(
'filter-matched',
(filter: CosmeticFilter | NetworkFilter, context: MatchingContext) => {
console.log('filter-matched', filter, context);
},
);

await page.goto('https://www.mangareader.net/');
await page.screenshot({ path: 'output.png' });
await blocker.disableBlockingInPage(page);
await browser.close();
Expand Down
20 changes: 15 additions & 5 deletions packages/adblocker-puppeteer-example/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { fullLists, PuppeteerBlocker, Request } from '@cliqz/adblocker-puppeteer';
import fetch from 'cross-fetch';
import * as puppeteer from 'puppeteer';
import { promises as fs } from 'fs';

function getUrlToLoad(): string {
let url = 'https://www.mangareader.to/';
Expand Down Expand Up @@ -35,6 +34,10 @@ function getUrlToLoad(): string {
const page = await browser.newPage();
await blocker.enableBlockingInPage(page);

blocker.on('request-allowed', (request: Request) => {
console.log('allow', request.url);
});

blocker.on('request-blocked', (request: Request) => {
console.log('blocked', request.url);
});
Expand All @@ -47,17 +50,24 @@ function getUrlToLoad(): string {
console.log('whitelisted', request.url);
});

blocker.on('csp-injected', (request: Request) => {
console.log('csp', request.url);
blocker.on('csp-injected', (csps: string, request: Request) => {
console.log('csp', request.url, csps.length);
});

blocker.on('script-injected', (script: string, url: string) => {
console.log('script', script.length, url);
console.log('script', url, script.length);
});

blocker.on('style-injected', (style: string, url: string) => {
console.log('style', style.length, url);
console.log('style', url, style.length);
});

blocker.on(
'filter-matched',
(filter: CosmeticFilter | NetworkFilter, context: MatchingContext) => {
console.log('filter-matched', filter, context);
},
);

await page.goto(getUrlToLoad());
})();
32 changes: 26 additions & 6 deletions packages/adblocker-webextension-example/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import { browser } from 'webextension-polyfill-ts';

import {
BlockingResponse,
CosmeticFilter,
fullLists,
HTMLSelector,
NetworkFilter,
Request,
WebExtensionBlocker,
} from '@cliqz/adblocker-webextension';
import { MatchingContext } from '@cliqz/adblocker';

/**
* Keep track of number of network requests altered for each tab
Expand Down Expand Up @@ -62,6 +65,11 @@ WebExtensionBlocker.fromLists(fetch, fullLists, {
}).then((blocker: WebExtensionBlocker) => {
blocker.enableBlockingInBrowser(browser);

blocker.on('request-allowed', (request: Request, result: BlockingResponse) => {
incrementBlockedCounter(request, result);
console.log('allow', request.url);
});

blocker.on('request-blocked', (request: Request, result: BlockingResponse) => {
incrementBlockedCounter(request, result);
console.log('block', request.url);
Expand All @@ -72,21 +80,33 @@ WebExtensionBlocker.fromLists(fetch, fullLists, {
console.log('redirect', request.url, result);
});

blocker.on('csp-injected', (request: Request) => {
console.log('csp', request.url);
blocker.on('request-whitelisted', (request: Request, result: BlockingResponse) => {
incrementBlockedCounter(request, result);
console.log('whitelist', request.url, result);
});

blocker.on('html-filtered', (htmlSelectors: HTMLSelector[], url: string) => {
console.log('html selectors', url, htmlSelectors);
});

blocker.on('csp-injected', (csps: string, request: Request) => {
console.log('csp', request.url, csps.length);
});

blocker.on('script-injected', (script: string, url: string) => {
console.log('script', script.length, url);
console.log('script', url, script.length);
});

blocker.on('style-injected', (style: string, url: string) => {
console.log('style', url, style.length);
});

blocker.on('html-filtered', (htmlSelectors: HTMLSelector[]) => {
console.log('html selectors', htmlSelectors);
});
blocker.on(
'filter-matched',
(filter: CosmeticFilter | NetworkFilter, context: MatchingContext) => {
console.log('filter-matched', filter, context);
},
);

console.log('Ready to roll!');
});
15 changes: 15 additions & 0 deletions packages/adblocker-webextension/adblocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,11 @@ export class WebExtensionBlocker extends FiltersEngine {
getExtendedRules: false,
getRulesFromDOM: false,
getRulesFromHostname: true,

reference: {
tabId: details.tabId,
frameId: details.frameId,
},
});
if (active === false) {
return;
Expand Down Expand Up @@ -460,6 +465,11 @@ export class WebExtensionBlocker extends FiltersEngine {
getExtendedRules: false,
getRulesFromDOM: false,
getRulesFromHostname: false,

reference: {
tabId: sender.tab?.id,
frameId: sender.frameId,
},
});

if (active === false) {
Expand Down Expand Up @@ -497,6 +507,11 @@ export class WebExtensionBlocker extends FiltersEngine {

// This will be done every time we get information about DOM mutation
getRulesFromDOM: msg.lifecycle === 'dom-update',

reference: {
tabId: sender.tab?.id,
frameId: sender.frameId,
},
});

if (active === false) {
Expand Down
7 changes: 6 additions & 1 deletion packages/adblocker/adblocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

export { default as FiltersEngine, ENGINE_VERSION, BlockingResponse } from './src/engine/engine';
export {
default as FiltersEngine,
ENGINE_VERSION,
BlockingResponse,
MatchingContext,
} from './src/engine/engine';
export { default as ReverseIndex } from './src/engine/reverse-index';
export {
default as Request,
Expand Down
45 changes: 37 additions & 8 deletions packages/adblocker/src/engine/bucket/cosmetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,11 @@ export default class CosmeticFilterBucket {
hostname: string;

isFilterExcluded?: (filter: CosmeticFilter) => boolean;
}): CosmeticFilter[] {
}): {
rules: CosmeticFilter[];
matches: CosmeticFilter[];
exceptions: Map<CosmeticFilter, CosmeticFilter>;
} {
// Tokens from `hostname` and `domain` which will be used to lookup filters
// from the reverse index. The same tokens are re-used for multiple indices.
const hostnameTokens = createLookupTokens(hostname, domain);
Expand All @@ -364,21 +368,34 @@ export default class CosmeticFilterBucket {
return true;
});

const exceptions: Map<CosmeticFilter, CosmeticFilter> = new Map();

// If we found at least one candidate, check if we have unhidden rules.
const disabledRules: Set<string> = new Set();
const disabledRules: Map<string, CosmeticFilter> = new Map();
if (rules.length !== 0) {
this.unhideIndex.iterMatchingFilters(hostnameTokens, (rule: CosmeticFilter) => {
if (rule.match(hostname, domain) && !isFilterExcluded?.(rule)) {
disabledRules.add(rule.getSelector());
disabledRules.set(rule.getSelector(), rule);
}

return true;
});
}

return rules.filter(
(rule) => disabledRules.size === 0 || disabledRules.has(rule.getSelector()) === false,
);
const modifications: CosmeticFilter[] = [];
for (const rule of rules) {
if (disabledRules.size !== 0 && disabledRules.has(rule.getSelector())) {
exceptions.set(rule, disabledRules.get(rule.getSelector())!);
} else {
modifications.push(rule);
}
}

return {
rules: modifications,
matches: rules,
exceptions,
};
}

/**
Expand Down Expand Up @@ -425,6 +442,8 @@ export default class CosmeticFilterBucket {
injections: CosmeticFilter[];
extended: IMessageFromBackground['extended'];
stylesheet: string;
matches: CosmeticFilter[];
exceptions: Map<CosmeticFilter, CosmeticFilter>;
} {
// Tokens from `hostname` and `domain` which will be used to lookup filters
// from the reverse index. The same tokens are re-used for multiple indices.
Expand Down Expand Up @@ -505,6 +524,10 @@ export default class CosmeticFilterBucket {
);
}

// Additional data for engine events
const matches: CosmeticFilter[] = [...rules];
const exceptions: Map<CosmeticFilter, CosmeticFilter> = new Map();

const extended: CosmeticFilter[] = [];
const injections: CosmeticFilter[] = [];
const styles: CosmeticFilter[] = [];
Expand All @@ -519,10 +542,12 @@ export default class CosmeticFilterBucket {
// Collect unhidden selectors. They will be used to filter-out canceled
// rules from other indices.
let injectionsDisabled = false;
const disabledRules: Set<string> = new Set();
// Create a binding from here to provide a map of rules with exception rules
// with minimal performance impact
const disabledRules: Map<string, CosmeticFilter> = new Map();
this.unhideIndex.iterMatchingFilters(hostnameTokens, (rule: CosmeticFilter) => {
if (rule.match(hostname, domain) && !isFilterExcluded?.(rule)) {
disabledRules.add(rule.getSelector());
disabledRules.set(rule.getSelector(), rule);

// Detect special +js() rules to disable scriptlet injections
if (
Expand All @@ -541,6 +566,8 @@ export default class CosmeticFilterBucket {
for (const rule of rules) {
// Make sure `rule` is not un-hidden by a #@# filter
if (disabledRules.size !== 0 && disabledRules.has(rule.getSelector())) {
exceptions.set(rule, disabledRules.get(rule.getSelector())!);

continue;
}

Expand Down Expand Up @@ -606,6 +633,8 @@ export default class CosmeticFilterBucket {
extended: extendedProcessed,
injections,
stylesheet,
matches,
exceptions,
};
}

Expand Down
Loading

0 comments on commit ba99f8a

Please sign in to comment.