From c7069a4b9a060e074401db67650ea5fe4d7c24e1 Mon Sep 17 00:00:00 2001 From: Sergey P Date: Mon, 20 Feb 2023 15:57:40 +0530 Subject: [PATCH] Allow individual scripts once --- .../shields_panel_data_handler.cc | 4 +- .../common/brave_shield_constants.h | 4 +- .../common/brave_shields_panel.mojom | 3 +- .../advanced-controls-content/index.tsx | 2 +- .../panel/components/tree-list/index.tsx | 64 ++++++++++++---- .../panel/components/tree-list/style.ts | 3 + .../panel/components/tree-list/tree-node.tsx | 75 ++++++++++++++++-- .../resources/panel/container.tsx | 76 +++++++++++++++---- .../resources/panel/state/component_types.ts | 31 ++++++++ .../panel/stories/component-panel.tsx | 64 ++++++++++------ .../resources/panel/stories/locale.ts | 4 +- .../resources/brave_shields_strings.grdp | 12 +++ 12 files changed, 278 insertions(+), 64 deletions(-) diff --git a/browser/ui/webui/brave_shields/shields_panel_data_handler.cc b/browser/ui/webui/brave_shields/shields_panel_data_handler.cc index 4a2e0313735d..539ad93d9b78 100644 --- a/browser/ui/webui/brave_shields/shields_panel_data_handler.cc +++ b/browser/ui/webui/brave_shields/shields_panel_data_handler.cc @@ -169,8 +169,10 @@ void ShieldsPanelDataHandler::UpdateSiteBlockInfo() { active_shields_data_controller_->GetTotalBlockedCount(); site_block_info_.ads_list = active_shields_data_controller_->GetBlockedAdsList(); - site_block_info_.js_list = + site_block_info_.blocked_js_list = active_shields_data_controller_->GetBlockedJsList(); + site_block_info_.allowed_js_list = + active_shields_data_controller_->GetAllowedJsList(); site_block_info_.fingerprints_list = active_shields_data_controller_->GetFingerprintsList(); site_block_info_.http_redirects_list = diff --git a/components/brave_shields/common/brave_shield_constants.h b/components/brave_shields/common/brave_shield_constants.h index 338c22700209..05cf4e50fc5a 100644 --- a/components/brave_shields/common/brave_shield_constants.h +++ b/components/brave_shields/common/brave_shield_constants.h @@ -96,7 +96,9 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveShieldsBlockedScriptsLabel", IDS_BRAVE_SHIELDS_BLOCKED_SCRIPTS_LABEL}, {"braveShieldsManaged", IDS_BRAVE_SHIELDS_MANAGED}, -}; + {"braveShieldsAllowScriptOnce", IDS_BRAVE_SHIELDS_ALLOW_SCRIPT_ONCE}, + {"braveShieldsScriptAllowedOnce", IDS_BRAVE_SHIELDS_SCRIPT_ALLOWED_ONCE}, + {"braveShieldsAllowScriptsAll", IDS_BRAVE_SHIELDS_ALLOW_SCRIPTS_ALL}}; const char kCookieListEnabledHistogram[] = "Brave.Shields.CookieListEnabled"; const char kCookieListPromptHistogram[] = "Brave.Shields.CookieListPrompt"; diff --git a/components/brave_shields/common/brave_shields_panel.mojom b/components/brave_shields/common/brave_shields_panel.mojom index b32a95e13ed5..71935c6892eb 100644 --- a/components/brave_shields/common/brave_shields_panel.mojom +++ b/components/brave_shields/common/brave_shields_panel.mojom @@ -57,7 +57,8 @@ struct SiteBlockInfo { url.mojom.Url favicon_url; array ads_list; array http_redirects_list; - array js_list; + array blocked_js_list; + array allowed_js_list; array fingerprints_list; }; diff --git a/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx b/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx index c7bf6acad63d..5c23d59aa09e 100644 --- a/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx +++ b/components/brave_shields/resources/panel/components/advanced-controls-content/index.tsx @@ -104,7 +104,7 @@ function AdvancedControlsContent () { const adsListCount = siteBlockInfo?.adsList.length ?? 0 const httpRedirectsListCount = siteBlockInfo?.httpRedirectsList.length ?? 0 - const jsListCount = siteBlockInfo?.jsList.length ?? 0 + const jsListCount = siteBlockInfo?.blockedJsList.length ?? 0 const isHttpsByDefaultEnabled = loadTimeData.getBoolean('isHttpsByDefaultEnabled') const isTorProfile = loadTimeData.getBoolean('isTorProfile') diff --git a/components/brave_shields/resources/panel/components/tree-list/index.tsx b/components/brave_shields/resources/panel/components/tree-list/index.tsx index e08a7d3ed99f..965b96d83d7a 100644 --- a/components/brave_shields/resources/panel/components/tree-list/index.tsx +++ b/components/brave_shields/resources/panel/components/tree-list/index.tsx @@ -8,32 +8,38 @@ import * as S from './style' import DataContext from '../../state/context' import { getLocale } from '../../../../../common/locale' import TreeNode from './tree-node' -import { ViewType } from '../../state/component_types' -import { Url } from 'gen/url/mojom/url.mojom.m.js' +import { + ViewType, + ResourceInfo, + ResourceType, + ResourceState +} from '../../state/component_types' import Button from '$web-components/button' +import getPanelBrowserAPI from '../../api/panel_browser_api' interface Props { - data: Url[] - totalBlockedCount: number - blockedCountTitle: string + resourcesList: ResourceInfo[] + type: ResourceType + totalCount: number + totalTitle: string } -function groupByOrigin (data: Url[]) { - const map: Map = new Map() +function groupByOrigin (data: ResourceInfo[]) { + const map: Map = new Map() const includesDupeOrigin = (searchOrigin: string) => { - const results = data.map(entry => new URL(entry.url).origin) + const results = data.map(entry => new URL(entry.url.url).origin) .filter(entry => entry.includes(searchOrigin)) return results.length > 1 } data.forEach(entry => { - const url = new URL(entry.url) + const url = new URL(entry.url.url) const origin = url.origin const items = map.get(origin) if (items) { - items.push(url.pathname + url.search) + items.push(entry) return // continue } @@ -44,10 +50,31 @@ function groupByOrigin (data: Url[]) { return map } +function getScriptsOriginsWithState (data: ResourceInfo[], + state: ResourceState): string[] { + const list: string[] = [] + + data.forEach(entry => { + const url = new URL(entry.url.url) + if (list.includes(url.origin) || entry.state !== state) { + return // continue + } + + list.push(url.origin) + }) + + return list +} + function TreeList (props: Props) { const { siteBlockInfo, setViewType } = React.useContext(DataContext) - const mappedData = React.useMemo(() => groupByOrigin(props.data), [props.data]) - + const mappedBlockedScripts = React.useMemo(() => + groupByOrigin(props.resourcesList), [props.resourcesList]) + const allowAllScripts = () => { + const origins: string[] = + getScriptsOriginsWithState(props.resourcesList, ResourceState.Blocked) + getPanelBrowserAPI().dataHandler.allowScriptsOnce(origins) + } return ( @@ -58,17 +85,22 @@ function TreeList (props: Props) { {siteBlockInfo?.host} - {props.totalBlockedCount} - {props.blockedCountTitle} + {props.totalCount} + {props.totalTitle} + { allowAllScripts()}> + {getLocale('braveShieldsAllowScriptsAll')} + + }
- {[...mappedData.keys()].map((origin, idx) => { + {[...mappedBlockedScripts.keys()].map((origin, idx) => { return () })}
diff --git a/components/brave_shields/resources/panel/components/tree-list/style.ts b/components/brave_shields/resources/panel/components/tree-list/style.ts index 255883acc57d..574781a35451 100644 --- a/components/brave_shields/resources/panel/components/tree-list/style.ts +++ b/components/brave_shields/resources/panel/components/tree-list/style.ts @@ -14,6 +14,9 @@ export const Box = styled.div` right: 0; left: 0; z-index: 2; + + display: flex; + flex-direction: column; ` export const HeaderBox = styled.section` diff --git a/components/brave_shields/resources/panel/components/tree-list/tree-node.tsx b/components/brave_shields/resources/panel/components/tree-list/tree-node.tsx index 1e7646071068..769c3f7cb013 100644 --- a/components/brave_shields/resources/panel/components/tree-list/tree-node.tsx +++ b/components/brave_shields/resources/panel/components/tree-list/tree-node.tsx @@ -5,6 +5,13 @@ import * as React from 'react' import styled from 'styled-components' import UrlElement from './url-element' +import { + ResourceInfo, + ResourceState, + ResourceType +} from '../../state/component_types' +import getPanelBrowserAPI from '../../api/panel_browser_api' +import { getLocale } from '../../../../../common/locale' const Tree = styled.div` display: grid; @@ -62,9 +69,15 @@ const TreeContents = styled.div` overflow: hidden; /* to wrap contents */ ` +const AllowedOnceText = styled.div` + overflow: hidden; /* to wrap contents */ + color: gray +` + interface TreeNodeProps { host: string - resourceList: string[] + type: ResourceType + resourceList: ResourceInfo[] } function rectToQuad (rect: DOMRect) { @@ -87,6 +100,21 @@ function getRelativeBoundingRect (from: DOMRect, to: Element) { ) } +const getResourcesState = (resourceList: ResourceInfo[]) + : ResourceState | undefined => { + if (!resourceList.length) { return undefined } + let state: ResourceState | undefined = resourceList[0].state + resourceList.forEach(entry => { + if (state !== entry.state) { + state = undefined + return undefined + } + return entry.state + }) + + return state +} + function TreeNode (props: TreeNodeProps) { const treeChildrenBoxRef = React.useRef() as React.MutableRefObject const svgBoxRef = React.useRef() as React.MutableRefObject @@ -184,6 +212,11 @@ function TreeNode (props: TreeNodeProps) { measure() }, [props.resourceList, isExpanded]) + const handleActionClick = () => { + getPanelBrowserAPI().dataHandler.allowScriptsOnce([props.host]) + } + const isAllowedOnce = + getResourcesState(props.resourceList) === ResourceState.AllowedOnce return ( @@ -191,21 +224,47 @@ function TreeNode (props: TreeNodeProps) { {hasResources && isExpanded ? renderLeftAxis() : null} + + + {isAllowedOnce && ( + + {getLocale('braveShieldsScriptAllowedOnce')} + + )} + + {hasResources && isExpanded ? (
{ - props.resourceList.map((resourceUrl: string, idx) => { + props.resourceList.map((resource: ResourceInfo, idx) => { return ( - measure()} - /> + + measure()} + /> + + {props.type === ResourceType.Script && + resource.state === ResourceState.Blocked && ( + handleActionClick()}> + {getLocale('braveShieldsAllowScriptOnce')} + + )} + {props.type === ResourceType.Script && + resource.state === ResourceState.AllowedOnce && ( + + {getLocale('braveShieldsScriptAllowedOnce')} + + )} + + ) }) } +
) : null}
diff --git a/components/brave_shields/resources/panel/container.tsx b/components/brave_shields/resources/panel/container.tsx index 7fc2e96a50a9..8c904879c3d5 100644 --- a/components/brave_shields/resources/panel/container.tsx +++ b/components/brave_shields/resources/panel/container.tsx @@ -6,42 +6,92 @@ import * as React from 'react' import MainPanel from './components/main-panel' import TreeList from './components/tree-list' -import { ViewType } from './state/component_types' +import { + MakeResourceInfoList, + ResourceInfo, + ResourceState, + ResourceType, + ViewType +} from './state/component_types' import DataContext from './state/context' import styled from 'styled-components' import { getLocale } from '../../../common/locale' +import { SiteBlockInfo } from './api/panel_browser_api' const Box = styled.div` position: relative; ` +function GetResourceListForView (view: ViewType, + state: ResourceState, + siteBlockInfo: SiteBlockInfo): ResourceInfo[] { + if (view === ViewType.AdsList) { + return MakeResourceInfoList(siteBlockInfo.adsList, ResourceType.Ad, state) + } + if (view === ViewType.HttpsList) { + return MakeResourceInfoList(siteBlockInfo.httpRedirectsList, + ResourceType.Http, + state) + } + if (view === ViewType.ScriptsList && state === ResourceState.Blocked) { + return MakeResourceInfoList(siteBlockInfo.blockedJsList, + ResourceType.Http, + state) + } + if (view === ViewType.ScriptsList && state === ResourceState.AllowedOnce) { + return MakeResourceInfoList(siteBlockInfo.allowedJsList, + ResourceType.Http, + state) + } + + return [] +} + function Container () { const { siteBlockInfo, viewType } = React.useContext(DataContext) const detailView = viewType !== ViewType.Main && siteBlockInfo - + const resourceBlockedList = siteBlockInfo ? + GetResourceListForView(viewType, ResourceState.Blocked, siteBlockInfo) : [] + const resourceAllowedList = siteBlockInfo ? + GetResourceListForView(viewType, + ResourceState.AllowedOnce, + siteBlockInfo) : [] const renderDetailView = () => { if (viewType === ViewType.AdsList && detailView) { return () } if (viewType === ViewType.HttpsList && detailView) { return () } if (viewType === ViewType.ScriptsList && detailView) { - return () + return ( + + + + ) + } return null diff --git a/components/brave_shields/resources/panel/state/component_types.ts b/components/brave_shields/resources/panel/state/component_types.ts index 6d158a9015f1..8eda29289fc8 100644 --- a/components/brave_shields/resources/panel/state/component_types.ts +++ b/components/brave_shields/resources/panel/state/component_types.ts @@ -2,9 +2,40 @@ // 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 https://mozilla.org/MPL/2.0/. + +import { Url } from 'gen/url/mojom/url.mojom.m.js' + export enum ViewType { ScriptsList, AdsList, HttpsList, Main } + +export enum ResourceType { + Script, + Ad, + Http +} + +export enum ResourceState { + Blocked, + AllowedOnce +} + +export type ResourceInfo = { + url: Url + state: ResourceState + type: ResourceType +} + +export function MakeResourceInfoList (data: Url[], + type: ResourceType, + state: ResourceState): ResourceInfo[] { + const list: ResourceInfo[] = [] + data.forEach(entry => { + list.push({ url: entry, type: type, state: state }) + }) + + return list +} diff --git a/components/brave_shields/resources/panel/stories/component-panel.tsx b/components/brave_shields/resources/panel/stories/component-panel.tsx index a6e144613c4f..1a8aac94b7c0 100644 --- a/components/brave_shields/resources/panel/stories/component-panel.tsx +++ b/components/brave_shields/resources/panel/stories/component-panel.tsx @@ -14,29 +14,34 @@ import shieldsLightTheme from '../theme/shields-light' import ThemeProvider from '../../../../common/BraveCoreThemeProvider' import DataContext from '../state/context' import { AdBlockMode, FingerprintMode, CookieBlockMode, HttpsUpgradeMode } from '../api/panel_browser_api' -import { ViewType } from '../state/component_types' +import { + ViewType, + ResourceType, + ResourceState, + MakeResourceInfoList +} from '../state/component_types' import { getLocale } from '../../../../common/locale' const LIST_JS = [ - { 'url': 'https://www.reddit.com/' }, - { 'url': 'https://www.redditstatic.com/desktop2x/AuthorHovercard~Reddit.ca2d5405cdd178092347.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Chat~Governance~Reddit.f64af7713c3261596c7c.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/CollectionCommentsPage~CommentsPage~ModerationPages~PostCreation~ProfileComments~ProfileOverview~Pro~d39c0d57.a37d209138f811a78126.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/CollectionCommentsPage~CommentsPage~ModerationPages~ProfileComments~ProfileOverview~ProfilePrivate~R~969c2956.44543839280e344fe35c.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Frontpage.25b06963dc7114d6d5e5.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Governance~ModListing~Reddit~ReportFlow~Subreddit.dcc2d2bd4fa3a45348eb.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Governance~ModListing~Reddit~Subreddit.3f168f79abf6d859d163.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Governance~Reddit.e7882cd59d01f560d9ed.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/Governance~Reddit~Subreddit~reddit-components-BlankPost~reddit-components-ClassicPost~reddit-compone~3b56c92e.14312dc2a044d45449c9.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/ModListing~PostCreation~Reddit~StandalonePostPage~Subreddit.b725e7ecf5d621c30c53.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/PostCreation~Reddit~StandalonePostPage~SubredditTopContent~TopWeekPostsDiscoveryUnit~reddit-componen~2583c786.1cc84f65074a696c0941.js' }, - { 'url': 'https://www.redditstatic.com/desktop2x/PostCreation~Reddit~StandalonePostPage~Subreddit~reddit-components-ClassicPost~reddit-components-Com~82e48dd3.85bb0a8c6be58f085676.js' }, - { 'url': 'https://api.github.com/_private/browser/stats' } + { url: 'https://www.reddit.com/', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/AuthorHovercard~Reddit.ca2d5405cdd178092347.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Chat~Governance~Reddit.f64af7713c3261596c7c.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/CollectionCommentsPage~CommentsPage~ModerationPages~PostCreation~ProfileComments~ProfileOverview~Pro~d39c0d57.a37d209138f811a78126.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/CollectionCommentsPage~CommentsPage~ModerationPages~ProfileComments~ProfileOverview~ProfilePrivate~R~969c2956.44543839280e344fe35c.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Frontpage.25b06963dc7114d6d5e5.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Governance~ModListing~Reddit~ReportFlow~Subreddit.dcc2d2bd4fa3a45348eb.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Governance~ModListing~Reddit~Subreddit.3f168f79abf6d859d163.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Governance~Reddit.e7882cd59d01f560d9ed.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/Governance~Reddit~Subreddit~reddit-components-BlankPost~reddit-components-ClassicPost~reddit-compone~3b56c92e.14312dc2a044d45449c9.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/ModListing~PostCreation~Reddit~StandalonePostPage~Subreddit.b725e7ecf5d621c30c53.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/PostCreation~Reddit~StandalonePostPage~SubredditTopContent~TopWeekPostsDiscoveryUnit~reddit-componen~2583c786.1cc84f65074a696c0941.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://www.redditstatic.com/desktop2x/PostCreation~Reddit~StandalonePostPage~Subreddit~reddit-components-ClassicPost~reddit-components-Com~82e48dd3.85bb0a8c6be58f085676.js', state: ResourceState.Blocked, type: ResourceType.Script }, + { url: 'https://api.github.com/_private/browser/stats', state: ResourceState.Blocked, type: ResourceType.Script } ] const LIST_ADS = [ - { url: 'ads.brave.com' }, - { url: 'ads2.brave.com' } + { url: 'ads.brave.com', state: ResourceState.Blocked, type: ResourceType.Ad }, + { url: 'ads2.brave.com', state: ResourceState.Blocked, type: ResourceType.Ad } ] export default { @@ -57,7 +62,8 @@ export default { isBraveShieldsEnabled: boolean('Enable Shields', true), isBraveShieldsManaged: boolean('Shields Managed', false), adsList: LIST_ADS, - jsList: LIST_JS, + blockedJsList: LIST_JS, + allowedJsList: LIST_JS, httpRedirectsList: [], fingerprintsList: [], faviconUrl: { url: 'https://brave.com/static-assets/images/brave-favicon.png' } @@ -102,13 +108,27 @@ export const _ResourceList = () => { if (!siteBlockInfo) { return } - + const blockedList = siteBlockInfo ? + MakeResourceInfoList(siteBlockInfo?.blockedJsList, + ResourceType.Script, + ResourceState.Blocked) : [] + const allowedList = siteBlockInfo ? + MakeResourceInfoList(siteBlockInfo?.allowedJsList, + ResourceType.Script, + ResourceState.AllowedOnce) : [] return ( + ) diff --git a/components/brave_shields/resources/panel/stories/locale.ts b/components/brave_shields/resources/panel/stories/locale.ts index fc8cdafcd142..0aec22274a8c 100644 --- a/components/brave_shields/resources/panel/stories/locale.ts +++ b/components/brave_shields/resources/panel/stories/locale.ts @@ -40,5 +40,7 @@ provideStrings({ braveShieldsReportSiteDesc: 'Tell us if the site wasn\'t working properly with Shields up.', braveShieldsDownDesc: 'You are viewing this site without Brave\'s privacy protection', braveShieldsBlockedScriptsLabel: 'Blocked scripts', - braveShieldsManaged: 'Settings managed by organization' + braveShieldsManaged: 'Settings managed by organization', + braveShieldsAllowScriptOnce: 'Allow', + braveShieldsScriptAllowedOnce: 'Allowed once' }) diff --git a/components/resources/brave_shields_strings.grdp b/components/resources/brave_shields_strings.grdp index 6319b2dcb1dd..48a6cfa7c67e 100644 --- a/components/resources/brave_shields_strings.grdp +++ b/components/resources/brave_shields_strings.grdp @@ -53,6 +53,18 @@ Settings managed by organization + + Allow + + + + Allowed once + + + + Allow all + + Filter lists