diff --git a/BUILD.gn b/BUILD.gn index 1fbb6c3..082aea2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -17,10 +17,14 @@ action("brave-extension") { "$target_gen_dir/brave_extension/build/js/braveShieldsPanel.bundle.js", "$target_gen_dir/brave_extension/build/braveShieldsPanel.html", "$target_gen_dir/brave_extension/build/bravelizer.css", - "$target_gen_dir/brave_extension/build/img/icon-128.png", "$target_gen_dir/brave_extension/build/img/icon-16.png", - "$target_gen_dir/brave_extension/build/img/icon-16-disabled.png", + "$target_gen_dir/brave_extension/build/img/icon-32.png", "$target_gen_dir/brave_extension/build/img/icon-48.png", + "$target_gen_dir/brave_extension/build/img/icon-64.png", + "$target_gen_dir/brave_extension/build/img/icon-128.png", + "$target_gen_dir/brave_extension/build/img/icon-256.png", + "$target_gen_dir/brave_extension/build/img/icon.svg", + "$target_gen_dir/brave_extension/build/img/icon-off.svg", "$target_gen_dir/brave_extension/build/_locales/en_US/messages.json", ] diff --git a/app/actions/runtimeActions.ts b/app/actions/runtimeActions.ts new file mode 100644 index 0000000..8c32575 --- /dev/null +++ b/app/actions/runtimeActions.ts @@ -0,0 +1,11 @@ +/* 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/runtimeActionTypes' + +export function runtimeDidStartup () { + return { + type: types.RUNTIME_DID_STARTUP + } +} diff --git a/app/assets/img/icon-128.png b/app/assets/img/icon-128.png index 10fe418..67cc0aa 100644 Binary files a/app/assets/img/icon-128.png and b/app/assets/img/icon-128.png differ diff --git a/app/assets/img/icon-16-disabled.png b/app/assets/img/icon-16-disabled.png deleted file mode 100644 index 414158d..0000000 Binary files a/app/assets/img/icon-16-disabled.png and /dev/null differ diff --git a/app/assets/img/icon-16.png b/app/assets/img/icon-16.png index 9957ca0..a1d6f2b 100644 Binary files a/app/assets/img/icon-16.png and b/app/assets/img/icon-16.png differ diff --git a/app/assets/img/icon-256.png b/app/assets/img/icon-256.png new file mode 100644 index 0000000..7c1e117 Binary files /dev/null and b/app/assets/img/icon-256.png differ diff --git a/app/assets/img/icon-32.png b/app/assets/img/icon-32.png new file mode 100644 index 0000000..dc68225 Binary files /dev/null and b/app/assets/img/icon-32.png differ diff --git a/app/assets/img/icon-48.png b/app/assets/img/icon-48.png index 643e14b..f9d29f7 100644 Binary files a/app/assets/img/icon-48.png and b/app/assets/img/icon-48.png differ diff --git a/app/assets/img/icon-64.png b/app/assets/img/icon-64.png new file mode 100644 index 0000000..ce14c23 Binary files /dev/null and b/app/assets/img/icon-64.png differ diff --git a/app/assets/img/icon-off.svg b/app/assets/img/icon-off.svg new file mode 100644 index 0000000..d31649d --- /dev/null +++ b/app/assets/img/icon-off.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/assets/img/icon.svg b/app/assets/img/icon.svg new file mode 100644 index 0000000..503f594 --- /dev/null +++ b/app/assets/img/icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/background/actions/runtimeActions.ts b/app/background/actions/runtimeActions.ts new file mode 100644 index 0000000..f3852f2 --- /dev/null +++ b/app/background/actions/runtimeActions.ts @@ -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 runtimeActions from '../../actions/runtimeActions' +export default bindActionCreators(runtimeActions, store.dispatch) diff --git a/app/background/api/browserActionAPI.ts b/app/background/api/browserActionAPI.ts index 4d05937..97032d6 100644 --- a/app/background/api/browserActionAPI.ts +++ b/app/background/api/browserActionAPI.ts @@ -4,13 +4,37 @@ import { isHttpOrHttps } from '../../helpers/urlUtils' +export const shieldsOnIcon = 'img/icon.svg' +export const shieldsOffIcon = 'img/icon-off.svg' + +/** + * Initializes the browser action UI + */ +export function init () { + // Setup badge color + chrome.browserAction.setBadgeBackgroundColor({ + color: [66, 66, 66, 100] + }) + // Initial / default icon + chrome.browserAction.setIcon({ + path: shieldsOnIcon + }) + // By default, icon is disabled, + // so that we do not enable the icon in a new tab and then disable it + // when the context is not http(s). + chrome.browserAction.disable() +} + /** * Sets the badge text * @param {string} text - The text to put on the badge */ -export const setBadgeText = (text: string) => { +export const setBadgeText = (tabId: number, text: string) => { if (chrome.browserAction) { - chrome.browserAction.setBadgeText({ text: String(text) }) + chrome.browserAction.setBadgeText({ + tabId, + text: String(text) + }) } } @@ -18,13 +42,17 @@ export const setBadgeText = (text: string) => { * Updates the shields icon based on shields state */ export const setIcon = (url: string, tabId: number, shieldsOn: boolean) => { - const shieldsEnabledIcon = 'img/icon-16.png' - const shieldsDisabledIcon = 'img/icon-16-disabled.png' + const actionIsDisabled = !isHttpOrHttps(url) if (chrome.browserAction) { chrome.browserAction.setIcon({ - path: shieldsOn && isHttpOrHttps(url) ? shieldsEnabledIcon : shieldsDisabledIcon, + path: shieldsOn ? shieldsOnIcon : shieldsOffIcon, tabId }) + if (actionIsDisabled) { + chrome.browserAction.disable(tabId) + } else { + chrome.browserAction.enable(tabId) + } } } diff --git a/app/background/events/runtimeEvents.ts b/app/background/events/runtimeEvents.ts index ff124da..459164c 100644 --- a/app/background/events/runtimeEvents.ts +++ b/app/background/events/runtimeEvents.ts @@ -4,8 +4,10 @@ import windowActions from '../actions/windowActions' import tabActions from '../actions/tabActions' +import runtimeActions from '../actions/runtimeActions' chrome.runtime.onStartup.addListener(() => { + runtimeActions.runtimeDidStartup() chrome.windows.getAllAsync({ populate: true }).then((windows: chrome.windows.Window[]) => { windows.forEach((win: chrome.windows.Window) => { windowActions.windowCreated(win) diff --git a/app/background/reducers.ts b/app/background/reducers.ts index 26afa33..2e80973 100644 --- a/app/background/reducers.ts +++ b/app/background/reducers.ts @@ -6,8 +6,10 @@ import { combineReducers } from 'redux' import shieldsPanelReducer from './reducers/shieldsPanelReducer' import cosmeticFilterReducer from './reducers/cosmeticFilterReducer' +import runtimeReducer from './reducers/runtimeReducer' export default combineReducers({ shieldsPanel: shieldsPanelReducer, - cosmeticFilter: cosmeticFilterReducer + cosmeticFilter: cosmeticFilterReducer, + runtime: runtimeReducer }) diff --git a/app/background/reducers/runtimeReducer.ts b/app/background/reducers/runtimeReducer.ts new file mode 100644 index 0000000..f343436 --- /dev/null +++ b/app/background/reducers/runtimeReducer.ts @@ -0,0 +1,15 @@ +import { Actions } from '../../types/actions' +import * as runtimeActions from '../../constants/runtimeActionTypes' +import * as browserActionAPI from '../api/browserActionAPI' + +type State = { } + +export default function runtimeReducer (state: State = { }, action: Actions): State { + switch (action.type) { + case runtimeActions.RUNTIME_DID_STARTUP: { + browserActionAPI.init() + break + } + } + return state +} diff --git a/app/background/reducers/shieldsPanelReducer.ts b/app/background/reducers/shieldsPanelReducer.ts index 361e911..829976f 100644 --- a/app/background/reducers/shieldsPanelReducer.ts +++ b/app/background/reducers/shieldsPanelReducer.ts @@ -30,7 +30,7 @@ const updateBadgeText = (state: State) => { if (tab) { const total = tab.adsBlocked + tab.trackersBlocked + tab.javascriptBlocked + tab.fingerprintingBlocked + tab.httpsRedirected // do not show any badge if there are no blocked items - setBadgeText(total > 0 ? total.toString() : '') + setBadgeText(tabId, total > 99 ? '99+' : total > 0 ? total.toString() : '') } } diff --git a/app/constants/runtimeActionTypes.ts b/app/constants/runtimeActionTypes.ts new file mode 100644 index 0000000..94d7311 --- /dev/null +++ b/app/constants/runtimeActionTypes.ts @@ -0,0 +1 @@ +export const RUNTIME_DID_STARTUP = 'RUNTIME_DID_STARTUP' diff --git a/app/manifest.dev.json b/app/manifest.dev.json index e336bc8..898c89c 100644 --- a/app/manifest.dev.json +++ b/app/manifest.dev.json @@ -10,8 +10,11 @@ }, "icons": { "16": "img/icon-16.png", + "32": "img/icon-32.png", "48": "img/icon-48.png", - "128": "img/icon-128.png" + "64": "img/icon-64.png", + "128": "img/icon-128.png", + "256": "img/icon-256.png" }, "web_accessible_resources": [ ], diff --git a/app/manifest.prod.json b/app/manifest.prod.json index 7bf7a38..01fc547 100644 --- a/app/manifest.prod.json +++ b/app/manifest.prod.json @@ -10,8 +10,11 @@ }, "icons": { "16": "img/icon-16.png", + "32": "img/icon-32.png", "48": "img/icon-48.png", - "128": "img/icon-128.png" + "64": "img/icon-64.png", + "128": "img/icon-128.png", + "256": "img/icon-256.png" }, "web_accessible_resources": [ ], diff --git a/app/types/actions/index.ts b/app/types/actions/index.ts index 62d952e..c7ac38f 100644 --- a/app/types/actions/index.ts +++ b/app/types/actions/index.ts @@ -3,10 +3,12 @@ import { tabActions } from './tabActions' import { webNavigationActions } from './webNavigationActions' import { windowActions } from './windowActions' import { cosmeticFilterActions } from './cosmeticFilterActions' +import { runtimeActions } from './runtimeActions' export type Actions = shieldPanelActions | tabActions | webNavigationActions | windowActions | - cosmeticFilterActions + cosmeticFilterActions | + runtimeActions diff --git a/app/types/actions/runtimeActions.ts b/app/types/actions/runtimeActions.ts new file mode 100644 index 0000000..a0856ee --- /dev/null +++ b/app/types/actions/runtimeActions.ts @@ -0,0 +1,11 @@ +import * as types from '../../constants/runtimeActionTypes' + +interface RuntimeDidStartupReturn { + type: typeof types.RUNTIME_DID_STARTUP +} + +export interface RuntimeDidStartup { + (): RuntimeDidStartupReturn +} + +export type runtimeActions = RuntimeDidStartupReturn diff --git a/test/app/background/api/browserActionAPITest.ts b/test/app/background/api/browserActionAPITest.ts index 3547b8a..aaf6757 100644 --- a/test/app/background/api/browserActionAPITest.ts +++ b/test/app/background/api/browserActionAPITest.ts @@ -13,7 +13,8 @@ describe('BrowserAction API', () => { before(function () { this.spy = sinon.spy(chrome.browserAction, 'setBadgeText') this.text = '42' - browserActionAPI.setBadgeText(this.text) + this.tabId = 1337 + browserActionAPI.setBadgeText(this.tabId, this.text) }) after(function () { this.spy.restore() @@ -21,55 +22,52 @@ describe('BrowserAction API', () => { it('calls chrome.browserAction.setBadgeText with the text', function () { assert(this.spy.calledOnce) assert.deepEqual(this.spy.getCall(0).args[0], { + tabId: this.tabId, text: this.text }) }) }) describe('setIcon', function () { - const enabledIconPath = 'img/icon-16.png' - const disabledIconPath = 'img/icon-16-disabled.png' before(function () { - this.spy = sinon.spy(chrome.browserAction, 'setIcon') + this.setIconSpy = sinon.spy(chrome.browserAction, 'setIcon') + this.disableSpy = sinon.spy(chrome.browserAction, 'disable') + this.enableSpy = sinon.spy(chrome.browserAction, 'enable') this.url = 'https://brave.com' this.tabId = 1 this.shieldsEnabled = true }) after(function () { - this.spy.restore() + this.setIconSpy.restore() + this.disableSpy.restore() + this.enableSpy.restore() }) afterEach(function () { - this.spy.reset() + this.setIconSpy.reset() + this.disableSpy.reset() + this.enableSpy.reset() }) - it('sets the enabled icon when protocol is http', function () { + it('sets enabled when protocol is http', function () { this.url = 'http://not-very-awesome-http-page.com' browserActionAPI.setIcon(this.url, this.tabId, this.shieldsEnabled) - assert.deepEqual(this.spy.getCall(0).args[0], { - path: enabledIconPath, - tabId: this.tabId - }) + assert.deepEqual(this.enableSpy.getCall(0).args[0], this.tabId) }) it('sets the enabled icon when protocol is https', function () { this.url = 'https://very-awesome-https-page.com' browserActionAPI.setIcon(this.url, this.tabId, this.shieldsEnabled) - assert.deepEqual(this.spy.getCall(0).args[0], { - path: enabledIconPath, - tabId: this.tabId - }) + assert.deepEqual(this.enableSpy.getCall(0).args[0], this.tabId) }) it('sets the disabled icon when the protocol is neither https nor http', function () { this.url = 'brave://welcome' browserActionAPI.setIcon(this.url, this.tabId, this.shieldsEnabled) - assert.deepEqual(this.spy.getCall(0).args[0], { - path: disabledIconPath, - tabId: this.tabId - }) + assert.deepEqual(this.disableSpy.getCall(0).args[0], this.tabId) }) it('sets the disabled icon when the protocol is http and shield is off', function () { this.url = 'http://not-very-awesome-http-page.com' this.shieldsEnabled = false browserActionAPI.setIcon(this.url, this.tabId, this.shieldsEnabled) - assert.deepEqual(this.spy.getCall(0).args[0], { - path: disabledIconPath, + assert.deepEqual(this.enableSpy.getCall(0).args[0], this.tabId) + assert.deepEqual(this.setIconSpy.getCall(0).args[0], { + path: browserActionAPI.shieldsOffIcon, tabId: this.tabId }) }) @@ -77,8 +75,9 @@ describe('BrowserAction API', () => { this.url = 'https://very-awesome-https-page.com' this.shieldsEnabled = false browserActionAPI.setIcon(this.url, this.tabId, this.shieldsEnabled) - assert.deepEqual(this.spy.getCall(0).args[0], { - path: disabledIconPath, + assert.deepEqual(this.enableSpy.getCall(0).args[0], this.tabId) + assert.deepEqual(this.setIconSpy.getCall(0).args[0], { + path: browserActionAPI.shieldsOffIcon, tabId: this.tabId }) }) diff --git a/test/app/background/reducers/shieldsPanelReducerTest.ts b/test/app/background/reducers/shieldsPanelReducerTest.ts index e0653f8..7db9ad0 100644 --- a/test/app/background/reducers/shieldsPanelReducerTest.ts +++ b/test/app/background/reducers/shieldsPanelReducerTest.ts @@ -490,7 +490,7 @@ describe('braveShieldsPanelReducer', () => { } }) assert.equal(this.spy.calledOnce, true) - assert.equal(this.spy.getCall(0).args[0], '12') + assert.equal(this.spy.getCall(0).args[1], '12') }) it('increments for JS blocking', function () { let nextState = shieldsPanelReducer(state, { diff --git a/test/testData.ts b/test/testData.ts index 1857f3d..ac0ad40 100644 --- a/test/testData.ts +++ b/test/testData.ts @@ -74,8 +74,11 @@ export const getMockChrome = () => { onStartup: new ChromeEvent() }, browserAction: { - setBadgeText: function (text: string) {}, - setIcon: function (icon: string, tabId: number) {} + setBadgeBackgroundColor: function (properties: object) {}, + setBadgeText: function (textProperties: object) {}, + setIcon: function (iconProperties: object) {}, + enable: function (tabId?: number) {}, + disable: function (tabId?: number) {} }, tabs: { queryAsync: function () { @@ -149,6 +152,7 @@ export const initialState = deepFreeze({ tabs: {}, windows: {} }, + runtime: {}, shieldsPanel: { currentWindowId: -1, tabs: {},