Injecting styles
enestatli opened this issue · comments
Hello, thanks for this really useful package.
I cannot inject styles to the website, actually somehow it's broken I think. Blocking urls work flawlessly. Do you have any idea why it's happening?
Platform:
Android, Real Device
"@cliqz/adblocker": "1.23.7",
"react-native-webview": "^11.17.2",
"react": "^17.0.2",
"react-native": "npm:react-native-tvos@0.66.3-0",
const onInterceptRequest: OnInterceptRequest = event => {
const {styles, scripts} = engine.getCosmeticsFilters({
url: event.url,
domain: event.url,
hostname: event.url,
});
if (couter === 0) {
// console.log('styles', typeof styles, styles);
setStyles(styles);
setScrpts(scripts);
couter++;
}
};
<WebView
onInterceptRequest={onInterceptRequest}
injectedJavaScript={`
alert('start')
function addCss(cssCode) {
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = cssCode;
} else {
styleElement.appendChild(document.createTextNode(cssCode));
}
document.getElementsByTagName("head")[0].appendChild(styleElement);
};
var str = '${s.replace(/'/gm, '"').replace(/(\r\n|\n|\r)/gm, '')}';
addCss(str);
alert('finishes')
`}
/>
const engine = await FiltersEngine.fromLists(fetch, [
'https://easylist.to/easylist/easylist.txt',
]);
Hi @enestatli,
I am not very familiar with React native to be honest. From the example above, did you confirm that engine.getCosmeticsFilters
returns some styles but then the injection does not work?
In a previous version of the adblocker we used to have code for this purpose, it looked like the following:
export function injectCSSRule(rule: string, doc: Document): void {
const parent = doc.head || doc.getElementsByTagName('head')[0] || doc.documentElement;
if (parent !== null) {
const css = doc.createElement('style');
css.type = 'text/css';
css.id = 'adblocker-rules';
css.appendChild(doc.createTextNode(rule));
parent.appendChild(css);
}
}
Maybe you can try to integrate this function in your project and see if it helps?
Best,
Thank you for replied me back!
I am not very familiar with React native to be honest. From the example above, did you confirm that
engine.getCosmeticsFilters
returns some styles but then the injection does not work?
I can confirm it returns very long styles ends with ...table[height="630"], table[height="640"], table[style*="max-width:"], td > a[href], video { display: none !important; }
Maybe you can try to integrate this function in your project and see if it helps?
Let me try, I will let you know, thank you!
Hey @remusao,
Unfortunately that didn't work either, it has to be in styles I guess. Also, without replacing ' to " and removing all line returns with the following code var str = '${s.replace(/(\r\n|\n|\r)/gm, '')}';
injected function does not work.
Maybe I should try my chance by injecting scripts, can you send me an example?
Do you have any idea why easylist matches "any" entered site url? For example, it matches with google.com or facebook.com. If I am able to handle this, I do not need to inject anything :)
Best
One way to go about it would be to try to inject some stylesheet in a page without the adblocker, to understand why the injection in the page is not working properly. From what I understand, it does not seem like the issue you encounter is related to the adblocker library but instead has to do with the boiler plate to integrate it into react native.
Regarding the easylist matching anything, can you share a particular request which which is matched but you would not expect it to? I can have a look then.
Best,
One way to go about it would be to try to inject some stylesheet in a page without the adblocker, to understand why the injection in the page is not working properly. From what I understand, it does not seem like the issue you encounter is related to the adblocker library but instead has to do with the boiler plate to integrate it into react native.
I have tried injecting the following, it paints the body. const injected = ` document.body.style.backgroundColor = 'red'; true; `;
Here is the list of styles engine.getCosmeticsFilters
returns that I am trying to inject to the page
https://raw.githubusercontent.com/enestatli/adblock-stylesheet-response/main/adblock-stylesheet.txt
Regarding the easylist matching anything, can you share a particular request which which is matched but you would not expect it to? I can have a look then.
const onInterceptRequest: OnInterceptRequest = event => {
const {match} = engine.match(
Request.fromRawDetails({
type: 'script',
url: event.url,
}),
);
console.log('MATCH', match);
if (!match) {
console.log('Not match URL:', event.url);
}
if (match) {
console.log('Match block URL: ', event.url);
// return 'block';
}
return 'continue';
};
LOG MATCH true
LOG Match block URL: https://m.facebook.com/
LOG MATCH true
LOG Match block URL: http://google.com/
LOG MATCH true
LOG Match block URL: http://bbc.co.uk/
LOG MATCH true
LOG Match block URL: http://eksisozluk.com/
Basically it matches every website that I am trying to enter.
I have tried injecting the following, it paints the body.
const injected = ` document.body.style.backgroundColor = 'red'; true; `;
This is injected as a script right? I was wondering if you tried to inject a stylesheet, something like body { display: none !important; }
.
Basically it matches every website that I am trying to enter.
That is surprising. Can change the code above to print the matching rule? If you initialize the engine in debug mode:
const engine = await FiltersEngine.fromLists(fetch, [
'https://easylist.to/easylist/easylist.txt',
], { debug: true });
Then,
const {match, filter} = engine.match(
Request.fromRawDetails({
type: 'script',
url: event.url,
}),
);
console.log(`matching filter: ${filter}`);
@enestatli
try this, it works on my rn project.
const {styles} = result;
if (styles) {
let cssText = '';
let prev = '';
for (let i of styles) {
if (i === '"' && prev !== '\\') {
cssText = cssText + '\\"';
}
if (i === '"' && prev === '\\') {
cssText = cssText + '"';
}
if (i !== '"' && i !== '\r' && i !== '\n' && i !== '\r\n') {
cssText = cssText + i;
}
prev = i;
}
const funcText = `
(function() {
var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode("${cssText}"));
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
})();
true;
`;
webviewRef.current?.injectJavaScript?.(funcText);
}
Sorry for the late response!
This is injected as a script right? I was wondering if you tried to inject a stylesheet, something like
body { display: none !important; }
.
Yes, it's right.
console.log(
matching filter: ${filter}
);
Current terminal shows same logs for every request which is useless, I will send you the logs when working on my Windows machine. Btw, task is postponed because of it takes too much time needed for initializing engine. Thanks for your help and making such a beautiful module! :)
@enestatli try this, it works on my rn project.
const {styles} = result; if (styles) { let cssText = ''; for (let i of styles) { if (i === '"') { cssText = cssText + '\\"'; } else if (i !== '\r' && i !== '\n' && i !== '\r\n') { cssText = cssText + i; } } const funcText = ` (function() { var style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("${cssText}")); var head = document.getElementsByTagName("head")[0]; head.appendChild(style); })(); true; `; webviewRef.current?.injectJavaScript?.(funcText); }
@saber0318 Hey, thank you. I will try.
Did you try for both platforms? Can you also please tell me, how long does the engine take to initialize?
- only android.
- it take about 150ms to excute FiltersEngine.parse in development environment.
- i modified the code, and i am still perfecting it.
2. it take about 150ms to excute FiltersEngine.parse in development environment.
That's amazing. Can you please when/where exactly you initialize the engine? For me it tooks too much time when parsing easylist.txt
Just to make sure I am not missing something here, but the FiltersEngine.parse
only needs to be executed once when the app loads and then the engine can be reused for all requests right? If this 150ms at app start is still too slow, it is possible to use FiltersEngine.deserialize
to load an already created and serialized engine, which is significantly faster (it should be a few milliseconds).
Just to make sure I am not missing something here, but the
FiltersEngine.parse
only needs to be executed once when the app loads and then the engine can be reused for all requests right?
Yes, it is initialized only once. I wish it took 150ms, but more than 4500ms right now. Might be the case related to my project scope. I will investigate further and let you know. Thank you!
Out of curiosity, which hardware are you using for the test?
Out of curiosity, which hardware are you using for the test?
Simulators, iPhone 13 and Android Google 3XL.
Closing this. Please reopen if you'd like to continue the discussion with your findings on performance.