ghostery / adblocker

Efficient embeddable adblocker library

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Correct way to inject ad blocker in React Native WebView

Alwinator opened this issue · comments

I am working on a react native app with a web view that should block ads. The web view offers a way to inject javascript code. What is the correct way to inject your ad blocker?

I tried around but found no solution. It could look similar to the code below:

<WebView
  source={{
    uri: 'https://website-with-ads.example.com',
  }}
  javaScriptEnabled={true}
  injectedJavaScript={`
    var my_script = document.createElement('script');
    my_script.setAttribute('src','https://cdn.jsdelivr.net/npm/@cliqz/adblocker-webextension/dist/adblocker.umd.min.js');
    document.head.appendChild(my_script);
`}
/>

Thanks for your support!

Update:
I have built a webpack bundler that automatically injects the javascript in the react native webview. I was able to inject the following script without errors:

import {FiltersEngine} from '@cliqz/adblocker';

FiltersEngine.fromPrebuiltAdsAndTracking(fetch).then(engine => {
  console.log(engine);
});

However, what is the next step? Where can I do the matching? I need something like an onScriptLoading event.

@remusao Any ideas?

commented

Hi @Alwinator,

Unfortunately I am not very familiar with the APIs available in React Native WebView. What you will need is a way to intercept network requests for pages loaded in the WebView, and then ask the adblocker engine which action to take (e.g. block, allow, redirect). If you can figure out which APIs can be used I can help with the next steps.

Best,

@remusao Does it also work by injecting JavaScript? I am able to manipulate the DOM. I need some way to intercept network requests. (Completely independent from the ReactNative WebView)

Idea 1

I thought of overwriting the XMLHttpRequest.prototype.send, but this does not work for HTML script and iframe elements.

Idea 2

Another approach would be to get all scripts and iframes and check them, which also does not work fine.

function processElement(element) {
 // process element.src with engine
 // and then element.remove()
}
const observer = new MutationObserver((mutations, observer) => {
  for (const mutation of mutations) {
    for (const addedNode of mutation.addedNodes) {
      if (addedNode.tagName === 'SCRIPT' || addedNode.tagName === 'IFRAME') {
        processElement(addedNode);
      }
    }
  }
});
observer.observe(document.body, {childList: true, subtree: true});

document.addEventListener('DOMContentLoaded', () => {
  Array.from(document.getElementsByTagName('script')).forEach(processElement);
  Array.from(document.getElementsByTagName('iframe')).forEach(processElement);
});

Idea 3

Maybe a better approach would be to achieve it with a service worker as described here. I will try this approach in the next few days when I have time.

Do you think it is in general possible without a web browser extension?

commented

@Alwinator i like your Service Worker idea. If you control the website and are able to inject a service worker it should be possible to load the adblocker there and perform the blocking or ads from there. You would likely need to write some amount of boiler plate since the library does not have a nice abstraction for that.