Skip to content

Commit

Permalink
add cosmetic filter feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Snuupy committed Aug 10, 2018
1 parent 3395adb commit b914102
Show file tree
Hide file tree
Showing 36 changed files with 2,137 additions and 744 deletions.
27 changes: 27 additions & 0 deletions app/actions/cosmeticFilterActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */

import * as types from '../constants/cosmeticFilterTypes'
import * as actions from '../types/actions/cosmeticFilterActions'

export const siteCosmeticFilterAdded: actions.SiteCosmeticFilterAdded = (origin: string, cssfilter: string) => {
return {
type: types.SITE_COSMETIC_FILTER_ADDED,
origin,
cssfilter
}
}

export const siteCosmeticFilterRemoved: actions.SiteCosmeticFilterRemoved = (origin: string) => {
return {
type: types.SITE_COSMETIC_FILTER_REMOVED,
origin
}
}

export const allCosmeticFiltersRemoved: actions.AllCosmeticFiltersRemoved = () => {
return {
type: types.ALL_COSMETIC_FILTERS_REMOVED
}
}
9 changes: 9 additions & 0 deletions app/actions/webNavigationActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ export const onBeforeNavigate: actions.OnBeforeNavigate = (tabId, url, isMainFra
isMainFrame
}
}

export const onCommitted: actions.OnCommitted = (tabId, url, isMainFrame) => {
return {
type: types.ON_COMMITTED,
tabId,
url,
isMainFrame
}
}
8 changes: 8 additions & 0 deletions app/background/actions/cosmeticFilterActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */

import { bindActionCreators } from 'redux'
import store from '../store'
import * as cosmeticFilterActions from '../../actions/cosmeticFilterActions'
export default bindActionCreators(cosmeticFilterActions, store.dispatch)
36 changes: 36 additions & 0 deletions app/background/api/cosmeticFilterAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export const addSiteCosmeticFilter = async (origin: string, cssfilter: string) => {
chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => {
let storeList = Object.assign({}, storeData.cosmeticFilterList)
if (storeList[origin] === undefined || storeList[origin].length === 0) { // nothing in filter list for origin
storeList[origin] = [cssfilter]
} else { // add entry
storeList[origin].push(cssfilter)
}
chrome.storage.local.set({ 'cosmeticFilterList': storeList })
})
}

export const removeSiteFilter = (origin: string) => {
chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => {
let storeList = Object.assign({}, storeData.cosmeticFilterList)
delete storeList[origin]
chrome.storage.local.set({ 'cosmeticFilterList': storeList })
})
}

export const applySiteFilters = (hostname: string) => {
chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => {
if (storeData.cosmeticFilterList[hostname] !== undefined) {
storeData.cosmeticFilterList[hostname].map((rule: string) => {
console.log('applying rule', rule)
chrome.tabs.insertCSS({
code: `${rule} {display: none;}`
})
})
}
})
}

export const removeAllFilters = () => {
chrome.storage.local.set({ 'cosmeticFilterList': {} })
}
1 change: 1 addition & 0 deletions app/background/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ require('./events/tabsEvents')
require('./events/shieldsEvents')
require('./events/runtimeEvents')
require('./events/webNavigationEvents')
require('./events/cosmeticFilterEvents')
70 changes: 70 additions & 0 deletions app/background/events/cosmeticFilterEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import cosmeticFilterActions from '../actions/cosmeticFilterActions'

let rule = {
host: '',
selector: ''
}

// add context menu
chrome.runtime.onInstalled.addListener(function () {
// parent menu
chrome.contextMenus.create({
title: 'Brave',
id: 'brave',
contexts: ['all']
})
// block ad child menu
chrome.contextMenus.create({
title: 'Block element via selector',
id: 'addBlockElement',
parentId: 'brave',
contexts: ['all']
})
chrome.contextMenus.create({
title: 'Clear CSS rules for this site',
id: 'resetSiteFilterSettings',
parentId: 'brave',
contexts: ['all']
})
chrome.contextMenus.create({
title: 'Clear CSS rules for all sites',
id: 'resetAllFilterSettings',
parentId: 'brave',
contexts: ['all']
})
})

// contextMenu listener - when triggered, grab latest selector
chrome.contextMenus.onClicked.addListener(function (info, tab) {
switch (info.menuItemId) {
case 'addBlockElement':
{
rule.selector = window.prompt('CSS selector to block: ', `${rule.selector}`) || ''
chrome.tabs.insertCSS({
code: `${rule.selector} {display: none;}`
})
cosmeticFilterActions.siteCosmeticFilterAdded(rule.host, rule.selector)
break
}
case 'resetSiteFilterSettings':
{
cosmeticFilterActions.siteCosmeticFilterRemoved(rule.host)
break
}
case 'resetAllFilterSettings':
{
cosmeticFilterActions.allCosmeticFiltersRemoved()
break
}
default: {
console.warn('[cosmeticFilterEvents] invalid context menu option: ${info.menuItemId}')
}
}
})

// content script listener for right click DOM selection event
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
rule.host = msg.baseURI
rule.selector = msg.selector
sendResponse(rule)
})
5 changes: 5 additions & 0 deletions app/background/events/webNavigationEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ chrome.webNavigation.onBeforeNavigate.addListener(function ({ tabId, url, frameI
const isMainFrame: boolean = frameId === 0
actions.onBeforeNavigate(tabId, url, isMainFrame)
})

chrome.webNavigation.onCommitted.addListener(function ({ tabId, url, frameId }: chrome.webNavigation.WebNavigationTransitionCallbackDetails) {
const isMainFrame: boolean = frameId === 0
actions.onCommitted(tabId, url, isMainFrame)
})
4 changes: 3 additions & 1 deletion app/background/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import { combineReducers } from 'redux'

import shieldsPanelReducer from './reducers/shieldsPanelReducer'
import cosmeticFilterReducer from './reducers/cosmeticFilterReducer'

export default combineReducers({
shieldsPanel: shieldsPanelReducer
shieldsPanel: shieldsPanelReducer,
cosmeticFilter: cosmeticFilterReducer
})
153 changes: 153 additions & 0 deletions app/background/reducers/cosmeticFilterReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// /* This Source Code Form is subject to the terms of the Mozilla Public
// * 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/. */

import * as shieldsPanelTypes from '../../constants/shieldsPanelTypes'
import * as windowTypes from '../../constants/windowTypes'
import * as tabTypes from '../../constants/tabTypes'
import * as webNavigationTypes from '../../constants/webNavigationTypes'
import {
setAllowBraveShields,
requestShieldPanelData
} from '../api/shieldsAPI'
import { reloadTab } from '../api/tabsAPI'
import * as shieldsPanelState from '../../state/shieldsPanelState'
import { State, Tab } from '../../types/state/shieldsPannelState'
import { Actions } from '../../types/actions/index'
import * as cosmeticFilterTypes from '../../constants/cosmeticFilterTypes'
import {
removeSiteFilter,
addSiteCosmeticFilter,
applySiteFilters,
removeAllFilters
} from '../api/cosmeticFilterAPI'

const focusedWindowChanged = (state: State, windowId: number): State => {
if (windowId !== -1) {
state = shieldsPanelState.updateFocusedWindow(state, windowId)
if (shieldsPanelState.getActiveTabId(state)) {
requestShieldPanelData(shieldsPanelState.getActiveTabId(state))
} else {
console.warn('no tab id so cannot request shield data from window focus change!')
}
}
return state
}

const updateActiveTab = (state: State, windowId: number, tabId: number): State => {
requestShieldPanelData(tabId)
return shieldsPanelState.updateActiveTab(state, windowId, tabId)
}

export default function cosmeticFilterReducer (state: State = {
tabs: {},
windows: {},
currentWindowId: -1 },
action: Actions) {
switch (action.type) {
case webNavigationTypes.ON_BEFORE_NAVIGATION:
{
if (action.isMainFrame) {
state = shieldsPanelState.resetBlockingStats(state, action.tabId)
state = shieldsPanelState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin)
}
break
}
case webNavigationTypes.ON_COMMITTED:
{
const tabData: Tab = shieldsPanelState.getActiveTabData(state)
applySiteFilters(tabData.hostname)
break
}
case windowTypes.WINDOW_REMOVED:
{
state = shieldsPanelState.removeWindowInfo(state, action.windowId)
break
}
case windowTypes.WINDOW_CREATED:
{
if (action.window.focused || Object.keys(state.windows).length === 0) {
state = focusedWindowChanged(state, action.window.id)
}
break
}
case windowTypes.WINDOW_FOCUS_CHANGED:
{
state = focusedWindowChanged(state, action.windowId)
break
}
case tabTypes.ACTIVE_TAB_CHANGED:
{
const windowId: number = action.windowId
const tabId: number = action.tabId
state = updateActiveTab(state, windowId, tabId)
break
}
case tabTypes.TAB_DATA_CHANGED:
{
const tab: chrome.tabs.Tab = action.tab
if (tab.active && tab.id) {
state = updateActiveTab(state, tab.windowId, tab.id)
}
break
}
case tabTypes.TAB_CREATED:
{
const tab: chrome.tabs.Tab = action.tab
if (!tab) {
break
}

if (tab.active && tab.id) {
state = updateActiveTab(state, tab.windowId, tab.id)
}
break
}
case shieldsPanelTypes.SHIELDS_PANEL_DATA_UPDATED:
{
state = shieldsPanelState.updateTabShieldsData(state, action.details.id, action.details)
break
}
case shieldsPanelTypes.SHIELDS_TOGGLED:
{
const tabId: number = shieldsPanelState.getActiveTabId(state)
const tabData: Tab = shieldsPanelState.getActiveTabData(state)
if (!tabData) {
break
}
setAllowBraveShields(tabData.origin, action.setting)
.then(() => {
reloadTab(tabId, true).catch((e) => {
console.error('Tab reload was not successful', e)
})
requestShieldPanelData(shieldsPanelState.getActiveTabId(state))
})
.catch((e: any) => {
console.error('Could not set shields', e)
})
state = shieldsPanelState
.updateTabShieldsData(state, tabId, { braveShields: action.setting })
break
}
case cosmeticFilterTypes.SITE_COSMETIC_FILTER_REMOVED:
{
let url = action.origin
removeSiteFilter(url)
break
}
case cosmeticFilterTypes.ALL_COSMETIC_FILTERS_REMOVED:
{
removeAllFilters()
break
}
case cosmeticFilterTypes.SITE_COSMETIC_FILTER_ADDED:
{
addSiteCosmeticFilter(action.origin, action.cssfilter)
.catch((e) => {
console.error('Could not add filter:', e)
})
break
}
}
return state
}
8 changes: 8 additions & 0 deletions app/constants/cosmeticFilterTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */

export const SITE_COSMETIC_FILTER_REMOVED = 'SITE_COSMETIC_FILTER_REMOVED'
export const SITE_COSMETIC_FILTER_ADDED = 'SITE_COSMETIC_FILTER_ADDED'
export const LOGGED_STORAGE = 'LOGGED_STORAGE'
export const ALL_COSMETIC_FILTERS_REMOVED = 'ALL_COSMETIC_FILTERS_REMOVED'
1 change: 1 addition & 0 deletions app/constants/webNavigationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

export const ON_BEFORE_NAVIGATION = 'ON_BEFORE_NAVIGATION'
export const ON_COMMITTED = 'ON_COMMITTED'
15 changes: 15 additions & 0 deletions app/content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const unique = require('unique-selector').default

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

document.addEventListener('contextmenu', (event) => {
let selector = unique(event.target) // this has to be done here, events can't be passed through the messaging API
let baseURI = getCurrentURL()
console.log(selector, baseURI)
chrome.runtime.sendMessage({
selector: selector,
baseURI: baseURI
})
}, true)
15 changes: 14 additions & 1 deletion app/manifest.dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,20 @@
"background": {
"page": "background.html"
},
"permissions": [ "contentSettings", "management", "tabs", "storage", "webNavigation" ],
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/content.bundle.js"
],
"run_at": "document_start",
"all_frames": true
}
],
"permissions": [ "contentSettings", "management", "tabs", "storage", "webNavigation", "contextMenus", "*://*/*" ],
"content_security_policy": "default-src 'self'; script-src 'self' http://localhost:3000 https://localhost:3000 'unsafe-eval'; connect-src http://localhost:3000 https://localhost:3000; style-src * 'unsafe-inline' 'self' blob:; img-src 'self' data:;",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAupOLMy5Fd4dCSOtjcApsAQOnuBdTs+OvBVt/3P93noIrf068x0xXkvxbn+fpigcqfNamiJ5CjGyfx9zAIs7zcHwbxjOw0Uih4SllfgtK+svNTeE0r5atMWE0xR489BvsqNuPSxYJUmW28JqhaSZ4SabYrRx114KcU6ko7hkjyPkjQa3P+chStJjIKYgu5tWBiMJp5QVLelKoM+xkY6S7efvJ8AfajxCViLGyDQPDviGr2D0VvIBob0D1ZmAoTvYOWafcNCaqaejPDybFtuLFX3pZBqfyOCyyzGhucyCmfBXJALKbhjRAqN5glNsUmGhhPK87TuGATQfVuZtenMvXMQIDAQAB"
}
Loading

0 comments on commit b914102

Please sign in to comment.