From aa4ae95d8bd3b5a41eeb3e2fb7ed2c8902b1e298 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 30 Jul 2025 12:18:01 +0530 Subject: [PATCH 01/98] Update minification logic in script configuration --- vite.extension.config.mts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/vite.extension.config.mts b/vite.extension.config.mts index cd9d424d21..f48fa119e2 100644 --- a/vite.extension.config.mts +++ b/vite.extension.config.mts @@ -65,12 +65,24 @@ const scripts: Script[] = [ ] as const; const createScriptConfig = (script: (typeof scripts)[number]) => { + let minifier: boolean | 'terser' = 'terser'; + if (script.name === 'prebid-interface') { + minifier = false; + } else { + if (isDev) { + minifier = false; + } else { + minifier = 'terser'; + } + } + return defineConfig({ build: { watch: isDev ? {} : null, emptyOutDir: false, + target: 'esnext', outDir: `../../dist/extension`, - minify: !isDev, + minify: minifier, rollupOptions: { input: { [script.name]: script.path, From 3bcec1fec2ba7a4be853773f5dbe3789ff489042 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 2 Sep 2025 19:44:39 +0530 Subject: [PATCH 02/98] Refactor: Remove Script Blocking feature and update related components --- packages/common/src/prtToken.types.ts | 1 + packages/extension/src/serviceWorker/index.ts | 4 + .../devtools/pages/privacyProtection/index.ts | 1 - .../probabilisticRevealTokens/index.tsx | 46 ++- .../scriptBlocking/index.tsx | 66 ----- .../scriptBlocking/mdlTable/index.tsx | 274 ------------------ .../scriptBlocking/mdlTable/legend.tsx | 42 --- .../mdlTable/rowContextMenu.tsx | 126 -------- .../scriptBlocking/panel.tsx | 47 --- .../stateProviders/scriptBlocking/context.ts | 10 + .../scriptBlocking/scriptBlocking.tsx | 50 +++- packages/extension/src/view/devtools/tabs.ts | 22 -- 12 files changed, 105 insertions(+), 584 deletions(-) delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel.tsx diff --git a/packages/common/src/prtToken.types.ts b/packages/common/src/prtToken.types.ts index aa94d69229..68b103ac92 100644 --- a/packages/common/src/prtToken.types.ts +++ b/packages/common/src/prtToken.types.ts @@ -40,6 +40,7 @@ export interface PRTMetadata { humanReadableSignal: string; decryptionKeyAvailable: boolean; prtHeader: string; + nonZeroUintsignal: boolean; } export type UniquePlainTextToken = PlaintTextToken & { diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 727cf2f671..ccf7fb476d 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -361,6 +361,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { 'Sec-Probabilistic-Reveal-Token', headers ); + const origin = extractHeader('origin', headers) ?? isValidURL(createURL(headers)) ? new URL(createURL(headers)).origin @@ -413,6 +414,9 @@ chrome.debugger.onEvent.addListener((source, method, params) => { humanReadableSignal: plainTextToken?.humanReadableSignal ?? '', origin: isValidURL(origin) ? origin : '', decryptionKeyAvailable: Boolean(decodedToken), + nonZeroUintsignal: !plainTextToken?.uint8Signal.every( + (bit) => bit === 0 + ), }; DataStore.tabs[tabId].newUpdatesPRT++; } diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/index.ts b/packages/extension/src/view/devtools/pages/privacyProtection/index.ts index 3b47230dc3..d120e0f06b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/index.ts +++ b/packages/extension/src/view/devtools/pages/privacyProtection/index.ts @@ -16,6 +16,5 @@ export { default as BounceTracking } from './bounceTracking'; export { default as UserAgentReduction } from './userAgentReduction'; export { default as IPProtection } from './ipProtection'; -export { default as ScriptBlocking } from './scriptBlocking'; export { default as PrivateStateTokens } from './privateStateTokens'; export { default as PrivacyProtection } from './privacyProtection'; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index a6a172dd2f..8711a22ec8 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -27,12 +27,15 @@ import { noop, } from '@google-psat/design-system'; import React, { useMemo, useRef, useState } from 'react'; -import type { PRTMetadata } from '@google-psat/common'; +import { isValidURL, type PRTMetadata } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ -import { useProbabilisticRevealTokens } from '../../../../stateProviders'; +import { + useProbabilisticRevealTokens, + useScriptBlocking, +} from '../../../../stateProviders'; import RowContextMenuForPRT from './rowContextMenu'; const ProbabilisticRevealTokens = () => { @@ -50,6 +53,10 @@ const ProbabilisticRevealTokens = () => { plainTextTokensData: state.plainTextTokens, })); + const { scriptBlockingData } = useScriptBlocking(({ state }) => ({ + scriptBlockingData: state.scriptBlockingData, + })); + const rowContextMenuRef = useRef | null>(null); @@ -68,12 +75,41 @@ const ProbabilisticRevealTokens = () => { cell: (info) => info, }, { - header: 'Decryption key available', + header: 'Decrypted', accessorKey: 'decryptionKeyAvailable', - cell: (info) => info.toString(), + cell: (info, _) => { + return info ? : ''; + }, + }, + { + header: 'Owner', + accessorKey: '0', + cell: (_, details) => { + const origin: string = isValidURL(details?.origin) + ? new URL(details?.origin).host.slice(4) + : ''; + return scriptBlockingData[origin]?.owner; + }, + }, + { + header: 'Signal', + accessorKey: 'nonZeroUintsignal', + cell: (info, _) => { + return info ? : ''; + }, + }, + { + header: 'Blocking Scope', + accessorKey: '', + cell: (_, details) => { + const origin: string = isValidURL(details?.origin) + ? new URL(details?.origin).host.slice(4) + : ''; + return scriptBlockingData[origin]?.scriptBlocking; + }, }, ], - [] + [scriptBlockingData] ); const formedJson = useMemo(() => { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx deleted file mode 100644 index 61641a4152..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React, { useMemo } from 'react'; -import { - InfoCard, - PSInfoKey, - TabsProvider, - type TabItems, -} from '@google-psat/design-system'; -import Panel from './panel'; -import MDLTable from './mdlTable'; - -const ScriptBlocking = () => { - const tabItems = useMemo( - () => ({ - Learning: [ - { - title: 'Overview', - content: { - Element: InfoCard, - props: { - infoKey: PSInfoKey.ScriptBlocking, - showQuickLinks: true, - isLandingPageContainer: true, - }, - className: 'p-4', - }, - }, - { - title: 'Blocked Domain List', - content: { - Element: MDLTable, - props: {}, - className: 'overflow-auto h-full', - containerClassName: 'h-full', - }, - }, - ], - }), - [] - ); - - return ( - - - - ); -}; - -export default ScriptBlocking; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx deleted file mode 100644 index 3716b9d1c3..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * External dependencies - */ -import { - noop, - ProgressBar, - Table, - TableProvider, - type TableFilter, - type TableColumn, - type TableRow, - Link, - ResizableTray, -} from '@google-psat/design-system'; -import React, { - useEffect, - useMemo, - useRef, - useState, - useCallback, -} from 'react'; -import type { MDLTableData } from '@google-psat/common'; -/** - * Internal dependencies - */ -import Legend from './legend'; -import { useScriptBlocking } from '../../../../stateProviders'; -import RowContextMenuForScriptBlocking from './rowContextMenu'; - -export const IMPACTED_BY_SCRIPT_BLOCKING = { - NONE: 'Not Impacted By Script Blocking', - PARTIAL: 'Some URLs are Blocked', - ENTIRE: 'Entire Domain Blocked', -}; - -const DATA_URL = - 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md'; - -const MDLTable = () => { - const [selectedKey, setSelectedKey] = useState(null); - const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); - const [isLoading, setIsLoading] = useState(true); - const { uniqueResponseDomains } = useScriptBlocking(({ state }) => ({ - uniqueResponseDomains: state.uniqueResponseDomains, - })); - - const rowContextMenuRef = useRef | null>(null); - - const [initialTableData, setinitialTableData] = useState< - { domain: string; owner: string; scriptBlocking: string }[] - >([]); - - useEffect(() => { - (async () => { - setIsLoading(true); - const response = await fetch(DATA_URL); - - if (!response.ok) { - setIsLoading(false); - throw new Error(`HTTP error! status: ${response.status}`); - } - - const text = await response.text(); - - const lines = text - .split('\n') - .filter((line) => line.includes('|')) - .slice(2); - - const mdlData = lines - .map((line) => line.split('|').map((item) => item.trim())) - .filter((item) => item[2] !== IMPACTED_BY_SCRIPT_BLOCKING.NONE); - - setinitialTableData(() => { - const data = mdlData.map((item: string[]) => { - let owner = item[1]; - - if (item[1].includes('PSL Domain')) { - owner = 'PSL Domain'; - } - - const scriptBlocking = item[2]; - - return { - domain: item[0], - owner, - scriptBlocking, - }; - }); - - setIsLoading(false); - - return data; - }); - })(); - }, []); - - const checkbox = useCallback( - () => ( - - ), - [] - ); - - const tableData: MDLTableData[] = useMemo(() => { - if (initialTableData.length === 0) { - return []; - } - - const data: MDLTableData[] = []; - - initialTableData.forEach((item) => { - let available = false; - if (uniqueResponseDomains.includes(item.domain)) { - available = true; - } - - const canPush = showOnlyHighlighted ? available : true; - - if (canPush) { - data.push({ - ...item, - highlighted: available, - highlightedClass: - available && item.scriptBlocking.startsWith('Some URLs are Blocked') - ? 'bg-amber-100' - : '', - } as MDLTableData); - } - }); - - return data.sort((a, b) => Number(b.highlighted) - Number(a.highlighted)); - }, [uniqueResponseDomains, initialTableData, showOnlyHighlighted]); - - const tableColumns = useMemo( - () => [ - { - header: 'Domain', - accessorKey: 'domain', - cell: (info) => info, - initialWidth: 100, - }, - { - header: 'Owner', - accessorKey: 'owner', - cell: (info) => { - if (info === 'PSL Domain') { - return ( - - PSL Domain - - ); - } - - return info; - }, - initialWidth: 100, - }, - { - header: 'Impacted by Script Blocking', - accessorKey: 'scriptBlocking', - cell: (info) => info, - }, - ], - [] - ); - - const filters = useMemo( - () => ({ - owner: { - title: 'Owner', - }, - scriptBlocking: { - title: 'Impacted by Script Blocking', - hasStaticFilterValues: true, - filterValues: { - [IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL]: { - selected: false, - description: IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL, - }, - [IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE]: { - selected: false, - description: IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE, - }, - }, - }, - }), - [] - ); - - if (isLoading) { - return ( -
- -
- ); - } - - return ( -
- { - setSelectedKey((rowData as MDLTableData)?.domain || null); - }} - onRowContextMenu={ - rowContextMenuRef.current - ? rowContextMenuRef.current?.onRowContextMenu - : noop - } - getRowObjectKey={(row: TableRow) => - (row.originalData as MDLTableData).domain || '' - } - tablePersistentSettingsKey="mdlTable" - > - - - - - - - - ); -}; - -export default MDLTable; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx deleted file mode 100644 index dce9fa1a43..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * External dependencies. - */ -import { Link } from '@google-psat/design-system'; -import React from 'react'; - -const Legend = () => { - return ( -
-

- The Blocked Domain List is a subset of the{' '} - - Masked Domain List (MDL) - {' '} - under Google’s{' '} - - IP Protection - {' '} - initiative. Domains on this list are either entirely or patially - blocked. -

-
- ); -}; - -export default Legend; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx deleted file mode 100644 index 155c71f889..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies - */ -import React, { - forwardRef, - useCallback, - useImperativeHandle, - useState, -} from 'react'; -import { createPortal } from 'react-dom'; -import { type MDLTableData } from '@google-psat/common'; -import type { TableRow } from '@google-psat/design-system'; -import { I18n } from '@google-psat/i18n'; - -const RowContextMenuForScriptBlocking = forwardRef<{ - onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; -}>(function RowContextMenu(_, ref) { - const [contextMenuOpen, setContextMenuOpen] = useState(false); - const [columnPosition, setColumnPosition] = useState({ - x: 0, - y: 0, - }); - const [rowData, setRowData] = useState(); - - const handleRightClick = useCallback( - (e: React.MouseEvent, { originalData }: TableRow) => { - e.preventDefault(); - const x = e.clientX, - y = e.clientY; - setColumnPosition({ x, y }); - document.body.style.overflow = contextMenuOpen ? 'auto' : 'hidden'; - setContextMenuOpen(!contextMenuOpen); - setRowData(originalData as MDLTableData); - }, - [contextMenuOpen] - ); - - useImperativeHandle(ref, () => ({ - onRowContextMenu(e, row) { - handleRightClick(e, row); - }, - })); - - const handleFilterClick = useCallback(() => { - const filter = `domain:${rowData?.domain}`; - - // @ts-ignore - if (chrome.devtools.panels?.network?.show && rowData?.domain) { - // @ts-ignore - chrome.devtools.panels.network.show({ filter }); - setContextMenuOpen(false); - return; - } - - try { - // Need to do this since chrome doesnt allow the clipboard access in extension. - const copyFrom = document.createElement('textarea'); - copyFrom.textContent = filter; - document.body.appendChild(copyFrom); - copyFrom.select(); - document.execCommand('copy'); - copyFrom.blur(); - document.body.removeChild(copyFrom); - setContextMenuOpen(false); - } catch (error) { - //Fail silently - } - }, [rowData]); - - return ( - <> - {rowData?.domain && - rowData?.highlighted && - contextMenuOpen && - createPortal( -
-
- -
-
setContextMenuOpen(false)} - className="absolute w-screen h-screen z-10 top-0 left-0" - /> -
, - document.body - )} - - ); -}); - -export default RowContextMenuForScriptBlocking; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel.tsx deleted file mode 100644 index 6e812de54c..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React from 'react'; -import { LandingPage, useTabs } from '@google-psat/design-system'; - -const Panel = () => { - const { panel } = useTabs(({ state, actions }) => ({ - panel: state.panel, - highlightTab: actions.highlightTab, - })); - - const ActiveTabContent = panel.Element; - const { props, className, containerClassName } = panel; - - return ( - - -
- ) - } - extraClasses={containerClassName} - {...props} - /> - ); -}; - -export default Panel; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts index fd48020487..6259f694af 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts @@ -18,9 +18,18 @@ */ import { noop, createContext } from '@google-psat/common'; +export type ScriptBlockingData = { + [origin: string]: { + domain: string; + owner: string; + scriptBlocking: string; + }; +}; + export interface ScriptBlockingStoreContext { state: { uniqueResponseDomains: string[]; + scriptBlockingData: ScriptBlockingData; }; actions: { setUniqueResponseDomains: (newValue: string[]) => void; @@ -30,6 +39,7 @@ export interface ScriptBlockingStoreContext { const initialState: ScriptBlockingStoreContext = { state: { uniqueResponseDomains: [], + scriptBlockingData: {}, }, actions: { setUniqueResponseDomains: noop, diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx index 50507d5033..625cd9c1b4 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx @@ -27,13 +27,60 @@ import { isEqual } from 'lodash-es'; /** * Internal dependencies. */ -import Context from './context'; import { EXTRA_DATA } from '../../../../constants'; +import Context, { type ScriptBlockingData } from './context'; const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { const [uniqueResponseDomains, setUniqueResponseDomains] = useState( [] ); + const [scriptBlockingData, setScriptBlockingData] = + useState({}); + + useEffect(() => { + (async () => { + const data = await fetch( + 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md' + ); + + if (!data.ok) { + throw new Error(`HTTP error! status: ${data.status}`); + } + + const text = await data.text(); + + const lines = text + .split('\n') + .filter((line) => line.includes('|')) + .slice(2); + + const mdlData = lines.map((line) => + line.split('|').map((item) => item.trim()) + ); + + setScriptBlockingData(() => { + const _data = mdlData.reduce((acc, item: string[]) => { + let owner = item[1]; + + if (item[1].includes('PSL Domain')) { + owner = 'PSL Domain'; + } + + const scriptBlocking = item[2]; + + acc[item[0] as string] = { + domain: item[0], + owner, + scriptBlocking, + }; + + return acc; + }, {} as ScriptBlockingData); + + return _data; + }); + })(); + }, []); const messagePassingListener = useCallback( (message: { @@ -73,6 +120,7 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { value={{ state: { uniqueResponseDomains, + scriptBlockingData, }, actions: { setUniqueResponseDomains, diff --git a/packages/extension/src/view/devtools/tabs.ts b/packages/extension/src/view/devtools/tabs.ts index 87ec213dd9..f043f0a3da 100644 --- a/packages/extension/src/view/devtools/tabs.ts +++ b/packages/extension/src/view/devtools/tabs.ts @@ -49,8 +49,6 @@ import { ProtectionIcon, SiteBoundariesIconWhite, DemosIcon, - BlockIcon, - BlockIconWhite, IncognitoIcon, } from '@google-psat/design-system'; import { I18n } from '@google-psat/i18n'; @@ -85,7 +83,6 @@ import { FederatedCredential, IPProtection, PrivateStateTokens, - ScriptBlocking, } from './pages'; import HelpCenter from './pages/learning/helpCenter'; import Demos from './pages/learning/demos'; @@ -347,25 +344,6 @@ const TABS: SidebarItems = { }, children: {}, }, - [SIDEBAR_ITEMS_KEYS.SCRIPT_BLOCKING]: { - title: () => 'Script Blocking', - panel: { - Element: ScriptBlocking, - }, - icon: { - Element: BlockIcon, - props: { - className: 'fill-granite-gray relative right-[3px]', - }, - }, - selectedIcon: { - Element: BlockIconWhite, - props: { - className: 'fill-bright-gray relative right-[3px]', - }, - }, - children: {}, - }, [SIDEBAR_ITEMS_KEYS.BOUNCE_TRACKING]: { title: () => I18n.getMessage('bounceTracking'), panel: { From 58e525298458b2ac1f5bd5f46201c6f170be2911 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 3 Sep 2025 16:09:05 +0530 Subject: [PATCH 03/98] Remove script blocking provider. Change name of probabilistic reveal token to ipProxy. --- .../extension/src/view/devtools/index.tsx | 11 +- .../probabilisticRevealTokens/index.tsx | 51 +++---- .../src/view/devtools/stateProviders/index.ts | 1 - .../probabilisticRevealTokens/context.ts | 20 ++- .../probabilisticRevealTokens/index.ts | 9 +- ...der.tsx => ipProxyContextTypeProvider.tsx} | 120 ++++++++++++++-- ...abilisticRevealTokens.ts => useIPProxy.ts} | 17 +-- .../stateProviders/scriptBlocking/context.ts | 49 ------- .../stateProviders/scriptBlocking/index.ts | 18 --- .../scriptBlocking/scriptBlocking.tsx | 135 ------------------ .../scriptBlocking/useScriptBlocking.ts | 44 ------ .../extension/src/view/devtools/tests/app.tsx | 7 +- 12 files changed, 164 insertions(+), 318 deletions(-) rename packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/{probabilisticRevealTokensProvider.tsx => ipProxyContextTypeProvider.tsx} (53%) rename packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/{useProbabilisticRevealTokens.ts => useIPProxy.ts} (65%) delete mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts delete mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts delete mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx delete mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx index ad775db459..1c3546ef3c 100644 --- a/packages/extension/src/view/devtools/index.tsx +++ b/packages/extension/src/view/devtools/index.tsx @@ -37,8 +37,7 @@ import { AttributionReportingProvider, TopicsClassifierProvider, PrebidContextProvider, - ScriptBlockingProvider, - ProbabilisticRevealTokensProvider, + IPProxyProvider, } from './stateProviders'; const root = document.getElementById('root'); @@ -60,11 +59,9 @@ if (root) { - - - - - + + + diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 8711a22ec8..a96767f718 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -32,10 +32,7 @@ import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ -import { - useProbabilisticRevealTokens, - useScriptBlocking, -} from '../../../../stateProviders'; +import { useIPProxy } from '../../../../stateProviders'; import RowContextMenuForPRT from './rowContextMenu'; const ProbabilisticRevealTokens = () => { @@ -46,14 +43,12 @@ const ProbabilisticRevealTokens = () => { decryptedTokensData, prtTokensData, plainTextTokensData, - } = useProbabilisticRevealTokens(({ state }) => ({ + scriptBlockingData, + } = useIPProxy(({ state }) => ({ perTokenMetadata: state.perTokenMetadata, decryptedTokensData: state.decryptedTokens, prtTokensData: state.prtTokens, plainTextTokensData: state.plainTextTokens, - })); - - const { scriptBlockingData } = useScriptBlocking(({ state }) => ({ scriptBlockingData: state.scriptBlockingData, })); @@ -64,50 +59,50 @@ const ProbabilisticRevealTokens = () => { const tableColumns = useMemo( () => [ { - header: 'PRT', - accessorKey: 'prtHeader', - cell: (info) => info, - minWidth: 500, - }, - { - header: 'Origin', + header: 'Domain', accessorKey: 'origin', cell: (info) => info, }, - { - header: 'Decrypted', - accessorKey: 'decryptionKeyAvailable', - cell: (info, _) => { - return info ? : ''; - }, - }, { header: 'Owner', accessorKey: '0', cell: (_, details) => { - const origin: string = isValidURL(details?.origin) - ? new URL(details?.origin).host.slice(4) + const origin: string = isValidURL((details as PRTMetadata)?.origin) + ? new URL((details as PRTMetadata)?.origin).host.slice(4) : ''; return scriptBlockingData[origin]?.owner; }, }, + { + header: 'Decrypted', + accessorKey: 'decryptionKeyAvailable', + cell: (info) => { + return info ? : ''; + }, + }, { header: 'Signal', accessorKey: 'nonZeroUintsignal', - cell: (info, _) => { + cell: (info) => { return info ? : ''; }, }, { header: 'Blocking Scope', - accessorKey: '', + accessorKey: '1', cell: (_, details) => { - const origin: string = isValidURL(details?.origin) - ? new URL(details?.origin).host.slice(4) + const origin: string = isValidURL((details as PRTMetadata)?.origin) + ? new URL((details as PRTMetadata)?.origin).host.slice(4) : ''; return scriptBlockingData[origin]?.scriptBlocking; }, }, + { + header: 'PRT Prefix', + accessorKey: 'prtHeader', + cell: (info) => info, + minWidth: 50, + }, ], [scriptBlockingData] ); diff --git a/packages/extension/src/view/devtools/stateProviders/index.ts b/packages/extension/src/view/devtools/stateProviders/index.ts index e48b6304f7..34d0f5b772 100644 --- a/packages/extension/src/view/devtools/stateProviders/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/index.ts @@ -21,5 +21,4 @@ export * from './webStories'; export * from './attributionReporting'; export * from './prebid'; export * from './topicsClassifier'; -export * from './scriptBlocking'; export * from './probabilisticRevealTokens'; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts index 00cf66f81e..9aba9b21e2 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts @@ -24,24 +24,34 @@ import { type PRTMetadata, } from '@google-psat/common'; -export interface ProbabilisticRevealTokensContextType { +export type ScriptBlockingData = { + [origin: string]: { + domain: string; + owner: string; + scriptBlocking: string; + }; +}; + +export interface IPProxyContextType { state: { plainTextTokens: UniquePlainTextToken[]; decryptedTokens: UniqueDecryptedToken[]; prtTokens: ProbablisticRevealToken[]; perTokenMetadata: PRTMetadata[]; + scriptBlockingData: ScriptBlockingData; + uniqueResponseDomains: string[]; }; } -const initialState: ProbabilisticRevealTokensContextType = { +const initialState: IPProxyContextType = { state: { plainTextTokens: [], decryptedTokens: [], prtTokens: [], perTokenMetadata: [], + scriptBlockingData: {}, + uniqueResponseDomains: [], }, }; -export default createContext( - initialState -); +export default createContext(initialState); diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts index ff27962cdd..a766cf335c 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts @@ -13,9 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default as ProbabilisticRevealTokensProvider } from './probabilisticRevealTokensProvider'; -export { - default as ProbabilisticRevealTokensContext, - type ProbabilisticRevealTokensContextType, -} from './context'; -export { default as useProbabilisticRevealTokens } from './useProbabilisticRevealTokens'; +export { default as IPProxyProvider } from './ipProxyContextTypeProvider'; +export { default as IPProxyContext, type IPProxyContextType } from './context'; +export { default as useIPProxy } from './useIPProxy'; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx similarity index 53% rename from packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx rename to packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx index d7c912f6b0..7d1081e14e 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx @@ -23,40 +23,61 @@ import React, { useCallback, useMemo, } from 'react'; +import type { + ProbablisticRevealToken, + PRTMetadata, + UniqueDecryptedToken, + UniquePlainTextToken, +} from '@google-psat/common'; import { isEqual } from 'lodash-es'; /** * Internal dependencies. */ -import Context, { type ProbabilisticRevealTokensContextType } from './context'; -import { TAB_TOKEN_DATA } from '../../../../constants'; +import Context, { + type IPProxyContextType, + type ScriptBlockingData, +} from './context'; +import { EXTRA_DATA, TAB_TOKEN_DATA } from '../../../../constants'; const Provider = ({ children }: PropsWithChildren) => { const [decryptedTokens, setDecryptedTokens] = useState< - ProbabilisticRevealTokensContextType['state']['decryptedTokens'] + IPProxyContextType['state']['decryptedTokens'] >([]); const [prtTokens, setPrtTokens] = useState< - ProbabilisticRevealTokensContextType['state']['prtTokens'] + IPProxyContextType['state']['prtTokens'] >([]); const [plainTextTokens, setPlainTextTokens] = useState< - ProbabilisticRevealTokensContextType['state']['plainTextTokens'] + IPProxyContextType['state']['plainTextTokens'] >([]); const [perTokenMetadata, setPerTokenMetadata] = useState< - ProbabilisticRevealTokensContextType['state']['perTokenMetadata'] + IPProxyContextType['state']['perTokenMetadata'] >([]); + const [uniqueResponseDomains, setUniqueResponseDomains] = useState( + [] + ); + const [scriptBlockingData, setScriptBlockingData] = + useState({}); + const messagePassingListener = useCallback( (message: { type: string; payload: { tabId: string; - tokens: ProbabilisticRevealTokensContextType['state']; + tokens: { + plainTextTokens: UniquePlainTextToken[]; + decryptedTokens: UniqueDecryptedToken[]; + prtTokens: ProbablisticRevealToken[]; + perTokenMetadata: PRTMetadata[]; + }; + uniqueResponseDomains: string[]; }; }) => { - if (![TAB_TOKEN_DATA].includes(message.type)) { + if (![TAB_TOKEN_DATA, EXTRA_DATA].includes(message.type)) { return; } @@ -71,7 +92,7 @@ const Provider = ({ children }: PropsWithChildren) => { return; } - if (message.payload.tokens) { + if (message.payload.tokens && TAB_TOKEN_DATA) { setPrtTokens((prev) => { if (isEqual(prev, message.payload.tokens.prtTokens)) { return prev; @@ -100,6 +121,14 @@ const Provider = ({ children }: PropsWithChildren) => { return message.payload.tokens.perTokenMetadata; }); } + + if (EXTRA_DATA === message.type) { + setUniqueResponseDomains((prev) => { + return isEqual(message.payload.uniqueResponseDomains, prev) + ? prev + : message.payload.uniqueResponseDomains || []; + }); + } }, [] ); @@ -128,6 +157,66 @@ const Provider = ({ children }: PropsWithChildren) => { [] ); + useEffect(() => { + (async () => { + const data = await fetch( + 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md' + ); + + if (!data.ok) { + throw new Error(`HTTP error! status: ${data.status}`); + } + + const text = await data.text(); + + const lines = text + .split('\n') + .filter((line) => line.includes('|')) + .slice(2); + + const mdlData = lines.map((line) => + line.split('|').map((item) => item.trim()) + ); + + setScriptBlockingData(() => { + const _data = mdlData.reduce((acc, item: string[]) => { + let owner = item[1]; + + if (item[1].includes('PSL Domain')) { + owner = 'PSL Domain'; + } + + let scriptBlocking; + + switch (item[2] as string) { + case 'Not Impacted By Script Blocking': + scriptBlocking = 'None'; + break; + case 'Some URLs are Blocked': + scriptBlocking = 'Partial'; + break; + case 'Entire Domain Blocked': + scriptBlocking = 'Complete'; + break; + default: + scriptBlocking = 'None'; + break; + } + + acc[item[0] as string] = { + domain: item[0], + owner, + scriptBlocking, + }; + + return acc; + }, {} as ScriptBlockingData); + + return _data; + }); + })(); + }, []); + useEffect(() => { chrome.runtime?.onMessage?.addListener(messagePassingListener); chrome.webNavigation?.onCommitted?.addListener( @@ -142,16 +231,25 @@ const Provider = ({ children }: PropsWithChildren) => { }; }, [messagePassingListener, onCommittedNavigationListener]); - const memoisedValue: ProbabilisticRevealTokensContextType = useMemo(() => { + const memoisedValue: IPProxyContextType = useMemo(() => { return { state: { plainTextTokens, decryptedTokens, prtTokens, perTokenMetadata, + scriptBlockingData, + uniqueResponseDomains, }, }; - }, [plainTextTokens, prtTokens, perTokenMetadata, decryptedTokens]); + }, [ + uniqueResponseDomains, + scriptBlockingData, + plainTextTokens, + prtTokens, + perTokenMetadata, + decryptedTokens, + ]); return {children}; }; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts similarity index 65% rename from packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts rename to packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts index ba12a58729..e48231072f 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts @@ -21,24 +21,21 @@ import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. */ -import Context, { type ProbabilisticRevealTokensContextType } from './context'; +import Context, { type IPProxyContextType } from './context'; -export function useAttributionReporting(): ProbabilisticRevealTokensContextType; -export function useAttributionReporting( - selector: (state: ProbabilisticRevealTokensContextType) => T -): T; +export function useIPProxy(): IPProxyContextType; +export function useIPProxy(selector: (state: IPProxyContextType) => T): T; /** * Cookie store hook. * @param selector Selector function to partially select state. * @returns selected part of the state */ -export function useAttributionReporting( - selector: ( - state: ProbabilisticRevealTokensContextType - ) => T | ProbabilisticRevealTokensContextType = (state) => state +export function useIPProxy( + selector: (state: IPProxyContextType) => T | IPProxyContextType = (state) => + state ) { return useContextSelector(Context, selector); } -export default useAttributionReporting; +export default useIPProxy; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts deleted file mode 100644 index 6259f694af..0000000000 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import { noop, createContext } from '@google-psat/common'; - -export type ScriptBlockingData = { - [origin: string]: { - domain: string; - owner: string; - scriptBlocking: string; - }; -}; - -export interface ScriptBlockingStoreContext { - state: { - uniqueResponseDomains: string[]; - scriptBlockingData: ScriptBlockingData; - }; - actions: { - setUniqueResponseDomains: (newValue: string[]) => void; - }; -} - -const initialState: ScriptBlockingStoreContext = { - state: { - uniqueResponseDomains: [], - scriptBlockingData: {}, - }, - actions: { - setUniqueResponseDomains: noop, - }, -}; - -export default createContext(initialState); diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts deleted file mode 100644 index 5b73391fc0..0000000000 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { default as ScriptBlockingProvider } from './scriptBlocking'; -export { default as ScriptBlockingContext } from './context'; -export { default as useScriptBlocking } from './useScriptBlocking'; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx deleted file mode 100644 index 625cd9c1b4..0000000000 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlocking.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React, { - type PropsWithChildren, - useState, - useEffect, - useCallback, -} from 'react'; -import { isEqual } from 'lodash-es'; - -/** - * Internal dependencies. - */ -import { EXTRA_DATA } from '../../../../constants'; -import Context, { type ScriptBlockingData } from './context'; - -const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { - const [uniqueResponseDomains, setUniqueResponseDomains] = useState( - [] - ); - const [scriptBlockingData, setScriptBlockingData] = - useState({}); - - useEffect(() => { - (async () => { - const data = await fetch( - 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md' - ); - - if (!data.ok) { - throw new Error(`HTTP error! status: ${data.status}`); - } - - const text = await data.text(); - - const lines = text - .split('\n') - .filter((line) => line.includes('|')) - .slice(2); - - const mdlData = lines.map((line) => - line.split('|').map((item) => item.trim()) - ); - - setScriptBlockingData(() => { - const _data = mdlData.reduce((acc, item: string[]) => { - let owner = item[1]; - - if (item[1].includes('PSL Domain')) { - owner = 'PSL Domain'; - } - - const scriptBlocking = item[2]; - - acc[item[0] as string] = { - domain: item[0], - owner, - scriptBlocking, - }; - - return acc; - }, {} as ScriptBlockingData); - - return _data; - }); - })(); - }, []); - - const messagePassingListener = useCallback( - (message: { - type: string; - payload: { - uniqueResponseDomains?: string[]; - tabId: string; - }; - }) => { - if ( - message.type !== EXTRA_DATA || - String(message.payload.tabId) !== - String(chrome.devtools.inspectedWindow.tabId) - ) { - return; - } - - setUniqueResponseDomains((prev) => { - return isEqual(message.payload.uniqueResponseDomains, prev) - ? prev - : message.payload.uniqueResponseDomains || []; - }); - }, - [] - ); - - useEffect(() => { - chrome.runtime?.onMessage?.addListener(messagePassingListener); - - return () => { - chrome.runtime?.onMessage?.removeListener(messagePassingListener); - }; - }, [messagePassingListener]); - - return ( - - {children} - - ); -}; - -export default ScriptBlockingProvider; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts deleted file mode 100644 index e5700ea79d..0000000000 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies - */ -import { useContextSelector } from '@google-psat/common'; - -/** - * Internal dependencies. - */ -import Context, { type ScriptBlockingStoreContext } from './context'; - -export function useScriptBlocking(): ScriptBlockingStoreContext; -export function useScriptBlocking( - selector: (state: ScriptBlockingStoreContext) => T -): T; - -/** - * Script Blocking state provider hook. - * @param selector Selector function to partially select state. - * @returns selected part of the state - */ -export function useScriptBlocking( - selector: ( - state: ScriptBlockingStoreContext - ) => T | ScriptBlockingStoreContext = (state) => state -) { - return useContextSelector(Context, selector); -} - -export default useScriptBlocking; diff --git a/packages/extension/src/view/devtools/tests/app.tsx b/packages/extension/src/view/devtools/tests/app.tsx index d058a1032b..3c56d83409 100644 --- a/packages/extension/src/view/devtools/tests/app.tsx +++ b/packages/extension/src/view/devtools/tests/app.tsx @@ -33,7 +33,7 @@ import { useProtectedAudience, useSettings, usePrebid, - useProbabilisticRevealTokens, + useIPProxy, } from '../stateProviders'; // @ts-ignore // eslint-disable-next-line import/no-unresolved @@ -45,7 +45,7 @@ jest.mock('../stateProviders', () => ({ useSettings: jest.fn(), useProtectedAudience: jest.fn(), usePrebid: jest.fn(), - useProbabilisticRevealTokens: jest.fn(), + useIPProxy: jest.fn(), })); jest.mock( @@ -62,8 +62,7 @@ const mockUseTablePersistentSettingStore = const mockUseProtectedAudienceStore = useProtectedAudience as jest.Mock; const mockUseSettingsStore = useSettings as jest.Mock; const mockPrebidStore = usePrebid as jest.Mock; -const mockProbabilisticRevealTokensStore = - useProbabilisticRevealTokens as jest.Mock; +const mockProbabilisticRevealTokensStore = useIPProxy as jest.Mock; describe('App', () => { beforeAll(() => { From 673ee5437c8a36d07fa3bd95c6ed96828f2b203f Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 13:16:13 +0530 Subject: [PATCH 04/98] Fix: Update incognito mode setting from 'spanning' to 'split' --- packages/extension/src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/manifest.json b/packages/extension/src/manifest.json index df64a8650f..3538276672 100644 --- a/packages/extension/src/manifest.json +++ b/packages/extension/src/manifest.json @@ -10,7 +10,7 @@ "48": "icons/icon-48.png", "128": "icons/icon-128.png" }, - "incognito": "spanning", + "incognito": "split", "permissions": [ "storage", "tabs", From 5587af3ef46b951a9715d13f5b4ef5415d67d5fc Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 14:28:09 +0530 Subject: [PATCH 05/98] Fix: Handle errors silently in setupIntervals and PRTStore, and prevent domain setting in incognito mode --- .../chromeListeners/utils/setupIntervals.ts | 6 ++- packages/extension/src/store/PRTStore.ts | 3 +- .../src/view/devtools/pages/layout.tsx | 47 ++++++++++--------- .../utils/setDomainsInAllowList.ts | 4 ++ .../ipProxyContextTypeProvider.tsx | 4 +- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts b/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts index a881ebe4c3..4ecdc3d7d9 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts @@ -25,7 +25,11 @@ const setupIntervals = () => { // @see https://developer.chrome.com/blog/longer-esw-lifetimes#whats_changed // Doing this to keep the service worker alive so that we dont loose any data and introduce any bug. setInterval(async () => { - await chrome.storage.session.get(); + try { + await chrome.storage.session.get(); + } catch (error) { + //fail silently + } }, 20000); // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index b5247443ef..9ef6aaea36 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -111,7 +111,7 @@ class PRTStore extends DataStore { overrideForInitialSync: boolean ) { try { - if (DataStore.tabs[tabId].newUpdatesCA <= 0 && !overrideForInitialSync) { + if (DataStore.tabs[tabId].newUpdatesPRT <= 0 && !overrideForInitialSync) { return; } @@ -119,7 +119,6 @@ class PRTStore extends DataStore { ...this.tabTokens[tabId], perTokenMetadata: Object.values(this.tabTokens[tabId].perTokenMetadata), }; - await chrome.runtime.sendMessage({ type: TAB_TOKEN_DATA, payload: { diff --git a/packages/extension/src/view/devtools/pages/layout.tsx b/packages/extension/src/view/devtools/pages/layout.tsx index 59a8c01491..304bd14151 100644 --- a/packages/extension/src/view/devtools/pages/layout.tsx +++ b/packages/extension/src/view/devtools/pages/layout.tsx @@ -198,15 +198,31 @@ const Layout = ({ setSidebarData }: LayoutProps) => { } } - (async () => { - const currentTab = await getCurrentTab(); - const isFirstVisit = - (await chrome.storage.sync.get('isFirstTime'))?.isFirstTime ?? false; + return data; + }); + }, [ + selectedAdUnit, + canStartInspecting, + frameHasCookies, + isInspecting, + isKeySelected, + isSidebarFocused, + setIsInspecting, + setSidebarData, + tabFrames, + ]); + useEffect(() => { + (async () => { + const currentTab = await getCurrentTab(); + const isFirstVisit = + (await chrome.storage.sync.get('isFirstTime'))?.isFirstTime ?? false; + setSidebarData((prev) => { + const data = { ...prev }; if (currentTab?.incognito) { delete data[SIDEBAR_ITEMS_KEYS.OPEN_INCOGNITO_TAB]; data[SIDEBAR_ITEMS_KEYS.SETTINGS].addDivider = false; - return; + return data; } data[SIDEBAR_ITEMS_KEYS.OPEN_INCOGNITO_TAB].popupTitle = incognitoAccess @@ -246,23 +262,10 @@ const Layout = ({ setSidebarData }: LayoutProps) => { }, }; } - })(); - - return data; - }); - }, [ - selectedAdUnit, - canStartInspecting, - frameHasCookies, - isInspecting, - isKeySelected, - isSidebarFocused, - setIsInspecting, - setSidebarData, - tabFrames, - incognitoAccess, - openIncognitoTab, - ]); + return data; + }); + })(); + }, [incognitoAccess, isSidebarFocused, openIncognitoTab, setSidebarData]); const buttonReloadActionCompnent = useMemo(() => { return ( diff --git a/packages/extension/src/view/devtools/stateProviders/allowedList/utils/setDomainsInAllowList.ts b/packages/extension/src/view/devtools/stateProviders/allowedList/utils/setDomainsInAllowList.ts index 6230584d5e..ded505488e 100644 --- a/packages/extension/src/view/devtools/stateProviders/allowedList/utils/setDomainsInAllowList.ts +++ b/packages/extension/src/view/devtools/stateProviders/allowedList/utils/setDomainsInAllowList.ts @@ -44,6 +44,10 @@ const setDomainsInAllowList = async ( return; } + if (isIncognito) { + return; + } + // @ts-ignore - The chrome-type definition is outdated, and the return type is a promise. const details = (await chrome.contentSettings.cookies.get({ primaryUrl: primaryUrl, diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx index 7d1081e14e..27eeaadf24 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx @@ -86,13 +86,13 @@ const Provider = ({ children }: PropsWithChildren) => { } if ( - message.payload.tabId !== + message.payload.tabId.toString() !== chrome.devtools.inspectedWindow.tabId.toString() ) { return; } - if (message.payload.tokens && TAB_TOKEN_DATA) { + if (message.payload.tokens && TAB_TOKEN_DATA === message.type) { setPrtTokens((prev) => { if (isEqual(prev, message.payload.tokens.prtTokens)) { return prev; From 771f48970e1c90a1f7526b5476a37e01bfc2a35a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 17:11:25 +0530 Subject: [PATCH 06/98] feat: Add IPTableData type and integrate it into ProbabilisticRevealTokens component --- .../src/components/table/useTable/types.ts | 10 +++ .../probabilisticRevealTokens/index.tsx | 70 +++++++++++++++++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/packages/design-system/src/components/table/useTable/types.ts b/packages/design-system/src/components/table/useTable/types.ts index ba6873a02f..63845cd2c0 100644 --- a/packages/design-system/src/components/table/useTable/types.ts +++ b/packages/design-system/src/components/table/useTable/types.ts @@ -64,6 +64,15 @@ export type UserEID = { source: string; }; +export type IPTableData = { + origin: string; + owner: string; + prtHeader: string; + decryptionKeyAvailable: boolean; + nonZeroUintsignal: boolean; + blockingScope: string; +}; + export type TableData = ( | CookieTableData | InterestGroups @@ -80,6 +89,7 @@ export type TableData = ( | UserEID | MDLTableData | PRTMetadata + | IPTableData ) & { highlighted?: boolean; highlightedClass?: string; // Optional class for highlighting rows diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index a96767f718..c612617444 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -25,8 +25,15 @@ import { ResizableTray, JsonView, noop, + type IPTableData, } from '@google-psat/design-system'; -import React, { useMemo, useRef, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { isValidURL, type PRTMetadata } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; /** @@ -52,6 +59,23 @@ const ProbabilisticRevealTokens = () => { scriptBlockingData: state.scriptBlockingData, })); + const [tableData, setTableData] = useState([]); + const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); + + const checkbox = useCallback(() => { + return ( + + ); + }, []); + const rowContextMenuRef = useRef | null>(null); @@ -65,8 +89,12 @@ const ProbabilisticRevealTokens = () => { }, { header: 'Owner', - accessorKey: '0', - cell: (_, details) => { + accessorKey: 'owner', + cell: (info, details) => { + if (info) { + return info; + } + const origin: string = isValidURL((details as PRTMetadata)?.origin) ? new URL((details as PRTMetadata)?.origin).host.slice(4) : ''; @@ -89,8 +117,12 @@ const ProbabilisticRevealTokens = () => { }, { header: 'Blocking Scope', - accessorKey: '1', - cell: (_, details) => { + accessorKey: 'blockingScope', + cell: (info, details) => { + if (info) { + return info; + } + const origin: string = isValidURL((details as PRTMetadata)?.origin) ? new URL((details as PRTMetadata)?.origin).host.slice(4) : ''; @@ -107,6 +139,25 @@ const ProbabilisticRevealTokens = () => { [scriptBlockingData] ); + useEffect(() => { + const mappedData = Object.values(scriptBlockingData).map( + ({ domain, owner, scriptBlocking }) => { + return { + origin: domain, + owner, + blockingScope: scriptBlocking, + decryptionKeyAvailable: false, + nonZeroUintsignal: false, + prtHeader: '', + }; + } + ); + setTableData([ + ...(perTokenMetadata as unknown as IPTableData[]), + ...mappedData, + ]); + }, [perTokenMetadata, scriptBlockingData]); + const formedJson = useMemo(() => { if (!selectedJSON) { return null; @@ -187,8 +238,13 @@ const ProbabilisticRevealTokens = () => { >
setSelectedJSON(row as PRTMetadata)} getRowObjectKey={(row: TableRow) => (row.originalData as PRTMetadata).prtHeader.toString() @@ -198,10 +254,10 @@ const ProbabilisticRevealTokens = () => { } >
From bcf7c4c2bca6ea4cd63849657b4200430c99c643 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 19:26:53 +0530 Subject: [PATCH 07/98] feat: Enhance IPTableData type with highlighted properties and update ProbabilisticRevealTokens component for improved domain visibility --- .../src/components/table/useTable/types.ts | 2 + .../probabilisticRevealTokens/index.tsx | 147 +++++++++++------- 2 files changed, 95 insertions(+), 54 deletions(-) diff --git a/packages/design-system/src/components/table/useTable/types.ts b/packages/design-system/src/components/table/useTable/types.ts index 63845cd2c0..d7c7bae5da 100644 --- a/packages/design-system/src/components/table/useTable/types.ts +++ b/packages/design-system/src/components/table/useTable/types.ts @@ -71,6 +71,8 @@ export type IPTableData = { decryptionKeyAvailable: boolean; nonZeroUintsignal: boolean; blockingScope: string; + highlighted?: boolean; + highlightedClass?: string; }; export type TableData = ( diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index c612617444..57830376c5 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -27,13 +27,7 @@ import { noop, type IPTableData, } from '@google-psat/design-system'; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { isValidURL, type PRTMetadata } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; /** @@ -51,28 +45,40 @@ const ProbabilisticRevealTokens = () => { prtTokensData, plainTextTokensData, scriptBlockingData, + uniqueResponseDomains, } = useIPProxy(({ state }) => ({ perTokenMetadata: state.perTokenMetadata, decryptedTokensData: state.decryptedTokens, prtTokensData: state.prtTokens, plainTextTokensData: state.plainTextTokens, scriptBlockingData: state.scriptBlockingData, + uniqueResponseDomains: state.uniqueResponseDomains, })); - const [tableData, setTableData] = useState([]); - const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); + const [shouldShowMDL, setShouldShowMDL] = useState(false); + const [showOnlyHighlighted, setShowOnlyHighlighted] = + useState(false); const checkbox = useCallback(() => { return ( - +
+ + +
); }, []); @@ -90,16 +96,7 @@ const ProbabilisticRevealTokens = () => { { header: 'Owner', accessorKey: 'owner', - cell: (info, details) => { - if (info) { - return info; - } - - const origin: string = isValidURL((details as PRTMetadata)?.origin) - ? new URL((details as PRTMetadata)?.origin).host.slice(4) - : ''; - return scriptBlockingData[origin]?.owner; - }, + cell: (info) => info, }, { header: 'Decrypted', @@ -118,16 +115,7 @@ const ProbabilisticRevealTokens = () => { { header: 'Blocking Scope', accessorKey: 'blockingScope', - cell: (info, details) => { - if (info) { - return info; - } - - const origin: string = isValidURL((details as PRTMetadata)?.origin) - ? new URL((details as PRTMetadata)?.origin).host.slice(4) - : ''; - return scriptBlockingData[origin]?.scriptBlocking; - }, + cell: (info) => info, }, { header: 'PRT Prefix', @@ -136,11 +124,11 @@ const ProbabilisticRevealTokens = () => { minWidth: 50, }, ], - [scriptBlockingData] + [] ); - useEffect(() => { - const mappedData = Object.values(scriptBlockingData).map( + const tableData = useMemo(() => { + const mdl = Object.values(scriptBlockingData).map( ({ domain, owner, scriptBlocking }) => { return { origin: domain, @@ -152,11 +140,67 @@ const ProbabilisticRevealTokens = () => { }; } ); - setTableData([ - ...(perTokenMetadata as unknown as IPTableData[]), - ...mappedData, - ]); - }, [perTokenMetadata, scriptBlockingData]); + + const token = perTokenMetadata.map((_token) => { + const origin = isValidURL((_token as PRTMetadata)?.origin) + ? new URL((_token as PRTMetadata)?.origin).host.slice(4) + : ''; + + return { + ..._token, + owner: scriptBlockingData[origin]?.owner, + blockingScope: scriptBlockingData[origin]?.scriptBlocking, + }; + }); + let unSortedData: IPTableData[] = [...(token as unknown as IPTableData[])]; + + if (shouldShowMDL) { + unSortedData.push(...(mdl as unknown as IPTableData[])); + } + + if (showOnlyHighlighted) { + unSortedData = unSortedData.map((item) => { + return { + ...item, + highlighted: uniqueResponseDomains.includes(item?.origin), + highlightedClass: + uniqueResponseDomains.includes(item?.origin) && + item?.blockingScope.startsWith('Partial') + ? 'bg-amber-100' + : '', + }; + }); + } + + return unSortedData.sort((a, b) => { + const aHasHeader = Object.prototype.hasOwnProperty.call(a, 'prtHeader'); + const bHasHeader = Object.prototype.hasOwnProperty.call(b, 'prtHeader'); + + if (aHasHeader && !bHasHeader) { + return -1; + } + if (!aHasHeader && bHasHeader) { + return 1; + } + + if (showOnlyHighlighted) { + if (a.highlighted && !b.highlighted) { + return -1; + } + if (!a.highlighted && b.highlighted) { + return 1; + } + } + + return 0; + }); + }, [ + perTokenMetadata, + scriptBlockingData, + shouldShowMDL, + showOnlyHighlighted, + uniqueResponseDomains, + ]); const formedJson = useMemo(() => { if (!selectedJSON) { @@ -238,13 +282,9 @@ const ProbabilisticRevealTokens = () => { >
setSelectedJSON(row as PRTMetadata)} getRowObjectKey={(row: TableRow) => (row.originalData as PRTMetadata).prtHeader.toString() @@ -254,8 +294,7 @@ const ProbabilisticRevealTokens = () => { } >
From d30cca04898f7866c4bc2fa2f53140a65e805ddc Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 19:37:40 +0530 Subject: [PATCH 08/98] feat: Update unSortedData handling in ProbabilisticRevealTokens for improved highlighting and script blocking integration --- .../probabilisticRevealTokens/index.tsx | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 57830376c5..51f2992a41 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -159,17 +159,40 @@ const ProbabilisticRevealTokens = () => { } if (showOnlyHighlighted) { - unSortedData = unSortedData.map((item) => { - return { - ...item, - highlighted: uniqueResponseDomains.includes(item?.origin), - highlightedClass: - uniqueResponseDomains.includes(item?.origin) && - item?.blockingScope.startsWith('Partial') - ? 'bg-amber-100' - : '', - }; - }); + if (shouldShowMDL) { + unSortedData = unSortedData.map((item) => { + return { + ...item, + highlighted: uniqueResponseDomains.includes(item?.origin), + highlightedClass: + uniqueResponseDomains.includes(item?.origin) && + item?.blockingScope.startsWith('Partial') + ? 'bg-amber-100' + : '', + }; + }); + } else { + unSortedData.push( + ...uniqueResponseDomains + .filter((item) => scriptBlockingData[item]) + .map((item) => { + return { + blockingScope: scriptBlockingData[item].scriptBlocking, + prtHeader: '', + origin: scriptBlockingData[item].domain, + owner: scriptBlockingData[item].owner, + decryptionKeyAvailable: false, + highlighted: true, + nonZeroUintsignal: false, + highlightedClass: scriptBlockingData[ + item + ].scriptBlocking.startsWith('Partial') + ? 'bg-amber-100' + : '', + }; + }) + ); + } } return unSortedData.sort((a, b) => { From a3d5bca6e040941bf28d5bb7247323582f30b864 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 19:40:54 +0530 Subject: [PATCH 09/98] fix: Update row object key retrieval to use 'origin' instead of 'prtHeader' for improved data accuracy --- .../ipProtection/probabilisticRevealTokens/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 51f2992a41..8db1701f03 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -310,7 +310,7 @@ const ProbabilisticRevealTokens = () => { tableSearchKeys={['origin', 'owner']} onRowClick={(row) => setSelectedJSON(row as PRTMetadata)} getRowObjectKey={(row: TableRow) => - (row.originalData as PRTMetadata).prtHeader.toString() + (row.originalData as PRTMetadata).origin.toString() } onRowContextMenu={ rowContextMenuRef.current?.onRowContextMenu ?? noop From caec6c3d9b86d977e9a85bb85b560d64175680a6 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 19:41:29 +0530 Subject: [PATCH 10/98] fix: Update title in Observability tab from 'Probabilistic Reveal Tokens' to 'Masked Domain List' for consistency --- .../devtools/pages/privacyProtection/ipProtection/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx index 8c0db60d73..7ae04f6608 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx @@ -58,7 +58,7 @@ const IPProtection = () => { ], Observability: [ { - title: 'Probabilistic Reveal Tokens', + title: 'Masked Domain List', content: { Element: ProbabilisticRevealTokens, className: 'overflow-auto h-full', From 45035fe2fa126c87c7234651aa94c77ebe345974 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 19:41:53 +0530 Subject: [PATCH 11/98] fix: Remove MDLTable from Observability tab for cleaner structure --- .../pages/privacyProtection/ipProtection/index.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx index 7ae04f6608..6b2ee43adc 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx @@ -27,7 +27,6 @@ import { * Internal dependencies. */ import Panel from './panel'; -import MDLTable from './mdlTable'; import ProbabilisticRevealTokens from './probabilisticRevealTokens'; const IPProtection = () => { @@ -46,15 +45,6 @@ const IPProtection = () => { className: 'p-4', }, }, - { - title: 'Masked Domain List', - content: { - Element: MDLTable, - props: {}, - className: 'overflow-auto h-full', - containerClassName: 'h-full', - }, - }, ], Observability: [ { From ee7e06e61339448f61138c254b45b8c9243e3c0c Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 4 Sep 2025 20:03:14 +0530 Subject: [PATCH 12/98] feat: Add CirclePieChart components for PRTs and Script Blocking visualization --- .../probabilisticRevealTokens/index.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 8db1701f03..6a64a1cee0 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -26,6 +26,7 @@ import { JsonView, noop, type IPTableData, + CirclePieChart, } from '@google-psat/design-system'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import { isValidURL, type PRTMetadata } from '@google-psat/common'; @@ -290,6 +291,42 @@ const ProbabilisticRevealTokens = () => { return (
+
+
+
+ + +
+
+
Date: Thu, 4 Sep 2025 20:09:48 +0530 Subject: [PATCH 13/98] fix: Adjust layout of CirclePieChart component for improved responsiveness --- .../ipProtection/probabilisticRevealTokens/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 6a64a1cee0..f85b556fc1 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -291,9 +291,9 @@ const ProbabilisticRevealTokens = () => { return (
-
-
-
+
+
+
{ infoIconClassName="absolute -right-3" />
-
+
Date: Mon, 8 Sep 2025 11:21:19 +0530 Subject: [PATCH 14/98] feat: Implement setActiveGroup action and integrate it into Tabs component for improved group management --- .../src/components/tabs/index.tsx | 52 ++++++-------- .../src/components/tabs/useTabs/context.ts | 2 + .../src/components/tabs/useTabs/provider.tsx | 67 +++++++++---------- 3 files changed, 52 insertions(+), 69 deletions(-) diff --git a/packages/design-system/src/components/tabs/index.tsx b/packages/design-system/src/components/tabs/index.tsx index ad6c767acc..c8eb62eb76 100644 --- a/packages/design-system/src/components/tabs/index.tsx +++ b/packages/design-system/src/components/tabs/index.tsx @@ -35,17 +35,18 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { activeTab, activeGroup, setActiveTab, + setActiveGroup, groupedTitles, titles, isTabHighlighted, shouldAddSpacer, getTabGroup, isGroup, - loading, } = useTabs(({ state, actions }) => ({ activeTab: state.activeTab, activeGroup: state.activeGroup, setActiveTab: actions.setActiveTab, + setActiveGroup: actions.setActiveGroup, groupedTitles: state.groupedTitles, titles: state.titles, isTabHighlighted: actions.isTabHighlighted, @@ -57,18 +58,6 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { const timeoutRef = useRef(null); - const [expandedGroup, setExpandedGroup] = useState(null); - - useEffect(() => { - if (loading) { - return; - } - - if (activeGroup && expandedGroup === null) { - setExpandedGroup(activeGroup); - } - }, [activeGroup, expandedGroup, loading]); - useEffect(() => { return () => { if (timeoutRef.current) { @@ -87,17 +76,15 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { setIsAnimating(true); - if (expandedGroup === group) { - setExpandedGroup(null); - } else { - setExpandedGroup(group); + if (activeGroup !== group) { + setActiveGroup(group); } timeoutRef.current = setTimeout(() => { setIsAnimating(false); }, 300); }, - [expandedGroup, isAnimating] + [activeGroup, isAnimating, setActiveGroup] ); const handleKeyDown = useCallback( @@ -108,37 +95,36 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { if (event.shiftKey) { const previousIndex = activeTab - 1; if (previousIndex >= 0) { - setActiveTab(previousIndex); - const group = getTabGroup(previousIndex); - if (expandedGroup !== group) { - console.log(group, expandedGroup); + if (activeGroup !== group) { handleGroupClick(group); } - } else { - setActiveTab(titles.length - 1); + setActiveTab(previousIndex); + } else { const group = getTabGroup(titles.length - 1); - if (expandedGroup !== group) { + if (activeGroup !== group) { handleGroupClick(group); } + + setActiveTab(titles.length - 1); } } else { const nextIndex = activeTab + 1; if (nextIndex < titles.length) { - setActiveTab(nextIndex); - const group = getTabGroup(nextIndex); - if (expandedGroup !== group) { + if (activeGroup !== group) { handleGroupClick(group); } - } else { - setActiveTab(0); + setActiveTab(nextIndex); + } else { const group = getTabGroup(0); - if (expandedGroup !== group) { + if (activeGroup !== group) { handleGroupClick(group); } + + setActiveTab(0); } } } @@ -148,7 +134,7 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { titles.length, setActiveTab, getTabGroup, - expandedGroup, + activeGroup, handleGroupClick, ] ); @@ -171,7 +157,7 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { )} > {Object.entries(groupedTitles).map(([group, data]) => { - const isExpanded = expandedGroup === group; + const isExpanded = activeGroup === group; return (
void; setActiveTab: (tab: number) => void; + setActiveGroup: (group: string) => void; highlightTab: ( tab: number, count?: boolean | number, @@ -76,6 +77,7 @@ const initialState: TabsStoreContext = { actions: { setStorage: noop, setActiveTab: noop, + setActiveGroup: noop, highlightTab: noop, isTabHighlighted: () => false, shouldAddSpacer: () => false, diff --git a/packages/design-system/src/components/tabs/useTabs/provider.tsx b/packages/design-system/src/components/tabs/useTabs/provider.tsx index fc46e8a7ee..bfe3b12fab 100644 --- a/packages/design-system/src/components/tabs/useTabs/provider.tsx +++ b/packages/design-system/src/components/tabs/useTabs/provider.tsx @@ -55,43 +55,34 @@ export const TabsProvider = ({ } }, [isGroup, items]); - const [activeGroup, setActiveGroup] = useState(null); + const [activeGroup, _setActiveGroup] = useState(null); + const [activeTab, setActiveTab] = useState(0); - useEffect(() => { - setActiveGroup((prev) => - prev === null ? Object.keys(groupedItems)[0] ?? null : prev - ); - }, [groupedItems]); - - const [activeTab, _setActiveTab] = useState(0); - const activeTabRef = useRef(activeTab); const groupItemsRef = useRef(groupedItems); useEffect(() => { groupItemsRef.current = groupedItems; }, [groupedItems]); - const setActiveTab = useCallback((tab: number) => { - activeTabRef.current = tab; - _setActiveTab(tab); + useEffect(() => { + _setActiveGroup((prev) => + prev === null ? Object.keys(groupItemsRef.current)[0] ?? null : prev + ); + }, []); - let trackedIndex = 0; - const group = Object.entries(groupItemsRef.current).find(([, _items]) => { - const groupTitles = _items.map((item) => { - return { - title: item.title, - index: trackedIndex++, - }; - }); + const setActiveGroup = useCallback((group: string) => { + _setActiveGroup(group); - return groupTitles.some(({ index }) => index === tab); - }); + const keys = Object.keys(groupItemsRef.current); + let index = 0; + let tabIndex = 0; - if (group) { - setActiveGroup(group[0]); - } else { - setActiveGroup(null); + while (index < keys.length && keys[index] !== group) { + tabIndex += groupItemsRef.current[keys[index]]?.length || 0; + index++; } + + setActiveTab(tabIndex); }, []); const tabItems = useMemo(() => { @@ -105,10 +96,6 @@ export const TabsProvider = ({ Record >({}); - useEffect(() => { - activeTabRef.current = activeTab; - }, [activeTab]); - useEffect(() => { _setStorage(Array(tabItems.length).fill('')); }, [tabItems.length]); @@ -174,7 +161,7 @@ export const TabsProvider = ({ const highlightTab = useCallback( (tab: number, update: number | boolean = true, increment = false) => { - if (tab === activeTabRef.current) { + if (tab === activeTab) { return; } @@ -186,7 +173,7 @@ export const TabsProvider = ({ return next; }); }, - [] + [activeTab] ); const isTabHighlighted = useCallback( @@ -197,10 +184,10 @@ export const TabsProvider = ({ ); useEffect(() => { - if (highlightedTabs[activeTabRef.current]) { + if (highlightedTabs[activeTab]) { setHighlightedTabs((prev) => { const next = { ...prev }; - next[activeTabRef.current] = false; + next[activeTab] = false; return next; }); } @@ -250,14 +237,20 @@ export const TabsProvider = ({ if (!sessionStorage[name]) { sessionStorage[name] = { activeTab: 0, + activeGroup: 0, }; } const _activeTab = sessionStorage[name].activeTab || 0; + const _activeGroup = + sessionStorage[name].activeGroup || + Object.keys(groupItemsRef.current)[0]; + + setActiveGroup(_activeGroup); setActiveTab(_activeTab); setLoading(false); })(); - }, [name, setActiveTab]); + }, [name, setActiveGroup]); useEffect(() => { if (!name) { @@ -269,12 +262,13 @@ export const TabsProvider = ({ { [name]: { activeTab, + activeGroup, }, }, 'tabs' ); })(); - }, [name, activeTab]); + }, [name, activeTab, activeGroup]); return ( Date: Mon, 8 Sep 2025 12:20:39 +0530 Subject: [PATCH 15/98] feat: Add Script Blocking feature and refactor Probabilistic Reveal Tokens - Introduced a new Script Blocking context and provider to manage unique response domains. - Created a new Script Blocking page with a table displaying blocked domains and their statuses. - Refactored the Probabilistic Reveal Tokens context and provider to remove unused properties and improve state management. - Updated the devtools sidebar to include a new Script Blocking tab. - Removed deprecated IP Proxy context and related files. - Enhanced the overall structure and organization of state providers for better maintainability. --- .../src/components/table/useTable/types.ts | 12 - .../extension/src/view/devtools/index.tsx | 11 +- .../probabilisticRevealTokens/index.tsx | 149 +--------- .../scriptBlocking/index.tsx | 69 +++++ .../scriptBlocking/mdlTable/index.tsx | 274 ++++++++++++++++++ .../scriptBlocking/mdlTable/legend.tsx | 42 +++ .../mdlTable/rowContextMenu.tsx | 126 ++++++++ .../scriptBlocking/panel/index.tsx | 47 +++ .../src/view/devtools/stateProviders/index.ts | 1 + .../probabilisticRevealTokens/context.ts | 20 +- .../probabilisticRevealTokens/index.ts | 9 +- ... => probabilisticRevealTokensProvider.tsx} | 122 +------- ...oxy.ts => useProbabilisticRevealTokens.ts} | 17 +- .../stateProviders/scriptBlocking/context.ts | 39 +++ .../stateProviders/scriptBlocking/index.ts | 18 ++ .../scriptBlocking/scriptBlockingProvider.tsx | 87 ++++++ .../scriptBlocking/useScriptBlocking.ts | 44 +++ packages/extension/src/view/devtools/tabs.ts | 22 ++ 18 files changed, 815 insertions(+), 294 deletions(-) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel/index.tsx rename packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/{ipProxyContextTypeProvider.tsx => probabilisticRevealTokensProvider.tsx} (53%) rename packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/{useIPProxy.ts => useProbabilisticRevealTokens.ts} (64%) create mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx create mode 100644 packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts diff --git a/packages/design-system/src/components/table/useTable/types.ts b/packages/design-system/src/components/table/useTable/types.ts index d7c7bae5da..ba6873a02f 100644 --- a/packages/design-system/src/components/table/useTable/types.ts +++ b/packages/design-system/src/components/table/useTable/types.ts @@ -64,17 +64,6 @@ export type UserEID = { source: string; }; -export type IPTableData = { - origin: string; - owner: string; - prtHeader: string; - decryptionKeyAvailable: boolean; - nonZeroUintsignal: boolean; - blockingScope: string; - highlighted?: boolean; - highlightedClass?: string; -}; - export type TableData = ( | CookieTableData | InterestGroups @@ -91,7 +80,6 @@ export type TableData = ( | UserEID | MDLTableData | PRTMetadata - | IPTableData ) & { highlighted?: boolean; highlightedClass?: string; // Optional class for highlighting rows diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx index 1c3546ef3c..ad775db459 100644 --- a/packages/extension/src/view/devtools/index.tsx +++ b/packages/extension/src/view/devtools/index.tsx @@ -37,7 +37,8 @@ import { AttributionReportingProvider, TopicsClassifierProvider, PrebidContextProvider, - IPProxyProvider, + ScriptBlockingProvider, + ProbabilisticRevealTokensProvider, } from './stateProviders'; const root = document.getElementById('root'); @@ -59,9 +60,11 @@ if (root) { - - - + + + + + diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index f85b556fc1..b2514eae61 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -25,16 +25,15 @@ import { ResizableTray, JsonView, noop, - type IPTableData, CirclePieChart, } from '@google-psat/design-system'; -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { isValidURL, type PRTMetadata } from '@google-psat/common'; +import React, { useMemo, useRef, useState } from 'react'; +import { type PRTMetadata } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ -import { useIPProxy } from '../../../../stateProviders'; +import { useProbabilisticRevealTokens } from '../../../../stateProviders'; import RowContextMenuForPRT from './rowContextMenu'; const ProbabilisticRevealTokens = () => { @@ -45,44 +44,13 @@ const ProbabilisticRevealTokens = () => { decryptedTokensData, prtTokensData, plainTextTokensData, - scriptBlockingData, - uniqueResponseDomains, - } = useIPProxy(({ state }) => ({ + } = useProbabilisticRevealTokens(({ state }) => ({ perTokenMetadata: state.perTokenMetadata, decryptedTokensData: state.decryptedTokens, prtTokensData: state.prtTokens, plainTextTokensData: state.plainTextTokens, - scriptBlockingData: state.scriptBlockingData, - uniqueResponseDomains: state.uniqueResponseDomains, })); - const [shouldShowMDL, setShouldShowMDL] = useState(false); - const [showOnlyHighlighted, setShowOnlyHighlighted] = - useState(false); - - const checkbox = useCallback(() => { - return ( -
- - -
- ); - }, []); - const rowContextMenuRef = useRef | null>(null); @@ -113,119 +81,15 @@ const ProbabilisticRevealTokens = () => { return info ? : ''; }, }, - { - header: 'Blocking Scope', - accessorKey: 'blockingScope', - cell: (info) => info, - }, { header: 'PRT Prefix', accessorKey: 'prtHeader', - cell: (info) => info, - minWidth: 50, + cell: (info) => (info as string).slice(0, 10), }, ], [] ); - const tableData = useMemo(() => { - const mdl = Object.values(scriptBlockingData).map( - ({ domain, owner, scriptBlocking }) => { - return { - origin: domain, - owner, - blockingScope: scriptBlocking, - decryptionKeyAvailable: false, - nonZeroUintsignal: false, - prtHeader: '', - }; - } - ); - - const token = perTokenMetadata.map((_token) => { - const origin = isValidURL((_token as PRTMetadata)?.origin) - ? new URL((_token as PRTMetadata)?.origin).host.slice(4) - : ''; - - return { - ..._token, - owner: scriptBlockingData[origin]?.owner, - blockingScope: scriptBlockingData[origin]?.scriptBlocking, - }; - }); - let unSortedData: IPTableData[] = [...(token as unknown as IPTableData[])]; - - if (shouldShowMDL) { - unSortedData.push(...(mdl as unknown as IPTableData[])); - } - - if (showOnlyHighlighted) { - if (shouldShowMDL) { - unSortedData = unSortedData.map((item) => { - return { - ...item, - highlighted: uniqueResponseDomains.includes(item?.origin), - highlightedClass: - uniqueResponseDomains.includes(item?.origin) && - item?.blockingScope.startsWith('Partial') - ? 'bg-amber-100' - : '', - }; - }); - } else { - unSortedData.push( - ...uniqueResponseDomains - .filter((item) => scriptBlockingData[item]) - .map((item) => { - return { - blockingScope: scriptBlockingData[item].scriptBlocking, - prtHeader: '', - origin: scriptBlockingData[item].domain, - owner: scriptBlockingData[item].owner, - decryptionKeyAvailable: false, - highlighted: true, - nonZeroUintsignal: false, - highlightedClass: scriptBlockingData[ - item - ].scriptBlocking.startsWith('Partial') - ? 'bg-amber-100' - : '', - }; - }) - ); - } - } - - return unSortedData.sort((a, b) => { - const aHasHeader = Object.prototype.hasOwnProperty.call(a, 'prtHeader'); - const bHasHeader = Object.prototype.hasOwnProperty.call(b, 'prtHeader'); - - if (aHasHeader && !bHasHeader) { - return -1; - } - if (!aHasHeader && bHasHeader) { - return 1; - } - - if (showOnlyHighlighted) { - if (a.highlighted && !b.highlighted) { - return -1; - } - if (!a.highlighted && b.highlighted) { - return 1; - } - } - - return 0; - }); - }, [ - perTokenMetadata, - scriptBlockingData, - shouldShowMDL, - showOnlyHighlighted, - uniqueResponseDomains, - ]); - const formedJson = useMemo(() => { if (!selectedJSON) { return null; @@ -342,7 +206,7 @@ const ProbabilisticRevealTokens = () => { >
setSelectedJSON(row as PRTMetadata)} @@ -356,7 +220,6 @@ const ProbabilisticRevealTokens = () => {
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx new file mode 100644 index 0000000000..35360bc3ee --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React, { useMemo } from 'react'; +import { + InfoCard, + PSInfoKey, + TabsProvider, + type TabItems, +} from '@google-psat/design-system'; +/** + * Internal dependencies. + */ +import Panel from './panel'; +import MDLTable from './mdlTable'; + +const ScriptBlocking = () => { + const tabItems = useMemo( + () => ({ + Learning: [ + { + title: 'Overview', + content: { + Element: InfoCard, + props: { + infoKey: PSInfoKey.ScriptBlocking, + showQuickLinks: true, + isLandingPageContainer: true, + }, + className: 'p-4', + }, + }, + { + title: 'Blocked Domain List', + content: { + Element: MDLTable, + props: {}, + className: 'overflow-auto h-full', + containerClassName: 'h-full', + }, + }, + ], + }), + [] + ); + + return ( + + + + ); +}; + +export default ScriptBlocking; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx new file mode 100644 index 0000000000..3716b9d1c3 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -0,0 +1,274 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import { + noop, + ProgressBar, + Table, + TableProvider, + type TableFilter, + type TableColumn, + type TableRow, + Link, + ResizableTray, +} from '@google-psat/design-system'; +import React, { + useEffect, + useMemo, + useRef, + useState, + useCallback, +} from 'react'; +import type { MDLTableData } from '@google-psat/common'; +/** + * Internal dependencies + */ +import Legend from './legend'; +import { useScriptBlocking } from '../../../../stateProviders'; +import RowContextMenuForScriptBlocking from './rowContextMenu'; + +export const IMPACTED_BY_SCRIPT_BLOCKING = { + NONE: 'Not Impacted By Script Blocking', + PARTIAL: 'Some URLs are Blocked', + ENTIRE: 'Entire Domain Blocked', +}; + +const DATA_URL = + 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md'; + +const MDLTable = () => { + const [selectedKey, setSelectedKey] = useState(null); + const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); + const [isLoading, setIsLoading] = useState(true); + const { uniqueResponseDomains } = useScriptBlocking(({ state }) => ({ + uniqueResponseDomains: state.uniqueResponseDomains, + })); + + const rowContextMenuRef = useRef | null>(null); + + const [initialTableData, setinitialTableData] = useState< + { domain: string; owner: string; scriptBlocking: string }[] + >([]); + + useEffect(() => { + (async () => { + setIsLoading(true); + const response = await fetch(DATA_URL); + + if (!response.ok) { + setIsLoading(false); + throw new Error(`HTTP error! status: ${response.status}`); + } + + const text = await response.text(); + + const lines = text + .split('\n') + .filter((line) => line.includes('|')) + .slice(2); + + const mdlData = lines + .map((line) => line.split('|').map((item) => item.trim())) + .filter((item) => item[2] !== IMPACTED_BY_SCRIPT_BLOCKING.NONE); + + setinitialTableData(() => { + const data = mdlData.map((item: string[]) => { + let owner = item[1]; + + if (item[1].includes('PSL Domain')) { + owner = 'PSL Domain'; + } + + const scriptBlocking = item[2]; + + return { + domain: item[0], + owner, + scriptBlocking, + }; + }); + + setIsLoading(false); + + return data; + }); + })(); + }, []); + + const checkbox = useCallback( + () => ( + + ), + [] + ); + + const tableData: MDLTableData[] = useMemo(() => { + if (initialTableData.length === 0) { + return []; + } + + const data: MDLTableData[] = []; + + initialTableData.forEach((item) => { + let available = false; + if (uniqueResponseDomains.includes(item.domain)) { + available = true; + } + + const canPush = showOnlyHighlighted ? available : true; + + if (canPush) { + data.push({ + ...item, + highlighted: available, + highlightedClass: + available && item.scriptBlocking.startsWith('Some URLs are Blocked') + ? 'bg-amber-100' + : '', + } as MDLTableData); + } + }); + + return data.sort((a, b) => Number(b.highlighted) - Number(a.highlighted)); + }, [uniqueResponseDomains, initialTableData, showOnlyHighlighted]); + + const tableColumns = useMemo( + () => [ + { + header: 'Domain', + accessorKey: 'domain', + cell: (info) => info, + initialWidth: 100, + }, + { + header: 'Owner', + accessorKey: 'owner', + cell: (info) => { + if (info === 'PSL Domain') { + return ( + + PSL Domain + + ); + } + + return info; + }, + initialWidth: 100, + }, + { + header: 'Impacted by Script Blocking', + accessorKey: 'scriptBlocking', + cell: (info) => info, + }, + ], + [] + ); + + const filters = useMemo( + () => ({ + owner: { + title: 'Owner', + }, + scriptBlocking: { + title: 'Impacted by Script Blocking', + hasStaticFilterValues: true, + filterValues: { + [IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL]: { + selected: false, + description: IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL, + }, + [IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE]: { + selected: false, + description: IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE, + }, + }, + }, + }), + [] + ); + + if (isLoading) { + return ( +
+ +
+ ); + } + + return ( +
+ { + setSelectedKey((rowData as MDLTableData)?.domain || null); + }} + onRowContextMenu={ + rowContextMenuRef.current + ? rowContextMenuRef.current?.onRowContextMenu + : noop + } + getRowObjectKey={(row: TableRow) => + (row.originalData as MDLTableData).domain || '' + } + tablePersistentSettingsKey="mdlTable" + > + +
+ + + + + + ); +}; + +export default MDLTable; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx new file mode 100644 index 0000000000..dce9fa1a43 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import { Link } from '@google-psat/design-system'; +import React from 'react'; + +const Legend = () => { + return ( +
+

+ The Blocked Domain List is a subset of the{' '} + + Masked Domain List (MDL) + {' '} + under Google’s{' '} + + IP Protection + {' '} + initiative. Domains on this list are either entirely or patially + blocked. +

+
+ ); +}; + +export default Legend; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx new file mode 100644 index 0000000000..155c71f889 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import React, { + forwardRef, + useCallback, + useImperativeHandle, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; +import { type MDLTableData } from '@google-psat/common'; +import type { TableRow } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; + +const RowContextMenuForScriptBlocking = forwardRef<{ + onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; +}>(function RowContextMenu(_, ref) { + const [contextMenuOpen, setContextMenuOpen] = useState(false); + const [columnPosition, setColumnPosition] = useState({ + x: 0, + y: 0, + }); + const [rowData, setRowData] = useState(); + + const handleRightClick = useCallback( + (e: React.MouseEvent, { originalData }: TableRow) => { + e.preventDefault(); + const x = e.clientX, + y = e.clientY; + setColumnPosition({ x, y }); + document.body.style.overflow = contextMenuOpen ? 'auto' : 'hidden'; + setContextMenuOpen(!contextMenuOpen); + setRowData(originalData as MDLTableData); + }, + [contextMenuOpen] + ); + + useImperativeHandle(ref, () => ({ + onRowContextMenu(e, row) { + handleRightClick(e, row); + }, + })); + + const handleFilterClick = useCallback(() => { + const filter = `domain:${rowData?.domain}`; + + // @ts-ignore + if (chrome.devtools.panels?.network?.show && rowData?.domain) { + // @ts-ignore + chrome.devtools.panels.network.show({ filter }); + setContextMenuOpen(false); + return; + } + + try { + // Need to do this since chrome doesnt allow the clipboard access in extension. + const copyFrom = document.createElement('textarea'); + copyFrom.textContent = filter; + document.body.appendChild(copyFrom); + copyFrom.select(); + document.execCommand('copy'); + copyFrom.blur(); + document.body.removeChild(copyFrom); + setContextMenuOpen(false); + } catch (error) { + //Fail silently + } + }, [rowData]); + + return ( + <> + {rowData?.domain && + rowData?.highlighted && + contextMenuOpen && + createPortal( +
+
+ +
+
setContextMenuOpen(false)} + className="absolute w-screen h-screen z-10 top-0 left-0" + /> +
, + document.body + )} + + ); +}); + +export default RowContextMenuForScriptBlocking; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel/index.tsx new file mode 100644 index 0000000000..6e812de54c --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/panel/index.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { LandingPage, useTabs } from '@google-psat/design-system'; + +const Panel = () => { + const { panel } = useTabs(({ state, actions }) => ({ + panel: state.panel, + highlightTab: actions.highlightTab, + })); + + const ActiveTabContent = panel.Element; + const { props, className, containerClassName } = panel; + + return ( + + +
+ ) + } + extraClasses={containerClassName} + {...props} + /> + ); +}; + +export default Panel; diff --git a/packages/extension/src/view/devtools/stateProviders/index.ts b/packages/extension/src/view/devtools/stateProviders/index.ts index 34d0f5b772..e48b6304f7 100644 --- a/packages/extension/src/view/devtools/stateProviders/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/index.ts @@ -21,4 +21,5 @@ export * from './webStories'; export * from './attributionReporting'; export * from './prebid'; export * from './topicsClassifier'; +export * from './scriptBlocking'; export * from './probabilisticRevealTokens'; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts index 9aba9b21e2..00cf66f81e 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts @@ -24,34 +24,24 @@ import { type PRTMetadata, } from '@google-psat/common'; -export type ScriptBlockingData = { - [origin: string]: { - domain: string; - owner: string; - scriptBlocking: string; - }; -}; - -export interface IPProxyContextType { +export interface ProbabilisticRevealTokensContextType { state: { plainTextTokens: UniquePlainTextToken[]; decryptedTokens: UniqueDecryptedToken[]; prtTokens: ProbablisticRevealToken[]; perTokenMetadata: PRTMetadata[]; - scriptBlockingData: ScriptBlockingData; - uniqueResponseDomains: string[]; }; } -const initialState: IPProxyContextType = { +const initialState: ProbabilisticRevealTokensContextType = { state: { plainTextTokens: [], decryptedTokens: [], prtTokens: [], perTokenMetadata: [], - scriptBlockingData: {}, - uniqueResponseDomains: [], }, }; -export default createContext(initialState); +export default createContext( + initialState +); diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts index a766cf335c..9bca1004f1 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/index.ts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default as IPProxyProvider } from './ipProxyContextTypeProvider'; -export { default as IPProxyContext, type IPProxyContextType } from './context'; -export { default as useIPProxy } from './useIPProxy'; +export { default as ProbabilisticRevealTokensProvider } from './probabilisticRevealTokensProvider'; +export { + default as ProbabilisticRevealTokens, + type ProbabilisticRevealTokensContextType, +} from './context'; +export { default as useProbabilisticRevealTokens } from './useProbabilisticRevealTokens'; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx similarity index 53% rename from packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx rename to packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx index 27eeaadf24..d7c912f6b0 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/ipProxyContextTypeProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx @@ -23,61 +23,40 @@ import React, { useCallback, useMemo, } from 'react'; -import type { - ProbablisticRevealToken, - PRTMetadata, - UniqueDecryptedToken, - UniquePlainTextToken, -} from '@google-psat/common'; import { isEqual } from 'lodash-es'; /** * Internal dependencies. */ -import Context, { - type IPProxyContextType, - type ScriptBlockingData, -} from './context'; -import { EXTRA_DATA, TAB_TOKEN_DATA } from '../../../../constants'; +import Context, { type ProbabilisticRevealTokensContextType } from './context'; +import { TAB_TOKEN_DATA } from '../../../../constants'; const Provider = ({ children }: PropsWithChildren) => { const [decryptedTokens, setDecryptedTokens] = useState< - IPProxyContextType['state']['decryptedTokens'] + ProbabilisticRevealTokensContextType['state']['decryptedTokens'] >([]); const [prtTokens, setPrtTokens] = useState< - IPProxyContextType['state']['prtTokens'] + ProbabilisticRevealTokensContextType['state']['prtTokens'] >([]); const [plainTextTokens, setPlainTextTokens] = useState< - IPProxyContextType['state']['plainTextTokens'] + ProbabilisticRevealTokensContextType['state']['plainTextTokens'] >([]); const [perTokenMetadata, setPerTokenMetadata] = useState< - IPProxyContextType['state']['perTokenMetadata'] + ProbabilisticRevealTokensContextType['state']['perTokenMetadata'] >([]); - const [uniqueResponseDomains, setUniqueResponseDomains] = useState( - [] - ); - const [scriptBlockingData, setScriptBlockingData] = - useState({}); - const messagePassingListener = useCallback( (message: { type: string; payload: { tabId: string; - tokens: { - plainTextTokens: UniquePlainTextToken[]; - decryptedTokens: UniqueDecryptedToken[]; - prtTokens: ProbablisticRevealToken[]; - perTokenMetadata: PRTMetadata[]; - }; - uniqueResponseDomains: string[]; + tokens: ProbabilisticRevealTokensContextType['state']; }; }) => { - if (![TAB_TOKEN_DATA, EXTRA_DATA].includes(message.type)) { + if (![TAB_TOKEN_DATA].includes(message.type)) { return; } @@ -86,13 +65,13 @@ const Provider = ({ children }: PropsWithChildren) => { } if ( - message.payload.tabId.toString() !== + message.payload.tabId !== chrome.devtools.inspectedWindow.tabId.toString() ) { return; } - if (message.payload.tokens && TAB_TOKEN_DATA === message.type) { + if (message.payload.tokens) { setPrtTokens((prev) => { if (isEqual(prev, message.payload.tokens.prtTokens)) { return prev; @@ -121,14 +100,6 @@ const Provider = ({ children }: PropsWithChildren) => { return message.payload.tokens.perTokenMetadata; }); } - - if (EXTRA_DATA === message.type) { - setUniqueResponseDomains((prev) => { - return isEqual(message.payload.uniqueResponseDomains, prev) - ? prev - : message.payload.uniqueResponseDomains || []; - }); - } }, [] ); @@ -157,66 +128,6 @@ const Provider = ({ children }: PropsWithChildren) => { [] ); - useEffect(() => { - (async () => { - const data = await fetch( - 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md' - ); - - if (!data.ok) { - throw new Error(`HTTP error! status: ${data.status}`); - } - - const text = await data.text(); - - const lines = text - .split('\n') - .filter((line) => line.includes('|')) - .slice(2); - - const mdlData = lines.map((line) => - line.split('|').map((item) => item.trim()) - ); - - setScriptBlockingData(() => { - const _data = mdlData.reduce((acc, item: string[]) => { - let owner = item[1]; - - if (item[1].includes('PSL Domain')) { - owner = 'PSL Domain'; - } - - let scriptBlocking; - - switch (item[2] as string) { - case 'Not Impacted By Script Blocking': - scriptBlocking = 'None'; - break; - case 'Some URLs are Blocked': - scriptBlocking = 'Partial'; - break; - case 'Entire Domain Blocked': - scriptBlocking = 'Complete'; - break; - default: - scriptBlocking = 'None'; - break; - } - - acc[item[0] as string] = { - domain: item[0], - owner, - scriptBlocking, - }; - - return acc; - }, {} as ScriptBlockingData); - - return _data; - }); - })(); - }, []); - useEffect(() => { chrome.runtime?.onMessage?.addListener(messagePassingListener); chrome.webNavigation?.onCommitted?.addListener( @@ -231,25 +142,16 @@ const Provider = ({ children }: PropsWithChildren) => { }; }, [messagePassingListener, onCommittedNavigationListener]); - const memoisedValue: IPProxyContextType = useMemo(() => { + const memoisedValue: ProbabilisticRevealTokensContextType = useMemo(() => { return { state: { plainTextTokens, decryptedTokens, prtTokens, perTokenMetadata, - scriptBlockingData, - uniqueResponseDomains, }, }; - }, [ - uniqueResponseDomains, - scriptBlockingData, - plainTextTokens, - prtTokens, - perTokenMetadata, - decryptedTokens, - ]); + }, [plainTextTokens, prtTokens, perTokenMetadata, decryptedTokens]); return {children}; }; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts similarity index 64% rename from packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts rename to packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts index e48231072f..3a1f69d4c9 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useIPProxy.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/useProbabilisticRevealTokens.ts @@ -21,21 +21,24 @@ import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. */ -import Context, { type IPProxyContextType } from './context'; +import Context, { type ProbabilisticRevealTokensContextType } from './context'; -export function useIPProxy(): IPProxyContextType; -export function useIPProxy(selector: (state: IPProxyContextType) => T): T; +export function useProbabilisticRevealTokens(): ProbabilisticRevealTokensContextType; +export function useProbabilisticRevealTokens( + selector: (state: ProbabilisticRevealTokensContextType) => T +): T; /** * Cookie store hook. * @param selector Selector function to partially select state. * @returns selected part of the state */ -export function useIPProxy( - selector: (state: IPProxyContextType) => T | IPProxyContextType = (state) => - state +export function useProbabilisticRevealTokens( + selector: ( + state: ProbabilisticRevealTokensContextType + ) => T | ProbabilisticRevealTokensContextType = (state) => state ) { return useContextSelector(Context, selector); } -export default useIPProxy; +export default useProbabilisticRevealTokens; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts new file mode 100644 index 0000000000..fd48020487 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import { noop, createContext } from '@google-psat/common'; + +export interface ScriptBlockingStoreContext { + state: { + uniqueResponseDomains: string[]; + }; + actions: { + setUniqueResponseDomains: (newValue: string[]) => void; + }; +} + +const initialState: ScriptBlockingStoreContext = { + state: { + uniqueResponseDomains: [], + }, + actions: { + setUniqueResponseDomains: noop, + }, +}; + +export default createContext(initialState); diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts new file mode 100644 index 0000000000..122c8f96ea --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as ScriptBlockingProvider } from './scriptBlockingProvider'; +export { default as ScriptBlockingContext } from './context'; +export { default as useScriptBlocking } from './useScriptBlocking'; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx new file mode 100644 index 0000000000..50507d5033 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React, { + type PropsWithChildren, + useState, + useEffect, + useCallback, +} from 'react'; +import { isEqual } from 'lodash-es'; + +/** + * Internal dependencies. + */ +import Context from './context'; +import { EXTRA_DATA } from '../../../../constants'; + +const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { + const [uniqueResponseDomains, setUniqueResponseDomains] = useState( + [] + ); + + const messagePassingListener = useCallback( + (message: { + type: string; + payload: { + uniqueResponseDomains?: string[]; + tabId: string; + }; + }) => { + if ( + message.type !== EXTRA_DATA || + String(message.payload.tabId) !== + String(chrome.devtools.inspectedWindow.tabId) + ) { + return; + } + + setUniqueResponseDomains((prev) => { + return isEqual(message.payload.uniqueResponseDomains, prev) + ? prev + : message.payload.uniqueResponseDomains || []; + }); + }, + [] + ); + + useEffect(() => { + chrome.runtime?.onMessage?.addListener(messagePassingListener); + + return () => { + chrome.runtime?.onMessage?.removeListener(messagePassingListener); + }; + }, [messagePassingListener]); + + return ( + + {children} + + ); +}; + +export default ScriptBlockingProvider; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts new file mode 100644 index 0000000000..e5700ea79d --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/useScriptBlocking.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import { useContextSelector } from '@google-psat/common'; + +/** + * Internal dependencies. + */ +import Context, { type ScriptBlockingStoreContext } from './context'; + +export function useScriptBlocking(): ScriptBlockingStoreContext; +export function useScriptBlocking( + selector: (state: ScriptBlockingStoreContext) => T +): T; + +/** + * Script Blocking state provider hook. + * @param selector Selector function to partially select state. + * @returns selected part of the state + */ +export function useScriptBlocking( + selector: ( + state: ScriptBlockingStoreContext + ) => T | ScriptBlockingStoreContext = (state) => state +) { + return useContextSelector(Context, selector); +} + +export default useScriptBlocking; diff --git a/packages/extension/src/view/devtools/tabs.ts b/packages/extension/src/view/devtools/tabs.ts index f043f0a3da..07c59e69f6 100644 --- a/packages/extension/src/view/devtools/tabs.ts +++ b/packages/extension/src/view/devtools/tabs.ts @@ -50,6 +50,8 @@ import { SiteBoundariesIconWhite, DemosIcon, IncognitoIcon, + BlockIcon, + BlockIconWhite, } from '@google-psat/design-system'; import { I18n } from '@google-psat/i18n'; import { addUTMParams } from '@google-psat/common'; @@ -87,6 +89,7 @@ import { import HelpCenter from './pages/learning/helpCenter'; import Demos from './pages/learning/demos'; import Incognito from './pages/incognito'; +import ScriptBlocking from './pages/privacyProtection/scriptBlocking'; const TABS: SidebarItems = { [SIDEBAR_ITEMS_KEYS.PRIVACY_SANDBOX]: { @@ -344,6 +347,25 @@ const TABS: SidebarItems = { }, children: {}, }, + [SIDEBAR_ITEMS_KEYS.SCRIPT_BLOCKING]: { + title: () => 'Script Blocking', + panel: { + Element: ScriptBlocking, + }, + icon: { + Element: BlockIcon, + props: { + className: 'fill-granite-gray relative right-[3px]', + }, + }, + selectedIcon: { + Element: BlockIconWhite, + props: { + className: 'fill-bright-gray relative right-[3px]', + }, + }, + children: {}, + }, [SIDEBAR_ITEMS_KEYS.BOUNCE_TRACKING]: { title: () => I18n.getMessage('bounceTracking'), panel: { From 9875573bf141fa17a1c96d3d3fe5ba84b5d4266c Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 8 Sep 2025 12:30:20 +0530 Subject: [PATCH 16/98] Fix tests after revert --- packages/extension/src/view/devtools/tests/app.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/extension/src/view/devtools/tests/app.tsx b/packages/extension/src/view/devtools/tests/app.tsx index 3c56d83409..d058a1032b 100644 --- a/packages/extension/src/view/devtools/tests/app.tsx +++ b/packages/extension/src/view/devtools/tests/app.tsx @@ -33,7 +33,7 @@ import { useProtectedAudience, useSettings, usePrebid, - useIPProxy, + useProbabilisticRevealTokens, } from '../stateProviders'; // @ts-ignore // eslint-disable-next-line import/no-unresolved @@ -45,7 +45,7 @@ jest.mock('../stateProviders', () => ({ useSettings: jest.fn(), useProtectedAudience: jest.fn(), usePrebid: jest.fn(), - useIPProxy: jest.fn(), + useProbabilisticRevealTokens: jest.fn(), })); jest.mock( @@ -62,7 +62,8 @@ const mockUseTablePersistentSettingStore = const mockUseProtectedAudienceStore = useProtectedAudience as jest.Mock; const mockUseSettingsStore = useSettings as jest.Mock; const mockPrebidStore = usePrebid as jest.Mock; -const mockProbabilisticRevealTokensStore = useIPProxy as jest.Mock; +const mockProbabilisticRevealTokensStore = + useProbabilisticRevealTokens as jest.Mock; describe('App', () => { beforeAll(() => { From 4bb70fb9ca32f434ebadd189f5e77cbec871319c Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 8 Sep 2025 12:31:38 +0530 Subject: [PATCH 17/98] Fix tests --- .../src/components/tabs/tests/index.tsx | 5 ++++ .../src/components/tabs/useTabs/provider.tsx | 4 ++- .../components/tabs/useTabs/tests/index.tsx | 27 ++++++++++++------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/design-system/src/components/tabs/tests/index.tsx b/packages/design-system/src/components/tabs/tests/index.tsx index 761bb73d02..f3e586058f 100644 --- a/packages/design-system/src/components/tabs/tests/index.tsx +++ b/packages/design-system/src/components/tabs/tests/index.tsx @@ -35,6 +35,7 @@ const mockUseTabs = useTabs as jest.Mock; describe('Tabs', () => { it('should render', () => { const setActiveTab = jest.fn(); + const setActiveGroup = jest.fn(); mockUseTabs.mockReturnValue({ activeTab: 0, @@ -53,6 +54,7 @@ describe('Tabs', () => { ], }, setActiveTab, + setActiveGroup, isTabHighlighted: jest.fn((tab: number) => (tab === 0 ? 1 : 99)), shouldAddSpacer: jest.fn(() => false), getTabGroup: jest.fn((tab: number) => @@ -83,6 +85,7 @@ describe('Tabs', () => { it('should handle grouped tabs', () => { const setActiveTab = jest.fn(); + const setActiveGroup = jest.fn(); mockUseTabs.mockReturnValue({ activeTab: 0, @@ -111,6 +114,7 @@ describe('Tabs', () => { ], }, setActiveTab, + setActiveGroup, shouldAddSpacer: jest.fn(() => false), isTabHighlighted: jest.fn(() => false), isGroup: true, @@ -157,6 +161,7 @@ describe('Tabs', () => { ], }, setActiveTab, + setActiveGroup, shouldAddSpacer: jest.fn(() => false), isTabHighlighted: jest.fn(() => false), }); diff --git a/packages/design-system/src/components/tabs/useTabs/provider.tsx b/packages/design-system/src/components/tabs/useTabs/provider.tsx index bfe3b12fab..921c330393 100644 --- a/packages/design-system/src/components/tabs/useTabs/provider.tsx +++ b/packages/design-system/src/components/tabs/useTabs/provider.tsx @@ -55,7 +55,9 @@ export const TabsProvider = ({ } }, [isGroup, items]); - const [activeGroup, _setActiveGroup] = useState(null); + const [activeGroup, _setActiveGroup] = useState( + isGroup ? Object.keys(items)[0] ?? null : null + ); const [activeTab, setActiveTab] = useState(0); const groupItemsRef = useRef(groupedItems); diff --git a/packages/design-system/src/components/tabs/useTabs/tests/index.tsx b/packages/design-system/src/components/tabs/useTabs/tests/index.tsx index 7691145f6d..c3cab41bfa 100644 --- a/packages/design-system/src/components/tabs/useTabs/tests/index.tsx +++ b/packages/design-system/src/components/tabs/useTabs/tests/index.tsx @@ -134,15 +134,20 @@ describe('useTabs', () => { }; const GroupedTestingComponent = () => { - const { activeGroup, activeTab, setActiveTab, groupedTitles } = useTabs( - ({ state, actions }) => ({ - activeGroup: state.activeGroup, - activeTab: state.activeTab, - setActiveTab: actions.setActiveTab, - groupedTitles: state.groupedTitles, - shouldAddSpacer: actions.shouldAddSpacer, - }) - ); + const { + activeGroup, + activeTab, + setActiveTab, + setActiveGroup, + groupedTitles, + } = useTabs(({ state, actions }) => ({ + activeGroup: state.activeGroup, + activeTab: state.activeTab, + setActiveTab: actions.setActiveTab, + setActiveGroup: actions.setActiveGroup, + groupedTitles: state.groupedTitles, + shouldAddSpacer: actions.shouldAddSpacer, + })); return (
@@ -152,6 +157,7 @@ describe('useTabs', () => { className={`${ activeGroup === group ? 'active-group-button' : '' }`} + onClick={() => setActiveGroup(group)} > {group} @@ -214,6 +220,9 @@ describe('useTabs', () => { // Check that the active group is 'group-1' expect(screen.getByText('group-1')).toHaveClass('active-group-button'); + fireEvent.click(screen.getByText('group-2')); + expect(title3).toHaveClass('active'); + fireEvent.click(screen.getByText('title4')); expect(title4).toHaveClass('active'); From 0a743f3aaea3bc954d92e5747511f65e69b05e60 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 8 Sep 2025 13:57:43 +0530 Subject: [PATCH 18/98] fix: Remove unused properties and improve PRT metadata handling --- packages/common/src/prtToken.types.ts | 4 +- .../runtimeOnInstalledListener.ts | 25 ++++++++-- packages/extension/src/serviceWorker/index.ts | 50 +++++++++++++++---- packages/extension/src/store/PRTStore.ts | 1 - .../extension/src/utils/headerFunctions.ts | 4 ++ .../probabilisticRevealTokensProvider.tsx | 2 +- 6 files changed, 68 insertions(+), 18 deletions(-) diff --git a/packages/common/src/prtToken.types.ts b/packages/common/src/prtToken.types.ts index 68b103ac92..7098c940d0 100644 --- a/packages/common/src/prtToken.types.ts +++ b/packages/common/src/prtToken.types.ts @@ -29,7 +29,6 @@ interface DecryptedToken { interface PlaintTextToken { uint8Signal: Uint8Array; - humanReadableSignal: string; ordinal: Uint8Array; version: number; hmacValid: boolean; @@ -37,10 +36,9 @@ interface PlaintTextToken { export interface PRTMetadata { origin: string; - humanReadableSignal: string; decryptionKeyAvailable: boolean; prtHeader: string; - nonZeroUintsignal: boolean; + nonZeroUint8Signal: boolean; } export type UniquePlainTextToken = PlaintTextToken & { diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts index 9b5a77770c..698f27a5d4 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts @@ -31,22 +31,39 @@ export const runtimeOnInstalledListener = async ( await chrome.storage.sync.set({ isUsingCDP: false, isFirstTime: true, + prtStatistics: {}, + scriptBlocking: {}, }); await updateGlobalVariableAndAttachCDP(); } if (details.reason === 'update') { const preSetSettings = await chrome.storage.sync.get(); + const objectToInstantiate: { [key: string]: any } = { ...preSetSettings }; await updateGlobalVariableAndAttachCDP(); - if (Object.keys(preSetSettings).includes('isUsingCDP')) { + if ( + Object.keys(preSetSettings).includes('isUsingCDP') && + Object.keys(preSetSettings).includes('prtStatistics') && + Object.keys(preSetSettings).includes('scriptBlocking') + ) { return; } + if (!Object.keys(preSetSettings).includes('isUsingCDP')) { + objectToInstantiate['isUsingCDP'] = false; + } + + if (!Object.keys(preSetSettings).includes('prtStatistics')) { + objectToInstantiate['prtStatistics'] = {}; + } + + if (!Object.keys(preSetSettings).includes('scriptBlocking')) { + objectToInstantiate['scriptBlocking'] = {}; + } + await chrome.storage.sync.clear(); - await chrome.storage.sync.set({ - isUsingCDP: false, - }); + await chrome.storage.sync.set({ ...objectToInstantiate }); } }; diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index ccf7fb476d..001255dc64 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -362,18 +362,36 @@ chrome.debugger.onEvent.addListener((source, method, params) => { headers ); - const origin = - extractHeader('origin', headers) ?? isValidURL(createURL(headers)) - ? new URL(createURL(headers)).origin - : ''; + let origin = ''; - if (!prtHeader) { + if (extractHeader('origin', headers)) { + origin = extractHeader('origin', headers); + } else if ( + DataStore.requestIdToCDPURLMapping[tabId]?.[requestId]?.url && + isValidURL( + DataStore.requestIdToCDPURLMapping[tabId]?.[requestId]?.url + ) + ) { + origin = new URL( + DataStore.requestIdToCDPURLMapping[tabId]?.[requestId]?.url + ).origin; + } else if ( + createURL(headers) && + isValidURL(createURL(headers) ?? '') + ) { + origin = new URL(createURL(headers) ?? '').origin; + } + + if (!prtHeader || !origin) { return; } const prt = PRTStore.getTokenFromHeaderString(prtHeader); const decodedToken = await PRTStore.decryptTokenHeader(prtHeader); const plainTextToken = await PRTStore.getPlaintextToken(decodedToken); + const nonZeroUint8Signal = plainTextToken?.uint8Signal + ? !plainTextToken.uint8Signal.every((bit) => bit === 0) + : false; if ( prt && @@ -406,17 +424,31 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ...plainTextToken, prtHeader, }); + + chrome.storage.sync.get('prtStatistics', (result) => { + const totaltTokens = + result.prtStatistics?.[origin]?.totalTokens ?? 0; + const nonZeroSignal = + result.prtStatistics?.[origin]?.nonZeroSignal ?? 0; + + chrome.storage.sync.set({ + prtStatistics: { + ...(result.prtStatistics ?? {}), + [origin]: { + totaltTokens: totaltTokens + 1, + nonZeroSignal: nonZeroSignal + (nonZeroUint8Signal ? 1 : 0), + }, + }, + }); + }); } if (!PRTStore.tabTokens[tabId]?.perTokenMetadata?.[prtHeader]) { PRTStore.tabTokens[tabId].perTokenMetadata[prtHeader] = { prtHeader, - humanReadableSignal: plainTextToken?.humanReadableSignal ?? '', origin: isValidURL(origin) ? origin : '', decryptionKeyAvailable: Boolean(decodedToken), - nonZeroUintsignal: !plainTextToken?.uint8Signal.every( - (bit) => bit === 0 - ), + nonZeroUint8Signal, }; DataStore.tabs[tabId].newUpdatesPRT++; } diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 9ef6aaea36..f9f4bc1e6a 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -347,7 +347,6 @@ class PRTStore extends DataStore { version, ordinal, uint8Signal: signal, - humanReadableSignal: btoa(String.fromCharCode.apply(null, plaintext)), hmacValid, } as UniquePlainTextToken; } catch (e) { diff --git a/packages/extension/src/utils/headerFunctions.ts b/packages/extension/src/utils/headerFunctions.ts index 466223a1f2..1c8962aea8 100644 --- a/packages/extension/src/utils/headerFunctions.ts +++ b/packages/extension/src/utils/headerFunctions.ts @@ -41,5 +41,9 @@ export const createURL = (headers: Protocol.Network.Headers) => { const path = extractHeader(':path', headers); const port = extractHeader(':port', headers); + if (!authority || !scheme || !path) { + return null; + } + return `${scheme}://${authority}${port ? `:${port}` : ''}${path}`; }; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx index d7c912f6b0..2475f93e93 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx @@ -65,7 +65,7 @@ const Provider = ({ children }: PropsWithChildren) => { } if ( - message.payload.tabId !== + message.payload.tabId.toString() !== chrome.devtools.inspectedWindow.tabId.toString() ) { return; From d7b597e1181e3cabafb1c99a344bba1edb4c79b6 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 8 Sep 2025 14:07:20 +0530 Subject: [PATCH 19/98] feat: Add Masked Domain List tab with MDLTable component to IPProtection --- .../pages/privacyProtection/ipProtection/index.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx index 6b2ee43adc..9811704268 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx @@ -28,6 +28,7 @@ import { */ import Panel from './panel'; import ProbabilisticRevealTokens from './probabilisticRevealTokens'; +import MDLTable from './mdlTable'; const IPProtection = () => { const tabItems = useMemo( @@ -45,10 +46,19 @@ const IPProtection = () => { className: 'p-4', }, }, + { + title: 'Masked Domain List', + content: { + Element: MDLTable, + props: {}, + className: 'overflow-auto h-full', + containerClassName: 'h-full', + }, + }, ], Observability: [ { - title: 'Masked Domain List', + title: 'IP Proxying', content: { Element: ProbabilisticRevealTokens, className: 'overflow-auto h-full', From 66fd51205cef52ca9555c89a31d4a706d8038208 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 9 Sep 2025 18:04:54 +0530 Subject: [PATCH 20/98] feat: Enhance PRTStore with detailed statistics tracking and update logic --- .../chromeListeners/runtimeStartUpListener.ts | 13 ++++ packages/extension/src/serviceWorker/index.ts | 7 +- packages/extension/src/store/PRTStore.ts | 71 +++++++++++++++++++ .../src/store/utils/updateStatistics.ts | 60 ++++++++++++++++ 4 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 packages/extension/src/store/utils/updateStatistics.ts diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts index 9ce61d9779..b501ac671c 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts @@ -17,6 +17,7 @@ * Internal dependencies */ import { DataStore } from '../../store/dataStore'; +import PRTStore from '../../store/PRTStore'; import { setupIntervals } from './utils'; export const onStartUpListener = async () => { @@ -26,4 +27,16 @@ export const onStartUpListener = async () => { if (Object.keys(storage).includes('isUsingCDP')) { DataStore.globalIsUsingCDP = storage.isUsingCDP; } + + if (Object.keys(storage).includes('prtStatistics')) { + PRTStore.statistics.prtStatistics.globalView = { + ...storage?.prtStatistics, + }; + } + + if (Object.keys(storage).includes('prtStatistics')) { + PRTStore.statistics.scriptBlocking.globalView = { + ...storage?.scriptBlocking, + }; + } }; diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 001255dc64..b305febd22 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -33,6 +33,7 @@ import attachCDP from './attachCDP'; import readHeaderAndRegister from './readHeaderAndRegister'; import PRTStore from '../store/PRTStore'; import { createURL, extractHeader } from '../utils/headerFunctions'; +import updateStatistics from '../store/utils/updateStatistics'; const ALLOWED_EVENTS = [ 'Network.responseReceived', @@ -426,16 +427,18 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }); chrome.storage.sync.get('prtStatistics', (result) => { - const totaltTokens = + const totalTokens = result.prtStatistics?.[origin]?.totalTokens ?? 0; const nonZeroSignal = result.prtStatistics?.[origin]?.nonZeroSignal ?? 0; + updateStatistics(origin, nonZeroUint8Signal); + chrome.storage.sync.set({ prtStatistics: { ...(result.prtStatistics ?? {}), [origin]: { - totaltTokens: totaltTokens + 1, + totalTokens: totalTokens + 1, nonZeroSignal: nonZeroSignal + (nonZeroUint8Signal ? 1 : 0), }, }, diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index f9f4bc1e6a..55b53d53a0 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -62,6 +62,70 @@ type SingleTabTokens = { class PRTStore extends DataStore { tabTokens: TabToken = {}; + mdlData: { + [domain: string]: { + scriptBlockingScope: 'NONE' | 'PARTIAL' | 'COMPLETE'; + domain: string; + owner: string; + }; + } = {}; + + statistics: { + prtStatistics: { + globalView: { + [domain: string]: { + totalTokens: number; + nonZeroSignal: number; + }; + }; + localView: { + [domain: string]: { + totalTokens: number; + nonZeroSignal: number; + }; + }; + }; + scriptBlocking: { + globalView: { + scriptsStats: { + [domain: string]: { + totalScripts: number; + blockedScripts: number; + }; + }; + totalDomains: number; + blockedDomains: number; + }; + localView: { + scriptsStats: { + [domain: string]: { + totalScripts: number; + blockedScripts: number; + }; + }; + totalDomains: number; + blockedDomains: number; + }; + }; + } = { + prtStatistics: { + globalView: {}, + localView: {}, + }, + scriptBlocking: { + globalView: { + scriptsStats: {}, + totalDomains: 0, + blockedDomains: 0, + }, + localView: { + scriptsStats: {}, + totalDomains: 0, + blockedDomains: 0, + }, + }, + }; + constructor() { super(); } @@ -93,11 +157,18 @@ class PRTStore extends DataStore { prtTokens: [], perTokenMetadata: {}, }; + this.statistics.prtStatistics.localView = {}; + this.statistics.scriptBlocking.localView = { + scriptsStats: {}, + totalDomains: 0, + blockedDomains: 0, + }; //@ts-ignore globalThis.PSAT = { //@ts-ignore ...globalThis.PSAT, tabTokens: this.tabTokens, + statistics: this.statistics, }; } diff --git a/packages/extension/src/store/utils/updateStatistics.ts b/packages/extension/src/store/utils/updateStatistics.ts new file mode 100644 index 0000000000..fb3240ec0f --- /dev/null +++ b/packages/extension/src/store/utils/updateStatistics.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Internal dependencies + */ + +import PRTStore from '../PRTStore'; + +const updateStatistics = (origin: string, nonZeroUint8Signal: boolean) => { + if (PRTStore.statistics.prtStatistics.localView[origin]) { + PRTStore.statistics.prtStatistics.localView[origin] = { + totalTokens: + (PRTStore.statistics.prtStatistics.localView[origin]?.totalTokens ?? + 0) + 1, + nonZeroSignal: + (PRTStore.statistics.prtStatistics.localView[origin]?.nonZeroSignal ?? + 0) + (nonZeroUint8Signal ? 1 : 0), + }; + } else { + PRTStore.statistics.prtStatistics.localView = { + [origin]: { + totalTokens: 1, + nonZeroSignal: nonZeroUint8Signal ? 1 : 0, + }, + }; + } + + if (PRTStore.statistics.prtStatistics.globalView[origin]) { + PRTStore.statistics.prtStatistics.globalView[origin] = { + totalTokens: + (PRTStore.statistics.prtStatistics.globalView[origin]?.totalTokens ?? + 0) + 1, + nonZeroSignal: + (PRTStore.statistics.prtStatistics.globalView[origin]?.nonZeroSignal ?? + 0) + (nonZeroUint8Signal ? 1 : 0), + }; + } else { + PRTStore.statistics.prtStatistics.globalView = { + [origin]: { + totalTokens: 1, + nonZeroSignal: nonZeroUint8Signal ? 1 : 0, + }, + }; + } +}; + +export default updateStatistics; From 49f3e894629441d21e1ad82d7bb66f70819fedf2 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 11 Sep 2025 16:55:04 +0530 Subject: [PATCH 21/98] Create dummy story for stats header --- .../src/components/statsHeader/index.tsx | 33 ++++++++++++++++ .../stories/StatsHeader.stories.tsx | 38 +++++++++++++++++++ .../components/statsHeader/tests/index.tsx | 33 ++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 packages/design-system/src/components/statsHeader/index.tsx create mode 100644 packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx create mode 100644 packages/design-system/src/components/statsHeader/tests/index.tsx diff --git a/packages/design-system/src/components/statsHeader/index.tsx b/packages/design-system/src/components/statsHeader/index.tsx new file mode 100644 index 0000000000..afe114bf08 --- /dev/null +++ b/packages/design-system/src/components/statsHeader/index.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +interface StatsHeaderProps { + title: string; +} + +const StatsHeader = ({ title }: StatsHeaderProps) => { + return ( +
+

{title}

+
+ ); +}; + +export default StatsHeader; diff --git a/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx b/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx new file mode 100644 index 0000000000..6f330e1903 --- /dev/null +++ b/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import type { Meta, StoryObj } from '@storybook/react'; + +/** + * Internal dependencies. + */ +import StatsHeader from '..'; + +const meta: Meta = { + title: 'DesignSystem/StatsHeader', + component: StatsHeader, + tags: ['autodocs'], +}; + +export default meta; + +export const Primary: StoryObj = { + args: { + title: 'Stats Header Title', + }, +}; diff --git a/packages/design-system/src/components/statsHeader/tests/index.tsx b/packages/design-system/src/components/statsHeader/tests/index.tsx new file mode 100644 index 0000000000..0437b976b3 --- /dev/null +++ b/packages/design-system/src/components/statsHeader/tests/index.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + +/** + * Internal dependencies. + */ +import StatsHeader from '..'; + +describe('StatsHeader', () => { + it('renders the text inside a button', () => { + render( undefined} text="Analyze this tab" />); + expect(screen.getByText('Analyze this tab')).toBeInTheDocument(); + }); +}); From 722ef8e86264fb16589442a70d31264550eac9a3 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 11 Sep 2025 17:03:32 +0530 Subject: [PATCH 22/98] Add stats circles --- .../src/components/statsHeader/index.tsx | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/design-system/src/components/statsHeader/index.tsx b/packages/design-system/src/components/statsHeader/index.tsx index afe114bf08..6485230846 100644 --- a/packages/design-system/src/components/statsHeader/index.tsx +++ b/packages/design-system/src/components/statsHeader/index.tsx @@ -18,15 +18,56 @@ */ import React from 'react'; +/** + * Internal dependencies. + */ +import CirclePieChart from '../circlePieChart'; + interface StatsHeaderProps { title: string; } const StatsHeader = ({ title }: StatsHeaderProps) => { return ( -
-

{title}

-
+
+ {title} +
+
+
+ + +
+
+
+
); }; From 1a59f717a8d87188e3dd4ae487e9625a315276aa Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 12 Sep 2025 07:53:02 +0530 Subject: [PATCH 23/98] Create separate component for stats header --- .../probabilisticRevealTokens/index.tsx | 40 +----------- .../ipProtection/stasHeader.tsx | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 37 deletions(-) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index b2514eae61..ab4b59d4bb 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -25,16 +25,17 @@ import { ResizableTray, JsonView, noop, - CirclePieChart, } from '@google-psat/design-system'; import React, { useMemo, useRef, useState } from 'react'; import { type PRTMetadata } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; + /** * Internal dependencies */ import { useProbabilisticRevealTokens } from '../../../../stateProviders'; import RowContextMenuForPRT from './rowContextMenu'; +import StatsHeader from '../stasHeader'; const ProbabilisticRevealTokens = () => { const [selectedJSON, setSelectedJSON] = useState(null); @@ -155,42 +156,7 @@ const ProbabilisticRevealTokens = () => { return (
-
-
-
- - -
-
-
+ { + return ( +
+
+
+ + +
+
+
+ ); +}; + +export default StatsHeader; From a8ff94ae8878f060d0fd9cb1b7e41a3f33209163 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 12 Sep 2025 10:24:25 +0530 Subject: [PATCH 24/98] Add pill toggle --- .../ipProtection/stasHeader.tsx | 103 ++++++++++++------ 1 file changed, 71 insertions(+), 32 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx index 5ef7c92cc4..ebb695c7cd 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx @@ -16,44 +16,83 @@ /** * External dependencies. */ -import React from 'react'; -import { CirclePieChart } from '@google-psat/design-system'; +import React, { useState } from 'react'; +import { CirclePieChart, PillToggle } from '@google-psat/design-system'; const StatsHeader = () => { + const [pillToggle, setPillToggle] = useState('Site'); + const [highlightOption, setHighlightOption] = useState('Site'); + + const sitePieCharts = ( + <> + + + + ); + + const globalPieCharts = ( + <> + + + + ); + return (
+
+ +
- - + {pillToggle === 'Site' ? sitePieCharts : globalPieCharts}
From 434c52abc5b44fa6294b4992a4992997411a763f Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 12 Sep 2025 11:39:28 +0530 Subject: [PATCH 25/98] fix: Normalize hostname by removing 'www.' prefix in DataStore --- packages/extension/src/store/dataStore.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 00edd860d1..5c8b220c39 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -91,7 +91,8 @@ export class DataStore { return; } - const hostname = new URL(request.url).hostname; + let hostname = new URL(request.url).hostname; + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; if ( hostname !== 'null' && From 7a5891c7010fdf43c1b9182ece9eccd1bc788bef Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 12 Sep 2025 15:29:00 +0530 Subject: [PATCH 26/98] Create common component for mdl table --- .../probabilisticRevealTokens/index.tsx | 77 ++-------- .../mdlCommonPanel/index.tsx | 122 ++++++++++++++++ .../mdlCommonPanel/rowContextMenu.tsx | 134 ++++++++++++++++++ .../scriptBlocking/mdlTable/index.tsx | 73 ++-------- .../{ipProtection => }/stasHeader.tsx | 0 5 files changed, 280 insertions(+), 126 deletions(-) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx rename packages/extension/src/view/devtools/pages/privacyProtection/{ipProtection => }/stasHeader.tsx (100%) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index ab4b59d4bb..43b05e7641 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -17,25 +17,15 @@ /** * External dependencies */ -import { - Table, - TableProvider, - type TableColumn, - type TableRow, - ResizableTray, - JsonView, - noop, -} from '@google-psat/design-system'; -import React, { useMemo, useRef, useState } from 'react'; +import { type TableColumn } from '@google-psat/design-system'; +import React, { useMemo, useState } from 'react'; import { type PRTMetadata } from '@google-psat/common'; -import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ import { useProbabilisticRevealTokens } from '../../../../stateProviders'; -import RowContextMenuForPRT from './rowContextMenu'; -import StatsHeader from '../stasHeader'; +import MdlCommonPanel from '../../mdlCommonPanel'; const ProbabilisticRevealTokens = () => { const [selectedJSON, setSelectedJSON] = useState(null); @@ -52,10 +42,6 @@ const ProbabilisticRevealTokens = () => { plainTextTokensData: state.plainTextTokens, })); - const rowContextMenuRef = useRef | null>(null); - const tableColumns = useMemo( () => [ { @@ -155,56 +141,13 @@ const ProbabilisticRevealTokens = () => { ]); return ( -
- - -
- setSelectedJSON(row as PRTMetadata)} - getRowObjectKey={(row: TableRow) => - (row.originalData as PRTMetadata).origin.toString() - } - onRowContextMenu={ - rowContextMenuRef.current?.onRowContextMenu ?? noop - } - > -
- - - - -
- {formedJson ? ( -
- -
- ) : ( -
-

- {I18n.getMessage('selectRowToPreview')} -

-
- )} -
- + setSelectedJSON(row as PRTMetadata)} + /> ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx new file mode 100644 index 0000000000..c16d9de037 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -0,0 +1,122 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import { + Table, + TableProvider, + type TableRow, + type TableFilter, + ResizableTray, + JsonView, + noop, +} from '@google-psat/design-system'; +import React, { useRef } from 'react'; +import { type PRTMetadata } from '@google-psat/common'; +import { I18n } from '@google-psat/i18n'; + +/** + * Internal dependencies + */ +import RowContextMenuForPRT from './rowContextMenu'; +import StatsHeader from '../stasHeader'; + +interface MdlCommonPanelProps { + formedJson: PRTMetadata | null; + tableColumns: Array<{ + header: string; + accessorKey: keyof PRTMetadata; + cell: (info: any) => any; + }>; + tableData: PRTMetadata[]; + selectedKey: string | null; + onRowClick: (row: PRTMetadata) => void; + extraInterfaceToTopBar?: React.ReactNode; + filters: TableFilter[]; +} + +const MdlCommonPanel = ({ + formedJson, + tableColumns, + tableData, + selectedKey, + onRowClick, + extraInterfaceToTopBar = undefined, + filters, +}: MdlCommonPanelProps) => { + const rowContextMenuRef = useRef | null>(null); + + return ( +
+ + +
+ + (row.originalData as PRTMetadata).origin.toString() + } + onRowContextMenu={ + rowContextMenuRef.current?.onRowContextMenu ?? noop + } + > +
+ + + + +
+ {formedJson ? ( +
+ +
+ ) : ( +
+

+ {I18n.getMessage('selectRowToPreview')} +

+
+ )} +
+ + ); +}; + +export default MdlCommonPanel; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx new file mode 100644 index 0000000000..3255138215 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx @@ -0,0 +1,134 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies + */ +import React, { + forwardRef, + useCallback, + useImperativeHandle, + useMemo, + useState, +} from 'react'; +import { createPortal } from 'react-dom'; +import { isValidURL, type PRTMetadata } from '@google-psat/common'; +import type { TableRow } from '@google-psat/design-system'; +import { I18n } from '@google-psat/i18n'; + +const RowContextMenuForPRT = forwardRef<{ + onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; +}>(function RowContextMenu(_, ref) { + const [contextMenuOpen, setContextMenuOpen] = useState(false); + const [columnPosition, setColumnPosition] = useState({ + x: 0, + y: 0, + }); + const [prtMetaData, setMetadata] = useState(); + + const domain = useMemo( + () => + prtMetaData?.origin && isValidURL(prtMetaData?.origin) + ? new URL(prtMetaData?.origin).hostname + : '', + [prtMetaData] + ); + + const handleRightClick = useCallback( + (e: React.MouseEvent, { originalData }: TableRow) => { + e.preventDefault(); + const x = e.clientX, + y = e.clientY; + setColumnPosition({ x, y }); + document.body.style.overflow = contextMenuOpen ? 'auto' : 'hidden'; + setContextMenuOpen(!contextMenuOpen); + setMetadata(originalData as PRTMetadata); + }, + [contextMenuOpen] + ); + + useImperativeHandle(ref, () => ({ + onRowContextMenu(e, row) { + handleRightClick(e, row); + }, + })); + + const handleFilterClick = useCallback(() => { + const filter = `has-request-header:sec-probabilistic-reveal-token domain:${domain}`; + + // @ts-ignore + if (chrome.devtools.panels?.network?.show) { + // @ts-ignore + chrome.devtools.panels.network.show({ filter }); + setContextMenuOpen(false); + return; + } + + try { + // Need to do this since chrome doesnt allow the clipboard access in extension. + const copyFrom = document.createElement('textarea'); + copyFrom.textContent = filter; + document.body.appendChild(copyFrom); + copyFrom.select(); + document.execCommand('copy'); + copyFrom.blur(); + document.body.removeChild(copyFrom); + setContextMenuOpen(false); + } catch (error) { + //Fail silently + } + }, [domain]); + + return ( + <> + {domain && + contextMenuOpen && + createPortal( +
+
+ +
+
setContextMenuOpen(false)} + className="absolute w-screen h-screen z-10 top-0 left-0" + /> +
, + document.body + )} + + ); +}); + +export default RowContextMenuForPRT; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 3716b9d1c3..a13e234e96 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -18,30 +18,19 @@ * External dependencies */ import { - noop, ProgressBar, - Table, - TableProvider, type TableFilter, type TableColumn, - type TableRow, Link, - ResizableTray, } from '@google-psat/design-system'; -import React, { - useEffect, - useMemo, - useRef, - useState, - useCallback, -} from 'react'; +import React, { useEffect, useMemo, useState, useCallback } from 'react'; import type { MDLTableData } from '@google-psat/common'; + /** * Internal dependencies */ -import Legend from './legend'; import { useScriptBlocking } from '../../../../stateProviders'; -import RowContextMenuForScriptBlocking from './rowContextMenu'; +import MdlCommonPanel from '../../mdlCommonPanel'; export const IMPACTED_BY_SCRIPT_BLOCKING = { NONE: 'Not Impacted By Script Blocking', @@ -60,10 +49,6 @@ const MDLTable = () => { uniqueResponseDomains: state.uniqueResponseDomains, })); - const rowContextMenuRef = useRef | null>(null); - const [initialTableData, setinitialTableData] = useState< { domain: string; owner: string; scriptBlocking: string }[] >([]); @@ -227,47 +212,17 @@ const MDLTable = () => { } return ( -
- { - setSelectedKey((rowData as MDLTableData)?.domain || null); - }} - onRowContextMenu={ - rowContextMenuRef.current - ? rowContextMenuRef.current?.onRowContextMenu - : noop - } - getRowObjectKey={(row: TableRow) => - (row.originalData as MDLTableData).domain || '' - } - tablePersistentSettingsKey="mdlTable" - > - -
- - - - - + + setSelectedKey((row as MDLTableData)?.domain || null) + } + extraInterfaceToTopBar={checkbox} + filters={filters} + /> ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/stasHeader.tsx similarity index 100% rename from packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/stasHeader.tsx rename to packages/extension/src/view/devtools/pages/privacyProtection/stasHeader.tsx From 003639b9427b0daeebb3739a8038137df1668639 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 12 Sep 2025 16:03:44 +0530 Subject: [PATCH 27/98] Add configuration for stats --- .../src/components/statsHeader/index.tsx | 74 ------------------ .../stories/StatsHeader.stories.tsx | 38 --------- .../components/statsHeader/tests/index.tsx | 33 -------- .../probabilisticRevealTokens/index.tsx | 28 +++++++ .../mdlCommonPanel/index.tsx | 6 +- .../{ => mdlCommonPanel}/stasHeader.tsx | 78 +++++++------------ .../scriptBlocking/mdlTable/index.tsx | 28 +++++++ 7 files changed, 88 insertions(+), 197 deletions(-) delete mode 100644 packages/design-system/src/components/statsHeader/index.tsx delete mode 100644 packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx delete mode 100644 packages/design-system/src/components/statsHeader/tests/index.tsx rename packages/extension/src/view/devtools/pages/privacyProtection/{ => mdlCommonPanel}/stasHeader.tsx (60%) diff --git a/packages/design-system/src/components/statsHeader/index.tsx b/packages/design-system/src/components/statsHeader/index.tsx deleted file mode 100644 index 6485230846..0000000000 --- a/packages/design-system/src/components/statsHeader/index.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React from 'react'; - -/** - * Internal dependencies. - */ -import CirclePieChart from '../circlePieChart'; - -interface StatsHeaderProps { - title: string; -} - -const StatsHeader = ({ title }: StatsHeaderProps) => { - return ( -
- {title} -
-
-
- - -
-
-
-
- ); -}; - -export default StatsHeader; diff --git a/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx b/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx deleted file mode 100644 index 6f330e1903..0000000000 --- a/packages/design-system/src/components/statsHeader/stories/StatsHeader.stories.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import type { Meta, StoryObj } from '@storybook/react'; - -/** - * Internal dependencies. - */ -import StatsHeader from '..'; - -const meta: Meta = { - title: 'DesignSystem/StatsHeader', - component: StatsHeader, - tags: ['autodocs'], -}; - -export default meta; - -export const Primary: StoryObj = { - args: { - title: 'Stats Header Title', - }, -}; diff --git a/packages/design-system/src/components/statsHeader/tests/index.tsx b/packages/design-system/src/components/statsHeader/tests/index.tsx deleted file mode 100644 index 0437b976b3..0000000000 --- a/packages/design-system/src/components/statsHeader/tests/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; - -/** - * Internal dependencies. - */ -import StatsHeader from '..'; - -describe('StatsHeader', () => { - it('renders the text inside a button', () => { - render( undefined} text="Analyze this tab" />); - expect(screen.getByText('Analyze this tab')).toBeInTheDocument(); - }); -}); diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 43b05e7641..1195b4f8a3 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -140,6 +140,33 @@ const ProbabilisticRevealTokens = () => { selectedJSON, ]); + const stats = { + site: [ + { + title: 'PRTs with Signal', + centerCount: 4, + color: '#AF7AA3', + }, + { + title: 'PRTs without Signal', + centerCount: 3, + color: '#F54021', + }, + ], + global: [ + { + title: 'PRTs with Signals', + centerCount: 2, + color: '#AF7AA3', + }, + { + title: 'PRTs without Signals', + centerCount: 1, + color: '#F54021', + }, + ], + }; + return ( { tableData={perTokenMetadata} selectedKey={selectedJSON?.origin.toString()} onRowClick={(row) => setSelectedJSON(row as PRTMetadata)} + stats={stats} /> ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index c16d9de037..7c231e356b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -34,7 +34,7 @@ import { I18n } from '@google-psat/i18n'; * Internal dependencies */ import RowContextMenuForPRT from './rowContextMenu'; -import StatsHeader from '../stasHeader'; +import StatsHeader, { type Stats } from './stasHeader'; interface MdlCommonPanelProps { formedJson: PRTMetadata | null; @@ -48,6 +48,7 @@ interface MdlCommonPanelProps { onRowClick: (row: PRTMetadata) => void; extraInterfaceToTopBar?: React.ReactNode; filters: TableFilter[]; + stats: Stats; } const MdlCommonPanel = ({ @@ -58,6 +59,7 @@ const MdlCommonPanel = ({ onRowClick, extraInterfaceToTopBar = undefined, filters, + stats, }: MdlCommonPanelProps) => { const rowContextMenuRef = useRef - + { +type StatItem = { + title: string; + centerCount: number; + color: string; +}; + +export type Stats = { + site: StatItem[]; + global: StatItem[]; +}; + +interface StatsHeaderProps { + stats: Stats; +} + +const StatsHeader = ({ stats }: StatsHeaderProps) => { const [pillToggle, setPillToggle] = useState('Site'); const [highlightOption, setHighlightOption] = useState('Site'); - const sitePieCharts = ( + const renderPieCharts = (items: StatItem[]) => ( <> - - + {items.map(({ title, centerCount, color }) => ( + + ))} ); - const globalPieCharts = ( - <> - - - - ); + const sitePieCharts = renderPieCharts(stats.site); + const globalPieCharts = renderPieCharts(stats.global); return (
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index a13e234e96..f2fcbce8c9 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -203,6 +203,33 @@ const MDLTable = () => { [] ); + const stats = { + site: [ + { + title: 'Competely Blocked', + centerCount: 5, + color: '#F3AE4E', + }, + { + title: 'Partially Blocked', + centerCount: 8, + color: '#4C79F4', + }, + ], + global: [ + { + title: 'Competely Blocked', + centerCount: 1, + color: '#F3AE4E', + }, + { + title: 'Partially Blocked', + centerCount: 9, + color: '#4C79F4', + }, + ], + }; + if (isLoading) { return (
@@ -222,6 +249,7 @@ const MDLTable = () => { } extraInterfaceToTopBar={checkbox} filters={filters} + stats={stats} /> ); }; From 2ed63e4d61d21ce068440fc01c3db93c4c87a8bf Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 12 Sep 2025 19:33:39 +0530 Subject: [PATCH 28/98] feat: Add statistics tracking for tokens in PRTStore and context provider --- packages/extension/src/store/PRTStore.ts | 33 +++++++++++++++++++ .../probabilisticRevealTokens/context.ts | 24 +++++++++++++- .../probabilisticRevealTokensProvider.tsx | 28 ++++++++++++++-- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 55b53d53a0..c14c33f59d 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -190,11 +190,44 @@ class PRTStore extends DataStore { ...this.tabTokens[tabId], perTokenMetadata: Object.values(this.tabTokens[tabId].perTokenMetadata), }; + + //@ts-ignore + const { prtStatistics = {} } = chrome.storage.sync.get('prtStatistics'); + + const localStats = this.tabTokens[tabId].plainTextTokens.reduce( + (acc, token) => { + const isZeroSignal = token.uint8Signal.every((bit) => bit === 0); + acc.totalTokens += 1; + + if (!isZeroSignal) { + acc.nonZeroTokens += 1; + } + + return acc; + }, + { totalTokens: 0, nonZeroTokens: 0 } + ); + + const globalStats = Object.keys(prtStatistics).reduce( + (acc, key) => { + if (prtStatistics[key]) { + acc.totalTokens = prtStatistics + acc.totalTokens; + acc.nonZeroTokens = prtStatistics + acc.nonZeroTokens; + } + return acc; + }, + { totalTokens: 0, nonZeroTokens: 0 } + ); + await chrome.runtime.sendMessage({ type: TAB_TOKEN_DATA, payload: { tabId, tokens: tokenData, + stats: { + globalView: globalStats, + localView: localStats, + }, }, }); diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts index 00cf66f81e..0489b83367 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts @@ -24,21 +24,43 @@ import { type PRTMetadata, } from '@google-psat/common'; +type PRTStatistics = { + globalView: { + totalTokens: number; + nonZeroTokens: number; + }; + localView: { + totalTokens: number; + nonZeroTokens: number; + }; +}; + export interface ProbabilisticRevealTokensContextType { state: { plainTextTokens: UniquePlainTextToken[]; decryptedTokens: UniqueDecryptedToken[]; prtTokens: ProbablisticRevealToken[]; perTokenMetadata: PRTMetadata[]; + statistics: PRTStatistics; }; } -const initialState: ProbabilisticRevealTokensContextType = { +export const initialState: ProbabilisticRevealTokensContextType = { state: { plainTextTokens: [], decryptedTokens: [], prtTokens: [], perTokenMetadata: [], + statistics: { + localView: { + totalTokens: 0, + nonZeroTokens: 0, + }, + globalView: { + totalTokens: 0, + nonZeroTokens: 0, + }, + }, }, }; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx index 2475f93e93..c0254e5603 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx @@ -28,7 +28,10 @@ import { isEqual } from 'lodash-es'; /** * Internal dependencies. */ -import Context, { type ProbabilisticRevealTokensContextType } from './context'; +import Context, { + initialState, + type ProbabilisticRevealTokensContextType, +} from './context'; import { TAB_TOKEN_DATA } from '../../../../constants'; const Provider = ({ children }: PropsWithChildren) => { @@ -36,6 +39,10 @@ const Provider = ({ children }: PropsWithChildren) => { ProbabilisticRevealTokensContextType['state']['decryptedTokens'] >([]); + const [statistics, setStatistics] = useState< + ProbabilisticRevealTokensContextType['state']['statistics'] + >(initialState.state.statistics); + const [prtTokens, setPrtTokens] = useState< ProbabilisticRevealTokensContextType['state']['prtTokens'] >([]); @@ -54,6 +61,7 @@ const Provider = ({ children }: PropsWithChildren) => { payload: { tabId: string; tokens: ProbabilisticRevealTokensContextType['state']; + stats: ProbabilisticRevealTokensContextType['state']['statistics']; }; }) => { if (![TAB_TOKEN_DATA].includes(message.type)) { @@ -100,6 +108,15 @@ const Provider = ({ children }: PropsWithChildren) => { return message.payload.tokens.perTokenMetadata; }); } + + if (message.payload.stats) { + setStatistics((prev) => { + if (isEqual(prev, message.payload.stats)) { + return prev; + } + return message.payload.stats; + }); + } }, [] ); @@ -149,9 +166,16 @@ const Provider = ({ children }: PropsWithChildren) => { decryptedTokens, prtTokens, perTokenMetadata, + statistics, }, }; - }, [plainTextTokens, prtTokens, perTokenMetadata, decryptedTokens]); + }, [ + plainTextTokens, + decryptedTokens, + prtTokens, + perTokenMetadata, + statistics, + ]); return {children}; }; From 38934ec4ce3cd0a5e39c20ad5484ce7704ae3514 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 12 Sep 2025 20:59:32 +0530 Subject: [PATCH 29/98] feat: Enhance script blocking statistics tracking and update response domain handling --- packages/extension/src/serviceWorker/index.ts | 2 +- packages/extension/src/store/PRTStore.ts | 227 +++++++++++++++--- packages/extension/src/store/dataStore.ts | 57 +---- .../probabilisticRevealTokens/index.tsx | 14 +- .../mdlCommonPanel/index.tsx | 43 ++-- .../scriptBlocking/mdlTable/index.tsx | 20 +- .../stateProviders/scriptBlocking/context.ts | 24 +- .../scriptBlocking/scriptBlockingProvider.tsx | 20 +- 8 files changed, 291 insertions(+), 116 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index b305febd22..8f62ff38f2 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -558,7 +558,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }; } - dataStore.updateUniqueResponseDomains(tabId, requestId); + PRTStore.updateUniqueResponseDomains(tabId, requestId); if (cookieStore.getUnParsedResponseHeadersForCA(tabId)?.[requestId]) { cookieStore.parseResponseHeadersForCA( diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index c14c33f59d..b04ad75f65 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -16,11 +16,12 @@ /** * External dependencies. */ -import type { - UniqueDecryptedToken, - UniquePlainTextToken, - ProbablisticRevealToken, - PRTMetadata, +import { + type UniqueDecryptedToken, + type UniquePlainTextToken, + type ProbablisticRevealToken, + type PRTMetadata, + isValidURL, } from '@google-psat/common'; import { ec as ellipticEc } from 'elliptic'; @@ -28,7 +29,7 @@ import { ec as ellipticEc } from 'elliptic'; * Internal dependencies. */ import { DataStore } from './dataStore'; -import { TAB_TOKEN_DATA } from '../constants'; +import { EXTRA_DATA, TAB_TOKEN_DATA } from '../constants'; const PRT_SIZE = 79; const PRT_POINT_SIZE = 33; @@ -87,24 +88,12 @@ class PRTStore extends DataStore { }; scriptBlocking: { globalView: { - scriptsStats: { - [domain: string]: { - totalScripts: number; - blockedScripts: number; - }; - }; - totalDomains: number; - blockedDomains: number; + partiallyBlockedDomains: number; + completelyBlockedDomains: number; }; localView: { - scriptsStats: { - [domain: string]: { - totalScripts: number; - blockedScripts: number; - }; - }; - totalDomains: number; - blockedDomains: number; + partiallyBlockedDomains: number; + completelyBlockedDomains: number; }; }; } = { @@ -114,20 +103,79 @@ class PRTStore extends DataStore { }, scriptBlocking: { globalView: { - scriptsStats: {}, - totalDomains: 0, - blockedDomains: 0, + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, }, localView: { - scriptsStats: {}, - totalDomains: 0, - blockedDomains: 0, + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, }, }, }; + uniqueResponseDomains: { + [tabId: string]: string[]; + } = {}; + constructor() { super(); + + (async () => { + const data = await fetch( + 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md' + ); + + if (!data.ok) { + throw new Error(`HTTP error! status: ${data.status}`); + } + + const text = await data.text(); + + const lines = text + .split('\n') + .filter((line) => line.includes('|')) + .slice(2); + + const mdlData = lines.map((line) => + line.split('|').map((item) => item.trim()) + ); + + const _data = mdlData.reduce((acc, item: string[]) => { + let owner = item[1]; + + if (item[1].includes('PSL Domain')) { + owner = 'PSL Domain'; + } + + let scriptBlocking = ''; + + switch (item[2]) { + case 'Not Impacted By Script Blocking': + scriptBlocking = 'NONE'; + break; + case 'Some URLs are Blocked': + scriptBlocking = 'PARTIAL'; + break; + case 'Entire Domain Blocked': + scriptBlocking = 'COMPLETE'; + break; + default: + break; + } + + if (!acc[item[0]]) { + acc[item[0]] = { + domain: item[0], + owner, + scriptBlockingScope: scriptBlocking, + }; + } + + return acc; + }, {} as { [key: string]: any }); + + this.mdlData = _data; + })(); } clear(): void { @@ -137,6 +185,59 @@ class PRTStore extends DataStore { }); } + updateUniqueResponseDomains(tabId: string, requestId: string) { + if (!DataStore.requestIdToCDPURLMapping[tabId]) { + return; + } + + const request = DataStore.requestIdToCDPURLMapping[tabId][requestId]; + + if ( + !request || + !isValidURL(request.url) || + request.url.startsWith('chrome://') || + request.url.startsWith('chrome-extension://') || + request.url.startsWith('file://') + ) { + return; + } + + let hostname = new URL(request.url).hostname; + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + + if ( + hostname !== 'null' && + !this.uniqueResponseDomains[tabId].includes(hostname) + ) { + this.uniqueResponseDomains[tabId].push(hostname); + DataStore.tabs[tabId].newUpdatesScriptBlocking++; + + chrome.storage.sync.get('scriptBlocking', (result) => { + const completelyBlockedDomains = + result.scriptBlocking?.[origin]?.completelyBlockedDomains ?? 0; + const partiallyBlockedDomains = + result.prtStatistics?.[origin]?.partiallyBlockedDomains ?? 0; + + chrome.storage.sync.set({ + scriptBlocking: { + completelyBlockedDomains: + completelyBlockedDomains + + (this.mdlData[hostname] && + this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' + ? 1 + : 0), + partiallyBlockedDomais: + partiallyBlockedDomains + + (this.mdlData[hostname] && + this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' + ? 1 + : 0), + }, + }); + }); + } + } + getTabsData(tabId = ''): SingleTabTokens | typeof this.tabTokens { if (tabId) { return this.tabTokens[tabId]; @@ -147,6 +248,7 @@ class PRTStore extends DataStore { deinitialiseVariablesForTab(tabId: string): void { super.deinitialiseVariablesForTab(tabId); delete this.tabTokens[tabId]; + delete this.uniqueResponseDomains[parseInt(tabId)]; } initialiseVariablesForNewTab(tabId: string): void { @@ -159,10 +261,10 @@ class PRTStore extends DataStore { }; this.statistics.prtStatistics.localView = {}; this.statistics.scriptBlocking.localView = { - scriptsStats: {}, - totalDomains: 0, - blockedDomains: 0, + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, }; + this.uniqueResponseDomains[parseInt(tabId)] = []; //@ts-ignore globalThis.PSAT = { //@ts-ignore @@ -181,6 +283,11 @@ class PRTStore extends DataStore { tabId: string, overrideForInitialSync: boolean ) { + await this.processPRTData(tabId, overrideForInitialSync); + await this.processScriptBlockingData(tabId, overrideForInitialSync); + } + + async processPRTData(tabId: string, overrideForInitialSync: boolean) { try { if (DataStore.tabs[tabId].newUpdatesPRT <= 0 && !overrideForInitialSync) { return; @@ -192,7 +299,9 @@ class PRTStore extends DataStore { }; //@ts-ignore - const { prtStatistics = {} } = chrome.storage.sync.get('prtStatistics'); + const { prtStatistics = {} } = await chrome.storage.sync.get( + 'prtStatistics' + ); const localStats = this.tabTokens[tabId].plainTextTokens.reduce( (acc, token) => { @@ -236,6 +345,60 @@ class PRTStore extends DataStore { // Fail silently } } + + async processScriptBlockingData( + tabId: string, + overrideForInitialSync: boolean + ) { + if ( + overrideForInitialSync || + ((DataStore.tabs[tabId].devToolsOpenState || + DataStore.tabs[tabId].popupOpenState) && + DataStore.tabs[tabId].newUpdatesScriptBlocking > 0) + ) { + //@ts-ignore + const { scriptBlocking = {} } = await chrome.storage.sync.get( + 'scriptBlocking' + ); + + const globalView = scriptBlocking; + + const localView = this.uniqueResponseDomains[tabId].reduce( + (acc, domain) => { + if (!this.mdlData[domain]) { + return acc; + } + + if (this.mdlData[domain].scriptBlockingScope === 'COMPLETE') { + acc.completelyBlockedDomains += 1; + } + + if (this.mdlData[domain].scriptBlockingScope === 'PARTIAL') { + acc.partiallyBlockedDomains += 1; + } + + return acc; + }, + { + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, + } + ); + + await chrome.runtime.sendMessage({ + type: EXTRA_DATA, + payload: { + uniqueResponseDomains: this.uniqueResponseDomains[tabId], + stats: { + globalView, + localView, + }, + tabId: Number(tabId), + }, + }); + DataStore.tabs[tabId].newUpdatesScriptBlocking = 0; + } + } /** * Deserializes a raw byte buffer into a PRT object, performing structural validation. * @param {ArrayBuffer} serializedPrt The raw byte array representing the serialized PRT. diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 5c8b220c39..fa89d03086 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -23,7 +23,6 @@ import { isValidURL } from '@google-psat/common'; * Internal dependencies. */ import { doesFrameExist } from '../utils/doesFrameExist'; -import { EXTRA_DATA } from '../constants'; export class DataStore { /** @@ -74,64 +73,14 @@ export class DataStore { }; } = {}; - updateUniqueResponseDomains(tabId: string, requestId: string) { - if (!DataStore.requestIdToCDPURLMapping[tabId]) { - return; - } - - const request = DataStore.requestIdToCDPURLMapping[tabId][requestId]; - - if ( - !request || - !isValidURL(request.url) || - request.url.startsWith('chrome://') || - request.url.startsWith('chrome-extension://') || - request.url.startsWith('file://') - ) { - return; - } - - let hostname = new URL(request.url).hostname; - hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; - - if ( - hostname !== 'null' && - !DataStore.tabs[tabId].uniqueResponseDomains.includes(hostname) - ) { - DataStore.tabs[tabId].uniqueResponseDomains.push(hostname); - DataStore.tabs[tabId].newUpdatesScriptBlocking++; - } - } - - async sendUpdatedDataToPopupAndDevTools( + sendUpdatedDataToPopupAndDevTools( tabId: string, - overrideForInitialSync = false + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _overrideForInitialSync = false ) { if (!DataStore.tabs[tabId]) { return; } - - try { - if ( - overrideForInitialSync || - ((DataStore.tabs[tabId].devToolsOpenState || - DataStore.tabs[tabId].popupOpenState) && - DataStore.tabs[tabId].newUpdatesScriptBlocking > 0) - ) { - await chrome.runtime.sendMessage({ - type: EXTRA_DATA, // For sending extra data. - payload: { - uniqueResponseDomains: DataStore.tabs[tabId].uniqueResponseDomains, - tabId: Number(tabId), - }, - }); - DataStore.tabs[tabId].newUpdatesScriptBlocking = 0; - } - } catch (error) { - // eslint-disable-next-line no-console - console.warn(error); - //Fail silently. Ignoring the console.warn here because the only error this will throw is of "Error: Could not establish connection". - } } /** diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 1195b4f8a3..5aa84a3126 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -35,11 +35,13 @@ const ProbabilisticRevealTokens = () => { decryptedTokensData, prtTokensData, plainTextTokensData, + statistics, } = useProbabilisticRevealTokens(({ state }) => ({ perTokenMetadata: state.perTokenMetadata, decryptedTokensData: state.decryptedTokens, prtTokensData: state.prtTokens, plainTextTokensData: state.plainTextTokens, + statistics: state.statistics, })); const tableColumns = useMemo( @@ -144,24 +146,27 @@ const ProbabilisticRevealTokens = () => { site: [ { title: 'PRTs with Signal', - centerCount: 4, + centerCount: statistics.localView.nonZeroTokens, color: '#AF7AA3', }, { title: 'PRTs without Signal', - centerCount: 3, + centerCount: + statistics.localView.totalTokens - statistics.localView.nonZeroTokens, color: '#F54021', }, ], global: [ { title: 'PRTs with Signals', - centerCount: 2, + centerCount: statistics.globalView.nonZeroTokens, color: '#AF7AA3', }, { title: 'PRTs without Signals', - centerCount: 1, + centerCount: + statistics.globalView.totalTokens - + statistics.globalView.nonZeroTokens, color: '#F54021', }, ], @@ -171,6 +176,7 @@ const ProbabilisticRevealTokens = () => { setSelectedJSON(row as PRTMetadata)} diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index 7c231e356b..5ec9e9da6c 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -25,9 +25,17 @@ import { ResizableTray, JsonView, noop, + type TableData, + type TableColumn, } from '@google-psat/design-system'; import React, { useRef } from 'react'; -import { type PRTMetadata } from '@google-psat/common'; +import { + type MDLTableData, + type ProbablisticRevealToken, + type PRTMetadata, + type UniqueDecryptedToken, + type UniquePlainTextToken, +} from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; /** @@ -36,19 +44,22 @@ import { I18n } from '@google-psat/i18n'; import RowContextMenuForPRT from './rowContextMenu'; import StatsHeader, { type Stats } from './stasHeader'; +type FormedJson = { + prtHeader: PRTMetadata; + decryptedTokens?: UniqueDecryptedToken; + prtToken: ProbablisticRevealToken; + plainTextToken?: UniquePlainTextToken; +}; interface MdlCommonPanelProps { - formedJson: PRTMetadata | null; - tableColumns: Array<{ - header: string; - accessorKey: keyof PRTMetadata; - cell: (info: any) => any; - }>; - tableData: PRTMetadata[]; - selectedKey: string | null; - onRowClick: (row: PRTMetadata) => void; - extraInterfaceToTopBar?: React.ReactNode; - filters: TableFilter[]; + formedJson: FormedJson | null; + tableColumns: TableColumn[]; + tableData: PRTMetadata[] | MDLTableData[]; + selectedKey?: string; + onRowClick: (row: TableData | null) => void; + extraInterfaceToTopBar?: () => React.JSX.Element; + filters?: TableFilter; stats: Stats; + tableSearchKeys: string[]; } const MdlCommonPanel = ({ @@ -57,7 +68,8 @@ const MdlCommonPanel = ({ tableData, selectedKey, onRowClick, - extraInterfaceToTopBar = undefined, + tableSearchKeys = [], + extraInterfaceToTopBar, filters, stats, }: MdlCommonPanelProps) => { @@ -86,10 +98,11 @@ const MdlCommonPanel = ({ data={tableData} tableFilterData={filters} tableColumns={tableColumns} - tableSearchKeys={['origin', 'owner']} + tableSearchKeys={tableSearchKeys} onRowClick={onRowClick} getRowObjectKey={(row: TableRow) => - (row.originalData as PRTMetadata).origin.toString() + (row.originalData as PRTMetadata).origin?.toString() ?? + (row.originalData as MDLTableData).domain?.toString() } onRowContextMenu={ rowContextMenuRef.current?.onRowContextMenu ?? noop diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index f2fcbce8c9..e30b44ab4b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -45,9 +45,12 @@ const MDLTable = () => { const [selectedKey, setSelectedKey] = useState(null); const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); const [isLoading, setIsLoading] = useState(true); - const { uniqueResponseDomains } = useScriptBlocking(({ state }) => ({ - uniqueResponseDomains: state.uniqueResponseDomains, - })); + const { uniqueResponseDomains, statistics } = useScriptBlocking( + ({ state }) => ({ + uniqueResponseDomains: state.uniqueResponseDomains, + statistics: state.statistics, + }) + ); const [initialTableData, setinitialTableData] = useState< { domain: string; owner: string; scriptBlocking: string }[] @@ -207,24 +210,24 @@ const MDLTable = () => { site: [ { title: 'Competely Blocked', - centerCount: 5, + centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', }, { title: 'Partially Blocked', - centerCount: 8, + centerCount: statistics.localView.partiallyBlockedDomains, color: '#4C79F4', }, ], global: [ { title: 'Competely Blocked', - centerCount: 1, + centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', }, { title: 'Partially Blocked', - centerCount: 9, + centerCount: statistics.globalView.partiallyBlockedDomains, color: '#4C79F4', }, ], @@ -242,8 +245,9 @@ const MDLTable = () => { setSelectedKey((row as MDLTableData)?.domain || null) } diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts index fd48020487..a33d220d21 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts @@ -18,18 +18,40 @@ */ import { noop, createContext } from '@google-psat/common'; +type ScriptBlockingStatistics = { + globalView: { + partiallyBlockedDomains: number; + completelyBlockedDomains: number; + }; + localView: { + partiallyBlockedDomains: number; + completelyBlockedDomains: number; + }; +}; + export interface ScriptBlockingStoreContext { state: { uniqueResponseDomains: string[]; + statistics: ScriptBlockingStatistics; }; actions: { setUniqueResponseDomains: (newValue: string[]) => void; }; } -const initialState: ScriptBlockingStoreContext = { +export const initialState: ScriptBlockingStoreContext = { state: { uniqueResponseDomains: [], + statistics: { + localView: { + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, + }, + globalView: { + partiallyBlockedDomains: 0, + completelyBlockedDomains: 0, + }, + }, }, actions: { setUniqueResponseDomains: noop, diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx index 50507d5033..d7b475ada3 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx @@ -27,7 +27,10 @@ import { isEqual } from 'lodash-es'; /** * Internal dependencies. */ -import Context from './context'; +import Context, { + initialState, + type ScriptBlockingStoreContext, +} from './context'; import { EXTRA_DATA } from '../../../../constants'; const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { @@ -35,11 +38,16 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { [] ); + const [statistics, setStatistics] = useState< + ScriptBlockingStoreContext['state']['statistics'] + >(initialState.state.statistics); + const messagePassingListener = useCallback( (message: { type: string; payload: { uniqueResponseDomains?: string[]; + stats: ScriptBlockingStoreContext['state']['statistics']; tabId: string; }; }) => { @@ -56,6 +64,15 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { ? prev : message.payload.uniqueResponseDomains || []; }); + + if (message.payload.stats) { + setStatistics((prev) => { + if (isEqual(prev, message.payload.stats)) { + return prev; + } + return message.payload.stats; + }); + } }, [] ); @@ -73,6 +90,7 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { value={{ state: { uniqueResponseDomains, + statistics, }, actions: { setUniqueResponseDomains, From 6b0829e99a24b56565af631cbbc5fe90daf7b212 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 12 Sep 2025 21:04:49 +0530 Subject: [PATCH 30/98] fix: Correctly aggregate token statistics in PRTStore --- packages/extension/src/store/PRTStore.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index b04ad75f65..9918d428de 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -320,8 +320,9 @@ class PRTStore extends DataStore { const globalStats = Object.keys(prtStatistics).reduce( (acc, key) => { if (prtStatistics[key]) { - acc.totalTokens = prtStatistics + acc.totalTokens; - acc.nonZeroTokens = prtStatistics + acc.nonZeroTokens; + acc.totalTokens = prtStatistics[key].totalTokens + acc.totalTokens; + acc.nonZeroTokens = + prtStatistics[key].nonZeroTokens + acc.nonZeroTokens; } return acc; }, From edfb8875840f2ee7ef74156c70a366c9e77c8b7a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 12 Sep 2025 21:21:34 +0530 Subject: [PATCH 31/98] fix: Simplify origin extraction logic and add logging for prtHeader --- packages/extension/src/serviceWorker/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 8f62ff38f2..cc0116ea9d 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -365,9 +365,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { let origin = ''; - if (extractHeader('origin', headers)) { - origin = extractHeader('origin', headers); - } else if ( + if ( DataStore.requestIdToCDPURLMapping[tabId]?.[requestId]?.url && isValidURL( DataStore.requestIdToCDPURLMapping[tabId]?.[requestId]?.url From 8f4c6d951532e14ce389793566e8c7d60f87ef3f Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 15 Sep 2025 12:48:57 +0530 Subject: [PATCH 32/98] Fix failing test --- .../serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts | 2 +- tests/jest.setup.cjs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts index 5739aade77..86203ed8e6 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts @@ -30,7 +30,6 @@ import { DataStore } from '../../../store/dataStore'; describe('chrome.tabs.onCreated.addListener', () => { beforeAll(() => { globalThis.chrome = SinonChrome as unknown as typeof chrome; - SinonChrome.tabs.onCreated.addListener(onTabCreatedListener); globalThis.fetch = function () { return Promise.resolve({ json: () => @@ -40,6 +39,7 @@ describe('chrome.tabs.onCreated.addListener', () => { text: () => Promise.resolve({}), }); } as unknown as typeof fetch; + SinonChrome.tabs.onCreated.addListener(onTabCreatedListener); }); test('Openeing new tab and if tabId is missing it should not create new tab.', async () => { diff --git a/tests/jest.setup.cjs b/tests/jest.setup.cjs index 466112050e..45ac0e4645 100644 --- a/tests/jest.setup.cjs +++ b/tests/jest.setup.cjs @@ -18,7 +18,9 @@ */ const React = require('react'); const { TextEncoder, TextDecoder } = require('node:util'); +const fetch = require('node-fetch'); global.React = React; global.TextDecoder = TextDecoder; global.TextEncoder = TextEncoder; +global.fetch = fetch; From d41d6bef16934d6a5a3911955d65f83ab2fe2135 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 15 Sep 2025 14:31:23 +0530 Subject: [PATCH 33/98] fix: Update Node.js setup to use version file and enable npm caching --- .github/workflows/tests-cli-e2e-tests.yml | 3 +- .../workflows/tests-extension-e2e-tests.yml | 3 +- package-lock.json | 52 ++++++++++--------- packages/extension/package.json | 4 +- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/.github/workflows/tests-cli-e2e-tests.yml b/.github/workflows/tests-cli-e2e-tests.yml index 4759c1ae46..efb91fbd1f 100644 --- a/.github/workflows/tests-cli-e2e-tests.yml +++ b/.github/workflows/tests-cli-e2e-tests.yml @@ -38,7 +38,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version-file: '.nvmrc' + cache: npm - name: Set Chrome executable path run: | diff --git a/.github/workflows/tests-extension-e2e-tests.yml b/.github/workflows/tests-extension-e2e-tests.yml index dd519c41f3..0ea364a33c 100644 --- a/.github/workflows/tests-extension-e2e-tests.yml +++ b/.github/workflows/tests-extension-e2e-tests.yml @@ -44,7 +44,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: 18 + node-version-file: '.nvmrc' + cache: npm - name: Set Chrome executable path run: | diff --git a/package-lock.json b/package-lock.json index b2edddf9e9..e0f801020f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2877,29 +2877,6 @@ "node": ">=12.4.0" } }, - "node_modules/@p5-wrapper/react": { - "version": "5.0.0-rc.3", - "license": "MIT", - "dependencies": { - "microdiff": "^1.5.0", - "react-error-boundary": "^6.0.0" - }, - "peerDependencies": { - "p5": ">= 1.11.3", - "react": ">= 19.0.0", - "react-dom": ">= 19.0.0" - } - }, - "node_modules/@p5-wrapper/react/node_modules/react-error-boundary": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -21640,7 +21617,7 @@ "@google-psat/explorable-explanations": "*", "@google-psat/i18n": "*", "@google-psat/report": "*", - "@p5-wrapper/react": "^5.0.0-rc.2", + "@p5-wrapper/react": "5.0.0-rc.2", "@types/elliptic": "^6.4.18", "@types/lodash-es": "^4.17.12", "classnames": "^2.3.2", @@ -21676,6 +21653,33 @@ "vite": "^6.3.5" } }, + "packages/extension/node_modules/@p5-wrapper/react": { + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@p5-wrapper/react/-/react-5.0.0-rc.2.tgz", + "integrity": "sha512-3uhaCHWUE4PqCvEd71h80pyT9JQRPEwUpvZoKHTynmi6TkYFxq7cKNndYx8yQP3KwuDfVB3gzZpzza18or9cEw==", + "license": "MIT", + "dependencies": { + "microdiff": "^1.5.0", + "react-error-boundary": "^5.0.0" + }, + "peerDependencies": { + "p5": ">= 1.11.3", + "react": ">= 19.0.0", + "react-dom": ">= 19.0.0" + } + }, + "packages/extension/node_modules/@p5-wrapper/react/node_modules/react-error-boundary": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", + "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "packages/extension/node_modules/devtools-protocol": { "version": "0.0.1345247", "dev": true, diff --git a/packages/extension/package.json b/packages/extension/package.json index ae9dc9dd67..c1bdced6c1 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -27,9 +27,9 @@ "@google-psat/explorable-explanations": "*", "@google-psat/i18n": "*", "@google-psat/report": "*", - "@p5-wrapper/react": "^5.0.0-rc.2", - "@types/lodash-es": "^4.17.12", + "@p5-wrapper/react": "5.0.0-rc.2", "@types/elliptic": "^6.4.18", + "@types/lodash-es": "^4.17.12", "classnames": "^2.3.2", "d3": "^7.9.0", "elliptic": "^6.6.1", From 1aab77f47089c8c7ac8b624b3187e29267178ad7 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 15 Sep 2025 15:19:16 +0530 Subject: [PATCH 34/98] Sync the UI for statistics update. --- packages/extension/src/store/PRTStore.ts | 12 +++--- .../probabilisticRevealTokens/index.tsx | 8 ++-- .../probabilisticRevealTokens/context.ts | 8 ++-- .../probabilisticRevealTokensProvider.tsx | 41 ++++++++++++++++++- .../scriptBlocking/scriptBlockingProvider.tsx | 23 ++++++++++- 5 files changed, 76 insertions(+), 16 deletions(-) diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 9918d428de..691116f789 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -226,7 +226,7 @@ class PRTStore extends DataStore { this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' ? 1 : 0), - partiallyBlockedDomais: + partiallyBlockedDomains: partiallyBlockedDomains + (this.mdlData[hostname] && this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' @@ -309,24 +309,24 @@ class PRTStore extends DataStore { acc.totalTokens += 1; if (!isZeroSignal) { - acc.nonZeroTokens += 1; + acc.nonZeroSignal += 1; } return acc; }, - { totalTokens: 0, nonZeroTokens: 0 } + { totalTokens: 0, nonZeroSignal: 0 } ); const globalStats = Object.keys(prtStatistics).reduce( (acc, key) => { if (prtStatistics[key]) { acc.totalTokens = prtStatistics[key].totalTokens + acc.totalTokens; - acc.nonZeroTokens = - prtStatistics[key].nonZeroTokens + acc.nonZeroTokens; + acc.nonZeroSignal = + prtStatistics[key].nonZeroSignal + acc.nonZeroSignal; } return acc; }, - { totalTokens: 0, nonZeroTokens: 0 } + { totalTokens: 0, nonZeroSignal: 0 } ); await chrome.runtime.sendMessage({ diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 5aa84a3126..b4994ae684 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -146,27 +146,27 @@ const ProbabilisticRevealTokens = () => { site: [ { title: 'PRTs with Signal', - centerCount: statistics.localView.nonZeroTokens, + centerCount: statistics.localView.nonZeroSignal, color: '#AF7AA3', }, { title: 'PRTs without Signal', centerCount: - statistics.localView.totalTokens - statistics.localView.nonZeroTokens, + statistics.localView.totalTokens - statistics.localView.nonZeroSignal, color: '#F54021', }, ], global: [ { title: 'PRTs with Signals', - centerCount: statistics.globalView.nonZeroTokens, + centerCount: statistics.globalView.nonZeroSignal, color: '#AF7AA3', }, { title: 'PRTs without Signals', centerCount: statistics.globalView.totalTokens - - statistics.globalView.nonZeroTokens, + statistics.globalView.nonZeroSignal, color: '#F54021', }, ], diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts index 0489b83367..10e0dadbb9 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts @@ -27,11 +27,11 @@ import { type PRTStatistics = { globalView: { totalTokens: number; - nonZeroTokens: number; + nonZeroSignal: number; }; localView: { totalTokens: number; - nonZeroTokens: number; + nonZeroSignal: number; }; }; @@ -54,11 +54,11 @@ export const initialState: ProbabilisticRevealTokensContextType = { statistics: { localView: { totalTokens: 0, - nonZeroTokens: 0, + nonZeroSignal: 0, }, globalView: { totalTokens: 0, - nonZeroTokens: 0, + nonZeroSignal: 0, }, }, }, diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx index c0254e5603..c3d1b3d842 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx @@ -145,19 +145,58 @@ const Provider = ({ children }: PropsWithChildren) => { [] ); + const syncStorageListener = useCallback( + async (changes: { [key: string]: chrome.storage.StorageChange }) => { + const hasChangesForPrtStatisticsData = + Object.keys(changes).includes('prtStatistics') && + Object.keys(changes.prtStatistics).includes('newValue'); + const { prtStatistics = {} } = await chrome.storage.sync.get( + 'prtStatistics' + ); + + const globalStats = Object.keys(prtStatistics).reduce( + (acc, key) => { + if (prtStatistics[key]) { + acc.totalTokens = prtStatistics[key].totalTokens + acc.totalTokens; + acc.nonZeroSignal = + prtStatistics[key].nonZeroSignal + acc.nonZeroSignal; + } + return acc; + }, + { totalTokens: 0, nonZeroSignal: 0 } + ); + + if (hasChangesForPrtStatisticsData) { + setStatistics((prev) => { + return { + ...prev, + globalView: globalStats, + }; + }); + } + }, + [] + ); + useEffect(() => { chrome.runtime?.onMessage?.addListener(messagePassingListener); chrome.webNavigation?.onCommitted?.addListener( onCommittedNavigationListener ); + chrome.storage.sync.onChanged.addListener(syncStorageListener); return () => { chrome.runtime?.onMessage?.removeListener(messagePassingListener); chrome.webNavigation?.onCommitted?.removeListener( onCommittedNavigationListener ); + chrome.storage.sync.onChanged.removeListener(syncStorageListener); }; - }, [messagePassingListener, onCommittedNavigationListener]); + }, [ + messagePassingListener, + onCommittedNavigationListener, + syncStorageListener, + ]); const memoisedValue: ProbabilisticRevealTokensContextType = useMemo(() => { return { diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx index d7b475ada3..c6216685d0 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx @@ -77,13 +77,34 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { [] ); + const syncStorageListener = useCallback( + (changes: { [key: string]: chrome.storage.StorageChange }) => { + const hasChangesForScriptBlockingData = + Object.keys(changes).includes('scriptBlocking') && + Object.keys(changes.scriptBlocking).includes('newValue'); + if (hasChangesForScriptBlockingData) { + setStatistics((prev) => { + return { + ...prev, + globalView: { + ...changes.scriptBlocking.newValue, + }, + }; + }); + } + }, + [] + ); + useEffect(() => { chrome.runtime?.onMessage?.addListener(messagePassingListener); + chrome.storage.sync.onChanged.addListener(syncStorageListener); return () => { chrome.runtime?.onMessage?.removeListener(messagePassingListener); + chrome.storage.sync.onChanged.removeListener(syncStorageListener); }; - }, [messagePassingListener]); + }, [messagePassingListener, syncStorageListener]); return ( Date: Mon, 15 Sep 2025 15:52:06 +0530 Subject: [PATCH 35/98] fix: Refactor MDLTable to use useScriptBlocking and remove unused tab logic --- .../ipProtection/mdlTable/index.tsx | 61 ++++--------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx index cfb5d3c39b..a708df00ff 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx @@ -34,7 +34,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; * Internal dependencies */ import Legend from './legend'; -import { getCurrentTab } from '../../../../../../utils/getCurrentTab'; +import { useScriptBlocking } from '../../../../stateProviders'; const MDLTable = () => { const [selectedKey, setSelectedKey] = useState(null); @@ -51,49 +51,9 @@ const MDLTable = () => { const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); - const [tab, setTab] = useState(null); - - useEffect(() => { - const currentTab = async () => { - const _tab = await getCurrentTab(); - if (_tab) { - setTab(_tab); - } - }; - - currentTab(); - }, []); - - useEffect(() => { - const fetchTab = async ({ - frameId, - frameType, - tabId, - }: chrome.webNavigation.WebNavigationFramedCallbackDetails) => { - if ( - !( - chrome.devtools.inspectedWindow.tabId === tabId && - frameType === 'outermost_frame' && - frameId === 0 - ) - ) { - return; - } - - const currentTab = await getCurrentTab(); - if (!currentTab) { - return; - } - - setTab(currentTab); - }; - - chrome.webNavigation.onCommitted.addListener(fetchTab); - - return () => { - chrome.webNavigation.onCommitted.removeListener(fetchTab); - }; - }, []); + const { uniqueResponseDomains } = useScriptBlocking(({ state }) => ({ + uniqueResponseDomains: state.uniqueResponseDomains, + })); const checkbox = useCallback(() => { return ( @@ -133,6 +93,7 @@ const MDLTable = () => { setTableData(() => { const _data = mdlData .map((item: string[]) => { + let available = false; let owner = item[1]; if (item[1].includes('PSL Domain')) { @@ -141,16 +102,16 @@ const MDLTable = () => { const scriptBlocking = item[2]; - const hostname = tab?.url ? new URL(tab.url).hostname : ''; + if (uniqueResponseDomains.includes(item[0])) { + available = true; + } return { domain: item[0], owner, scriptBlocking, - highlighted: hostname.includes(item[0]), - highlightedClass: hostname.includes(item[0]) - ? 'bg-amber-100' - : '', + highlighted: available, + highlightedClass: available ? 'bg-amber-100' : '', }; }) .filter((item) => { @@ -169,7 +130,7 @@ const MDLTable = () => { return _data; }); })(); - }, [showOnlyHighlighted, tab?.url]); + }, [showOnlyHighlighted, uniqueResponseDomains]); const tableColumns = useMemo( () => [ From b162a97a7293de6f8eb9836212b6a2212f081791 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 15 Sep 2025 16:26:38 +0530 Subject: [PATCH 36/98] Lower opacity if highlighted row is selected --- .../src/components/table/components/tableBody/bodyRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx index fa8e0c9fe8..49556ae960 100644 --- a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx +++ b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx @@ -82,10 +82,10 @@ const BodyRow = ({ rowKey === selectedKey && (isRowFocused ? isHighlighted - ? isHighlightedClass + ? `${isHighlightedClass} opacity-70` : 'bg-lavender-sky text-black dark:bg-midnight-slate dark:text-chinese-silver' : isHighlighted - ? isHighlightedClass + ? `${isHighlightedClass} opacity-70` : 'bg-silver-mist text-black dark:bg-dark-graphite dark:text-chinese-silver') ); const extraClasses = getExtraClasses(); From 0eb302921e8eb1c9881015063fee3a2708212ae0 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 15 Sep 2025 17:39:32 +0530 Subject: [PATCH 37/98] feat: Add optional bottomPanel and showJson props to MdlCommonPanel --- .../mdlCommonPanel/index.tsx | 28 +++++++++++++------ .../scriptBlocking/mdlTable/index.tsx | 3 ++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index 5ec9e9da6c..22d318ef29 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -60,6 +60,8 @@ interface MdlCommonPanelProps { filters?: TableFilter; stats: Stats; tableSearchKeys: string[]; + bottomPanel?: () => React.JSX.Element; + showJson?: boolean; } const MdlCommonPanel = ({ @@ -72,6 +74,8 @@ const MdlCommonPanel = ({ extraInterfaceToTopBar, filters, stats, + bottomPanel, + showJson = true, }: MdlCommonPanelProps) => { const rowContextMenuRef = useRef
- {formedJson ? ( -
- -
+ {showJson ? ( + formedJson ? ( +
+ +
+ ) : ( +
+

+ {I18n.getMessage('selectRowToPreview')} +

+
+ ) + ) : bottomPanel ? ( + bottomPanel() ) : ( -
-

- {I18n.getMessage('selectRowToPreview')} -

-
+ <> )}
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index e30b44ab4b..7bdc54c747 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -31,6 +31,7 @@ import type { MDLTableData } from '@google-psat/common'; */ import { useScriptBlocking } from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommonPanel'; +import Legend from './legend'; export const IMPACTED_BY_SCRIPT_BLOCKING = { NONE: 'Not Impacted By Script Blocking', @@ -254,6 +255,8 @@ const MDLTable = () => { extraInterfaceToTopBar={checkbox} filters={filters} stats={stats} + showJson={false} + bottomPanel={Legend} /> ); }; From a60fefc72fb80969d947000fed216eaa611e4443 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 10:55:56 +0530 Subject: [PATCH 38/98] feat: Enhance PRTMetadata with owner field and add filtering capabilities in MDLTable and ProbabilisticRevealTokens --- packages/common/src/prtToken.types.ts | 1 + packages/extension/src/serviceWorker/index.ts | 8 +++ .../probabilisticRevealTokens/index.tsx | 68 ++++++++++++++++++- .../scriptBlocking/mdlTable/index.tsx | 34 +++++++--- 4 files changed, 98 insertions(+), 13 deletions(-) diff --git a/packages/common/src/prtToken.types.ts b/packages/common/src/prtToken.types.ts index 7098c940d0..493d23184d 100644 --- a/packages/common/src/prtToken.types.ts +++ b/packages/common/src/prtToken.types.ts @@ -39,6 +39,7 @@ export interface PRTMetadata { decryptionKeyAvailable: boolean; prtHeader: string; nonZeroUint8Signal: boolean; + owner: string; } export type UniquePlainTextToken = PlaintTextToken & { diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index cc0116ea9d..46030bba3c 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -445,11 +445,19 @@ chrome.debugger.onEvent.addListener((source, method, params) => { } if (!PRTStore.tabTokens[tabId]?.perTokenMetadata?.[prtHeader]) { + const hostname = isValidURL(origin) ? new URL(origin).hostname : ''; + const formedOrigin = hostname.startsWith('www.') + ? hostname.slice(4) + : hostname; + PRTStore.tabTokens[tabId].perTokenMetadata[prtHeader] = { prtHeader, origin: isValidURL(origin) ? origin : '', decryptionKeyAvailable: Boolean(decodedToken), nonZeroUint8Signal, + owner: PRTStore.mdlData[formedOrigin]?.owner + ? PRTStore.mdlData[formedOrigin]?.owner + : '', }; DataStore.tabs[tabId].newUpdatesPRT++; } diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index b4994ae684..4b0a6adfaa 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -17,7 +17,11 @@ /** * External dependencies */ -import { type TableColumn } from '@google-psat/design-system'; +import { + type InfoType, + type TableColumn, + type TableFilter, +} from '@google-psat/design-system'; import React, { useMemo, useState } from 'react'; import { type PRTMetadata } from '@google-psat/common'; @@ -65,7 +69,7 @@ const ProbabilisticRevealTokens = () => { }, { header: 'Signal', - accessorKey: 'nonZeroUintsignal', + accessorKey: 'nonZeroUint8Signal', cell: (info) => { return info ? : ''; }, @@ -142,6 +146,65 @@ const ProbabilisticRevealTokens = () => { selectedJSON, ]); + const filters = useMemo( + () => ({ + owner: { + title: 'Owner', + }, + nonZeroUint8Signal: { + title: 'PRT with Zero Signal', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + True: { + selected: false, + description: "PRT's that reveal IP address", + }, + False: { + selected: false, + description: "PRT's that do not reveal IP address", + }, + }, + comparator: (value: InfoType, filterValue: string) => { + switch (filterValue) { + case 'True': + return !value as boolean; + case 'False': + return value as boolean; + default: + return true; + } + }, + }, + decryptionKeyAvailable: { + title: 'Decrypted', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + True: { + selected: false, + description: "PRT's that have been decrypted", + }, + False: { + selected: false, + description: "PRT's that have not been decrypted", + }, + }, + comparator: (value: InfoType, filterValue: string) => { + switch (filterValue) { + case 'True': + return value as boolean; + case 'False': + return !value as boolean; + default: + return true; + } + }, + }, + }), + [] + ); + const stats = { site: [ { @@ -176,6 +239,7 @@ const ProbabilisticRevealTokens = () => { { [] ); + const calculateFilters = useCallback((data: MDLTableData[]) => { + const _filters: { + [key: string]: { + selected: boolean; + description: string; + }; + } = {}; + + data.forEach((singleData) => { + _filters[singleData.scriptBlocking] = { + selected: false, + description: IMPACTED_BY_SCRIPT_BLOCKING[ + singleData.scriptBlocking as keyof typeof IMPACTED_BY_SCRIPT_BLOCKING + ] as string, + }; + }); + + return _filters; + }, []); + const filters = useMemo( () => ({ owner: { @@ -192,19 +212,11 @@ const MDLTable = () => { scriptBlocking: { title: 'Impacted by Script Blocking', hasStaticFilterValues: true, - filterValues: { - [IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL]: { - selected: false, - description: IMPACTED_BY_SCRIPT_BLOCKING.PARTIAL, - }, - [IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE]: { - selected: false, - description: IMPACTED_BY_SCRIPT_BLOCKING.ENTIRE, - }, - }, + hasPrecalculatedFilterValues: true, + filterValues: calculateFilters(tableData), }, }), - [] + [calculateFilters, tableData] ); const stats = { From cc7fc0a715d2f8735d7e200f6870d568e20d42f3 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 11:56:46 +0530 Subject: [PATCH 39/98] feat: Add @p5-wrapper/react with lazy loading and update build scripts for improved performance --- package-lock.json | 56 ++-- packages/extension/package.json | 4 +- .../explorableExplanation/panel.tsx | 56 ++-- vite.extension.config.mts | 280 ++++++++---------- 4 files changed, 182 insertions(+), 214 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44955c1803..e1d3850b28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2877,6 +2877,33 @@ "node": ">=12.4.0" } }, + "node_modules/@p5-wrapper/react": { + "version": "5.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@p5-wrapper/react/-/react-5.0.0-rc.2.tgz", + "integrity": "sha512-3uhaCHWUE4PqCvEd71h80pyT9JQRPEwUpvZoKHTynmi6TkYFxq7cKNndYx8yQP3KwuDfVB3gzZpzza18or9cEw==", + "license": "MIT", + "dependencies": { + "microdiff": "^1.5.0", + "react-error-boundary": "^5.0.0" + }, + "peerDependencies": { + "p5": ">= 1.11.3", + "react": ">= 19.0.0", + "react-dom": ">= 19.0.0" + } + }, + "node_modules/@p5-wrapper/react/node_modules/react-error-boundary": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", + "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -14714,6 +14741,8 @@ }, "node_modules/microdiff": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/microdiff/-/microdiff-1.5.0.tgz", + "integrity": "sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==", "license": "MIT" }, "node_modules/micromatch": { @@ -21654,33 +21683,6 @@ "vite": "^6.3.5" } }, - "packages/extension/node_modules/@p5-wrapper/react": { - "version": "5.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@p5-wrapper/react/-/react-5.0.0-rc.2.tgz", - "integrity": "sha512-3uhaCHWUE4PqCvEd71h80pyT9JQRPEwUpvZoKHTynmi6TkYFxq7cKNndYx8yQP3KwuDfVB3gzZpzza18or9cEw==", - "license": "MIT", - "dependencies": { - "microdiff": "^1.5.0", - "react-error-boundary": "^5.0.0" - }, - "peerDependencies": { - "p5": ">= 1.11.3", - "react": ">= 19.0.0", - "react-dom": ">= 19.0.0" - } - }, - "packages/extension/node_modules/@p5-wrapper/react/node_modules/react-error-boundary": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", - "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, "packages/extension/node_modules/devtools-protocol": { "version": "0.0.1345247", "dev": true, diff --git a/packages/extension/package.json b/packages/extension/package.json index f7283ea51f..b7ccaec37f 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -9,8 +9,8 @@ "license": "Apache-2.0", "scripts": { "prebuild": "rimraf ../../dist/extension", - "dev": "cross-env NODE_ENV=development npm run build", - "build": "cross-env VITE_CJS_IGNORE_WARNING=true NODE_NO_WARNINGS=1 node --loader ts-node/esm ../../vite.extension.config.mts" + "dev": "cross-env NODE_ENV=development npm run build -- --watch", + "build": "cross-env VITE_CJS_IGNORE_WARNING=true NODE_NO_WARNINGS=1 NODE_OPTIONS=--max-old-space-size=4096 node --loader ts-node/esm ../../vite.extension.config.mts" }, "bugs": { "url": "https://github.com/GoogleChromeLabs/ps-analysis-tool/issues" diff --git a/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx b/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx index b9b03ee30f..bf2116f0f9 100644 --- a/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx +++ b/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx @@ -23,6 +23,7 @@ import React, { useEffect, useRef, useMemo, + Suspense, } from 'react'; import { app, @@ -32,7 +33,6 @@ import { config, // @ts-ignore package does not have types } from '@google-psat/explorable-explanations'; -import { ReactP5Wrapper } from '@p5-wrapper/react'; import { DraggableTray, useTabs } from '@google-psat/design-system'; import { getSessionStorage, updateSessionStorage } from '@google-psat/common'; import classNames from 'classnames'; @@ -44,6 +44,12 @@ import Header from '../../../explorableExplanation/header'; import type { CurrentSiteData, StepType } from './auctionEventTransformers'; import { useSettings } from '../../../../stateProviders'; +const ReactP5Wrapper = React.lazy(() => + import('@p5-wrapper/react').then((module) => ({ + default: module.ReactP5Wrapper, + })) +); + const STORAGE_KEY = 'paExplorableExplanation'; const DEFAULT_SETTINGS = { isAutoScroll: true, @@ -446,28 +452,34 @@ const Panel = ({
{/* Main Canvas */} - + + + {/* Interest Group Canvas */} - - + + + + + + ); diff --git a/vite.extension.config.mts b/vite.extension.config.mts index b20e7a30f1..8c4c9b31f5 100644 --- a/vite.extension.config.mts +++ b/vite.extension.config.mts @@ -13,200 +13,154 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** - * External dependencies: - */ import { build, defineConfig, mergeConfig } from 'vite'; import { viteSingleFile } from 'vite-plugin-singlefile'; import { viteStaticCopy } from 'vite-plugin-static-copy'; import path from 'path'; +import { fileURLToPath } from 'url'; -/** - * Internal dependencies: - */ -import baseConfig, { __dirname } from './vite.shared.config.mjs'; +import baseConfig from './vite.shared.config.mjs'; -const isDev = process.env.NODE_ENV === 'development'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -type Script = { - name: string; - path: string; - // all content scripts must be in iife format - format: 'es' | 'iife'; -}; - -// content scripts and worker defined in the manifest.json -const scripts: Script[] = [ - { - name: 'service-worker', - path: 'src/serviceWorker/index.ts', - format: 'es', // defined as module in manifest.json - }, - { - name: 'content-script', - path: 'src/contentScript/index.ts', - format: 'iife', - }, - { - name: 'js-cookie-content-script', - path: 'src/contentScript/jsCookie.ts', - format: 'iife', - }, - { - name: 'prebid-content-script', - path: 'src/contentScript/prebid/prebidContentScript.ts', - format: 'iife', - }, - { - name: 'prebid-interface', - path: 'src/contentScript/prebid/prebidInterface.tsx', - format: 'iife', - }, -] as const; - -const createScriptConfig = (script: (typeof scripts)[number]) => { - let minifier: boolean | 'terser' = 'terser'; - if (isDev) { - minifier = false; - } else { - minifier = 'terser'; - } +const isDev = process.env.NODE_ENV === 'development'; +const distDir = path.resolve(__dirname, 'dist/extension'); - return defineConfig({ - build: { - watch: isDev ? {} : null, - emptyOutDir: false, - target: 'esnext', - outDir: `../../dist/extension`, - minify: minifier, - rollupOptions: { - input: { - [script.name]: script.path, - }, - output: { - format: script.format || 'iife', - chunkFileNames: '[name].js', - entryFileNames: '[name].js', - }, - }, +const runBuilds = async () => { + const scripts = [ + { + name: 'service-worker', + path: 'src/serviceWorker/index.ts', + format: 'es', }, - }); -}; - -const createExtensionConfig = (target: string) => { - const commonConfig = mergeConfig(baseConfig, { - base: '', // important to make sure the paths are correct in output index.html - sourcemap: isDev ? 'inline' : false, - build: { - emptyOutDir: false, // don't empty the output directory, output folder is re-used - outDir: `../../../../../dist/extension/${target}`, - minify: !isDev, + { + name: 'content-script', + path: 'src/contentScript/index.ts', + format: 'iife', + }, + { + name: 'js-cookie-content-script', + path: 'src/contentScript/jsCookie.ts', + format: 'iife', }, - }); + { + name: 'prebid-content-script', + path: 'src/contentScript/prebid/prebidContentScript.ts', + format: 'iife', + }, + { + name: 'prebid-interface', + path: 'src/contentScript/prebid/prebidInterface.tsx', + format: 'iife', + }, + ]; - if (target === 'popup') { - return mergeConfig(commonConfig, { - root: path.resolve(__dirname, 'packages/extension/src/view/popup'), - build: { - rollupOptions: { - input: { - popup: 'src/view/popup/index.html', + for (const script of scripts) { + // eslint-disable-next-line no-await-in-loop + await build( + mergeConfig(baseConfig, { + build: { + watch: isDev ? {} : null, + emptyOutDir: false, + target: 'esnext', + outDir: distDir, + minify: !isDev ? 'terser' : false, + rollupOptions: { + input: { + [script.name]: path.resolve( + __dirname, + 'packages/extension', + script.path + ), + }, + output: { + format: script.format, + entryFileNames: '[name].js', + }, }, }, - }, - }); + }) + ); } - if (target === 'report') { - return mergeConfig(commonConfig, { - root: path.resolve(__dirname, 'packages/extension/src/view/report'), - build: { - outDir: `../../../../../dist/extension/devtools`, - rollupOptions: { - input: { - dashboard: './src/view/report/dashboard.html', - }, - }, - }, + // 2. Build UI Components (Popup, DevTools, Report, etc.) + const uiTargets = [ + // Popup Page + { + root: 'packages/extension/src/view/popup', + outDir: path.join(distDir, 'popup'), + input: { popup: 'src/view/popup/index.html' }, + watch: isDev ? {} : null, + }, + // DevTools Panel UI + { + root: 'packages/extension/src/view/devtools', + outDir: path.join(distDir, 'devtools'), + input: { index: './src/view/devtools/index.html' }, + watch: isDev ? {} : null, + }, + // Report Page (inlined into a single file) + { + root: 'packages/extension/src/view/report', + outDir: path.join(distDir, 'devtools'), + input: { dashboard: './src/view/report/dashboard.html' }, plugins: [viteSingleFile()], - }); - } - - if (target === 'assets') { - return mergeConfig(commonConfig, { - root: path.resolve(__dirname, 'packages/extension/src/view/devtools'), - build: { - outDir: `../../../../../dist/extension/devtools`, - rollupOptions: { - input: { - devtools: './src/view/devtools/devtools.html', - }, - }, - }, + }, + // DevTools setup page and static asset copying + { + root: 'packages/extension/src/view/devtools', + outDir: path.join(distDir, 'devtools'), + input: { devtools: './src/view/devtools/devtools.html' }, + watch: isDev ? {} : null, plugins: [ viteStaticCopy({ targets: [ - { - src: '../../manifest.json', - dest: '../', - }, - { - src: '../../../icons', - dest: '../', - }, - { - src: '../../../../../assets', - dest: '../', - }, - { - src: '../../../../../data', - dest: '../', - }, + { src: '../../manifest.json', dest: '../' }, + { src: '../../../icons', dest: '../' }, + { src: '../../../../../assets', dest: '../' }, + { src: '../../../../../data', dest: '../' }, { src: '../../../../i18n/_locales/messages/*', dest: '../_locales/', }, - { - src: '../../../../../node_modules/p5/lib/p5.min.js', - dest: './', - }, + { src: '../../../../../node_modules/p5/lib/p5.min.js', dest: './' }, ], }), ], - }); - } + }, + ]; - if (target === 'devtools') { - return mergeConfig(commonConfig, { - root: path.resolve(__dirname, 'packages/extension/src/view/devtools'), - build: { - watch: isDev ? {} : null, - rollupOptions: { - input: { - index: './src/view/devtools/index.html', + for (const target of uiTargets) { + // eslint-disable-next-line no-await-in-loop + await build( + mergeConfig(baseConfig, { + root: path.resolve(__dirname, target.root), + base: '', // Ensures correct asset paths in HTML + build: { + watch: target.watch || null, + emptyOutDir: false, + sourcemap: isDev ? true : false, + outDir: target.outDir, + minify: !isDev, + rollupOptions: { + input: target.input, }, }, - }, - }); - } - - return commonConfig; -}; - -const start = async () => { - // vite/rollup does not support multiple input files when using inlineDynamicImports(forced with iife) - // so we need to create a config for each script - for (const script of scripts) { - // eslint-disable-next-line no-await-in-loop - await build(createScriptConfig(script)); // run each build in queue, when `watch: true` promise will be resolved anyway after first build - } - - for (const target of ['popup', 'report', 'assets', 'devtools']) { - // eslint-disable-next-line no-await-in-loop - await build(createExtensionConfig(target)); + plugins: target.plugins || [], + }) + ); } }; (async () => { - await start(); + try { + await runBuilds(); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Build failed:', error); + process.exit(1); + } })(); + +export default defineConfig({}); From 3ee10c9efa33b6260d7e6cf4bfe0ee4e1dea9d54 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 11:59:36 +0530 Subject: [PATCH 40/98] Change the header of the signal filter. --- .../ipProtection/probabilisticRevealTokens/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 4b0a6adfaa..31464016f5 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -152,24 +152,24 @@ const ProbabilisticRevealTokens = () => { title: 'Owner', }, nonZeroUint8Signal: { - title: 'PRT with Zero Signal', + title: 'Signal', hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: { - True: { + 'PRT with no Signal': { selected: false, description: "PRT's that reveal IP address", }, - False: { + 'PRT with signal': { selected: false, description: "PRT's that do not reveal IP address", }, }, comparator: (value: InfoType, filterValue: string) => { switch (filterValue) { - case 'True': + case 'PRT with no Signal': return !value as boolean; - case 'False': + case 'PRT with signal': return value as boolean; default: return true; From c83936d19137af84f409b9f4c2f06cbbdbbb499d Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 14:34:33 +0530 Subject: [PATCH 41/98] fix: Simplify access to scriptBlocking and prtStatistics in PRTStore --- packages/extension/src/store/PRTStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 691116f789..a30645e25d 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -214,9 +214,9 @@ class PRTStore extends DataStore { chrome.storage.sync.get('scriptBlocking', (result) => { const completelyBlockedDomains = - result.scriptBlocking?.[origin]?.completelyBlockedDomains ?? 0; + result.scriptBlocking?.completelyBlockedDomains ?? 0; const partiallyBlockedDomains = - result.prtStatistics?.[origin]?.partiallyBlockedDomains ?? 0; + result.prtStatistics?.partiallyBlockedDomains ?? 0; chrome.storage.sync.set({ scriptBlocking: { From 72a7c0ef58835adf0234cc8b07fa977be192a4b0 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 14:43:11 +0530 Subject: [PATCH 42/98] feat: Add ProgressBar fallback to Suspense components and import DraggableTray in Panel --- .../explorableExplanation/panel.tsx | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx b/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx index bf2116f0f9..e685f68bbc 100644 --- a/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx +++ b/packages/extension/src/view/devtools/pages/privateAdvertising/protectedAudience/explorableExplanation/panel.tsx @@ -33,7 +33,11 @@ import { config, // @ts-ignore package does not have types } from '@google-psat/explorable-explanations'; -import { DraggableTray, useTabs } from '@google-psat/design-system'; +import { + DraggableTray, + ProgressBar, + useTabs, +} from '@google-psat/design-system'; import { getSessionStorage, updateSessionStorage } from '@google-psat/common'; import classNames from 'classnames'; @@ -45,6 +49,7 @@ import type { CurrentSiteData, StepType } from './auctionEventTransformers'; import { useSettings } from '../../../../stateProviders'; const ReactP5Wrapper = React.lazy(() => + //@ts-expect-error -- this is because the component is being exported as a different type than what lazy expects import('@p5-wrapper/react').then((module) => ({ default: module.ReactP5Wrapper, })) @@ -452,11 +457,23 @@ const Panel = ({ {/* Main Canvas */} - + + + + } + > {/* Interest Group Canvas */} - + + + + } + > - + + + + } + > From 87b42e0bc502103c12ac5023087ebe50b9de3529 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 16 Sep 2025 15:10:13 +0530 Subject: [PATCH 43/98] refactor: Optimize statistics update logic in PRTStore and updateStatistics utility --- packages/extension/src/serviceWorker/index.ts | 23 +++++-------------- packages/extension/src/store/PRTStore.ts | 16 ++++++------- .../src/store/utils/updateStatistics.ts | 2 ++ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 46030bba3c..21b2fc2b8c 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -424,23 +424,12 @@ chrome.debugger.onEvent.addListener((source, method, params) => { prtHeader, }); - chrome.storage.sync.get('prtStatistics', (result) => { - const totalTokens = - result.prtStatistics?.[origin]?.totalTokens ?? 0; - const nonZeroSignal = - result.prtStatistics?.[origin]?.nonZeroSignal ?? 0; - - updateStatistics(origin, nonZeroUint8Signal); - - chrome.storage.sync.set({ - prtStatistics: { - ...(result.prtStatistics ?? {}), - [origin]: { - totalTokens: totalTokens + 1, - nonZeroSignal: nonZeroSignal + (nonZeroUint8Signal ? 1 : 0), - }, - }, - }); + updateStatistics(origin, nonZeroUint8Signal); + + await chrome.storage.sync.set({ + prtStatistics: { + ...PRTStore.statistics.prtStatistics.globalView, + }, }); } diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index a30645e25d..97118d2846 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -303,14 +303,14 @@ class PRTStore extends DataStore { 'prtStatistics' ); - const localStats = this.tabTokens[tabId].plainTextTokens.reduce( - (acc, token) => { - const isZeroSignal = token.uint8Signal.every((bit) => bit === 0); - acc.totalTokens += 1; - - if (!isZeroSignal) { - acc.nonZeroSignal += 1; - } + const localStats = Object.keys( + this.statistics.prtStatistics.localView + ).reduce( + (acc, origin) => { + acc.totalTokens += + this.statistics.prtStatistics.localView[origin].totalTokens; + acc.nonZeroSignal += + this.statistics.prtStatistics.localView[origin].nonZeroSignal; return acc; }, diff --git a/packages/extension/src/store/utils/updateStatistics.ts b/packages/extension/src/store/utils/updateStatistics.ts index fb3240ec0f..6a213a0263 100644 --- a/packages/extension/src/store/utils/updateStatistics.ts +++ b/packages/extension/src/store/utils/updateStatistics.ts @@ -31,6 +31,7 @@ const updateStatistics = (origin: string, nonZeroUint8Signal: boolean) => { }; } else { PRTStore.statistics.prtStatistics.localView = { + ...PRTStore.statistics.prtStatistics.localView, [origin]: { totalTokens: 1, nonZeroSignal: nonZeroUint8Signal ? 1 : 0, @@ -49,6 +50,7 @@ const updateStatistics = (origin: string, nonZeroUint8Signal: boolean) => { }; } else { PRTStore.statistics.prtStatistics.globalView = { + ...PRTStore.statistics.prtStatistics.globalView, [origin]: { totalTokens: 1, nonZeroSignal: nonZeroUint8Signal ? 1 : 0, From 660f1dc861402a432fbad618c91dbee974201bd9 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Wed, 17 Sep 2025 10:18:28 +0530 Subject: [PATCH 44/98] Fix circle bottom title width --- .../design-system/src/components/circlePieChart/index.tsx | 6 +++++- .../pages/privacyProtection/mdlCommonPanel/stasHeader.tsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/design-system/src/components/circlePieChart/index.tsx b/packages/design-system/src/components/circlePieChart/index.tsx index e89bbefcd0..0d90075660 100644 --- a/packages/design-system/src/components/circlePieChart/index.tsx +++ b/packages/design-system/src/components/circlePieChart/index.tsx @@ -32,6 +32,7 @@ interface CirclePieChartProps { fallbackText?: string; infoIconClassName?: string; centerTitleExtraClasses?: string; + bottomTitleExtraClasses?: string; pieChartExtraClasses?: string; } @@ -42,6 +43,7 @@ const CirclePieChart = ({ data, title, centerTitleExtraClasses = '', + bottomTitleExtraClasses = '', pieChartExtraClasses = '', }: CirclePieChartProps) => { const centerTitleClasses = centerCount <= MAX_COUNT ? 'text-2xl' : 'text-l'; @@ -73,7 +75,9 @@ const CirclePieChart = ({ )} {title && ( -
+

{title}

diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index 32f393d256..ee8db5f48e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -47,6 +47,7 @@ const StatsHeader = ({ stats }: StatsHeaderProps) => { centerCount={centerCount} data={[{ count: 10, color }]} // keep or derive from your data source infoIconClassName="absolute -right-3" + bottomTitleExtraClasses="min-w-[150px]" /> ))} From 304520438aa5dd7d20a46eaec1056a29be2bbe21 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Wed, 17 Sep 2025 10:23:22 +0530 Subject: [PATCH 45/98] Update filters for PRTs --- .../ipProtection/probabilisticRevealTokens/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 31464016f5..3477add976 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -156,14 +156,14 @@ const ProbabilisticRevealTokens = () => { hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: { - 'PRT with no Signal': { - selected: false, - description: "PRT's that reveal IP address", - }, 'PRT with signal': { selected: false, description: "PRT's that do not reveal IP address", }, + 'PRT without signal': { + selected: false, + description: "PRT's that reveal IP address", + }, }, comparator: (value: InfoType, filterValue: string) => { switch (filterValue) { @@ -221,12 +221,12 @@ const ProbabilisticRevealTokens = () => { ], global: [ { - title: 'PRTs with Signals', + title: 'PRTs with Signal', centerCount: statistics.globalView.nonZeroSignal, color: '#AF7AA3', }, { - title: 'PRTs without Signals', + title: 'PRTs without Signal', centerCount: statistics.globalView.totalTokens - statistics.globalView.nonZeroSignal, From a72c7fcf3481c8a7f60a2a616bb53cb4ebda9cfe Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Wed, 17 Sep 2025 12:04:31 +0530 Subject: [PATCH 46/98] Fix typo --- .../pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx index dce9fa1a43..f1db944a66 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx @@ -32,7 +32,7 @@ const Legend = () => { IP Protection {' '} - initiative. Domains on this list are either entirely or patially + initiative. Domains on this list are either entirely or partially blocked.

From 5a3d90a63aef7c3f3bf0570f6777f54924af7230 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 17 Sep 2025 13:16:01 +0530 Subject: [PATCH 47/98] fix: Correct filter labels for PRTs and enhance script blocking filter logic --- .../probabilisticRevealTokens/index.tsx | 4 ++-- .../scriptBlocking/mdlTable/index.tsx | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 3477add976..8b811d0556 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -156,11 +156,11 @@ const ProbabilisticRevealTokens = () => { hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: { - 'PRT with signal': { + 'PRTs with signal': { selected: false, description: "PRT's that do not reveal IP address", }, - 'PRT without signal': { + 'PRTs without signal': { selected: false, description: "PRT's that reveal IP address", }, diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 8e370dd8c2..fe284fb13e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -22,6 +22,7 @@ import { type TableFilter, type TableColumn, Link, + type InfoType, } from '@google-psat/design-system'; import React, { useEffect, useMemo, useState, useCallback } from 'react'; import type { MDLTableData } from '@google-psat/common'; @@ -192,8 +193,13 @@ const MDLTable = () => { }; } = {}; + const titleMap = { + COMPLETE: 'Completely Blocked', + PARTIAL: 'Partially Blocked', + }; + data.forEach((singleData) => { - _filters[singleData.scriptBlocking] = { + _filters[titleMap[singleData.scriptBlocking as keyof typeof titleMap]] = { selected: false, description: IMPACTED_BY_SCRIPT_BLOCKING[ singleData.scriptBlocking as keyof typeof IMPACTED_BY_SCRIPT_BLOCKING @@ -214,6 +220,16 @@ const MDLTable = () => { hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateFilters(tableData), + comparator: (value: InfoType, filterValue: string) => { + switch (filterValue) { + case 'COMPLETE': + return value === 'COMPLETE'; + case 'PARTIAL': + return value === 'PARTIAL'; + default: + return false; + } + }, }, }), [calculateFilters, tableData] From 41a91283472ca1dd4ad69db98124d4da5bf2903a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 17 Sep 2025 17:17:49 +0530 Subject: [PATCH 48/98] feat: Enhance ProbabilisticRevealTokens with filter state management and update StatsHeader to support clickable pie charts --- .../probabilisticRevealTokens/index.tsx | 35 ++++++++++++---- .../mdlCommonPanel/stasHeader.tsx | 40 ++++++++++++++----- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 8b811d0556..3ce40fc788 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -33,6 +33,9 @@ import MdlCommonPanel from '../../mdlCommonPanel'; const ProbabilisticRevealTokens = () => { const [selectedJSON, setSelectedJSON] = useState(null); + const [preSetFilters, setPresetFilters] = useState<{ + [key: string]: Record; + }>({ filter: {} }); const { perTokenMetadata, @@ -157,19 +160,23 @@ const ProbabilisticRevealTokens = () => { hasPrecalculatedFilterValues: true, filterValues: { 'PRTs with signal': { - selected: false, - description: "PRT's that do not reveal IP address", + selected: ( + preSetFilters?.filter?.nonZeroUint8Signal ?? [] + ).includes('PRTs with signal'), + description: "PRT's that reveal IP address", }, 'PRTs without signal': { - selected: false, - description: "PRT's that reveal IP address", + selected: ( + preSetFilters?.filter?.nonZeroUint8Signal ?? [] + ).includes('PRTs without signal'), + description: "PRT's that do not reveal IP address", }, }, comparator: (value: InfoType, filterValue: string) => { switch (filterValue) { - case 'PRT with no Signal': + case 'PRTs without signal': return !value as boolean; - case 'PRT with signal': + case 'PRTs with signal': return value as boolean; default: return true; @@ -202,7 +209,7 @@ const ProbabilisticRevealTokens = () => { }, }, }), - [] + [preSetFilters?.filter?.nonZeroUint8Signal] ); const stats = { @@ -211,12 +218,26 @@ const ProbabilisticRevealTokens = () => { title: 'PRTs with Signal', centerCount: statistics.localView.nonZeroSignal, color: '#AF7AA3', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + nonZeroUint8Signal: ['PRTs with signal'], + }, + })), }, { title: 'PRTs without Signal', centerCount: statistics.localView.totalTokens - statistics.localView.nonZeroSignal, color: '#F54021', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + nonZeroUint8Signal: ['PRTs without signal'], + }, + })), }, ], global: [ diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index ee8db5f48e..b0d2ef1cca 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -18,11 +18,13 @@ */ import React, { useState } from 'react'; import { CirclePieChart, PillToggle } from '@google-psat/design-system'; +import classnames from 'classnames'; type StatItem = { title: string; centerCount: number; color: string; + onClick?: () => void; }; export type Stats = { @@ -40,15 +42,35 @@ const StatsHeader = ({ stats }: StatsHeaderProps) => { const renderPieCharts = (items: StatItem[]) => ( <> - {items.map(({ title, centerCount, color }) => ( - + {items.map(({ title, centerCount, color, onClick }, index) => ( + ))} ); From 7f80a50b8fac97d985bd605112a5dfa3fb99ba2e Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 18 Sep 2025 14:16:03 +0530 Subject: [PATCH 49/98] Rename global to session --- .../pages/privacyProtection/mdlCommonPanel/stasHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index b0d2ef1cca..00e5b45448 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -82,7 +82,7 @@ const StatsHeader = ({ stats }: StatsHeaderProps) => {
Date: Thu, 18 Sep 2025 14:22:51 +0530 Subject: [PATCH 50/98] Update text for script blocking checkbox --- .../pages/privacyProtection/scriptBlocking/mdlTable/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index fe284fb13e..0f5cf5794d 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -112,7 +112,7 @@ const MDLTable = () => { onChange={() => setShowOnlyHighlighted((prev) => !prev)} defaultChecked /> - Show only highlighted domains + Show Only blocked domains ), [] From 73790a28a7b177dde404f12d75da13144f589c33 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 18 Sep 2025 14:24:02 +0530 Subject: [PATCH 51/98] Update title for 'Impacted by Script Blocking' to scope --- .../pages/privacyProtection/scriptBlocking/mdlTable/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 0f5cf5794d..ae52272708 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -177,7 +177,7 @@ const MDLTable = () => { initialWidth: 100, }, { - header: 'Impacted by Script Blocking', + header: 'Scope', accessorKey: 'scriptBlocking', cell: (info) => info, }, From 674c7343e668c8834af1870a35537ba7a53252fa Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 18 Sep 2025 14:24:53 +0530 Subject: [PATCH 52/98] Rename titles for stats matrix --- .../pages/privacyProtection/scriptBlocking/mdlTable/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index ae52272708..9e5845b970 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -194,8 +194,8 @@ const MDLTable = () => { } = {}; const titleMap = { - COMPLETE: 'Completely Blocked', - PARTIAL: 'Partially Blocked', + COMPLETE: 'Scope Complete', + PARTIAL: 'Scope Partial', }; data.forEach((singleData) => { From ac0ae61d4cec41c35a0b77f3cb65cc917e8e1944 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 18 Sep 2025 14:30:10 +0530 Subject: [PATCH 53/98] Fix spacing of the header stats --- .../pages/privacyProtection/mdlCommonPanel/stasHeader.tsx | 2 +- .../privacyProtection/scriptBlocking/mdlTable/index.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index 00e5b45448..de2516b200 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -92,7 +92,7 @@ const StatsHeader = ({ stats }: StatsHeaderProps) => { />
-
+
{pillToggle === 'Site' ? sitePieCharts : globalPieCharts}
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 9e5845b970..44585c7095 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -238,24 +238,24 @@ const MDLTable = () => { const stats = { site: [ { - title: 'Competely Blocked', + title: 'Scope Complete', centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', }, { - title: 'Partially Blocked', + title: 'Scope Partial', centerCount: statistics.localView.partiallyBlockedDomains, color: '#4C79F4', }, ], global: [ { - title: 'Competely Blocked', + title: 'Scope Complete', centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', }, { - title: 'Partially Blocked', + title: 'Scope Partial', centerCount: statistics.globalView.partiallyBlockedDomains, color: '#4C79F4', }, From 89b63d7b35d67d5903dd35208a674b9d057c2a7d Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 18 Sep 2025 14:37:53 +0530 Subject: [PATCH 54/98] Rename PRT stats --- .../ipProtection/probabilisticRevealTokens/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 3ce40fc788..ff6573d654 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -215,19 +215,19 @@ const ProbabilisticRevealTokens = () => { const stats = { site: [ { - title: 'PRTs with Signal', + title: 'PRT', centerCount: statistics.localView.nonZeroSignal, color: '#AF7AA3', onClick: () => setPresetFilters((prev) => ({ ...prev, filter: { - nonZeroUint8Signal: ['PRTs with signal'], + nonZeroUint8Signal: ['PRT'], }, })), }, { - title: 'PRTs without Signal', + title: 'Signals', centerCount: statistics.localView.totalTokens - statistics.localView.nonZeroSignal, color: '#F54021', @@ -235,19 +235,19 @@ const ProbabilisticRevealTokens = () => { setPresetFilters((prev) => ({ ...prev, filter: { - nonZeroUint8Signal: ['PRTs without signal'], + nonZeroUint8Signal: ['Signals'], }, })), }, ], global: [ { - title: 'PRTs with Signal', + title: 'PRT', centerCount: statistics.globalView.nonZeroSignal, color: '#AF7AA3', }, { - title: 'PRTs without Signal', + title: 'Singnals', centerCount: statistics.globalView.totalTokens - statistics.globalView.nonZeroSignal, From ee000755e79aa2f4eeeebb8281b2565fd5aa04e4 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 19 Sep 2025 16:25:08 +0530 Subject: [PATCH 55/98] Format json being shown Open json by default Change data being shown in circles --- packages/extension/src/utils/getSignal.ts | 58 +++++++++++++++++++ .../probabilisticRevealTokens/index.tsx | 27 +++++---- .../mdlCommonPanel/index.tsx | 23 ++++---- .../mdlCommonPanel/stasHeader.tsx | 9 ++- 4 files changed, 91 insertions(+), 26 deletions(-) create mode 100644 packages/extension/src/utils/getSignal.ts diff --git a/packages/extension/src/utils/getSignal.ts b/packages/extension/src/utils/getSignal.ts new file mode 100644 index 0000000000..9b5855b094 --- /dev/null +++ b/packages/extension/src/utils/getSignal.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Converts number array to IPv6 address format. + * @param {number[]} buffer The signal from which data needs to be decoded + * @returns IPv6 address + */ +function formatIPv6(buffer: number[]) { + const parts = []; + for (let i = 0; i < 16; i += 2) { + parts.push(((buffer[i] << 8) | buffer[i + 1]).toString(16)); + } + // Basic zero compression + return parts.join(':').replace(/:(0:)+/, '::'); +} + +/** + * Converts array buffer to an IP Address + * @param {number[]} signal The signal from which data needs to be decoded + * @returns IPv6 address + */ +function getSignal(signal: number[]) { + if (signal.every((byte) => byte === 0)) { + return btoa(String.fromCharCode.apply(null, signal as number[])); + } + + const ipv4MappedPrefix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]; + const prefixMatch = signal + .slice(0, 12) + .every((val, i) => val === ipv4MappedPrefix[i]); + + if (signal.length === 16 && prefixMatch) { + const ipv4Bytes = signal.slice(12); + return Array.from(ipv4Bytes).join('.'); + } + + if (signal.length === 16) { + return formatIPv6(signal); + } + + return btoa(String.fromCharCode.apply(null, signal)); +} + +export default getSignal; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index ff6573d654..dc961c0b0d 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -30,6 +30,7 @@ import { type PRTMetadata } from '@google-psat/common'; */ import { useProbabilisticRevealTokens } from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommonPanel'; +import getSignal from '../../../../../../utils/getSignal'; const ProbabilisticRevealTokens = () => { const [selectedJSON, setSelectedJSON] = useState(null); @@ -127,19 +128,19 @@ const ProbabilisticRevealTokens = () => { if (!_decryptedToken || !_plainTextToken) { return { - prtHeader, - prtToken: _prtToken, + ...prtHeader, + ..._prtToken, }; } delete _decryptedToken.prtHeader; delete _plainTextToken.prtHeader; + const { uint8Signal, ...rest } = _plainTextToken; + return { - prtHeader, - decryptedTokens: _decryptedToken, - prtToken: _prtToken, - plainTextToken: _plainTextToken, + ...rest, + ip: getSignal((Object.values(uint8Signal) as unknown as number[]) ?? []), }; }, [ decryptedTokensData, @@ -216,7 +217,7 @@ const ProbabilisticRevealTokens = () => { site: [ { title: 'PRT', - centerCount: statistics.localView.nonZeroSignal, + centerCount: statistics.localView.totalTokens, color: '#AF7AA3', onClick: () => setPresetFilters((prev) => ({ @@ -228,8 +229,7 @@ const ProbabilisticRevealTokens = () => { }, { title: 'Signals', - centerCount: - statistics.localView.totalTokens - statistics.localView.nonZeroSignal, + centerCount: statistics.localView.nonZeroSignal, color: '#F54021', onClick: () => setPresetFilters((prev) => ({ @@ -243,14 +243,12 @@ const ProbabilisticRevealTokens = () => { global: [ { title: 'PRT', - centerCount: statistics.globalView.nonZeroSignal, + centerCount: statistics.globalView.totalTokens, color: '#AF7AA3', }, { - title: 'Singnals', - centerCount: - statistics.globalView.totalTokens - - statistics.globalView.nonZeroSignal, + title: 'Signals', + centerCount: statistics.globalView.nonZeroSignal, color: '#F54021', }, ], @@ -266,6 +264,7 @@ const ProbabilisticRevealTokens = () => { selectedKey={selectedJSON?.origin.toString()} onRowClick={(row) => setSelectedJSON(row as PRTMetadata)} stats={stats} + showJson /> ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index 22d318ef29..e2dc0edf47 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -28,13 +28,11 @@ import { type TableData, type TableColumn, } from '@google-psat/design-system'; -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import { type MDLTableData, type ProbablisticRevealToken, type PRTMetadata, - type UniqueDecryptedToken, - type UniquePlainTextToken, } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; @@ -44,12 +42,15 @@ import { I18n } from '@google-psat/i18n'; import RowContextMenuForPRT from './rowContextMenu'; import StatsHeader, { type Stats } from './stasHeader'; -type FormedJson = { - prtHeader: PRTMetadata; - decryptedTokens?: UniqueDecryptedToken; - prtToken: ProbablisticRevealToken; - plainTextToken?: UniquePlainTextToken; +type NonDecryptedJson = PRTMetadata & ProbablisticRevealToken; +type DecryptedJson = { + ordinal: Uint8Array; + version: number; + hmacValid: boolean; + ip: string; }; + +type FormedJson = NonDecryptedJson | DecryptedJson; interface MdlCommonPanelProps { formedJson: FormedJson | null; tableColumns: TableColumn[]; @@ -81,9 +82,11 @@ const MdlCommonPanel = ({ typeof RowContextMenuForPRT > | null>(null); + const [pillState, setPillState] = useState('Site'); + return (
- +
>; } -const StatsHeader = ({ stats }: StatsHeaderProps) => { +const StatsHeader = ({ stats, setPillState }: StatsHeaderProps) => { const [pillToggle, setPillToggle] = useState('Site'); const [highlightOption, setHighlightOption] = useState('Site'); + useEffect(() => { + setPillState(pillToggle); + }, [pillToggle, setPillState]); + const renderPieCharts = (items: StatItem[]) => ( <> {items.map(({ title, centerCount, color, onClick }, index) => ( From d87315d63c813a7e5c8b022830018f6c30e103ab Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 19 Sep 2025 16:40:28 +0530 Subject: [PATCH 56/98] Add show request filter for script blocking. --- .../probabilisticRevealTokens/index.tsx | 1 + .../mdlCommonPanel/index.tsx | 4 +- .../mdlCommonPanel/rowContextMenu.tsx | 54 ++++++++++++++----- .../scriptBlocking/mdlTable/index.tsx | 1 + 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index dc961c0b0d..0f78dda742 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -265,6 +265,7 @@ const ProbabilisticRevealTokens = () => { onRowClick={(row) => setSelectedJSON(row as PRTMetadata)} stats={stats} showJson + tab="PRT" /> ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index e2dc0edf47..65e297439c 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -63,6 +63,7 @@ interface MdlCommonPanelProps { tableSearchKeys: string[]; bottomPanel?: () => React.JSX.Element; showJson?: boolean; + tab: string; } const MdlCommonPanel = ({ @@ -77,6 +78,7 @@ const MdlCommonPanel = ({ stats, bottomPanel, showJson = true, + tab = '', }: MdlCommonPanelProps) => { const rowContextMenuRef = useRef - +
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx index 3255138215..b1f0649cc5 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx @@ -24,27 +24,49 @@ import React, { useState, } from 'react'; import { createPortal } from 'react-dom'; -import { isValidURL, type PRTMetadata } from '@google-psat/common'; +import { + isValidURL, + type MDLTableData, + type PRTMetadata, +} from '@google-psat/common'; import type { TableRow } from '@google-psat/design-system'; import { I18n } from '@google-psat/i18n'; -const RowContextMenuForPRT = forwardRef<{ - onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; -}>(function RowContextMenu(_, ref) { +type ContextMenuProp = { + tab: string; +}; + +const RowContextMenuForPRT = forwardRef< + { + onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; + }, + ContextMenuProp +>(function RowContextMenu({ tab }: ContextMenuProp, ref) { const [contextMenuOpen, setContextMenuOpen] = useState(false); const [columnPosition, setColumnPosition] = useState({ x: 0, y: 0, }); - const [prtMetaData, setMetadata] = useState(); + const [data, setMetadata] = useState(null); - const domain = useMemo( - () => - prtMetaData?.origin && isValidURL(prtMetaData?.origin) - ? new URL(prtMetaData?.origin).hostname - : '', - [prtMetaData] - ); + const domain = useMemo(() => { + if (!data) { + return null; + } + + if ((data as PRTMetadata)?.origin) { + if (isValidURL((data as PRTMetadata)?.origin)) { + return new URL((data as PRTMetadata)?.origin).hostname; + } else { + return ''; + } + } else { + if ((data as MDLTableData)?.domain) { + return (data as MDLTableData)?.domain; + } + } + return ''; + }, [data]); const handleRightClick = useCallback( (e: React.MouseEvent, { originalData }: TableRow) => { @@ -66,7 +88,9 @@ const RowContextMenuForPRT = forwardRef<{ })); const handleFilterClick = useCallback(() => { - const filter = `has-request-header:sec-probabilistic-reveal-token domain:${domain}`; + const filter = `${ + tab === 'PRT' ? 'has-request-header:sec-probabilistic-reveal-token ' : '' + }domain:${domain}`; // @ts-ignore if (chrome.devtools.panels?.network?.show) { @@ -113,7 +137,9 @@ const RowContextMenuForPRT = forwardRef<{ { // @ts-ignore chrome.devtools.panels?.network?.show - ? 'Show requests with this token.' + ? tab === 'scriptBlocking' + ? 'Show requests with this domain.' + : 'Show requests with this token.' : I18n.getMessage('copyNetworkFilter') } diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 44585c7095..7f1dc15468 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -285,6 +285,7 @@ const MDLTable = () => { stats={stats} showJson={false} bottomPanel={Legend} + tab="scriptBlocking" /> ); }; From 1e57cd79c3ddbe1fb50e9d978b4a5ff341c103b3 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Mon, 22 Sep 2025 12:20:07 +0400 Subject: [PATCH 57/98] Keep PRT hidden by default --- .../ipProtection/probabilisticRevealTokens/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 0f78dda742..7fb300a55c 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -82,6 +82,7 @@ const ProbabilisticRevealTokens = () => { header: 'PRT Prefix', accessorKey: 'prtHeader', cell: (info) => (info as string).slice(0, 10), + isHiddenByDefault: true, }, ], [] From 700cf384962f794b60acfa3844b89ebe9cf36d4d Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Mon, 22 Sep 2025 12:36:59 +0400 Subject: [PATCH 58/98] Add two more circles in stats header, so now it would be Domains, MDL, PRT, Signals for both Global and Site. --- .../probabilisticRevealTokens/index.tsx | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 7fb300a55c..c571d75e71 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -216,10 +216,20 @@ const ProbabilisticRevealTokens = () => { const stats = { site: [ + { + title: 'Domains', + centerCount: 2, + color: '#F3AE4E', + }, + { + title: 'MDL', + centerCount: 5, + color: '#4C79F4', + }, { title: 'PRT', centerCount: statistics.localView.totalTokens, - color: '#AF7AA3', + color: '#EC7159', onClick: () => setPresetFilters((prev) => ({ ...prev, @@ -231,7 +241,7 @@ const ProbabilisticRevealTokens = () => { { title: 'Signals', centerCount: statistics.localView.nonZeroSignal, - color: '#F54021', + color: '#5CC971', onClick: () => setPresetFilters((prev) => ({ ...prev, @@ -242,15 +252,25 @@ const ProbabilisticRevealTokens = () => { }, ], global: [ + { + title: 'Domains', + centerCount: 12, + color: '#F3AE4E', + }, + { + title: 'MDL', + centerCount: 50, + color: '#4C79F4', + }, { title: 'PRT', centerCount: statistics.globalView.totalTokens, - color: '#AF7AA3', + color: '#EC7159', }, { title: 'Signals', centerCount: statistics.globalView.nonZeroSignal, - color: '#F54021', + color: '#5CC971', }, ], }; From 4c21026c94219cf42be72ac6940326c978b57c39 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Mon, 22 Sep 2025 12:43:15 +0400 Subject: [PATCH 59/98] Reduce gap between circles --- .../pages/privacyProtection/mdlCommonPanel/stasHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index d3f21c3693..b5030514ac 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -97,7 +97,7 @@ const StatsHeader = ({ stats, setPillState }: StatsHeaderProps) => { />
-
+
{pillToggle === 'Site' ? sitePieCharts : globalPieCharts}
From 56e245970a92f45c7c25432658a60c925dd2af41 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Mon, 22 Sep 2025 13:19:30 +0400 Subject: [PATCH 60/98] Refactor header stats to center circles --- .../mdlCommonPanel/stasHeader.tsx | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index b5030514ac..d6b79dd963 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -83,24 +83,30 @@ const StatsHeader = ({ stats, setPillState }: StatsHeaderProps) => { const sitePieCharts = renderPieCharts(stats.site); const globalPieCharts = renderPieCharts(stats.global); + const pillToggleButtons = ( + + ); + return (
-
- -
-
-
- {pillToggle === 'Site' ? sitePieCharts : globalPieCharts} +
{pillToggleButtons}
+
+
+ {pillToggle === 'Site' ? sitePieCharts : globalPieCharts} +
-
+
+ {pillToggleButtons} +
{' '} + {/*Work around to center the circles div without using position absolute that would break the responsive design or hardcoding px values.*/}
); }; From 3810f6ff57a21764ba1f772ede1cb27005227e48 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 22 Sep 2025 16:02:33 +0530 Subject: [PATCH 61/98] Add MDL filtering and remove filters for the MDL common table. Remove redundant files. Add calculation logic for the prt store. --- packages/extension/src/store/PRTStore.ts | 12 +- .../extension/src/view/devtools/index.tsx | 8 +- .../probabilisticRevealTokens/index.tsx | 109 +++++++++++--- .../rowContextMenu.tsx | 134 ------------------ .../mdlCommonPanel/index.tsx | 8 +- .../scriptBlocking/mdlTable/index.tsx | 122 +++++----------- .../mdlTable/rowContextMenu.tsx | 126 ---------------- .../probabilisticRevealTokens/context.ts | 4 + .../probabilisticRevealTokensProvider.tsx | 25 +++- .../stateProviders/scriptBlocking/context.ts | 10 ++ .../stateProviders/scriptBlocking/index.ts | 5 +- .../scriptBlocking/scriptBlockingProvider.tsx | 61 ++++++++ 12 files changed, 246 insertions(+), 378 deletions(-) delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/rowContextMenu.tsx delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 97118d2846..d78041d81f 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -323,13 +323,21 @@ class PRTStore extends DataStore { acc.totalTokens = prtStatistics[key].totalTokens + acc.totalTokens; acc.nonZeroSignal = prtStatistics[key].nonZeroSignal + acc.nonZeroSignal; + let hostname = isValidURL(key) ? new URL(key).hostname : ''; + + hostname = hostname.startsWith('www.') + ? hostname.slice(4) + : hostname; + acc.mdl += hostname && this.mdlData[hostname] ? 1 : 0; } return acc; }, - { totalTokens: 0, nonZeroSignal: 0 } + { totalTokens: 0, nonZeroSignal: 0, domains: 0, mdl: 0 } ); - await chrome.runtime.sendMessage({ + globalStats.domains = Object.keys(prtStatistics).length; + + globalStats.mdl = await chrome.runtime.sendMessage({ type: TAB_TOKEN_DATA, payload: { tabId, diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx index ad775db459..2059303982 100644 --- a/packages/extension/src/view/devtools/index.tsx +++ b/packages/extension/src/view/devtools/index.tsx @@ -60,11 +60,11 @@ if (root) { - - + + - - + + diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index c571d75e71..276e05591b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -22,13 +22,16 @@ import { type TableColumn, type TableFilter, } from '@google-psat/design-system'; -import React, { useMemo, useState } from 'react'; -import { type PRTMetadata } from '@google-psat/common'; +import React, { useCallback, useMemo, useState } from 'react'; +import { isValidURL, type PRTMetadata } from '@google-psat/common'; /** * Internal dependencies */ -import { useProbabilisticRevealTokens } from '../../../../stateProviders'; +import { + useProbabilisticRevealTokens, + useScriptBlocking, +} from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommonPanel'; import getSignal from '../../../../../../utils/getSignal'; @@ -52,6 +55,11 @@ const ProbabilisticRevealTokens = () => { statistics: state.statistics, })); + const { scriptBlockingData, isLoading } = useScriptBlocking(({ state }) => ({ + scriptBlockingData: state.scriptBlockingData, + isLoading: state.isLoading, + })); + const tableColumns = useMemo( () => [ { @@ -151,11 +159,40 @@ const ProbabilisticRevealTokens = () => { selectedJSON, ]); + const mdlComparator = useCallback( + (value: InfoType, filterValue: string) => { + let hostname = isValidURL(value as string) + ? new URL(value as string).hostname + : ''; + + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + + if (!hostname || isLoading) { + return false; + } + + switch (filterValue) { + case 'True': + return ( + scriptBlockingData.filter( + (_data) => value && hostname === _data.domain + ).length > 0 + ); + case 'False': + return ( + scriptBlockingData.filter( + (_data) => value && hostname === _data.domain + ).length === 0 + ); + default: + return true; + } + }, + [isLoading, scriptBlockingData] + ); + const filters = useMemo( () => ({ - owner: { - title: 'Owner', - }, nonZeroUint8Signal: { title: 'Signal', hasStaticFilterValues: true, @@ -210,33 +247,67 @@ const ProbabilisticRevealTokens = () => { } }, }, + origin: { + title: 'MDL', + hasStaticFilterValues: true, + hasPrecalculatedFilterValues: true, + filterValues: { + True: { + selected: (preSetFilters?.filter?.mdl ?? []).includes('True'), + description: 'Domains that are in MDL', + }, + False: { + selected: (preSetFilters?.filter?.mdl ?? []).includes('False'), + description: 'Domains that are not in MDL', + }, + }, + comparator: (value: InfoType, filterValue: string) => + mdlComparator(value, filterValue), + }, }), - [preSetFilters?.filter?.nonZeroUint8Signal] + [ + preSetFilters?.filter?.mdl, + preSetFilters?.filter?.nonZeroUint8Signal, + mdlComparator, + ] ); const stats = { site: [ { title: 'Domains', - centerCount: 2, + centerCount: perTokenMetadata.length, color: '#F3AE4E', }, { title: 'MDL', - centerCount: 5, - color: '#4C79F4', - }, - { - title: 'PRT', - centerCount: statistics.localView.totalTokens, - color: '#EC7159', + centerCount: perTokenMetadata.filter(({ origin }) => { + let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; + + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + + if (!hostname) { + return false; + } + + return ( + scriptBlockingData.filter((_data) => _data.domain === hostname) + .length > 0 + ); + }).length, onClick: () => setPresetFilters((prev) => ({ ...prev, filter: { - nonZeroUint8Signal: ['PRT'], + mdl: ['True'], }, })), + color: '#4C79F4', + }, + { + title: 'PRT', + centerCount: statistics.localView.totalTokens, + color: '#EC7159', }, { title: 'Signals', @@ -246,7 +317,7 @@ const ProbabilisticRevealTokens = () => { setPresetFilters((prev) => ({ ...prev, filter: { - nonZeroUint8Signal: ['Signals'], + nonZeroUint8Signal: ['PRTs with signal'], }, })), }, @@ -254,12 +325,12 @@ const ProbabilisticRevealTokens = () => { global: [ { title: 'Domains', - centerCount: 12, + centerCount: statistics.globalView.domains, color: '#F3AE4E', }, { title: 'MDL', - centerCount: 50, + centerCount: statistics.globalView.mdl, color: '#4C79F4', }, { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/rowContextMenu.tsx deleted file mode 100644 index 3255138215..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/rowContextMenu.tsx +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies - */ -import React, { - forwardRef, - useCallback, - useImperativeHandle, - useMemo, - useState, -} from 'react'; -import { createPortal } from 'react-dom'; -import { isValidURL, type PRTMetadata } from '@google-psat/common'; -import type { TableRow } from '@google-psat/design-system'; -import { I18n } from '@google-psat/i18n'; - -const RowContextMenuForPRT = forwardRef<{ - onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; -}>(function RowContextMenu(_, ref) { - const [contextMenuOpen, setContextMenuOpen] = useState(false); - const [columnPosition, setColumnPosition] = useState({ - x: 0, - y: 0, - }); - const [prtMetaData, setMetadata] = useState(); - - const domain = useMemo( - () => - prtMetaData?.origin && isValidURL(prtMetaData?.origin) - ? new URL(prtMetaData?.origin).hostname - : '', - [prtMetaData] - ); - - const handleRightClick = useCallback( - (e: React.MouseEvent, { originalData }: TableRow) => { - e.preventDefault(); - const x = e.clientX, - y = e.clientY; - setColumnPosition({ x, y }); - document.body.style.overflow = contextMenuOpen ? 'auto' : 'hidden'; - setContextMenuOpen(!contextMenuOpen); - setMetadata(originalData as PRTMetadata); - }, - [contextMenuOpen] - ); - - useImperativeHandle(ref, () => ({ - onRowContextMenu(e, row) { - handleRightClick(e, row); - }, - })); - - const handleFilterClick = useCallback(() => { - const filter = `has-request-header:sec-probabilistic-reveal-token domain:${domain}`; - - // @ts-ignore - if (chrome.devtools.panels?.network?.show) { - // @ts-ignore - chrome.devtools.panels.network.show({ filter }); - setContextMenuOpen(false); - return; - } - - try { - // Need to do this since chrome doesnt allow the clipboard access in extension. - const copyFrom = document.createElement('textarea'); - copyFrom.textContent = filter; - document.body.appendChild(copyFrom); - copyFrom.select(); - document.execCommand('copy'); - copyFrom.blur(); - document.body.removeChild(copyFrom); - setContextMenuOpen(false); - } catch (error) { - //Fail silently - } - }, [domain]); - - return ( - <> - {domain && - contextMenuOpen && - createPortal( -
-
- -
-
setContextMenuOpen(false)} - className="absolute w-screen h-screen z-10 top-0 left-0" - /> -
, - document.body - )} - - ); -}); - -export default RowContextMenuForPRT; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index 65e297439c..d39530565f 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -39,7 +39,7 @@ import { I18n } from '@google-psat/i18n'; /** * Internal dependencies */ -import RowContextMenuForPRT from './rowContextMenu'; +import RowContextMenuForMDLTable from './rowContextMenu'; import StatsHeader, { type Stats } from './stasHeader'; type NonDecryptedJson = PRTMetadata & ProbablisticRevealToken; @@ -81,7 +81,7 @@ const MdlCommonPanel = ({ tab = '', }: MdlCommonPanelProps) => { const rowContextMenuRef = useRef | null>(null); const [pillState, setPillState] = useState('Site'); @@ -105,7 +105,7 @@ const MdlCommonPanel = ({
- +
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 7f1dc15468..5d24131744 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -24,84 +24,29 @@ import { Link, type InfoType, } from '@google-psat/design-system'; -import React, { useEffect, useMemo, useState, useCallback } from 'react'; +import React, { useMemo, useState, useCallback } from 'react'; import type { MDLTableData } from '@google-psat/common'; /** * Internal dependencies */ -import { useScriptBlocking } from '../../../../stateProviders'; +import { + useScriptBlocking, + IMPACTED_BY_SCRIPT_BLOCKING, +} from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommonPanel'; import Legend from './legend'; -export const IMPACTED_BY_SCRIPT_BLOCKING = { - NONE: 'Not Impacted By Script Blocking', - PARTIAL: 'Some URLs are Blocked', - ENTIRE: 'Entire Domain Blocked', -}; - -const DATA_URL = - 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md'; - const MDLTable = () => { const [selectedKey, setSelectedKey] = useState(null); const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); - const [isLoading, setIsLoading] = useState(true); - const { uniqueResponseDomains, statistics } = useScriptBlocking( - ({ state }) => ({ + const { uniqueResponseDomains, statistics, scriptBlockingData, isLoading } = + useScriptBlocking(({ state }) => ({ uniqueResponseDomains: state.uniqueResponseDomains, statistics: state.statistics, - }) - ); - - const [initialTableData, setinitialTableData] = useState< - { domain: string; owner: string; scriptBlocking: string }[] - >([]); - - useEffect(() => { - (async () => { - setIsLoading(true); - const response = await fetch(DATA_URL); - - if (!response.ok) { - setIsLoading(false); - throw new Error(`HTTP error! status: ${response.status}`); - } - - const text = await response.text(); - - const lines = text - .split('\n') - .filter((line) => line.includes('|')) - .slice(2); - - const mdlData = lines - .map((line) => line.split('|').map((item) => item.trim())) - .filter((item) => item[2] !== IMPACTED_BY_SCRIPT_BLOCKING.NONE); - - setinitialTableData(() => { - const data = mdlData.map((item: string[]) => { - let owner = item[1]; - - if (item[1].includes('PSL Domain')) { - owner = 'PSL Domain'; - } - - const scriptBlocking = item[2]; - - return { - domain: item[0], - owner, - scriptBlocking, - }; - }); - - setIsLoading(false); - - return data; - }); - })(); - }, []); + scriptBlockingData: state.scriptBlockingData, + isLoading: state.isLoading, + })); const checkbox = useCallback( () => ( @@ -119,34 +64,39 @@ const MDLTable = () => { ); const tableData: MDLTableData[] = useMemo(() => { - if (initialTableData.length === 0) { + if (scriptBlockingData.length === 0) { return []; } const data: MDLTableData[] = []; - initialTableData.forEach((item) => { - let available = false; - if (uniqueResponseDomains.includes(item.domain)) { - available = true; - } - - const canPush = showOnlyHighlighted ? available : true; - - if (canPush) { - data.push({ - ...item, - highlighted: available, - highlightedClass: - available && item.scriptBlocking.startsWith('Some URLs are Blocked') - ? 'bg-amber-100' - : '', - } as MDLTableData); - } - }); + scriptBlockingData + .filter( + (item) => item.scriptBlocking !== IMPACTED_BY_SCRIPT_BLOCKING.NONE + ) + .forEach((item) => { + let available = false; + if (uniqueResponseDomains.includes(item.domain)) { + available = true; + } + + const canPush = showOnlyHighlighted ? available : true; + + if (canPush) { + data.push({ + ...item, + highlighted: available, + highlightedClass: + available && + item.scriptBlocking.startsWith('Some URLs are Blocked') + ? 'bg-amber-100' + : '', + } as MDLTableData); + } + }); return data.sort((a, b) => Number(b.highlighted) - Number(a.highlighted)); - }, [uniqueResponseDomains, initialTableData, showOnlyHighlighted]); + }, [uniqueResponseDomains, scriptBlockingData, showOnlyHighlighted]); const tableColumns = useMemo( () => [ diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx deleted file mode 100644 index 155c71f889..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/rowContextMenu.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies - */ -import React, { - forwardRef, - useCallback, - useImperativeHandle, - useState, -} from 'react'; -import { createPortal } from 'react-dom'; -import { type MDLTableData } from '@google-psat/common'; -import type { TableRow } from '@google-psat/design-system'; -import { I18n } from '@google-psat/i18n'; - -const RowContextMenuForScriptBlocking = forwardRef<{ - onRowContextMenu: (e: React.MouseEvent, row: TableRow) => void; -}>(function RowContextMenu(_, ref) { - const [contextMenuOpen, setContextMenuOpen] = useState(false); - const [columnPosition, setColumnPosition] = useState({ - x: 0, - y: 0, - }); - const [rowData, setRowData] = useState(); - - const handleRightClick = useCallback( - (e: React.MouseEvent, { originalData }: TableRow) => { - e.preventDefault(); - const x = e.clientX, - y = e.clientY; - setColumnPosition({ x, y }); - document.body.style.overflow = contextMenuOpen ? 'auto' : 'hidden'; - setContextMenuOpen(!contextMenuOpen); - setRowData(originalData as MDLTableData); - }, - [contextMenuOpen] - ); - - useImperativeHandle(ref, () => ({ - onRowContextMenu(e, row) { - handleRightClick(e, row); - }, - })); - - const handleFilterClick = useCallback(() => { - const filter = `domain:${rowData?.domain}`; - - // @ts-ignore - if (chrome.devtools.panels?.network?.show && rowData?.domain) { - // @ts-ignore - chrome.devtools.panels.network.show({ filter }); - setContextMenuOpen(false); - return; - } - - try { - // Need to do this since chrome doesnt allow the clipboard access in extension. - const copyFrom = document.createElement('textarea'); - copyFrom.textContent = filter; - document.body.appendChild(copyFrom); - copyFrom.select(); - document.execCommand('copy'); - copyFrom.blur(); - document.body.removeChild(copyFrom); - setContextMenuOpen(false); - } catch (error) { - //Fail silently - } - }, [rowData]); - - return ( - <> - {rowData?.domain && - rowData?.highlighted && - contextMenuOpen && - createPortal( -
-
- -
-
setContextMenuOpen(false)} - className="absolute w-screen h-screen z-10 top-0 left-0" - /> -
, - document.body - )} - - ); -}); - -export default RowContextMenuForScriptBlocking; diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts index 10e0dadbb9..02e0fddacd 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/context.ts @@ -28,6 +28,8 @@ type PRTStatistics = { globalView: { totalTokens: number; nonZeroSignal: number; + mdl: number; + domains: number; }; localView: { totalTokens: number; @@ -59,6 +61,8 @@ export const initialState: ProbabilisticRevealTokensContextType = { globalView: { totalTokens: 0, nonZeroSignal: 0, + mdl: 0, + domains: 0, }, }, }, diff --git a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx index c3d1b3d842..7ed1ed430a 100644 --- a/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/probabilisticRevealTokens/probabilisticRevealTokensProvider.tsx @@ -33,6 +33,8 @@ import Context, { type ProbabilisticRevealTokensContextType, } from './context'; import { TAB_TOKEN_DATA } from '../../../../constants'; +import { useScriptBlocking } from '../scriptBlocking'; +import { isValidURL } from '@google-psat/common'; const Provider = ({ children }: PropsWithChildren) => { const [decryptedTokens, setDecryptedTokens] = useState< @@ -55,6 +57,10 @@ const Provider = ({ children }: PropsWithChildren) => { ProbabilisticRevealTokensContextType['state']['perTokenMetadata'] >([]); + const { scriptBlockingData } = useScriptBlocking(({ state }) => ({ + scriptBlockingData: state.scriptBlockingData, + })); + const messagePassingListener = useCallback( (message: { type: string; @@ -160,12 +166,27 @@ const Provider = ({ children }: PropsWithChildren) => { acc.totalTokens = prtStatistics[key].totalTokens + acc.totalTokens; acc.nonZeroSignal = prtStatistics[key].nonZeroSignal + acc.nonZeroSignal; + + let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; + + hostname = hostname.startsWith('www.') + ? hostname.slice(4) + : hostname; + + acc.mdl += + hostname && + scriptBlockingData.filter((_data) => _data.domain === hostname) + .length > 0 + ? 1 + : 0; } return acc; }, - { totalTokens: 0, nonZeroSignal: 0 } + { totalTokens: 0, nonZeroSignal: 0, domains: 0, mdl: 0 } ); + globalStats.domains = Object.keys(prtStatistics).length; + if (hasChangesForPrtStatisticsData) { setStatistics((prev) => { return { @@ -175,7 +196,7 @@ const Provider = ({ children }: PropsWithChildren) => { }); } }, - [] + [scriptBlockingData] ); useEffect(() => { diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts index a33d220d21..c98a67eb84 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/context.ts @@ -29,10 +29,18 @@ type ScriptBlockingStatistics = { }; }; +type ScriptBlockingData = { + domain: string; + owner: string; + scriptBlocking: string; +}[]; + export interface ScriptBlockingStoreContext { state: { uniqueResponseDomains: string[]; statistics: ScriptBlockingStatistics; + scriptBlockingData: ScriptBlockingData; + isLoading: boolean; }; actions: { setUniqueResponseDomains: (newValue: string[]) => void; @@ -52,6 +60,8 @@ export const initialState: ScriptBlockingStoreContext = { completelyBlockedDomains: 0, }, }, + scriptBlockingData: [], + isLoading: false, }, actions: { setUniqueResponseDomains: noop, diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts index 122c8f96ea..ea26de29c0 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/index.ts @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { default as ScriptBlockingProvider } from './scriptBlockingProvider'; +export { + default as ScriptBlockingProvider, + IMPACTED_BY_SCRIPT_BLOCKING, +} from './scriptBlockingProvider'; export { default as ScriptBlockingContext } from './context'; export { default as useScriptBlocking } from './useScriptBlocking'; diff --git a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx index c6216685d0..5b010b8ea0 100644 --- a/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/scriptBlocking/scriptBlockingProvider.tsx @@ -33,15 +33,74 @@ import Context, { } from './context'; import { EXTRA_DATA } from '../../../../constants'; +export const IMPACTED_BY_SCRIPT_BLOCKING = { + NONE: 'Not Impacted By Script Blocking', + PARTIAL: 'Some URLs are Blocked', + ENTIRE: 'Entire Domain Blocked', +}; + +const DATA_URL = + 'https://raw.githubusercontent.com/GoogleChrome/ip-protection/refs/heads/main/Masked-Domain-List.md'; + const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { const [uniqueResponseDomains, setUniqueResponseDomains] = useState( [] ); + const [isLoading, setIsLoading] = useState(true); const [statistics, setStatistics] = useState< ScriptBlockingStoreContext['state']['statistics'] >(initialState.state.statistics); + const [initialTableData, setinitialTableData] = useState< + { domain: string; owner: string; scriptBlocking: string }[] + >([]); + + useEffect(() => { + (async () => { + setIsLoading(true); + const response = await fetch(DATA_URL); + + if (!response.ok) { + setIsLoading(false); + throw new Error(`HTTP error! status: ${response.status}`); + } + + const text = await response.text(); + + const lines = text + .split('\n') + .filter((line) => line.includes('|')) + .slice(2); + + const mdlData = lines.map((line) => + line.split('|').map((item) => item.trim()) + ); + + setinitialTableData(() => { + const data = mdlData.map((item: string[]) => { + let owner = item[1]; + + if (item[1].includes('PSL Domain')) { + owner = 'PSL Domain'; + } + + const scriptBlocking = item[2]; + + return { + domain: item[0], + owner, + scriptBlocking, + }; + }); + + setIsLoading(false); + + return data; + }); + })(); + }, []); + const messagePassingListener = useCallback( (message: { type: string; @@ -112,6 +171,8 @@ const ScriptBlockingProvider = ({ children }: PropsWithChildren) => { state: { uniqueResponseDomains, statistics, + scriptBlockingData: initialTableData, + isLoading, }, actions: { setUniqueResponseDomains, From e3defcd76cc541f4d604b875416be5b3d09f624a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 22 Sep 2025 16:34:19 +0530 Subject: [PATCH 62/98] Add click events to scriptBlocking stats. --- .../mdlCommonPanel/rowContextMenu.tsx | 15 ++-- .../scriptBlocking/mdlTable/index.tsx | 75 ++++++++++++------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx index b1f0649cc5..9a68f9d17e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx @@ -48,25 +48,28 @@ const RowContextMenuForPRT = forwardRef< y: 0, }); const [data, setMetadata] = useState(null); - const domain = useMemo(() => { if (!data) { return null; } - if ((data as PRTMetadata)?.origin) { + if (tab === 'PRT') { if (isValidURL((data as PRTMetadata)?.origin)) { return new URL((data as PRTMetadata)?.origin).hostname; } else { return ''; } - } else { + } + + if (tab === 'scriptBlocking') { if ((data as MDLTableData)?.domain) { - return (data as MDLTableData)?.domain; + return (data as MDLTableData)?.highlighted + ? (data as MDLTableData)?.domain + : ''; } } return ''; - }, [data]); + }, [data, tab]); const handleRightClick = useCallback( (e: React.MouseEvent, { originalData }: TableRow) => { @@ -113,7 +116,7 @@ const RowContextMenuForPRT = forwardRef< } catch (error) { //Fail silently } - }, [domain]); + }, [domain, tab]); return ( <> diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 5d24131744..a783bd2166 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -40,6 +40,9 @@ import Legend from './legend'; const MDLTable = () => { const [selectedKey, setSelectedKey] = useState(null); const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); + const [preSetFilters, setPresetFilters] = useState<{ + [key: string]: Record; + }>({ filter: {} }); const { uniqueResponseDomains, statistics, scriptBlockingData, isLoading } = useScriptBlocking(({ state }) => ({ uniqueResponseDomains: state.uniqueResponseDomains, @@ -135,30 +138,36 @@ const MDLTable = () => { [] ); - const calculateFilters = useCallback((data: MDLTableData[]) => { - const _filters: { - [key: string]: { - selected: boolean; - description: string; - }; - } = {}; - - const titleMap = { - COMPLETE: 'Scope Complete', - PARTIAL: 'Scope Partial', - }; - - data.forEach((singleData) => { - _filters[titleMap[singleData.scriptBlocking as keyof typeof titleMap]] = { - selected: false, - description: IMPACTED_BY_SCRIPT_BLOCKING[ - singleData.scriptBlocking as keyof typeof IMPACTED_BY_SCRIPT_BLOCKING - ] as string, + const calculateFilters = useCallback( + (data: MDLTableData[]) => { + const _filters: { + [key: string]: { + selected: boolean; + description: string; + }; + } = {}; + + const titleMap = { + 'Entire Domain Blocked': 'Scope Complete', + 'Some URLs are Blocked': 'Scope Partial', }; - }); - return _filters; - }, []); + data.forEach((singleData) => { + _filters[titleMap[singleData.scriptBlocking as keyof typeof titleMap]] = + { + selected: preSetFilters?.filter?.scriptBlocking?.includes( + titleMap[singleData.scriptBlocking as keyof typeof titleMap] + ), + description: IMPACTED_BY_SCRIPT_BLOCKING[ + singleData.scriptBlocking as keyof typeof IMPACTED_BY_SCRIPT_BLOCKING + ] as string, + }; + }); + + return _filters; + }, + [preSetFilters?.filter?.scriptBlocking] + ); const filters = useMemo( () => ({ @@ -172,10 +181,10 @@ const MDLTable = () => { filterValues: calculateFilters(tableData), comparator: (value: InfoType, filterValue: string) => { switch (filterValue) { - case 'COMPLETE': - return value === 'COMPLETE'; - case 'PARTIAL': - return value === 'PARTIAL'; + case 'Scope Complete': + return value === 'Entire Domain Blocked'; + case 'Scope Partial': + return value === 'Some URLs are Blocked'; default: return false; } @@ -191,11 +200,25 @@ const MDLTable = () => { title: 'Scope Complete', centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Scope Complete'], + }, + })), }, { title: 'Scope Partial', centerCount: statistics.localView.partiallyBlockedDomains, color: '#4C79F4', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Scope Partial'], + }, + })), }, ], global: [ From a7594fccd97c1fb760de256ca64c14bcd5f3d439 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 22 Sep 2025 17:07:06 +0530 Subject: [PATCH 63/98] Change design for empty circle chart --- .../src/components/circlePieChart/emptyCirclePieChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx b/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx index b09f63dbbd..d9ee3aa226 100644 --- a/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx +++ b/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx @@ -28,11 +28,11 @@ const EmptyCirclePieChart = () => {
-

+

0

From bbdd2ff52fc046c5292a68581d55b1394c0525db Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 22 Sep 2025 17:11:23 +0530 Subject: [PATCH 64/98] Change names --- .../scriptBlocking/mdlTable/index.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index a783bd2166..8e79ed3457 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -37,6 +37,11 @@ import { import MdlCommonPanel from '../../mdlCommonPanel'; import Legend from './legend'; +const titleMap = { + 'Entire Domain Blocked': 'Scope Complete', + 'Some URLs are Blocked': 'Scope Partial', +}; + const MDLTable = () => { const [selectedKey, setSelectedKey] = useState(null); const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); @@ -132,7 +137,7 @@ const MDLTable = () => { { header: 'Scope', accessorKey: 'scriptBlocking', - cell: (info) => info, + cell: (info) => titleMap[info as keyof typeof titleMap].slice(6), }, ], [] @@ -147,11 +152,6 @@ const MDLTable = () => { }; } = {}; - const titleMap = { - 'Entire Domain Blocked': 'Scope Complete', - 'Some URLs are Blocked': 'Scope Partial', - }; - data.forEach((singleData) => { _filters[titleMap[singleData.scriptBlocking as keyof typeof titleMap]] = { @@ -175,7 +175,7 @@ const MDLTable = () => { title: 'Owner', }, scriptBlocking: { - title: 'Impacted by Script Blocking', + title: 'Scope', hasStaticFilterValues: true, hasPrecalculatedFilterValues: true, filterValues: calculateFilters(tableData), From c1fb335be5f8d4dfc5ed428192c77494a3bf76ef Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 22 Sep 2025 18:21:51 +0530 Subject: [PATCH 65/98] Move sync storage to session storage, Add total domains and total blockings circle. Add css classes to show disabled table. --- .../runtimeOnInstalledListener.ts | 16 +--- .../chromeListeners/runtimeStartUpListener.ts | 9 ++- packages/extension/src/serviceWorker/index.ts | 2 +- packages/extension/src/store/PRTStore.ts | 73 +++++++------------ .../mdlCommonPanel/index.tsx | 12 ++- .../mdlCommonPanel/stasHeader.tsx | 7 +- .../scriptBlocking/mdlTable/index.tsx | 53 ++++++++++---- .../probabilisticRevealTokensProvider.tsx | 10 +-- .../stateProviders/scriptBlocking/context.ts | 4 + .../scriptBlocking/scriptBlockingProvider.tsx | 5 +- 10 files changed, 99 insertions(+), 92 deletions(-) diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts index 698f27a5d4..87da4d6267 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts @@ -31,8 +31,6 @@ export const runtimeOnInstalledListener = async ( await chrome.storage.sync.set({ isUsingCDP: false, isFirstTime: true, - prtStatistics: {}, - scriptBlocking: {}, }); await updateGlobalVariableAndAttachCDP(); } @@ -43,11 +41,7 @@ export const runtimeOnInstalledListener = async ( await updateGlobalVariableAndAttachCDP(); - if ( - Object.keys(preSetSettings).includes('isUsingCDP') && - Object.keys(preSetSettings).includes('prtStatistics') && - Object.keys(preSetSettings).includes('scriptBlocking') - ) { + if (Object.keys(preSetSettings).includes('isUsingCDP')) { return; } @@ -55,14 +49,6 @@ export const runtimeOnInstalledListener = async ( objectToInstantiate['isUsingCDP'] = false; } - if (!Object.keys(preSetSettings).includes('prtStatistics')) { - objectToInstantiate['prtStatistics'] = {}; - } - - if (!Object.keys(preSetSettings).includes('scriptBlocking')) { - objectToInstantiate['scriptBlocking'] = {}; - } - await chrome.storage.sync.clear(); await chrome.storage.sync.set({ ...objectToInstantiate }); } diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts index b501ac671c..f013fb4afd 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts @@ -22,21 +22,22 @@ import { setupIntervals } from './utils'; export const onStartUpListener = async () => { const storage = await chrome.storage.sync.get(); + const sessionStorage = await chrome.storage.session.get(); setupIntervals(); if (Object.keys(storage).includes('isUsingCDP')) { DataStore.globalIsUsingCDP = storage.isUsingCDP; } - if (Object.keys(storage).includes('prtStatistics')) { + if (Object.keys(sessionStorage).includes('prtStatistics')) { PRTStore.statistics.prtStatistics.globalView = { - ...storage?.prtStatistics, + ...sessionStorage?.prtStatistics, }; } - if (Object.keys(storage).includes('prtStatistics')) { + if (Object.keys(sessionStorage).includes('scriptBlocking')) { PRTStore.statistics.scriptBlocking.globalView = { - ...storage?.scriptBlocking, + ...sessionStorage?.scriptBlocking, }; } }; diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 21b2fc2b8c..b2f2b6f729 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -426,7 +426,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { updateStatistics(origin, nonZeroUint8Signal); - await chrome.storage.sync.set({ + await chrome.storage.session.set({ prtStatistics: { ...PRTStore.statistics.prtStatistics.globalView, }, diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index d78041d81f..9c2151c76e 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -90,10 +90,12 @@ class PRTStore extends DataStore { globalView: { partiallyBlockedDomains: number; completelyBlockedDomains: number; + domains: number; }; localView: { partiallyBlockedDomains: number; completelyBlockedDomains: number; + domains: number; }; }; } = { @@ -105,10 +107,12 @@ class PRTStore extends DataStore { globalView: { partiallyBlockedDomains: 0, completelyBlockedDomains: 0, + domains: 0, }, localView: { partiallyBlockedDomains: 0, completelyBlockedDomains: 0, + domains: 0, }, }, }; @@ -212,28 +216,24 @@ class PRTStore extends DataStore { this.uniqueResponseDomains[tabId].push(hostname); DataStore.tabs[tabId].newUpdatesScriptBlocking++; - chrome.storage.sync.get('scriptBlocking', (result) => { - const completelyBlockedDomains = - result.scriptBlocking?.completelyBlockedDomains ?? 0; - const partiallyBlockedDomains = - result.prtStatistics?.partiallyBlockedDomains ?? 0; - - chrome.storage.sync.set({ - scriptBlocking: { - completelyBlockedDomains: - completelyBlockedDomains + - (this.mdlData[hostname] && - this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' - ? 1 - : 0), - partiallyBlockedDomains: - partiallyBlockedDomains + - (this.mdlData[hostname] && - this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' - ? 1 - : 0), - }, - }); + this.statistics.scriptBlocking.globalView.completelyBlockedDomains += + this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' ? 1 : 0; + this.statistics.scriptBlocking.globalView.partiallyBlockedDomains += + this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' ? 1 : 0; + + this.statistics.scriptBlocking.globalView.domains += 1; + + this.statistics.scriptBlocking.localView.completelyBlockedDomains += + this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' ? 1 : 0; + this.statistics.scriptBlocking.localView.partiallyBlockedDomains += + this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' ? 1 : 0; + + this.statistics.scriptBlocking.localView.domains += 1; + + chrome.storage.session.set({ + scriptBlocking: { + ...this.statistics.scriptBlocking.globalView, + }, }); } } @@ -263,6 +263,7 @@ class PRTStore extends DataStore { this.statistics.scriptBlocking.localView = { partiallyBlockedDomains: 0, completelyBlockedDomains: 0, + domains: 0, }; this.uniqueResponseDomains[parseInt(tabId)] = []; //@ts-ignore @@ -299,7 +300,7 @@ class PRTStore extends DataStore { }; //@ts-ignore - const { prtStatistics = {} } = await chrome.storage.sync.get( + const { prtStatistics = {} } = await chrome.storage.session.get( 'prtStatistics' ); @@ -366,41 +367,19 @@ class PRTStore extends DataStore { DataStore.tabs[tabId].newUpdatesScriptBlocking > 0) ) { //@ts-ignore - const { scriptBlocking = {} } = await chrome.storage.sync.get( + const { scriptBlocking = {} } = await chrome.storage.session.get( 'scriptBlocking' ); const globalView = scriptBlocking; - const localView = this.uniqueResponseDomains[tabId].reduce( - (acc, domain) => { - if (!this.mdlData[domain]) { - return acc; - } - - if (this.mdlData[domain].scriptBlockingScope === 'COMPLETE') { - acc.completelyBlockedDomains += 1; - } - - if (this.mdlData[domain].scriptBlockingScope === 'PARTIAL') { - acc.partiallyBlockedDomains += 1; - } - - return acc; - }, - { - partiallyBlockedDomains: 0, - completelyBlockedDomains: 0, - } - ); - await chrome.runtime.sendMessage({ type: EXTRA_DATA, payload: { uniqueResponseDomains: this.uniqueResponseDomains[tabId], stats: { globalView, - localView, + localView: this.statistics.scriptBlocking.localView, }, tabId: Number(tabId), }, diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index d39530565f..73b6728ea3 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -35,7 +35,7 @@ import { type PRTMetadata, } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; - +import classNames from 'classnames'; /** * Internal dependencies */ @@ -102,7 +102,15 @@ const MdlCommonPanel = ({ className="w-full flex flex-col" trayId="active-sources-table-bottom-tray" > -
+
void; + data?: { count: number; color: string }[]; }; export type Stats = { @@ -47,7 +48,7 @@ const StatsHeader = ({ stats, setPillState }: StatsHeaderProps) => { const renderPieCharts = (items: StatItem[]) => ( <> - {items.map(({ title, centerCount, color, onClick }, index) => ( + {items.map(({ title, centerCount, color, onClick, data }, index) => ( - ))} + style={{ cursor: !onClick ? 'default' : 'pointer' }} + onClick={() => { + onClick?.(); + }} + > + + + ) + )} ); From 0a54afa9f9492d723c3f0dea8f4387ba9f912b93 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 23 Sep 2025 13:35:24 +0530 Subject: [PATCH 68/98] Add another circle and optional chaining --- packages/extension/src/store/PRTStore.ts | 8 ++++---- .../scriptBlocking/mdlTable/index.tsx | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/store/PRTStore.ts b/packages/extension/src/store/PRTStore.ts index 9c2151c76e..6036807c23 100644 --- a/packages/extension/src/store/PRTStore.ts +++ b/packages/extension/src/store/PRTStore.ts @@ -217,16 +217,16 @@ class PRTStore extends DataStore { DataStore.tabs[tabId].newUpdatesScriptBlocking++; this.statistics.scriptBlocking.globalView.completelyBlockedDomains += - this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' ? 1 : 0; + this.mdlData[hostname]?.scriptBlockingScope === 'COMPLETE' ? 1 : 0; this.statistics.scriptBlocking.globalView.partiallyBlockedDomains += - this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' ? 1 : 0; + this.mdlData[hostname]?.scriptBlockingScope === 'PARTIAL' ? 1 : 0; this.statistics.scriptBlocking.globalView.domains += 1; this.statistics.scriptBlocking.localView.completelyBlockedDomains += - this.mdlData[hostname].scriptBlockingScope === 'COMPLETE' ? 1 : 0; + this.mdlData[hostname]?.scriptBlockingScope === 'COMPLETE' ? 1 : 0; this.statistics.scriptBlocking.localView.partiallyBlockedDomains += - this.mdlData[hostname].scriptBlockingScope === 'PARTIAL' ? 1 : 0; + this.mdlData[hostname]?.scriptBlockingScope === 'PARTIAL' ? 1 : 0; this.statistics.scriptBlocking.localView.domains += 1; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 34e3c190f1..d6eeeec6dd 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -202,6 +202,13 @@ const MDLTable = () => { centerCount: statistics.localView.domains, color: '#25ACAD', }, + { + title: 'Block List Domains', + centerCount: + statistics.localView.partiallyBlockedDomains + + statistics.localView.completelyBlockedDomains, + color: '#7D8471', + }, { title: 'Scope Complete', centerCount: statistics.localView.completelyBlockedDomains, @@ -233,6 +240,13 @@ const MDLTable = () => { centerCount: statistics.globalView.domains, color: '#25ACAD', }, + { + title: 'Block List Domains', + centerCount: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#7D8471', + }, { title: 'Total Blockings', centerCount: From dac78e3376294d0d56ce73fd3323c6e17114eb06 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 23 Sep 2025 13:26:39 +0400 Subject: [PATCH 69/98] Add tooltip to signals --- .../circlePieChart/emptyCirclePieChart.tsx | 10 ++++++- .../src/components/circlePieChart/index.tsx | 17 ++++------- .../src/components/circlePieChart/tooltip.tsx | 29 +++++++++++++++++++ 3 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 packages/design-system/src/components/circlePieChart/tooltip.tsx diff --git a/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx b/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx index d9ee3aa226..7548cbb95c 100644 --- a/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx +++ b/packages/design-system/src/components/circlePieChart/emptyCirclePieChart.tsx @@ -22,8 +22,15 @@ import { VictoryPie } from 'victory-pie'; * Internal dependencies */ import { COLOR_MAP } from '../../theme/colors'; +import Tooltip from './tooltip'; -const EmptyCirclePieChart = () => { +const EmptyCirclePieChart = ({ + tooltipText = '', + showTooltip = false, +}: { + tooltipText?: string; + showTooltip?: boolean; +}) => { return (
{

0

+ {tooltipText && showTooltip && }
); }; diff --git a/packages/design-system/src/components/circlePieChart/index.tsx b/packages/design-system/src/components/circlePieChart/index.tsx index 77088fe19a..13958ff5b3 100644 --- a/packages/design-system/src/components/circlePieChart/index.tsx +++ b/packages/design-system/src/components/circlePieChart/index.tsx @@ -24,6 +24,7 @@ import classNames from 'classnames'; * Internal dependencies. */ import EmptyCirclePieChart from './emptyCirclePieChart'; +import Tooltip from './tooltip'; interface CirclePieChartProps { centerCount: number; @@ -59,7 +60,10 @@ const CirclePieChart = ({ >
{centerCount <= 0 ? ( - + ) : (
{tooltipText && showTooltip && ( -
- {tooltipText} -
+ )}
)} diff --git a/packages/design-system/src/components/circlePieChart/tooltip.tsx b/packages/design-system/src/components/circlePieChart/tooltip.tsx new file mode 100644 index 0000000000..f0e3ae7765 --- /dev/null +++ b/packages/design-system/src/components/circlePieChart/tooltip.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +const Tooltip = ({ tooltipText }: { tooltipText?: string }) => { + return ( +
+ {tooltipText} +
+ ); +}; + +export default Tooltip; From 7f67b439aaa12530e776951dbcf9265132baa2b6 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 23 Sep 2025 14:55:12 +0400 Subject: [PATCH 70/98] Add tooltip to the script blocking --- .../privacyProtection/scriptBlocking/mdlTable/index.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index d6eeeec6dd..3cf6d71406 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -201,6 +201,7 @@ const MDLTable = () => { title: 'Total Domains', centerCount: statistics.localView.domains, color: '#25ACAD', + tooltipText: 'All page domains', }, { title: 'Block List Domains', @@ -208,11 +209,13 @@ const MDLTable = () => { statistics.localView.partiallyBlockedDomains + statistics.localView.completelyBlockedDomains, color: '#7D8471', + tooltipText: 'Matches in block list', }, { title: 'Scope Complete', centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', + tooltipText: 'Fully blocked domains', onClick: () => setPresetFilters((prev) => ({ ...prev, @@ -225,6 +228,7 @@ const MDLTable = () => { title: 'Scope Partial', centerCount: statistics.localView.partiallyBlockedDomains, color: '#4C79F4', + tooltipText: 'Partially blocked domains', onClick: () => setPresetFilters((prev) => ({ ...prev, @@ -239,6 +243,7 @@ const MDLTable = () => { title: 'Total Domains', centerCount: statistics.globalView.domains, color: '#25ACAD', + tooltipText: 'All page domains', }, { title: 'Block List Domains', @@ -246,6 +251,7 @@ const MDLTable = () => { statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, color: '#7D8471', + tooltipText: 'Matches in block list', }, { title: 'Total Blockings', @@ -262,16 +268,19 @@ const MDLTable = () => { count: statistics.globalView.completelyBlockedDomains, }, ], + tooltipText: 'Blocked domains', }, { title: 'Scope Complete', centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', + tooltipText: 'Fully blocked domains', }, { title: 'Scope Partial', centerCount: statistics.globalView.partiallyBlockedDomains, color: '#4C79F4', + tooltipText: 'Partially blocked domains', }, ], }; From 7a46272958d95ac5c037d1717dd1fcf3d45c4de4 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Tue, 23 Sep 2025 17:59:01 +0400 Subject: [PATCH 71/98] Improve tooltip text --- .../ipProtection/probabilisticRevealTokens/index.tsx | 8 ++++---- .../scriptBlocking/mdlTable/index.tsx | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index b374854458..285e95d28e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -331,25 +331,25 @@ const ProbabilisticRevealTokens = () => { title: 'Domains', centerCount: statistics.globalView.domains, color: '#F3AE4E', - tooltipText: 'Unique domains on page', + tooltipText: 'Total unique domains in browsing session', }, { title: 'MDL', centerCount: statistics.globalView.mdl, color: '#4C79F4', - tooltipText: 'Page domains in MDL', + tooltipText: 'Browsing session domains in MDL', }, { title: 'PRT', centerCount: statistics.globalView.totalTokens, color: '#EC7159', - tooltipText: 'Unique tokens sent in requests', + tooltipText: 'Total unique tokens sent in requests', }, { title: 'Signals', centerCount: statistics.globalView.nonZeroSignal, color: '#5CC971', - tooltipText: 'PRTs that decode to IP address', + tooltipText: 'Total PRTs that decode to IP address', }, ], }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 3cf6d71406..c3bdcd3123 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -209,13 +209,13 @@ const MDLTable = () => { statistics.localView.partiallyBlockedDomains + statistics.localView.completelyBlockedDomains, color: '#7D8471', - tooltipText: 'Matches in block list', + tooltipText: 'Page domains in block list', }, { title: 'Scope Complete', centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', - tooltipText: 'Fully blocked domains', + tooltipText: 'Completely blocked domains', onClick: () => setPresetFilters((prev) => ({ ...prev, @@ -243,7 +243,7 @@ const MDLTable = () => { title: 'Total Domains', centerCount: statistics.globalView.domains, color: '#25ACAD', - tooltipText: 'All page domains', + tooltipText: 'Total browsing session domains', }, { title: 'Block List Domains', @@ -251,7 +251,7 @@ const MDLTable = () => { statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, color: '#7D8471', - tooltipText: 'Matches in block list', + tooltipText: 'Total domains in block list', }, { title: 'Total Blockings', @@ -274,7 +274,7 @@ const MDLTable = () => { title: 'Scope Complete', centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', - tooltipText: 'Fully blocked domains', + tooltipText: 'Completely blocked domains', }, { title: 'Scope Partial', From 6bf86a84e32a785c59e77666a191dae2c0fd851a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 24 Sep 2025 21:39:14 +0530 Subject: [PATCH 72/98] Refactor MDLTable and MdlCommonPanel components; update props and remove unused features --- .../ipProtection/mdlTable/index.tsx | 29 +-------- .../mdlCommonPanel/index.tsx | 4 +- .../scriptBlocking/index.tsx | 13 ++++ .../scriptBlocking/mdlTable/index.tsx | 64 ++++++++----------- 4 files changed, 43 insertions(+), 67 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx index a708df00ff..1862938bde 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx @@ -28,7 +28,7 @@ import { Link, ResizableTray, } from '@google-psat/design-system'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; /** * Internal dependencies @@ -49,26 +49,10 @@ const MDLTable = () => { }[] >([]); - const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); - const { uniqueResponseDomains } = useScriptBlocking(({ state }) => ({ uniqueResponseDomains: state.uniqueResponseDomains, })); - const checkbox = useCallback(() => { - return ( - - ); - }, []); - useEffect(() => { (async () => { const data = await fetch( @@ -114,13 +98,6 @@ const MDLTable = () => { highlightedClass: available ? 'bg-amber-100' : '', }; }) - .filter((item) => { - if (showOnlyHighlighted) { - return item.highlighted; - } - - return true; - }) .sort((a, b) => { return Number(b.highlighted) - Number(a.highlighted); }); @@ -130,7 +107,7 @@ const MDLTable = () => { return _data; }); })(); - }, [showOnlyHighlighted, uniqueResponseDomains]); + }, [uniqueResponseDomains]); const tableColumns = useMemo( () => [ @@ -211,7 +188,7 @@ const MDLTable = () => { className="h-full flex" trayId="mdl-table-bottom-tray" > -
+
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx index 73b6728ea3..8b8b133b41 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx @@ -59,7 +59,7 @@ interface MdlCommonPanelProps { onRowClick: (row: TableData | null) => void; extraInterfaceToTopBar?: () => React.JSX.Element; filters?: TableFilter; - stats: Stats; + stats: Stats | null; tableSearchKeys: string[]; bottomPanel?: () => React.JSX.Element; showJson?: boolean; @@ -88,7 +88,7 @@ const MdlCommonPanel = ({ return (
- + {stats && } { className: 'p-4', }, }, + { + title: 'Blocked List', + content: { + Element: MDLTable, + props: { + type: 'Learning', + }, + className: 'overflow-auto h-full', + containerClassName: 'h-full', + }, + }, + ], + Observability: [ { title: 'Blocked Domain List', content: { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index c3bdcd3123..5c9c1cb4ce 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -42,9 +42,12 @@ const titleMap = { 'Some URLs are Blocked': 'Scope Partial', }; -const MDLTable = () => { +type MDLTableProps = { + type?: 'Observability' | 'Learning'; +}; + +const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { const [selectedKey, setSelectedKey] = useState(null); - const [showOnlyHighlighted, setShowOnlyHighlighted] = useState(true); const [preSetFilters, setPresetFilters] = useState<{ [key: string]: Record; }>({ filter: {} }); @@ -56,21 +59,6 @@ const MDLTable = () => { isLoading: state.isLoading, })); - const checkbox = useCallback( - () => ( - - ), - [] - ); - const tableData: MDLTableData[] = useMemo(() => { if (scriptBlockingData.length === 0) { return []; @@ -83,28 +71,27 @@ const MDLTable = () => { (item) => item.scriptBlocking !== IMPACTED_BY_SCRIPT_BLOCKING.NONE ) .forEach((item) => { + if (type === 'Learning') { + data.push({ + ...item, + } as MDLTableData); + return; + } + let available = false; if (uniqueResponseDomains.includes(item.domain)) { available = true; } - const canPush = showOnlyHighlighted ? available : true; - - if (canPush) { + if (available) { data.push({ ...item, - highlighted: available, - highlightedClass: - available && - item.scriptBlocking.startsWith('Some URLs are Blocked') - ? 'bg-amber-100' - : '', } as MDLTableData); } }); - return data.sort((a, b) => Number(b.highlighted) - Number(a.highlighted)); - }, [uniqueResponseDomains, scriptBlockingData, showOnlyHighlighted]); + return data; + }, [uniqueResponseDomains, scriptBlockingData, type]); const tableColumns = useMemo( () => [ @@ -198,13 +185,13 @@ const MDLTable = () => { const stats = { site: [ { - title: 'Total Domains', + title: 'Domains', centerCount: statistics.localView.domains, color: '#25ACAD', tooltipText: 'All page domains', }, { - title: 'Block List Domains', + title: 'BDL', centerCount: statistics.localView.partiallyBlockedDomains + statistics.localView.completelyBlockedDomains, @@ -212,7 +199,7 @@ const MDLTable = () => { tooltipText: 'Page domains in block list', }, { - title: 'Scope Complete', + title: 'Complete', centerCount: statistics.localView.completelyBlockedDomains, color: '#F3AE4E', tooltipText: 'Completely blocked domains', @@ -225,7 +212,7 @@ const MDLTable = () => { })), }, { - title: 'Scope Partial', + title: 'Partial', centerCount: statistics.localView.partiallyBlockedDomains, color: '#4C79F4', tooltipText: 'Partially blocked domains', @@ -240,13 +227,13 @@ const MDLTable = () => { ], global: [ { - title: 'Total Domains', + title: 'Domains', centerCount: statistics.globalView.domains, color: '#25ACAD', tooltipText: 'Total browsing session domains', }, { - title: 'Block List Domains', + title: 'BDL', centerCount: statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, @@ -254,7 +241,7 @@ const MDLTable = () => { tooltipText: 'Total domains in block list', }, { - title: 'Total Blockings', + title: 'Blockings', centerCount: statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, @@ -271,13 +258,13 @@ const MDLTable = () => { tooltipText: 'Blocked domains', }, { - title: 'Scope Complete', + title: 'Complete', centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', tooltipText: 'Completely blocked domains', }, { - title: 'Scope Partial', + title: 'Partial', centerCount: statistics.globalView.partiallyBlockedDomains, color: '#4C79F4', tooltipText: 'Partially blocked domains', @@ -303,9 +290,8 @@ const MDLTable = () => { onRowClick={(row) => setSelectedKey((row as MDLTableData)?.domain || null) } - extraInterfaceToTopBar={checkbox} filters={filters} - stats={stats} + stats={type === 'Learning' ? null : stats} showJson={false} bottomPanel={Legend} tab="scriptBlocking" From a35255559c1d29429cdd82bcc0a76af0778a2cf3 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 25 Sep 2025 18:47:02 +0400 Subject: [PATCH 73/98] Add session insights tab --- .../privacyProtection/ipProtection/index.tsx | 9 +++++++ .../sessionInsights.tsx | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx index 9811704268..f25371ada6 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx @@ -28,6 +28,7 @@ import { */ import Panel from './panel'; import ProbabilisticRevealTokens from './probabilisticRevealTokens'; +import SessionInsights from './probabilisticRevealTokens/sessionInsights'; import MDLTable from './mdlTable'; const IPProtection = () => { @@ -65,6 +66,14 @@ const IPProtection = () => { containerClassName: 'h-full', }, }, + { + title: 'Session Insights', + content: { + Element: SessionInsights, + className: 'overflow-auto h-full', + containerClassName: 'h-full', + }, + }, ], }), [] diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx new file mode 100644 index 0000000000..e3ed209e4d --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx @@ -0,0 +1,25 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; + +const SessionInsights = () => { + return
Session Insights
; +}; + +export default SessionInsights; From 4d8a11168dd558a03be8bc8b2e910843da8b8a81 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Thu, 25 Sep 2025 19:28:32 +0400 Subject: [PATCH 74/98] Remove tabs and move session data to new tabs --- .../probabilisticRevealTokens/index.tsx | 128 +++++++----------- .../sessionInsights.tsx | 76 ++++++++++- .../mdlCommonPanel/stasHeader.tsx | 116 ++++++---------- .../scriptBlocking/index.tsx | 11 ++ .../scriptBlocking/mdlTable/index.tsx | 127 ++++++----------- .../mdlTable/sessionInsights.tsx | 118 ++++++++++++++++ 6 files changed, 335 insertions(+), 241 deletions(-) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 285e95d28e..b5bbd59e1c 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -272,87 +272,59 @@ const ProbabilisticRevealTokens = () => { ] ); - const stats = { - site: [ - { - title: 'Domains', - centerCount: perTokenMetadata.length, - color: '#F3AE4E', - tooltipText: 'Unique domains on page', - }, - { - title: 'MDL', - centerCount: perTokenMetadata.filter(({ origin }) => { - let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; + const stats = [ + { + title: 'Domains', + centerCount: perTokenMetadata.length, + color: '#F3AE4E', + tooltipText: 'Unique domains on page', + }, + { + title: 'MDL', + centerCount: perTokenMetadata.filter(({ origin }) => { + let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; - hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; - if (!hostname) { - return false; - } + if (!hostname) { + return false; + } - return ( - scriptBlockingData.filter((_data) => _data.domain === hostname) - .length > 0 - ); - }).length, - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - mdl: ['True'], - }, - })), - color: '#4C79F4', - tooltipText: 'Page domains in MDL', - }, - { - title: 'PRT', - centerCount: statistics.localView.totalTokens, - color: '#EC7159', - tooltipText: 'Unique tokens sent in requests', - }, - { - title: 'Signals', - centerCount: statistics.localView.nonZeroSignal, - color: '#5CC971', - tooltipText: 'PRTs that decode to IP address', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - nonZeroUint8Signal: ['PRTs with signal'], - }, - })), - }, - ], - global: [ - { - title: 'Domains', - centerCount: statistics.globalView.domains, - color: '#F3AE4E', - tooltipText: 'Total unique domains in browsing session', - }, - { - title: 'MDL', - centerCount: statistics.globalView.mdl, - color: '#4C79F4', - tooltipText: 'Browsing session domains in MDL', - }, - { - title: 'PRT', - centerCount: statistics.globalView.totalTokens, - color: '#EC7159', - tooltipText: 'Total unique tokens sent in requests', - }, - { - title: 'Signals', - centerCount: statistics.globalView.nonZeroSignal, - color: '#5CC971', - tooltipText: 'Total PRTs that decode to IP address', - }, - ], - }; + return ( + scriptBlockingData.filter((_data) => _data.domain === hostname) + .length > 0 + ); + }).length, + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + mdl: ['True'], + }, + })), + color: '#4C79F4', + tooltipText: 'Page domains in MDL', + }, + { + title: 'PRT', + centerCount: statistics.localView.totalTokens, + color: '#EC7159', + tooltipText: 'Unique tokens sent in requests', + }, + { + title: 'Signals', + centerCount: statistics.localView.nonZeroSignal, + color: '#5CC971', + tooltipText: 'PRTs that decode to IP address', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + nonZeroUint8Signal: ['PRTs with signal'], + }, + })), + }, + ]; return ( { - return
Session Insights
; + const { statistics } = useProbabilisticRevealTokens(({ state }) => ({ + statistics: state.statistics, + })); + + const stats = [ + { + title: 'Domains', + centerCount: statistics.globalView.domains, + color: '#F3AE4E', + tooltipText: 'Total unique domains in browsing session', + }, + { + title: 'MDL', + centerCount: statistics.globalView.mdl, + color: '#4C79F4', + tooltipText: 'Browsing session domains in MDL', + }, + { + title: 'PRT', + centerCount: statistics.globalView.totalTokens, + color: '#EC7159', + tooltipText: 'Total unique tokens sent in requests', + }, + { + title: 'Signals', + centerCount: statistics.globalView.nonZeroSignal, + color: '#5CC971', + tooltipText: 'Total PRTs that decode to IP address', + }, + ]; + + return ( +
+ {stats.map( + ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( + + ) + )} +
+ ); }; export default SessionInsights; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx index 49455513c4..9237709f65 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx @@ -16,8 +16,8 @@ /** * External dependencies. */ -import React, { useEffect, useState } from 'react'; -import { CirclePieChart, PillToggle } from '@google-psat/design-system'; +import React from 'react'; +import { CirclePieChart } from '@google-psat/design-system'; import classnames from 'classnames'; type StatItem = { @@ -29,89 +29,55 @@ type StatItem = { tooltipText?: string; }; -export type Stats = { - site: StatItem[]; - global: StatItem[]; -}; +export type Stats = StatItem[]; interface StatsHeaderProps { - stats: Stats; - setPillState: React.Dispatch>; + stats: StatItem[]; } -const StatsHeader = ({ stats, setPillState }: StatsHeaderProps) => { - const [pillToggle, setPillToggle] = useState('Site'); - const [highlightOption, setHighlightOption] = useState('Site'); - - useEffect(() => { - setPillState(pillToggle); - }, [pillToggle, setPillState]); - - const renderPieCharts = (items: StatItem[]) => ( - <> - {items.map( - ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( - - ) - )} - - ); - - const sitePieCharts = renderPieCharts(stats.site); - const globalPieCharts = renderPieCharts(stats.global); - - const pillToggleButtons = ( - - ); - +const StatsHeader = ({ stats }: StatsHeaderProps) => { return (
-
{pillToggleButtons}
- {pillToggle === 'Site' ? sitePieCharts : globalPieCharts} + {stats.map( + ( + { title, centerCount, color, onClick, data, tooltipText }, + index + ) => ( + + ) + )}
-
- {pillToggleButtons} -
{' '} - {/*Work around to center the circles div without using position absolute that would break the responsive design or hardcoding px values.*/}
); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx index fab9af6ebd..526d748f52 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx @@ -23,11 +23,13 @@ import { TabsProvider, type TabItems, } from '@google-psat/design-system'; + /** * Internal dependencies. */ import Panel from './panel'; import MDLTable from './mdlTable'; +import SessionInsights from './mdlTable/sessionInsights'; const ScriptBlocking = () => { const tabItems = useMemo( @@ -67,6 +69,15 @@ const ScriptBlocking = () => { containerClassName: 'h-full', }, }, + { + title: 'Session Insights', + content: { + Element: SessionInsights, + props: {}, + className: 'overflow-auto h-full', + containerClassName: 'h-full', + }, + }, ], }), [] diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 5c9c1cb4ce..1716badfa2 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -182,95 +182,48 @@ const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { [calculateFilters, tableData] ); - const stats = { - site: [ - { - title: 'Domains', - centerCount: statistics.localView.domains, - color: '#25ACAD', - tooltipText: 'All page domains', - }, - { - title: 'BDL', - centerCount: - statistics.localView.partiallyBlockedDomains + - statistics.localView.completelyBlockedDomains, - color: '#7D8471', - tooltipText: 'Page domains in block list', - }, - { - title: 'Complete', - centerCount: statistics.localView.completelyBlockedDomains, - color: '#F3AE4E', - tooltipText: 'Completely blocked domains', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - scriptBlocking: ['Complete'], - }, - })), - }, - { - title: 'Partial', - centerCount: statistics.localView.partiallyBlockedDomains, - color: '#4C79F4', - tooltipText: 'Partially blocked domains', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - scriptBlocking: ['Partial'], - }, - })), - }, - ], - global: [ - { - title: 'Domains', - centerCount: statistics.globalView.domains, - color: '#25ACAD', - tooltipText: 'Total browsing session domains', - }, - { - title: 'BDL', - centerCount: - statistics.globalView.partiallyBlockedDomains + - statistics.globalView.completelyBlockedDomains, - color: '#7D8471', - tooltipText: 'Total domains in block list', - }, - { - title: 'Blockings', - centerCount: - statistics.globalView.partiallyBlockedDomains + - statistics.globalView.completelyBlockedDomains, - data: [ - { - color: '#4C79F4', - count: statistics.globalView.partiallyBlockedDomains, + const stats = [ + { + title: 'Domains', + centerCount: statistics.localView.domains, + color: '#25ACAD', + tooltipText: 'All page domains', + }, + { + title: 'BDL', + centerCount: + statistics.localView.partiallyBlockedDomains + + statistics.localView.completelyBlockedDomains, + color: '#7D8471', + tooltipText: 'Page domains in block list', + }, + { + title: 'Complete', + centerCount: statistics.localView.completelyBlockedDomains, + color: '#F3AE4E', + tooltipText: 'Completely blocked domains', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Complete'], }, - { - color: '#F3AE4E', - count: statistics.globalView.completelyBlockedDomains, + })), + }, + { + title: 'Partial', + centerCount: statistics.localView.partiallyBlockedDomains, + color: '#4C79F4', + tooltipText: 'Partially blocked domains', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Partial'], }, - ], - tooltipText: 'Blocked domains', - }, - { - title: 'Complete', - centerCount: statistics.globalView.completelyBlockedDomains, - color: '#F3AE4E', - tooltipText: 'Completely blocked domains', - }, - { - title: 'Partial', - centerCount: statistics.globalView.partiallyBlockedDomains, - color: '#4C79F4', - tooltipText: 'Partially blocked domains', - }, - ], - }; + })), + }, + ]; if (isLoading) { return ( diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx new file mode 100644 index 0000000000..43cdfb4d1d --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx @@ -0,0 +1,118 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { CirclePieChart } from '@google-psat/design-system'; + +/** + * Internal dependencies. + */ +import { useScriptBlocking } from '../../../../stateProviders'; +import classnames from 'classnames'; + +const SessionInsights = () => { + const { statistics } = useScriptBlocking(({ state }) => ({ + statistics: state.statistics, + })); + + const stats = [ + { + title: 'Domains', + centerCount: statistics.globalView.domains, + color: '#25ACAD', + tooltipText: 'Total browsing session domains', + }, + { + title: 'BDL', + centerCount: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#7D8471', + tooltipText: 'Total domains in block list', + }, + { + title: 'Blockings', + centerCount: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + data: [ + { + color: '#4C79F4', + count: statistics.globalView.partiallyBlockedDomains, + }, + { + color: '#F3AE4E', + count: statistics.globalView.completelyBlockedDomains, + }, + ], + tooltipText: 'Blocked domains', + }, + { + title: 'Complete', + centerCount: statistics.globalView.completelyBlockedDomains, + color: '#F3AE4E', + tooltipText: 'Completely blocked domains', + }, + { + title: 'Partial', + centerCount: statistics.globalView.partiallyBlockedDomains, + color: '#4C79F4', + tooltipText: 'Partially blocked domains', + }, + ]; + + return ( +
+ {stats.map( + ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( + + ) + )} +
+ ); +}; + +export default SessionInsights; From 79cff28b83218269f3bd85f01d32a517c9f3ca57 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 26 Sep 2025 10:57:10 +0400 Subject: [PATCH 75/98] Refactor code to create common component for session insights tab --- .../probabilisticRevealTokens/index.tsx | 2 +- .../sessionInsights.tsx | 41 +---------- .../{mdlCommonPanel => mdlCommon}/index.tsx | 5 +- .../mdlCommon/insightsStats.tsx | 68 +++++++++++++++++++ .../rowContextMenu.tsx | 0 .../stasHeader.tsx | 14 ++-- .../privacyProtection/mdlCommon/types.ts | 24 +++++++ .../scriptBlocking/mdlTable/index.tsx | 2 +- .../mdlTable/sessionInsights.tsx | 41 +---------- 9 files changed, 105 insertions(+), 92 deletions(-) rename packages/extension/src/view/devtools/pages/privacyProtection/{mdlCommonPanel => mdlCommon}/index.tsx (97%) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx rename packages/extension/src/view/devtools/pages/privacyProtection/{mdlCommonPanel => mdlCommon}/rowContextMenu.tsx (100%) rename packages/extension/src/view/devtools/pages/privacyProtection/{mdlCommonPanel => mdlCommon}/stasHeader.tsx (92%) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index b5bbd59e1c..94fc51f5f1 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -32,7 +32,7 @@ import { useProbabilisticRevealTokens, useScriptBlocking, } from '../../../../stateProviders'; -import MdlCommonPanel from '../../mdlCommonPanel'; +import MdlCommonPanel from '../../mdlCommon'; import getSignal from '../../../../../../utils/getSignal'; const ProbabilisticRevealTokens = () => { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx index df5877ae71..4bac3d8e50 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx @@ -17,13 +17,12 @@ * External dependencies. */ import React from 'react'; -import { CirclePieChart } from '@google-psat/design-system'; /** * Internal dependencies. */ import { useProbabilisticRevealTokens } from '../../../../stateProviders'; -import classnames from 'classnames'; +import InsightsStats from '../../mdlCommon/insightsStats'; const SessionInsights = () => { const { statistics } = useProbabilisticRevealTokens(({ state }) => ({ @@ -57,43 +56,7 @@ const SessionInsights = () => { }, ]; - return ( -
- {stats.map( - ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( - - ) - )} -
- ); + return ; }; export default SessionInsights; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx similarity index 97% rename from packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx rename to packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx index 8b8b133b41..71a58285bc 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx @@ -40,7 +40,8 @@ import classNames from 'classnames'; * Internal dependencies */ import RowContextMenuForMDLTable from './rowContextMenu'; -import StatsHeader, { type Stats } from './stasHeader'; +import StatsHeader from './stasHeader'; +import { type StatItem } from './types'; type NonDecryptedJson = PRTMetadata & ProbablisticRevealToken; type DecryptedJson = { @@ -59,7 +60,7 @@ interface MdlCommonPanelProps { onRowClick: (row: TableData | null) => void; extraInterfaceToTopBar?: () => React.JSX.Element; filters?: TableFilter; - stats: Stats | null; + stats: StatItem[] | null; tableSearchKeys: string[]; bottomPanel?: () => React.JSX.Element; showJson?: boolean; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx new file mode 100644 index 0000000000..3e6d9f7b5b --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { CirclePieChart } from '@google-psat/design-system'; +import classnames from 'classnames'; + +/** + * Internal dependencies. + */ +import type { StatItem } from './types'; + +const InsightsStats = ({ stats }: { stats: StatItem[] }) => { + return ( +
+ {stats.map( + ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( + + ) + )} +
+ ); +}; + +export default InsightsStats; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/rowContextMenu.tsx similarity index 100% rename from packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/rowContextMenu.tsx rename to packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/rowContextMenu.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/stasHeader.tsx similarity index 92% rename from packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx rename to packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/stasHeader.tsx index 9237709f65..f6f1e09bc9 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommonPanel/stasHeader.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/stasHeader.tsx @@ -20,16 +20,10 @@ import React from 'react'; import { CirclePieChart } from '@google-psat/design-system'; import classnames from 'classnames'; -type StatItem = { - title: string; - centerCount: number; - color?: string; - onClick?: () => void; - data?: { count: number; color: string }[]; - tooltipText?: string; -}; - -export type Stats = StatItem[]; +/** + * Internal dependencies. + */ +import type { StatItem } from './types'; interface StatsHeaderProps { stats: StatItem[]; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts new file mode 100644 index 0000000000..9005ccec67 --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type StatItem = { + title: string; + centerCount: number; + color?: string; + onClick?: () => void; + data?: { count: number; color: string }[]; + tooltipText?: string; +}; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 1716badfa2..5a720ae972 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -34,7 +34,7 @@ import { useScriptBlocking, IMPACTED_BY_SCRIPT_BLOCKING, } from '../../../../stateProviders'; -import MdlCommonPanel from '../../mdlCommonPanel'; +import MdlCommonPanel from '../../mdlCommon'; import Legend from './legend'; const titleMap = { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx index 43cdfb4d1d..170fadc6a7 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx @@ -17,13 +17,12 @@ * External dependencies. */ import React from 'react'; -import { CirclePieChart } from '@google-psat/design-system'; /** * Internal dependencies. */ import { useScriptBlocking } from '../../../../stateProviders'; -import classnames from 'classnames'; +import InsightsStats from '../../mdlCommon/insightsStats'; const SessionInsights = () => { const { statistics } = useScriptBlocking(({ state }) => ({ @@ -76,43 +75,7 @@ const SessionInsights = () => { }, ]; - return ( -
- {stats.map( - ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( - - ) - )} -
- ); + return ; }; export default SessionInsights; From dc6714008de5b89b73074f58fd4379123adafa15 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 26 Sep 2025 11:01:05 +0400 Subject: [PATCH 76/98] Arrage the pie chart circles --- .../pages/privacyProtection/mdlCommon/insightsStats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx index 3e6d9f7b5b..94cd3a6b7d 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -27,7 +27,7 @@ import type { StatItem } from './types'; const InsightsStats = ({ stats }: { stats: StatItem[] }) => { return ( -
+
{stats.map( ({ title, centerCount, color, onClick, data, tooltipText }, index) => ( - ) - )} + style={{ cursor: !onClick ? 'default' : 'pointer' }} + onClick={() => { + onClick?.(); + }} + > + + + ) + )} +
+
+
+ +
+
); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts index 9005ccec67..4320581034 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts @@ -21,4 +21,6 @@ export type StatItem = { onClick?: () => void; data?: { count: number; color: string }[]; tooltipText?: string; + countClassName: string; + description: string; }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx index 170fadc6a7..411acf251e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx @@ -34,7 +34,8 @@ const SessionInsights = () => { title: 'Domains', centerCount: statistics.globalView.domains, color: '#25ACAD', - tooltipText: 'Total browsing session domains', + description: 'Total browsing session domains', + countClassName: 'text-emerald', }, { title: 'BDL', @@ -42,13 +43,15 @@ const SessionInsights = () => { statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, color: '#7D8471', - tooltipText: 'Total domains in block list', + description: 'Total domains in block list', + countClassName: 'text-max-yellow-red', }, { title: 'Blockings', centerCount: statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, + color: '#4C79F4', data: [ { color: '#4C79F4', @@ -59,22 +62,67 @@ const SessionInsights = () => { count: statistics.globalView.completelyBlockedDomains, }, ], - tooltipText: 'Blocked domains', + description: 'Blocked domains', + countClassName: 'text-blue-berry', }, { title: 'Complete', centerCount: statistics.globalView.completelyBlockedDomains, color: '#F3AE4E', - tooltipText: 'Completely blocked domains', + description: 'Completely blocked domains', + countClassName: 'text-blue-berry', }, { title: 'Partial', centerCount: statistics.globalView.partiallyBlockedDomains, color: '#4C79F4', - tooltipText: 'Partially blocked domains', + description: 'Partially blocked domains', + countClassName: 'text-blue-berry', }, ]; + // { + // color: '#5CC971', + // containerClasses: '', + // count: 7, + // countClassName: 'text-emerald', + // description: + // 'These are essential cookies that are necessary for a website to function properly. They enable basic functionalities such as page navigation, access to secure areas, and remembering user preferences (e.g., language, font size), etc.', + // isExpanded: true, + // title: 'Functional', + // }, + // { + // color: '#F3AE4E', + // containerClasses: '', + // count: 9, + // countClassName: 'text-max-yellow-red', + // description: + // "They are used to track visitors across websites to gather information about their browsing habits. The data collected is often used by advertisers to deliver targeted advertisements that are relevant to the user's interests.", + // isExpanded: true, + // title: 'Marketing', + // }, + // { + // color: '#4C79F4', + // containerClasses: '', + // count: 2, + // countClassName: 'text-blue-berry', + // description: + // 'Used to gather information about how users interact with a website. They provide website owners with insights into user behavior, such as the number of visitors, the most popular pages, and the average time spent on the site. This data helps website owners understand and improve the user experience, optimize content, and identify areas for enhancement.', + // isExpanded: true, + // title: 'Analytics', + // }, + // { + // color: '#EC7159', + // containerClasses: '', + // count: 23, + // countClassName: 'text-blue-berry', + // description: + // 'We are unable to categorize certain cookies since we do not possess any relevant information about them. Nonetheless, you may visit sites like cookiedatabase.org and cookiesearch.org to acquire additional details about these cookies.', + // isExpanded: true, + // title: 'Uncategorized', + // }, + // ]; + return ; }; From 3b600dcb4f72ca414cb803cc3b46b513d0d10f35 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 26 Sep 2025 12:10:21 +0400 Subject: [PATCH 78/98] Fix typescript issue --- .../view/devtools/pages/privacyProtection/mdlCommon/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts index 4320581034..590afbcee4 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts @@ -17,7 +17,7 @@ export type StatItem = { title: string; centerCount: number; - color?: string; + color: string; onClick?: () => void; data?: { count: number; color: string }[]; tooltipText?: string; From 788d26479ed328e468505275a977732c7a0fc208 Mon Sep 17 00:00:00 2001 From: sayedtaqui Date: Fri, 26 Sep 2025 12:31:26 +0400 Subject: [PATCH 79/98] Keep explaination open by default --- .../pages/privacyProtection/mdlCommon/insightsStats.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx index 09f8c380be..fabdd93805 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -48,7 +48,7 @@ const InsightsStats = ({ stats }: InsightsStatsProps) => { return (
-
+
{stats.map( ( { title, centerCount, color, onClick, data, tooltipText }, @@ -90,10 +90,9 @@ const InsightsStats = ({ stats }: InsightsStatsProps) => {
From 86b196f372c4d28c4d60d64634e95d1714d863ba Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 29 Sep 2025 11:51:54 +0530 Subject: [PATCH 80/98] Rename file and update props. --- .../privacyProtection/mdlCommon/index.tsx | 23 +++------- .../{stasHeader.tsx => statsHeader.tsx} | 0 .../mdlTable/sessionInsights.tsx | 42 ------------------- 3 files changed, 6 insertions(+), 59 deletions(-) rename packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/{stasHeader.tsx => statsHeader.tsx} (100%) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx index 71a58285bc..7a0e8cbd76 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/index.tsx @@ -28,19 +28,18 @@ import { type TableData, type TableColumn, } from '@google-psat/design-system'; -import React, { useRef, useState } from 'react'; +import React, { useRef } from 'react'; import { type MDLTableData, type ProbablisticRevealToken, type PRTMetadata, } from '@google-psat/common'; import { I18n } from '@google-psat/i18n'; -import classNames from 'classnames'; /** * Internal dependencies */ import RowContextMenuForMDLTable from './rowContextMenu'; -import StatsHeader from './stasHeader'; +import StatsHeader from './statsHeader'; import { type StatItem } from './types'; type NonDecryptedJson = PRTMetadata & ProbablisticRevealToken; @@ -85,11 +84,9 @@ const MdlCommonPanel = ({ typeof RowContextMenuForMDLTable > | null>(null); - const [pillState, setPillState] = useState('Site'); - return (
- {stats && } + {stats && } -
+
{ }, ]; - // { - // color: '#5CC971', - // containerClasses: '', - // count: 7, - // countClassName: 'text-emerald', - // description: - // 'These are essential cookies that are necessary for a website to function properly. They enable basic functionalities such as page navigation, access to secure areas, and remembering user preferences (e.g., language, font size), etc.', - // isExpanded: true, - // title: 'Functional', - // }, - // { - // color: '#F3AE4E', - // containerClasses: '', - // count: 9, - // countClassName: 'text-max-yellow-red', - // description: - // "They are used to track visitors across websites to gather information about their browsing habits. The data collected is often used by advertisers to deliver targeted advertisements that are relevant to the user's interests.", - // isExpanded: true, - // title: 'Marketing', - // }, - // { - // color: '#4C79F4', - // containerClasses: '', - // count: 2, - // countClassName: 'text-blue-berry', - // description: - // 'Used to gather information about how users interact with a website. They provide website owners with insights into user behavior, such as the number of visitors, the most popular pages, and the average time spent on the site. This data helps website owners understand and improve the user experience, optimize content, and identify areas for enhancement.', - // isExpanded: true, - // title: 'Analytics', - // }, - // { - // color: '#EC7159', - // containerClasses: '', - // count: 23, - // countClassName: 'text-blue-berry', - // description: - // 'We are unable to categorize certain cookies since we do not possess any relevant information about them. Nonetheless, you may visit sites like cookiedatabase.org and cookiesearch.org to acquire additional details about these cookies.', - // isExpanded: true, - // title: 'Uncategorized', - // }, - // ]; - return ; }; From ca352d589cd88c9845635ff914ee7ccb05d134e7 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 29 Sep 2025 14:15:55 +0530 Subject: [PATCH 81/98] Add Glossary and Panel components; refactor ProbabilisticRevealTokens to integrate tabs and update stats display --- .../probabilisticRevealTokens/index.tsx | 205 +++++++++++------- .../privacyProtection/mdlCommon/glossary.tsx | 44 ++++ .../privacyProtection/mdlCommon/panel.tsx | 38 ++++ 3 files changed, 208 insertions(+), 79 deletions(-) create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/glossary.tsx create mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 94fc51f5f1..3d9bb7ea14 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -18,7 +18,11 @@ * External dependencies */ import { + JsonView, + Tabs, + TabsProvider, type InfoType, + type TabItems, type TableColumn, type TableFilter, } from '@google-psat/design-system'; @@ -34,6 +38,8 @@ import { } from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommon'; import getSignal from '../../../../../../utils/getSignal'; +import Panel from '../../mdlCommon/panel'; +import Glossary from '../../mdlCommon/glossary'; const ProbabilisticRevealTokens = () => { const [selectedJSON, setSelectedJSON] = useState(null); @@ -60,40 +66,61 @@ const ProbabilisticRevealTokens = () => { isLoading: state.isLoading, })); - const tableColumns = useMemo( + const stats = useMemo( () => [ { - header: 'Domain', - accessorKey: 'origin', - cell: (info) => info, + title: 'Domains', + centerCount: perTokenMetadata.length, + color: '#F3AE4E', + tooltipText: 'Unique domains on page', }, { - header: 'Owner', - accessorKey: 'owner', - cell: (info) => info, + title: 'MDL', + centerCount: perTokenMetadata.filter(({ origin }) => { + let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; + + hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + + if (!hostname) { + return false; + } + + return ( + scriptBlockingData.filter((_data) => _data.domain === hostname) + .length > 0 + ); + }).length, + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + mdl: ['True'], + }, + })), + color: '#4C79F4', + tooltipText: 'Page domains in MDL', }, { - header: 'Decrypted', - accessorKey: 'decryptionKeyAvailable', - cell: (info) => { - return info ? : ''; - }, + title: 'PRT', + centerCount: statistics.localView.totalTokens, + color: '#EC7159', + tooltipText: 'Unique tokens sent in requests', }, { - header: 'Signal', - accessorKey: 'nonZeroUint8Signal', - cell: (info) => { - return info ? : ''; - }, - }, - { - header: 'PRT Prefix', - accessorKey: 'prtHeader', - cell: (info) => (info as string).slice(0, 10), - isHiddenByDefault: true, + title: 'Signals', + centerCount: statistics.localView.nonZeroSignal, + color: '#5CC971', + tooltipText: 'PRTs that decode to IP address', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + nonZeroUint8Signal: ['PRTs with signal'], + }, + })), }, ], - [] + [perTokenMetadata, scriptBlockingData, statistics] ); const formedJson = useMemo(() => { @@ -159,6 +186,69 @@ const ProbabilisticRevealTokens = () => { selectedJSON, ]); + const tabItems = useMemo( + () => [ + { + title: 'Glossary', + content: { + Element: Glossary, + className: 'p-4', + props: { + statItems: stats, + }, + }, + }, + { + title: 'Json View', + content: { + //@ts-expect-error -- the component is lazy loaded and memoised thats why the error is being shown. + Element: JsonView, + className: 'p-4', + props: { + src: formedJson ?? {}, + }, + }, + }, + ], + [formedJson, stats] + ); + + const tableColumns = useMemo( + () => [ + { + header: 'Domain', + accessorKey: 'origin', + cell: (info) => info, + }, + { + header: 'Owner', + accessorKey: 'owner', + cell: (info) => info, + }, + { + header: 'Decrypted', + accessorKey: 'decryptionKeyAvailable', + cell: (info) => { + return info ? : ''; + }, + }, + { + header: 'Signal', + accessorKey: 'nonZeroUint8Signal', + cell: (info) => { + return info ? : ''; + }, + }, + { + header: 'PRT Prefix', + accessorKey: 'prtHeader', + cell: (info) => (info as string).slice(0, 10), + isHiddenByDefault: true, + }, + ], + [] + ); + const mdlComparator = useCallback( (value: InfoType, filterValue: string) => { let hostname = isValidURL(value as string) @@ -272,63 +362,20 @@ const ProbabilisticRevealTokens = () => { ] ); - const stats = [ - { - title: 'Domains', - centerCount: perTokenMetadata.length, - color: '#F3AE4E', - tooltipText: 'Unique domains on page', - }, - { - title: 'MDL', - centerCount: perTokenMetadata.filter(({ origin }) => { - let hostname = isValidURL(origin) ? new URL(origin).hostname : ''; - - hostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; - - if (!hostname) { - return false; - } - - return ( - scriptBlockingData.filter((_data) => _data.domain === hostname) - .length > 0 - ); - }).length, - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - mdl: ['True'], - }, - })), - color: '#4C79F4', - tooltipText: 'Page domains in MDL', - }, - { - title: 'PRT', - centerCount: statistics.localView.totalTokens, - color: '#EC7159', - tooltipText: 'Unique tokens sent in requests', - }, - { - title: 'Signals', - centerCount: statistics.localView.nonZeroSignal, - color: '#5CC971', - tooltipText: 'PRTs that decode to IP address', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - nonZeroUint8Signal: ['PRTs with signal'], - }, - })), - }, - ]; + const bottomPanel = useCallback(() => { + return ( + +
+ + +
+
+ ); + }, [tabItems]); return ( { selectedKey={selectedJSON?.origin.toString()} onRowClick={(row) => setSelectedJSON(row as PRTMetadata)} stats={stats} - showJson + showJson={false} tab="PRT" /> ); diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/glossary.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/glossary.tsx new file mode 100644 index 0000000000..006bee877b --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/glossary.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal dependencies + */ +import type { StatItem } from './types'; + +type GlossaryProps = { + statItems: StatItem[]; +}; +const Glossary = ({ statItems }: GlossaryProps) => { + return ( +
+

Glossary

+
+ {statItems.map((item) => ( +
+
+ {item.tooltipText} +
+ ))} +
+
+ ); +}; + +export default Glossary; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx new file mode 100644 index 0000000000..cf2891fcdc --- /dev/null +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * External dependencies. + */ +import React from 'react'; +import { useTabs } from '@google-psat/design-system'; + +const Panel = () => { + const { panel } = useTabs(({ state, actions }) => ({ + panel: state.panel, + highlightTab: actions.highlightTab, + })); + + const ActiveTabContent = panel.Element; + const { props, className } = panel; + + return ( +
+ {ActiveTabContent && } +
+ ); +}; + +export default Panel; From 611ec509c0c8605d0b2228dbe30f916fc6348866 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 29 Sep 2025 14:41:06 +0530 Subject: [PATCH 82/98] Enhance UI components: add formedJson prop to MdlCommonPanel, display item titles in Glossary, update StatItem type for optional fields, integrate Glossary and Panel in MDLTable with tabs, and simplify Legend styling. --- .../probabilisticRevealTokens/index.tsx | 1 + .../privacyProtection/mdlCommon/glossary.tsx | 1 + .../privacyProtection/mdlCommon/types.ts | 4 +- .../scriptBlocking/mdlTable/index.tsx | 130 ++++++++++++------ .../scriptBlocking/mdlTable/legend.tsx | 2 +- 5 files changed, 93 insertions(+), 45 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 3d9bb7ea14..583167a7c3 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -375,6 +375,7 @@ const ProbabilisticRevealTokens = () => { return ( { className="w-2.5 shrink-0 h-2.5 rounded-full flex items-center justify-center" style={{ backgroundColor: item.color }} /> + {item.title}: {item.tooltipText}
))} diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts index 590afbcee4..a6691837d5 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts @@ -21,6 +21,6 @@ export type StatItem = { onClick?: () => void; data?: { count: number; color: string }[]; tooltipText?: string; - countClassName: string; - description: string; + countClassName?: string; + description?: string; }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 5a720ae972..33ac27ad2d 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -23,6 +23,9 @@ import { type TableColumn, Link, type InfoType, + Tabs, + TabsProvider, + type TabItems, } from '@google-psat/design-system'; import React, { useMemo, useState, useCallback } from 'react'; import type { MDLTableData } from '@google-psat/common'; @@ -36,6 +39,8 @@ import { } from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommon'; import Legend from './legend'; +import Glossary from '../../mdlCommon/glossary'; +import Panel from '../../mdlCommon/panel'; const titleMap = { 'Entire Domain Blocked': 'Scope Complete', @@ -182,48 +187,89 @@ const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { [calculateFilters, tableData] ); - const stats = [ - { - title: 'Domains', - centerCount: statistics.localView.domains, - color: '#25ACAD', - tooltipText: 'All page domains', - }, - { - title: 'BDL', - centerCount: - statistics.localView.partiallyBlockedDomains + - statistics.localView.completelyBlockedDomains, - color: '#7D8471', - tooltipText: 'Page domains in block list', - }, - { - title: 'Complete', - centerCount: statistics.localView.completelyBlockedDomains, - color: '#F3AE4E', - tooltipText: 'Completely blocked domains', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - scriptBlocking: ['Complete'], - }, - })), - }, - { - title: 'Partial', - centerCount: statistics.localView.partiallyBlockedDomains, - color: '#4C79F4', - tooltipText: 'Partially blocked domains', - onClick: () => - setPresetFilters((prev) => ({ - ...prev, - filter: { - scriptBlocking: ['Partial'], + const stats = useMemo( + () => [ + { + title: 'Domains', + centerCount: statistics.localView.domains, + color: '#25ACAD', + tooltipText: 'All page domains', + }, + { + title: 'BDL', + centerCount: + statistics.localView.partiallyBlockedDomains + + statistics.localView.completelyBlockedDomains, + color: '#7D8471', + tooltipText: 'Page domains in block list', + }, + { + title: 'Complete', + centerCount: statistics.localView.completelyBlockedDomains, + color: '#F3AE4E', + tooltipText: 'Completely blocked domains', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Complete'], + }, + })), + }, + { + title: 'Partial', + centerCount: statistics.localView.partiallyBlockedDomains, + color: '#4C79F4', + tooltipText: 'Partially blocked domains', + onClick: () => + setPresetFilters((prev) => ({ + ...prev, + filter: { + scriptBlocking: ['Partial'], + }, + })), + }, + ], + [ + statistics.localView.completelyBlockedDomains, + statistics.localView.domains, + statistics.localView.partiallyBlockedDomains, + ] + ); + + const tabItems = useMemo( + () => [ + { + title: 'Glossary', + content: { + Element: Glossary, + className: 'p-4', + props: { + statItems: stats, }, - })), - }, - ]; + }, + }, + { + title: 'Legend', + content: { + Element: Legend, + className: 'p-4', + }, + }, + ], + [stats] + ); + + const bottomPanel = useCallback(() => { + return ( + +
+ + +
+
+ ); + }, [tabItems]); if (isLoading) { return ( @@ -246,7 +292,7 @@ const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { filters={filters} stats={type === 'Learning' ? null : stats} showJson={false} - bottomPanel={Legend} + bottomPanel={bottomPanel} tab="scriptBlocking" /> ); diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx index f1db944a66..0650972e1b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/legend.tsx @@ -22,7 +22,7 @@ import React from 'react'; const Legend = () => { return ( -
+

The Blocked Domain List is a subset of the{' '} From 1bfd8e5811ca6a83d932f3032ab164f86b3b7891 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 29 Sep 2025 14:47:37 +0530 Subject: [PATCH 83/98] Refactor SessionInsights: update Blockings color and remove unnecessary data structure --- .../scriptBlocking/mdlTable/sessionInsights.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx index 143ea92129..bacd2bee53 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx @@ -51,17 +51,7 @@ const SessionInsights = () => { centerCount: statistics.globalView.partiallyBlockedDomains + statistics.globalView.completelyBlockedDomains, - color: '#4C79F4', - data: [ - { - color: '#4C79F4', - count: statistics.globalView.partiallyBlockedDomains, - }, - { - color: '#F3AE4E', - count: statistics.globalView.completelyBlockedDomains, - }, - ], + color: '#EC7159', description: 'Blocked domains', countClassName: 'text-blue-berry', }, From a557e6af6ce485dde7d9b770997f3f196ea3e7bc Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 29 Sep 2025 17:20:13 +0530 Subject: [PATCH 84/98] Refactor MDLTable and ProbabilisticRevealTokens: replace Panel component with DraggableTray and manage collapse state --- .../probabilisticRevealTokens/index.tsx | 15 ++++---- .../privacyProtection/mdlCommon/panel.tsx | 38 ------------------- .../scriptBlocking/mdlTable/index.tsx | 15 ++++---- 3 files changed, 16 insertions(+), 52 deletions(-) delete mode 100644 packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx index 583167a7c3..8059ed8d6b 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/index.tsx @@ -18,15 +18,15 @@ * External dependencies */ import { + DraggableTray, JsonView, - Tabs, TabsProvider, type InfoType, type TabItems, type TableColumn, type TableFilter, } from '@google-psat/design-system'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { isValidURL, type PRTMetadata } from '@google-psat/common'; /** @@ -38,7 +38,6 @@ import { } from '../../../../stateProviders'; import MdlCommonPanel from '../../mdlCommon'; import getSignal from '../../../../../../utils/getSignal'; -import Panel from '../../mdlCommon/panel'; import Glossary from '../../mdlCommon/glossary'; const ProbabilisticRevealTokens = () => { @@ -46,6 +45,11 @@ const ProbabilisticRevealTokens = () => { const [preSetFilters, setPresetFilters] = useState<{ [key: string]: Record; }>({ filter: {} }); + const [isCollapsed, setIsCollapsed] = useState(false); + const draggableTrayRef = useRef({ + isCollapsed, + setIsCollapsed, + }); const { perTokenMetadata, @@ -365,10 +369,7 @@ const ProbabilisticRevealTokens = () => { const bottomPanel = useCallback(() => { return ( -

- - -
+ ); }, [tabItems]); diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx deleted file mode 100644 index cf2891fcdc..0000000000 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/panel.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * External dependencies. - */ -import React from 'react'; -import { useTabs } from '@google-psat/design-system'; - -const Panel = () => { - const { panel } = useTabs(({ state, actions }) => ({ - panel: state.panel, - highlightTab: actions.highlightTab, - })); - - const ActiveTabContent = panel.Element; - const { props, className } = panel; - - return ( -
- {ActiveTabContent && } -
- ); -}; - -export default Panel; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx index 33ac27ad2d..a5c668bf7e 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/index.tsx @@ -23,11 +23,11 @@ import { type TableColumn, Link, type InfoType, - Tabs, TabsProvider, type TabItems, + DraggableTray, } from '@google-psat/design-system'; -import React, { useMemo, useState, useCallback } from 'react'; +import React, { useMemo, useState, useCallback, useRef } from 'react'; import type { MDLTableData } from '@google-psat/common'; /** @@ -40,7 +40,6 @@ import { import MdlCommonPanel from '../../mdlCommon'; import Legend from './legend'; import Glossary from '../../mdlCommon/glossary'; -import Panel from '../../mdlCommon/panel'; const titleMap = { 'Entire Domain Blocked': 'Scope Complete', @@ -56,6 +55,11 @@ const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { const [preSetFilters, setPresetFilters] = useState<{ [key: string]: Record; }>({ filter: {} }); + const [isCollapsed, setIsCollapsed] = useState(false); + const draggableTrayRef = useRef({ + isCollapsed, + setIsCollapsed, + }); const { uniqueResponseDomains, statistics, scriptBlockingData, isLoading } = useScriptBlocking(({ state }) => ({ uniqueResponseDomains: state.uniqueResponseDomains, @@ -263,10 +267,7 @@ const MDLTable = ({ type = 'Observability' }: MDLTableProps) => { const bottomPanel = useCallback(() => { return ( -
- - -
+
); }, [tabItems]); From f065e34783d2251903da6a6950f744d556855600 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 29 Sep 2025 19:08:08 +0530 Subject: [PATCH 85/98] Refactor MDLTable: update highlightedClass to a function for dynamic class assignment and update colors for mdl table highlight --- packages/common/src/mdl.types.ts | 2 +- .../table/components/tableBody/bodyRow.tsx | 14 ++++++++------ .../src/components/table/useTable/types.ts | 2 +- .../ipProtection/mdlTable/index.tsx | 12 ++++++++++-- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/common/src/mdl.types.ts b/packages/common/src/mdl.types.ts index c46c6b3cad..d5f7a3a18c 100644 --- a/packages/common/src/mdl.types.ts +++ b/packages/common/src/mdl.types.ts @@ -18,5 +18,5 @@ export interface MDLTableData { owner: string; scriptBlocking: string; highlighted?: boolean; - highlightedClass?: string; + highlightedClass?: (selected: boolean) => string; } diff --git a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx index 49556ae960..34c55cdfe7 100644 --- a/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx +++ b/packages/design-system/src/components/table/components/tableBody/bodyRow.tsx @@ -68,24 +68,26 @@ const BodyRow = ({ const rowKey = getRowObjectKey(row); const isHighlighted = row.originalData?.highlighted; const scrollToHighlighted = row.originalData?.scrollToHighlighted; - const isHighlightedClass = - row.originalData?.highlightedClass || 'bg-dirty-pink dark:text-black'; + const highlightedClass = (selected: boolean) => + row.originalData?.highlightedClass?.(selected) || + 'bg-dirty-pink dark:text-black'; + const classes = classnames( rowKey !== selectedKey && (index % 2 ? isHighlighted - ? isHighlightedClass + ? highlightedClass(false) : 'bg-anti-flash-white dark:bg-charleston-green' : isHighlighted - ? isHighlightedClass + ? highlightedClass(false) : 'bg-white dark:bg-raisin-black'), rowKey === selectedKey && (isRowFocused ? isHighlighted - ? `${isHighlightedClass} opacity-70` + ? `${highlightedClass(true)}` : 'bg-lavender-sky text-black dark:bg-midnight-slate dark:text-chinese-silver' : isHighlighted - ? `${isHighlightedClass} opacity-70` + ? `${highlightedClass(true)}` : 'bg-silver-mist text-black dark:bg-dark-graphite dark:text-chinese-silver') ); const extraClasses = getExtraClasses(); diff --git a/packages/design-system/src/components/table/useTable/types.ts b/packages/design-system/src/components/table/useTable/types.ts index ba6873a02f..19cbcc21d1 100644 --- a/packages/design-system/src/components/table/useTable/types.ts +++ b/packages/design-system/src/components/table/useTable/types.ts @@ -82,7 +82,7 @@ export type TableData = ( | PRTMetadata ) & { highlighted?: boolean; - highlightedClass?: string; // Optional class for highlighting rows + highlightedClass?: () => string; // Optional class for highlighting rows scrollToHighlighted?: boolean; // Optional flag to scroll to highlighted row }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx index 1862938bde..e29d9eaa4d 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/mdlTable/index.tsx @@ -45,7 +45,7 @@ const MDLTable = () => { domain: string; owner: string; highlighted: boolean; - highlightedClass: string; + highlightedClass?: () => string; }[] >([]); @@ -95,7 +95,15 @@ const MDLTable = () => { owner, scriptBlocking, highlighted: available, - highlightedClass: available ? 'bg-amber-100' : '', + highlightedClass: available + ? (selected: boolean) => { + if (selected) { + return 'bg-amber-200/80 dark:bg-amber-200/70'; + } + + return 'bg-amber-100/60 dark:bg-amber-200/90'; + } + : undefined, }; }) .sort((a, b) => { From b397f2b535b5ba5fb657eb3bcaf3e6353a033302 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 30 Sep 2025 13:32:24 +0530 Subject: [PATCH 86/98] Refactor IPProtection and ScriptBlocking: rename titles for consistency and update SessionInsights to use memoized stats and matrixData --- .../privacyProtection/ipProtection/index.tsx | 4 +- .../sessionInsights.tsx | 103 ++++++++++++------ .../mdlCommon/insightsStats.tsx | 15 +-- .../privacyProtection/mdlCommon/types.ts | 1 - .../scriptBlocking/index.tsx | 4 +- 5 files changed, 77 insertions(+), 50 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx index f25371ada6..4e07f8f394 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/index.tsx @@ -59,7 +59,7 @@ const IPProtection = () => { ], Observability: [ { - title: 'IP Proxying', + title: 'Site', content: { Element: ProbabilisticRevealTokens, className: 'overflow-auto h-full', @@ -67,7 +67,7 @@ const IPProtection = () => { }, }, { - title: 'Session Insights', + title: 'Session', content: { Element: SessionInsights, className: 'overflow-auto h-full', diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx index 24471f856e..30854597d7 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx @@ -16,7 +16,7 @@ /** * External dependencies. */ -import React from 'react'; +import React, { useMemo } from 'react'; /** * Internal dependencies. @@ -29,38 +29,77 @@ const SessionInsights = () => { statistics: state.statistics, })); - const stats = [ - { - title: 'Domains', - centerCount: statistics.globalView.domains, - color: '#F3AE4E', - description: 'Total unique domains in browsing session', - countClassName: 'text-emerald', - }, - { - title: 'MDL', - centerCount: statistics.globalView.mdl, - color: '#4C79F4', - description: 'Browsing session domains in MDL', - countClassName: 'text-emerald', - }, - { - title: 'PRT', - centerCount: statistics.globalView.totalTokens, - color: '#EC7159', - description: 'Total unique tokens sent in requests', - countClassName: 'text-emerald', - }, - { - title: 'Signals', - centerCount: statistics.globalView.nonZeroSignal, - color: '#5CC971', - description: 'Total PRTs that decode to IP address', - countClassName: 'text-emerald', - }, - ]; + const { stats, matrixData } = useMemo( + () => ({ + stats: [ + { + title: 'Domains', + centerCount: statistics.globalView.domains, + color: '#F3AE4E', + data: [ + { + color: '#F3AE4E', + count: statistics.globalView.domains, + }, + { + color: '#4C79F4', + count: statistics.globalView.mdl, + }, + ], + countClassName: 'text-emerald', + }, + { + title: 'PRT', + centerCount: statistics.globalView.totalTokens, + color: '#EC7159', + data: [ + { + color: '#EC7159', + count: statistics.localView.totalTokens, + }, + { + color: '#5CC971', + count: statistics.localView.nonZeroSignal, + }, + ], + countClassName: 'text-emerald', + }, + ], + matrixData: [ + { + color: '#F3AE4E', + title: 'Domains', + count: statistics.globalView.domains, + description: 'Unique domains', + countClassName: 'text-emerald', + }, + { + color: '#4C79F4', + title: 'MDL', + count: statistics.globalView.mdl, + description: 'Domains in the MDL', + countClassName: 'text-indigo', + }, + { + color: '#EC7159', + title: 'PRT', + count: statistics.globalView.totalTokens, + description: 'Unique PRT tokens sent in requests', + countClassName: 'text-emerald', + }, + { + color: '#5CC971', + title: 'Signals', + count: statistics.globalView.nonZeroSignal, + description: 'PRTs that decode to IP address', + countClassName: 'text-emerald', + }, + ], + }), + [statistics] + ); - return ; + return ; }; export default SessionInsights; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx index fabdd93805..4e766fb16f 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -31,21 +31,10 @@ import type { StatItem } from './types'; interface InsightsStatsProps { stats: StatItem[]; + matrixData: MatrixComponentProps[]; } -const InsightsStats = ({ stats }: InsightsStatsProps) => { - const matrixData: MatrixComponentProps[] = stats.map((item) => { - return { - title: item.title, - count: item.centerCount, - color: item.color, - countClassName: item.countClassName, - description: item.description, - isExpanded: true, - containerClasses: '', - }; - }); - +const InsightsStats = ({ stats, matrixData }: InsightsStatsProps) => { return (
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts index a6691837d5..43b24dd2d3 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/types.ts @@ -22,5 +22,4 @@ export type StatItem = { data?: { count: number; color: string }[]; tooltipText?: string; countClassName?: string; - description?: string; }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx index 526d748f52..327066a94c 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/index.tsx @@ -61,7 +61,7 @@ const ScriptBlocking = () => { ], Observability: [ { - title: 'Blocked Domain List', + title: 'Site', content: { Element: MDLTable, props: {}, @@ -70,7 +70,7 @@ const ScriptBlocking = () => { }, }, { - title: 'Session Insights', + title: 'Session', content: { Element: SessionInsights, props: {}, From 5d40dd539b88f7e5c5cdc6d8b23867b0b39b4d14 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 30 Sep 2025 14:31:49 +0530 Subject: [PATCH 87/98] Refactor SessionInsights and InsightsStats: update stats structure to use 'count' instead of 'centerCount', integrate useMemo for performance optimization, and replace CirclePieChart with CookiesLandingWrapper for improved layout. --- .../matrix/matrixComponent/index.tsx | 1 + .../sessionInsights.tsx | 4 +- .../mdlCommon/insightsStats.tsx | 69 ++------- .../mdlTable/sessionInsights.tsx | 133 ++++++++++++------ 4 files changed, 108 insertions(+), 99 deletions(-) diff --git a/packages/design-system/src/components/matrix/matrixComponent/index.tsx b/packages/design-system/src/components/matrix/matrixComponent/index.tsx index 544d404925..cff495f870 100644 --- a/packages/design-system/src/components/matrix/matrixComponent/index.tsx +++ b/packages/design-system/src/components/matrix/matrixComponent/index.tsx @@ -43,6 +43,7 @@ const MatrixComponent = ({ isExpanded = false, countClassName, }: MatrixComponentProps) => { + console.log(description); return (
diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx index 30854597d7..a912cdada2 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/ipProtection/probabilisticRevealTokens/sessionInsights.tsx @@ -34,7 +34,7 @@ const SessionInsights = () => { stats: [ { title: 'Domains', - centerCount: statistics.globalView.domains, + count: statistics.globalView.domains, color: '#F3AE4E', data: [ { @@ -50,7 +50,7 @@ const SessionInsights = () => { }, { title: 'PRT', - centerCount: statistics.globalView.totalTokens, + count: statistics.globalView.totalTokens, color: '#EC7159', data: [ { diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx index 4e766fb16f..97352c7104 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -18,74 +18,35 @@ */ import React from 'react'; import { - CirclePieChart, + CookiesLandingWrapper, MatrixContainer, type MatrixComponentProps, } from '@google-psat/design-system'; -import classnames from 'classnames'; /** * Internal dependencies. */ -import type { StatItem } from './types'; +import type { DataMapping } from '@google-psat/common'; interface InsightsStatsProps { - stats: StatItem[]; + stats: DataMapping[]; matrixData: MatrixComponentProps[]; } const InsightsStats = ({ stats, matrixData }: InsightsStatsProps) => { return ( -
-
- {stats.map( - ( - { title, centerCount, color, onClick, data, tooltipText }, - index - ) => ( - - ) - )} -
-
-
- -
-
-
+ + {matrixData.length > 0 && ( + + )} + ); }; diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx index bacd2bee53..2189fbc604 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/scriptBlocking/mdlTable/sessionInsights.tsx @@ -16,7 +16,7 @@ /** * External dependencies. */ -import React from 'react'; +import React, { useMemo } from 'react'; /** * Internal dependencies. @@ -29,49 +29,96 @@ const SessionInsights = () => { statistics: state.statistics, })); - const stats = [ - { - title: 'Domains', - centerCount: statistics.globalView.domains, - color: '#25ACAD', - description: 'Total browsing session domains', - countClassName: 'text-emerald', - }, - { - title: 'BDL', - centerCount: - statistics.globalView.partiallyBlockedDomains + - statistics.globalView.completelyBlockedDomains, - color: '#7D8471', - description: 'Total domains in block list', - countClassName: 'text-max-yellow-red', - }, - { - title: 'Blockings', - centerCount: - statistics.globalView.partiallyBlockedDomains + - statistics.globalView.completelyBlockedDomains, - color: '#EC7159', - description: 'Blocked domains', - countClassName: 'text-blue-berry', - }, - { - title: 'Complete', - centerCount: statistics.globalView.completelyBlockedDomains, - color: '#F3AE4E', - description: 'Completely blocked domains', - countClassName: 'text-blue-berry', - }, - { - title: 'Partial', - centerCount: statistics.globalView.partiallyBlockedDomains, - color: '#4C79F4', - description: 'Partially blocked domains', - countClassName: 'text-blue-berry', - }, - ]; + const { stats, matrixData } = useMemo( + () => ({ + stats: [ + { + title: 'Domains', + count: statistics.globalView.domains, + color: '#25ACAD', + data: [ + { + count: statistics.globalView.domains, + color: '#25ACAD', + }, + { + count: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#4C79F4', + }, + ], + countClassName: 'text-emerald', + }, + { + title: 'Blockings', + count: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#EC7159', + data: [ + { + count: statistics.globalView.completelyBlockedDomains, + color: '#F3AE4E', + }, + { + count: statistics.globalView.partiallyBlockedDomains, + color: '#4C79F4', + }, + ], + countClassName: 'text-blue-berry', + }, + ], + matrixData: [ + { + title: 'Domains', + count: statistics.globalView.domains, + color: '#25ACAD', + description: 'Total browsing session domains', + countClassName: 'text-emerald', + }, + { + title: 'BDL', + count: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#7D8471', + description: 'Total domains in block list', + countClassName: 'text-max-yellow-red', + }, + { + title: 'Blockings', + count: + statistics.globalView.partiallyBlockedDomains + + statistics.globalView.completelyBlockedDomains, + color: '#EC7159', + description: 'Blocked domains', + countClassName: 'text-blue-berry', + }, + { + title: 'Complete', + count: statistics.globalView.completelyBlockedDomains, + color: '#F3AE4E', + description: 'Completely blocked domains', + countClassName: 'text-blue-berry', + }, + { + title: 'Partial', + count: statistics.globalView.partiallyBlockedDomains, + color: '#4C79F4', + description: 'Partially blocked domains', + countClassName: 'text-blue-berry', + }, + ], + }), + [ + statistics.globalView.completelyBlockedDomains, + statistics.globalView.domains, + statistics.globalView.partiallyBlockedDomains, + ] + ); - return ; + return ; }; export default SessionInsights; From bc550f61b85332043df4350aab14beed82007cc9 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 30 Sep 2025 14:45:37 +0530 Subject: [PATCH 88/98] Refactor InsightsStats: update testId in CookiesLandingWrapper for consistency --- .../pages/privacyProtection/mdlCommon/insightsStats.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx index 97352c7104..6e89493c2f 100644 --- a/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx +++ b/packages/extension/src/view/devtools/pages/privacyProtection/mdlCommon/insightsStats.tsx @@ -35,10 +35,7 @@ interface InsightsStatsProps { const InsightsStats = ({ stats, matrixData }: InsightsStatsProps) => { return ( - + {matrixData.length > 0 && ( Date: Tue, 30 Sep 2025 14:53:58 +0530 Subject: [PATCH 89/98] Fix getSignal function to return 'No Signal' for empty input instead of base64 encoding --- packages/extension/src/utils/getSignal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/utils/getSignal.ts b/packages/extension/src/utils/getSignal.ts index 9b5855b094..8950837da7 100644 --- a/packages/extension/src/utils/getSignal.ts +++ b/packages/extension/src/utils/getSignal.ts @@ -35,7 +35,7 @@ function formatIPv6(buffer: number[]) { */ function getSignal(signal: number[]) { if (signal.every((byte) => byte === 0)) { - return btoa(String.fromCharCode.apply(null, signal as number[])); + return 'No Signal'; } const ipv4MappedPrefix = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff]; From 25c47fa2665476a487fa171e5f71360ff72bad58 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 30 Sep 2025 10:28:10 +0530 Subject: [PATCH 90/98] fix: check if group is selected before changing active tab --- packages/design-system/src/components/tabs/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/design-system/src/components/tabs/index.tsx b/packages/design-system/src/components/tabs/index.tsx index c8eb62eb76..665336f645 100644 --- a/packages/design-system/src/components/tabs/index.tsx +++ b/packages/design-system/src/components/tabs/index.tsx @@ -231,7 +231,13 @@ const Tabs = ({ showBottomBorder = true, fontSizeClass }: TabsProps) => { )} >