ghostery / adblocker

Efficient embeddable adblocker library

Home Page:https://www.ghostery.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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',
      ]);
commented

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

commented

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.

commented

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?

@enestatli

  1. only android.
  2. it take about 150ms to excute FiltersEngine.parse in development environment.
  3. i modified the code, and i am still perfecting it.

@saber0318

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

commented

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!

commented

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.

commented

Closing this. Please reopen if you'd like to continue the discussion with your findings on performance.