From 86d5b33441b914d6a2b88dc47e60a9a4beb51e0d Mon Sep 17 00:00:00 2001 From: Cezar Augusto Date: Tue, 21 May 2019 18:20:36 -0300 Subject: [PATCH] add new noScript interface - * scripts now show intermediary state "allowed once" and "blocked once" * lists with zero items are now hidden * add ability to see grouped scripts fix https://github.com/brave/brave-browser/issues/4572 fix https://github.com/brave/brave-browser/issues/4570 fix https://github.com/brave/brave-browser/issues/4567 address https://github.com/brave/brave-browser/issues/2996 --- .../_locales/en_US/messages.json | 4 + .../actions/shieldsPanelActions.ts | 34 +++- .../reducers/cosmeticFilterReducer.ts | 26 ++- .../reducers/shieldsPanelReducer.ts | 37 +++- .../components/braveShields.tsx | 17 +- .../components/controls/scriptsControl.tsx | 52 +++-- .../components/list/dynamic.tsx | 139 ------------- .../components/list/noScript.tsx | 189 +++++++++++++++++ .../components/list/noScriptContent.tsx | 137 +++++++++++++ .../components/privacyControls.tsx | 15 +- .../constants/shieldsPanelTypes.ts | 5 +- .../brave_extension/helpers/noScriptUtils.ts | 58 +++++- .../brave_extension/helpers/urlUtils.ts | 18 ++ .../brave_extension/state/noScriptState.ts | 90 +++++++++ .../state/shieldsPanelState.ts | 44 +--- .../types/actions/shieldsPanelActions.ts | 35 ++-- .../types/constants/shieldsPanelTypes.ts | 5 +- .../types/state/noScriptState.ts | 16 ++ .../types/state/shieldsPannelState.ts | 8 - .../actions/shieldsPanelActions_test.ts | 8 - .../reducers/cosmeticFilterReducer_test.ts | 36 +++- .../reducers/shieldsPanelReducer_test.ts | 191 ++++-------------- .../controls/scriptsControl_test.tsx | 8 +- .../state/shieldsPanelState_test.ts | 21 +- 24 files changed, 750 insertions(+), 443 deletions(-) delete mode 100644 components/brave_extension/extension/brave_extension/components/list/dynamic.tsx create mode 100644 components/brave_extension/extension/brave_extension/components/list/noScript.tsx create mode 100644 components/brave_extension/extension/brave_extension/components/list/noScriptContent.tsx diff --git a/components/brave_extension/extension/brave_extension/_locales/en_US/messages.json b/components/brave_extension/extension/brave_extension/_locales/en_US/messages.json index 3377c88c2519..01a138c6e29f 100644 --- a/components/brave_extension/extension/brave_extension/_locales/en_US/messages.json +++ b/components/brave_extension/extension/brave_extension/_locales/en_US/messages.json @@ -127,6 +127,10 @@ "message": "Allowed once", "description": "Message for the resources blocked *allowed once* option" }, + "allowScriptsOnce": { + "message": "Allow scripts once", + "description": "Message for the shortcut in the main interface to allow all scripts once" + }, "cancel": { "message": "Cancel", "description": "Message for the button inside the static list of resources blocked to cancel the operation" diff --git a/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts b/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts index f3124e7f6bbf..b08d6cb53cb2 100644 --- a/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts +++ b/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts @@ -74,17 +74,39 @@ export const allowScriptOriginsOnce: actions.AllowScriptOriginsOnce = () => { } } -export const changeNoScriptSettings: actions.ChangeNoScriptSettings = (origin) => { +/** + * Set a given script resource state to be in the allowed/blocked list + * @param {string} url - The resource URL + * @param {boolean} maybeBlock - Whether or not the resource should be blocked + */ +export const setScriptBlockedCurrentState: actions.SetScriptBlockedCurrentState = (url) => { + return { + type: types.SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE, + url + } +} + +/** + * Set all child resources of a given hostname to be in the allowed/blocked list + * @param {string} origin - The blocked resource hostname + * @param {boolean} maybeBlock - Whether or not the resource should be blocked + */ +export const setGroupedScriptsBlockedCurrentState: actions.SetGroupedScriptsBlockedCurrentState = (origin, maybeBlock) => { return { - type: types.CHANGE_NO_SCRIPT_SETTINGS, - origin + type: types.SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, + origin, + maybeBlock } } -export const changeAllNoScriptSettings: actions.ChangeAllNoScriptSettings = (shouldBlock) => { +/** + * Set all resources in blocked/allowed state to be in the allowed/blocked list + * @param {boolean} maybeBlock - Whether or not the resource should be blocked + */ +export const setAllScriptsBlockedCurrentState: actions.SetAllScriptsBlockedCurrentState = (maybeBlock) => { return { - type: types.CHANGE_ALL_NO_SCRIPT_SETTINGS, - shouldBlock + type: types.SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, + maybeBlock } } diff --git a/components/brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer.ts b/components/brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer.ts index f99428ded109..512a92706583 100644 --- a/components/brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer.ts +++ b/components/brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer.ts @@ -1,20 +1,19 @@ -// /* 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/. */ +/* 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/. */ +// Types 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 * as cosmeticFilterTypes from '../../constants/cosmeticFilterTypes' import { State } from '../../types/state/shieldsPannelState' import { Actions } from '../../types/actions/index' -import * as cosmeticFilterTypes from '../../constants/cosmeticFilterTypes' + +// APIs +import { setAllowBraveShields, requestShieldPanelData } from '../api/shieldsAPI' +import { reloadTab } from '../api/tabsAPI' import { removeSiteFilter, addSiteCosmeticFilter, @@ -22,6 +21,11 @@ import { removeAllFilters } from '../api/cosmeticFilterAPI' +// State helpers +import * as shieldsPanelState from '../../state/shieldsPanelState' +import * as noScriptState from '../../state/noScriptState' +import { getOrigin } from '../../helpers/urlUtils' + const focusedWindowChanged = (state: State, windowId: number): State => { if (windowId !== -1) { state = shieldsPanelState.updateFocusedWindow(state, windowId) @@ -54,7 +58,7 @@ export default function cosmeticFilterReducer (state: State = { if (action.isMainFrame) { state = shieldsPanelState.resetBlockingStats(state, action.tabId) state = shieldsPanelState.resetBlockingResources(state, action.tabId) - state = shieldsPanelState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin) + state = noScriptState.resetNoScriptInfo(state, action.tabId, getOrigin(action.url)) } applySiteFilters(tabData.hostname) break diff --git a/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts b/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts index deab6c6d9ada..49e87987cb03 100644 --- a/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts +++ b/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts @@ -38,7 +38,7 @@ export default function shieldsPanelReducer (state: State = { tabs: {}, windows: if (action.isMainFrame) { state = shieldsPanelState.resetBlockingStats(state, action.tabId) state = shieldsPanelState.resetBlockingResources(state, action.tabId) - state = shieldsPanelState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin) + state = noScriptState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin) } break } @@ -265,14 +265,37 @@ export default function shieldsPanelReducer (state: State = { tabs: {}, windows: }) break } - case shieldsPanelTypes.CHANGE_NO_SCRIPT_SETTINGS: { - const tabId: number = shieldsPanelState.getActiveTabId(state) - state = shieldsPanelState.changeNoScriptSettings(state, tabId, action.origin) + // NoScriptInfo is the name we call for the list of scripts that are either + // blocked or allowed by the user. Each script have three properties: + // .................................................................................... + // `actuallyBlocked`: + // .................................................................................... + // When set to `true` it blocks the script immediatelly. This is the initial state + // when the user toggle scripts blocked in the main panel screen and also the initial state + // for when users toggle `block/allow` or `block all/allow all` + // .................................................................................... + // `willBlock`: + // .................................................................................... + // When set to `true` it moves the script to its respective list. This is the final state + // when the user choose to close Shields either by clicking `cancel`, moving back to the + // main screen, or closing Shields browser action. This state is triggered only after those actions + // and its state inherit the state of `actuallyBlocked`. + // .................................................................................... + // `userInteracted`: + // .................................................................................... + // This property is for display only. With this we can tell whether or not the user have + // interacted with the script which can change the button state to allow/block (no user interaction) + // or blocked once/allowed once (user has interacted). + case shieldsPanelTypes.SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE: { + state = noScriptState.setScriptBlockedCurrentState(state, action.url) break } - case shieldsPanelTypes.CHANGE_ALL_NO_SCRIPT_SETTINGS: { - const tabId: number = shieldsPanelState.getActiveTabId(state) - state = shieldsPanelState.changeAllNoScriptSettings(state, tabId, action.shouldBlock) + case shieldsPanelTypes.SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE: { + state = noScriptState.setGroupedScriptsBlockedCurrentState(state, action.origin, action.maybeBlock) + break + } + case shieldsPanelTypes.SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE: { + state = noScriptState.setAllScriptsBlockedCurrentState(state, action.maybeBlock) break } case shieldsPanelTypes.SET_FINAL_SCRIPTS_BLOCKED_ONCE_STATE: { diff --git a/components/brave_extension/extension/brave_extension/components/braveShields.tsx b/components/brave_extension/extension/brave_extension/components/braveShields.tsx index e7570456e4c1..0a1149e83045 100644 --- a/components/brave_extension/extension/brave_extension/components/braveShields.tsx +++ b/components/brave_extension/extension/brave_extension/components/braveShields.tsx @@ -27,8 +27,9 @@ import { BlockFingerprinting, BlockCookies, AllowScriptOriginsOnce, - ChangeNoScriptSettings, - ChangeAllNoScriptSettings, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, SetFinalScriptsBlockedState } from '../types/actions/shieldsPanelActions' @@ -41,8 +42,9 @@ interface Props { blockFingerprinting: BlockFingerprinting blockCookies: BlockCookies allowScriptOriginsOnce: AllowScriptOriginsOnce - changeNoScriptSettings: ChangeNoScriptSettings - changeAllNoScriptSettings: ChangeAllNoScriptSettings + setScriptBlockedCurrentState: SetScriptBlockedCurrentState + setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState + setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState setFinalScriptsBlockedState: SetFinalScriptsBlockedState } shieldsPanelTabData: Tab @@ -128,10 +130,12 @@ export default class Shields extends React.PureComponent { javascript={shieldsPanelTabData.javascript} javascriptBlocked={shieldsPanelTabData.javascriptBlocked} noScriptInfo={shieldsPanelTabData.noScriptInfo} - changeNoScriptSettings={actions.changeNoScriptSettings} blockJavaScript={actions.blockJavaScript} - changeAllNoScriptSettings={actions.changeAllNoScriptSettings} allowScriptOriginsOnce={actions.allowScriptOriginsOnce} + setScriptBlockedCurrentState={actions.setScriptBlockedCurrentState} + setGroupedScriptsBlockedCurrentState={actions.setGroupedScriptsBlockedCurrentState} + setAllScriptsBlockedCurrentState={actions.setAllScriptsBlockedCurrentState} + setFinalScriptsBlockedState={actions.setFinalScriptsBlockedState} // Cookies blockCookies={actions.blockCookies} cookies={shieldsPanelTabData.cookies} @@ -140,7 +144,6 @@ export default class Shields extends React.PureComponent { fingerprintingBlocked={shieldsPanelTabData.fingerprintingBlocked} fingerprintingBlockedResources={shieldsPanelTabData.fingerprintingBlockedResources} blockFingerprinting={actions.blockFingerprinting} - setFinalScriptsBlockedState={actions.setFinalScriptsBlockedState} /> ) diff --git a/components/brave_extension/extension/brave_extension/components/controls/scriptsControl.tsx b/components/brave_extension/extension/brave_extension/components/controls/scriptsControl.tsx index 416098861505..59b2fe295d40 100644 --- a/components/brave_extension/extension/brave_extension/components/controls/scriptsControl.tsx +++ b/components/brave_extension/extension/brave_extension/components/controls/scriptsControl.tsx @@ -11,11 +11,12 @@ import { ArrowDownIcon, BlockedInfoRowStats, BlockedInfoRowText, + LinkAction, Toggle } from 'brave-ui/features/shields' // Group Components -import DynamicList from '../list/dynamic' +import NoScript from '../list/noScript' // Locale import { getLocale } from '../../background/api/localeAPI' @@ -33,10 +34,11 @@ import { import { BlockJSOptions } from '../../types/other/blockTypes' import { NoScriptInfo } from '../../types/other/noScriptInfo' import { - ChangeNoScriptSettings, BlockJavaScript, - ChangeAllNoScriptSettings, AllowScriptOriginsOnce, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, SetFinalScriptsBlockedState } from '../../types/actions/shieldsPanelActions' @@ -52,10 +54,11 @@ interface JavaScriptProps { javascript: BlockJSOptions javascriptBlocked: number noScriptInfo: NoScriptInfo - changeNoScriptSettings: ChangeNoScriptSettings blockJavaScript: BlockJavaScript - changeAllNoScriptSettings: ChangeAllNoScriptSettings allowScriptOriginsOnce: AllowScriptOriginsOnce + setScriptBlockedCurrentState: SetScriptBlockedCurrentState + setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState + setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState setFinalScriptsBlockedState: SetFinalScriptsBlockedState } @@ -116,21 +119,28 @@ export default class ScriptsControls extends React.PureComponent { this.props.blockJavaScript(shouldBlockJavaScript) } + onClickAllowScriptsOnce = () => { + this.props.setAllScriptsBlockedCurrentState(false) + this.props.setFinalScriptsBlockedState() + this.props.allowScriptOriginsOnce() + } + render () { const { favicon, hostname, isBlockedListOpen, allowScriptOriginsOnce, - changeNoScriptSettings, - changeAllNoScriptSettings, - setFinalScriptsBlockedState, - noScriptInfo + noScriptInfo, + setScriptBlockedCurrentState, + setGroupedScriptsBlockedCurrentState, + setAllScriptsBlockedCurrentState, + setFinalScriptsBlockedState } = this.props const { scriptsBlockedOpen } = this.state return ( <> - + { {this.javascriptBlockedDisplay} {getLocale('scriptsBlocked')} + { + this.maybeDisableResourcesRow === false + && ( + + {getLocale('allowScriptsOnce')} + + ) + } { { scriptsBlockedOpen && - } diff --git a/components/brave_extension/extension/brave_extension/components/list/dynamic.tsx b/components/brave_extension/extension/brave_extension/components/list/dynamic.tsx deleted file mode 100644 index 7262718dc2de..000000000000 --- a/components/brave_extension/extension/brave_extension/components/list/dynamic.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* 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 React from 'react' - -import { - BlockedListHeader, - BlockedListSummary, - BlockedListContent, - BlockedListItemHeader, - BlockedListDynamic, - BlockedListItemWithOptions, - BlockedListFooterWithOptions, - ArrowUpIcon, - LinkAction, - Favicon, - SiteInfoText, - BlockedListSummaryText, - BlockedListItemHeaderStats, - BlockedListItemHeaderText, - ShieldsButton -} from 'brave-ui/features/shields' - -// Locale -import { getLocale } from '../../background/api/localeAPI' - -// Types -import { NoScriptInfo } from '../../types/other/noScriptInfo' -import { - AllowScriptOriginsOnce, - ChangeNoScriptSettings, - ChangeAllNoScriptSettings, - SetFinalScriptsBlockedState -} from '../../types/actions/shieldsPanelActions' - -// Utils -import { getBlockScriptText } from '../../helpers/noScriptUtils' - -interface Props { - favicon: string - hostname: string - origin: string - name: string - list: NoScriptInfo - onClose?: (event?: React.MouseEvent) => void - allowScriptOriginsOnce: AllowScriptOriginsOnce - changeNoScriptSettings: ChangeNoScriptSettings - changeAllNoScriptSettings: ChangeAllNoScriptSettings - setFinalScriptsBlockedState: SetFinalScriptsBlockedState -} - -export default class DynamicList extends React.PureComponent { - getBlockedListSize = (isBlocked: boolean) => { - const { list } = this.props - return Object.keys(list) - .filter((origin: string) => list[origin].willBlock === isBlocked).length - } - - onClickBlockOrAllowScript = (event: React.MouseEvent) => { - this.props.changeNoScriptSettings(event.currentTarget.id) - this.props.allowScriptOriginsOnce() - } - - onClickAllowOrBlockAll (shouldBlock: boolean) { - this.props.changeAllNoScriptSettings(shouldBlock) - this.props.allowScriptOriginsOnce() - } - - onClickConfirmApplyScripts = () => { - this.props.setFinalScriptsBlockedState() - } - - getList = (isBlocked: boolean) => { - const { list } = this.props - return Object.keys(list).map((origin, index) => { - if (list[origin].willBlock === isBlocked) { - return null - } - return ( - - {origin} - - {getBlockScriptText(list[origin].userInteracted, !isBlocked)} - - - ) - }) - } - - render () { - const { favicon, hostname, name, onClose } = this.props - return ( - - - - {hostname} - -
- - - {name} - - - - - {this.getBlockedListSize(true)} - - {getLocale('blockedScripts')} - - {getLocale('allowAll')} - - - {this.getList(false)} - - - {this.getBlockedListSize(false)} - - {getLocale('allowedScripts')} - - {getLocale('blockAll')} - - - {this.getList(true)} - -
- - {getLocale('cancel')} - - -
- ) - } -} diff --git a/components/brave_extension/extension/brave_extension/components/list/noScript.tsx b/components/brave_extension/extension/brave_extension/components/list/noScript.tsx new file mode 100644 index 000000000000..581aca68c11e --- /dev/null +++ b/components/brave_extension/extension/brave_extension/components/list/noScript.tsx @@ -0,0 +1,189 @@ +/* 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/. */ + +// React API +import * as React from 'react' + +// Types +import { NoScriptInfo } from '../../types/other/noScriptInfo' +import { + AllowScriptOriginsOnce, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, + SetFinalScriptsBlockedState +} from '../../types/actions/shieldsPanelActions' + +// Components +import NoScriptContent from './noScriptContent' + +// Helpers +import { + filterNoScriptInfoByWillBlockState, + checkEveryItemIsBlockedOrAllowedByUser, + generateNoScriptInfoDataStructure, + getBlockAllText +} from '../../helpers/noScriptUtils' + +// Feature-specific components +import { + BlockedListHeader, + BlockedListSummary, + BlockedListContent, + BlockedListItemHeader, + BlockedListDynamic, + BlockedListFooter, + ArrowUpIcon, + LinkAction, + Favicon, + SiteInfoText, + BlockedListSummaryText, + BlockedListItemHeaderStats, + BlockedListItemHeaderText, + ShieldsButton +} from 'brave-ui/features/shields' + +// Helpers +import { getLocale } from '../../background/api/localeAPI' + +interface Props { + favicon: string + hostname: string + noScriptInfo: NoScriptInfo + onClose: (event?: React.MouseEvent) => void + allowScriptOriginsOnce: AllowScriptOriginsOnce + setScriptBlockedCurrentState: SetScriptBlockedCurrentState + setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState + setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState + setFinalScriptsBlockedState: SetFinalScriptsBlockedState +} + +export default class CoreFeature extends React.PureComponent { + get noScriptInfo () { + return this.props.noScriptInfo + } + + get generatedNoScriptData () { + return generateNoScriptInfoDataStructure(this.noScriptInfo) + } + + componentDidMount () { + window.addEventListener('blur', () => { + this.props.setFinalScriptsBlockedState() + window.close() + }) + } + + getBlockedScriptsLength (maybeBlock: boolean) { + return filterNoScriptInfoByWillBlockState(Object.entries(this.noScriptInfo), maybeBlock).length + } + + maybeDisabledBlockOrAllowAll (maybeBlock: boolean) { + return checkEveryItemIsBlockedOrAllowedByUser(Object.entries(this.noScriptInfo), maybeBlock) + } + + blockOrAllowAll (blockOrAllow: boolean) { + this.props.setAllScriptsBlockedCurrentState(blockOrAllow) + this.props.allowScriptOriginsOnce() + } + + setFinalScriptsBlockedState = (event?: any) => { + // indicate local state that those scripts are going to be blocked + this.props.setFinalScriptsBlockedState() + // close the scripts modal layer + if (event) { + this.props.onClose(event) + } + } + + getBlockAllText (shouldBlock: boolean) { + return getBlockAllText(this.noScriptInfo, shouldBlock) + } + + render () { + const { favicon, hostname } = this.props + return ( + + + + {hostname} + +
+ + + {getLocale('scriptsOnThisSite')} + + + { + this.getBlockedScriptsLength(true) > 0 && ( + <> + + + {this.getBlockedScriptsLength(true)} + + + {getLocale('blockedScripts')} + + + {this.getBlockAllText(true)} + + + + + ) + } + { + this.getBlockedScriptsLength(false) > 0 && ( + <> + + + {this.getBlockedScriptsLength(false)} + + + {getLocale('allowedScripts')} + + + {this.getBlockAllText(false)} + + + + + ) + } + +
+ + + +
+ ) + } +} diff --git a/components/brave_extension/extension/brave_extension/components/list/noScriptContent.tsx b/components/brave_extension/extension/brave_extension/components/list/noScriptContent.tsx new file mode 100644 index 000000000000..0ff0e137d7f5 --- /dev/null +++ b/components/brave_extension/extension/brave_extension/components/list/noScriptContent.tsx @@ -0,0 +1,137 @@ +/* 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/. */ + +// React API +import * as React from 'react' + +// Types +import { NoScriptEntry } from '../../types/other/noScriptInfo' +import { + AllowScriptOriginsOnce, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, + SetFinalScriptsBlockedState +} from '../../types/actions/shieldsPanelActions' + +// Components +import { + BlockedListItemWithOptions, + LinkAction, + BlockedListItemDetails, + BlockedListItemSummary +} from 'brave-ui/features/shields' + +// Helpers +import { getHostname, stripProtocolFromUrl } from '../../helpers/urlUtils' +import { + getBlockScriptText, + filterNoScriptInfoByWillBlockState, + checkEveryItemIsBlockedOrAllowedByUser +} from '../../helpers/noScriptUtils' + +interface Props { + noScriptInfo: Array + maybeBlock: boolean + allowScriptOriginsOnce: AllowScriptOriginsOnce + setScriptBlockedCurrentState: SetScriptBlockedCurrentState + setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState + setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState + setFinalScriptsBlockedState: SetFinalScriptsBlockedState +} + +export default class NoScriptList extends React.PureComponent { + getBlockScriptText = (userInteracted: boolean, maybeBlock: boolean) => { + return getBlockScriptText(userInteracted, maybeBlock) + } + + setBlockState (url: string) { + this.props.setScriptBlockedCurrentState(url) + this.props.allowScriptOriginsOnce() + } + + setBlockStateGroup (url: string, maybeBlock: boolean) { + this.props.setGroupedScriptsBlockedCurrentState(url, maybeBlock) + this.props.allowScriptOriginsOnce() + } + + getSingleScriptRow = (url: string, scriptData: NoScriptEntry, key: number, maybeBlock: boolean) => { + return ( + + {stripProtocolFromUrl(url)} + + {this.getBlockScriptText(scriptData.userInteracted, maybeBlock)} + + + ) + } + + getNestedOrSingleScriptsLoop = (modifiedNoScriptInfo: Array, maybeNested: boolean, maybeBlock: boolean) => { + return ( + modifiedNoScriptInfo.map((script: NoScriptEntry, key: number) => { + const url = script[0] + const noScriptData = script[1] + if (noScriptData.willBlock !== maybeBlock) { + return null + } + if (maybeNested) { + return ( + + {stripProtocolFromUrl(url)} + + ) + } + return this.getSingleScriptRow(url, noScriptData, key, maybeBlock) + }) + ) + } + + getGroupedScriptsRow = (script: NoScriptEntry, key: number, maybeBlock: boolean) => { + const url = script[0] + const noScriptData = script[1] + const hasNoScriptData = filterNoScriptInfoByWillBlockState(noScriptData, maybeBlock).length >= 2 + const everyItemIsBlockedOrAllowed = checkEveryItemIsBlockedOrAllowedByUser(noScriptData, maybeBlock) + + if (hasNoScriptData) { + return ( +
  • + + + {getHostname(url)} + + {this.getBlockScriptText(everyItemIsBlockedOrAllowed, maybeBlock)} + + + {this.getNestedOrSingleScriptsLoop(noScriptData, true, maybeBlock)} + +
  • + ) + } + // if script is nested but separated from the group, render a detached script + return this.getNestedOrSingleScriptsLoop(noScriptData, false, maybeBlock) + } + + render () { + const { noScriptInfo, maybeBlock } = this.props + return noScriptInfo.map((script: NoScriptEntry, key: number) => { + const scriptData = script[1] + const url = scriptData[0][0] + const scriptInfo = scriptData[0][1] + + return scriptData.length > 1 + ? ( + this.getGroupedScriptsRow(script, key, maybeBlock) + ) + : scriptInfo.willBlock === maybeBlock && ( + this.getSingleScriptRow(url, scriptInfo, key, maybeBlock) + ) + }) + } +} diff --git a/components/brave_extension/extension/brave_extension/components/privacyControls.tsx b/components/brave_extension/extension/brave_extension/components/privacyControls.tsx index bdafcbc2e77e..a84e028acf81 100644 --- a/components/brave_extension/extension/brave_extension/components/privacyControls.tsx +++ b/components/brave_extension/extension/brave_extension/components/privacyControls.tsx @@ -11,12 +11,13 @@ import DeviceRecognitionControl from './controls/deviceRecognitionControl' // Types import { - ChangeNoScriptSettings, BlockJavaScript, - ChangeAllNoScriptSettings, AllowScriptOriginsOnce, BlockCookies, BlockFingerprinting, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, SetFinalScriptsBlockedState } from '../types/actions/shieldsPanelActions' import { BlockCookiesOptions, BlockJSOptions, BlockFPOptions } from '../types/other/blockTypes' @@ -33,10 +34,11 @@ interface JavaScriptProps { javascript: BlockJSOptions javascriptBlocked: number noScriptInfo: NoScriptInfo - changeNoScriptSettings: ChangeNoScriptSettings blockJavaScript: BlockJavaScript - changeAllNoScriptSettings: ChangeAllNoScriptSettings allowScriptOriginsOnce: AllowScriptOriginsOnce + setScriptBlockedCurrentState: SetScriptBlockedCurrentState + setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState + setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState setFinalScriptsBlockedState: SetFinalScriptsBlockedState } @@ -68,10 +70,11 @@ export default class PrivacyControls extends React.PureComponent { javascript={this.props.javascript} javascriptBlocked={this.props.javascriptBlocked} noScriptInfo={this.props.noScriptInfo} - changeNoScriptSettings={this.props.changeNoScriptSettings} blockJavaScript={this.props.blockJavaScript} - changeAllNoScriptSettings={this.props.changeAllNoScriptSettings} allowScriptOriginsOnce={this.props.allowScriptOriginsOnce} + setScriptBlockedCurrentState={this.props.setScriptBlockedCurrentState} + setGroupedScriptsBlockedCurrentState={this.props.setGroupedScriptsBlockedCurrentState} + setAllScriptsBlockedCurrentState={this.props.setAllScriptsBlockedCurrentState} setFinalScriptsBlockedState={this.props.setFinalScriptsBlockedState} /> { + return Object.entries(noScriptInfo).filter((script) => { + return getOrigin(url) === getOrigin(script[0]) + }) +} + +/** + * Generate data structure for the NoScript object. + * This is useful to group scripts by origin and is used for presentational + * purposes only, as data is stored same way as it comes from the back-end. + * @param {NoScriptInfo} noScriptInfo - The NoScriptInfo state + * @returns {Array} - The new generated NoScriptInfo data + */ +export const generateNoScriptInfoDataStructure = (noScriptInfo: NoScriptInfo): Array => { + let newData = [] + for (const [url] of Object.entries(noScriptInfo)) { + const entry = newData.some((item) => item[0] === getOrigin(url)) + if (!entry) { + newData.push([ getOrigin(url), filterResourcesBySameOrigin(noScriptInfo, url) ]) + } + } + return newData +} + +/** + * Filter NoScriptInfo by `willBlock` state + * @param {Array} modifiedNoScriptInfo - The NoScriptInfo state + * @param {boolean} maybeBlock - Whether or not the resource should be blocked + * @returns {Array} - The new generated NoScriptInfo data + */ +export const filterNoScriptInfoByWillBlockState = ( + modifiedNoScriptInfo: Array, + maybeBlock: boolean +): Array => { + return modifiedNoScriptInfo.filter(script => script[1].willBlock === maybeBlock) +} + /** * Check if all scripts in NoScriptInfo are either allowed or blocked by the user - * @param {NoScriptInfo} noScriptInfo - The modifiedNoScriptInfo state + * @param {[string, NoScriptEntry][]} modifiedNoScriptInfo - The modifiedNoScriptInfo state * @param {boolean} isBlocked - Whether or not all scripts are blocked * @returns {boolean} - Whether or not the new generated NoScriptInfo data is either blocked or allowed */ export const checkEveryItemIsBlockedOrAllowedByUser = ( - modifiedNoScriptInfo: NoScriptInfo, + modifiedNoScriptInfo: [string, NoScriptEntry][], isBlocked: boolean ): boolean => { - return Object.entries(modifiedNoScriptInfo) + return modifiedNoScriptInfo .filter(script => script[1].willBlock === isBlocked) .every(script => script[1].userInteracted) } @@ -36,7 +82,7 @@ export const getBlockAllText = ( noScriptInfo: NoScriptInfo, isBlocked: boolean ): string => { - const allInteracted = checkEveryItemIsBlockedOrAllowedByUser(noScriptInfo, isBlocked) + const allInteracted = checkEveryItemIsBlockedOrAllowedByUser(Object.entries(noScriptInfo), isBlocked) if (isBlocked) { if (allInteracted) { @@ -75,5 +121,5 @@ export const getBlockScriptText = (haveUserInteracted: boolean, isBlocked: boole export const getAllowedScriptsOrigins = (modifiedNoScriptInfo: NoScriptInfo): Array => { const getAllowedOrigins = Object.entries(modifiedNoScriptInfo) .filter(url => url[1].actuallyBlocked === false) - return getAllowedOrigins.map(url => url[0]) + return getAllowedOrigins.map(url => getOrigin(url[0]) + '/') } diff --git a/components/brave_extension/extension/brave_extension/helpers/urlUtils.ts b/components/brave_extension/extension/brave_extension/helpers/urlUtils.ts index 09ec3acbccdf..ae69f116d4b1 100644 --- a/components/brave_extension/extension/brave_extension/helpers/urlUtils.ts +++ b/components/brave_extension/extension/brave_extension/helpers/urlUtils.ts @@ -14,3 +14,21 @@ export const isHttpOrHttps = (url?: string) => { export const hasPortNumber = (url: string) => { return typeof urlParser.parse(url).port === 'string' } + +/** + * Get the URL origin via Web API + * @param {string} url - The URL to get the origin from + */ +export const getOrigin = (url: string) => new window.URL(url).origin + +/** + * Get the URL hostname via Web API + * @param {string} url - The URL to get the origin from + */ +export const getHostname = (url: string) => new window.URL(url).hostname + +/** + * Strip http/https protocol + * @param {string} url - The URL to strip the protocol from + */ +export const stripProtocolFromUrl = (url: string) => url.replace(/(^\w+:|^)\/\//, '') diff --git a/components/brave_extension/extension/brave_extension/state/noScriptState.ts b/components/brave_extension/extension/brave_extension/state/noScriptState.ts index a72756e3252c..34a2e243241c 100644 --- a/components/brave_extension/extension/brave_extension/state/noScriptState.ts +++ b/components/brave_extension/extension/brave_extension/state/noScriptState.ts @@ -7,11 +7,17 @@ import { Tabs } from '../types/state/shieldsPannelState' import { GetNoScriptInfo, ModifyNoScriptInfo, + ResetNoScriptInfo, + SetScriptBlockedCurrentState, + SetGroupedScriptsBlockedCurrentState, + SetAllScriptsBlockedCurrentState, SetFinalScriptsBlockedState } from '../types/state/noScriptState' // Helpers import { getActiveTabId } from './shieldsPanelState' +import { filterNoScriptInfoByWillBlockState } from '../helpers/noScriptUtils' +import { getOrigin } from '../helpers/urlUtils' /** * Get NoScriptInfo initial state @@ -50,6 +56,90 @@ export const modifyNoScriptInfo: ModifyNoScriptInfo = (state, tabId, url, modifi return { ...state, tabs } } +/** + * Reset NoScriptInfo to its initial state + * @param {State} state - The Application state + * @param {number} tabId - The current tab ID + * @returns {State} The modified application state + */ +export const resetNoScriptInfo: ResetNoScriptInfo = (state, tabId, newOrigin) => { + const tabs: Tabs = { ...state.tabs } + + if (newOrigin !== tabs[tabId].origin) { // navigate away + tabs[tabId].noScriptInfo = {} + } + return { ...state, tabs } +} + +/** + * Set current script to be either blocked or allowed + * @param {State} state - The Application state + * @param {string} url - The current script URL + * @returns {State} The modified application state + */ +export const setScriptBlockedCurrentState: SetScriptBlockedCurrentState = (state, url) => { + const tabId: number = getActiveTabId(state) + const noScriptInfo = getNoScriptInfo(state, tabId) + + const scriptBlockedState = { + userInteracted: true, + actuallyBlocked: !noScriptInfo[url].actuallyBlocked + } + state = modifyNoScriptInfo(state, tabId, url, scriptBlockedState) + return state +} + +/** + * Set all scripts in a group to be either blocked or allowed + * @param {State} state - The Application state + * @param {string} origin - The current script URL origin + * @returns {State} The modified application state + */ +export const setGroupedScriptsBlockedCurrentState: SetGroupedScriptsBlockedCurrentState = ( + state, + origin, + maybeBlock +) => { + const tabId = getActiveTabId(state) + const noScriptInfo = getNoScriptInfo(state, tabId) + const groupedScripts = Object.entries(noScriptInfo) + + for (const [url] of filterNoScriptInfoByWillBlockState(groupedScripts, maybeBlock)) { + const groupedScriptsBlockedState = { + userInteracted: true, + actuallyBlocked: !noScriptInfo[url].actuallyBlocked + } + + if (origin === getOrigin(url)) { + state = modifyNoScriptInfo(state, tabId, url, groupedScriptsBlockedState) + } + } + return state +} + +/** + * Set all scripts to be either blocked or allowed + * @param {State} state - The Application state + * @returns {State} The modified application state + */ +export const setAllScriptsBlockedCurrentState: SetAllScriptsBlockedCurrentState = ( + state, + maybeBlock +) => { + const tabId = getActiveTabId(state) + const noScriptInfo = getNoScriptInfo(state, tabId) + const allBlockedScripts = Object.entries(noScriptInfo) + + for (const [url] of filterNoScriptInfoByWillBlockState(allBlockedScripts, !maybeBlock)) { + const groupedScriptsBlockedState = { + userInteracted: true, + actuallyBlocked: maybeBlock + } + state = modifyNoScriptInfo(state, tabId, url, groupedScriptsBlockedState) + } + return state +} + /** * Set all scripts to be either blocked or allowed after user finish * @param {State} state - The Application state diff --git a/components/brave_extension/extension/brave_extension/state/shieldsPanelState.ts b/components/brave_extension/extension/brave_extension/state/shieldsPanelState.ts index 9916ee51ecef..4b339355718c 100644 --- a/components/brave_extension/extension/brave_extension/state/shieldsPanelState.ts +++ b/components/brave_extension/extension/brave_extension/state/shieldsPanelState.ts @@ -1,8 +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/. */ + * 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/. */ +// Types import * as shieldState from '../types/state/shieldsPannelState' + +// Helpers import { unique } from '../helpers/arrayUtils' import { getTotalResourcesBlocked } from '../helpers/shieldsUtils' import { setBadgeText, setIcon } from '../background/api/browserActionAPI' @@ -93,9 +96,8 @@ export const updateResourceBlocked: shieldState.UpdateResourceBlocked = (state, tabs[tabId].httpsRedirectedResources = unique([ ...tabs[tabId].httpsRedirectedResources, subresource ]) tabs[tabId].httpsRedirected = tabs[tabId].httpsRedirectedResources.length } else if (blockType === 'javascript') { - const origin = new window.URL(subresource).origin + '/' tabs[tabId].noScriptInfo = { ...tabs[tabId].noScriptInfo } - tabs[tabId].noScriptInfo[origin] = { ...{ actuallyBlocked: true, willBlock: true, userInteracted: false } } + tabs[tabId].noScriptInfo[subresource] = { ...{ actuallyBlocked: true, willBlock: true, userInteracted: false } } tabs[tabId].javascriptBlockedResources = unique([ ...tabs[tabId].javascriptBlockedResources, subresource ]) tabs[tabId].javascriptBlocked = tabs[tabId].javascriptBlockedResources.length } else if (blockType === 'fingerprinting') { @@ -106,40 +108,6 @@ export const updateResourceBlocked: shieldState.UpdateResourceBlocked = (state, return { ...state, tabs } } -export const changeNoScriptSettings: shieldState.ChangeNoScriptSettings = (state, tabId, origin) => { - const tabs: shieldState.Tabs = { ...state.tabs } - tabs[tabId] = { ...{ adsBlocked: 0, trackersBlocked: 0, httpsRedirected: 0, javascriptBlocked: 0, fingerprintingBlocked: 0, noScriptInfo: {} }, ...tabs[tabId] } - tabs[tabId].noScriptInfo[origin].actuallyBlocked = !tabs[tabId].noScriptInfo[origin].actuallyBlocked - tabs[tabId].noScriptInfo[origin].userInteracted = true - return { ...state, tabs } -} - -export const resetNoScriptInfo: shieldState.ResetNoScriptInfo = (state, tabId, newOrigin) => { - const tabs: shieldState.Tabs = { ...state.tabs } - if (newOrigin !== tabs[tabId].origin) { // navigate away - tabs[tabId].noScriptInfo = {} - } - return { ...state, tabs } -} - -export const changeAllNoScriptSettings: shieldState.ChangeAllNoScriptSettings = (state, tabId, shouldBlock) => { - const tabs: shieldState.Tabs = { ...state.tabs } - Object.keys(tabs[tabId].noScriptInfo).map(key => { - tabs[tabId].noScriptInfo[key].actuallyBlocked = shouldBlock - tabs[tabId].noScriptInfo[key].userInteracted = true - }) - return { ...state, tabs } -} - -export const persistAllNoScriptSettings: shieldState.PersistAllNoScriptSettings = (state, tabId) => { - const tabs: shieldState.Tabs = { ...state.tabs } - Object.keys(tabs[tabId].noScriptInfo).map(key => { - tabs[tabId].noScriptInfo[key].willBlock = tabs[tabId].noScriptInfo[key].actuallyBlocked - tabs[tabId].noScriptInfo[key].userInteracted = false - }) - return { ...state, tabs } -} - export const resetBlockingStats: shieldState.ResetBlockingStats = (state, tabId) => { const tabs: shieldState.Tabs = { ...state.tabs } tabs[tabId] = { ...tabs[tabId], ...{ adsBlocked: 0, trackersBlocked: 0, httpsRedirected: 0, javascriptBlocked: 0, fingerprintingBlocked: 0 } } diff --git a/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts b/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts index 932f9f3360ef..3477a336e353 100644 --- a/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts +++ b/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts @@ -112,22 +112,32 @@ export interface AllowScriptOriginsOnce { (): AllowScriptOriginsOnceReturn } -interface ChangeNoScriptSettingsReturn { - type: types.CHANGE_NO_SCRIPT_SETTINGS, - origin: string +interface SetScriptBlockedCurrentStateReturn { + type: types.SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE, + url: string +} + +export interface SetScriptBlockedCurrentState { + (url: string): SetScriptBlockedCurrentStateReturn +} + +interface SetGroupedScriptsBlockedCurrentStateReturn { + type: types.SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, + origin: string, + maybeBlock: boolean } -export interface ChangeNoScriptSettings { - (origin: string): ChangeNoScriptSettingsReturn +export interface SetGroupedScriptsBlockedCurrentState { + (origin: string, maybeBlock: boolean): SetGroupedScriptsBlockedCurrentStateReturn } -interface ChangeAllNoScriptSettingsReturn { - type: types.CHANGE_ALL_NO_SCRIPT_SETTINGS, - shouldBlock: boolean +interface SetAllScriptsBlockedCurrentStateReturn { + type: types.SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, + maybeBlock: boolean } -export interface ChangeAllNoScriptSettings { - (shouldBlock: boolean): ChangeAllNoScriptSettingsReturn +export interface SetAllScriptsBlockedCurrentState { + (maybeBlock: boolean): SetAllScriptsBlockedCurrentStateReturn } interface SetFinalScriptsBlockedStateReturn { @@ -149,6 +159,7 @@ export type shieldPanelActions = BlockFingerprintingReturn | BlockCookiesReturn | AllowScriptOriginsOnceReturn | - ChangeNoScriptSettingsReturn | - ChangeAllNoScriptSettingsReturn | + SetScriptBlockedCurrentStateReturn | + SetGroupedScriptsBlockedCurrentStateReturn | + SetAllScriptsBlockedCurrentStateReturn | SetFinalScriptsBlockedStateReturn diff --git a/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts b/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts index 2df944853ba9..ad23414857d8 100644 --- a/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts +++ b/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts @@ -14,6 +14,7 @@ export type JAVASCRIPT_TOGGLED = typeof types.JAVASCRIPT_TOGGLED export type BLOCK_FINGERPRINTING = typeof types.BLOCK_FINGERPRINTING export type BLOCK_COOKIES = typeof types.BLOCK_COOKIES export type ALLOW_SCRIPT_ORIGINS_ONCE = typeof types.ALLOW_SCRIPT_ORIGINS_ONCE -export type CHANGE_NO_SCRIPT_SETTINGS = typeof types.CHANGE_NO_SCRIPT_SETTINGS -export type CHANGE_ALL_NO_SCRIPT_SETTINGS = typeof types.CHANGE_ALL_NO_SCRIPT_SETTINGS +export type SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE = typeof types.SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE +export type SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE = typeof types.SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE +export type SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE = typeof types.SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE export type SET_FINAL_SCRIPTS_BLOCKED_ONCE_STATE = typeof types.SET_FINAL_SCRIPTS_BLOCKED_ONCE_STATE diff --git a/components/brave_extension/extension/brave_extension/types/state/noScriptState.ts b/components/brave_extension/extension/brave_extension/types/state/noScriptState.ts index 096f6a80dad8..0d54c24ff439 100644 --- a/components/brave_extension/extension/brave_extension/types/state/noScriptState.ts +++ b/components/brave_extension/extension/brave_extension/types/state/noScriptState.ts @@ -13,6 +13,22 @@ export interface ModifyNoScriptInfo { (state: State, tabId: number, url: string, modifiedInfo: {}): State } +export interface ResetNoScriptInfo { + (state: State, tabId: number, newOrigin: string): State +} + +export interface SetScriptBlockedCurrentState { + (state: State, url: string): State +} + +export interface SetGroupedScriptsBlockedCurrentState { + (state: State, origin: string, maybeBlock: boolean): State +} + +export interface SetAllScriptsBlockedCurrentState { + (state: State, maybeBlock: boolean): State +} + export interface SetFinalScriptsBlockedState { (state: State): State } diff --git a/components/brave_extension/extension/brave_extension/types/state/shieldsPannelState.ts b/components/brave_extension/extension/brave_extension/types/state/shieldsPannelState.ts index 02ab30ada96e..027df964109d 100644 --- a/components/brave_extension/extension/brave_extension/types/state/shieldsPannelState.ts +++ b/components/brave_extension/extension/brave_extension/types/state/shieldsPannelState.ts @@ -81,18 +81,10 @@ export interface ResetBlockingResources { (state: State, tabId: number): State } -export interface ChangeNoScriptSettings { - (state: State, tabId: number, origin: string): State -} - export interface ResetNoScriptInfo { (state: State, tabId: number, newOrigin: string): State } -export interface ChangeAllNoScriptSettings { - (state: State, tabId: number, shouldBlock: boolean): State -} - export interface UpdateShieldsIconBadgeText { (state: State): void } diff --git a/components/test/brave_extension/actions/shieldsPanelActions_test.ts b/components/test/brave_extension/actions/shieldsPanelActions_test.ts index 926b57066054..89e9d63d2c12 100644 --- a/components/test/brave_extension/actions/shieldsPanelActions_test.ts +++ b/components/test/brave_extension/actions/shieldsPanelActions_test.ts @@ -103,12 +103,4 @@ describe('shieldsPanelActions', () => { type: types.ALLOW_SCRIPT_ORIGINS_ONCE }) }) - - it('changeNoScriptSettings action', () => { - const origin = 'https://a.com' - expect(actions.changeNoScriptSettings(origin)).toEqual({ - type: types.CHANGE_NO_SCRIPT_SETTINGS, - origin - }) - }) }) diff --git a/components/test/brave_extension/background/reducers/cosmeticFilterReducer_test.ts b/components/test/brave_extension/background/reducers/cosmeticFilterReducer_test.ts index f619ff83fe34..18e20615c9ed 100644 --- a/components/test/brave_extension/background/reducers/cosmeticFilterReducer_test.ts +++ b/components/test/brave_extension/background/reducers/cosmeticFilterReducer_test.ts @@ -2,22 +2,32 @@ * 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/. */ +// Types import * as shieldPanelTypes from '../../../../brave_extension/extension/brave_extension/constants/shieldsPanelTypes' import * as cosmeticFilterTypes from '../../../../brave_extension/extension/brave_extension/constants/cosmeticFilterTypes' import * as windowTypes from '../../../../brave_extension/extension/brave_extension/constants/windowTypes' import * as tabTypes from '../../../../brave_extension/extension/brave_extension/constants/tabTypes' import * as webNavigationTypes from '../../../../brave_extension/extension/brave_extension/constants/webNavigationTypes' -import shieldsPanelReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer' +import { State } from '../../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' +import { ShieldDetails } from '../../../../brave_extension/extension/brave_extension/types/actions/shieldsPanelActions' + +// APIs import * as shieldsAPI from '../../../../brave_extension/extension/brave_extension/background/api/shieldsAPI' -import cosmeticFilterReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer' import * as cosmeticFilterAPI from '../../../../brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI' import * as tabsAPI from '../../../../brave_extension/extension/brave_extension/background/api/tabsAPI' + +// Reducers +import shieldsPanelReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer' +import cosmeticFilterReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/cosmeticFilterReducer' + +// State helpers import * as shieldsPanelState from '../../../../brave_extension/extension/brave_extension/state/shieldsPanelState' +import * as noScriptState from '../../../../brave_extension/extension/brave_extension/state/noScriptState' + +// Helpers import { initialState } from '../../../testData' import * as deepFreeze from 'deep-freeze-node' -import { ShieldDetails } from '../../../../brave_extension/extension/brave_extension/types/actions/shieldsPanelActions' import * as actions from '../../../../brave_extension/extension/brave_extension/actions/shieldsPanelActions' -import { State } from '../../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' describe('cosmeticFilterReducer', () => { it('should handle initial state', () => { @@ -31,7 +41,7 @@ describe('cosmeticFilterReducer', () => { let resetBlockingResourcesSpy: jest.SpyInstance beforeEach(() => { spy = jest.spyOn(shieldsPanelState, 'resetBlockingStats') - resetNoScriptInfoSpy = jest.spyOn(shieldsPanelState, 'resetNoScriptInfo') + resetNoScriptInfoSpy = jest.spyOn(noScriptState, 'resetNoScriptInfo') resetBlockingResourcesSpy = jest.spyOn(shieldsPanelState, 'resetBlockingResources') }) afterEach(() => { @@ -173,7 +183,9 @@ describe('cosmeticFilterReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false }, changeInfo: {} }) @@ -193,7 +205,9 @@ describe('cosmeticFilterReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false }, changeInfo: {} }) @@ -228,7 +242,9 @@ describe('cosmeticFilterReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false } }) expect(updateActiveTabSpy).toBeCalledTimes(1) @@ -246,7 +262,9 @@ describe('cosmeticFilterReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false } }) expect(updateActiveTabSpy).not.toBeCalled() diff --git a/components/test/brave_extension/background/reducers/shieldsPanelReducer_test.ts b/components/test/brave_extension/background/reducers/shieldsPanelReducer_test.ts index a7f905fbbd72..4ce3c778f2ca 100644 --- a/components/test/brave_extension/background/reducers/shieldsPanelReducer_test.ts +++ b/components/test/brave_extension/background/reducers/shieldsPanelReducer_test.ts @@ -2,20 +2,30 @@ * 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/. */ +// Types import * as types from '../../../../brave_extension/extension/brave_extension/constants/shieldsPanelTypes' import * as windowTypes from '../../../../brave_extension/extension/brave_extension/constants/windowTypes' import * as tabTypes from '../../../../brave_extension/extension/brave_extension/constants/tabTypes' import * as webNavigationTypes from '../../../../brave_extension/extension/brave_extension/constants/webNavigationTypes' -import shieldsPanelReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer' +import { State } from '../../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' +import { ShieldDetails } from '../../../../brave_extension/extension/brave_extension/types/actions/shieldsPanelActions' + +// APIs import * as shieldsAPI from '../../../../brave_extension/extension/brave_extension/background/api/shieldsAPI' import * as tabsAPI from '../../../../brave_extension/extension/brave_extension/background/api/tabsAPI' import * as browserActionAPI from '../../../../brave_extension/extension/brave_extension/background/api/browserActionAPI' + +// Reducers +import shieldsPanelReducer from '../../../../brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer' + +// State helpers import * as shieldsPanelState from '../../../../brave_extension/extension/brave_extension/state/shieldsPanelState' +import * as noScriptState from '../../../../brave_extension/extension/brave_extension/state/noScriptState' + +// Utils import { initialState } from '../../../testData' import * as deepFreeze from 'deep-freeze-node' -import { ShieldDetails } from '../../../../brave_extension/extension/brave_extension/types/actions/shieldsPanelActions' import * as actions from '../../../../brave_extension/extension/brave_extension/actions/shieldsPanelActions' -import { State } from '../../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' describe('braveShieldsPanelReducer', () => { it('should handle initial state', () => { @@ -30,7 +40,7 @@ describe('braveShieldsPanelReducer', () => { const tabId = 1 beforeEach(() => { spy = jest.spyOn(shieldsPanelState, 'resetBlockingStats') - resetNoScriptInfoSpy = jest.spyOn(shieldsPanelState, 'resetNoScriptInfo') + resetNoScriptInfoSpy = jest.spyOn(noScriptState, 'resetNoScriptInfo') resetBlockingResourcesSpy = jest.spyOn(shieldsPanelState, 'resetBlockingResources') }) afterEach(() => { @@ -173,7 +183,9 @@ describe('braveShieldsPanelReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false }, changeInfo: {} }) @@ -193,7 +205,9 @@ describe('braveShieldsPanelReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false }, changeInfo: {} }) @@ -229,7 +243,9 @@ describe('braveShieldsPanelReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false } }) expect(updateActiveTabSpy).toBeCalledTimes(1) @@ -247,7 +263,9 @@ describe('braveShieldsPanelReducer', () => { pinned: false, highlighted: false, incognito: false, - selected: false + selected: false, + discarded: false, + autoDiscardable: false } }) expect(updateActiveTabSpy).not.toBeCalled() @@ -527,7 +545,7 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://test.brave.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://test.brave.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [], adsBlockedResources: [], @@ -572,7 +590,7 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://a.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://a.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [], adsBlockedResources: [], @@ -615,8 +633,8 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://a.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false }, - 'https://b.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://a.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false }, + 'https://b.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [], adsBlockedResources: [], @@ -662,8 +680,8 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://a.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false }, - 'https://b.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://a.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false }, + 'https://b.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [], adsBlockedResources: [], @@ -681,51 +699,6 @@ describe('braveShieldsPanelReducer', () => { }) }) - it('increments JS blocking consecutively without duplicates', () => { - const tabId = 2 - let nextState = shieldsPanelReducer(state, { - type: types.RESOURCE_BLOCKED, - details: { - blockType: 'javascript', - tabId: tabId, - subresource: 'https://a.com/index.js' - } - }) - expect(nextState.tabs[tabId].javascriptBlockedResources).toEqual( - [ 'https://a.com/index.js' ] - ) - - nextState = shieldsPanelReducer(nextState, { - type: types.RESOURCE_BLOCKED, - details: { - blockType: 'javascript', - tabId: tabId, - subresource: 'https://b.com/index.js' - } - }) - expect(nextState.tabs[tabId].javascriptBlockedResources).toEqual( - [ - 'https://a.com/index.js', - 'https://b.com/index.js' - ] - ) - - nextState = shieldsPanelReducer(nextState, { - type: types.RESOURCE_BLOCKED, - details: { - blockType: 'javascript', - tabId: tabId, - subresource: 'https://b.com/index.js' - } - }) - expect(nextState.tabs[tabId].javascriptBlockedResources).toEqual( - [ - 'https://a.com/index.js', - 'https://b.com/index.js' - ] - ) - }) - it('increments for fingerprinting blocked', () => { let nextState = shieldsPanelReducer(state, { type: types.RESOURCE_BLOCKED, @@ -1152,7 +1125,7 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://test.brave.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://test.brave.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [ 'https://test.brave.com' ], adsBlockedResources: [ 'https://test.brave.com' ], @@ -1194,7 +1167,7 @@ describe('braveShieldsPanelReducer', () => { fingerprinting: 'block', cookies: 'block', noScriptInfo: { - 'https://test.brave.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://test.brave.com/index.js': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, trackersBlockedResources: [ 'https://test.brave.com' ], adsBlockedResources: [ 'https://test.brave.com' ], @@ -1255,100 +1228,4 @@ describe('braveShieldsPanelReducer', () => { expect(setAllowScriptOriginsOnceSpy).toBeCalledWith([], tabId) }) }) - - describe('CHANGE_NO_SCRIPT_SETTINGS', () => { - let spy: jest.SpyInstance - beforeEach(() => { - spy = jest.spyOn(shieldsPanelState, 'changeNoScriptSettings') - }) - afterEach(() => { - spy.mockRestore() - }) - it('should call changeNoScriptSettings', () => { - const tabId = 2 - const stateWithNoScriptInfo: State = { - tabs: { - 2: { - origin, - hostname: 'brave.com', - adsBlocked: 0, - controlsOpen: true, - braveShields: 'allow', - trackersBlocked: 0, - httpsRedirected: 0, - javascriptBlocked: 0, - fingerprintingBlocked: 0, - id: 2, - httpUpgradableResources: 'block', - javascript: 'block', - trackers: 'block', - ads: 'block', - fingerprinting: 'block', - cookies: 'block', - url: 'https://brave.com', - noScriptInfo: { - 'https://brave.com': { - actuallyBlocked: false, - willBlock: true, - userInteracted: true - } - }, - adsBlockedResources: [], - trackersBlockedResources: [], - httpsRedirectedResources: [], - javascriptBlockedResources: [], - fingerprintingBlockedResources: [] - } - }, - windows: { - 1: 2 - }, - currentWindowId: 1 - } - let nextState = shieldsPanelReducer(stateWithNoScriptInfo, { - type: types.CHANGE_NO_SCRIPT_SETTINGS, - origin - }) - expect(nextState).toEqual({ - tabs: { - 2: { - origin, - hostname: 'brave.com', - adsBlocked: 0, - controlsOpen: true, - braveShields: 'allow', - trackersBlocked: 0, - httpsRedirected: 0, - javascriptBlocked: 0, - fingerprintingBlocked: 0, - id: 2, - httpUpgradableResources: 'block', - javascript: 'block', - trackers: 'block', - ads: 'block', - fingerprinting: 'block', - cookies: 'block', - url: 'https://brave.com', - noScriptInfo: { - 'https://brave.com': { - actuallyBlocked: true, - willBlock: true, - userInteracted: true - } - }, - adsBlockedResources: [], - trackersBlockedResources: [], - httpsRedirectedResources: [], - javascriptBlockedResources: [], - fingerprintingBlockedResources: [] - } - }, - windows: { - 1: 2 - }, - currentWindowId: 1 - }) - expect(spy).toBeCalledWith(stateWithNoScriptInfo, tabId, origin) - }) - }) }) diff --git a/components/test/brave_extension/components/controls/scriptsControl_test.tsx b/components/test/brave_extension/components/controls/scriptsControl_test.tsx index 396e03ece157..8f06ad1577ae 100644 --- a/components/test/brave_extension/components/controls/scriptsControl_test.tsx +++ b/components/test/brave_extension/components/controls/scriptsControl_test.tsx @@ -16,10 +16,12 @@ const fakeProps: Props = { javascript: 'allow', javascriptBlocked: 0, noScriptInfo: {}, - changeNoScriptSettings: (origin: string) => ({ type: actionTypes.CHANGE_NO_SCRIPT_SETTINGS, origin }), blockJavaScript: (setting: BlockJSOptions) => ({ type: actionTypes.JAVASCRIPT_TOGGLED, setting }), - changeAllNoScriptSettings: (shouldBlock: boolean) => ({ type: actionTypes.CHANGE_ALL_NO_SCRIPT_SETTINGS, shouldBlock }), - allowScriptOriginsOnce: () => ({ type: actionTypes.ALLOW_SCRIPT_ORIGINS_ONCE }) + allowScriptOriginsOnce: () => ({ type: actionTypes.ALLOW_SCRIPT_ORIGINS_ONCE }), + setScriptBlockedCurrentState: () => ({ type: actionTypes.SET_SCRIPT_BLOCKED_ONCE_CURRENT_STATE, url: '' }), + setGroupedScriptsBlockedCurrentState: () => ({ type: actionTypes.SET_GROUPED_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, origin: '', maybeBlock: true }), + setAllScriptsBlockedCurrentState: () => ({ type: actionTypes.SET_ALL_SCRIPTS_BLOCKED_ONCE_CURRENT_STATE, maybeBlock: true }), + setFinalScriptsBlockedState: () => ({ type: actionTypes.SET_FINAL_SCRIPTS_BLOCKED_ONCE_STATE }) } describe('ScriptsControl component', () => { diff --git a/components/test/brave_extension/state/shieldsPanelState_test.ts b/components/test/brave_extension/state/shieldsPanelState_test.ts index 189a84c11a33..6b9b3914cf12 100644 --- a/components/test/brave_extension/state/shieldsPanelState_test.ts +++ b/components/test/brave_extension/state/shieldsPanelState_test.ts @@ -2,10 +2,11 @@ * 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 { State } from '../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' import * as deepFreeze from 'deep-freeze-node' import * as shieldsPanelState from '../../../brave_extension/extension/brave_extension/state/shieldsPanelState' +import * as noScriptState from '../../../brave_extension/extension/brave_extension/state/noScriptState' import * as shieldsAPI from '../../../brave_extension/extension/brave_extension/background/api/shieldsAPI' -import { State } from '../../../brave_extension/extension/brave_extension/types/state/shieldsPannelState' const state: State = deepFreeze({ currentWindowId: 1, @@ -715,7 +716,7 @@ describe('shieldsPanelState test', () => { javascriptBlocked: 1, fingerprintingBlocked: 0, noScriptInfo: { - 'https://test.brave.com/': { actuallyBlocked: true, willBlock: true, userInteracted: false } + 'https://test.brave.com': { actuallyBlocked: true, willBlock: true, userInteracted: false } }, adsBlockedResources: [], fingerprintingBlockedResources: [], @@ -805,7 +806,7 @@ describe('shieldsPanelState test', () => { } it('reset noScriptInfo for a specific tab without navigating away', () => { this.tabId = 2 - expect(shieldsPanelState.resetNoScriptInfo( + expect(noScriptState.resetNoScriptInfo( stateWithAllowedScriptOrigins, this.tabId, 'https://brave.com')).toEqual({ currentWindowId: 1, tabs: { @@ -828,8 +829,16 @@ describe('shieldsPanelState test', () => { fingerprintingBlocked: 0, url: 'https://brave.com', noScriptInfo: { - 'https://a.com': { actuallyBlocked: true, willBlock: true, userInteracted: false }, - 'https://b.com': { actuallyBlocked: true, willBlock: false, userInteracted: false } + 'https://a.com': { + actuallyBlocked: true, + userInteracted: false, + willBlock: true + }, + 'https://b.com': { + actuallyBlocked: true, + userInteracted: false, + willBlock: false + } }, adsBlockedResources: [], trackersBlockedResources: [], @@ -874,7 +883,7 @@ describe('shieldsPanelState test', () => { }) it('reset noScriptInfo for a specific tab with navigating away', () => { this.tabId = 2 - expect(shieldsPanelState.resetNoScriptInfo( + expect(noScriptState.resetNoScriptInfo( stateWithAllowedScriptOrigins, this.tabId, 'https://test.brave.com')).toEqual({ currentWindowId: 1, tabs: {