import type { Page } from 'brave-real-puppeteer-core';
import { Logger } from './logger';
const log = new Logger();
/**
* NATIVE/CONTENT AD BLOCKING
* Blocks: Taboola, Outbrain, Revcontent, MGID, sponsored content, native ads
*/
export async function injectNativeAdBlocking(page: Page): Promise<void> {
// ==========================================
// 1. BLOCK NATIVE AD NETWORK REQUESTS
// ==========================================
const client = await page.target().createCDPSession();
await client.send('Network.enable');
// Block native ad network requests at network level
await client.send('Network.setBlockedURLs', {
urls: [
// Taboola
'*cdn.taboola.com*', '*trc.taboola.com*', '*api.taboola.com*',
'*taboola.com/libtrc*', '*images.taboola.com*',
// Outbrain
'*outbrain.com*', '*widgets.outbrain.com*', '*paid.outbrain.com*',
'*log.outbrainimg.com*', '*tr.outbrain.com*',
// Revcontent
'*trends.revcontent.com*', '*cdn.revcontent.com*',
'*assets.revcontent.com*', '*labs-cdn.revcontent.com*',
// MGID
'*mgid.com*', '*servicer.mgid.com*', '*jsc.mgid.com*',
// Content.ad
'*content.ad*', '*api.content.ad*',
// Yahoo/Gemini native ads
'*ads.yahoo.com*', '*gemini.yahoo.com*',
// Facebook Audience Network
'*an.facebook.com*', '*fb.com/audience*',
// Other native ad networks
'*zergnet.com*', '*dianomi.com*', '*nativo.net*',
'*sharethrough.com*', '*triplelift.com*',
'*stackadapt.com*', '*adroll.com*'
]
});
// ==========================================
// 2. CDP SCRIPT INJECTION FOR NATIVE ADS
// ==========================================
await client.send('Page.enable');
await client.send('Page.addScriptToEvaluateOnNewDocument', {
source: `
(function() {
'use strict';
// ==========================================
// NATIVE AD NETWORKS BLOCKING
// ==========================================
const NATIVE_AD_GLOBALS = [
// Taboola
'_taboola', 'TRC', 'Taboola', '__trcq', 'tbl_dtd',
// Outbrain
'OB_SETTINGS', 'OBR', 'OUTBRAIN', 'OB_rele498',
// Revcontent
'rcWidget', 'revcontentSettings', 'revcontent',
// MGID
'_mgq', 'MGID', 'mgid',
// ZergNet
'ZergNet', 'zergnet',
// Content.ad
'contentad', 'contentAd',
// Dianomi
'dianomi',
// Nativo
'nativo', 'NtvPlacement',
// ShareThrough
'sharethrough', 'STR'
];
// Block all native ad global objects
NATIVE_AD_GLOBALS.forEach(name => {
try {
Object.defineProperty(window, name, {
get: function() {
console.log('[NativeAdBlocker] Blocked:', name);
return undefined;
},
set: function() { return true; },
configurable: false
});
} catch(e) {}
});
// ==========================================
// BLOCK TABOOLA SCRIPT INJECTION
// ==========================================
// Taboola uses _taboola.push() to inject widgets
window._taboola = new Proxy([], {
get: function(target, prop) {
if (prop === 'push') {
return function() {
console.log('[NativeAdBlocker] Blocked Taboola push');
return 0;
};
}
if (prop === 'length') return 0;
return undefined;
},
set: function() { return true; }
});
// ==========================================
// BLOCK OUTBRAIN SCRIPT INJECTION
// ==========================================
window.OBR = {
extern: { video: function() {} },
loaded: true
};
window.OUTBRAIN = {
widget: {
render: function() { console.log('[NativeAdBlocker] Blocked Outbrain render'); }
}
};
// ==========================================
// NATIVE AD ELEMENT DETECTION & REMOVAL
// ==========================================
const NATIVE_AD_SELECTORS = [
// Taboola
'.trc_rbox_container', '.trc-content-sponsored', '.trc_related_container',
'#taboola-below-article-thumbnails', '#taboola-right-rail-thumbnails',
'[id*="taboola"]', '[class*="taboola"]', '[data-taboola]',
// Outbrain
'.OUTBRAIN', '.ob-widget', '.ob-widget-section', '.outbrain',
'[id*="outbrain"]', '[class*="outbrain"]', '[data-widget-id*="outbrain"]',
// Revcontent
'[class*="revcontent"]', '[id*="revcontent"]', '.rc-wc',
// MGID
'[class*="mgid"]', '[id*="mgid"]', '.mgbox',
// ZergNet
'[class*="zergnet"]', '[id*="zergnet"]',
// Generic native/sponsored content
'[class*="native-ad"]', '[class*="native_ad"]', '[class*="nativeAd"]',
'[class*="sponsored-content"]', '[class*="sponsored_content"]',
'[class*="promoted-content"]', '[class*="promoted_content"]',
'[class*="partner-content"]', '[class*="partner_content"]',
'[class*="paid-content"]', '[class*="paid_content"]',
'[class*="recommended-"]', '[class*="recommendations"]',
// Sponsored labels
'[aria-label*="Sponsored"]', '[aria-label*="Advertisement"]',
'[data-tracking*="sponsored"]', '[data-ad-type]',
// Content.ad
'[class*="content-ad"]', '[id*="content-ad"]',
// ShareThrough
'[class*="sharethrough"]', '[data-str-native]',
// Dianomi
'[class*="dianomi"]', '[id*="dianomi"]',
// More specific selectors
'.dfp-ad', '.post-sponsorship', '.sponsored-post',
'.ad-widget', '.widget_ad', '.widget-ad'
];
// Sponsored text patterns
const SPONSORED_TEXT_PATTERNS = [
'sponsored', 'promoted', 'advertisement', 'ad by',
'paid content', 'partner content', 'from our partners',
'around the web', 'you may like', 'recommended for you',
'more from', 'trending now', 'paid post', 'branded content'
];
function isNativeAd(el) {
if (!el) return false;
// Check class/id
const className = (el.className || '').toLowerCase();
const id = (el.id || '').toLowerCase();
const adIndicators = ['taboola', 'outbrain', 'revcontent', 'mgid',
'zergnet', 'native-ad', 'sponsored', 'promoted', 'partner'];
if (adIndicators.some(ind => className.includes(ind) || id.includes(ind))) {
return true;
}
// Check for sponsored text in small elements
const text = (el.innerText || '').toLowerCase();
if (text.length < 100) {
if (SPONSORED_TEXT_PATTERNS.some(p => text.includes(p))) {
return true;
}
}
// Check data attributes
const dataAttrs = Array.from(el.attributes || [])
.filter(a => a.name.startsWith('data-'))
.map(a => a.value.toLowerCase());
if (dataAttrs.some(v => v.includes('sponsored') || v.includes('native') || v.includes('taboola'))) {
return true;
}
return false;
}
function hideElement(el) {
if (!el) return;
el.style.setProperty('display', 'none', 'important');
el.style.setProperty('visibility', 'hidden', 'important');
el.style.setProperty('opacity', '0', 'important');
el.style.setProperty('pointer-events', 'none', 'important');
el.style.setProperty('height', '0', 'important');
el.style.setProperty('overflow', 'hidden', 'important');
}
function removeNativeAds() {
// Remove by selector
NATIVE_AD_SELECTORS.forEach(selector => {
try {
document.querySelectorAll(selector).forEach(el => {
hideElement(el);
console.log('[NativeAdBlocker] Hid:', selector);
});
} catch(e) {}
});
// Check all elements for sponsored content
document.querySelectorAll('div, section, article, aside').forEach(el => {
if (isNativeAd(el)) {
hideElement(el);
}
});
// Remove "Sponsored" / "Promoted" labels and their containers
document.querySelectorAll('span, div, p, a, label').forEach(el => {
const text = (el.innerText || '').toLowerCase().trim();
if (SPONSORED_TEXT_PATTERNS.includes(text) ||
text === 'ad' || text === 'ads' || text === 'advertisement') {
// Hide the label
hideElement(el);
// Also hide parent if it's a small container
const parent = el.parentElement;
if (parent && parent.children.length <= 3) {
const grandparent = parent.parentElement;
if (grandparent && isNativeAd(grandparent)) {
hideElement(grandparent);
}
}
}
});
}
// ==========================================
// MUTATION OBSERVER FOR DYNAMIC NATIVE ADS
// ==========================================
const observer = new MutationObserver(function(mutations) {
let shouldClean = false;
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (isNativeAd(node)) {
hideElement(node);
} else {
shouldClean = true;
}
}
});
});
if (shouldClean) {
// Debounced cleanup
clearTimeout(window.__nativeAdCleanupTimeout);
window.__nativeAdCleanupTimeout = setTimeout(removeNativeAds, 100);
}
});
// Start observing when DOM is ready
function startObserver() {
if (document.body) {
observer.observe(document.body, { childList: true, subtree: true });
removeNativeAds();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startObserver);
} else {
startObserver();
}
// ==========================================
// IFRAME NATIVE AD BLOCKING
// ==========================================
const originalCreateElement = document.createElement.bind(document);
document.createElement = function(tagName) {
const element = originalCreateElement(tagName);
if (tagName.toLowerCase() === 'iframe') {
// Watch for src changes to block ad iframes
const originalSetAttribute = element.setAttribute.bind(element);
element.setAttribute = function(name, value) {
if (name === 'src') {
const src = String(value).toLowerCase();
const adDomains = ['taboola', 'outbrain', 'revcontent', 'mgid',
'zergnet', 'content.ad', 'dianomi', 'nativo'];
if (adDomains.some(d => src.includes(d))) {
console.log('[NativeAdBlocker] Blocked ad iframe:', src.substring(0, 60));
return; // Don't set the src
}
}
return originalSetAttribute(name, value);
};
// Also watch the src property
Object.defineProperty(element, 'src', {
get: function() { return this.getAttribute('src') || ''; },
set: function(value) {
const src = String(value).toLowerCase();
const adDomains = ['taboola', 'outbrain', 'revcontent', 'mgid',
'zergnet', 'content.ad', 'dianomi', 'nativo'];
if (adDomains.some(d => src.includes(d))) {
console.log('[NativeAdBlocker] Blocked ad iframe src:', src.substring(0, 60));
return;
}
this.setAttribute('src', value);
},
configurable: true
});
}
return element;
};
// ==========================================
// PERIODIC CLEANUP
// ==========================================
setInterval(removeNativeAds, 2000);
// Initial cleanup at multiple stages
setTimeout(removeNativeAds, 500);
setTimeout(removeNativeAds, 1000);
setTimeout(removeNativeAds, 3000);
setTimeout(removeNativeAds, 5000);
console.log('[NativeAdBlocker] Native/content ad blocking active');
})();
`
});
log.info('NativeAds', 'Native/content ad blocking initialized');
}