From 26caaa08f7616ab5faeefab3d7a80bb60b83311e Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 11:43:47 +0530 Subject: [PATCH 01/40] Add PA analysis in service worker. Improve attach CDP function. --- .../extension/src/serviceWorker/attachCDP.ts | 53 ++++-- packages/extension/src/serviceWorker/index.ts | 174 ++++++++++++++---- .../src/store/synchnorousCookieStore.ts | 128 +++++++++++-- .../extension/src/store/utils/formatTime.ts | 33 ++++ .../extension/src/store/utils/networkTime.ts | 42 +++++ 5 files changed, 365 insertions(+), 65 deletions(-) create mode 100644 packages/extension/src/store/utils/formatTime.ts create mode 100644 packages/extension/src/store/utils/networkTime.ts diff --git a/packages/extension/src/serviceWorker/attachCDP.ts b/packages/extension/src/serviceWorker/attachCDP.ts index 55597a9df5..aa6a61b49d 100644 --- a/packages/extension/src/serviceWorker/attachCDP.ts +++ b/packages/extension/src/serviceWorker/attachCDP.ts @@ -16,24 +16,43 @@ /** * This function will attach the debugger to the given target. * @param {{ [key: string]: number | string }} target The target where debugger needs to be attached. + * @param { boolean } childDebugee determine whether the target is child debugee or not. */ -export default async function attachCDP(target: { - [key: string]: number | string; -}) { - try { - await chrome.debugger.attach(target, '1.3'); - await chrome.debugger.sendCommand(target, 'Target.setAutoAttach', { - // If this is set to true, debugger will be attached to every new target that is added to the current target. +export default function attachCDP( + target: { [key: string]: number | string }, + childDebugee = false +) { + chrome.debugger.attach(target, '1.3', () => { + chrome.debugger.sendCommand(target, 'Target.setAutoAttach', { autoAttach: true, - waitForDebuggerOnStart: false, - //Enables "flat" access to the session via specifying sessionId attribute in the commands. - // If this is set to true the debugger is also attached to the child targets of that the target it has been attached to. - flatten: true, + flatten: false, + waitForDebuggerOnStart: true, }); - await chrome.debugger.sendCommand(target, 'Network.enable'); - await chrome.debugger.sendCommand(target, 'Audits.enable'); - await chrome.debugger.sendCommand(target, 'Page.enable'); - } catch (error) { - //Fail silently - } + + chrome.debugger.sendCommand( + target, + 'Storage.setInterestGroupAuctionTracking', + { enable: true } + ); + chrome.debugger.sendCommand(target, 'Audits.enable'); + chrome.debugger.sendCommand(target, 'Storage.setInterestGroupTracking', { + enable: true, + }); + chrome.debugger.sendCommand(target, 'Page.enable'); + chrome.debugger.sendCommand(target, 'Network.enable'); + + if (childDebugee) { + // In non-flat mode, we must send runIfWaitingForDebugger via + // sendMessageToTarget for it to actually work. + const message = { + id: 0, + method: 'Runtime.runIfWaitingForDebugger', + params: {}, + }; + chrome.debugger.sendCommand(target, 'Target.sendMessageToTarget', { + message: JSON.stringify(message), + targetId: target?.targetId, + }); + } + }); } diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 08f5aced2f..0048459d5d 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -23,13 +23,19 @@ import { Protocol } from 'devtools-protocol'; */ import syncCookieStore from '../store/synchnorousCookieStore'; import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; -import attachCDP from './attachCDP'; + import './chromeListeners'; +import networkTime from '../store/utils/networkTime'; +import attachCDP from './attachCDP'; const ALLOWED_EVENTS = [ 'Network.responseReceived', 'Network.requestWillBeSentExtraInfo', 'Network.responseReceivedExtraInfo', + 'Network.loadingFailed', + 'Storage.interestGroupAuctionEventOccurred', + 'Storage.interestGroupAuctionNetworkRequestCreated', + 'Network.loadingFinished', 'Audits.issueAdded', 'Network.requestWillBeSent', 'Page.frameAttached', @@ -62,21 +68,13 @@ chrome.debugger.onEvent.addListener((source, method, params) => { targets = await chrome.debugger.getTargets(); - await Promise.all( - targets.map(async ({ id, url }) => { - if (url.startsWith('http')) { - await attachCDP({ targetId: id }); - } - }) - ); - // This is to get a list of all targets being attached to the main frame. if (method === 'Target.attachedToTarget' && params) { const { targetInfo: { targetId, url }, } = params as Protocol.Target.AttachedToTargetEvent; - await attachCDP({ targetId }); + attachCDP({ targetId }); targets = await chrome.debugger.getTargets(); const parentFrameId = targets.filter( @@ -145,6 +143,73 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return; } + if (method === 'Storage.interestGroupAuctionEventOccurred' && params) { + const interestGroupAuctionEventOccured = + params as Protocol.Storage.InterestGroupAuctionEventOccurredEvent; + + const { uniqueAuctionId } = interestGroupAuctionEventOccured; + if (interestGroupAuctionEventOccured.type === 'started') { + syncCookieStore.auctionDataForTabId[parseInt(tabId)][ + uniqueAuctionId + ] = { + auctionTime: interestGroupAuctionEventOccured.eventTime, + auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + }; + } + + if ( + interestGroupAuctionEventOccured.type === 'configResolved' || + interestGroupAuctionEventOccured.type === 'started' + ) { + syncCookieStore.auctionDataForTabId[parseInt(tabId)][ + uniqueAuctionId + ] = { + ...syncCookieStore.auctionDataForTabId[parseInt(tabId)][ + uniqueAuctionId + ], + auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + }; + } + return; + } + + if ( + method === 'Storage.interestGroupAuctionNetworkRequestCreated' && + params + ) { + const interestGroupAuctionNetworkRequestCreatedParams = + params as Protocol.Storage.InterestGroupAuctionNetworkRequestCreatedEvent; + + const requestId = + interestGroupAuctionNetworkRequestCreatedParams.requestId; + + syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId] = { + auctions: interestGroupAuctionNetworkRequestCreatedParams.auctions, + type: interestGroupAuctionNetworkRequestCreatedParams.type, + }; + + if (syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]) { + syncCookieStore.auctionEvents[parseInt(tabId)].push({ + bidCurrency: '', + bid: 0, + name: '', + ownerOrigin: '', + type: + 'Start fetch ' + + interestGroupAuctionNetworkRequestCreatedParams.type, + time: networkTime( + requestId, + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + .timeStamp, + tabId + ), + auctionConfig: {}, + }); + } + + return; + } + //If we get requestWillBeSent before requestWillBeSentExtraInfo then we add the frame if to the object. // If we get requestWillBeSent afterwards then we will remove the add the frameId and then process the requestWillBeSentExtraInfo. if (method === 'Network.requestWillBeSent' && params) { @@ -152,14 +217,12 @@ chrome.debugger.onEvent.addListener((source, method, params) => { requestId, request: { url: requestUrl }, frameId = '', + timestamp, + wallTime, } = params as Protocol.Network.RequestWillBeSentEvent; let finalFrameId = frameId; - if (!finalFrameId) { - return; - } - targets = await chrome.debugger.getTargets(); const setTargets = new Set(); @@ -181,6 +244,8 @@ chrome.debugger.onEvent.addListener((source, method, params) => { finalFrameId, frameId, url: requestUrl, + timeStamp: timestamp, + wallTime, }, }; } else { @@ -190,13 +255,33 @@ chrome.debugger.onEvent.addListener((source, method, params) => { finalFrameId, frameId, url: requestUrl, + timeStamp: timestamp, + wallTime, }, }; } - if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { - syncCookieStore.parseRequestHeaders( - syncCookieStore.unParsedRequestHeaders[tabId][requestId], + if (syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId]) { + syncCookieStore.auctionEvents[parseInt(tabId)].push({ + bidCurrency: '', + bid: 0, + name: '', + ownerOrigin: '', + type: + 'Start fetch ' + + syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId] + .type, + time: networkTime(requestId, timestamp, tabId), + auctionConfig: {}, + }); + } + + if ( + syncCookieStore.tabs[Number(tabId)]?.isCookieAnalysisEnabled && + syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId] + ) { + syncCookieStore.parseRequestHeadersForCA( + syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) @@ -205,12 +290,36 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return; } + if (method === 'Network.loadingFinished' && params) { + const loadingFinishedParams = + params as Protocol.Network.LoadingFinishedEvent; + + syncCookieStore.parseRequestHeadersForPA( + loadingFinishedParams.requestId, + loadingFinishedParams.timestamp, + tabId, + 'Finished Fetch' + ); + } + + if (method === 'Network.loadingFailed' && params) { + const loadingFailedParams = + params as Protocol.Network.LoadingFinishedEvent; + + syncCookieStore.parseRequestHeadersForPA( + loadingFailedParams.requestId, + loadingFailedParams.timestamp, + tabId, + 'Failed Fetch' + ); + } + if (method === 'Network.requestWillBeSentExtraInfo') { const { requestId } = params as Protocol.Network.RequestWillBeSentExtraInfoEvent; if (syncCookieStore.requestIdToCDPURLMapping[tabId]?.[requestId]) { - syncCookieStore.parseRequestHeaders( + syncCookieStore.parseRequestHeadersForCA( params as Protocol.Network.RequestWillBeSentExtraInfoEvent, requestId, tabId, @@ -224,7 +333,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ) ); } else { - syncCookieStore.unParsedRequestHeaders[tabId][requestId] = + syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId] = params as Protocol.Network.RequestWillBeSentExtraInfoEvent; } return; @@ -264,6 +373,9 @@ chrome.debugger.onEvent.addListener((source, method, params) => { if (!syncCookieStore.requestIdToCDPURLMapping[tabId]) { syncCookieStore.requestIdToCDPURLMapping[tabId] = { [requestId]: { + ...(syncCookieStore.requestIdToCDPURLMapping[tabId]?.[ + requestId + ] ?? {}), finalFrameId, frameId, url: requestUrl, @@ -273,6 +385,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { syncCookieStore.requestIdToCDPURLMapping[tabId] = { ...syncCookieStore.requestIdToCDPURLMapping[tabId], [requestId]: { + ...syncCookieStore.requestIdToCDPURLMapping[tabId][requestId], finalFrameId, frameId, url: requestUrl, @@ -280,25 +393,24 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }; } - if (syncCookieStore.unParsedResponseHeaders[tabId][requestId]) { - syncCookieStore.parseResponseHeaders( - syncCookieStore.unParsedResponseHeaders[tabId][requestId], + if (syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId]) { + syncCookieStore.parseResponseHeadersForCA( + syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) ); } - if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { - syncCookieStore.parseRequestHeaders( - syncCookieStore.unParsedRequestHeaders[tabId][requestId], + if (syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId]) { + syncCookieStore.parseRequestHeadersForCA( + syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) ); } - delete syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]; return; } @@ -321,23 +433,23 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ]) ); - syncCookieStore.parseResponseHeaders( + syncCookieStore.parseResponseHeadersForCA( params as Protocol.Network.ResponseReceivedExtraInfoEvent, requestId, tabId, frameIds ); - if (syncCookieStore.unParsedRequestHeaders[tabId][requestId]) { - syncCookieStore.parseRequestHeaders( - syncCookieStore.unParsedRequestHeaders[tabId][requestId], + if (syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId]) { + syncCookieStore.parseRequestHeadersForCA( + syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, frameIds ); } } else { - syncCookieStore.unParsedResponseHeaders[tabId][requestId] = + syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId] = params as Protocol.Network.ResponseReceivedExtraInfoEvent; } return; diff --git a/packages/extension/src/store/synchnorousCookieStore.ts b/packages/extension/src/store/synchnorousCookieStore.ts index af9c252523..5ec3f15efd 100644 --- a/packages/extension/src/store/synchnorousCookieStore.ts +++ b/packages/extension/src/store/synchnorousCookieStore.ts @@ -35,7 +35,16 @@ import { NEW_COOKIE_DATA } from '../constants'; import isValidURL from '../utils/isValidURL'; import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; - +import networkTime from './utils/networkTime'; +interface singleAuctionEvent { + bidCurrency: string; + bid: number; + name: string; + ownerOrigin: string; + type: string; + time: string | Date | number | undefined; + auctionConfig: object; +} class SynchnorousCookieStore { /** * The cookie data of the tabs. @@ -46,6 +55,25 @@ class SynchnorousCookieStore { }; } = {}; + /** + * The cookie data of the tabs. + */ + auctionEvents: { + [tabId: number]: singleAuctionEvent[]; + } = {}; + + /** + * The auction data of the tabs. + */ + auctionDataForTabId: { + [tabId: number]: { + [uniqueAuctionId: Protocol.Storage.InterestGroupAuctionId]: { + auctionTime: Protocol.Network.TimeSinceEpoch; + auctionConfig?: any; + }; + }; + } = {}; + /** * The cookie data of the tabs. */ @@ -70,6 +98,8 @@ class SynchnorousCookieStore { frameId: string; url: string; finalFrameId: string; + timeStamp: Protocol.Network.MonotonicTime; + wallTime: Protocol.Network.TimeSinceEpoch; }; }; } = {}; @@ -78,7 +108,7 @@ class SynchnorousCookieStore { * This variable stores the unParsedRequest headers received from Network.requestWillBeSentExtraInfo. * These are the requests whose Network.requestWillBeSent counter part havent yet been fired. */ - unParsedRequestHeaders: { + unParsedRequestHeadersForCA: { [tabId: string]: { [requestId: string]: Protocol.Network.RequestWillBeSentExtraInfoEvent; }; @@ -88,7 +118,20 @@ class SynchnorousCookieStore { * This variable stores the unParsedResonse headers received from Network.responseReceivedExtraInfo. * These are the responses whose Network.responseReceived counter part havent yet been fired. */ - unParsedResponseHeaders: { + unParsedRequestHeadersForPA: { + [tabId: string]: { + [requestId: string]: { + auctions: Protocol.Storage.InterestGroupAuctionId[]; + type: Protocol.Storage.InterestGroupAuctionFetchType; + }; + }; + } = {}; + + /** + * This variable stores the unParsedResonse headers received from Network.responseReceivedExtraInfo. + * These are the responses whose Network.responseReceived counter part havent yet been fired. + */ + unParsedResponseHeadersForCA: { [tabId: string]: { [requestId: string]: Protocol.Network.ResponseReceivedExtraInfoEvent; }; @@ -117,6 +160,8 @@ class SynchnorousCookieStore { newUpdates: number; frameIDURLSet: Record; parentChildFrameAssociation: Record; + isCookieAnalysisEnabled: boolean; + isPAAnalysisEnabled: boolean; }; } = {}; @@ -172,13 +217,52 @@ class SynchnorousCookieStore { } /** - * This function parses response headers + * This function parses response headers for Protected Analysis PA. + * @param {string} requestId This is used to get the related data for parsing the request. + * @param { Protocol.Network.MonotonicTime } timestamp Timestamp of the request + * @param {string} tabId The tabId this request is associated to. + * @param {string} method determines which event called the function. + */ + parseRequestHeadersForPA( + requestId: string, + timestamp: Protocol.Network.MonotonicTime, + tabId: string, + method: string + ) { + if (!this.unParsedRequestHeadersForPA[tabId][requestId]?.auctions) { + return; + } + const { auctions, type } = + this.unParsedRequestHeadersForPA[tabId][requestId]; + + const calculatedNetworkTime = networkTime(requestId, timestamp, tabId); + auctions.forEach((uniqueAuctionId) => { + if (!this.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]) { + return; + } + const { auctionConfig = {} } = + this.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]; + + this.auctionEvents[parseInt(tabId)].push({ + bidCurrency: auctionConfig?.bidCurrency ?? '', + bid: auctionConfig?.bid ?? '', + name: auctionConfig?.name ?? '', + ownerOrigin: auctionConfig?.ownerOrigin ?? '', + type: method + type, + time: calculatedNetworkTime, + auctionConfig, + }); + }); + } + + /** + * This function parses response headers for Cookie Analysis. * @param {Protocol.Network.ResponseReceivedExtraInfoEvent} response The response to be parsed. * @param {string} requestId This is used to get the related data for parsing the response. * @param {string} tabId The tabId this request is associated to. * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. */ - parseResponseHeaders( + parseResponseHeadersForCA( response: Protocol.Network.ResponseReceivedExtraInfoEvent, requestId: string, tabId: string, @@ -200,17 +284,17 @@ class SynchnorousCookieStore { ); this.update(Number(tabId), cookies); - delete this.unParsedResponseHeaders[tabId][requestId]; + delete this.unParsedResponseHeadersForCA[tabId][requestId]; } /** - * This function parses request headers + * This function parses request headers for Cookie Analysis. * @param {Protocol.Network.RequestWillBeSentExtraInfoEvent} request The response to be parsed. * @param {string} requestId This is used to get the related data for parsing the response. * @param {string} tabId The tabId this request is associated to. * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. */ - parseRequestHeaders( + parseRequestHeadersForCA( request: Protocol.Network.RequestWillBeSentExtraInfoEvent, requestId: string, tabId: string, @@ -227,13 +311,13 @@ class SynchnorousCookieStore { requestId ); - delete this.unParsedRequestHeaders[tabId][requestId]; + delete this.unParsedRequestHeadersForCA[tabId][requestId]; if (cookies.length === 0) { return; } this.update(Number(tabId), cookies); - delete this.unParsedRequestHeaders[tabId][requestId]; + delete this.unParsedRequestHeadersForCA[tabId][requestId]; } /** @@ -360,9 +444,11 @@ class SynchnorousCookieStore { globalThis.PSAT = { tabsData: this.tabsData, tabs: this.tabs, + auctionEvents: this.auctionEvents, }; this.tabsData[tabId] = {}; + this.auctionEvents[tabId] = []; this.tabs[tabId] = { url: '', devToolsOpenState: false, @@ -370,7 +456,12 @@ class SynchnorousCookieStore { newUpdates: 0, frameIDURLSet: {}, parentChildFrameAssociation: {}, + isCookieAnalysisEnabled: true, + isPAAnalysisEnabled: true, }; + + this.auctionDataForTabId[tabId] = {}; + (async () => { if (!this.cookieDB) { this.cookieDB = await fetchDictionary(); @@ -397,8 +488,8 @@ class SynchnorousCookieStore { * @param {string} tabId The tab whose data has to be deinitialised. */ deinitialiseVariablesForTab(tabId: string) { - delete this.unParsedRequestHeaders[tabId]; - delete this.unParsedResponseHeaders[tabId]; + delete this.unParsedRequestHeadersForCA[tabId]; + delete this.unParsedResponseHeadersForCA[tabId]; delete this.requestIdToCDPURLMapping[tabId]; delete this.frameIdToResourceMap[tabId]; } @@ -466,16 +557,19 @@ class SynchnorousCookieStore { * @param {string} tabId The tab whose data has to be initialised. */ initialiseVariablesForNewTab(tabId: string) { - this.unParsedRequestHeaders[tabId] = {}; - this.unParsedResponseHeaders[tabId] = {}; + this.unParsedRequestHeadersForCA[tabId] = {}; + this.unParsedResponseHeadersForCA[tabId] = {}; this.requestIdToCDPURLMapping[tabId] = {}; this.frameIdToResourceMap[tabId] = {}; + this.unParsedRequestHeadersForPA[tabId] = {}; //@ts-ignore globalThis.PSATAdditionalData = { - unParsedRequestHeaders: this.unParsedRequestHeaders, - unParsedResponseHeaders: this.unParsedResponseHeaders, + unParsedRequestHeadersForCA: this.unParsedRequestHeadersForCA, + unParsedResponseHeadersForCA: this.unParsedResponseHeadersForCA, requestIdToCDPURLMapping: this.requestIdToCDPURLMapping, frameIdToResourceMap: this.frameIdToResourceMap, + auctionDataForTabId: this.auctionDataForTabId, + unParsedRequestHeadersForPA: this.unParsedRequestHeadersForPA, }; } @@ -493,7 +587,7 @@ class SynchnorousCookieStore { this.tabs[tabId].newUpdates = 0; this.tabs[tabId].frameIDURLSet = {}; this.tabs[tabId].parentChildFrameAssociation = {}; - + this.auctionEvents[tabId] = []; this.sendUpdatedDataToPopupAndDevTools(tabId, true); } diff --git a/packages/extension/src/store/utils/formatTime.ts b/packages/extension/src/store/utils/formatTime.ts new file mode 100644 index 0000000000..220cab376c --- /dev/null +++ b/packages/extension/src/store/utils/formatTime.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2024 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 { Protocol } from 'devtools-protocol'; +/** + * This formats the time and returns it in a readable format + * @param { Protocol.Network.TimeSinceEpoch } startTime time at which the event started. + * @param { Protocol.Network.TimeSinceEpoch } eventTime time at which the event occured. + * @returns {Date} the formatted time. + */ +export default function formatTime( + startTime: Protocol.Network.TimeSinceEpoch, + eventTime: Protocol.Network.TimeSinceEpoch +) { + return startTime + ? `${((eventTime - startTime) * 1000).toFixed(2)}ms` + : new Date(eventTime * 1000); +} diff --git a/packages/extension/src/store/utils/networkTime.ts b/packages/extension/src/store/utils/networkTime.ts new file mode 100644 index 0000000000..811136f2df --- /dev/null +++ b/packages/extension/src/store/utils/networkTime.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2024 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 { Protocol } from 'devtools-protocol'; +import synchnorousCookieStore from '../synchnorousCookieStore'; + +/** + * Helps decode timestamps in network-related events, which are only convertible + * to global time in context of corresponding Network.requestWillBeSent + * @param { string } requestId request identifier for which network time needs to be calculated + * @param { Protocol.Network.MonotonicTime } timestamp Timestamp of the request + * @param { string } tabId Tab the request is associated to. + * @returns timestamp + */ +export default function networkTime( + requestId: string, + timestamp: Protocol.Network.MonotonicTime, + tabId: string +) { + const timeInfo = + synchnorousCookieStore.requestIdToCDPURLMapping[tabId][requestId]; + // Somehow missed the start event? + if (!timeInfo) { + return undefined; + } + return timeInfo.wallTime + (timestamp - timeInfo.timeStamp); +} From f99a184af208bd14e7a95678ea936ae1856d41e1 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 13:43:22 +0530 Subject: [PATCH 02/40] Add storage.interestGroupAccessed method to service worker. --- .../extension/src/serviceWorker/attachCDP.ts | 67 +++++++++--------- packages/extension/src/serviceWorker/index.ts | 68 ++++++++++++++++++- .../src/store/synchnorousCookieStore.ts | 8 +++ 3 files changed, 107 insertions(+), 36 deletions(-) diff --git a/packages/extension/src/serviceWorker/attachCDP.ts b/packages/extension/src/serviceWorker/attachCDP.ts index aa6a61b49d..6112d4775f 100644 --- a/packages/extension/src/serviceWorker/attachCDP.ts +++ b/packages/extension/src/serviceWorker/attachCDP.ts @@ -16,43 +16,42 @@ /** * This function will attach the debugger to the given target. * @param {{ [key: string]: number | string }} target The target where debugger needs to be attached. - * @param { boolean } childDebugee determine whether the target is child debugee or not. */ -export default function attachCDP( - target: { [key: string]: number | string }, - childDebugee = false -) { - chrome.debugger.attach(target, '1.3', () => { - chrome.debugger.sendCommand(target, 'Target.setAutoAttach', { - autoAttach: true, - flatten: false, - waitForDebuggerOnStart: true, - }); +export default function attachCDP(target: { [key: string]: number | string }) { + chrome.debugger.attach(target, '1.3', async () => { + if (chrome.runtime.lastError) { + // eslint-disable-next-line no-console + console.warn(chrome.runtime.lastError); + } + try { + await chrome.debugger.sendCommand(target, 'Target.setAutoAttach', { + autoAttach: true, + flatten: false, + waitForDebuggerOnStart: true, + }); - chrome.debugger.sendCommand( - target, - 'Storage.setInterestGroupAuctionTracking', - { enable: true } - ); - chrome.debugger.sendCommand(target, 'Audits.enable'); - chrome.debugger.sendCommand(target, 'Storage.setInterestGroupTracking', { - enable: true, - }); - chrome.debugger.sendCommand(target, 'Page.enable'); - chrome.debugger.sendCommand(target, 'Network.enable'); + await chrome.debugger.sendCommand( + target, + 'Storage.setInterestGroupAuctionTracking', + { enable: true } + ); - if (childDebugee) { - // In non-flat mode, we must send runIfWaitingForDebugger via - // sendMessageToTarget for it to actually work. - const message = { - id: 0, - method: 'Runtime.runIfWaitingForDebugger', - params: {}, - }; - chrome.debugger.sendCommand(target, 'Target.sendMessageToTarget', { - message: JSON.stringify(message), - targetId: target?.targetId, - }); + await chrome.debugger.sendCommand(target, 'Audits.enable'); + + await chrome.debugger.sendCommand( + target, + 'Storage.setInterestGroupTracking', + { + enable: true, + } + ); + + await chrome.debugger.sendCommand(target, 'Page.enable'); + + await chrome.debugger.sendCommand(target, 'Network.enable'); + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); } }); } diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 0048459d5d..65ee999a1d 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -26,7 +26,6 @@ import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; import './chromeListeners'; import networkTime from '../store/utils/networkTime'; -import attachCDP from './attachCDP'; const ALLOWED_EVENTS = [ 'Network.responseReceived', @@ -38,6 +37,7 @@ const ALLOWED_EVENTS = [ 'Network.loadingFinished', 'Audits.issueAdded', 'Network.requestWillBeSent', + 'Storage.interestGroupAccessed', 'Page.frameAttached', 'Page.frameNavigated', 'Target.attachedToTarget', @@ -74,7 +74,52 @@ chrome.debugger.onEvent.addListener((source, method, params) => { targetInfo: { targetId, url }, } = params as Protocol.Target.AttachedToTargetEvent; - attachCDP({ targetId }); + const childDebuggee = { targetId }; + chrome.debugger.attach(childDebuggee, '1.3', async () => { + if (chrome.runtime.lastError) { + // eslint-disable-next-line no-console + console.warn(chrome.runtime.lastError); + } + try { + await chrome.debugger.sendCommand( + childDebuggee, + 'Storage.setInterestGroupAuctionTracking', + { enable: true } + ); + + await chrome.debugger.sendCommand( + childDebuggee, + 'Network.enable', + {} + ); + + await chrome.debugger.sendCommand( + childDebuggee, + 'Audits.enable', + {} + ); + + await chrome.debugger.sendCommand(childDebuggee, 'Page.enable', {}); + + const message = { + id: 0, + method: 'Runtime.runIfWaitingForDebugger', + params: {}, + }; + + await chrome.debugger.sendCommand( + source, + 'Target.sendMessageToTarget', + { + message: JSON.stringify(message), + targetId: targetId, + } + ); + } catch (error) { + // eslint-disable-next-line no-console + console.warn(error); + } + }); targets = await chrome.debugger.getTargets(); const parentFrameId = targets.filter( @@ -173,6 +218,23 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return; } + if (method === 'Storage.interestGroupAccessed' && params) { + const interestGroupAccessedParams = + params as Protocol.Storage.InterestGroupAccessedEvent; + + syncCookieStore.auctionEvents[parseInt(tabId)].push({ + bidCurrency: '', + bid: 0, + name: interestGroupAccessedParams.name, + ownerOrigin: interestGroupAccessedParams.ownerOrigin, + type: interestGroupAccessedParams.type, + time: interestGroupAccessedParams.accessTime, + auctionConfig: {}, + interestGroupConfig: interestGroupAccessedParams, + eventType: 'interestGroupAccessed', + }); + } + if ( method === 'Storage.interestGroupAuctionNetworkRequestCreated' && params @@ -204,6 +266,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { tabId ), auctionConfig: {}, + eventType: 'interestGroupAuctionNetworkRequestCreated', }); } @@ -273,6 +336,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { .type, time: networkTime(requestId, timestamp, tabId), auctionConfig: {}, + eventType: 'interestGroupAuctionNetworkRequestCreated', }); } diff --git a/packages/extension/src/store/synchnorousCookieStore.ts b/packages/extension/src/store/synchnorousCookieStore.ts index 5ec3f15efd..db4eaa2106 100644 --- a/packages/extension/src/store/synchnorousCookieStore.ts +++ b/packages/extension/src/store/synchnorousCookieStore.ts @@ -44,6 +44,11 @@ interface singleAuctionEvent { type: string; time: string | Date | number | undefined; auctionConfig: object; + interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; + eventType: + | 'interestGroupAuctionNetworkRequestCompleted' + | 'interestGroupAuctionNetworkRequestCreated' + | 'interestGroupAccessed'; } class SynchnorousCookieStore { /** @@ -251,6 +256,7 @@ class SynchnorousCookieStore { type: method + type, time: calculatedNetworkTime, auctionConfig, + eventType: 'interestGroupAuctionNetworkRequestCompleted', }); }); } @@ -598,6 +604,8 @@ class SynchnorousCookieStore { removeTabData(tabId: number) { delete this.tabsData[tabId]; delete this.tabs[tabId]; + delete this.auctionDataForTabId[tabId]; + delete this.auctionEvents[tabId]; } /** From c5644ffd6e429c99a3f9176a387bb9e2545f1d6f Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 14:29:08 +0530 Subject: [PATCH 03/40] Add bid value to the auction events. --- packages/extension/src/serviceWorker/index.ts | 59 ++++++++++++++++--- .../src/store/synchnorousCookieStore.ts | 13 +++- .../extension/src/store/utils/networkTime.ts | 2 +- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 65ee999a1d..0d02d5d539 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -26,6 +26,7 @@ import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; import './chromeListeners'; import networkTime from '../store/utils/networkTime'; +import formatTime from '../store/utils/formatTime'; const ALLOWED_EVENTS = [ 'Network.responseReceived', @@ -214,6 +215,24 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ], auctionConfig: interestGroupAuctionEventOccured.auctionConfig, }; + + syncCookieStore.auctionEvents[parseInt(tabId)].push({ + bidCurrency: '', + bid: 0, + name: '', + ownerOrigin: '', + type: interestGroupAuctionEventOccured.type, + formattedTime: + syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + interestGroupAuctionEventOccured.eventTime + ), + time: interestGroupAuctionEventOccured.eventTime, + auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + eventType: 'interestGroupAuctionEventOccurred', + }); } return; } @@ -223,10 +242,17 @@ chrome.debugger.onEvent.addListener((source, method, params) => { params as Protocol.Storage.InterestGroupAccessedEvent; syncCookieStore.auctionEvents[parseInt(tabId)].push({ - bidCurrency: '', - bid: 0, + bidCurrency: interestGroupAccessedParams?.bidCurrency ?? '', + bid: interestGroupAccessedParams?.bid ?? 0, name: interestGroupAccessedParams.name, ownerOrigin: interestGroupAccessedParams.ownerOrigin, + formattedTime: + syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + interestGroupAccessedParams.accessTime + ), type: interestGroupAccessedParams.type, time: interestGroupAccessedParams.accessTime, auctionConfig: {}, @@ -251,20 +277,30 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }; if (syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]) { + const time: number = + networkTime( + requestId, + syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + .timeStamp, + tabId + ) ?? new Date().getTime(); + syncCookieStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', bid: 0, name: '', ownerOrigin: '', + formattedTime: + syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + time + ), type: 'Start fetch ' + interestGroupAuctionNetworkRequestCreatedParams.type, - time: networkTime( - requestId, - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] - .timeStamp, - tabId - ), + time, auctionConfig: {}, eventType: 'interestGroupAuctionNetworkRequestCreated', }); @@ -334,6 +370,13 @@ chrome.debugger.onEvent.addListener((source, method, params) => { 'Start fetch ' + syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId] .type, + formattedTime: + syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + networkTime(requestId, timestamp, tabId) + ), time: networkTime(requestId, timestamp, tabId), auctionConfig: {}, eventType: 'interestGroupAuctionNetworkRequestCreated', diff --git a/packages/extension/src/store/synchnorousCookieStore.ts b/packages/extension/src/store/synchnorousCookieStore.ts index db4eaa2106..2384d24445 100644 --- a/packages/extension/src/store/synchnorousCookieStore.ts +++ b/packages/extension/src/store/synchnorousCookieStore.ts @@ -36,16 +36,19 @@ import isValidURL from '../utils/isValidURL'; import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; import networkTime from './utils/networkTime'; +import formatTime from './utils/formatTime'; interface singleAuctionEvent { bidCurrency: string; bid: number; name: string; ownerOrigin: string; type: string; - time: string | Date | number | undefined; + formattedTime: string | Date; + time: number; auctionConfig: object; interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; eventType: + | 'interestGroupAuctionEventOccurred' | 'interestGroupAuctionNetworkRequestCompleted' | 'interestGroupAuctionNetworkRequestCreated' | 'interestGroupAccessed'; @@ -241,6 +244,7 @@ class SynchnorousCookieStore { this.unParsedRequestHeadersForPA[tabId][requestId]; const calculatedNetworkTime = networkTime(requestId, timestamp, tabId); + auctions.forEach((uniqueAuctionId) => { if (!this.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]) { return; @@ -254,6 +258,13 @@ class SynchnorousCookieStore { name: auctionConfig?.name ?? '', ownerOrigin: auctionConfig?.ownerOrigin ?? '', type: method + type, + formattedTime: + this.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + this.auctionEvents[parseInt(tabId)][0].time, + networkTime(requestId, timestamp, tabId) + ), time: calculatedNetworkTime, auctionConfig, eventType: 'interestGroupAuctionNetworkRequestCompleted', diff --git a/packages/extension/src/store/utils/networkTime.ts b/packages/extension/src/store/utils/networkTime.ts index 811136f2df..1e5e30d0fa 100644 --- a/packages/extension/src/store/utils/networkTime.ts +++ b/packages/extension/src/store/utils/networkTime.ts @@ -36,7 +36,7 @@ export default function networkTime( synchnorousCookieStore.requestIdToCDPURLMapping[tabId][requestId]; // Somehow missed the start event? if (!timeInfo) { - return undefined; + return new Date().getTime(); } return timeInfo.wallTime + (timestamp - timeInfo.timeStamp); } From ace2317ecc86f6ab0df1d2e1a04928c68e00d07c Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 15:09:28 +0530 Subject: [PATCH 04/40] Split syncCookieStore into dataStore, CookieStore and PAStore. Change its references. --- .../beforeSendHeadersListener.ts | 17 +- .../onCommittedNavigationListener.ts | 24 +- .../chromeListeners/onEnabledListener.ts | 4 +- .../onResponseStartedListener.ts | 17 +- .../runtimeOnInstalledListener.ts | 4 +- .../runtimeOnMessageListener.ts | 75 ++--- .../chromeListeners/runtimeStartUpListener.ts | 6 +- .../syncStorageOnChangedListener.ts | 24 +- .../chromeListeners/tabOnCreatedListener.ts | 26 +- .../chromeListeners/tabOnRemovedListener.ts | 6 +- .../chromeListeners/utils/setupIntervals.ts | 12 +- .../utils/updateGlobalVariableAndAttachCDP.ts | 19 +- .../windowsOnRemovedListener.ts | 6 +- packages/extension/src/serviceWorker/index.ts | 153 ++++----- packages/extension/src/store/PAStore.ts | 78 +++++ packages/extension/src/store/cookieStore.ts | 268 +++++++++++++++ ...synchnorousCookieStore.ts => dataStore.ts} | 313 ++---------------- .../extension/src/store/utils/networkTime.ts | 2 +- .../src/utils/getAndParseNetworkCookies.ts | 2 +- .../extension/src/utils/listenToNewTab.ts | 2 +- 20 files changed, 551 insertions(+), 507 deletions(-) create mode 100644 packages/extension/src/store/PAStore.ts create mode 100644 packages/extension/src/store/cookieStore.ts rename packages/extension/src/store/{synchnorousCookieStore.ts => dataStore.ts} (63%) diff --git a/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts b/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts index 775e7414c3..2b5f6b7f6b 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/beforeSendHeadersListener.ts @@ -17,7 +17,8 @@ * Internal dependencies */ import parseHeaders from '../../utils/parseHeaders'; -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; +import cookieStore from '../../store/cookieStore'; import { getTab } from '../../utils/getTab'; export const onBeforeSendHeadersListener = ({ @@ -27,26 +28,26 @@ export const onBeforeSendHeadersListener = ({ frameId, requestId, }: chrome.webRequest.WebRequestHeadersDetails) => { - if (synchnorousCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { return; } (async () => { const tab = await getTab(tabId); - let tabUrl = synchnorousCookieStore?.getTabUrl(tabId); + let tabUrl = dataStore?.getTabUrl(tabId); if (tab && tab.pendingUrl) { tabUrl = tab.pendingUrl; } const cookies = await parseHeaders( - synchnorousCookieStore.globalIsUsingCDP, + dataStore.globalIsUsingCDP, 'request', - synchnorousCookieStore.tabToRead, - synchnorousCookieStore.tabMode, + dataStore.tabToRead, + dataStore.tabMode, tabId, url, - synchnorousCookieStore.cookieDB ?? {}, + dataStore.cookieDB ?? {}, tabUrl, frameId.toString(), requestId, @@ -58,6 +59,6 @@ export const onBeforeSendHeadersListener = ({ } // Adds the cookies from the request headers to the cookies object. - synchnorousCookieStore?.update(tabId, cookies); + cookieStore?.update(tabId, cookies); })(); }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/onCommittedNavigationListener.ts b/packages/extension/src/serviceWorker/chromeListeners/onCommittedNavigationListener.ts index 807075a22c..6e2463ac9e 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/onCommittedNavigationListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/onCommittedNavigationListener.ts @@ -17,7 +17,7 @@ * Internal dependencies */ import { TABID_STORAGE } from '../../constants'; -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import getQueryParams from '../../utils/getQueryParams'; import attachCDP from '../attachCDP'; @@ -36,7 +36,7 @@ export const onCommittedNavigationListener = async ({ } const targets = await chrome.debugger.getTargets(); - const mainFrameId = synchnorousCookieStore?.globalIsUsingCDP + const mainFrameId = dataStore?.globalIsUsingCDP ? targets.filter((target) => target.tabId && target.tabId === tabId)[0] ?.id : 0; @@ -50,19 +50,19 @@ export const onCommittedNavigationListener = async ({ isUsingCDP: queryParams.psat_cdp === 'on', }); - synchnorousCookieStore.globalIsUsingCDP = queryParams.psat_cdp === 'on'; - synchnorousCookieStore.tabMode = + dataStore.globalIsUsingCDP = queryParams.psat_cdp === 'on'; + dataStore.tabMode = queryParams.psat_multitab === 'on' ? 'unlimited' : 'single'; } - synchnorousCookieStore?.updateUrl(tabId, url); + dataStore?.updateUrl(tabId, url); if (url && !url.startsWith('chrome://')) { - synchnorousCookieStore?.removeCookieData(tabId); + dataStore?.removeCookieData(tabId); - if (synchnorousCookieStore.globalIsUsingCDP) { - synchnorousCookieStore.deinitialiseVariablesForTab(tabId.toString()); - synchnorousCookieStore.initialiseVariablesForNewTab(tabId.toString()); + if (dataStore.globalIsUsingCDP) { + dataStore.deinitialiseVariablesForTab(tabId.toString()); + dataStore.initialiseVariablesForNewTab(tabId.toString()); await attachCDP({ tabId }); @@ -76,11 +76,7 @@ export const onCommittedNavigationListener = async ({ } ); - synchnorousCookieStore.updateParentChildFrameAssociation( - tabId, - targetId, - '0' - ); + dataStore.updateParentChildFrameAssociation(tabId, targetId, '0'); } } await chrome.tabs.sendMessage(tabId, { diff --git a/packages/extension/src/serviceWorker/chromeListeners/onEnabledListener.ts b/packages/extension/src/serviceWorker/chromeListeners/onEnabledListener.ts index 5c3ce61a04..dd6b0be941 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/onEnabledListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/onEnabledListener.ts @@ -16,7 +16,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import { updateGlobalVariableAndAttachCDP, setupIntervals } from './utils'; export const onEnabledListener = async ( @@ -26,7 +26,7 @@ export const onEnabledListener = async ( return; } - synchnorousCookieStore?.clear(); + dataStore?.clear(); setupIntervals(); diff --git a/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts index de8c8d9f7e..af212a4845 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/onResponseStartedListener.ts @@ -17,8 +17,9 @@ * Internal dependencies */ import parseHeaders from '../../utils/parseHeaders'; -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import { getTab } from '../../utils/getTab'; +import cookieStore from '../../store/cookieStore'; export const onResponseStartedListener = ({ tabId, @@ -27,26 +28,26 @@ export const onResponseStartedListener = ({ frameId, requestId, }: chrome.webRequest.WebResponseCacheDetails) => { - if (synchnorousCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { return; } (async () => { const tab = await getTab(tabId); - let tabUrl = synchnorousCookieStore?.getTabUrl(tabId); + let tabUrl = dataStore?.getTabUrl(tabId); if (tab && tab.pendingUrl) { tabUrl = tab.pendingUrl; } const cookies = await parseHeaders( - synchnorousCookieStore.globalIsUsingCDP, + dataStore.globalIsUsingCDP, 'response', - synchnorousCookieStore.tabToRead, - synchnorousCookieStore.tabMode, + dataStore.tabToRead, + dataStore.tabMode, tabId, url, - synchnorousCookieStore.cookieDB ?? {}, + dataStore.cookieDB ?? {}, tabUrl, frameId.toString(), requestId, @@ -58,6 +59,6 @@ export const onResponseStartedListener = ({ } // Adds the cookies from the request headers to the cookies object. - synchnorousCookieStore?.update(tabId, cookies); + cookieStore?.update(tabId, cookies); })(); }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts index b92072b59d..828dfedb8c 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnInstalledListener.ts @@ -16,13 +16,13 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import { updateGlobalVariableAndAttachCDP, setupIntervals } from './utils'; export const runtimeOnInstalledListener = async ( details: chrome.runtime.InstalledDetails ) => { - synchnorousCookieStore?.clear(); + dataStore?.clear(); setupIntervals(); diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts index 085b254d38..450ab5b530 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeOnMessageListener.ts @@ -17,7 +17,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import { DEVTOOLS_CLOSE, DEVTOOLS_OPEN, @@ -34,6 +34,7 @@ import attachCDP from '../attachCDP'; import reloadCurrentTab from '../../utils/reloadCurrentTab'; import { getTab } from '../../utils/getTab'; import sendMessageWrapper from '../../utils/sendMessageWrapper'; +import cookieStore from '../../store/cookieStore'; // eslint-disable-next-line complexity export const runtimeOnMessageListener = async (request: any) => { @@ -44,14 +45,14 @@ export const runtimeOnMessageListener = async (request: any) => { const incomingMessageType = request.type; if (SET_TAB_TO_READ === incomingMessageType) { - synchnorousCookieStore.tabToRead = request?.payload?.tabId?.toString(); + dataStore.tabToRead = request?.payload?.tabId?.toString(); const newTab = await listenToNewTab(request?.payload?.tabId); // Can't use sendResponse as delay is too long. So using sendMessage instead. await sendMessageWrapper(SET_TAB_TO_READ, { tabId: Number(newTab), }); - if (synchnorousCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { await attachCDP({ tabId: Number(newTab) }); } @@ -63,13 +64,13 @@ export const runtimeOnMessageListener = async (request: any) => { const actionsPerformed: { [key: string]: boolean | number } = {}; if (sessionStorage?.allowedNumberOfTabs) { - synchnorousCookieStore.tabMode = sessionStorage.allowedNumberOfTabs; + dataStore.tabMode = sessionStorage.allowedNumberOfTabs; actionsPerformed.allowedNumberOfTabs = sessionStorage.allowedNumberOfTabs === 'unlimited' ? 0 : 1; } if (Object.keys(sessionStorage).includes('isUsingCDP')) { - synchnorousCookieStore.globalIsUsingCDP = sessionStorage.isUsingCDP; + dataStore.globalIsUsingCDP = sessionStorage.isUsingCDP; actionsPerformed.globalIsUsingCDP = true; } @@ -80,8 +81,8 @@ export const runtimeOnMessageListener = async (request: any) => { }); await chrome.storage.sync.set({ - allowedNumberOfTabs: synchnorousCookieStore.tabMode, - isUsingCDP: synchnorousCookieStore.globalIsUsingCDP, + allowedNumberOfTabs: dataStore.tabMode, + isUsingCDP: dataStore.globalIsUsingCDP, }); const tabs = await chrome.tabs.query({}); @@ -92,21 +93,21 @@ export const runtimeOnMessageListener = async (request: any) => { return; } - if (synchnorousCookieStore.tabMode === 'unlimited') { - synchnorousCookieStore.initialiseVariablesForNewTab(id.toString()); + if (dataStore.tabMode === 'unlimited') { + dataStore.initialiseVariablesForNewTab(id.toString()); const currentTab = targets.filter( ({ tabId }) => tabId && id && tabId === id ); - synchnorousCookieStore?.addTabData(id); - synchnorousCookieStore?.updateParentChildFrameAssociation( + dataStore?.addTabData(id); + dataStore?.updateParentChildFrameAssociation( id, currentTab[0].id, '0' ); } try { - if (synchnorousCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { await attachCDP({ tabId: id }); } else { await chrome.debugger.detach({ tabId: id }); @@ -131,71 +132,59 @@ export const runtimeOnMessageListener = async (request: any) => { if (DEVTOOLS_OPEN === incomingMessageType) { const dataToSend: { [key: string]: string | boolean } = {}; - dataToSend['tabMode'] = synchnorousCookieStore.tabMode; + dataToSend['tabMode'] = dataStore.tabMode; - if (synchnorousCookieStore.tabMode === 'single') { - dataToSend['tabToRead'] = synchnorousCookieStore.tabToRead; + if (dataStore.tabMode === 'single') { + dataToSend['tabToRead'] = dataStore.tabToRead; } if ( - !synchnorousCookieStore?.tabs[incomingMessageTabId] && - synchnorousCookieStore.tabMode === 'unlimited' + !dataStore?.tabs[incomingMessageTabId] && + dataStore.tabMode === 'unlimited' ) { const currentTab = await getTab(incomingMessageTabId); dataToSend['psatOpenedAfterPageLoad'] = request.payload.doNotReReload ? false : true; - synchnorousCookieStore?.addTabData(incomingMessageTabId); - synchnorousCookieStore?.updateUrl( - incomingMessageTabId, - currentTab?.url || '' - ); + dataStore?.addTabData(incomingMessageTabId); + dataStore?.updateUrl(incomingMessageTabId, currentTab?.url || ''); } await sendMessageWrapper(INITIAL_SYNC, dataToSend); - synchnorousCookieStore?.updateDevToolsState(incomingMessageTabId, true); + dataStore?.updateDevToolsState(incomingMessageTabId, true); - if (synchnorousCookieStore?.tabsData[incomingMessageTabId]) { - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools( - incomingMessageTabId, - true - ); + if (dataStore?.tabsData[incomingMessageTabId]) { + dataStore?.sendUpdatedDataToPopupAndDevTools(incomingMessageTabId, true); } } if (POPUP_OPEN === incomingMessageType) { const dataToSend: { [key: string]: string } = {}; - dataToSend['tabMode'] = synchnorousCookieStore.tabMode; + dataToSend['tabMode'] = dataStore.tabMode; - if (synchnorousCookieStore.tabMode === 'single') { - dataToSend['tabToRead'] = synchnorousCookieStore.tabToRead; + if (dataStore.tabMode === 'single') { + dataToSend['tabToRead'] = dataStore.tabToRead; } await sendMessageWrapper(INITIAL_SYNC, dataToSend); - synchnorousCookieStore?.updatePopUpState(incomingMessageTabId, true); + dataStore?.updatePopUpState(incomingMessageTabId, true); - if (synchnorousCookieStore?.tabsData[incomingMessageTabId]) { - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools( - incomingMessageTabId, - true - ); + if (dataStore?.tabsData[incomingMessageTabId]) { + dataStore?.sendUpdatedDataToPopupAndDevTools(incomingMessageTabId, true); } } if (DEVTOOLS_CLOSE === incomingMessageType) { - synchnorousCookieStore?.updateDevToolsState(incomingMessageTabId, false); + dataStore?.updateDevToolsState(incomingMessageTabId, false); } if (POPUP_CLOSE === incomingMessageType) { - synchnorousCookieStore?.updatePopUpState(incomingMessageTabId, false); + dataStore?.updatePopUpState(incomingMessageTabId, false); } if (DEVTOOLS_SET_JAVASCSCRIPT_COOKIE === incomingMessageType) { - synchnorousCookieStore?.update( - incomingMessageTabId, - request?.payload?.cookieData - ); + cookieStore?.update(incomingMessageTabId, request?.payload?.cookieData); } }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts index a67e3d9eb5..9fd6d473f3 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/runtimeStartUpListener.ts @@ -16,7 +16,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import { setupIntervals } from './utils'; export const onStartUpListener = async () => { @@ -24,10 +24,10 @@ export const onStartUpListener = async () => { setupIntervals(); if (storage?.allowedNumberOfTabs) { - synchnorousCookieStore.tabMode = storage.allowedNumberOfTabs; + dataStore.tabMode = storage.allowedNumberOfTabs; } if (Object.keys(storage).includes('isUsingCDP')) { - synchnorousCookieStore.globalIsUsingCDP = storage.isUsingCDP; + dataStore.globalIsUsingCDP = storage.isUsingCDP; } }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts index 19f9ac0dc8..0585a8256b 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/syncStorageOnChangedListener.ts @@ -17,7 +17,7 @@ * Internal dependencies */ import { INITIAL_SYNC } from '../../constants'; -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; import resetCookieBadgeText from '../../store/utils/resetCookieBadgeText'; import sendMessageWrapper from '../../utils/sendMessageWrapper'; @@ -31,16 +31,16 @@ export const onSyncStorageChangedListenerForMultiTab = async (changes: { ) { return; } - synchnorousCookieStore.tabMode = changes.allowedNumberOfTabs.newValue; + dataStore.tabMode = changes.allowedNumberOfTabs.newValue; const tabs = await chrome.tabs.query({}); await sendMessageWrapper(INITIAL_SYNC, { - tabMode: synchnorousCookieStore.tabMode, - tabToRead: synchnorousCookieStore.tabToRead, + tabMode: dataStore.tabMode, + tabToRead: dataStore.tabToRead, }); if (changes?.allowedNumberOfTabs?.newValue === 'single') { - synchnorousCookieStore.tabToRead = ''; + dataStore.tabToRead = ''; tabs.map((tab) => { if (!tab?.id) { @@ -49,7 +49,7 @@ export const onSyncStorageChangedListenerForMultiTab = async (changes: { resetCookieBadgeText(tab.id); - synchnorousCookieStore?.removeTabData(tab.id); + dataStore?.removeTabData(tab.id); return tab; }); @@ -58,9 +58,9 @@ export const onSyncStorageChangedListenerForMultiTab = async (changes: { if (!tab?.id) { return; } - synchnorousCookieStore?.addTabData(tab.id); - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(tab.id); - synchnorousCookieStore?.updateDevToolsState(tab.id, true); + dataStore?.addTabData(tab.id); + dataStore?.sendUpdatedDataToPopupAndDevTools(tab.id); + dataStore?.updateDevToolsState(tab.id, true); }); } }; @@ -76,7 +76,7 @@ export const onSyncStorageChangedListenerForCDP = async (changes: { return; } - synchnorousCookieStore.globalIsUsingCDP = changes?.isUsingCDP?.newValue; + dataStore.globalIsUsingCDP = changes?.isUsingCDP?.newValue; const tabs = await chrome.tabs.query({}); const debuggerTargets = await chrome.debugger.getTargets(); @@ -91,7 +91,7 @@ export const onSyncStorageChangedListenerForCDP = async (changes: { try { await chrome.debugger.detach({ targetId: id }); if (tabId) { - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(tabId); + dataStore?.sendUpdatedDataToPopupAndDevTools(tabId); } } catch (error) { // eslint-disable-next-line no-console @@ -105,7 +105,7 @@ export const onSyncStorageChangedListenerForCDP = async (changes: { return; } - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(id); + dataStore?.sendUpdatedDataToPopupAndDevTools(id); }); } }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts index 65e4bd8ffb..00fdc2eb08 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tabOnCreatedListener.ts @@ -17,7 +17,7 @@ * Internal dependencies */ import { ALLOWED_NUMBER_OF_TABS } from '../../constants'; -import syncCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; export const onTabCreatedListener = async (tab: chrome.tabs.Tab) => { try { @@ -27,40 +27,40 @@ export const onTabCreatedListener = async (tab: chrome.tabs.Tab) => { const targets = await chrome.debugger.getTargets(); - if (syncCookieStore.tabMode && syncCookieStore.tabMode !== 'unlimited') { - const doesTabExist = syncCookieStore.tabToRead; + if (dataStore.tabMode && dataStore.tabMode !== 'unlimited') { + const doesTabExist = dataStore.tabToRead; if ( - Object.keys(syncCookieStore?.tabsData ?? {}).length >= + Object.keys(dataStore?.tabsData ?? {}).length >= ALLOWED_NUMBER_OF_TABS && doesTabExist ) { return; } - syncCookieStore.tabToRead = tab.id.toString(); - syncCookieStore?.addTabData(tab.id); + dataStore.tabToRead = tab.id.toString(); + dataStore?.addTabData(tab.id); - if (syncCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { const currentTab = targets.filter( ({ tabId }) => tabId && tab.id && tabId === tab.id ); - syncCookieStore.initialiseVariablesForNewTab(tab.id.toString()); + dataStore.initialiseVariablesForNewTab(tab.id.toString()); - syncCookieStore.updateParentChildFrameAssociation( + dataStore.updateParentChildFrameAssociation( tab.id, currentTab[0].id, '0' ); } } else { - syncCookieStore?.addTabData(tab.id); + dataStore?.addTabData(tab.id); - if (syncCookieStore.globalIsUsingCDP) { + if (dataStore.globalIsUsingCDP) { const currentTab = targets.filter( ({ tabId }) => tabId && tab.id && tabId === tab.id ); - syncCookieStore.initialiseVariablesForNewTab(tab.id.toString()); + dataStore.initialiseVariablesForNewTab(tab.id.toString()); - syncCookieStore.updateParentChildFrameAssociation( + dataStore.updateParentChildFrameAssociation( tab.id, currentTab[0].id, '0' diff --git a/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts index 5435fa095b..08eefdb0a8 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tabOnRemovedListener.ts @@ -16,10 +16,10 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; export const onTabRemovedListener = (tabId: number) => { - synchnorousCookieStore.deinitialiseVariablesForTab(tabId.toString()); + dataStore.deinitialiseVariablesForTab(tabId.toString()); - synchnorousCookieStore?.removeTabData(tabId); + dataStore?.removeTabData(tabId); }; diff --git a/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts b/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts index 74d41c780f..b0d87f6635 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/utils/setupIntervals.ts @@ -17,7 +17,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; +import dataStore from '../../../store/dataStore'; import { getAndParseNetworkCookies } from '../../../utils/getAndParseNetworkCookies'; const setupIntervals = () => { @@ -29,22 +29,22 @@ const setupIntervals = () => { // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. setInterval(() => { - if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + if (Object.keys(dataStore?.tabsData ?? {}).length === 0) { return; } - Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { - synchnorousCookieStore?.sendUpdatedDataToPopupAndDevTools(Number(key)); + Object.keys(dataStore?.tabsData ?? {}).forEach((key) => { + dataStore?.sendUpdatedDataToPopupAndDevTools(Number(key)); }); }, 1200); // @todo Send tab data of the active tab only, also if sending only the difference would make it any faster. setInterval(() => { - if (Object.keys(synchnorousCookieStore?.tabsData ?? {}).length === 0) { + if (Object.keys(dataStore?.tabsData ?? {}).length === 0) { return; } - Object.keys(synchnorousCookieStore?.tabsData ?? {}).forEach((key) => { + Object.keys(dataStore?.tabsData ?? {}).forEach((key) => { getAndParseNetworkCookies(key); }); }, 5000); diff --git a/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts b/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts index 72cc2f0ca3..7d9cf3eaa2 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts @@ -16,7 +16,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; +import dataStore from '../../../store/dataStore'; import attachCDP from '../../attachCDP'; const updateGlobalVariableAndAttachCDP = async () => { @@ -24,11 +24,10 @@ const updateGlobalVariableAndAttachCDP = async () => { const preSetSettings = await chrome.storage.sync.get(); - synchnorousCookieStore.tabMode = - preSetSettings?.allowedNumberOfTabs ?? 'single'; - synchnorousCookieStore.globalIsUsingCDP = preSetSettings?.isUsingCDP ?? false; + dataStore.tabMode = preSetSettings?.allowedNumberOfTabs ?? 'single'; + dataStore.globalIsUsingCDP = preSetSettings?.isUsingCDP ?? false; - if (synchnorousCookieStore.tabMode === 'unlimited') { + if (dataStore.tabMode === 'unlimited') { const allTabs = await chrome.tabs.query({}); const targets = await chrome.debugger.getTargets(); @@ -38,19 +37,17 @@ const updateGlobalVariableAndAttachCDP = async () => { return; } - synchnorousCookieStore?.addTabData(tab.id); + dataStore?.addTabData(tab.id); - if (synchnorousCookieStore.globalIsUsingCDP) { - synchnorousCookieStore.initialiseVariablesForNewTab( - tab.id.toString() - ); + if (dataStore.globalIsUsingCDP) { + dataStore.initialiseVariablesForNewTab(tab.id.toString()); await attachCDP({ tabId: tab.id }); const currentTab = targets.filter( ({ tabId }) => tabId && tab.id && tabId === tab.id ); - synchnorousCookieStore?.updateParentChildFrameAssociation( + dataStore?.updateParentChildFrameAssociation( tab.id, currentTab[0].id, '0' diff --git a/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts index 2e4d2fda2b..73d810e065 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/windowsOnRemovedListener.ts @@ -16,17 +16,17 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../../store/synchnorousCookieStore'; +import dataStore from '../../store/dataStore'; export const windowsOnRemovedListener = (windowId: number) => { chrome.tabs.query({ windowId }, (tabs) => { tabs.map((tab) => { if (tab.id) { - synchnorousCookieStore.deinitialiseVariablesForTab(tab.id.toString()); + dataStore.deinitialiseVariablesForTab(tab.id.toString()); } return tab; }); }); - synchnorousCookieStore?.removeWindowData(windowId); + dataStore?.removeWindowData(windowId); }; diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 0d02d5d539..fa97fd002d 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -21,12 +21,14 @@ import { Protocol } from 'devtools-protocol'; /** * Internal dependencies. */ -import syncCookieStore from '../store/synchnorousCookieStore'; import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; import './chromeListeners'; import networkTime from '../store/utils/networkTime'; import formatTime from '../store/utils/formatTime'; +import dataStore from '../store/dataStore'; +import cookieStore from '../store/cookieStore'; +import PAStore from '../store/PAStore'; const ALLOWED_EVENTS = [ 'Network.responseReceived', @@ -127,7 +129,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { (target) => target?.tabId && target.tabId === source.tabId )[0]?.id; - syncCookieStore?.addFrameToTabAndUpdateMetadata( + dataStore?.addFrameToTabAndUpdateMetadata( source.tabId ?? null, source.targetId ?? null, targetId, @@ -142,10 +144,10 @@ chrome.debugger.onEvent.addListener((source, method, params) => { if (source?.tabId) { tabId = source?.tabId?.toString(); } else if (source.targetId) { - const tab = Object.keys(syncCookieStore?.tabs ?? {}).filter( + const tab = Object.keys(dataStore?.tabs ?? {}).filter( (key) => source.targetId && - syncCookieStore?.getFrameIDSet(Number(key))?.has(source.targetId) + dataStore?.getFrameIDSet(Number(key))?.has(source.targetId) ); tabId = tab[0]; } @@ -154,7 +156,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const { frameId, parentFrameId } = params as Protocol.Page.FrameAttachedEvent; - await syncCookieStore?.addFrameToTabAndUpdateMetadata( + await dataStore?.addFrameToTabAndUpdateMetadata( source.tabId ?? null, source.targetId ?? null, frameId, @@ -173,7 +175,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return; } - await syncCookieStore?.addFrameToTabAndUpdateMetadata( + await dataStore?.addFrameToTabAndUpdateMetadata( source.tabId ?? null, source.targetId ?? null, id, @@ -182,10 +184,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ); } - if ( - syncCookieStore.tabMode !== 'unlimited' && - syncCookieStore.tabToRead !== tabId - ) { + if (dataStore.tabMode !== 'unlimited' && dataStore.tabToRead !== tabId) { return; } @@ -195,9 +194,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const { uniqueAuctionId } = interestGroupAuctionEventOccured; if (interestGroupAuctionEventOccured.type === 'started') { - syncCookieStore.auctionDataForTabId[parseInt(tabId)][ - uniqueAuctionId - ] = { + dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { auctionTime: interestGroupAuctionEventOccured.eventTime, auctionConfig: interestGroupAuctionEventOccured.auctionConfig, }; @@ -207,26 +204,22 @@ chrome.debugger.onEvent.addListener((source, method, params) => { interestGroupAuctionEventOccured.type === 'configResolved' || interestGroupAuctionEventOccured.type === 'started' ) { - syncCookieStore.auctionDataForTabId[parseInt(tabId)][ - uniqueAuctionId - ] = { - ...syncCookieStore.auctionDataForTabId[parseInt(tabId)][ - uniqueAuctionId - ], + dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { + ...dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId], auctionConfig: interestGroupAuctionEventOccured.auctionConfig, }; - syncCookieStore.auctionEvents[parseInt(tabId)].push({ + dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', bid: 0, name: '', ownerOrigin: '', type: interestGroupAuctionEventOccured.type, formattedTime: - syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + dataStore.auctionEvents[parseInt(tabId)].length === 0 ? '0 ms' : formatTime( - syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + dataStore.auctionEvents[parseInt(tabId)][0].time, interestGroupAuctionEventOccured.eventTime ), time: interestGroupAuctionEventOccured.eventTime, @@ -241,16 +234,16 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const interestGroupAccessedParams = params as Protocol.Storage.InterestGroupAccessedEvent; - syncCookieStore.auctionEvents[parseInt(tabId)].push({ + dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: interestGroupAccessedParams?.bidCurrency ?? '', bid: interestGroupAccessedParams?.bid ?? 0, name: interestGroupAccessedParams.name, ownerOrigin: interestGroupAccessedParams.ownerOrigin, formattedTime: - syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + dataStore.auctionEvents[parseInt(tabId)].length === 0 ? '0 ms' : formatTime( - syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + dataStore.auctionEvents[parseInt(tabId)][0].time, interestGroupAccessedParams.accessTime ), type: interestGroupAccessedParams.type, @@ -271,30 +264,29 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const requestId = interestGroupAuctionNetworkRequestCreatedParams.requestId; - syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId] = { + dataStore.unParsedRequestHeadersForPA[tabId][requestId] = { auctions: interestGroupAuctionNetworkRequestCreatedParams.auctions, type: interestGroupAuctionNetworkRequestCreatedParams.type, }; - if (syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]) { + if (dataStore.requestIdToCDPURLMapping[tabId][requestId]) { const time: number = networkTime( requestId, - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] - .timeStamp, + dataStore.requestIdToCDPURLMapping[tabId][requestId].timeStamp, tabId ) ?? new Date().getTime(); - syncCookieStore.auctionEvents[parseInt(tabId)].push({ + dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', bid: 0, name: '', ownerOrigin: '', formattedTime: - syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + dataStore.auctionEvents[parseInt(tabId)].length === 0 ? '0 ms' : formatTime( - syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + dataStore.auctionEvents[parseInt(tabId)][0].time, time ), type: @@ -330,15 +322,15 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return id; }); - finalFrameId = syncCookieStore.addFrameIdAndRequestUrlToResourceMap( + finalFrameId = dataStore.addFrameIdAndRequestUrlToResourceMap( tabId, frameId, setTargets, requestUrl ); - if (!syncCookieStore.requestIdToCDPURLMapping[tabId]) { - syncCookieStore.requestIdToCDPURLMapping[tabId] = { + if (!dataStore.requestIdToCDPURLMapping[tabId]) { + dataStore.requestIdToCDPURLMapping[tabId] = { [requestId]: { finalFrameId, frameId, @@ -348,8 +340,8 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }, }; } else { - syncCookieStore.requestIdToCDPURLMapping[tabId] = { - ...syncCookieStore.requestIdToCDPURLMapping[tabId], + dataStore.requestIdToCDPURLMapping[tabId] = { + ...dataStore.requestIdToCDPURLMapping[tabId], [requestId]: { finalFrameId, frameId, @@ -360,21 +352,20 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }; } - if (syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId]) { - syncCookieStore.auctionEvents[parseInt(tabId)].push({ + if (dataStore.unParsedRequestHeadersForPA[tabId][requestId]) { + dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', bid: 0, name: '', ownerOrigin: '', type: 'Start fetch ' + - syncCookieStore.unParsedRequestHeadersForPA[tabId][requestId] - .type, + dataStore.unParsedRequestHeadersForPA[tabId][requestId].type, formattedTime: - syncCookieStore.auctionEvents[parseInt(tabId)].length === 0 + dataStore.auctionEvents[parseInt(tabId)].length === 0 ? '0 ms' : formatTime( - syncCookieStore.auctionEvents[parseInt(tabId)][0].time, + dataStore.auctionEvents[parseInt(tabId)][0].time, networkTime(requestId, timestamp, tabId) ), time: networkTime(requestId, timestamp, tabId), @@ -384,11 +375,11 @@ chrome.debugger.onEvent.addListener((source, method, params) => { } if ( - syncCookieStore.tabs[Number(tabId)]?.isCookieAnalysisEnabled && - syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId] + dataStore.tabs[Number(tabId)]?.isCookieAnalysisEnabled && + dataStore.unParsedRequestHeadersForCA[tabId][requestId] ) { - syncCookieStore.parseRequestHeadersForCA( - syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], + cookieStore.parseRequestHeadersForCA( + dataStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) @@ -401,7 +392,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const loadingFinishedParams = params as Protocol.Network.LoadingFinishedEvent; - syncCookieStore.parseRequestHeadersForPA( + PAStore.parseRequestHeadersForPA( loadingFinishedParams.requestId, loadingFinishedParams.timestamp, tabId, @@ -413,7 +404,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const loadingFailedParams = params as Protocol.Network.LoadingFinishedEvent; - syncCookieStore.parseRequestHeadersForPA( + PAStore.parseRequestHeadersForPA( loadingFailedParams.requestId, loadingFailedParams.timestamp, tabId, @@ -425,22 +416,21 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const { requestId } = params as Protocol.Network.RequestWillBeSentExtraInfoEvent; - if (syncCookieStore.requestIdToCDPURLMapping[tabId]?.[requestId]) { - syncCookieStore.parseRequestHeadersForCA( + if (dataStore.requestIdToCDPURLMapping[tabId]?.[requestId]) { + cookieStore.parseRequestHeadersForCA( params as Protocol.Network.RequestWillBeSentExtraInfoEvent, requestId, tabId, Array.from( new Set([ - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + dataStore.requestIdToCDPURLMapping[tabId][requestId] ?.finalFrameId, - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] - ?.frameId, + dataStore.requestIdToCDPURLMapping[tabId][requestId]?.frameId, ]) ) ); } else { - syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId] = + dataStore.unParsedRequestHeadersForCA[tabId][requestId] = params as Protocol.Network.RequestWillBeSentExtraInfoEvent; } return; @@ -470,29 +460,27 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return id; }); - finalFrameId = syncCookieStore.addFrameIdAndRequestUrlToResourceMap( + finalFrameId = dataStore.addFrameIdAndRequestUrlToResourceMap( tabId, frameId, setTargets, requestUrl ); - if (!syncCookieStore.requestIdToCDPURLMapping[tabId]) { - syncCookieStore.requestIdToCDPURLMapping[tabId] = { + if (!dataStore.requestIdToCDPURLMapping[tabId]) { + dataStore.requestIdToCDPURLMapping[tabId] = { [requestId]: { - ...(syncCookieStore.requestIdToCDPURLMapping[tabId]?.[ - requestId - ] ?? {}), + ...(dataStore.requestIdToCDPURLMapping[tabId]?.[requestId] ?? {}), finalFrameId, frameId, url: requestUrl, }, }; } else { - syncCookieStore.requestIdToCDPURLMapping[tabId] = { - ...syncCookieStore.requestIdToCDPURLMapping[tabId], + dataStore.requestIdToCDPURLMapping[tabId] = { + ...dataStore.requestIdToCDPURLMapping[tabId], [requestId]: { - ...syncCookieStore.requestIdToCDPURLMapping[tabId][requestId], + ...dataStore.requestIdToCDPURLMapping[tabId][requestId], finalFrameId, frameId, url: requestUrl, @@ -500,18 +488,18 @@ chrome.debugger.onEvent.addListener((source, method, params) => { }; } - if (syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId]) { - syncCookieStore.parseResponseHeadersForCA( - syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId], + if (dataStore.unParsedResponseHeadersForCA[tabId][requestId]) { + cookieStore.parseResponseHeadersForCA( + dataStore.unParsedResponseHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) ); } - if (syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId]) { - syncCookieStore.parseRequestHeadersForCA( - syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], + if (dataStore.unParsedRequestHeadersForCA[tabId][requestId]) { + cookieStore.parseRequestHeadersForCA( + dataStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, Array.from(new Set([finalFrameId, frameId])) @@ -530,33 +518,32 @@ chrome.debugger.onEvent.addListener((source, method, params) => { return; } - if (syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]) { + if (dataStore.requestIdToCDPURLMapping[tabId][requestId]) { const frameIds = Array.from( new Set([ - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] + dataStore.requestIdToCDPURLMapping[tabId][requestId] ?.finalFrameId, - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId] - ?.frameId, + dataStore.requestIdToCDPURLMapping[tabId][requestId]?.frameId, ]) ); - syncCookieStore.parseResponseHeadersForCA( + cookieStore.parseResponseHeadersForCA( params as Protocol.Network.ResponseReceivedExtraInfoEvent, requestId, tabId, frameIds ); - if (syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId]) { - syncCookieStore.parseRequestHeadersForCA( - syncCookieStore.unParsedRequestHeadersForCA[tabId][requestId], + if (dataStore.unParsedRequestHeadersForCA[tabId][requestId]) { + cookieStore.parseRequestHeadersForCA( + dataStore.unParsedRequestHeadersForCA[tabId][requestId], requestId, tabId, frameIds ); } } else { - syncCookieStore.unParsedResponseHeadersForCA[tabId][requestId] = + dataStore.unParsedResponseHeadersForCA[tabId][requestId] = params as Protocol.Network.ResponseReceivedExtraInfoEvent; } return; @@ -591,14 +578,14 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const cookieObjectToUpdate = createCookieFromAuditsIssue( cookieIssueDetails, - syncCookieStore?.getTabUrl(Number(tabId)) ?? '', + dataStore?.getTabUrl(Number(tabId)) ?? '', [], - syncCookieStore.requestIdToCDPURLMapping[tabId][requestId]?.url, - syncCookieStore.cookieDB ?? {} + dataStore.requestIdToCDPURLMapping[tabId][requestId]?.url, + dataStore.cookieDB ?? {} ); if (cookieObjectToUpdate) { - syncCookieStore?.update(Number(tabId), [cookieObjectToUpdate]); + cookieStore?.update(Number(tabId), [cookieObjectToUpdate]); } return; } diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts new file mode 100644 index 0000000000..324a8c410c --- /dev/null +++ b/packages/extension/src/store/PAStore.ts @@ -0,0 +1,78 @@ +/* + * 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 { Protocol } from 'devtools-protocol'; + +/** + * Internal dependencies. + */ +import networkTime from './utils/networkTime'; +import formatTime from './utils/formatTime'; +import dataStore from './dataStore'; + +class PAStore { + /** + * This function parses response headers for Protected Analysis PA. + * @param {string} requestId This is used to get the related data for parsing the request. + * @param { Protocol.Network.MonotonicTime } timestamp Timestamp of the request + * @param {string} tabId The tabId this request is associated to. + * @param {string} method determines which event called the function. + */ + parseRequestHeadersForPA( + requestId: string, + timestamp: Protocol.Network.MonotonicTime, + tabId: string, + method: string + ) { + if (!dataStore.unParsedRequestHeadersForPA[tabId][requestId]?.auctions) { + return; + } + const { auctions, type } = + dataStore.unParsedRequestHeadersForPA[tabId][requestId]; + + const calculatedNetworkTime = networkTime(requestId, timestamp, tabId); + + auctions.forEach((uniqueAuctionId) => { + if (!dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]) { + return; + } + const { auctionConfig = {} } = + dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]; + + dataStore.auctionEvents[parseInt(tabId)].push({ + bidCurrency: auctionConfig?.bidCurrency ?? '', + bid: auctionConfig?.bid ?? '', + name: auctionConfig?.name ?? '', + ownerOrigin: auctionConfig?.ownerOrigin ?? '', + type: method + type, + formattedTime: + dataStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[parseInt(tabId)][0].time, + networkTime(requestId, timestamp, tabId) + ), + time: calculatedNetworkTime, + auctionConfig, + eventType: 'interestGroupAuctionNetworkRequestCompleted', + }); + }); + } +} + +export default new PAStore(); diff --git a/packages/extension/src/store/cookieStore.ts b/packages/extension/src/store/cookieStore.ts new file mode 100644 index 0000000000..bcc8d339d2 --- /dev/null +++ b/packages/extension/src/store/cookieStore.ts @@ -0,0 +1,268 @@ +/* + * 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 { + getCookieKey, + type CookieData, + type BlockedReason, + parseResponseReceivedExtraInfo, + parseRequestWillBeSentExtraInfo, + deriveBlockingStatus, +} from '@google-psat/common'; +import type { Protocol } from 'devtools-protocol'; + +/** + * Internal dependencies. + */ +import updateCookieBadgeText from './utils/updateCookieBadgeText'; +import dataStore from './dataStore'; + +class CookieStore { + /** + * This function parses response headers for Cookie Analysis. + * @param {Protocol.Network.ResponseReceivedExtraInfoEvent} response The response to be parsed. + * @param {string} requestId This is used to get the related data for parsing the response. + * @param {string} tabId The tabId this request is associated to. + * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. + */ + parseResponseHeadersForCA( + response: Protocol.Network.ResponseReceivedExtraInfoEvent, + requestId: string, + tabId: string, + frameIds: string[] + ) { + const { headers, blockedCookies, cookiePartitionKey, exemptedCookies } = + response; + + const cookies: CookieData[] = parseResponseReceivedExtraInfo( + headers, + blockedCookies, + exemptedCookies, + cookiePartitionKey, + dataStore.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', + dataStore.tabs[Number(tabId)].url ?? '', + dataStore.cookieDB ?? {}, + frameIds, + requestId + ); + this.update(Number(tabId), cookies); + + delete dataStore.unParsedResponseHeadersForCA[tabId][requestId]; + } + + /** + * This function parses request headers for Cookie Analysis. + * @param {Protocol.Network.RequestWillBeSentExtraInfoEvent} request The response to be parsed. + * @param {string} requestId This is used to get the related data for parsing the response. + * @param {string} tabId The tabId this request is associated to. + * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. + */ + parseRequestHeadersForCA( + request: Protocol.Network.RequestWillBeSentExtraInfoEvent, + requestId: string, + tabId: string, + frameIds: string[] + ) { + const { associatedCookies } = request; + + const cookies: CookieData[] = parseRequestWillBeSentExtraInfo( + associatedCookies, + dataStore.cookieDB ?? {}, + dataStore.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', + dataStore.tabs[Number(tabId)].url ?? '', + frameIds, + requestId + ); + + delete dataStore.unParsedRequestHeadersForCA[tabId][requestId]; + if (cookies.length === 0) { + return; + } + + this.update(Number(tabId), cookies); + delete dataStore.unParsedRequestHeadersForCA[tabId][requestId]; + } + + /** + * Adds exclusion and warning reasons for a given cookie. + * @param {string} cookieName Name of the cookie. + * @param {string[]} exclusionReasons reasons to be added to the blocked reason array. + * @param {string[]} warningReasons warning reasons to be added to the warning reason array. + * @param {number} tabId tabId where change has to be made. + */ + addCookieExclusionWarningReason( + cookieName: string, + exclusionReasons: BlockedReason[], + warningReasons: Protocol.Audits.CookieWarningReason[], + tabId: number + ) { + if (!dataStore.tabsData[tabId]) { + return; + } + if (dataStore.tabsData[tabId] && dataStore.tabsData[tabId][cookieName]) { + dataStore.tabsData[tabId][cookieName].blockedReasons = [ + ...new Set([ + ...(dataStore.tabsData[tabId][cookieName].blockedReasons ?? []), + ...exclusionReasons, + ]), + ]; + dataStore.tabsData[tabId][cookieName].warningReasons = [ + ...new Set([ + ...(dataStore.tabsData[tabId][cookieName].warningReasons ?? []), + ...warningReasons, + ]), + ]; + + dataStore.tabsData[tabId][cookieName].isBlocked = + exclusionReasons.length > 0 ? true : false; + dataStore.tabs[tabId].newUpdates++; + } else { + dataStore.tabs[tabId].newUpdates++; + // If none of them exists. This case is possible when the cookies hasnt processed and we already have an issue. + dataStore.tabsData[tabId] = { + ...dataStore.tabsData[tabId], + [cookieName]: { + ...(dataStore.tabsData[tabId][cookieName] ?? {}), + blockedReasons: [...exclusionReasons], + warningReasons: [...warningReasons], + isBlocked: exclusionReasons.length > 0 ? true : false, + }, + }; + } + } + + /** + * Update cookie store. + * @param {number} tabId Tab id. + * @param {Array} cookies Cookies data. + */ + // eslint-disable-next-line complexity + update(tabId: number, cookies: CookieData[]) { + try { + if (!dataStore.tabsData[tabId] || !dataStore.tabs[tabId]) { + return; + } + + for (const cookie of cookies) { + const cookieKey = getCookieKey(cookie.parsedCookie); + if (!cookieKey) { + continue; + } + + // Merge in previous blocked reasons. + const blockedReasons: BlockedReason[] = [ + ...new Set([ + ...(cookie?.blockedReasons ?? []), + ...(dataStore.tabsData[tabId]?.[cookieKey]?.blockedReasons ?? []), + ]), + ]; + + const warningReasons = Array.from( + new Set([ + ...(cookie?.warningReasons ?? []), + ...(dataStore.tabsData[tabId]?.[cookieKey]?.warningReasons ?? []), + ]) + ); + + const frameIdList = Array.from( + new Set([ + ...((cookie?.frameIdList ?? []) as number[]), + ...((dataStore.tabsData[tabId]?.[cookieKey]?.frameIdList ?? + []) as number[]), + ]) + ).map((frameId) => frameId.toString()); + + if (dataStore.tabsData[tabId]?.[cookieKey]) { + dataStore.tabs[tabId].newUpdates++; + // Merge in previous warning reasons. + const parsedCookie = { + ...dataStore.tabsData[tabId][cookieKey].parsedCookie, + ...cookie.parsedCookie, + samesite: ( + cookie.parsedCookie.samesite ?? + dataStore.tabsData[tabId][cookieKey].parsedCookie.samesite ?? + 'lax' + ).toLowerCase(), + httponly: + cookie.parsedCookie.httponly ?? + dataStore.tabsData[tabId][cookieKey].parsedCookie.httponly, + priority: + cookie.parsedCookie?.priority ?? + dataStore.tabsData[tabId][cookieKey].parsedCookie?.priority ?? + 'Medium', + partitionKey: '', + }; + if ( + cookie.parsedCookie?.partitionKey || + dataStore.tabsData[tabId][cookieKey].parsedCookie?.partitionKey + ) { + parsedCookie.partitionKey = + cookie.parsedCookie?.partitionKey || + dataStore.tabsData[tabId][cookieKey].parsedCookie?.partitionKey; + } + + const networkEvents: CookieData['networkEvents'] = { + requestEvents: [ + ...(dataStore.tabsData[tabId][cookieKey]?.networkEvents + ?.requestEvents || []), + ...(cookie.networkEvents?.requestEvents || []), + ], + responseEvents: [ + ...(dataStore.tabsData[tabId][cookieKey]?.networkEvents + ?.responseEvents || []), + ...(cookie.networkEvents?.responseEvents || []), + ], + }; + dataStore.tabsData[tabId][cookieKey] = { + ...dataStore.tabsData[tabId][cookieKey], + ...cookie, + parsedCookie, + isBlocked: blockedReasons.length > 0, + blockedReasons, + networkEvents, + blockingStatus: deriveBlockingStatus(networkEvents), + warningReasons, + url: dataStore.tabsData[tabId][cookieKey].url ?? cookie.url, + headerType: + dataStore.tabsData[tabId][cookieKey].headerType === 'javascript' + ? dataStore.tabsData[tabId][cookieKey].headerType + : cookie.headerType, + frameIdList, + exemptionReason: + cookie?.exemptionReason || + dataStore.tabsData[tabId][cookieKey]?.exemptionReason, + }; + } else { + dataStore.tabs[tabId].newUpdates++; + dataStore.tabsData[tabId][cookieKey] = { + ...cookie, + blockingStatus: deriveBlockingStatus(cookie.networkEvents), + }; + } + } + + updateCookieBadgeText(dataStore.tabsData[tabId], tabId); + } catch (error) { + //Fail silently + // eslint-disable-next-line no-console + console.warn(error); + } + } +} + +export default new CookieStore(); diff --git a/packages/extension/src/store/synchnorousCookieStore.ts b/packages/extension/src/store/dataStore.ts similarity index 63% rename from packages/extension/src/store/synchnorousCookieStore.ts rename to packages/extension/src/store/dataStore.ts index 2384d24445..8b1ec9304e 100644 --- a/packages/extension/src/store/synchnorousCookieStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -1,3 +1,18 @@ +/* + * Copyright 2024 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. + */ /* * Copyright 2023 Google LLC * @@ -16,27 +31,17 @@ /** * External dependencies. */ -import { - getCookieKey, - type CookieData, - type BlockedReason, - parseResponseReceivedExtraInfo, - type CookieDatabase, - parseRequestWillBeSentExtraInfo, - deriveBlockingStatus, -} from '@google-psat/common'; +import { type CookieData, type CookieDatabase } from '@google-psat/common'; import type { Protocol } from 'devtools-protocol'; /** * Internal dependencies. */ -import updateCookieBadgeText from './utils/updateCookieBadgeText'; import { NEW_COOKIE_DATA } from '../constants'; import isValidURL from '../utils/isValidURL'; import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; -import networkTime from './utils/networkTime'; -import formatTime from './utils/formatTime'; + interface singleAuctionEvent { bidCurrency: string; bid: number; @@ -53,7 +58,8 @@ interface singleAuctionEvent { | 'interestGroupAuctionNetworkRequestCreated' | 'interestGroupAccessed'; } -class SynchnorousCookieStore { + +class DataStore { /** * The cookie data of the tabs. */ @@ -224,119 +230,6 @@ class SynchnorousCookieStore { } } - /** - * This function parses response headers for Protected Analysis PA. - * @param {string} requestId This is used to get the related data for parsing the request. - * @param { Protocol.Network.MonotonicTime } timestamp Timestamp of the request - * @param {string} tabId The tabId this request is associated to. - * @param {string} method determines which event called the function. - */ - parseRequestHeadersForPA( - requestId: string, - timestamp: Protocol.Network.MonotonicTime, - tabId: string, - method: string - ) { - if (!this.unParsedRequestHeadersForPA[tabId][requestId]?.auctions) { - return; - } - const { auctions, type } = - this.unParsedRequestHeadersForPA[tabId][requestId]; - - const calculatedNetworkTime = networkTime(requestId, timestamp, tabId); - - auctions.forEach((uniqueAuctionId) => { - if (!this.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]) { - return; - } - const { auctionConfig = {} } = - this.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]; - - this.auctionEvents[parseInt(tabId)].push({ - bidCurrency: auctionConfig?.bidCurrency ?? '', - bid: auctionConfig?.bid ?? '', - name: auctionConfig?.name ?? '', - ownerOrigin: auctionConfig?.ownerOrigin ?? '', - type: method + type, - formattedTime: - this.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - this.auctionEvents[parseInt(tabId)][0].time, - networkTime(requestId, timestamp, tabId) - ), - time: calculatedNetworkTime, - auctionConfig, - eventType: 'interestGroupAuctionNetworkRequestCompleted', - }); - }); - } - - /** - * This function parses response headers for Cookie Analysis. - * @param {Protocol.Network.ResponseReceivedExtraInfoEvent} response The response to be parsed. - * @param {string} requestId This is used to get the related data for parsing the response. - * @param {string} tabId The tabId this request is associated to. - * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. - */ - parseResponseHeadersForCA( - response: Protocol.Network.ResponseReceivedExtraInfoEvent, - requestId: string, - tabId: string, - frameIds: string[] - ) { - const { headers, blockedCookies, cookiePartitionKey, exemptedCookies } = - response; - - const cookies: CookieData[] = parseResponseReceivedExtraInfo( - headers, - blockedCookies, - exemptedCookies, - cookiePartitionKey, - this.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', - this.tabs[Number(tabId)].url ?? '', - this.cookieDB ?? {}, - frameIds, - requestId - ); - this.update(Number(tabId), cookies); - - delete this.unParsedResponseHeadersForCA[tabId][requestId]; - } - - /** - * This function parses request headers for Cookie Analysis. - * @param {Protocol.Network.RequestWillBeSentExtraInfoEvent} request The response to be parsed. - * @param {string} requestId This is used to get the related data for parsing the response. - * @param {string} tabId The tabId this request is associated to. - * @param {string[]} frameIds This is used to associate the cookies from request to set of frameIds. - */ - parseRequestHeadersForCA( - request: Protocol.Network.RequestWillBeSentExtraInfoEvent, - requestId: string, - tabId: string, - frameIds: string[] - ) { - const { associatedCookies } = request; - - const cookies: CookieData[] = parseRequestWillBeSentExtraInfo( - associatedCookies, - this.cookieDB ?? {}, - this.requestIdToCDPURLMapping[tabId][requestId]?.url ?? '', - this.tabs[Number(tabId)].url ?? '', - frameIds, - requestId - ); - - delete this.unParsedRequestHeadersForCA[tabId][requestId]; - if (cookies.length === 0) { - return; - } - - this.update(Number(tabId), cookies); - delete this.unParsedRequestHeadersForCA[tabId][requestId]; - } - /** * This function adds frame to the appropriate tab. * @param {number} tabId The tabId of the event if available. @@ -401,54 +294,6 @@ class SynchnorousCookieStore { ); } - /** - * Adds exclusion and warning reasons for a given cookie. - * @param {string} cookieName Name of the cookie. - * @param {string[]} exclusionReasons reasons to be added to the blocked reason array. - * @param {string[]} warningReasons warning reasons to be added to the warning reason array. - * @param {number} tabId tabId where change has to be made. - */ - addCookieExclusionWarningReason( - cookieName: string, - exclusionReasons: BlockedReason[], - warningReasons: Protocol.Audits.CookieWarningReason[], - tabId: number - ) { - if (!this.tabsData[tabId]) { - return; - } - if (this.tabsData[tabId] && this.tabsData[tabId][cookieName]) { - this.tabsData[tabId][cookieName].blockedReasons = [ - ...new Set([ - ...(this.tabsData[tabId][cookieName].blockedReasons ?? []), - ...exclusionReasons, - ]), - ]; - this.tabsData[tabId][cookieName].warningReasons = [ - ...new Set([ - ...(this.tabsData[tabId][cookieName].warningReasons ?? []), - ...warningReasons, - ]), - ]; - - this.tabsData[tabId][cookieName].isBlocked = - exclusionReasons.length > 0 ? true : false; - this.tabs[tabId].newUpdates++; - } else { - this.tabs[tabId].newUpdates++; - // If none of them exists. This case is possible when the cookies hasnt processed and we already have an issue. - this.tabsData[tabId] = { - ...this.tabsData[tabId], - [cookieName]: { - ...(this.tabsData[tabId][cookieName] ?? {}), - blockedReasons: [...exclusionReasons], - warningReasons: [...warningReasons], - isBlocked: exclusionReasons.length > 0 ? true : false, - }, - }; - } - } - /** * Creates an entry for a tab * @param {number} tabId The tab id. @@ -698,124 +543,6 @@ class SynchnorousCookieStore { } } - /** - * Update cookie store. - * @param {number} tabId Tab id. - * @param {Array} cookies Cookies data. - */ - // eslint-disable-next-line complexity - update(tabId: number, cookies: CookieData[]) { - try { - if (!this.tabsData[tabId] || !this.tabs[tabId]) { - return; - } - - for (const cookie of cookies) { - const cookieKey = getCookieKey(cookie.parsedCookie); - if (!cookieKey) { - continue; - } - - // Merge in previous blocked reasons. - const blockedReasons: BlockedReason[] = [ - ...new Set([ - ...(cookie?.blockedReasons ?? []), - ...(this.tabsData[tabId]?.[cookieKey]?.blockedReasons ?? []), - ]), - ]; - - const warningReasons = Array.from( - new Set([ - ...(cookie?.warningReasons ?? []), - ...(this.tabsData[tabId]?.[cookieKey]?.warningReasons ?? []), - ]) - ); - - const frameIdList = Array.from( - new Set([ - ...((cookie?.frameIdList ?? []) as number[]), - ...((this.tabsData[tabId]?.[cookieKey]?.frameIdList ?? - []) as number[]), - ]) - ).map((frameId) => frameId.toString()); - - if (this.tabsData[tabId]?.[cookieKey]) { - this.tabs[tabId].newUpdates++; - // Merge in previous warning reasons. - const parsedCookie = { - ...this.tabsData[tabId][cookieKey].parsedCookie, - ...cookie.parsedCookie, - samesite: ( - cookie.parsedCookie.samesite ?? - this.tabsData[tabId][cookieKey].parsedCookie.samesite ?? - 'lax' - ).toLowerCase(), - httponly: - cookie.parsedCookie.httponly ?? - this.tabsData[tabId][cookieKey].parsedCookie.httponly, - priority: - cookie.parsedCookie?.priority ?? - this.tabsData[tabId][cookieKey].parsedCookie?.priority ?? - 'Medium', - partitionKey: '', - }; - if ( - cookie.parsedCookie?.partitionKey || - this.tabsData[tabId][cookieKey].parsedCookie?.partitionKey - ) { - parsedCookie.partitionKey = - cookie.parsedCookie?.partitionKey || - this.tabsData[tabId][cookieKey].parsedCookie?.partitionKey; - } - - const networkEvents: CookieData['networkEvents'] = { - requestEvents: [ - ...(this.tabsData[tabId][cookieKey]?.networkEvents - ?.requestEvents || []), - ...(cookie.networkEvents?.requestEvents || []), - ], - responseEvents: [ - ...(this.tabsData[tabId][cookieKey]?.networkEvents - ?.responseEvents || []), - ...(cookie.networkEvents?.responseEvents || []), - ], - }; - this.tabsData[tabId][cookieKey] = { - ...this.tabsData[tabId][cookieKey], - ...cookie, - parsedCookie, - isBlocked: blockedReasons.length > 0, - blockedReasons, - networkEvents, - blockingStatus: deriveBlockingStatus(networkEvents), - warningReasons, - url: this.tabsData[tabId][cookieKey].url ?? cookie.url, - headerType: - this.tabsData[tabId][cookieKey].headerType === 'javascript' - ? this.tabsData[tabId][cookieKey].headerType - : cookie.headerType, - frameIdList, - exemptionReason: - cookie?.exemptionReason || - this.tabsData[tabId][cookieKey]?.exemptionReason, - }; - } else { - this.tabs[tabId].newUpdates++; - this.tabsData[tabId][cookieKey] = { - ...cookie, - blockingStatus: deriveBlockingStatus(cookie.networkEvents), - }; - } - } - - updateCookieBadgeText(this.tabsData[tabId], tabId); - } catch (error) { - //Fail silently - // eslint-disable-next-line no-console - console.warn(error); - } - } - /** * Update FrameId set for a given url for a given tab. * @param {number} tabId The url whose url needs to be update. @@ -919,4 +646,4 @@ class SynchnorousCookieStore { } } -export default new SynchnorousCookieStore(); +export default new DataStore(); diff --git a/packages/extension/src/store/utils/networkTime.ts b/packages/extension/src/store/utils/networkTime.ts index 1e5e30d0fa..7b27fb7822 100644 --- a/packages/extension/src/store/utils/networkTime.ts +++ b/packages/extension/src/store/utils/networkTime.ts @@ -17,7 +17,7 @@ * External dependencies */ import type { Protocol } from 'devtools-protocol'; -import synchnorousCookieStore from '../synchnorousCookieStore'; +import synchnorousCookieStore from '../PAStore'; /** * Helps decode timestamps in network-related events, which are only convertible diff --git a/packages/extension/src/utils/getAndParseNetworkCookies.ts b/packages/extension/src/utils/getAndParseNetworkCookies.ts index 6f1f1b9e01..5d2783c4b1 100644 --- a/packages/extension/src/utils/getAndParseNetworkCookies.ts +++ b/packages/extension/src/utils/getAndParseNetworkCookies.ts @@ -21,7 +21,7 @@ import type { CookieData } from '@google-psat/common'; /** * Internal dependencies */ -import synchnorousCookieStore from '../store/synchnorousCookieStore'; +import synchnorousCookieStore from '../store/PAStore'; import parseNetworkCookies from './parseNetworkCookies'; export const getAndParseNetworkCookies = async (tabId: string) => { diff --git a/packages/extension/src/utils/listenToNewTab.ts b/packages/extension/src/utils/listenToNewTab.ts index d0feab56fc..864612ec95 100644 --- a/packages/extension/src/utils/listenToNewTab.ts +++ b/packages/extension/src/utils/listenToNewTab.ts @@ -16,7 +16,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../store/synchnorousCookieStore'; +import synchnorousCookieStore from '../store/PAStore'; import { getTab } from './getTab'; const listenToNewTab = async (tabId?: number) => { From 836ac9ce8cb38bbb55d1c5eb2238d5f3a52ee95b Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 16:23:38 +0530 Subject: [PATCH 05/40] Export interface from dataStore. --- packages/extension/src/store/dataStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 8b1ec9304e..1d2a476948 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -42,7 +42,7 @@ import isValidURL from '../utils/isValidURL'; import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; -interface singleAuctionEvent { +export interface singleAuctionEvent { bidCurrency: string; bid: number; name: string; From b50d673ec7171ae9ff2b6220b2c665b90bbad989 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 16:39:22 +0530 Subject: [PATCH 06/40] Change some more references. --- .../tests/beforeSendHeadersListener.ts | 30 +++++------- .../tests/onResponseStartedListener.ts | 30 +++++------- .../tests/tabOnCreatedListener.ts | 28 ++++++----- .../utils/updateGlobalVariableAndAttachCDP.ts | 46 +++++++++---------- .../extension/src/store/utils/networkTime.ts | 5 +- .../extension/src/utils/listenToNewTab.ts | 23 ++++------ 6 files changed, 71 insertions(+), 91 deletions(-) diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts index 8c5ff0c2aa..75161aaec8 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/beforeSendHeadersListener.ts @@ -26,7 +26,7 @@ import SinonChrome from 'sinon-chrome'; import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; import { onBeforeSendHeadersListener } from '../beforeSendHeadersListener'; import { requestHeaders } from '../test-utils/requestHeaders'; -import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; +import dataStore from '../../../store/dataStore'; describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { beforeAll(() => { @@ -48,14 +48,14 @@ describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { }); beforeEach(() => { - synchnorousCookieStore.globalIsUsingCDP = false; - synchnorousCookieStore.tabMode = 'single'; - synchnorousCookieStore.addTabData(1141143618); - synchnorousCookieStore.updateUrl(1141143618, 'https://bbc.com'); - synchnorousCookieStore.tabToRead = '1141143618'; + dataStore.globalIsUsingCDP = false; + dataStore.tabMode = 'single'; + dataStore.addTabData(1141143618); + dataStore.updateUrl(1141143618, 'https://bbc.com'); + dataStore.tabToRead = '1141143618'; }); afterEach(() => { - synchnorousCookieStore.removeTabData(1141143618); + dataStore.removeTabData(1141143618); }); test('Should parse request Cookies', async () => { @@ -69,9 +69,7 @@ describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(24); + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(24); }); test('Should not parse cookies if no cookie header is found in request header', async () => { @@ -85,13 +83,11 @@ describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(0); + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(0); }); test('Should not parse cookies if cdp is on', async () => { - synchnorousCookieStore.globalIsUsingCDP = true; + dataStore.globalIsUsingCDP = true; SinonChrome.webRequest.onBeforeSendHeaders.dispatch({ url: 'https://bbc.com', frameId: 0, @@ -102,9 +98,7 @@ describe('chrome.webRequest.onBeforeSendHeaders.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(0); - synchnorousCookieStore.globalIsUsingCDP = false; + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(0); + dataStore.globalIsUsingCDP = false; }); }); diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts index 4ce05500c8..ba28f979b6 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/onResponseStartedListener.ts @@ -27,7 +27,7 @@ import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-databas import { onResponseStartedListener } from '../onResponseStartedListener'; import { responseHeaders } from '../test-utils/requestHeaders'; -import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; +import dataStore from '../../../store/dataStore'; describe('chrome.webRequest.onResponseStarted.addListener', () => { beforeAll(() => { @@ -50,15 +50,15 @@ describe('chrome.webRequest.onResponseStarted.addListener', () => { }); beforeEach(() => { - synchnorousCookieStore.globalIsUsingCDP = false; - synchnorousCookieStore.tabMode = 'single'; - synchnorousCookieStore.addTabData(1141143618); - synchnorousCookieStore.updateUrl(1141143618, 'https://bbc.com'); - synchnorousCookieStore.tabToRead = '1141143618'; + dataStore.globalIsUsingCDP = false; + dataStore.tabMode = 'single'; + dataStore.addTabData(1141143618); + dataStore.updateUrl(1141143618, 'https://bbc.com'); + dataStore.tabToRead = '1141143618'; }); afterEach(() => { - synchnorousCookieStore.removeTabData(1141143618); + dataStore.removeTabData(1141143618); }); test('Should parse response Cookies', async () => { @@ -73,9 +73,7 @@ describe('chrome.webRequest.onResponseStarted.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(2); + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(2); }); test('Should not parse cookies if no cookie header is found in response header', async () => { @@ -91,13 +89,11 @@ describe('chrome.webRequest.onResponseStarted.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(0); + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(0); }); test('Should not parse cookies if cdp is on', async () => { - synchnorousCookieStore.globalIsUsingCDP = true; + dataStore.globalIsUsingCDP = true; SinonChrome.webRequest.onResponseStarted.dispatch({ url: 'https://bbc.com', frameId: 0, @@ -110,9 +106,7 @@ describe('chrome.webRequest.onResponseStarted.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect( - Object.keys(synchnorousCookieStore.tabsData[1141143618]).length - ).toEqual(0); - synchnorousCookieStore.globalIsUsingCDP = false; + expect(Object.keys(dataStore.tabsData[1141143618]).length).toEqual(0); + dataStore.globalIsUsingCDP = false; }); }); diff --git a/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts index b839c74ee5..c1b79446bf 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/tests/tabOnCreatedListener.ts @@ -25,7 +25,7 @@ import SinonChrome from 'sinon-chrome'; // eslint-disable-next-line import/no-unresolved import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; import { onTabCreatedListener } from '../tabOnCreatedListener'; -import synchnorousCookieStore from '../../../store/synchnorousCookieStore'; +import dataStore from '../../../store/dataStore'; describe('chrome.tabs.onCreated.addListener', () => { beforeAll(() => { @@ -44,8 +44,8 @@ describe('chrome.tabs.onCreated.addListener', () => { describe('Multitab Mode', () => { beforeAll(() => { - synchnorousCookieStore.globalIsUsingCDP = false; - synchnorousCookieStore.tabMode = 'unlimited'; + dataStore.globalIsUsingCDP = false; + dataStore.tabMode = 'unlimited'; }); test('Openeing new tabs should create an entry for the new tab in synchnorous cookie store.', async () => { @@ -67,20 +67,20 @@ describe('chrome.tabs.onCreated.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect(synchnorousCookieStore.tabs[123456]).toBeTruthy(); + expect(dataStore.tabs[123456]).toBeTruthy(); }); }); describe('Single Tab Mode', () => { beforeEach(() => { - synchnorousCookieStore.globalIsUsingCDP = false; - synchnorousCookieStore.tabMode = 'single'; - synchnorousCookieStore.addTabData(123456); - synchnorousCookieStore.tabToRead = ''; + dataStore.globalIsUsingCDP = false; + dataStore.tabMode = 'single'; + dataStore.addTabData(123456); + dataStore.tabToRead = ''; }); afterEach(() => { - synchnorousCookieStore.tabs = {}; - synchnorousCookieStore.tabsData = {}; + dataStore.tabs = {}; + dataStore.tabsData = {}; }); test('Openeing new tabs should create an entry for the new tab in synchnorous cookie store.', async () => { @@ -102,7 +102,7 @@ describe('chrome.tabs.onCreated.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect(synchnorousCookieStore.tabs[123456]).toBeTruthy(); + expect(dataStore.tabs[123456]).toBeTruthy(); }); test('Openeing more than 1 tab in single tab processing mode should not create an entry in synchrorous cookie store.', async () => { @@ -140,7 +140,7 @@ describe('chrome.tabs.onCreated.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect(synchnorousCookieStore.tabs[24567]).not.toBeTruthy(); + expect(dataStore.tabs[24567]).not.toBeTruthy(); }); }); @@ -162,8 +162,6 @@ describe('chrome.tabs.onCreated.addListener', () => { await new Promise((r) => setTimeout(r, 2000)); - expect(Object.keys(synchnorousCookieStore.tabs).length).toBeLessThanOrEqual( - 1 - ); + expect(Object.keys(dataStore.tabs).length).toBeLessThanOrEqual(1); }); }); diff --git a/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts b/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts index 7d9cf3eaa2..94430bdc13 100644 --- a/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts +++ b/packages/extension/src/serviceWorker/chromeListeners/utils/updateGlobalVariableAndAttachCDP.ts @@ -31,30 +31,28 @@ const updateGlobalVariableAndAttachCDP = async () => { const allTabs = await chrome.tabs.query({}); const targets = await chrome.debugger.getTargets(); - await Promise.all( - allTabs.map(async (tab) => { - if (!tab.id || tab.url?.startsWith('chrome://')) { - return; - } - - dataStore?.addTabData(tab.id); - - if (dataStore.globalIsUsingCDP) { - dataStore.initialiseVariablesForNewTab(tab.id.toString()); - - await attachCDP({ tabId: tab.id }); - - const currentTab = targets.filter( - ({ tabId }) => tabId && tab.id && tabId === tab.id - ); - dataStore?.updateParentChildFrameAssociation( - tab.id, - currentTab[0].id, - '0' - ); - } - }) - ); + allTabs.forEach((tab) => { + if (!tab.id || tab.url?.startsWith('chrome://')) { + return; + } + + dataStore?.addTabData(tab.id); + + if (dataStore.globalIsUsingCDP) { + dataStore.initialiseVariablesForNewTab(tab.id.toString()); + + attachCDP({ tabId: tab.id }); + + const currentTab = targets.filter( + ({ tabId }) => tabId && tab.id && tabId === tab.id + ); + dataStore?.updateParentChildFrameAssociation( + tab.id, + currentTab[0].id, + '0' + ); + } + }); } }; diff --git a/packages/extension/src/store/utils/networkTime.ts b/packages/extension/src/store/utils/networkTime.ts index 7b27fb7822..429c249ab4 100644 --- a/packages/extension/src/store/utils/networkTime.ts +++ b/packages/extension/src/store/utils/networkTime.ts @@ -17,7 +17,7 @@ * External dependencies */ import type { Protocol } from 'devtools-protocol'; -import synchnorousCookieStore from '../PAStore'; +import dataStore from '../dataStore'; /** * Helps decode timestamps in network-related events, which are only convertible @@ -32,8 +32,7 @@ export default function networkTime( timestamp: Protocol.Network.MonotonicTime, tabId: string ) { - const timeInfo = - synchnorousCookieStore.requestIdToCDPURLMapping[tabId][requestId]; + const timeInfo = dataStore.requestIdToCDPURLMapping[tabId][requestId]; // Somehow missed the start event? if (!timeInfo) { return new Date().getTime(); diff --git a/packages/extension/src/utils/listenToNewTab.ts b/packages/extension/src/utils/listenToNewTab.ts index 864612ec95..b900dfa1f5 100644 --- a/packages/extension/src/utils/listenToNewTab.ts +++ b/packages/extension/src/utils/listenToNewTab.ts @@ -16,7 +16,7 @@ /** * Internal dependencies */ -import synchnorousCookieStore from '../store/PAStore'; +import dataStore from '../store/dataStore'; import { getTab } from './getTab'; const listenToNewTab = async (tabId?: number) => { @@ -28,15 +28,12 @@ const listenToNewTab = async (tabId?: number) => { return ''; } - if ( - synchnorousCookieStore.tabMode && - synchnorousCookieStore.tabMode !== 'unlimited' - ) { - const storedTabData = Object.keys(synchnorousCookieStore?.tabsData ?? {}); + if (dataStore.tabMode && dataStore.tabMode !== 'unlimited') { + const storedTabData = Object.keys(dataStore?.tabsData ?? {}); await Promise.all( storedTabData.map(async (tabIdToDelete) => { - synchnorousCookieStore?.removeTabData(Number(tabIdToDelete)); + dataStore?.removeTabData(Number(tabIdToDelete)); try { await chrome.action.setBadgeText({ tabId: Number(tabIdToDelete), @@ -51,17 +48,17 @@ const listenToNewTab = async (tabId?: number) => { ); } - synchnorousCookieStore.tabToRead = newTabId; + dataStore.tabToRead = newTabId; - synchnorousCookieStore.initialiseVariablesForNewTab(newTabId); + dataStore.initialiseVariablesForNewTab(newTabId); - synchnorousCookieStore?.addTabData(Number(newTabId)); - synchnorousCookieStore?.updateDevToolsState(Number(newTabId), true); - synchnorousCookieStore?.updatePopUpState(Number(newTabId), true); + dataStore?.addTabData(Number(newTabId)); + dataStore?.updateDevToolsState(Number(newTabId), true); + dataStore?.updatePopUpState(Number(newTabId), true); const currentTab = await getTab(Number(newTabId)); - synchnorousCookieStore?.updateUrl(Number(newTabId), currentTab?.url ?? ''); + dataStore?.updateUrl(Number(newTabId), currentTab?.url ?? ''); return newTabId; }; From 7a506b7afde40bc692641854e82262646dd1df0a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 17:00:48 +0530 Subject: [PATCH 07/40] Add protected audience provider to the devtools. --- .../extension/src/view/devtools/index.tsx | 17 +-- .../src/view/devtools/stateProviders/index.ts | 1 + .../protectedAudience/context.ts | 39 ++++++ .../stateProviders/protectedAudience/index.ts | 21 ++++ .../protectedAudienceProvider.tsx | 112 ++++++++++++++++++ .../protectedAudience/useProtectedAudience.ts | 41 +++++++ 6 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/index.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx index 54207df66f..c6014dabc0 100644 --- a/packages/extension/src/view/devtools/index.tsx +++ b/packages/extension/src/view/devtools/index.tsx @@ -33,6 +33,7 @@ import { CookieProvider, SettingsProvider, AllowedListProvider, + ProtectedAudienceContextProvider, } from './stateProviders'; const isDarkMode = chrome.devtools.panels.themeName === 'dark'; @@ -45,13 +46,15 @@ if (root) { - - - - - - - + + + + + + + + + diff --git a/packages/extension/src/view/devtools/stateProviders/index.ts b/packages/extension/src/view/devtools/stateProviders/index.ts index a2d1bc49c6..de83565afc 100644 --- a/packages/extension/src/view/devtools/stateProviders/index.ts +++ b/packages/extension/src/view/devtools/stateProviders/index.ts @@ -16,3 +16,4 @@ export * from './cookie'; export * from './settings'; export * from './allowedList'; +export * from './protectedAudience'; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts new file mode 100644 index 0000000000..75a453a9fd --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -0,0 +1,39 @@ +/* + * 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 { createContext } from '@google-psat/common'; +import type { singleAuctionEvent } from '../../../../store/dataStore'; + +export type InterestGroups = singleAuctionEvent & { + details: any; +}; +export interface ProtectedAudienceContextType { + state: { + auctionEvents: singleAuctionEvent[]; + interestGroupDetails: InterestGroups[]; + }; +} + +const initialState: ProtectedAudienceContextType = { + state: { + auctionEvents: [], + interestGroupDetails: [], + }, +}; + +export default createContext(initialState); diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/index.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/index.ts new file mode 100644 index 0000000000..15b1638432 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/index.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ +export { default as ProtectedAudienceContextProvider } from './protectedAudienceProvider'; +export { + default as ProtectedAudienceContext, + type ProtectedAudienceContextType, +} from './context'; +export { default as useProtectedAudience } from './useProtectedAudience'; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx new file mode 100644 index 0000000000..4bbac83c9f --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -0,0 +1,112 @@ +/* + * 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 PropsWithChildren, + useEffect, + useState, + useCallback, + useMemo, +} from 'react'; +import type { Protocol } from 'devtools-protocol'; + +/** + * Internal dependencies. + */ +import Context, { + type InterestGroups, + type ProtectedAudienceContextType, +} from './context'; +import type { singleAuctionEvent } from '../../../../store/dataStore'; + +const Provider = ({ children }: PropsWithChildren) => { + const [auctionEvents, setAuctionEvents] = useState([]); + const [interestGroupDetails, setInterestGroupDetails] = useState< + InterestGroups[] + >([]); + + const messagePassingListener = useCallback( + // eslint-disable-next-line complexity + async (message: { + type: string; + payload: { + tabId?: number; + auctionEvents?: singleAuctionEvent[]; + }; + }) => { + if (!message.type) { + return; + } + + const tabId = chrome.devtools.inspectedWindow.tabId; + const incomingMessageType = message.type; + + if ( + incomingMessageType === 'AUCTION_EVENTS' && + message.payload.auctionEvents && + message.payload.auctionEvents?.length > 0 + ) { + if (message.payload.tabId === tabId) { + setAuctionEvents(message.payload.auctionEvents); + const shapedInterestGroupDetails = await Promise.all( + message.payload.auctionEvents + .filter((event) => event.eventType === 'interestGroupAccessed') + .map(async (event) => { + const result = (await chrome.debugger.sendCommand( + { tabId }, + 'Storage.getInterestGroupDetails', + { + name: '', + ownerOrigin: '', + } + )) as Protocol.Storage.GetInterestGroupDetailsResponse; + + return { + ...event, + details: result, + }; + }) + ); + setInterestGroupDetails(shapedInterestGroupDetails); + } + } + }, + [] + ); + + useEffect(() => { + chrome.runtime.onMessage.addListener(messagePassingListener); + + return () => { + chrome.runtime.onMessage.removeListener(messagePassingListener); + }; + }, [messagePassingListener]); + + const memoisedValue: ProtectedAudienceContextType = useMemo(() => { + return { + state: { + auctionEvents, + interestGroupDetails, + }, + }; + }, [auctionEvents, interestGroupDetails]); + + return {children}; +}; + +export default Provider; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts new file mode 100644 index 0000000000..6e62f50fe8 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts @@ -0,0 +1,41 @@ +/* + * 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 { useContextSelector } from '@google-psat/common'; + +/** + * Internal dependencies. + */ +import Context, { type CookieStoreContext } from './context'; + +export function useCookie(): CookieStoreContext; +export function useCookie(selector: (state: CookieStoreContext) => T): T; + +/** + * Cookie store hook. + * @param selector Selector function to partially select state. + * @returns selected part of the state + */ +export function useCookie( + selector: (state: CookieStoreContext) => T | CookieStoreContext = (state) => + state +) { + return useContextSelector(Context, selector); +} + +export default useCookie; From bb48995086b3fb42517226311f17bfcdf096710c Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 17:16:21 +0530 Subject: [PATCH 08/40] Convert bid to null. --- packages/extension/src/serviceWorker/index.ts | 28 ++++++++++++++++--- packages/extension/src/store/PAStore.ts | 2 +- packages/extension/src/store/dataStore.ts | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index fa97fd002d..55be48af03 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -211,7 +211,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', - bid: 0, + bid: null, name: '', ownerOrigin: '', type: interestGroupAuctionEventOccured.type, @@ -234,9 +234,29 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const interestGroupAccessedParams = params as Protocol.Storage.InterestGroupAccessedEvent; + let bid = null; + + if (interestGroupAccessedParams?.bid) { + bid = interestGroupAccessedParams?.bid; + } + if ( + !interestGroupAccessedParams?.bid && + interestGroupAccessedParams.type === 'win' + ) { + bid = dataStore.auctionEvents[parseInt(tabId)].filter( + (event) => + event?.type === 'bid' && + event.eventType === 'interestGroupAccessed' && + event?.interestGroupConfig?.uniqueAuctionId && + interestGroupAccessedParams?.uniqueAuctionId && + event?.interestGroupConfig?.uniqueAuctionId === + interestGroupAccessedParams.uniqueAuctionId + )?.[0]?.bid; + } + dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: interestGroupAccessedParams?.bidCurrency ?? '', - bid: interestGroupAccessedParams?.bid ?? 0, + bid: bid && bid > 0 ? bid : null, name: interestGroupAccessedParams.name, ownerOrigin: interestGroupAccessedParams.ownerOrigin, formattedTime: @@ -279,7 +299,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', - bid: 0, + bid: null, name: '', ownerOrigin: '', formattedTime: @@ -355,7 +375,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { if (dataStore.unParsedRequestHeadersForPA[tabId][requestId]) { dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: '', - bid: 0, + bid: null, name: '', ownerOrigin: '', type: diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 324a8c410c..e52364df66 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -56,7 +56,7 @@ class PAStore { dataStore.auctionEvents[parseInt(tabId)].push({ bidCurrency: auctionConfig?.bidCurrency ?? '', - bid: auctionConfig?.bid ?? '', + bid: auctionConfig?.bid ?? null, name: auctionConfig?.name ?? '', ownerOrigin: auctionConfig?.ownerOrigin ?? '', type: method + type, diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 1d2a476948..fb1802abc6 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -44,7 +44,7 @@ import { fetchDictionary } from '../utils/fetchCookieDictionary'; export interface singleAuctionEvent { bidCurrency: string; - bid: number; + bid: number | null; name: string; ownerOrigin: string; type: string; From 97e9170c708937257ccf591e774eadb62ed5ac23 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 8 Aug 2024 17:29:04 +0530 Subject: [PATCH 09/40] Add last mile connection to the devtools --- packages/extension/src/store/dataStore.ts | 7 ++++ .../protectedAudienceProvider.tsx | 35 ++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index fb1802abc6..fe3393eac2 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -531,6 +531,13 @@ class DataStore { }, }, }); + await chrome.runtime.sendMessage({ + type: 'AUCTION_EVENTS', + payload: { + tabId, + auctionEvents: this.auctionEvents[tabId], + }, + }); } if (sentMessageAnyWhere) { diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 4bbac83c9f..c889e96301 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -16,7 +16,7 @@ /** * External dependencies. */ -import { +import React, { type PropsWithChildren, useEffect, useState, @@ -33,6 +33,7 @@ import Context, { type ProtectedAudienceContextType, } from './context'; import type { singleAuctionEvent } from '../../../../store/dataStore'; +import { diff } from 'deep-object-diff'; const Provider = ({ children }: PropsWithChildren) => { const [auctionEvents, setAuctionEvents] = useState([]); @@ -62,17 +63,33 @@ const Provider = ({ children }: PropsWithChildren) => { message.payload.auctionEvents?.length > 0 ) { if (message.payload.tabId === tabId) { - setAuctionEvents(message.payload.auctionEvents); + setAuctionEvents((prevState) => { + if ( + message.payload.auctionEvents && + Object.keys(diff(prevState, message.payload.auctionEvents)) + .length > 0 + ) { + return message.payload.auctionEvents; + } + return prevState; + }); const shapedInterestGroupDetails = await Promise.all( message.payload.auctionEvents .filter((event) => event.eventType === 'interestGroupAccessed') .map(async (event) => { + if (!event?.name && !event?.ownerOrigin) { + return { + ...event, + details: {}, + }; + } + const result = (await chrome.debugger.sendCommand( { tabId }, 'Storage.getInterestGroupDetails', { - name: '', - ownerOrigin: '', + name: event?.name, + ownerOrigin: event?.ownerOrigin, } )) as Protocol.Storage.GetInterestGroupDetailsResponse; @@ -82,7 +99,15 @@ const Provider = ({ children }: PropsWithChildren) => { }; }) ); - setInterestGroupDetails(shapedInterestGroupDetails); + setInterestGroupDetails((prevState) => { + if ( + Object.keys(diff(prevState, shapedInterestGroupDetails)).length > + 0 + ) { + return shapedInterestGroupDetails; + } + return prevState; + }); } } }, From 405d595c7b84334f12e17c9cec132bffd2ef870d Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 9 Aug 2024 18:21:27 +0530 Subject: [PATCH 10/40] Add method to incorporate multiple auction ID. --- packages/extension/src/serviceWorker/index.ts | 105 +++++++++++------- packages/extension/src/store/PAStore.ts | 5 + packages/extension/src/store/dataStore.ts | 5 +- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 55be48af03..44c89540ca 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -197,6 +197,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { auctionTime: interestGroupAuctionEventOccured.eventTime, auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, }; } @@ -207,9 +208,11 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { ...dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId], auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, }; dataStore.auctionEvents[parseInt(tabId)].push({ + uniqueAuctionId, bidCurrency: '', bid: null, name: '', @@ -224,6 +227,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { ), time: interestGroupAuctionEventOccured.eventTime, auctionConfig: interestGroupAuctionEventOccured.auctionConfig, + parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, eventType: 'interestGroupAuctionEventOccurred', }); } @@ -255,6 +259,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { } dataStore.auctionEvents[parseInt(tabId)].push({ + uniqueAuctionId: interestGroupAccessedParams?.uniqueAuctionId, bidCurrency: interestGroupAccessedParams?.bidCurrency ?? '', bid: bid && bid > 0 ? bid : null, name: interestGroupAccessedParams.name, @@ -270,6 +275,11 @@ chrome.debugger.onEvent.addListener((source, method, params) => { time: interestGroupAccessedParams.accessTime, auctionConfig: {}, interestGroupConfig: interestGroupAccessedParams, + parentAuctionId: interestGroupAccessedParams?.uniqueAuctionId + ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ + interestGroupAccessedParams?.uniqueAuctionId + ]?.parentAuctionId + : undefined, eventType: 'interestGroupAccessed', }); } @@ -296,26 +306,35 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.requestIdToCDPURLMapping[tabId][requestId].timeStamp, tabId ) ?? new Date().getTime(); - - dataStore.auctionEvents[parseInt(tabId)].push({ - bidCurrency: '', - bid: null, - name: '', - ownerOrigin: '', - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - time - ), - type: - 'Start fetch ' + - interestGroupAuctionNetworkRequestCreatedParams.type, - time, - auctionConfig: {}, - eventType: 'interestGroupAuctionNetworkRequestCreated', - }); + interestGroupAuctionNetworkRequestCreatedParams.auctions.forEach( + (uniqueAuctionId) => { + dataStore.auctionEvents[parseInt(tabId)].push({ + uniqueAuctionId, + bidCurrency: '', + bid: null, + name: '', + ownerOrigin: '', + formattedTime: + dataStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[parseInt(tabId)][0].time, + time + ), + type: + 'Start fetch ' + + interestGroupAuctionNetworkRequestCreatedParams.type, + time, + auctionConfig: {}, + parentAuctionId: uniqueAuctionId + ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ + uniqueAuctionId + ]?.parentAuctionId + : undefined, + eventType: 'interestGroupAuctionNetworkRequestCreated', + }); + } + ); } return; @@ -373,24 +392,34 @@ chrome.debugger.onEvent.addListener((source, method, params) => { } if (dataStore.unParsedRequestHeadersForPA[tabId][requestId]) { - dataStore.auctionEvents[parseInt(tabId)].push({ - bidCurrency: '', - bid: null, - name: '', - ownerOrigin: '', - type: - 'Start fetch ' + - dataStore.unParsedRequestHeadersForPA[tabId][requestId].type, - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - networkTime(requestId, timestamp, tabId) - ), - time: networkTime(requestId, timestamp, tabId), - auctionConfig: {}, - eventType: 'interestGroupAuctionNetworkRequestCreated', + dataStore.unParsedRequestHeadersForPA[tabId][ + requestId + ].auctions.forEach((uniqueAuctionId) => { + dataStore.auctionEvents[parseInt(tabId)].push({ + uniqueAuctionId, + bidCurrency: '', + bid: null, + name: '', + ownerOrigin: '', + type: + 'Start fetch ' + + dataStore.unParsedRequestHeadersForPA[tabId][requestId].type, + formattedTime: + dataStore.auctionEvents[parseInt(tabId)].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[parseInt(tabId)][0].time, + networkTime(requestId, timestamp, tabId) + ), + time: networkTime(requestId, timestamp, tabId), + auctionConfig: {}, + parentAuctionId: uniqueAuctionId + ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ + uniqueAuctionId + ]?.parentAuctionId + : undefined, + eventType: 'interestGroupAuctionNetworkRequestCreated', + }); }); } diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index e52364df66..6d1cd07f6c 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -55,6 +55,7 @@ class PAStore { dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]; dataStore.auctionEvents[parseInt(tabId)].push({ + uniqueAuctionId, bidCurrency: auctionConfig?.bidCurrency ?? '', bid: auctionConfig?.bid ?? null, name: auctionConfig?.name ?? '', @@ -69,6 +70,10 @@ class PAStore { ), time: calculatedNetworkTime, auctionConfig, + parentAuctionId: uniqueAuctionId + ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[uniqueAuctionId] + ?.parentAuctionId + : undefined, eventType: 'interestGroupAuctionNetworkRequestCompleted', }); }); diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index fe3393eac2..af8ad341df 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -44,6 +44,7 @@ import { fetchDictionary } from '../utils/fetchCookieDictionary'; export interface singleAuctionEvent { bidCurrency: string; + uniqueAuctionId?: Protocol.Storage.InterestGroupAuctionId; bid: number | null; name: string; ownerOrigin: string; @@ -52,6 +53,7 @@ export interface singleAuctionEvent { time: number; auctionConfig: object; interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; + parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; eventType: | 'interestGroupAuctionEventOccurred' | 'interestGroupAuctionNetworkRequestCompleted' @@ -70,7 +72,7 @@ class DataStore { } = {}; /** - * The cookie data of the tabs. + * The auction data of the tabs. */ auctionEvents: { [tabId: number]: singleAuctionEvent[]; @@ -84,6 +86,7 @@ class DataStore { [uniqueAuctionId: Protocol.Storage.InterestGroupAuctionId]: { auctionTime: Protocol.Network.TimeSinceEpoch; auctionConfig?: any; + parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; }; }; } = {}; From 594af197aa020c55b9ea717e5864140859fed12c Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Sun, 11 Aug 2024 02:52:11 +0530 Subject: [PATCH 11/40] Handle multiSeller auctions. Console.log data in app.tsx. --- packages/extension/src/store/PAStore.ts | 19 ++- packages/extension/src/store/dataStore.ts | 54 +++++++- packages/extension/src/view/devtools/app.tsx | 11 ++ .../protectedAudience/context.ts | 13 +- .../protectedAudienceProvider.tsx | 115 ++++++++++++------ .../protectedAudience/useProtectedAudience.ts | 13 +- 6 files changed, 176 insertions(+), 49 deletions(-) diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 6d1cd07f6c..fcb1810308 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -23,7 +23,7 @@ import type { Protocol } from 'devtools-protocol'; */ import networkTime from './utils/networkTime'; import formatTime from './utils/formatTime'; -import dataStore from './dataStore'; +import dataStore, { type singleAuctionEvent } from './dataStore'; class PAStore { /** @@ -78,6 +78,23 @@ class PAStore { }); }); } + + /** + * Decides if auction is multiSeller or singleSeller + * @param {singleAuctionEvent[]} auctionEvents This is used to get the related data for parsing the request. + * @returns { boolean } True for multiSeller False for singleSeller + */ + isMUltiSellerAuction(auctionEvents: singleAuctionEvent[]): boolean { + const uniqueSellers = new Set(); + + auctionEvents + //@ts-ignore -- Ignoring this for now since we dont have any type of auctionConfig + .filter((event) => Boolean(event.auctionConfig?.seller)) + //@ts-ignore -- Ignoring this for now since we dont have any type of auctionConfig + .forEach(({ auctionConfig }) => uniqueSellers.add(auctionConfig?.seller)); + + return uniqueSellers.size > 1; + } } export default new PAStore(); diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index af8ad341df..996de97aaa 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -41,6 +41,7 @@ import { NEW_COOKIE_DATA } from '../constants'; import isValidURL from '../utils/isValidURL'; import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; +import PAStore from './PAStore'; export interface singleAuctionEvent { bidCurrency: string; @@ -534,11 +535,62 @@ class DataStore { }, }, }); + + const isMultiSellerAuction = PAStore.isMUltiSellerAuction( + this.auctionEvents[tabId] + ); + const groupedAuctionBids: { + [parentAuctionId: string]: { + 0: singleAuctionEvent[]; + [uniqueAuctionId: string]: singleAuctionEvent[]; + }; + } = {}; + + if (isMultiSellerAuction) { + this.auctionEvents[tabId].forEach((event) => { + if (!event?.parentAuctionId) { + if (event?.uniqueAuctionId) { + if (!groupedAuctionBids[event?.uniqueAuctionId]) { + groupedAuctionBids[event?.uniqueAuctionId] = { + 0: [], + }; + } + groupedAuctionBids[event?.uniqueAuctionId]['0'].push(event); + } + return; + } + + if (!groupedAuctionBids[event.parentAuctionId]) { + groupedAuctionBids[event.parentAuctionId] = { + 0: [], + }; + } + + if (!event?.uniqueAuctionId) { + return; + } + + if ( + !groupedAuctionBids[event.parentAuctionId][event.uniqueAuctionId] + ) { + groupedAuctionBids[event.parentAuctionId][event.uniqueAuctionId] = + []; + } + + groupedAuctionBids[event.parentAuctionId][ + event.uniqueAuctionId + ].push(event); + }); + } + await chrome.runtime.sendMessage({ type: 'AUCTION_EVENTS', payload: { tabId, - auctionEvents: this.auctionEvents[tabId], + auctionEvents: isMultiSellerAuction + ? groupedAuctionBids + : this.auctionEvents[tabId], + multiSellerAuction: isMultiSellerAuction, }, }); } diff --git a/packages/extension/src/view/devtools/app.tsx b/packages/extension/src/view/devtools/app.tsx index 452cdf5ad7..915133cc20 100644 --- a/packages/extension/src/view/devtools/app.tsx +++ b/packages/extension/src/view/devtools/app.tsx @@ -31,6 +31,7 @@ import TABS from './tabs'; import './app.css'; import { Layout } from './components'; import useContextInvalidated from './hooks/useContextInvalidated'; +import { useProtectedAudience } from './stateProviders'; const App: React.FC = () => { const [sidebarData, setSidebarData] = useState(TABS); @@ -65,6 +66,16 @@ const App: React.FC = () => { })(); }, []); + const { auctionEvents, interestGroupDetails } = useProtectedAudience( + ({ state }) => ({ + auctionEvents: state.auctionEvents, + interestGroupDetails: state.interestGroupDetails, + }) + ); + + // eslint-disable-next-line no-console -- leaving this intentionally since we dont have a definite UI to show the events in extension. + console.log(auctionEvents, interestGroupDetails); + return ( { - const [auctionEvents, setAuctionEvents] = useState([]); + const [auctionEvents, setAuctionEvents] = + useState(null); + const [isMultiSellerAuction, setIsMultiSellerAuction] = + useState(false); const [interestGroupDetails, setInterestGroupDetails] = useState< - InterestGroups[] + ProtectedAudienceContextType['state']['interestGroupDetails'] >([]); + const computeInterestGroupDetails = useCallback( + (auctionEventsToBeParsed: singleAuctionEvent[]) => { + return Promise.all( + auctionEventsToBeParsed + .filter((event) => event.eventType === 'interestGroupAccessed') + .map(async (event) => { + if (!event?.name && !event?.ownerOrigin) { + return { + ...event, + details: {}, + }; + } + + const result = (await chrome.debugger.sendCommand( + { tabId: chrome.devtools.inspectedWindow.tabId }, + 'Storage.getInterestGroupDetails', + { + name: event?.name, + ownerOrigin: event?.ownerOrigin, + } + )) as Protocol.Storage.GetInterestGroupDetailsResponse; + + return { + ...event, + details: { + ...result, + }, + }; + }) + ); + }, + [] + ); + const messagePassingListener = useCallback( // eslint-disable-next-line complexity async (message: { type: string; payload: { - tabId?: number; - auctionEvents?: singleAuctionEvent[]; + tabId: number; + auctionEvents: ProtectedAudienceContextType['state']['auctionEvents']; + multiSellerAuction: boolean; }; }) => { if (!message.type) { @@ -59,12 +93,16 @@ const Provider = ({ children }: PropsWithChildren) => { if ( incomingMessageType === 'AUCTION_EVENTS' && - message.payload.auctionEvents && - message.payload.auctionEvents?.length > 0 + message.payload.auctionEvents ) { if (message.payload.tabId === tabId) { + setIsMultiSellerAuction(message.payload.multiSellerAuction); setAuctionEvents((prevState) => { + if (!prevState && message.payload.auctionEvents) { + return message.payload.auctionEvents; + } if ( + prevState && message.payload.auctionEvents && Object.keys(diff(prevState, message.payload.auctionEvents)) .length > 0 @@ -73,32 +111,28 @@ const Provider = ({ children }: PropsWithChildren) => { } return prevState; }); - const shapedInterestGroupDetails = await Promise.all( - message.payload.auctionEvents - .filter((event) => event.eventType === 'interestGroupAccessed') - .map(async (event) => { - if (!event?.name && !event?.ownerOrigin) { - return { - ...event, - details: {}, - }; - } - - const result = (await chrome.debugger.sendCommand( - { tabId }, - 'Storage.getInterestGroupDetails', - { - name: event?.name, - ownerOrigin: event?.ownerOrigin, - } - )) as Protocol.Storage.GetInterestGroupDetailsResponse; - - return { - ...event, - details: result, - }; - }) - ); + + let shapedInterestGroupDetails: ProtectedAudienceContextType['state']['interestGroupDetails'] = + []; + + if (!Array.isArray(message.payload.auctionEvents)) { + const eventsToBeParsed = Object.values( + message.payload.auctionEvents + ).flat(); + + const singleAuctionEventsFlatMapped = eventsToBeParsed + .map((eventSet) => Object.values(eventSet).flat()) + .flat(); + + shapedInterestGroupDetails = await computeInterestGroupDetails( + singleAuctionEventsFlatMapped + ); + } else { + shapedInterestGroupDetails = await computeInterestGroupDetails( + message.payload.auctionEvents + ); + } + setInterestGroupDetails((prevState) => { if ( Object.keys(diff(prevState, shapedInterestGroupDetails)).length > @@ -111,7 +145,7 @@ const Provider = ({ children }: PropsWithChildren) => { } } }, - [] + [computeInterestGroupDetails] ); useEffect(() => { @@ -127,9 +161,10 @@ const Provider = ({ children }: PropsWithChildren) => { state: { auctionEvents, interestGroupDetails, + isMultiSellerAuction, }, }; - }, [auctionEvents, interestGroupDetails]); + }, [auctionEvents, interestGroupDetails, isMultiSellerAuction]); return {children}; }; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts index 6e62f50fe8..2e12414710 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/useProtectedAudience.ts @@ -21,10 +21,12 @@ import { useContextSelector } from '@google-psat/common'; /** * Internal dependencies. */ -import Context, { type CookieStoreContext } from './context'; +import Context, { type ProtectedAudienceContextType } from './context'; -export function useCookie(): CookieStoreContext; -export function useCookie(selector: (state: CookieStoreContext) => T): T; +export function useCookie(): ProtectedAudienceContextType; +export function useCookie( + selector: (state: ProtectedAudienceContextType) => T +): T; /** * Cookie store hook. @@ -32,8 +34,9 @@ export function useCookie(selector: (state: CookieStoreContext) => T): T; * @returns selected part of the state */ export function useCookie( - selector: (state: CookieStoreContext) => T | CookieStoreContext = (state) => - state + selector: ( + state: ProtectedAudienceContextType + ) => T | ProtectedAudienceContextType = (state) => state ) { return useContextSelector(Context, selector); } From dddcf5224e005ea1b01ba9010a30069e15c89543 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 14 Aug 2024 10:50:21 +0530 Subject: [PATCH 12/40] Descructure details in the extension. --- .../protectedAudience/protectedAudienceProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 06c799ee37..fa0d6e02b2 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -65,7 +65,7 @@ const Provider = ({ children }: PropsWithChildren) => { return { ...event, details: { - ...result, + ...result.details, }, }; }) From 73760d357545aff391c7ee32d11310d0ecfb79ca Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 14 Aug 2024 11:59:38 +0530 Subject: [PATCH 13/40] Refactor service worker. Move service worker PA methods to PAStore. --- packages/extension/src/serviceWorker/index.ts | 175 +++--------------- packages/extension/src/store/PAStore.ts | 163 +++++++++++++++- packages/extension/src/store/dataStore.ts | 26 ++- 3 files changed, 196 insertions(+), 168 deletions(-) diff --git a/packages/extension/src/serviceWorker/index.ts b/packages/extension/src/serviceWorker/index.ts index 44c89540ca..ade9fc5f96 100644 --- a/packages/extension/src/serviceWorker/index.ts +++ b/packages/extension/src/serviceWorker/index.ts @@ -24,8 +24,6 @@ import { Protocol } from 'devtools-protocol'; import createCookieFromAuditsIssue from '../utils/createCookieFromAuditsIssue'; import './chromeListeners'; -import networkTime from '../store/utils/networkTime'; -import formatTime from '../store/utils/formatTime'; import dataStore from '../store/dataStore'; import cookieStore from '../store/cookieStore'; import PAStore from '../store/PAStore'; @@ -192,45 +190,21 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const interestGroupAuctionEventOccured = params as Protocol.Storage.InterestGroupAuctionEventOccurredEvent; - const { uniqueAuctionId } = interestGroupAuctionEventOccured; - if (interestGroupAuctionEventOccured.type === 'started') { - dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { - auctionTime: interestGroupAuctionEventOccured.eventTime, - auctionConfig: interestGroupAuctionEventOccured.auctionConfig, - parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, - }; - } + const { uniqueAuctionId, eventTime, auctionConfig, parentAuctionId } = + interestGroupAuctionEventOccured; - if ( - interestGroupAuctionEventOccured.type === 'configResolved' || - interestGroupAuctionEventOccured.type === 'started' - ) { - dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId] = { - ...dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId], - auctionConfig: interestGroupAuctionEventOccured.auctionConfig, - parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, - }; + dataStore.auctionDataForTabId[tabId][uniqueAuctionId] = { + ...(dataStore.auctionDataForTabId[tabId]?.[uniqueAuctionId] ?? {}), + auctionConfig, + parentAuctionId, + auctionTime: eventTime, + }; + + PAStore.processInterestGroupAuctionEventOccurred( + interestGroupAuctionEventOccured, + tabId + ); - dataStore.auctionEvents[parseInt(tabId)].push({ - uniqueAuctionId, - bidCurrency: '', - bid: null, - name: '', - ownerOrigin: '', - type: interestGroupAuctionEventOccured.type, - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - interestGroupAuctionEventOccured.eventTime - ), - time: interestGroupAuctionEventOccured.eventTime, - auctionConfig: interestGroupAuctionEventOccured.auctionConfig, - parentAuctionId: interestGroupAuctionEventOccured.parentAuctionId, - eventType: 'interestGroupAuctionEventOccurred', - }); - } return; } @@ -238,50 +212,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const interestGroupAccessedParams = params as Protocol.Storage.InterestGroupAccessedEvent; - let bid = null; - - if (interestGroupAccessedParams?.bid) { - bid = interestGroupAccessedParams?.bid; - } - if ( - !interestGroupAccessedParams?.bid && - interestGroupAccessedParams.type === 'win' - ) { - bid = dataStore.auctionEvents[parseInt(tabId)].filter( - (event) => - event?.type === 'bid' && - event.eventType === 'interestGroupAccessed' && - event?.interestGroupConfig?.uniqueAuctionId && - interestGroupAccessedParams?.uniqueAuctionId && - event?.interestGroupConfig?.uniqueAuctionId === - interestGroupAccessedParams.uniqueAuctionId - )?.[0]?.bid; - } - - dataStore.auctionEvents[parseInt(tabId)].push({ - uniqueAuctionId: interestGroupAccessedParams?.uniqueAuctionId, - bidCurrency: interestGroupAccessedParams?.bidCurrency ?? '', - bid: bid && bid > 0 ? bid : null, - name: interestGroupAccessedParams.name, - ownerOrigin: interestGroupAccessedParams.ownerOrigin, - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - interestGroupAccessedParams.accessTime - ), - type: interestGroupAccessedParams.type, - time: interestGroupAccessedParams.accessTime, - auctionConfig: {}, - interestGroupConfig: interestGroupAccessedParams, - parentAuctionId: interestGroupAccessedParams?.uniqueAuctionId - ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ - interestGroupAccessedParams?.uniqueAuctionId - ]?.parentAuctionId - : undefined, - eventType: 'interestGroupAccessed', - }); + PAStore.processInterestGroupEvent(interestGroupAccessedParams, tabId); } if ( @@ -291,50 +222,16 @@ chrome.debugger.onEvent.addListener((source, method, params) => { const interestGroupAuctionNetworkRequestCreatedParams = params as Protocol.Storage.InterestGroupAuctionNetworkRequestCreatedEvent; - const requestId = - interestGroupAuctionNetworkRequestCreatedParams.requestId; + const { auctions, type, requestId } = + interestGroupAuctionNetworkRequestCreatedParams; dataStore.unParsedRequestHeadersForPA[tabId][requestId] = { - auctions: interestGroupAuctionNetworkRequestCreatedParams.auctions, - type: interestGroupAuctionNetworkRequestCreatedParams.type, + auctions, + type, }; if (dataStore.requestIdToCDPURLMapping[tabId][requestId]) { - const time: number = - networkTime( - requestId, - dataStore.requestIdToCDPURLMapping[tabId][requestId].timeStamp, - tabId - ) ?? new Date().getTime(); - interestGroupAuctionNetworkRequestCreatedParams.auctions.forEach( - (uniqueAuctionId) => { - dataStore.auctionEvents[parseInt(tabId)].push({ - uniqueAuctionId, - bidCurrency: '', - bid: null, - name: '', - ownerOrigin: '', - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - time - ), - type: - 'Start fetch ' + - interestGroupAuctionNetworkRequestCreatedParams.type, - time, - auctionConfig: {}, - parentAuctionId: uniqueAuctionId - ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ - uniqueAuctionId - ]?.parentAuctionId - : undefined, - eventType: 'interestGroupAuctionNetworkRequestCreated', - }); - } - ); + PAStore.processStartFetchEvents(auctions, tabId, requestId, type); } return; @@ -392,35 +289,10 @@ chrome.debugger.onEvent.addListener((source, method, params) => { } if (dataStore.unParsedRequestHeadersForPA[tabId][requestId]) { - dataStore.unParsedRequestHeadersForPA[tabId][ - requestId - ].auctions.forEach((uniqueAuctionId) => { - dataStore.auctionEvents[parseInt(tabId)].push({ - uniqueAuctionId, - bidCurrency: '', - bid: null, - name: '', - ownerOrigin: '', - type: - 'Start fetch ' + - dataStore.unParsedRequestHeadersForPA[tabId][requestId].type, - formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 - ? '0 ms' - : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, - networkTime(requestId, timestamp, tabId) - ), - time: networkTime(requestId, timestamp, tabId), - auctionConfig: {}, - parentAuctionId: uniqueAuctionId - ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[ - uniqueAuctionId - ]?.parentAuctionId - : undefined, - eventType: 'interestGroupAuctionNetworkRequestCreated', - }); - }); + const { auctions, type } = + dataStore.unParsedRequestHeadersForPA[tabId][requestId]; + + PAStore.processStartFetchEvents(auctions, tabId, requestId, type); } if ( @@ -482,6 +354,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => { dataStore.unParsedRequestHeadersForCA[tabId][requestId] = params as Protocol.Network.RequestWillBeSentExtraInfoEvent; } + return; } diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index fcb1810308..6c3eb63c7c 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -48,13 +48,13 @@ class PAStore { const calculatedNetworkTime = networkTime(requestId, timestamp, tabId); auctions.forEach((uniqueAuctionId) => { - if (!dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]) { + if (!dataStore.auctionDataForTabId[tabId][uniqueAuctionId]) { return; } const { auctionConfig = {} } = - dataStore.auctionDataForTabId[parseInt(tabId)][uniqueAuctionId]; + dataStore.auctionDataForTabId[tabId][uniqueAuctionId]; - dataStore.auctionEvents[parseInt(tabId)].push({ + dataStore.auctionEvents[tabId][uniqueAuctionId].push({ uniqueAuctionId, bidCurrency: auctionConfig?.bidCurrency ?? '', bid: auctionConfig?.bid ?? null, @@ -62,16 +62,16 @@ class PAStore { ownerOrigin: auctionConfig?.ownerOrigin ?? '', type: method + type, formattedTime: - dataStore.auctionEvents[parseInt(tabId)].length === 0 + dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 ? '0 ms' : formatTime( - dataStore.auctionEvents[parseInt(tabId)][0].time, + dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, networkTime(requestId, timestamp, tabId) ), time: calculatedNetworkTime, auctionConfig, parentAuctionId: uniqueAuctionId - ? dataStore.auctionDataForTabId[parseInt(tabId)]?.[uniqueAuctionId] + ? dataStore.auctionDataForTabId[tabId]?.[uniqueAuctionId] ?.parentAuctionId : undefined, eventType: 'interestGroupAuctionNetworkRequestCompleted', @@ -84,7 +84,9 @@ class PAStore { * @param {singleAuctionEvent[]} auctionEvents This is used to get the related data for parsing the request. * @returns { boolean } True for multiSeller False for singleSeller */ - isMUltiSellerAuction(auctionEvents: singleAuctionEvent[]): boolean { + isMUltiSellerAuction(auctionEvents: { + [uniqueAuctionId: string]: singleAuctionEvent[]; + }): boolean { const uniqueSellers = new Set(); auctionEvents @@ -95,6 +97,153 @@ class PAStore { return uniqueSellers.size > 1; } + + /** + * Processes interest group acccess event. + * @param {Protocol.Storage.InterestGroupAccessedEvent} interestGroupAccessedParams The params that were passed when interestGroupAccessed Event was fired + * @param {string} tabId The tabId for which the event has to be added. + */ + processInterestGroupEvent( + interestGroupAccessedParams: Protocol.Storage.InterestGroupAccessedEvent, + tabId: string + ) { + const { uniqueAuctionId, accessTime, ownerOrigin, name, type } = + interestGroupAccessedParams; + + if (!uniqueAuctionId) { + return; + } + const { + bid: initialBidValue, + componentSellerOrigin, + bidCurrency: initialBidCurrencyValue, + } = interestGroupAccessedParams; + + let bid; + + if (initialBidValue) { + bid = initialBidValue; + } + + if (!initialBidValue && type === 'win') { + bid = dataStore.auctionEvents[tabId][uniqueAuctionId].filter( + ({ + type: storedType, + eventType, + interestGroupConfig: { uniqueAuctionId: eventAuctionId } = {}, + }) => + storedType === 'bid' && + eventType === 'interestGroupAccessed' && + eventAuctionId && + uniqueAuctionId && + eventAuctionId === interestGroupAccessedParams.uniqueAuctionId + )?.[0]?.bid; + } + const eventData: singleAuctionEvent = { + uniqueAuctionId, + name, + ownerOrigin, + formattedTime: + dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, + accessTime + ), + type, + time: accessTime, + parentAuctionId: + dataStore.auctionDataForTabId[tabId]?.[uniqueAuctionId] + ?.parentAuctionId ?? undefined, + eventType: 'interestGroupAccessed' as singleAuctionEvent['eventType'], + }; + + if (componentSellerOrigin) { + eventData.componentSellerOrigin = componentSellerOrigin; + } + + if (bid) { + eventData.bid = bid; + } + + if (initialBidCurrencyValue) { + eventData.bidCurrency = initialBidCurrencyValue; + } + + dataStore.auctionEvents[tabId][uniqueAuctionId].push(eventData); + } + + /** + * Process StartFetchEvents + * @param {Protocol.Storage.InterestGroupAuctionId[]} auctions Unique auction id's which need to be added to the event data store. + * @param {string} tabId The tabId for which the event has to be added. + * @param {string} requestId The requestId associated with the event. + * @param {string} type The type of JS which was fetched. + */ + processStartFetchEvents( + auctions: Protocol.Storage.InterestGroupAuctionId[], + tabId: string, + requestId: string, + type: string + ) { + const time: number = + networkTime( + requestId, + dataStore.requestIdToCDPURLMapping[tabId][requestId].timeStamp, + tabId + ) ?? new Date().getTime(); + + auctions.forEach((uniqueAuctionId) => { + dataStore.auctionEvents[tabId][uniqueAuctionId].push({ + uniqueAuctionId, + formattedTime: + dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, + time + ), + type: 'Start fetch ' + type, + time, + parentAuctionId: + dataStore.auctionDataForTabId[tabId]?.[uniqueAuctionId] + ?.parentAuctionId, + eventType: 'interestGroupAuctionNetworkRequestCreated', + }); + }); + } + + /** + * Process InterestGroupAuctionEventOccurred + * @param {Protocol.Storage.InterestGroupAuctionEventOccurredEvent} interestGroupAuctionEventOccured Event data passed to the InterestGroupAuctionEventOccurred + * @param {string} tabId The tabId for which the event has to be added. + */ + processInterestGroupAuctionEventOccurred( + interestGroupAuctionEventOccured: Protocol.Storage.InterestGroupAuctionEventOccurredEvent, + tabId: string + ) { + const { uniqueAuctionId, eventTime, type, auctionConfig, parentAuctionId } = + interestGroupAuctionEventOccured; + + const eventData = { + uniqueAuctionId, + type, + formattedTime: + dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + ? '0 ms' + : formatTime( + dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, + eventTime + ), + time: eventTime, + auctionConfig, + parentAuctionId, + eventType: + 'interestGroupAuctionEventOccurred' as singleAuctionEvent['eventType'], + }; + + dataStore.auctionEvents[tabId][uniqueAuctionId].push(eventData); + } } export default new PAStore(); diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 996de97aaa..d11f5a9400 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -44,15 +44,16 @@ import { fetchDictionary } from '../utils/fetchCookieDictionary'; import PAStore from './PAStore'; export interface singleAuctionEvent { - bidCurrency: string; + bidCurrency?: string; uniqueAuctionId?: Protocol.Storage.InterestGroupAuctionId; - bid: number | null; - name: string; - ownerOrigin: string; + bid?: number; + name?: string; + ownerOrigin?: string; type: string; formattedTime: string | Date; + componentSellerOrigin?: string; time: number; - auctionConfig: object; + auctionConfig?: object; interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; eventType: @@ -76,14 +77,16 @@ class DataStore { * The auction data of the tabs. */ auctionEvents: { - [tabId: number]: singleAuctionEvent[]; + [tabId: string]: { + [uniqueAuctionId: string]: singleAuctionEvent[]; + }; } = {}; /** * The auction data of the tabs. */ auctionDataForTabId: { - [tabId: number]: { + [tabId: string]: { [uniqueAuctionId: Protocol.Storage.InterestGroupAuctionId]: { auctionTime: Protocol.Network.TimeSinceEpoch; auctionConfig?: any; @@ -314,7 +317,7 @@ class DataStore { }; this.tabsData[tabId] = {}; - this.auctionEvents[tabId] = []; + this.auctionEvents[tabId.toString()] = {}; this.tabs[tabId] = { url: '', devToolsOpenState: false, @@ -453,7 +456,7 @@ class DataStore { this.tabs[tabId].newUpdates = 0; this.tabs[tabId].frameIDURLSet = {}; this.tabs[tabId].parentChildFrameAssociation = {}; - this.auctionEvents[tabId] = []; + this.auctionEvents[tabId.toString()] = {}; this.sendUpdatedDataToPopupAndDevTools(tabId, true); } @@ -545,9 +548,12 @@ class DataStore { [uniqueAuctionId: string]: singleAuctionEvent[]; }; } = {}; + const auctionEventsToBeProcessed = Object.values( + this.auctionEvents[tabId] + ).flat(); if (isMultiSellerAuction) { - this.auctionEvents[tabId].forEach((event) => { + auctionEventsToBeProcessed.forEach((event) => { if (!event?.parentAuctionId) { if (event?.uniqueAuctionId) { if (!groupedAuctionBids[event?.uniqueAuctionId]) { From 52298017b86fbf8f165620547e83ddcdff07ccfd Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 14 Aug 2024 13:12:05 +0530 Subject: [PATCH 14/40] Fix errors in the refactor. --- packages/extension/src/store/PAStore.ts | 34 +++++++++++++++-------- packages/extension/src/store/dataStore.ts | 3 +- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 6c3eb63c7c..4ef75f6413 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -54,7 +54,7 @@ class PAStore { const { auctionConfig = {} } = dataStore.auctionDataForTabId[tabId][uniqueAuctionId]; - dataStore.auctionEvents[tabId][uniqueAuctionId].push({ + this.getAuctionEventsArray(tabId, uniqueAuctionId).push({ uniqueAuctionId, bidCurrency: auctionConfig?.bidCurrency ?? '', bid: auctionConfig?.bid ?? null, @@ -62,7 +62,7 @@ class PAStore { ownerOrigin: auctionConfig?.ownerOrigin ?? '', type: method + type, formattedTime: - dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + this.getAuctionEventsArray(tabId, uniqueAuctionId).length === 0 ? '0 ms' : formatTime( dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, @@ -84,9 +84,7 @@ class PAStore { * @param {singleAuctionEvent[]} auctionEvents This is used to get the related data for parsing the request. * @returns { boolean } True for multiSeller False for singleSeller */ - isMUltiSellerAuction(auctionEvents: { - [uniqueAuctionId: string]: singleAuctionEvent[]; - }): boolean { + isMUltiSellerAuction(auctionEvents: singleAuctionEvent[]): boolean { const uniqueSellers = new Set(); auctionEvents @@ -113,6 +111,7 @@ class PAStore { if (!uniqueAuctionId) { return; } + const { bid: initialBidValue, componentSellerOrigin, @@ -126,7 +125,7 @@ class PAStore { } if (!initialBidValue && type === 'win') { - bid = dataStore.auctionEvents[tabId][uniqueAuctionId].filter( + bid = this.getAuctionEventsArray(tabId, uniqueAuctionId).filter( ({ type: storedType, eventType, @@ -144,7 +143,7 @@ class PAStore { name, ownerOrigin, formattedTime: - dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + this.getAuctionEventsArray(tabId, uniqueAuctionId).length === 0 ? '0 ms' : formatTime( dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, @@ -170,7 +169,7 @@ class PAStore { eventData.bidCurrency = initialBidCurrencyValue; } - dataStore.auctionEvents[tabId][uniqueAuctionId].push(eventData); + this.getAuctionEventsArray(tabId, uniqueAuctionId).push(eventData); } /** @@ -194,7 +193,7 @@ class PAStore { ) ?? new Date().getTime(); auctions.forEach((uniqueAuctionId) => { - dataStore.auctionEvents[tabId][uniqueAuctionId].push({ + this.getAuctionEventsArray(tabId, uniqueAuctionId).push({ uniqueAuctionId, formattedTime: dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 @@ -229,7 +228,7 @@ class PAStore { uniqueAuctionId, type, formattedTime: - dataStore.auctionEvents[tabId][uniqueAuctionId].length === 0 + this.getAuctionEventsArray(tabId, uniqueAuctionId).length === 0 ? '0 ms' : formatTime( dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, @@ -242,7 +241,20 @@ class PAStore { 'interestGroupAuctionEventOccurred' as singleAuctionEvent['eventType'], }; - dataStore.auctionEvents[tabId][uniqueAuctionId].push(eventData); + this.getAuctionEventsArray(tabId, uniqueAuctionId).push(eventData); + } + + /** + * Push into auction events + * @param {string} tabId The ID of the tab auction event is associated with. + * @param {string} uniqueAuctionId The ID of the auction event whose array is to be fetched. + * @returns {object} An object holder reporesenting the event data. + */ + getAuctionEventsArray(tabId: string, uniqueAuctionId: string) { + if (!dataStore.auctionEvents[tabId][uniqueAuctionId]) { + dataStore.auctionEvents[tabId][uniqueAuctionId] = []; + } + return dataStore.auctionEvents[tabId][uniqueAuctionId]; } } diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index d11f5a9400..1bf82ad9eb 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -540,7 +540,7 @@ class DataStore { }); const isMultiSellerAuction = PAStore.isMUltiSellerAuction( - this.auctionEvents[tabId] + Object.values(this.auctionEvents[tabId]).flat() ); const groupedAuctionBids: { [parentAuctionId: string]: { @@ -548,6 +548,7 @@ class DataStore { [uniqueAuctionId: string]: singleAuctionEvent[]; }; } = {}; + const auctionEventsToBeProcessed = Object.values( this.auctionEvents[tabId] ).flat(); From 6e4d1650a5cbbe7e3be4d15f34cd18b7fee0dce5 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 21 Aug 2024 01:45:41 +0530 Subject: [PATCH 15/40] Fix lint errors --- .../src/store/tests/synchnorousCookieStore.ts | 112 ++++++++---------- 1 file changed, 48 insertions(+), 64 deletions(-) diff --git a/packages/extension/src/store/tests/synchnorousCookieStore.ts b/packages/extension/src/store/tests/synchnorousCookieStore.ts index be257e247c..c5984c67a5 100644 --- a/packages/extension/src/store/tests/synchnorousCookieStore.ts +++ b/packages/extension/src/store/tests/synchnorousCookieStore.ts @@ -25,7 +25,8 @@ import SinonChrome from 'sinon-chrome'; // eslint-disable-next-line import/no-unresolved import OpenCookieDatabase from 'ps-analysis-tool/assets/data/open-cookie-database.json'; import data from '../../utils/test-data/cookieMockData'; -import synchnorousCookieStore from '../synchnorousCookieStore'; +import synchnorousCookieStore from '../cookieStore'; +import dataStore from '../dataStore'; describe('SynchnorousCookieStore:', () => { beforeAll(() => { @@ -42,29 +43,25 @@ describe('SynchnorousCookieStore:', () => { }); beforeEach(() => { - synchnorousCookieStore.addTabData(123456); - synchnorousCookieStore.updateUrl(123456, 'https://bbc.com'); + dataStore.addTabData(123456); + dataStore.updateUrl(123456, 'https://bbc.com'); }); afterEach(() => { - synchnorousCookieStore.clear(); + dataStore.clear(); }); it('Should not update storage if there are no new cookies', () => { synchnorousCookieStore.update(123456, []); - expect(Object.keys(synchnorousCookieStore.tabsData[123456]).length).toBe(0); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(0); + expect(Object.keys(dataStore.tabsData[123456]).length).toBe(0); + expect(dataStore.tabs[123456].newUpdates).toBe(0); }); it('Should add new cookie if cookie doesnt exist', () => { - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'] - ).toBeUndefined(); + expect(dataStore.tabsData[123456]['_cbcnn.com/']).toBeUndefined(); synchnorousCookieStore.update(123456, [data.tabCookies['_cb']]); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'] - ).not.toBeUndefined(); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabsData[123456]['_cbcnn.com/']).not.toBeUndefined(); + expect(dataStore.tabs[123456].newUpdates).toBe(1); }); it('Should update cookie with new blocked reason', () => { @@ -72,15 +69,12 @@ describe('SynchnorousCookieStore:', () => { { ...data.tabCookies['_cb'], isBlocked: false, blockedReasons: [] }, ]); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/']?.isBlocked - ).toBe(false); + expect(dataStore.tabsData[123456]['_cbcnn.com/']?.isBlocked).toBe(false); expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/']?.blockedReasons - ?.length + dataStore.tabsData[123456]['_cbcnn.com/']?.blockedReasons?.length ).toBe(0); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabs[123456].newUpdates).toBe(1); synchnorousCookieStore.update(123456, [ { @@ -90,14 +84,12 @@ describe('SynchnorousCookieStore:', () => { }, ]); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/']?.isBlocked - ).toBe(true); + expect(dataStore.tabsData[123456]['_cbcnn.com/']?.isBlocked).toBe(true); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'].blockedReasons - ).toContain('InvalidDomain'); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(2); + expect(dataStore.tabsData[123456]['_cbcnn.com/'].blockedReasons).toContain( + 'InvalidDomain' + ); + expect(dataStore.tabs[123456].newUpdates).toBe(2); }); it('Should persist the partition key from the previous set data', () => { @@ -113,27 +105,25 @@ describe('SynchnorousCookieStore:', () => { ]); expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie - ?.partitionKey + dataStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie?.partitionKey ).toBe('https://bbc.com'); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabs[123456].newUpdates).toBe(1); synchnorousCookieStore.update(123456, [ { ...data.tabCookies['_cb'], parsedCookie: { ...data.tabCookies['_cb'].parsedCookie, - partitionKey: undefined, + partitionKey: '', priority: 'Medium', }, }, ]); expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie - ?.partitionKey + dataStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie?.partitionKey ).toBe('https://bbc.com'); - expect(synchnorousCookieStore.tabs[123456].newUpdates).toBe(2); + expect(dataStore.tabs[123456].newUpdates).toBe(2); }); it('Should not update the data if tabId is not present', () => { @@ -147,28 +137,28 @@ describe('SynchnorousCookieStore:', () => { }, }, ]); - expect(synchnorousCookieStore.tabs[12345]?.newUpdates).toBeUndefined(); + expect(dataStore.tabs[12345]?.newUpdates).toBeUndefined(); }); it('Should clear cookie store when clear is called', () => { - expect(synchnorousCookieStore.tabs[123456]).toBeDefined(); - expect(synchnorousCookieStore.tabsData[123456]).toBeDefined(); + expect(dataStore.tabs[123456]).toBeDefined(); + expect(dataStore.tabsData[123456]).toBeDefined(); - synchnorousCookieStore.clear(); + dataStore.clear(); - expect(synchnorousCookieStore.tabs[123456]).toBeUndefined(); - expect(synchnorousCookieStore.tabsData[123456]).toBeUndefined(); + expect(dataStore.tabs[123456]).toBeUndefined(); + expect(dataStore.tabsData[123456]).toBeUndefined(); }); it('Should return the tab url for tab if exists', () => { - expect(synchnorousCookieStore.getTabUrl(123456)).toBe('https://bbc.com'); - expect(synchnorousCookieStore.getTabUrl(12345)).toBeNull(); + expect(dataStore.getTabUrl(123456)).toBe('https://bbc.com'); + expect(dataStore.getTabUrl(12345)).toBeNull(); }); it('Should update the url of tab if it exists', () => { - expect(synchnorousCookieStore.getTabUrl(123456)).toBe('https://bbc.com'); - synchnorousCookieStore.updateUrl(123456, 'https://cnn.com'); - expect(synchnorousCookieStore.getTabUrl(123456)).toBe('https://cnn.com'); + expect(dataStore.getTabUrl(123456)).toBe('https://bbc.com'); + dataStore.updateUrl(123456, 'https://cnn.com'); + expect(dataStore.getTabUrl(123456)).toBe('https://cnn.com'); }); it('Should add cookie exclusion reason and warning reason', () => { @@ -179,43 +169,37 @@ describe('SynchnorousCookieStore:', () => { 123456 ); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'].warningReasons - ).toContain('WarnSameSiteUnspecifiedCrossSiteContext'); + expect(dataStore.tabsData[123456]['_cbcnn.com/'].warningReasons).toContain( + 'WarnSameSiteUnspecifiedCrossSiteContext' + ); synchnorousCookieStore.addCookieExclusionWarningReason( '_cbcnn.com/', - ['ExcludeSameSiteUnspecifiedTreatedAsLax'], + ['SameSiteUnspecifiedTreatedAsLax'], [], 123456 ); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'].blockedReasons - ).toContain('ExcludeSameSiteUnspecifiedTreatedAsLax'); + expect(dataStore.tabsData[123456]['_cbcnn.com/'].blockedReasons).toContain( + 'ExcludeSameSiteUnspecifiedTreatedAsLax' + ); }); it('Should remove tabData', () => { - expect(synchnorousCookieStore.tabs[123456]).toBeDefined(); - synchnorousCookieStore.removeTabData(123456); - expect(synchnorousCookieStore.tabs[123456]).toBeUndefined(); + expect(dataStore.tabs[123456]).toBeDefined(); + dataStore.removeTabData(123456); + expect(dataStore.tabs[123456]).toBeUndefined(); }); it('Should remove cookie', () => { - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'] - ).toBeUndefined(); + expect(dataStore.tabsData[123456]['_cbcnn.com/']).toBeUndefined(); synchnorousCookieStore.update(123456, [data.tabCookies['_cb']]); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'] - ).toBeDefined(); + expect(dataStore.tabsData[123456]['_cbcnn.com/']).toBeDefined(); - synchnorousCookieStore.removeCookieData(123456); + dataStore.removeCookieData(123456); - expect( - synchnorousCookieStore.tabsData[123456]['_cbcnn.com/'] - ).toBeUndefined(); + expect(dataStore.tabsData[123456]['_cbcnn.com/']).toBeUndefined(); }); }); From 2b10c1c3beed0656ad6d61338219b6dd635b4b53 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 21 Aug 2024 02:00:35 +0530 Subject: [PATCH 16/40] Fix tests. --- .../src/store/tests/synchnorousCookieStore.ts | 2 +- .../extension/src/view/devtools/tests/app.tsx | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/tests/synchnorousCookieStore.ts b/packages/extension/src/store/tests/synchnorousCookieStore.ts index c5984c67a5..08bd00158b 100644 --- a/packages/extension/src/store/tests/synchnorousCookieStore.ts +++ b/packages/extension/src/store/tests/synchnorousCookieStore.ts @@ -181,7 +181,7 @@ describe('SynchnorousCookieStore:', () => { ); expect(dataStore.tabsData[123456]['_cbcnn.com/'].blockedReasons).toContain( - 'ExcludeSameSiteUnspecifiedTreatedAsLax' + 'SameSiteUnspecifiedTreatedAsLax' ); }); diff --git a/packages/extension/src/view/devtools/tests/app.tsx b/packages/extension/src/view/devtools/tests/app.tsx index 4dac22c34c..9f206602fb 100644 --- a/packages/extension/src/view/devtools/tests/app.tsx +++ b/packages/extension/src/view/devtools/tests/app.tsx @@ -28,7 +28,11 @@ import { I18n } from '@google-psat/i18n'; * Internal dependencies. */ import App from '../app'; -import { useCookie, useSettings } from '../stateProviders'; +import { + useCookie, + useSettings, + useProtectedAudience, +} from '../stateProviders'; // @ts-ignore // eslint-disable-next-line import/no-unresolved import PSInfo from 'ps-analysis-tool/data/PSInfo.json'; @@ -37,6 +41,7 @@ import data from '../../../utils/test-data/cookieMockData'; jest.mock('../stateProviders', () => ({ useCookie: jest.fn(), useSettings: jest.fn(), + useProtectedAudience: jest.fn(), })); jest.mock( @@ -49,6 +54,7 @@ jest.mock( const mockUseCookieStore = useCookie as jest.Mock; const mockUseTablePersistentSettingStore = useTablePersistentSettingsStore as jest.Mock; +const mockUseProtectedAudienceStore = useProtectedAudience as jest.Mock; const mockUseSettingsStore = useSettings as jest.Mock; describe('App', () => { @@ -191,6 +197,10 @@ describe('App', () => { isCurrentTabBeingListenedTo: true, tabToRead: '40245632', }); + mockUseProtectedAudienceStore.mockReturnValue({ + auctionEvents: {}, + interestGroupDetails: [], + }); mockUseTablePersistentSettingStore.mockReturnValue({ getPreferences: () => '', setPreferences: noop, @@ -221,6 +231,10 @@ describe('App', () => { tabUrl: data.tabUrl, isCurrentTabBeingListenedTo: true, }); + mockUseProtectedAudienceStore.mockReturnValue({ + auctionEvents: {}, + interestGroupDetails: [], + }); mockUseTablePersistentSettingStore.mockReturnValue({ getPreferences: () => '', setPreferences: noop, From 64acdabc435d98ec5b7f9bbb50d9c78d58778647 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 22 Aug 2024 14:10:05 +0530 Subject: [PATCH 17/40] Move PA types to common package. --- packages/common/src/index.ts | 1 + .../common/src/protectedAudience.types.ts | 47 +++++++++++++++++++ packages/extension/src/store/PAStore.ts | 3 +- packages/extension/src/store/dataStore.ts | 35 +++----------- .../protectedAudience/context.ts | 3 +- .../protectedAudienceProvider.tsx | 2 +- 6 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 packages/common/src/protectedAudience.types.ts diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 9bd59310fd..31ac25ea47 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -48,3 +48,4 @@ export * from './worker/enums'; export * from './utils/generateReports'; export * from './cookies.types'; export * from './libraryDetection.types'; +export * from './protectedAudience.types'; diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts new file mode 100644 index 0000000000..c7f0740d29 --- /dev/null +++ b/packages/common/src/protectedAudience.types.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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 { Protocol } from 'devtools-protocol'; + +export interface singleAuctionEvent { + bidCurrency?: string; + uniqueAuctionId?: Protocol.Storage.InterestGroupAuctionId; + bid?: number; + name?: string; + ownerOrigin?: string; + type: string; + formattedTime: string | Date; + componentSellerOrigin?: string; + time: number; + auctionConfig?: object; + interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; + parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; + eventType: + | 'interestGroupAuctionEventOccurred' + | 'interestGroupAuctionNetworkRequestCompleted' + | 'interestGroupAuctionNetworkRequestCreated' + | 'interestGroupAccessed'; +} + +export interface auctionData { + [uniqueAuctionId: Protocol.Storage.InterestGroupAuctionId]: { + auctionTime: Protocol.Network.TimeSinceEpoch; + auctionConfig?: any; + parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; + }; +} diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 4ef75f6413..0f54a82127 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -16,6 +16,7 @@ /** * External dependencies. */ +import type { singleAuctionEvent } from '@google-psat/common'; import type { Protocol } from 'devtools-protocol'; /** @@ -23,7 +24,7 @@ import type { Protocol } from 'devtools-protocol'; */ import networkTime from './utils/networkTime'; import formatTime from './utils/formatTime'; -import dataStore, { type singleAuctionEvent } from './dataStore'; +import dataStore from './dataStore'; class PAStore { /** diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 1bf82ad9eb..038587a3dd 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -31,7 +31,12 @@ /** * External dependencies. */ -import { type CookieData, type CookieDatabase } from '@google-psat/common'; +import type { + CookieData, + CookieDatabase, + singleAuctionEvent, + auctionData, +} from '@google-psat/common'; import type { Protocol } from 'devtools-protocol'; /** @@ -43,26 +48,6 @@ import { doesFrameExist } from '../utils/doesFrameExist'; import { fetchDictionary } from '../utils/fetchCookieDictionary'; import PAStore from './PAStore'; -export interface singleAuctionEvent { - bidCurrency?: string; - uniqueAuctionId?: Protocol.Storage.InterestGroupAuctionId; - bid?: number; - name?: string; - ownerOrigin?: string; - type: string; - formattedTime: string | Date; - componentSellerOrigin?: string; - time: number; - auctionConfig?: object; - interestGroupConfig?: Protocol.Storage.InterestGroupAccessedEvent; - parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; - eventType: - | 'interestGroupAuctionEventOccurred' - | 'interestGroupAuctionNetworkRequestCompleted' - | 'interestGroupAuctionNetworkRequestCreated' - | 'interestGroupAccessed'; -} - class DataStore { /** * The cookie data of the tabs. @@ -86,13 +71,7 @@ class DataStore { * The auction data of the tabs. */ auctionDataForTabId: { - [tabId: string]: { - [uniqueAuctionId: Protocol.Storage.InterestGroupAuctionId]: { - auctionTime: Protocol.Network.TimeSinceEpoch; - auctionConfig?: any; - parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; - }; - }; + [tabId: string]: auctionData; } = {}; /** diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts index 5db08cb6bb..b3b4ab44cd 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -16,8 +16,7 @@ /** * External dependencies. */ -import { createContext } from '@google-psat/common'; -import type { singleAuctionEvent } from '../../../../store/dataStore'; +import { createContext, type singleAuctionEvent } from '@google-psat/common'; export type InterestGroups = singleAuctionEvent & { details: any; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index fa0d6e02b2..6ebf0102eb 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -24,12 +24,12 @@ import React, { useMemo, } from 'react'; import type { Protocol } from 'devtools-protocol'; +import type { singleAuctionEvent } from '@google-psat/common'; import { diff } from 'deep-object-diff'; /** * Internal dependencies. */ import Context, { type ProtectedAudienceContextType } from './context'; -import type { singleAuctionEvent } from '../../../../store/dataStore'; const Provider = ({ children }: PropsWithChildren) => { const [auctionEvents, setAuctionEvents] = From fca8fc824ba76c649356c29c2107f04edaf3c52f Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 22 Aug 2024 14:11:31 +0530 Subject: [PATCH 18/40] Update devtools protocol package to latest version --- package-lock.json | 36 +++++++++++++++++---------------- packages/common/package.json | 2 +- packages/extension/package.json | 2 +- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 619c1d6ab6..214eefcd9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24769,7 +24769,7 @@ }, "packages/analysis-utils": { "name": "@google-psat/analysis-utils", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/common": "*", @@ -24796,7 +24796,7 @@ }, "packages/cli": { "name": "@google-psat/cli", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/analysis-utils": "*", @@ -24828,7 +24828,7 @@ }, "packages/cli-dashboard": { "name": "@google-psat/cli-dashboard", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/common": "*", @@ -24889,24 +24889,25 @@ }, "packages/common": { "name": "@google-psat/common", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/i18n": "*", "tldts": "^6.0.14" }, "devDependencies": { - "devtools-protocol": "^0.0.1333880" + "devtools-protocol": "^0.0.1345247" } }, "packages/common/node_modules/devtools-protocol": { - "version": "0.0.1333880", - "dev": true, - "license": "BSD-3-Clause" + "version": "0.0.1345247", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1345247.tgz", + "integrity": "sha512-U6SOWj83Eznob0zaelx2CzURpexV5JwZ3Qsl2Q7cYsToMT3gDBVM2mbDgr0LNxhp9y2RG+7KZHHRRNrSnYozog==", + "dev": true }, "packages/design-system": { "name": "@google-psat/design-system", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/common": "*", @@ -24924,7 +24925,7 @@ }, "packages/eslint-import-resolver": { "name": "@google-psat/eslint-import-resolver", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "eslint-import-resolver-node": "^0.3.7" @@ -24961,18 +24962,19 @@ }, "devDependencies": { "@types/react-copy-to-clipboard": "^5.0.4", - "devtools-protocol": "^0.0.1333880", + "devtools-protocol": "^0.0.1345247", "html-inline-script-webpack-plugin": "^3.2.1" } }, "packages/extension/node_modules/devtools-protocol": { - "version": "0.0.1333880", - "dev": true, - "license": "BSD-3-Clause" + "version": "0.0.1345247", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1345247.tgz", + "integrity": "sha512-U6SOWj83Eznob0zaelx2CzURpexV5JwZ3Qsl2Q7cYsToMT3gDBVM2mbDgr0LNxhp9y2RG+7KZHHRRNrSnYozog==", + "dev": true }, "packages/i18n": { "name": "@google-psat/i18n", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "intl-messageformat": "^10.5.11" @@ -24980,7 +24982,7 @@ }, "packages/library-detection": { "name": "@google-psat/library-detection", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/common": "*", @@ -25005,7 +25007,7 @@ }, "packages/report": { "name": "@google-psat/report", - "version": "0.10.1", + "version": "0.10.1-1", "license": "Apache-2.0", "dependencies": { "@google-psat/common": "*", diff --git a/packages/common/package.json b/packages/common/package.json index 976cef89a5..3d1b15d66f 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -40,6 +40,6 @@ "tldts": "^6.0.14" }, "devDependencies": { - "devtools-protocol": "^0.0.1333880" + "devtools-protocol": "^0.0.1345247" } } diff --git a/packages/extension/package.json b/packages/extension/package.json index 1b039cdac9..be6beb627a 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@types/react-copy-to-clipboard": "^5.0.4", - "devtools-protocol": "^0.0.1333880", + "devtools-protocol": "^0.0.1345247", "html-inline-script-webpack-plugin": "^3.2.1" } } From bc16572e001a9ee69efda054efdf3812026e37a4 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 22 Aug 2024 14:21:28 +0530 Subject: [PATCH 19/40] Move InterestGroup and auctionEvents to common package --- packages/common/src/protectedAudience.types.ts | 13 +++++++++++++ .../protectedAudience/context.ts | 18 ++++++------------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts index c7f0740d29..6db99be098 100644 --- a/packages/common/src/protectedAudience.types.ts +++ b/packages/common/src/protectedAudience.types.ts @@ -45,3 +45,16 @@ export interface auctionData { parentAuctionId?: Protocol.Storage.InterestGroupAuctionId; }; } + +export type InterestGroups = singleAuctionEvent & { + details: any; +}; + +export type AuctionEventsType = + | singleAuctionEvent[] + | { + [parentAuctionId: string]: { + [uniqueAuctionId: string]: singleAuctionEvent[]; + }; + } + | null; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts index b3b4ab44cd..0a62ba799d 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -16,21 +16,15 @@ /** * External dependencies. */ -import { createContext, type singleAuctionEvent } from '@google-psat/common'; +import { + createContext, + type AuctionEventsType, + type InterestGroups, +} from '@google-psat/common'; -export type InterestGroups = singleAuctionEvent & { - details: any; -}; export interface ProtectedAudienceContextType { state: { - auctionEvents: - | singleAuctionEvent[] - | { - [parentAuctionId: string]: { - [uniqueAuctionId: string]: singleAuctionEvent[]; - }; - } - | null; + auctionEvents: AuctionEventsType; interestGroupDetails: InterestGroups[]; isMultiSellerAuction: boolean; }; From 097d7e36684af4bb542ef4ae215870bcc7be8087 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Thu, 22 Aug 2024 16:38:01 +0530 Subject: [PATCH 20/40] Add support to hear globalEvents. --- packages/extension/src/store/PAStore.ts | 22 ++++++++++++++++--- packages/extension/src/store/dataStore.ts | 8 +++++++ .../protectedAudienceProvider.tsx | 21 ++++-------------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 0f54a82127..a5ceafdc14 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -109,6 +109,20 @@ class PAStore { const { uniqueAuctionId, accessTime, ownerOrigin, name, type } = interestGroupAccessedParams; + let eventData: singleAuctionEvent = { + uniqueAuctionId, + name, + ownerOrigin, + formattedTime: new Date(accessTime * 1000), + type, + time: accessTime, + eventType: 'interestGroupAccessed' as singleAuctionEvent['eventType'], + }; + + this.getAuctionEventsArray('globalEvents', 'interestGroupEvents').push( + eventData + ); + if (!uniqueAuctionId) { return; } @@ -125,7 +139,7 @@ class PAStore { bid = initialBidValue; } - if (!initialBidValue && type === 'win') { + if (uniqueAuctionId && !initialBidValue && type === 'win') { bid = this.getAuctionEventsArray(tabId, uniqueAuctionId).filter( ({ type: storedType, @@ -139,15 +153,17 @@ class PAStore { eventAuctionId === interestGroupAccessedParams.uniqueAuctionId )?.[0]?.bid; } - const eventData: singleAuctionEvent = { + + eventData = { uniqueAuctionId, name, ownerOrigin, formattedTime: + uniqueAuctionId && this.getAuctionEventsArray(tabId, uniqueAuctionId).length === 0 ? '0 ms' : formatTime( - dataStore.auctionEvents[tabId][uniqueAuctionId][0].time, + dataStore.auctionEvents[tabId][uniqueAuctionId]?.[0].time, accessTime ), type, diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 038587a3dd..2d205279fe 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -297,6 +297,9 @@ class DataStore { this.tabsData[tabId] = {}; this.auctionEvents[tabId.toString()] = {}; + this.auctionEvents['globalEvents'] = { + interestGroupEvents: [], + }; this.tabs[tabId] = { url: '', devToolsOpenState: false, @@ -436,6 +439,9 @@ class DataStore { this.tabs[tabId].frameIDURLSet = {}; this.tabs[tabId].parentChildFrameAssociation = {}; this.auctionEvents[tabId.toString()] = {}; + this.auctionEvents['globalEvents'] = { + interestGroupEvents: [], + }; this.sendUpdatedDataToPopupAndDevTools(tabId, true); } @@ -577,6 +583,8 @@ class DataStore { ? groupedAuctionBids : this.auctionEvents[tabId], multiSellerAuction: isMultiSellerAuction, + globalEvents: + this.auctionEvents['globalEvents']['interestGroupEvents'], }, }); } diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 6ebf0102eb..4332660c1b 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -82,6 +82,7 @@ const Provider = ({ children }: PropsWithChildren) => { tabId: number; auctionEvents: ProtectedAudienceContextType['state']['auctionEvents']; multiSellerAuction: boolean; + globalEvents: singleAuctionEvent[]; }; }) => { if (!message.type) { @@ -115,23 +116,9 @@ const Provider = ({ children }: PropsWithChildren) => { let shapedInterestGroupDetails: ProtectedAudienceContextType['state']['interestGroupDetails'] = []; - if (!Array.isArray(message.payload.auctionEvents)) { - const eventsToBeParsed = Object.values( - message.payload.auctionEvents - ).flat(); - - const singleAuctionEventsFlatMapped = eventsToBeParsed - .map((eventSet) => Object.values(eventSet).flat()) - .flat(); - - shapedInterestGroupDetails = await computeInterestGroupDetails( - singleAuctionEventsFlatMapped - ); - } else { - shapedInterestGroupDetails = await computeInterestGroupDetails( - message.payload.auctionEvents - ); - } + shapedInterestGroupDetails = await computeInterestGroupDetails( + message.payload.globalEvents + ); setInterestGroupDetails((prevState) => { if ( From 31c9107d5c5d72feef3000b208b80be4d43420ae Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 23 Aug 2024 00:53:39 +0530 Subject: [PATCH 21/40] Add comment to explain auctionEvents structure. Refactor PA store to fix some bug when it was made to accomodate global events. --- packages/extension/src/store/PAStore.ts | 4 +- packages/extension/src/store/dataStore.ts | 70 ++++++++++++++--------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index a5ceafdc14..639e9dadf3 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -119,9 +119,7 @@ class PAStore { eventType: 'interestGroupAccessed' as singleAuctionEvent['eventType'], }; - this.getAuctionEventsArray('globalEvents', 'interestGroupEvents').push( - eventData - ); + this.getAuctionEventsArray(tabId, 'globalEvents').push(eventData); if (!uniqueAuctionId) { return; diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 2d205279fe..048a6ade33 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -59,16 +59,38 @@ class DataStore { } = {}; /** - * The auction data of the tabs. + * The auction event of the tabs (Interest group access as well as interest group auction events). */ auctionEvents: { [tabId: string]: { [uniqueAuctionId: string]: singleAuctionEvent[]; }; } = {}; + /** + * For tab 123456 auction events will have interestGroup accessed events as well as the interestGroupAuctionEvents. + * There can be 2 types of interestGroupAccessed events: + * 1) Join and Leave eventsare global and are fired on all the tabs. + * 2) Bid Win topLevelBid are fired where bidding is happening. + * + * To accomodate these 2 events we have 2 different keys in the auctionEvent for each tab: + * 1) The uniqueParentAuctionId, + * a) if its a multi-seller auction then it will have: + * i) a uniqueParentAuctionId where component auction Events are added. + * ii) top level auction in an id whose key is '0', + * b) if its a single sale then it will have a key with uniqueAuctionId where all the auctionEvents are added. + * 2) a 'globalEvents' key where all the global events are added like join leave etc. + * + * Structure may look like this: + * auctionEvents: { + * 123456: { + * 'globalEvents': [], + * '12413hsad23e1nsd': {} + * } + * }; + */ /** - * The auction data of the tabs. + * The auction data of the tabs which is added when interestGroupAuctionEvent occurs. */ auctionDataForTabId: { [tabId: string]: auctionData; @@ -524,8 +546,10 @@ class DataStore { }, }); + const { interestGroupEvents, ...rest } = this.auctionEvents[tabId]; + const isMultiSellerAuction = PAStore.isMUltiSellerAuction( - Object.values(this.auctionEvents[tabId]).flat() + Object.values(rest).flat() ); const groupedAuctionBids: { [parentAuctionId: string]: { @@ -534,44 +558,39 @@ class DataStore { }; } = {}; - const auctionEventsToBeProcessed = Object.values( - this.auctionEvents[tabId] - ).flat(); + const auctionEventsToBeProcessed = Object.values(rest).flat(); if (isMultiSellerAuction) { auctionEventsToBeProcessed.forEach((event) => { - if (!event?.parentAuctionId) { - if (event?.uniqueAuctionId) { - if (!groupedAuctionBids[event?.uniqueAuctionId]) { - groupedAuctionBids[event?.uniqueAuctionId] = { + const { parentAuctionId = null, uniqueAuctionId = null } = event; + + if (!parentAuctionId) { + if (uniqueAuctionId) { + if (!groupedAuctionBids[uniqueAuctionId]) { + groupedAuctionBids[uniqueAuctionId] = { 0: [], }; } - groupedAuctionBids[event?.uniqueAuctionId]['0'].push(event); + groupedAuctionBids[uniqueAuctionId]['0'].push(event); } return; } - if (!groupedAuctionBids[event.parentAuctionId]) { - groupedAuctionBids[event.parentAuctionId] = { + if (!groupedAuctionBids[parentAuctionId]) { + groupedAuctionBids[parentAuctionId] = { 0: [], }; } - if (!event?.uniqueAuctionId) { + if (!uniqueAuctionId) { return; } - if ( - !groupedAuctionBids[event.parentAuctionId][event.uniqueAuctionId] - ) { - groupedAuctionBids[event.parentAuctionId][event.uniqueAuctionId] = - []; + if (!groupedAuctionBids[parentAuctionId][uniqueAuctionId]) { + groupedAuctionBids[parentAuctionId][uniqueAuctionId] = []; } - groupedAuctionBids[event.parentAuctionId][ - event.uniqueAuctionId - ].push(event); + groupedAuctionBids[parentAuctionId][uniqueAuctionId].push(event); }); } @@ -579,12 +598,9 @@ class DataStore { type: 'AUCTION_EVENTS', payload: { tabId, - auctionEvents: isMultiSellerAuction - ? groupedAuctionBids - : this.auctionEvents[tabId], + auctionEvents: isMultiSellerAuction ? groupedAuctionBids : rest, multiSellerAuction: isMultiSellerAuction, - globalEvents: - this.auctionEvents['globalEvents']['interestGroupEvents'], + globalEvents: interestGroupEvents, }, }); } From f8196607e065caed26fe9b4376acac91ac8fe803 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 23 Aug 2024 00:54:24 +0530 Subject: [PATCH 22/40] Use globalEvents instead of interestGroupEvents. --- packages/extension/src/store/dataStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 048a6ade33..2db567b7e9 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -546,7 +546,7 @@ class DataStore { }, }); - const { interestGroupEvents, ...rest } = this.auctionEvents[tabId]; + const { globalEvents, ...rest } = this.auctionEvents[tabId]; const isMultiSellerAuction = PAStore.isMUltiSellerAuction( Object.values(rest).flat() @@ -600,7 +600,7 @@ class DataStore { tabId, auctionEvents: isMultiSellerAuction ? groupedAuctionBids : rest, multiSellerAuction: isMultiSellerAuction, - globalEvents: interestGroupEvents, + globalEvents: globalEvents, }, }); } From f1b99abd2c2747f219a0073bba61ee0d7be6161b Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 23 Aug 2024 21:03:12 +0530 Subject: [PATCH 23/40] compute received Bids and noBids. --- .../common/src/protectedAudience.types.ts | 19 ++- .../protectedAudience/context.ts | 5 + .../protectedAudienceProvider.tsx | 145 +++++++++++++++++- 3 files changed, 159 insertions(+), 10 deletions(-) diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts index 6db99be098..66a899977d 100644 --- a/packages/common/src/protectedAudience.types.ts +++ b/packages/common/src/protectedAudience.types.ts @@ -50,11 +50,14 @@ export type InterestGroups = singleAuctionEvent & { details: any; }; -export type AuctionEventsType = - | singleAuctionEvent[] - | { - [parentAuctionId: string]: { - [uniqueAuctionId: string]: singleAuctionEvent[]; - }; - } - | null; +export type MultiSellerAuction = { + [parentAuctionId: string]: { + [uniqueAuctionId: string]: singleAuctionEvent[]; + }; +}; + +export type SingleSellerAuction = { + [parentAuctionId: string]: singleAuctionEvent[]; +}; + +export type AuctionEventsType = SingleSellerAuction | MultiSellerAuction | null; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts index 0a62ba799d..a80d48c8c4 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -20,6 +20,7 @@ import { createContext, type AuctionEventsType, type InterestGroups, + type singleAuctionEvent, } from '@google-psat/common'; export interface ProtectedAudienceContextType { @@ -27,6 +28,8 @@ export interface ProtectedAudienceContextType { auctionEvents: AuctionEventsType; interestGroupDetails: InterestGroups[]; isMultiSellerAuction: boolean; + receivedBids: singleAuctionEvent[]; + noBids: string[]; }; } @@ -35,6 +38,8 @@ const initialState: ProtectedAudienceContextType = { auctionEvents: null, interestGroupDetails: [], isMultiSellerAuction: false, + receivedBids: [], + noBids: [], }, }; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 4332660c1b..45820171c4 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -24,22 +24,140 @@ import React, { useMemo, } from 'react'; import type { Protocol } from 'devtools-protocol'; -import type { singleAuctionEvent } from '@google-psat/common'; +import type { + MultiSellerAuction, + singleAuctionEvent, + SingleSellerAuction, +} from '@google-psat/common'; import { diff } from 'deep-object-diff'; /** * Internal dependencies. */ import Context, { type ProtectedAudienceContextType } from './context'; +const BIDDING_TYPES = [ + 'bid', + 'additionalBid', + 'topLevelBid', + 'topLevelAdditionalBid', +]; + const Provider = ({ children }: PropsWithChildren) => { const [auctionEvents, setAuctionEvents] = useState(null); + const [isMultiSellerAuction, setIsMultiSellerAuction] = useState(false); + const [interestGroupDetails, setInterestGroupDetails] = useState< ProtectedAudienceContextType['state']['interestGroupDetails'] >([]); + const [receivedBids, setReceivedBids] = useState< + ProtectedAudienceContextType['state']['receivedBids'] + >([]); + + const [noBids, setNoBids] = useState< + ProtectedAudienceContextType['state']['noBids'] + >([]); + + const computeBids = useCallback( + ( + _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], + _isMultiSellerAuction: boolean + ) => { + if ( + !_auctionEvents || + (_auctionEvents && Object.keys(_auctionEvents).length === 0) + ) { + return null; + } + + const _receivedBids: singleAuctionEvent[] = []; + const _noBids: string[] = []; + + const _interestGroupBuyers = new Set(); + + if (_isMultiSellerAuction) { + const multisellerAuctionEvents = _auctionEvents as MultiSellerAuction; + Object.keys(multisellerAuctionEvents).forEach((parentAuctionId) => { + Object.keys(multisellerAuctionEvents[parentAuctionId]).forEach( + (uniqueAuctionId) => { + if ('globalEvents' === uniqueAuctionId) { + return; + } + + const { auctionConfig } = + multisellerAuctionEvents[parentAuctionId]?.[ + uniqueAuctionId + ]?.[1] ?? {}; + + //@ts-ignore + auctionConfig?.interestGroupBuyers?.forEach((element) => { + _interestGroupBuyers.add(element); + }); + + _receivedBids.push( + ...multisellerAuctionEvents[parentAuctionId][ + uniqueAuctionId + ].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ) + ); + } + ); + }); + + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); + + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + _noBids.push( + ...Array.from(buyersWhoBid.difference(_interestGroupBuyers)) + ); + } + } else { + const singleSellerAuctionEvents = _auctionEvents as SingleSellerAuction; + Object.keys(singleSellerAuctionEvents).forEach((parentAuctionId) => { + if ('globalEvents' === parentAuctionId) { + return; + } + + _receivedBids.push( + ...singleSellerAuctionEvents[parentAuctionId].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ) + ); + }); + + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); + + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + _noBids.push( + ...Array.from(buyersWhoBid.difference(_interestGroupBuyers)) + ); + } + } + return { receivedBids: _receivedBids, noBids: _noBids }; + }, + [] + ); + const computeInterestGroupDetails = useCallback( (auctionEventsToBeParsed: singleAuctionEvent[]) => { return Promise.all( @@ -98,6 +216,7 @@ const Provider = ({ children }: PropsWithChildren) => { ) { if (message.payload.tabId === tabId) { setIsMultiSellerAuction(message.payload.multiSellerAuction); + setAuctionEvents((prevState) => { if (!prevState && message.payload.auctionEvents) { return message.payload.auctionEvents; @@ -120,6 +239,19 @@ const Provider = ({ children }: PropsWithChildren) => { message.payload.globalEvents ); + const computedBids: { + receivedBids: singleAuctionEvent[]; + noBids: string[]; + } | null = computeBids( + message.payload.auctionEvents, + message.payload.multiSellerAuction + ); + + if (computedBids) { + setReceivedBids(computedBids.receivedBids); + setNoBids(computedBids.noBids); + } + setInterestGroupDetails((prevState) => { if ( Object.keys(diff(prevState, shapedInterestGroupDetails)).length > @@ -127,6 +259,7 @@ const Provider = ({ children }: PropsWithChildren) => { ) { return shapedInterestGroupDetails; } + return prevState; }); } @@ -149,9 +282,17 @@ const Provider = ({ children }: PropsWithChildren) => { auctionEvents, interestGroupDetails, isMultiSellerAuction, + receivedBids, + noBids, }, }; - }, [auctionEvents, interestGroupDetails, isMultiSellerAuction]); + }, [ + auctionEvents, + interestGroupDetails, + isMultiSellerAuction, + noBids, + receivedBids, + ]); return {children}; }; From 4a92868a6250341d7fc13bde526e955274bba357 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 26 Aug 2024 11:59:31 +0530 Subject: [PATCH 24/40] Fix noBids in the protected audience provider. --- .../common/src/protectedAudience.types.ts | 8 ++ .../protectedAudience/context.ts | 5 +- .../protectedAudienceProvider.tsx | 93 ++++++++++++------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts index 66a899977d..bc746f454b 100644 --- a/packages/common/src/protectedAudience.types.ts +++ b/packages/common/src/protectedAudience.types.ts @@ -60,4 +60,12 @@ export type SingleSellerAuction = { [parentAuctionId: string]: singleAuctionEvent[]; }; +export type NoBidsType = { + [auctionId: string]: { + ownerOrigin: string; + name: string; + uniqueAuctionId: string; + }; +}; + export type AuctionEventsType = SingleSellerAuction | MultiSellerAuction | null; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts index a80d48c8c4..3e8ab593ac 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -21,6 +21,7 @@ import { type AuctionEventsType, type InterestGroups, type singleAuctionEvent, + type NoBidsType, } from '@google-psat/common'; export interface ProtectedAudienceContextType { @@ -29,7 +30,7 @@ export interface ProtectedAudienceContextType { interestGroupDetails: InterestGroups[]; isMultiSellerAuction: boolean; receivedBids: singleAuctionEvent[]; - noBids: string[]; + noBids: NoBidsType; }; } @@ -39,7 +40,7 @@ const initialState: ProtectedAudienceContextType = { interestGroupDetails: [], isMultiSellerAuction: false, receivedBids: [], - noBids: [], + noBids: {}, }, }; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 45820171c4..6e7f10a252 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -26,6 +26,7 @@ import React, { import type { Protocol } from 'devtools-protocol'; import type { MultiSellerAuction, + NoBidsType, singleAuctionEvent, SingleSellerAuction, } from '@google-psat/common'; @@ -59,7 +60,7 @@ const Provider = ({ children }: PropsWithChildren) => { const [noBids, setNoBids] = useState< ProtectedAudienceContextType['state']['noBids'] - >([]); + >({}); const computeBids = useCallback( ( @@ -74,12 +75,13 @@ const Provider = ({ children }: PropsWithChildren) => { } const _receivedBids: singleAuctionEvent[] = []; - const _noBids: string[] = []; + const _noBids: NoBidsType = {}; const _interestGroupBuyers = new Set(); if (_isMultiSellerAuction) { const multisellerAuctionEvents = _auctionEvents as MultiSellerAuction; + Object.keys(multisellerAuctionEvents).forEach((parentAuctionId) => { Object.keys(multisellerAuctionEvents[parentAuctionId]).forEach( (uniqueAuctionId) => { @@ -92,6 +94,13 @@ const Provider = ({ children }: PropsWithChildren) => { uniqueAuctionId ]?.[1] ?? {}; + const { name } = + multisellerAuctionEvents[parentAuctionId]?.[ + uniqueAuctionId + ]?.find( + (event) => event?.eventType === 'interestGroupAccessed' + ) ?? {}; + //@ts-ignore auctionConfig?.interestGroupBuyers?.forEach((element) => { _interestGroupBuyers.add(element); @@ -106,52 +115,70 @@ const Provider = ({ children }: PropsWithChildren) => { BIDDING_TYPES.includes(event.type) ) ); - } - ); - }); - - if (_interestGroupBuyers.size > 0) { - const buyersWhoBid = new Set(); - _receivedBids.forEach((event) => { - if (event.ownerOrigin) { - buyersWhoBid.add(event.ownerOrigin); + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); + + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + Array.from( + buyersWhoBid.difference(_interestGroupBuyers) + ).forEach((buyer) => { + _noBids[uniqueAuctionId] = { + ownerOrigin: buyer, + name: name ?? '', + uniqueAuctionId, + }; + }); + } } - }); - - _noBids.push( - ...Array.from(buyersWhoBid.difference(_interestGroupBuyers)) ); - } + }); } else { const singleSellerAuctionEvents = _auctionEvents as SingleSellerAuction; - Object.keys(singleSellerAuctionEvents).forEach((parentAuctionId) => { - if ('globalEvents' === parentAuctionId) { + + Object.keys(singleSellerAuctionEvents).forEach((uniqueAuctionId) => { + if ('globalEvents' === uniqueAuctionId) { return; } _receivedBids.push( - ...singleSellerAuctionEvents[parentAuctionId].filter( + ...singleSellerAuctionEvents[uniqueAuctionId].filter( (event) => event.eventType === 'interestGroupAccessed' && BIDDING_TYPES.includes(event.type) ) ); - }); - if (_interestGroupBuyers.size > 0) { - const buyersWhoBid = new Set(); + const { name } = + singleSellerAuctionEvents[uniqueAuctionId]?.find( + (event) => event?.eventType === 'interestGroupAccessed' + ) ?? {}; - _receivedBids.forEach((event) => { - if (event.ownerOrigin) { - buyersWhoBid.add(event.ownerOrigin); - } - }); + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); - _noBids.push( - ...Array.from(buyersWhoBid.difference(_interestGroupBuyers)) - ); - } + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + Array.from(buyersWhoBid.difference(_interestGroupBuyers)).forEach( + (buyer) => { + _noBids[uniqueAuctionId] = { + ownerOrigin: buyer, + name: name ?? '', + uniqueAuctionId, + }; + } + ); + } + }); } return { receivedBids: _receivedBids, noBids: _noBids }; }, @@ -241,7 +268,7 @@ const Provider = ({ children }: PropsWithChildren) => { const computedBids: { receivedBids: singleAuctionEvent[]; - noBids: string[]; + noBids: NoBidsType; } | null = computeBids( message.payload.auctionEvents, message.payload.multiSellerAuction @@ -265,7 +292,7 @@ const Provider = ({ children }: PropsWithChildren) => { } } }, - [computeInterestGroupDetails] + [computeBids, computeInterestGroupDetails] ); useEffect(() => { From 5b14df1d68d07b6e1e0e4f68cb28289538a6e608 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 26 Aug 2024 15:02:45 +0530 Subject: [PATCH 25/40] Fix nobids value. Add object diff in state update. Remove console.log. Add adUnit code to the data being provided in the bids and noBids. --- .../common/src/protectedAudience.types.ts | 1 + packages/extension/src/view/devtools/app.tsx | 11 --- .../protectedAudienceProvider.tsx | 80 +++++++++++++++---- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts index bc746f454b..2b2d97a4ae 100644 --- a/packages/common/src/protectedAudience.types.ts +++ b/packages/common/src/protectedAudience.types.ts @@ -65,6 +65,7 @@ export type NoBidsType = { ownerOrigin: string; name: string; uniqueAuctionId: string; + adUnitCode?: string; }; }; diff --git a/packages/extension/src/view/devtools/app.tsx b/packages/extension/src/view/devtools/app.tsx index 915133cc20..452cdf5ad7 100644 --- a/packages/extension/src/view/devtools/app.tsx +++ b/packages/extension/src/view/devtools/app.tsx @@ -31,7 +31,6 @@ import TABS from './tabs'; import './app.css'; import { Layout } from './components'; import useContextInvalidated from './hooks/useContextInvalidated'; -import { useProtectedAudience } from './stateProviders'; const App: React.FC = () => { const [sidebarData, setSidebarData] = useState(TABS); @@ -66,16 +65,6 @@ const App: React.FC = () => { })(); }, []); - const { auctionEvents, interestGroupDetails } = useProtectedAudience( - ({ state }) => ({ - auctionEvents: state.auctionEvents, - interestGroupDetails: state.interestGroupDetails, - }) - ); - - // eslint-disable-next-line no-console -- leaving this intentionally since we dont have a definite UI to show the events in extension. - console.log(auctionEvents, interestGroupDetails); - return ( { const _receivedBids: singleAuctionEvent[] = []; const _noBids: NoBidsType = {}; - const _interestGroupBuyers = new Set(); + const _interestGroupBuyers = new Set(); if (_isMultiSellerAuction) { const multisellerAuctionEvents = _auctionEvents as MultiSellerAuction; @@ -89,10 +89,12 @@ const Provider = ({ children }: PropsWithChildren) => { return; } - const { auctionConfig } = + const { auctionConfig, uniqueAuctionId: _uniqueAuctionId } = multisellerAuctionEvents[parentAuctionId]?.[ uniqueAuctionId - ]?.[1] ?? {}; + ]?.filter( + ({ type }) => type && type === 'configResolved' + )?.[0] ?? {}; const { name } = multisellerAuctionEvents[parentAuctionId]?.[ @@ -107,13 +109,20 @@ const Provider = ({ children }: PropsWithChildren) => { }); _receivedBids.push( - ...multisellerAuctionEvents[parentAuctionId][ - uniqueAuctionId - ].filter( - (event) => - event.eventType === 'interestGroupAccessed' && - BIDDING_TYPES.includes(event.type) - ) + ...multisellerAuctionEvents[parentAuctionId][uniqueAuctionId] + .filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ) + .map((event) => { + return { + ...event, + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + adUnitCode: JSON.parse(auctionConfig?.sellerSignals.value) + ?.divId, + }; + }) ); if (_interestGroupBuyers.size > 0) { @@ -126,12 +135,24 @@ const Provider = ({ children }: PropsWithChildren) => { }); Array.from( - buyersWhoBid.difference(_interestGroupBuyers) + _interestGroupBuyers.difference(buyersWhoBid) ).forEach((buyer) => { - _noBids[uniqueAuctionId] = { + const auctionId = + uniqueAuctionId === '0' + ? _uniqueAuctionId + : uniqueAuctionId; + + if (!auctionId) { + return; + } + + _noBids[auctionId] = { ownerOrigin: buyer, name: name ?? '', - uniqueAuctionId, + uniqueAuctionId: auctionId, + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + adUnitCode: JSON.parse(auctionConfig?.sellerSignals.value) + ?.divId, }; }); } @@ -154,6 +175,9 @@ const Provider = ({ children }: PropsWithChildren) => { ) ); + const { auctionConfig } = + singleSellerAuctionEvents[uniqueAuctionId]?.[1] ?? {}; + const { name } = singleSellerAuctionEvents[uniqueAuctionId]?.find( (event) => event?.eventType === 'interestGroupAccessed' @@ -168,11 +192,13 @@ const Provider = ({ children }: PropsWithChildren) => { } }); - Array.from(buyersWhoBid.difference(_interestGroupBuyers)).forEach( + Array.from(_interestGroupBuyers.difference(buyersWhoBid)).forEach( (buyer) => { _noBids[uniqueAuctionId] = { ownerOrigin: buyer, name: name ?? '', + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + adUnitCode: JSON.parse(auctionConfig?.sellerSignals)?.divId, uniqueAuctionId, }; } @@ -187,6 +213,10 @@ const Provider = ({ children }: PropsWithChildren) => { const computeInterestGroupDetails = useCallback( (auctionEventsToBeParsed: singleAuctionEvent[]) => { + if (!auctionEventsToBeParsed) { + return []; + } + return Promise.all( auctionEventsToBeParsed .filter((event) => event.eventType === 'interestGroupAccessed') @@ -275,8 +305,26 @@ const Provider = ({ children }: PropsWithChildren) => { ); if (computedBids) { - setReceivedBids(computedBids.receivedBids); - setNoBids(computedBids.noBids); + setReceivedBids((prevState) => { + if ( + Object.keys(diff(prevState, computedBids.receivedBids)).length > + 0 + ) { + return computedBids.receivedBids; + } + + return prevState; + }); + + setNoBids((prevState) => { + if ( + Object.keys(diff(prevState, computedBids.noBids)).length > 0 + ) { + return computedBids.noBids; + } + + return prevState; + }); } setInterestGroupDetails((prevState) => { From de69deae086a700f17d1d6db56e603ce27bea96d Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 26 Aug 2024 16:47:08 +0530 Subject: [PATCH 26/40] Add ads and Bidders in protectedAudience API provider. --- .../common/src/protectedAudience.types.ts | 14 ++ .../protectedAudience/context.ts | 7 +- .../protectedAudienceProvider.tsx | 120 ++++++++++++++---- 3 files changed, 115 insertions(+), 26 deletions(-) diff --git a/packages/common/src/protectedAudience.types.ts b/packages/common/src/protectedAudience.types.ts index 2b2d97a4ae..034ba642c7 100644 --- a/packages/common/src/protectedAudience.types.ts +++ b/packages/common/src/protectedAudience.types.ts @@ -66,7 +66,21 @@ export type NoBidsType = { name: string; uniqueAuctionId: string; adUnitCode?: string; + mediaContainerSize?: number[][]; }; }; +export type AdsAndBiddersType = { + [adUnitCode: string]: { + adUnitCode: string; + bidders: string[]; + mediaContainerSize: number[][]; + }; +}; + +export type ReceivedBids = singleAuctionEvent & { + adUnitCode?: string; + mediaContainerSize?: number[]; +}; + export type AuctionEventsType = SingleSellerAuction | MultiSellerAuction | null; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts index 3e8ab593ac..ccb885e2b9 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/context.ts @@ -20,8 +20,9 @@ import { createContext, type AuctionEventsType, type InterestGroups, - type singleAuctionEvent, + type ReceivedBids, type NoBidsType, + type AdsAndBiddersType, } from '@google-psat/common'; export interface ProtectedAudienceContextType { @@ -29,8 +30,9 @@ export interface ProtectedAudienceContextType { auctionEvents: AuctionEventsType; interestGroupDetails: InterestGroups[]; isMultiSellerAuction: boolean; - receivedBids: singleAuctionEvent[]; + receivedBids: ReceivedBids[]; noBids: NoBidsType; + adsAndBidders: AdsAndBiddersType; }; } @@ -41,6 +43,7 @@ const initialState: ProtectedAudienceContextType = { isMultiSellerAuction: false, receivedBids: [], noBids: {}, + adsAndBidders: {}, }, }; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 13287a9047..31bd5b4f83 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -29,6 +29,7 @@ import type { NoBidsType, singleAuctionEvent, SingleSellerAuction, + ReceivedBids, } from '@google-psat/common'; import { diff } from 'deep-object-diff'; /** @@ -62,6 +63,10 @@ const Provider = ({ children }: PropsWithChildren) => { ProtectedAudienceContextType['state']['noBids'] >({}); + const [adsAndBidders, setAdsAndBidders] = useState< + ProtectedAudienceContextType['state']['adsAndBidders'] + >({}); + const computeBids = useCallback( ( _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], @@ -74,7 +79,7 @@ const Provider = ({ children }: PropsWithChildren) => { return null; } - const _receivedBids: singleAuctionEvent[] = []; + const _receivedBids: ReceivedBids[] = []; const _noBids: NoBidsType = {}; const _interestGroupBuyers = new Set(); @@ -108,21 +113,27 @@ const Provider = ({ children }: PropsWithChildren) => { _interestGroupBuyers.add(element); }); + const filteredEvents = multisellerAuctionEvents[parentAuctionId][ + uniqueAuctionId + ].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ); + _receivedBids.push( - ...multisellerAuctionEvents[parentAuctionId][uniqueAuctionId] - .filter( - (event) => - event.eventType === 'interestGroupAccessed' && - BIDDING_TYPES.includes(event.type) - ) - .map((event) => { - return { - ...event, - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - adUnitCode: JSON.parse(auctionConfig?.sellerSignals.value) - ?.divId, - }; - }) + ...filteredEvents.map((event) => { + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + + return { + ...event, + mediaContainerSize: sellerSignals?.size, + adUnitCode: sellerSignals?.divId, + }; + }) ); if (_interestGroupBuyers.size > 0) { @@ -146,13 +157,16 @@ const Provider = ({ children }: PropsWithChildren) => { return; } + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + _noBids[auctionId] = { ownerOrigin: buyer, name: name ?? '', uniqueAuctionId: auctionId, - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - adUnitCode: JSON.parse(auctionConfig?.sellerSignals.value) - ?.divId, + adUnitCode: sellerSignals?.divId, }; }); } @@ -167,12 +181,27 @@ const Provider = ({ children }: PropsWithChildren) => { return; } + const filteredEvents = singleSellerAuctionEvents[ + uniqueAuctionId + ].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ); + _receivedBids.push( - ...singleSellerAuctionEvents[uniqueAuctionId].filter( - (event) => - event.eventType === 'interestGroupAccessed' && - BIDDING_TYPES.includes(event.type) - ) + ...filteredEvents.map((event) => { + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + + return { + ...event, + mediaContainerSize: sellerSignals.size, + adUnitCode: sellerSignals?.divId, + }; + }) ); const { auctionConfig } = @@ -297,7 +326,7 @@ const Provider = ({ children }: PropsWithChildren) => { ); const computedBids: { - receivedBids: singleAuctionEvent[]; + receivedBids: ReceivedBids[]; noBids: NoBidsType; } | null = computeBids( message.payload.auctionEvents, @@ -305,6 +334,47 @@ const Provider = ({ children }: PropsWithChildren) => { ); if (computedBids) { + const adUnitCodeToBidders: ProtectedAudienceContextType['state']['adsAndBidders'] = + {}; + + computedBids.receivedBids.forEach( + ({ adUnitCode, ownerOrigin, mediaContainerSize }) => { + if (!adUnitCode) { + return; + } + + adUnitCodeToBidders[adUnitCode] = { + adUnitCode: + adUnitCodeToBidders[adUnitCode].adUnitCode ?? adUnitCode, + bidders: Array.from( + new Set( + ...(adUnitCodeToBidders[adUnitCode]?.bidders ?? []), + ownerOrigin + ) + ), + mediaContainerSize: [ + Array.from( + new Set( + ...(adUnitCodeToBidders[adUnitCode] + ?.mediaContainerSize ?? []), + mediaContainerSize + ) + ), + ], + }; + } + ); + + setAdsAndBidders((prevState) => { + if ( + Object.keys(diff(prevState, adUnitCodeToBidders)).length > 0 + ) { + return adUnitCodeToBidders; + } + + return prevState; + }); + setReceivedBids((prevState) => { if ( Object.keys(diff(prevState, computedBids.receivedBids)).length > @@ -359,6 +429,7 @@ const Provider = ({ children }: PropsWithChildren) => { isMultiSellerAuction, receivedBids, noBids, + adsAndBidders, }, }; }, [ @@ -367,6 +438,7 @@ const Provider = ({ children }: PropsWithChildren) => { isMultiSellerAuction, noBids, receivedBids, + adsAndBidders, ]); return {children}; From 2a622333850dd05d6cfc9ba0b8ecfbd2630a9065 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 28 Aug 2024 16:59:18 +0530 Subject: [PATCH 27/40] Add adUnitCode optional chaining. Fix set spread of bidders. --- .../protectedAudience/protectedAudienceProvider.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 31bd5b4f83..5bee6576a5 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -345,13 +345,13 @@ const Provider = ({ children }: PropsWithChildren) => { adUnitCodeToBidders[adUnitCode] = { adUnitCode: - adUnitCodeToBidders[adUnitCode].adUnitCode ?? adUnitCode, - bidders: Array.from( - new Set( + adUnitCodeToBidders[adUnitCode]?.adUnitCode ?? adUnitCode, + bidders: [ + ...new Set([ ...(adUnitCodeToBidders[adUnitCode]?.bidders ?? []), - ownerOrigin - ) - ), + ...(ownerOrigin ? [ownerOrigin] : []), + ]), + ], mediaContainerSize: [ Array.from( new Set( From 933503d69ee05373edaa52b80cc251386ccb4aac Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 3 Sep 2024 17:01:01 +0530 Subject: [PATCH 28/40] Fix data being displayed on the panel. --- .../protectedAudience/protectedAudienceProvider.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 5bee6576a5..d217738660 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -189,6 +189,11 @@ const Provider = ({ children }: PropsWithChildren) => { BIDDING_TYPES.includes(event.type) ); + const { auctionConfig } = + singleSellerAuctionEvents?.[uniqueAuctionId]?.filter( + ({ type }) => type && type === 'configResolved' + )?.[0] ?? {}; + _receivedBids.push( ...filteredEvents.map((event) => { const sellerSignals = JSON.parse( @@ -204,9 +209,6 @@ const Provider = ({ children }: PropsWithChildren) => { }) ); - const { auctionConfig } = - singleSellerAuctionEvents[uniqueAuctionId]?.[1] ?? {}; - const { name } = singleSellerAuctionEvents[uniqueAuctionId]?.find( (event) => event?.eventType === 'interestGroupAccessed' From ddd2f6c0be2316c20bc256ba86dbf65d4bba747e Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 3 Sep 2024 19:31:16 +0530 Subject: [PATCH 29/40] Fix data refresh on tab reload. --- packages/extension/src/store/dataStore.ts | 2 ++ .../protectedAudience/protectedAudienceProvider.tsx | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 2db567b7e9..4359cce9db 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -464,6 +464,7 @@ class DataStore { this.auctionEvents['globalEvents'] = { interestGroupEvents: [], }; + this.auctionDataForTabId[tabId] = {}; this.sendUpdatedDataToPopupAndDevTools(tabId, true); } @@ -597,6 +598,7 @@ class DataStore { await chrome.runtime.sendMessage({ type: 'AUCTION_EVENTS', payload: { + refreshTabData: overrideForInitialSync, tabId, auctionEvents: isMultiSellerAuction ? groupedAuctionBids : rest, multiSellerAuction: isMultiSellerAuction, diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index d217738660..adbebfc504 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -70,8 +70,13 @@ const Provider = ({ children }: PropsWithChildren) => { const computeBids = useCallback( ( _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], - _isMultiSellerAuction: boolean + _isMultiSellerAuction: boolean, + refreshTabData = false ) => { + if (refreshTabData) { + return { receivedBids: [], noBids: {} }; + } + if ( !_auctionEvents || (_auctionEvents && Object.keys(_auctionEvents).length === 0) @@ -289,6 +294,7 @@ const Provider = ({ children }: PropsWithChildren) => { auctionEvents: ProtectedAudienceContextType['state']['auctionEvents']; multiSellerAuction: boolean; globalEvents: singleAuctionEvent[]; + refreshTabData: boolean; }; }) => { if (!message.type) { @@ -309,6 +315,7 @@ const Provider = ({ children }: PropsWithChildren) => { if (!prevState && message.payload.auctionEvents) { return message.payload.auctionEvents; } + if ( prevState && message.payload.auctionEvents && @@ -317,6 +324,7 @@ const Provider = ({ children }: PropsWithChildren) => { ) { return message.payload.auctionEvents; } + return prevState; }); @@ -332,7 +340,8 @@ const Provider = ({ children }: PropsWithChildren) => { noBids: NoBidsType; } | null = computeBids( message.payload.auctionEvents, - message.payload.multiSellerAuction + message.payload.multiSellerAuction, + message.payload.refreshTabData ); if (computedBids) { From fbf4f7412c2a38b94a7921fca84d11d4a1495cbb Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Wed, 4 Sep 2024 17:45:23 +0530 Subject: [PATCH 30/40] Reduce state updates. Refactor code move udeCallback to separate functions. --- .../extension/src/utils/shouldUpdateState.ts | 31 ++ .../protectedAudienceProvider.tsx | 292 ++---------------- .../utils/computeInterestGroupDetails.ts | 63 ++++ .../utils/computeReceivedBidsAndNoBids.ts | 215 +++++++++++++ .../protectedAudience/utils/index.ts | 17 + 5 files changed, 355 insertions(+), 263 deletions(-) create mode 100644 packages/extension/src/utils/shouldUpdateState.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeInterestGroupDetails.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts create mode 100644 packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/index.ts diff --git a/packages/extension/src/utils/shouldUpdateState.ts b/packages/extension/src/utils/shouldUpdateState.ts new file mode 100644 index 0000000000..e14fe69264 --- /dev/null +++ b/packages/extension/src/utils/shouldUpdateState.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2024 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 { diff } from 'deep-object-diff'; + +/** + * This is a utility function that decided whether or not we should update the state. + * @param previousState The previous state of the state object. + * @param update The new state of the state object + * @returns boolean Whether or not to update the state. + */ +function shouldUpdateState(previousState: any, update: any) { + return Object.keys(diff(previousState, update)).length > 0; +} + +export default shouldUpdateState; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index adbebfc504..a35bb5a254 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -23,26 +23,21 @@ import React, { useCallback, useMemo, } from 'react'; -import type { Protocol } from 'devtools-protocol'; import type { - MultiSellerAuction, NoBidsType, singleAuctionEvent, - SingleSellerAuction, ReceivedBids, } from '@google-psat/common'; -import { diff } from 'deep-object-diff'; + /** * Internal dependencies. */ import Context, { type ProtectedAudienceContextType } from './context'; - -const BIDDING_TYPES = [ - 'bid', - 'additionalBid', - 'topLevelBid', - 'topLevelAdditionalBid', -]; +import shouldUpdateState from '../../../../utils/shouldUpdateState'; +import { + computeInterestGroupDetails, + computeReceivedBidsAndNoBids, +} from './utils'; const Provider = ({ children }: PropsWithChildren) => { const [auctionEvents, setAuctionEvents] = @@ -67,224 +62,6 @@ const Provider = ({ children }: PropsWithChildren) => { ProtectedAudienceContextType['state']['adsAndBidders'] >({}); - const computeBids = useCallback( - ( - _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], - _isMultiSellerAuction: boolean, - refreshTabData = false - ) => { - if (refreshTabData) { - return { receivedBids: [], noBids: {} }; - } - - if ( - !_auctionEvents || - (_auctionEvents && Object.keys(_auctionEvents).length === 0) - ) { - return null; - } - - const _receivedBids: ReceivedBids[] = []; - const _noBids: NoBidsType = {}; - - const _interestGroupBuyers = new Set(); - - if (_isMultiSellerAuction) { - const multisellerAuctionEvents = _auctionEvents as MultiSellerAuction; - - Object.keys(multisellerAuctionEvents).forEach((parentAuctionId) => { - Object.keys(multisellerAuctionEvents[parentAuctionId]).forEach( - (uniqueAuctionId) => { - if ('globalEvents' === uniqueAuctionId) { - return; - } - - const { auctionConfig, uniqueAuctionId: _uniqueAuctionId } = - multisellerAuctionEvents[parentAuctionId]?.[ - uniqueAuctionId - ]?.filter( - ({ type }) => type && type === 'configResolved' - )?.[0] ?? {}; - - const { name } = - multisellerAuctionEvents[parentAuctionId]?.[ - uniqueAuctionId - ]?.find( - (event) => event?.eventType === 'interestGroupAccessed' - ) ?? {}; - - //@ts-ignore - auctionConfig?.interestGroupBuyers?.forEach((element) => { - _interestGroupBuyers.add(element); - }); - - const filteredEvents = multisellerAuctionEvents[parentAuctionId][ - uniqueAuctionId - ].filter( - (event) => - event.eventType === 'interestGroupAccessed' && - BIDDING_TYPES.includes(event.type) - ); - - _receivedBids.push( - ...filteredEvents.map((event) => { - const sellerSignals = JSON.parse( - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value - ); - - return { - ...event, - mediaContainerSize: sellerSignals?.size, - adUnitCode: sellerSignals?.divId, - }; - }) - ); - - if (_interestGroupBuyers.size > 0) { - const buyersWhoBid = new Set(); - - _receivedBids.forEach((event) => { - if (event.ownerOrigin) { - buyersWhoBid.add(event.ownerOrigin); - } - }); - - Array.from( - _interestGroupBuyers.difference(buyersWhoBid) - ).forEach((buyer) => { - const auctionId = - uniqueAuctionId === '0' - ? _uniqueAuctionId - : uniqueAuctionId; - - if (!auctionId) { - return; - } - - const sellerSignals = JSON.parse( - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value - ); - - _noBids[auctionId] = { - ownerOrigin: buyer, - name: name ?? '', - uniqueAuctionId: auctionId, - adUnitCode: sellerSignals?.divId, - }; - }); - } - } - ); - }); - } else { - const singleSellerAuctionEvents = _auctionEvents as SingleSellerAuction; - - Object.keys(singleSellerAuctionEvents).forEach((uniqueAuctionId) => { - if ('globalEvents' === uniqueAuctionId) { - return; - } - - const filteredEvents = singleSellerAuctionEvents[ - uniqueAuctionId - ].filter( - (event) => - event.eventType === 'interestGroupAccessed' && - BIDDING_TYPES.includes(event.type) - ); - - const { auctionConfig } = - singleSellerAuctionEvents?.[uniqueAuctionId]?.filter( - ({ type }) => type && type === 'configResolved' - )?.[0] ?? {}; - - _receivedBids.push( - ...filteredEvents.map((event) => { - const sellerSignals = JSON.parse( - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value - ); - - return { - ...event, - mediaContainerSize: sellerSignals.size, - adUnitCode: sellerSignals?.divId, - }; - }) - ); - - const { name } = - singleSellerAuctionEvents[uniqueAuctionId]?.find( - (event) => event?.eventType === 'interestGroupAccessed' - ) ?? {}; - - if (_interestGroupBuyers.size > 0) { - const buyersWhoBid = new Set(); - - _receivedBids.forEach((event) => { - if (event.ownerOrigin) { - buyersWhoBid.add(event.ownerOrigin); - } - }); - - Array.from(_interestGroupBuyers.difference(buyersWhoBid)).forEach( - (buyer) => { - _noBids[uniqueAuctionId] = { - ownerOrigin: buyer, - name: name ?? '', - //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - adUnitCode: JSON.parse(auctionConfig?.sellerSignals)?.divId, - uniqueAuctionId, - }; - } - ); - } - }); - } - return { receivedBids: _receivedBids, noBids: _noBids }; - }, - [] - ); - - const computeInterestGroupDetails = useCallback( - (auctionEventsToBeParsed: singleAuctionEvent[]) => { - if (!auctionEventsToBeParsed) { - return []; - } - - return Promise.all( - auctionEventsToBeParsed - .filter((event) => event.eventType === 'interestGroupAccessed') - .map(async (event) => { - if (!event?.name && !event?.ownerOrigin) { - return { - ...event, - details: {}, - }; - } - - const result = (await chrome.debugger.sendCommand( - { tabId: chrome.devtools.inspectedWindow.tabId }, - 'Storage.getInterestGroupDetails', - { - name: event?.name, - ownerOrigin: event?.ownerOrigin, - } - )) as Protocol.Storage.GetInterestGroupDetailsResponse; - - return { - ...event, - details: { - ...result.details, - }, - }; - }) - ); - }, - [] - ); - const messagePassingListener = useCallback( // eslint-disable-next-line complexity async (message: { @@ -297,6 +74,8 @@ const Provider = ({ children }: PropsWithChildren) => { refreshTabData: boolean; }; }) => { + let didAuctionEventsChange = false; + if (!message.type) { return; } @@ -313,21 +92,26 @@ const Provider = ({ children }: PropsWithChildren) => { setAuctionEvents((prevState) => { if (!prevState && message.payload.auctionEvents) { + didAuctionEventsChange = true; return message.payload.auctionEvents; } if ( prevState && message.payload.auctionEvents && - Object.keys(diff(prevState, message.payload.auctionEvents)) - .length > 0 + shouldUpdateState(prevState, message.payload.auctionEvents) ) { + didAuctionEventsChange = true; return message.payload.auctionEvents; } return prevState; }); + if (!didAuctionEventsChange) { + return; + } + let shapedInterestGroupDetails: ProtectedAudienceContextType['state']['interestGroupDetails'] = []; @@ -338,7 +122,7 @@ const Provider = ({ children }: PropsWithChildren) => { const computedBids: { receivedBids: ReceivedBids[]; noBids: NoBidsType; - } | null = computeBids( + } | null = computeReceivedBidsAndNoBids( message.payload.auctionEvents, message.payload.multiSellerAuction, message.payload.refreshTabData @@ -377,51 +161,33 @@ const Provider = ({ children }: PropsWithChildren) => { ); setAdsAndBidders((prevState) => { - if ( - Object.keys(diff(prevState, adUnitCodeToBidders)).length > 0 - ) { - return adUnitCodeToBidders; - } - - return prevState; + return shouldUpdateState(prevState, adUnitCodeToBidders) + ? adUnitCodeToBidders + : prevState; }); setReceivedBids((prevState) => { - if ( - Object.keys(diff(prevState, computedBids.receivedBids)).length > - 0 - ) { - return computedBids.receivedBids; - } - - return prevState; + return shouldUpdateState(prevState, computedBids.receivedBids) + ? computedBids.receivedBids + : prevState; }); setNoBids((prevState) => { - if ( - Object.keys(diff(prevState, computedBids.noBids)).length > 0 - ) { - return computedBids.noBids; - } - - return prevState; + return shouldUpdateState(prevState, computedBids.noBids) + ? computedBids.noBids + : prevState; }); } setInterestGroupDetails((prevState) => { - if ( - Object.keys(diff(prevState, shapedInterestGroupDetails)).length > - 0 - ) { - return shapedInterestGroupDetails; - } - - return prevState; + return shouldUpdateState(prevState, shapedInterestGroupDetails) + ? shapedInterestGroupDetails + : prevState; }); } } }, - [computeBids, computeInterestGroupDetails] + [] ); useEffect(() => { diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeInterestGroupDetails.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeInterestGroupDetails.ts new file mode 100644 index 0000000000..d4ce30a7b7 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeInterestGroupDetails.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2024 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 { singleAuctionEvent } from '@google-psat/common'; +import type Protocol from 'devtools-protocol'; + +/** + * This function parses the auction events to get interest group details wherever applicable. + * @param auctionEventsToBeParsed The auction events to be parsed to get the Interest Group Details. + * @returns The Interest group details for each event. + */ +function computeInterestGroupDetails( + auctionEventsToBeParsed: singleAuctionEvent[] +) { + if (!auctionEventsToBeParsed) { + return []; + } + + return Promise.all( + auctionEventsToBeParsed + .filter((event) => event.eventType === 'interestGroupAccessed') + .map(async (event) => { + if (!event?.name && !event?.ownerOrigin) { + return { + ...event, + details: {}, + }; + } + + const result = (await chrome.debugger.sendCommand( + { tabId: chrome.devtools.inspectedWindow.tabId }, + 'Storage.getInterestGroupDetails', + { + name: event?.name, + ownerOrigin: event?.ownerOrigin, + } + )) as Protocol.Storage.GetInterestGroupDetailsResponse; + + return { + ...event, + details: { + ...result.details, + }, + }; + }) + ); +} +export default computeInterestGroupDetails; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts new file mode 100644 index 0000000000..28891b9437 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts @@ -0,0 +1,215 @@ +/* + * Copyright 2024 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 { + MultiSellerAuction, + NoBidsType, + ReceivedBids, + SingleSellerAuction, +} from '@google-psat/common'; + +/** + * Internal dependencies + */ +import type { ProtectedAudienceContextType } from '../context'; + +const BIDDING_TYPES = [ + 'bid', + 'additionalBid', + 'topLevelBid', + 'topLevelAdditionalBid', +]; + +/** + * This function computes the received bids and no bids data to be displayed in the PSAT extension. + * @param _auctionEvents The array of auction events that are associated with the tab. + * @param _isMultiSellerAuction This speicifes if the auction event occuring is multi seller auction or single seller auction. + * @param refreshTabData If provided then it specifies the tab was reloaded. + * @returns null | receivedBids array and noBids object. + */ +function computeReceivedBidsAndNoBids( + _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], + _isMultiSellerAuction: boolean, + refreshTabData = false +) { + if (refreshTabData) { + return { receivedBids: [], noBids: {} }; + } + + if ( + !_auctionEvents || + (_auctionEvents && Object.keys(_auctionEvents).length === 0) + ) { + return null; + } + + const _receivedBids: ReceivedBids[] = []; + const _noBids: NoBidsType = {}; + + const _interestGroupBuyers = new Set(); + + if (_isMultiSellerAuction) { + const multisellerAuctionEvents = _auctionEvents as MultiSellerAuction; + + Object.keys(multisellerAuctionEvents).forEach((parentAuctionId) => { + Object.keys(multisellerAuctionEvents[parentAuctionId]).forEach( + (uniqueAuctionId) => { + if ('globalEvents' === uniqueAuctionId) { + return; + } + + const { auctionConfig, uniqueAuctionId: _uniqueAuctionId } = + multisellerAuctionEvents[parentAuctionId]?.[ + uniqueAuctionId + ]?.filter(({ type }) => type && type === 'configResolved')?.[0] ?? + {}; + + const { name } = + multisellerAuctionEvents[parentAuctionId]?.[uniqueAuctionId]?.find( + (event) => event?.eventType === 'interestGroupAccessed' + ) ?? {}; + + //@ts-ignore + auctionConfig?.interestGroupBuyers?.forEach((element) => { + _interestGroupBuyers.add(element); + }); + + const filteredEvents = multisellerAuctionEvents[parentAuctionId][ + uniqueAuctionId + ].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ); + + _receivedBids.push( + ...filteredEvents.map((event) => { + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + + return { + ...event, + mediaContainerSize: sellerSignals?.size, + adUnitCode: sellerSignals?.divId, + }; + }) + ); + + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); + + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + Array.from(_interestGroupBuyers.difference(buyersWhoBid)).forEach( + (buyer) => { + const auctionId = + uniqueAuctionId === '0' ? _uniqueAuctionId : uniqueAuctionId; + + if (!auctionId) { + return; + } + + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + + _noBids[auctionId] = { + ownerOrigin: buyer, + name: name ?? '', + uniqueAuctionId: auctionId, + adUnitCode: sellerSignals?.divId, + }; + } + ); + } + } + ); + }); + } else { + const singleSellerAuctionEvents = _auctionEvents as SingleSellerAuction; + + Object.keys(singleSellerAuctionEvents).forEach((uniqueAuctionId) => { + if ('globalEvents' === uniqueAuctionId) { + return; + } + + const filteredEvents = singleSellerAuctionEvents[uniqueAuctionId].filter( + (event) => + event.eventType === 'interestGroupAccessed' && + BIDDING_TYPES.includes(event.type) + ); + + const { auctionConfig } = + singleSellerAuctionEvents?.[uniqueAuctionId]?.filter( + ({ type }) => type && type === 'configResolved' + )?.[0] ?? {}; + + _receivedBids.push( + ...filteredEvents.map((event) => { + const sellerSignals = JSON.parse( + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + auctionConfig?.sellerSignals.value + ); + + return { + ...event, + mediaContainerSize: sellerSignals.size, + adUnitCode: sellerSignals?.divId, + }; + }) + ); + + const { name } = + singleSellerAuctionEvents[uniqueAuctionId]?.find( + (event) => event?.eventType === 'interestGroupAccessed' + ) ?? {}; + + if (_interestGroupBuyers.size > 0) { + const buyersWhoBid = new Set(); + + _receivedBids.forEach((event) => { + if (event.ownerOrigin) { + buyersWhoBid.add(event.ownerOrigin); + } + }); + + Array.from(_interestGroupBuyers.difference(buyersWhoBid)).forEach( + (buyer) => { + _noBids[uniqueAuctionId] = { + ownerOrigin: buyer, + name: name ?? '', + //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. + adUnitCode: JSON.parse(auctionConfig?.sellerSignals)?.divId, + uniqueAuctionId, + }; + } + ); + } + }); + } + return { receivedBids: _receivedBids, noBids: _noBids }; +} + +export default computeReceivedBidsAndNoBids; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/index.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/index.ts new file mode 100644 index 0000000000..1c93598a19 --- /dev/null +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 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 computeInterestGroupDetails } from './computeInterestGroupDetails'; +export { default as computeReceivedBidsAndNoBids } from './computeReceivedBidsAndNoBids'; From 786f8e5d88f9107d4a32417ad789c9418569a26a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 10 Sep 2024 20:12:38 +0530 Subject: [PATCH 31/40] Fix error of not updating. --- packages/extension/src/store/dataStore.ts | 2 +- .../protectedAudience/protectedAudienceProvider.tsx | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 4359cce9db..88dd071d92 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -602,7 +602,7 @@ class DataStore { tabId, auctionEvents: isMultiSellerAuction ? groupedAuctionBids : rest, multiSellerAuction: isMultiSellerAuction, - globalEvents: globalEvents, + globalEvents: globalEvents ?? [], }, }); } diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index a35bb5a254..6133d88edd 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -22,6 +22,7 @@ import React, { useState, useCallback, useMemo, + useRef, } from 'react'; import type { NoBidsType, @@ -46,6 +47,8 @@ const Provider = ({ children }: PropsWithChildren) => { const [isMultiSellerAuction, setIsMultiSellerAuction] = useState(false); + const globalEvents = useRef([]); + const [interestGroupDetails, setInterestGroupDetails] = useState< ProtectedAudienceContextType['state']['interestGroupDetails'] >([]); @@ -108,7 +111,13 @@ const Provider = ({ children }: PropsWithChildren) => { return prevState; }); - if (!didAuctionEventsChange) { + if ( + !didAuctionEventsChange && + !shouldUpdateState( + globalEvents.current, + message.payload.globalEvents + ) + ) { return; } @@ -119,6 +128,8 @@ const Provider = ({ children }: PropsWithChildren) => { message.payload.globalEvents ); + globalEvents.current = message.payload.globalEvents; + const computedBids: { receivedBids: ReceivedBids[]; noBids: NoBidsType; From 52efd2c996dfd6a60c6b01fdfb9d5f9e4e736ef4 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 30 Sep 2024 16:34:31 +0530 Subject: [PATCH 32/40] Fix undefined error. --- .../protectedAudience/utils/computeReceivedBidsAndNoBids.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts index 28891b9437..621f882d0b 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts @@ -101,7 +101,7 @@ function computeReceivedBidsAndNoBids( ...filteredEvents.map((event) => { const sellerSignals = JSON.parse( //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value + auctionConfig?.sellerSignals?.value ?? '{}' ); return { @@ -132,7 +132,7 @@ function computeReceivedBidsAndNoBids( const sellerSignals = JSON.parse( //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value + auctionConfig?.sellerSignals?.value ?? '{}' ); _noBids[auctionId] = { @@ -170,7 +170,7 @@ function computeReceivedBidsAndNoBids( ...filteredEvents.map((event) => { const sellerSignals = JSON.parse( //@ts-ignore -- since auction config is of type object but we know what data is being passed in this. - auctionConfig?.sellerSignals.value + auctionConfig?.sellerSignals?.value ?? '{}' ); return { From 9fea26ea99c503e30a1f7175688810450eee6115 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Mon, 30 Sep 2024 16:38:58 +0530 Subject: [PATCH 33/40] Remove logic to reset interest group data on page load or navigation. --- packages/extension/src/store/dataStore.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 88dd071d92..cd5a2983c5 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -319,9 +319,6 @@ class DataStore { this.tabsData[tabId] = {}; this.auctionEvents[tabId.toString()] = {}; - this.auctionEvents['globalEvents'] = { - interestGroupEvents: [], - }; this.tabs[tabId] = { url: '', devToolsOpenState: false, @@ -460,10 +457,12 @@ class DataStore { this.tabs[tabId].newUpdates = 0; this.tabs[tabId].frameIDURLSet = {}; this.tabs[tabId].parentChildFrameAssociation = {}; - this.auctionEvents[tabId.toString()] = {}; - this.auctionEvents['globalEvents'] = { - interestGroupEvents: [], - }; + Object.keys(this.auctionEvents[tabId.toString()]).forEach((key) => { + if (key === 'globalEvents') { + return; + } + delete this.auctionEvents[tabId.toString()][key]; + }); this.auctionDataForTabId[tabId] = {}; this.sendUpdatedDataToPopupAndDevTools(tabId, true); } From 30e1bc55cfe03d5e652e75070b9475c87e796f03 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 1 Oct 2024 18:43:17 +0530 Subject: [PATCH 34/40] Fix failing tests. --- .github/workflows/tests-cli-e2e-tests.yml | 5 ----- .github/workflows/tests-extension-e2e-tests.yml | 5 ----- 2 files changed, 10 deletions(-) diff --git a/.github/workflows/tests-cli-e2e-tests.yml b/.github/workflows/tests-cli-e2e-tests.yml index 1e66b3c2c9..f22c31f8b5 100644 --- a/.github/workflows/tests-cli-e2e-tests.yml +++ b/.github/workflows/tests-cli-e2e-tests.yml @@ -56,8 +56,3 @@ jobs: - name: Run the tests run: | npm run test:cli:e2e - - name: Archive build artifacts - uses: actions/upload-artifact@v2 - with: - name: my-artifact - path: cd tests/e2e-test/utils/ diff --git a/.github/workflows/tests-extension-e2e-tests.yml b/.github/workflows/tests-extension-e2e-tests.yml index 6935b4c4e8..8f346d808d 100644 --- a/.github/workflows/tests-extension-e2e-tests.yml +++ b/.github/workflows/tests-extension-e2e-tests.yml @@ -62,8 +62,3 @@ jobs: - name: Run the tests run: | npm run test:extension:e2e - - name: Archive build artifacts - uses: actions/upload-artifact@v2 - with: - name: my-artifact - path: cd tests/e2e-test/utils/ From 5eedfbed65dcbee989f4547387033ddfca3455ab Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 8 Oct 2024 13:14:43 +0530 Subject: [PATCH 35/40] Fix merge conflicts. --- packages/extension/src/store/PAStore.ts | 9 +++++++++ packages/extension/src/store/cookieStore.ts | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/store/PAStore.ts b/packages/extension/src/store/PAStore.ts index 639e9dadf3..495d6772ea 100644 --- a/packages/extension/src/store/PAStore.ts +++ b/packages/extension/src/store/PAStore.ts @@ -77,6 +77,7 @@ class PAStore { : undefined, eventType: 'interestGroupAuctionNetworkRequestCompleted', }); + dataStore.tabs[parseInt(tabId)].newUpdatesPA++; }); } @@ -121,6 +122,8 @@ class PAStore { this.getAuctionEventsArray(tabId, 'globalEvents').push(eventData); + dataStore.tabs[parseInt(tabId)].newUpdatesPA++; + if (!uniqueAuctionId) { return; } @@ -185,6 +188,8 @@ class PAStore { } this.getAuctionEventsArray(tabId, uniqueAuctionId).push(eventData); + + dataStore.tabs[parseInt(tabId)].newUpdatesPA++; } /** @@ -224,6 +229,8 @@ class PAStore { ?.parentAuctionId, eventType: 'interestGroupAuctionNetworkRequestCreated', }); + + dataStore.tabs[parseInt(tabId)].newUpdatesPA++; }); } @@ -257,6 +264,8 @@ class PAStore { }; this.getAuctionEventsArray(tabId, uniqueAuctionId).push(eventData); + + dataStore.tabs[parseInt(tabId)].newUpdatesPA++; } /** diff --git a/packages/extension/src/store/cookieStore.ts b/packages/extension/src/store/cookieStore.ts index bcc8d339d2..2bbf303f22 100644 --- a/packages/extension/src/store/cookieStore.ts +++ b/packages/extension/src/store/cookieStore.ts @@ -31,6 +31,7 @@ import type { Protocol } from 'devtools-protocol'; */ import updateCookieBadgeText from './utils/updateCookieBadgeText'; import dataStore from './dataStore'; +import shouldUpdateCounter from '../utils/shouldUpdateCounter'; class CookieStore { /** @@ -130,9 +131,9 @@ class CookieStore { dataStore.tabsData[tabId][cookieName].isBlocked = exclusionReasons.length > 0 ? true : false; - dataStore.tabs[tabId].newUpdates++; + dataStore.tabs[tabId].newUpdatesCA++; } else { - dataStore.tabs[tabId].newUpdates++; + dataStore.tabs[tabId].newUpdatesCA++; // If none of them exists. This case is possible when the cookies hasnt processed and we already have an issue. dataStore.tabsData[tabId] = { ...dataStore.tabsData[tabId], @@ -187,8 +188,16 @@ class CookieStore { ]) ).map((frameId) => frameId.toString()); + const updateCounterBoolean = shouldUpdateCounter( + dataStore.tabsData[tabId][cookieKey], + cookie + ); + + if (updateCounterBoolean) { + dataStore.tabs[tabId].newUpdatesCA++; + } + if (dataStore.tabsData[tabId]?.[cookieKey]) { - dataStore.tabs[tabId].newUpdates++; // Merge in previous warning reasons. const parsedCookie = { ...dataStore.tabsData[tabId][cookieKey].parsedCookie, @@ -228,6 +237,7 @@ class CookieStore { ...(cookie.networkEvents?.responseEvents || []), ], }; + dataStore.tabsData[tabId][cookieKey] = { ...dataStore.tabsData[tabId][cookieKey], ...cookie, @@ -248,7 +258,6 @@ class CookieStore { dataStore.tabsData[tabId][cookieKey]?.exemptionReason, }; } else { - dataStore.tabs[tabId].newUpdates++; dataStore.tabsData[tabId][cookieKey] = { ...cookie, blockingStatus: deriveBlockingStatus(cookie.networkEvents), From 228174b57061b1de5f7f517fb32d7e46e997e503 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 8 Oct 2024 19:38:17 +0530 Subject: [PATCH 36/40] Fix failing build. --- .../extension/src/utils/shouldUpdateState.ts | 31 ------------------- .../protectedAudienceProvider.tsx | 17 +++++----- 2 files changed, 7 insertions(+), 41 deletions(-) delete mode 100644 packages/extension/src/utils/shouldUpdateState.ts diff --git a/packages/extension/src/utils/shouldUpdateState.ts b/packages/extension/src/utils/shouldUpdateState.ts deleted file mode 100644 index e14fe69264..0000000000 --- a/packages/extension/src/utils/shouldUpdateState.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2024 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 { diff } from 'deep-object-diff'; - -/** - * This is a utility function that decided whether or not we should update the state. - * @param previousState The previous state of the state object. - * @param update The new state of the state object - * @returns boolean Whether or not to update the state. - */ -function shouldUpdateState(previousState: any, update: any) { - return Object.keys(diff(previousState, update)).length > 0; -} - -export default shouldUpdateState; diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index 6133d88edd..bdf576d6c1 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -34,11 +34,11 @@ import type { * Internal dependencies. */ import Context, { type ProtectedAudienceContextType } from './context'; -import shouldUpdateState from '../../../../utils/shouldUpdateState'; import { computeInterestGroupDetails, computeReceivedBidsAndNoBids, } from './utils'; +import { isEqual } from 'lodash-es'; const Provider = ({ children }: PropsWithChildren) => { const [auctionEvents, setAuctionEvents] = @@ -102,7 +102,7 @@ const Provider = ({ children }: PropsWithChildren) => { if ( prevState && message.payload.auctionEvents && - shouldUpdateState(prevState, message.payload.auctionEvents) + !isEqual(prevState, message.payload.auctionEvents) ) { didAuctionEventsChange = true; return message.payload.auctionEvents; @@ -113,10 +113,7 @@ const Provider = ({ children }: PropsWithChildren) => { if ( !didAuctionEventsChange && - !shouldUpdateState( - globalEvents.current, - message.payload.globalEvents - ) + isEqual(globalEvents.current, message.payload.globalEvents) ) { return; } @@ -172,26 +169,26 @@ const Provider = ({ children }: PropsWithChildren) => { ); setAdsAndBidders((prevState) => { - return shouldUpdateState(prevState, adUnitCodeToBidders) + return !isEqual(prevState, adUnitCodeToBidders) ? adUnitCodeToBidders : prevState; }); setReceivedBids((prevState) => { - return shouldUpdateState(prevState, computedBids.receivedBids) + return !isEqual(prevState, computedBids.receivedBids) ? computedBids.receivedBids : prevState; }); setNoBids((prevState) => { - return shouldUpdateState(prevState, computedBids.noBids) + return !isEqual(prevState, computedBids.noBids) ? computedBids.noBids : prevState; }); } setInterestGroupDetails((prevState) => { - return shouldUpdateState(prevState, shapedInterestGroupDetails) + return !isEqual(prevState, shapedInterestGroupDetails) ? shapedInterestGroupDetails : prevState; }); From 75a06f3b0721562ec7257b3f7def0ba7bb450c87 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Fri, 25 Oct 2024 11:18:25 +0530 Subject: [PATCH 37/40] Fix failing tests --- .../src/store/tests/synchnorousCookieStore.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/extension/src/store/tests/synchnorousCookieStore.ts b/packages/extension/src/store/tests/synchnorousCookieStore.ts index 08bd00158b..b721c3993e 100644 --- a/packages/extension/src/store/tests/synchnorousCookieStore.ts +++ b/packages/extension/src/store/tests/synchnorousCookieStore.ts @@ -54,14 +54,14 @@ describe('SynchnorousCookieStore:', () => { it('Should not update storage if there are no new cookies', () => { synchnorousCookieStore.update(123456, []); expect(Object.keys(dataStore.tabsData[123456]).length).toBe(0); - expect(dataStore.tabs[123456].newUpdates).toBe(0); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(0); }); it('Should add new cookie if cookie doesnt exist', () => { expect(dataStore.tabsData[123456]['_cbcnn.com/']).toBeUndefined(); synchnorousCookieStore.update(123456, [data.tabCookies['_cb']]); expect(dataStore.tabsData[123456]['_cbcnn.com/']).not.toBeUndefined(); - expect(dataStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(1); }); it('Should update cookie with new blocked reason', () => { @@ -74,7 +74,7 @@ describe('SynchnorousCookieStore:', () => { expect( dataStore.tabsData[123456]['_cbcnn.com/']?.blockedReasons?.length ).toBe(0); - expect(dataStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(1); synchnorousCookieStore.update(123456, [ { @@ -89,7 +89,7 @@ describe('SynchnorousCookieStore:', () => { expect(dataStore.tabsData[123456]['_cbcnn.com/'].blockedReasons).toContain( 'InvalidDomain' ); - expect(dataStore.tabs[123456].newUpdates).toBe(2); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(2); }); it('Should persist the partition key from the previous set data', () => { @@ -107,7 +107,7 @@ describe('SynchnorousCookieStore:', () => { expect( dataStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie?.partitionKey ).toBe('https://bbc.com'); - expect(dataStore.tabs[123456].newUpdates).toBe(1); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(1); synchnorousCookieStore.update(123456, [ { @@ -123,7 +123,7 @@ describe('SynchnorousCookieStore:', () => { expect( dataStore.tabsData[123456]['_cbcnn.com/']?.parsedCookie?.partitionKey ).toBe('https://bbc.com'); - expect(dataStore.tabs[123456].newUpdates).toBe(2); + expect(dataStore.tabs[123456].newUpdatesCA).toBe(2); }); it('Should not update the data if tabId is not present', () => { @@ -137,7 +137,7 @@ describe('SynchnorousCookieStore:', () => { }, }, ]); - expect(dataStore.tabs[12345]?.newUpdates).toBeUndefined(); + expect(dataStore.tabs[12345]?.newUpdatesCA).toBeUndefined(); }); it('Should clear cookie store when clear is called', () => { From 2100f6eeb2c0eef3efc1a5eba50f5ef14c6c6959 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 29 Oct 2024 13:07:21 +0530 Subject: [PATCH 38/40] Fix initial sync override. --- packages/extension/src/store/dataStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/store/dataStore.ts b/packages/extension/src/store/dataStore.ts index 66cc4c565c..f223a3b96b 100644 --- a/packages/extension/src/store/dataStore.ts +++ b/packages/extension/src/store/dataStore.ts @@ -517,7 +517,7 @@ class DataStore { this.tabs[tabId].newUpdatesCA > 0 || this.tabs[tabId].newUpdatesPA > 0) ) { - if (this.tabs[tabId].newUpdatesCA > 0) { + if (this.tabs[tabId].newUpdatesCA > 0 || overrideForInitialSync) { const newCookieData: { [cookieKey: string]: CookieData; } = {}; @@ -600,7 +600,7 @@ class DataStore { }); } - if (this.tabs[tabId].newUpdatesPA > 0) { + if (this.tabs[tabId].newUpdatesPA > 0 || overrideForInitialSync) { await chrome.runtime.sendMessage({ type: 'AUCTION_EVENTS', payload: { From b8ba3858660f2868563807b627ae9b941f679850 Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 29 Oct 2024 19:34:46 +0530 Subject: [PATCH 39/40] Fix failing tests. --- packages/cli/src/e2e-tests/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/e2e-tests/index.ts b/packages/cli/src/e2e-tests/index.ts index aaabd537b6..b9aba3af90 100644 --- a/packages/cli/src/e2e-tests/index.ts +++ b/packages/cli/src/e2e-tests/index.ts @@ -20,8 +20,8 @@ describe('CLI E2E Test', () => { it('Should run site analysis', () => { return coffee - .fork(cli, ['-u https://httpstatus.us/200', '-w 1000']) - .includes('stdout', '/out/httpstatus-us-200/report_') + .fork(cli, ['-u https://domain-aaa.com/', '-w 1000']) + .includes('stdout', '/out/domain-aaa-com/report_') .end(); }, 60000); }); From 649df0307622c7ae8ab0e9c84b98d96b6aa22b2a Mon Sep 17 00:00:00 2001 From: Amoghavarsha Kudaligi Date: Tue, 29 Oct 2024 19:45:04 +0530 Subject: [PATCH 40/40] Fix tab reload data refresh. --- .../protectedAudience/protectedAudienceProvider.tsx | 3 +-- .../utils/computeReceivedBidsAndNoBids.ts | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx index bdf576d6c1..00da9ae10a 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/protectedAudienceProvider.tsx @@ -132,8 +132,7 @@ const Provider = ({ children }: PropsWithChildren) => { noBids: NoBidsType; } | null = computeReceivedBidsAndNoBids( message.payload.auctionEvents, - message.payload.multiSellerAuction, - message.payload.refreshTabData + message.payload.multiSellerAuction ); if (computedBids) { diff --git a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts index 621f882d0b..e87361f632 100644 --- a/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts +++ b/packages/extension/src/view/devtools/stateProviders/protectedAudience/utils/computeReceivedBidsAndNoBids.ts @@ -39,18 +39,12 @@ const BIDDING_TYPES = [ * This function computes the received bids and no bids data to be displayed in the PSAT extension. * @param _auctionEvents The array of auction events that are associated with the tab. * @param _isMultiSellerAuction This speicifes if the auction event occuring is multi seller auction or single seller auction. - * @param refreshTabData If provided then it specifies the tab was reloaded. * @returns null | receivedBids array and noBids object. */ function computeReceivedBidsAndNoBids( _auctionEvents: ProtectedAudienceContextType['state']['auctionEvents'], - _isMultiSellerAuction: boolean, - refreshTabData = false + _isMultiSellerAuction: boolean ) { - if (refreshTabData) { - return { receivedBids: [], noBids: {} }; - } - if ( !_auctionEvents || (_auctionEvents && Object.keys(_auctionEvents).length === 0)