Skip to content

Commit 8764c4c

Browse files
committed
Ghostery 10: simple custom filters editor
1 parent 237b125 commit 8764c4c

File tree

8 files changed

+276
-9
lines changed

8 files changed

+276
-9
lines changed

extension-manifest-v2/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"foundation-sites": "^6.6.2",
5252
"ghostery-common": "^1.3.12",
5353
"history": "^4.10.1",
54-
"hybrids": "^8.2.5",
54+
"hybrids": "^8.2.11",
5555
"linkedom": "^0.14.21",
5656
"moment": "^2.29.1",
5757
"prop-types": "^15.6.2",

extension-manifest-v3/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@ghostery/ui": "^1.0.0",
4040
"@github/relative-time-element": "^4.3.0",
4141
"@whotracksme/webextension-packages": "^5.0.3",
42-
"hybrids": "^8.2.5",
42+
"hybrids": "^8.2.11",
4343
"idb": "^7.1.1",
4444
"jwt-decode": "^4.0.0",
4545
"tldts-experimental": "^6.0.19"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
* Ghostery Browser Extension
3+
* https://www.ghostery.com/
4+
*
5+
* Copyright 2017-present Ghostery GmbH. All rights reserved.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at http://mozilla.org/MPL/2.0
10+
*/
11+
12+
import { html, store } from 'hybrids';
13+
import { detectFilterType } from '@cliqz/adblocker';
14+
import convert from '../dnr-converter.js';
15+
import CustomFiltersInput from '../store/custom-filters-input.js';
16+
17+
async function onSave(host) {
18+
const { networkFilters, cosmeticFilters } = host.filters;
19+
const output = {
20+
networkFilters,
21+
cosmeticFilters,
22+
dnrRules: [],
23+
};
24+
const dnrErrors = [];
25+
26+
const results = await Promise.allSettled(
27+
networkFilters.map((filter) => convert(filter)),
28+
);
29+
30+
for (const result of results) {
31+
dnrErrors.push(...result.value.errors);
32+
output.dnrRules.push(...result.value.rules);
33+
}
34+
35+
host.dnrErrors = dnrErrors;
36+
37+
if (!dnrErrors.length) {
38+
store.submit(host.input);
39+
host.output = output;
40+
}
41+
}
42+
43+
export default {
44+
input: store(CustomFiltersInput, { draft: true }),
45+
output: {
46+
get: async () => {
47+
const {
48+
networkFilters = [],
49+
cosmeticFilters = [],
50+
dnrRules = [],
51+
} = (await chrome.storage.local.get(['custom-filters-output']))[
52+
'custom-filters-output'
53+
] || {};
54+
55+
return {
56+
networkFilters,
57+
cosmeticFilters,
58+
dnrRules,
59+
};
60+
},
61+
set: async (
62+
_,
63+
{ networkFilters = [], cosmeticFilters = [], dnrRules = [] } = {},
64+
lastValue,
65+
) => {
66+
if (lastValue) {
67+
await chrome.storage.local.set({
68+
'custom-filters-output': {
69+
networkFilters,
70+
cosmeticFilters,
71+
dnrRules,
72+
},
73+
});
74+
}
75+
return {
76+
networkFilters,
77+
cosmeticFilters,
78+
dnrRules,
79+
};
80+
},
81+
},
82+
dnrErrors: { set: (_, values = []) => values },
83+
filters: (host) => {
84+
const networkFilters = [];
85+
const cosmeticFilters = [];
86+
const errors = [];
87+
88+
let filters = [];
89+
90+
if (store.ready(host.input)) {
91+
filters = host.input.text
92+
.split('\n')
93+
.map((f) => f.trim())
94+
.filter(Boolean);
95+
}
96+
97+
for (const filter of filters) {
98+
const filterType = detectFilterType(filter);
99+
switch (filterType) {
100+
case 1: // NETWORK
101+
networkFilters.push(filter);
102+
break;
103+
case 2: // COSMETIC
104+
cosmeticFilters.push(filter);
105+
break;
106+
default:
107+
errors.push(`Filter not supported: '${filter}'`);
108+
}
109+
}
110+
111+
return {
112+
networkFilters,
113+
cosmeticFilters,
114+
errors,
115+
};
116+
},
117+
content: ({ input, filters, output, dnrErrors }) => html`
118+
<template layout="column gap:3">
119+
${store.ready(input) &&
120+
html`
121+
<textarea rows="10" oninput="${html.set(input, 'text')}">
122+
${input.text}</textarea
123+
>
124+
`}
125+
<div layout="row gap items:center">
126+
<ui-button
127+
size="small"
128+
type=${filters.errors.length > 0 ? 'outline-error' : 'outline'}
129+
disabled=${filters.errors.length > 0 || dnrErrors.length > 0}
130+
onclick="${onSave}"
131+
layout="shrink:0"
132+
>
133+
<a>Update</a>
134+
</ui-button>
135+
</div>
136+
<section layout="gap items:center">
137+
<h4>Errors</h4>
138+
<ul>
139+
${[...filters.errors, ...dnrErrors].map(
140+
(error) =>
141+
html`<li>
142+
<ui-text color="danger-500">${error}</ui-text>
143+
</li>`,
144+
)}
145+
</ul>
146+
<h4>Filters</h4>
147+
<div>Network filters: ${filters.networkFilters.length}</div>
148+
<div>Cosmetic filters: ${filters.cosmeticFilters.length}</div>
149+
<div>Filter errors: ${filters.errors.length}</div>
150+
${html.resolve(
151+
output.then(
152+
({ networkFilters, cosmeticFilters, dnrRules }) =>
153+
html`
154+
<h4>Output</h4>
155+
<div>Network filters: ${networkFilters.length}</div>
156+
<div>Cosmetic filters: ${cosmeticFilters.length}</div>
157+
<div>DNR rules: ${dnrRules.length}</div>
158+
<ul>
159+
${dnrRules.map(
160+
(rule) => html`<li>${JSON.stringify(rule, null, 2)}</li>`,
161+
)}
162+
</ul>
163+
`,
164+
),
165+
)}
166+
</section>
167+
</template>
168+
`,
169+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Ghostery Browser Extension
3+
* https://www.ghostery.com/
4+
*
5+
* Copyright 2017-present Ghostery GmbH. All rights reserved.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at http://mozilla.org/MPL/2.0
10+
*/
11+
12+
const requests = new Map();
13+
let requestCount = 0;
14+
let iframe;
15+
let isReady;
16+
17+
function createIframe() {
18+
window.addEventListener('message', (event) => {
19+
const requestId = event.data.rules.shift().condition.urlFilter;
20+
requests.get(requestId)(event.data);
21+
requests.delete(requestId);
22+
});
23+
24+
iframe = document.createElement('iframe');
25+
iframe.setAttribute('src', 'https://ghostery.github.io/urlfilter2dnr/');
26+
iframe.setAttribute('style', 'display: none;');
27+
28+
return new Promise((resolve) => {
29+
iframe.addEventListener('load', () => resolve());
30+
document.head.appendChild(iframe);
31+
});
32+
}
33+
34+
async function convert(filter) {
35+
if (!isReady) {
36+
isReady = createIframe();
37+
}
38+
await isReady;
39+
40+
const requestId = `request${requestCount++}`;
41+
iframe.contentWindow.postMessage(
42+
{
43+
action: 'convert',
44+
converter: 'adguard',
45+
filters: [requestId, filter],
46+
},
47+
'*',
48+
);
49+
return new Promise((resolve) => {
50+
requests.set(requestId, resolve);
51+
});
52+
}
53+
54+
export default convert;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { store } from 'hybrids';
2+
3+
const CustomFiltersInput = {
4+
text: '',
5+
[store.connect]: {
6+
async get() {
7+
const storage = await chrome.storage.local.get(['custom-filters-input']);
8+
return {
9+
text: storage['custom-filters-input'] || '',
10+
};
11+
},
12+
async set(_, { text }) {
13+
await chrome.storage.local.set({ 'custom-filters-input': text });
14+
return { text };
15+
},
16+
},
17+
};
18+
19+
export default CustomFiltersInput;

extension-manifest-v3/src/pages/settings/views/privacy.js

+25
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,31 @@ export default {
162162
></ui-settings-toggle>
163163
</div>
164164
</div>
165+
<ui-text type="headline-m" mobile-type="headline-s">
166+
Custom filters
167+
</ui-text>
168+
<div>
169+
<ui-text
170+
type="body-l"
171+
mobile-type="body-m"
172+
color="gray-600"
173+
layout="inline"
174+
>
175+
Create your own ad-blocking rules to customise your Ghostery
176+
experience. Find information on how to create them
177+
</ui-text>
178+
<ui-text underline color="primary-500" layout="inline">
179+
<a
180+
href="https://adguard.com/kb/general/ad-filtering/create-own-filters/"
181+
target="_blank"
182+
rel="noreferrer"
183+
>here</a
184+
></ui-text
185+
>.
186+
</div>
187+
<div layout="items:start">
188+
<gh-settings-custom-filters></gh-settings-custom-filters>
189+
</div>
165190
</section>
166191
167192
<gh-settings-devtools></gh-settings-devtools>

package-lock.json

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/ui/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@
2424
"test": "npm run lint"
2525
},
2626
"dependencies": {
27-
"hybrids": "^8.2.5"
27+
"hybrids": "^8.2.11"
2828
}
2929
}

0 commit comments

Comments
 (0)