Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement cosmetic filtering #3303

Merged
merged 9 commits into from Dec 14, 2019

use cosmetic filtering api

  • Loading branch information
antonok-edm committed Aug 20, 2019
commit 7512da5fff038e2c2c0f8d9c6b06ba6a1bba24a0
@@ -2,6 +2,22 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const generateCosmeticBlockingStylesheet = (hideSelectors: string[], styleSelectors: any) => {
let stylesheet = ''
if (hideSelectors.length > 0) {
stylesheet += hideSelectors[0]
for (const selector of hideSelectors.slice(1)) {
stylesheet += ',' + selector
}
stylesheet += '{display:none !important;}\n'
}
for (const selector in styleSelectors) {
stylesheet += selector + '{' + styleSelectors[selector] + '\n'
}

return stylesheet
}

export const addSiteCosmeticFilter = async (origin: string, cssfilter: string) => {
chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => {
let storeList = Object.assign({}, storeData.cosmeticFilterList)
@@ -22,6 +38,36 @@ export const removeSiteFilter = (origin: string) => {
})
}

export const applyAdblockCosmeticFilters = (tabId: number, hostname: string) => {
chrome.braveShields.hostnameCosmeticResources(hostname, (resources) => {
if (chrome.runtime.lastError) {
console.warn('Unable to get cosmetic filter data for the current host')
return
}

const stylesheet = generateCosmeticBlockingStylesheet(resources.hide_selectors, resources.style_selectors)
if (stylesheet) {
chrome.tabs.insertCSS(tabId, {
code: stylesheet,
cssOrigin: 'user',
runAt: 'document_start'
})
}

chrome.tabs.sendMessage(tabId, {
type: 'cosmeticFilterGenericExceptions',
exceptions: resources.exceptions
})

if (resources.injected_script) {
chrome.tabs.executeScript(tabId, {
code: resources.injected_script,
runAt: 'document_start'
})
}
})
}

export const applyCSSCosmeticFilters = (tabId: number, hostname: string) => {
chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => {
if (!storeData.cosmeticFilterList) {
@@ -49,6 +49,16 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
rule.host = msg.baseURI
break
}
case 'classIdStylesheet': {
chrome.braveShields.classIdStylesheet(msg.classes, msg.ids, msg.exceptions, stylesheet => {
chrome.tabs.insertCSS({
code: stylesheet,
cssOrigin: 'user',
runAt: 'document_start'
})
})

This comment has been minimized.

Copy link
@petemill

petemill Aug 30, 2019

Member

) should be on separate line since { is separate line to (

This comment has been minimized.

Copy link
@antonok-edm

antonok-edm Sep 3, 2019

Author Collaborator

Hmm, not sure what you mean? The first }) matches to ...insertCSS({, the second matches to ...Stylesheet(... => {.

break
}
}
})

@@ -33,7 +33,10 @@ import {
reportBrokenSite
} from '../api/shieldsAPI'
import { reloadTab } from '../api/tabsAPI'
import { applyCSSCosmeticFilters } from '../api/cosmeticFilterAPI'
import {
applyAdblockCosmeticFilters,
applyCSSCosmeticFilters
} from '../api/cosmeticFilterAPI'

// Helpers
import { getAllowedScriptsOrigins } from '../../helpers/noScriptUtils'
@@ -58,6 +61,7 @@ export default function shieldsPanelReducer (
state = shieldsPanelState.resetBlockingResources(state, action.tabId)
state = noScriptState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin)
}
applyAdblockCosmeticFilters(action.tabId, getHostname(action.url))
applyCSSCosmeticFilters(action.tabId, getHostname(action.url))
break
}
@@ -1,24 +1,79 @@
const unique = require('unique-selector').default

let target: EventTarget | null
let genericExceptions: Array<string> | undefined = undefined

const queriedIds = new Set()
const queriedClasses = new Set()
const regexWhitespace = /\s/

function getCurrentURL () {
return window.location.hostname
}

/*function applyCosmeticFilterMutationObserver (filterList: any) {
const getClassesAndIds = function (addedNodes: Element[]) {

This comment has been minimized.

Copy link
@bbondy

bbondy Nov 18, 2019

Member

cc @bridiver is there an observer you know of that we can use in c++ to get the list of IDs and class names on all elements instead of getting them here? It seems a little expensive to do on each page load.

How the API works, is you pass in any class names or IDs that might be used, and then the adblock engine determines the stylesheet to use for blocking.

This comment has been minimized.

Copy link
@antonok-edm

antonok-edm Nov 19, 2019

Author Collaborator

This is what uBlock Origin does and I haven't found the performance hit to be noticeable in practice, but it would be nice to take advantage of native APIs if possible.

const ids = []
const classes = []

for (const node of addedNodes) {
let nodeId = node.id
if (nodeId && nodeId.length !== 0) {
nodeId = nodeId.trim()
if (!queriedIds.has(nodeId) && nodeId.length !== 0) {
ids.push(nodeId)
queriedIds.add(nodeId)
}
}
let nodeClass = node.className
if (nodeClass && nodeClass.length !== 0 && !regexWhitespace.test(nodeClass)) {
if (!queriedClasses.has(nodeClass)) {
classes.push(nodeClass)
queriedClasses.add(nodeClass)
}
} else {
let nodeClasses = node.classList
if (nodeClasses) {
let j = nodeClasses.length
while (j--) {
const nodeClassJ = nodeClasses[j]
if (queriedClasses.has(nodeClassJ) === false) {
classes.push(nodeClassJ)
queriedClasses.add(nodeClassJ)
}
}
}
}
}
return { classes, ids }
}

const handleNewNodes = (newNodes: Element[]) => {
const { classes, ids } = getClassesAndIds(newNodes)
chrome.runtime.sendMessage({
type: 'classIdStylesheet',
classes,
ids,
exceptions: genericExceptions
})
}

function applyCosmeticFilterMutationObserver () {
let targetNode = document.documentElement
let observer = new MutationObserver(function (mutations) {
console.log('mutation observed')
injectIncrementalStyles(mutations)
let observer = new MutationObserver(mutations => {
const nodeList: Element[] = []
for (const mutation of mutations) {
for (let nodeIndex = 0; nodeIndex < mutation.addedNodes.length; nodeIndex++) {
nodeList.push(mutation.addedNodes[nodeIndex] as Element)
}
}
handleNewNodes(nodeList)
})
let observerConfig = {
childList: true,
subtree: true
// characterData: true
}
observer.observe(targetNode, observerConfig)
}*/
}

document.addEventListener('contextmenu', (event) => {
// send host and store target
@@ -35,6 +90,17 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
switch (action) {
case 'getTargetSelector': {
sendResponse(unique(target))
break
}
case 'cosmeticFilterGenericExceptions': {
genericExceptions = msg.exceptions

let allNodes = Array.from(document.querySelectorAll('[id],[class]'))
handleNewNodes(allNodes)
applyCosmeticFilterMutationObserver()

sendResponse(null)
break
}
}
})
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.