Skip to content
This repository has been archived by the owner on Jun 6, 2019. It is now read-only.

Cosmetic Filter Implementation #48

Merged
merged 1 commit into from
Aug 10, 2018
Merged

Conversation

Snuupy
Copy link
Contributor

@Snuupy Snuupy commented Jul 13, 2018

At the point where I can take code feedback.

Testing notes: this does not work for any elements that are in-line. It will only work when we do a .removeElement() with the MutationObserver implementation

Done:

  • Add a context menu which shows on right click / cmd + click
  • Have the element blocked on the page (display:none or remove from DOM)
  • Persist rules to store (ex: utilize the existing local storage used elsewhere)
  • Add a context menu item for removing rules on a given domain
  • Add a context menu item for removing rules on all domains
  • Fix webpack dev environment

To do for this PR:

  • Unit tests
    • Pass existing tests
  • Remove logging/unused context menus
  • Clean up / refactor code
    • Remove .vscode/settings.json, build-dev.js
    • Remove console.log statements
    • Reset prod.config.js to original version

For next PR(s):

  • Highlight corresponding element with candidate selector
  • DOM observer
  • Remove via JS
  • No flicker on load
  • Put right click context menu into redux workflow

@bsclifton
Copy link
Member

@cezaraugusto @NejcZdovc @bbondy @jonathansampson @petemill ready for review 😄

Copy link
Contributor

@cezaraugusto cezaraugusto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments left. noticed you added the cosmeticFilterEventsTest.ts but file is empty. let's remove it and add in a follow-up when we start writing tests for it

result.list[hostname] !== null &&
result.list[hostname].length !== 0) {
result.list[hostname].map((rule: string) => {
console.log('applying rule', rule)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok we can remove this

console.log('report broken page sub-context menu created')
})

// log storage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove this code block as we don't require it

case 'addBlockElement':
{
console.log(rule)
console.log('blockElement clicked in cosmeticFilterEvents.ts', rule.host, rule.selector)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}
case 'resetSiteFilterSettings':
{
console.log('dispatching cosmeticFilterAction to cosmeticFilterReducer from events')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

cosmeticFilterActions.siteCosmeticFilterRemoved(rule.host)
break
}
case 'reportBrokenPage':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this is needed we are only logging the result inside the action handler

@@ -0,0 +1,41 @@
// import { Tab } from '../../types/state/shieldsPannelState'
// import { getShieldSettingsForTabData, getTabData } from './shieldsAPI'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove these imports as we don't need them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


export const addSiteCosmeticFilter = async (origin: string, cssfilter: string) => {
chrome.storage.local.get('list', (result = {}) => {
let l = Object.assign({}, result.list)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we have a more descriptive variable name here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you referring to list, l, result, or any combination of those 3?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya I mean the l

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix'd


export const removeSiteFilter = (origin: string) => {
chrome.storage.local.get('list', (result = {}) => {
let l = Object.assign({}, result.list)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

// import tabActions from '../actions/tabActions'

// import * as cosmeticFilterAPI from '../api/cosmeticFilterAPI'
// background
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove the imports above as we don't use them


// add context menu
chrome.runtime.onInstalled.addListener(function () {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we remove this empty line? standardjs would complain but I'm not sure why it doesn't


// content script listener for right click DOM selection event
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
rule.host = msg.baseURI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the rule host include port number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose filing that as a separate issue and keeping it as-is for now as there are several other features that have higher priority (mutation observer, UI > port for now).

Copy link
Member

@petemill petemill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job getting this all setup and working with typescript and integrated in to the extension.

I know that we're going to work on some better UI than a simple text input prompt, but until we have that I think we can make a slight improvement here: I find the context menu titles a confusing UI since the first refers to Blocking an 'ad' but the others refer to filtering 'sites'. Secondly, the UI that pops up asks for a 'rule' but gives no explanation as to what to type.
I propose the following new strings for these menu item titles whilst the UI is in this state:

- Block element vis CSS rule
- Clear CSS rules for this site
- Clear CSS rules for all sites

Alternatively, instead of CSS Rule we could use Selector, since that's what we're asking for (we create the display: none rule internally, the user does not provide it).

I've left more specific code comments inline.

We're also going to need to squash this down to 1 or 2 commits since this adds 1 feature. However I did notice at least 1 thing that should remain a separate commit / PR that this can rebase on.

@@ -0,0 +1,36 @@
export const addSiteCosmeticFilter = async (origin: string, cssfilter: string) => {
chrome.storage.local.get('list', (result = {}) => {
let l = Object.assign({}, result.list)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more descriptive variable name here could be storeData instead of result and storeList instead of l.

parentId: 'brave',
contexts: ['all']
}, () => {
console.log('block custom ad sub-context menu created')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't have all the menu items automatically log for all users. If we want to keep it in, please put behind a debug flag, even if it's simply a const at the top of the module.

break
}
default: {
console.log('invalid context menu option - cosmeticFilterEvents.ts')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to log an invalid option then put what the invalid option was as part of the log string, e.g: [cosmeticFilterEvents] invalid context menu option: ${info.menuItemId}

} from '../api/cosmeticFilterAPI'

const focusedWindowChanged = (state: State, windowId: number): State => {
if (windowId !== -1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nicer to return early here, instead of multiple levels of nesting conditionals, e.g.

if (windowId === -1) {
	console.warn(...)
	return state
}
...

setAllowBraveShields(tabData.origin, action.setting)
.then(() => {
reloadTab(tabId, true).catch(() => {
console.error('Tab reload was not successful')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we log the error too? .catch(e => console.error('Tab reload was not successful', e))

app/content.js Outdated
selector: selector,
baseURI: baseURI
}, ((response) => {
console.log('content.js - response received: ', response)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should remove console.log by default

const filter = '#cssFilter'

let sampleValue: any
console.log(sampleValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that error is correct: you are only declaring or assigning the variable, but never reading it, so it doesn't seem necessary. Why do you need to assign it inside callsFake( via sampleValue = value if you're never using that value in any test, or in the stub for chrome.storage.local.get? Looks like you can just use empty functions inside callsFake, just have an empty stub, or use that value in the stub for get (only if it needs it).

@@ -374,8 +374,8 @@ describe('Shields API', () => {
it('resolves the returned promise', function (cb) {
this.p
.then(function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to create a function to call a function, can just pass in cb as the argument. Alternatively, you can just return the whole promise instead of using a callback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only fixed the typo, would you prefer if I changed all the shieldAPITests to conform to passing in cb instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, no that's ok - thanks anyway @Snuupy

.gitignore Outdated
@@ -64,3 +64,7 @@ build/

# Ignore python compiled files
*.pyc

# VSCode config
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really be done in a separate PR, or at least a separate commit once all of these commits are squashed.


// block ad child menu
chrome.contextMenus.create({
title: 'Block ad',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have an opinion on these context menu titles that I'll describe in the overall review comment

@@ -29,6 +29,7 @@ const focusedWindowChanged = (state: State, windowId: number): State => {
requestShieldPanelData(shieldsPanelState.getActiveTabId(state))
} else {
console.warn('no tab id so cannot request shield data from window focus change!')
return state
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need the extra return state here. The time to use that pattern is if you return early because of some condition, e.g.

if (condition) {
 return state
}
...continue processing...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@petemill petemill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Snuupy for a great start to the feature, and attention to all the review comments.

From a code / functionality POV, this lgtm and can be merged 👍

However, I did just notice that we're pointing to a github repo (although a Brave one) for a new package.json dependency - we may want to put this on npm as without that we won't benefit from semver or caching.

@Snuupy Snuupy merged commit 85aa24a into brave:master Aug 10, 2018
@bsclifton bsclifton deleted the cosmeticFilter branch August 10, 2018 21:19
@dri94
Copy link

dri94 commented Aug 30, 2018

There aren't clear instructions in how to merge brave-extension and brave-browser. Is this in the dev builds of brave browser now?

@Snuupy
Copy link
Contributor Author

Snuupy commented Aug 31, 2018

@dri94 Thanks for following up on the feature!

There aren't clear instructions in how to merge brave-extension and brave-browser.

Your observation is correct.

The way it works is the DEPS file in brave-core references a specific commit from each repository it pulls from to setup the development environment (or to create builds).

As you can see from line 8 in the DEPS file:

"vendor/brave-extension": "https://github.com/brave/brave-extension.git@2ea4ff3d9c9cbc0ad70c3ad3f3335a8d1d39ce1f"

If you look at the merge/commit log in brave-extension, you'll see that my commit (d192d23) is before the one referenced (2ea4ff3) which means by default the build process will include the cosmetic filtering feature.

Because of the way the build process works, I also had to update the brave-core/browser/resources/brave_extension.grd file to include the content script as well.

I know this is not exactly intuitive, but I hope this clears things up.

Is this in the dev builds of brave browser now?

Yes.

image

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants