Repository: GoogleChromeLabs/ps-analysis-tool
Pull request #57 (2023-07-10 15:35:16)

Fix: Empty cookie list
## Description

Cookie list and the popup summary displays no cookies intermittently. Changes in this PR fixes this issue by moving `tabUrl` management to state providers to display layer.

## Relevant Technical Choices
- Separate state providers for devtools and popup ui.
- Devtools state provider uses `chrome.devetools.inspectedWindow.eval` to get the tabs url reliably.
- Popup UI state provider uses previously used `getCurrentTab` utility to get a tabs url reliably.
- Popup UI state provider now calculates the cookie stats rather than the presentational component.
- Remove tab url management related functions from `CookieStore`.
- Remove event handling required for `tabUrl` management. ( State provider for devtools and pop up do that now )

<!-- Please describe your changes. -->

## Testing Instructions

- Install the tool as mentioned in the README.md file.
- Pin the popup icon in the browser.
- Open multiple tabs with different sites and open the devtools in some of them. 
- Cookie count on the icon badge, piechart in the popup and list in the devtools should have consistent data on every tab.

<!--
How can the changes in this PR be verified?
Please provide step-by-step instructions to test the changes.
-->

## Additional Information:

<!-- Any other information. -->

## Screenshot/Screencast

<!-- Please provide Screenshot/Screencast, if applicable -->

#### Video of broken functionality
https://github.com/GoogleChromeLabs/ps-analysis-tool/assets/53055971/5cae3db3-a9cb-41e3-ae51-af13b9e5ddab


---

<!--
Example:

Fixes #123
Partially addresses #22
See #834
-->

Fixes #



Patch:
diff --git a/packages/extension/src/localStore/consts.ts b/packages/extension/src/localStore/consts.ts
deleted file mode 100644
index 21ec6dfd..00000000
--- a/packages/extension/src/localStore/consts.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * Internal dependencies.
- */
-import { type TabData } from './types';
-
-export const EMPTY_TAB_DATA: TabData = {
-  cookies: {},
-  url: undefined,
-  focusedAt: undefined,
-};
diff --git a/packages/extension/src/localStore/cookieStore.ts b/packages/extension/src/localStore/cookieStore.ts
index 1a59db96..daff7984 100644
--- a/packages/extension/src/localStore/cookieStore.ts
+++ b/packages/extension/src/localStore/cookieStore.ts
@@ -18,7 +18,6 @@
  */
 import updateStorage from './updateStorage';
 import type { TabData, CookieData } from './types';
-import { getCurrentTab } from '../utils/getCurrentTabId';
 
 const CookieStore = {
   /**
@@ -28,9 +27,6 @@ const CookieStore = {
    */
   async update(tabId: string, cookies: CookieData[]) {
     const newCookies: { [key: string]: CookieData } = {};
-    const [tabData] = await getCurrentTab();
-
-    const tabUrl = tabData?.url;
 
     for (const cookie of cookies) {
       if (cookie) {
@@ -46,27 +42,11 @@ const CookieStore = {
 
       return {
         ...prevState,
-        url: tabUrl,
         cookies: updatedCookies,
       };
     });
   },
 
-  /**
-   * Update tab location.
-   * @param {string} tabId Tab id.
-   * @param {string} url Tab url.
-   * @param {number} focusedAt The timestamp, when the tab was focused.
-   */
-  async updateTabLocation(tabId: string, url: string, focusedAt: number) {
-    await updateStorage(tabId, (prevState: TabData) => ({
-      ...prevState,
-      cookies: {},
-      url,
-      focusedAt,
-    }));
-  },
-
   /**
    * Update the focusedAt timestamp for the tab.
    * @param {string} tabId The active tab id.
@@ -80,6 +60,19 @@ const CookieStore = {
     await chrome.storage.local.set(storage);
   },
 
+  /**
+   * creates an entry for a tab
+   * @param {string} tabId The tab id.
+   */
+  async addTabData(tabId: string) {
+    await chrome.storage.local.set({
+      [tabId]: {
+        cookies: {},
+        focusedAt: Date.now(),
+      },
+    });
+  },
+
   /**
    * Remove the tab data from the store.
    * @param {string} tabId The tab id.
diff --git a/packages/extension/src/localStore/index.ts b/packages/extension/src/localStore/index.ts
index 84225334..d869da50 100644
--- a/packages/extension/src/localStore/index.ts
+++ b/packages/extension/src/localStore/index.ts
@@ -13,6 +13,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-export * from './consts';
 export * from './types';
 export { default as CookieStore } from './cookieStore';
diff --git a/packages/extension/src/localStore/types.ts b/packages/extension/src/localStore/types.ts
index 8efd6503..38fe72b6 100644
--- a/packages/extension/src/localStore/types.ts
+++ b/packages/extension/src/localStore/types.ts
@@ -34,8 +34,7 @@ export type TabData = {
   cookies: {
     [key: string]: CookieData;
   } | null;
-  url: string | undefined;
-  focusedAt: number | undefined;
+  focusedAt: number | null;
 };
 
 export type Storage = {
diff --git a/packages/extension/src/manifest.json b/packages/extension/src/manifest.json
index ebe9f351..2ba02ddc 100644
--- a/packages/extension/src/manifest.json
+++ b/packages/extension/src/manifest.json
@@ -9,7 +9,13 @@
     "48": "icons/icon-48.png",
     "128": "icons/icon-128.png"
   },
-  "permissions": ["storage", "tabs", "webNavigation", "webRequest"],
+  "permissions": [
+    "storage",
+    "tabs",
+    "webNavigation",
+    "webRequest",
+    "unlimitedStorage"
+  ],
   "host_permissions": ["*://*/*"],
   "devtools_page": "devtools/devtools.html",
   "background": {
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieList.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieList.tsx
index 2eb4562a..9cd73a83 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieList.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/components/cookieList.tsx
@@ -28,7 +28,7 @@ interface CoookieListProps {
   cookies: {
     [key: string]: CookieData;
   };
-  tabUrl: string | undefined;
+  tabUrl: string | null;
   selectedKey: string | null;
   onClickItem: (key: string) => void;
 }
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
index 9987b691..b766ba94 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/index.tsx
@@ -21,14 +21,14 @@ import React, { useEffect, useState } from 'react';
 /**
  * Internal dependencies.
  */
-import { useCookieStore } from '../../../../stateProviders/syncCookieStore';
+import { useCookieStore } from '../../../stateProviders/syncCookieStore';
 import { CookieList, CookieDetails } from './components';
 import type { CookieData } from '../../../../../localStore';
 
 const Cookies = () => {
   const { cookies, tabUrl } = useCookieStore(({ state }) => ({
-    cookies: state?.cookies,
-    tabUrl: state?.url,
+    cookies: state.tabCookies,
+    tabUrl: state.tabUrl,
   }));
 
   const [selectedKey, setSelectedKey] = useState<string | null>(null);
diff --git a/packages/extension/src/view/devtools/index.tsx b/packages/extension/src/view/devtools/index.tsx
index 603a7f64..feb0b809 100644
--- a/packages/extension/src/view/devtools/index.tsx
+++ b/packages/extension/src/view/devtools/index.tsx
@@ -23,7 +23,7 @@ import { createRoot } from 'react-dom/client';
  * Internal dependencies.
  */
 import App from './app';
-import { Provider as ExternalStoreProvider } from '../stateProviders/syncCookieStore';
+import { Provider as ExternalStoreProvider } from './stateProviders/syncCookieStore';
 
 const root = document.getElementById('root');
 
diff --git a/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx
new file mode 100644
index 00000000..7140d152
--- /dev/null
+++ b/packages/extension/src/view/devtools/stateProviders/syncCookieStore/index.tsx
@@ -0,0 +1,137 @@
+/*
+ * 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, createContext } from 'use-context-selector';
+import React, {
+  type PropsWithChildren,
+  useEffect,
+  useState,
+  useCallback,
+} from 'react';
+
+/**
+ * Internal dependencies.
+ */
+import type { CookieData } from '../../../../localStore';
+
+export interface CookieStoreContext {
+  state: {
+    tabCookies: {
+      [key: string]: CookieData;
+    } | null;
+    tabUrl: string | null;
+  };
+  actions: object;
+}
+
+const initialState: CookieStoreContext = {
+  state: {
+    tabCookies: null,
+    tabUrl: null,
+  },
+  actions: {},
+};
+
+export const Context = createContext<CookieStoreContext>(initialState);
+
+export const Provider = ({ children }: PropsWithChildren) => {
+  const [tabId, setTabId] = useState<number | null>(null);
+
+  const [tabCookies, setTabCookies] =
+    useState<CookieStoreContext['state']['tabCookies']>(null);
+
+  const [tabUrl, setTabUrl] =
+    useState<CookieStoreContext['state']['tabUrl']>(null);
+
+  const intitialSync = useCallback(async () => {
+    const _tabId = chrome.devtools.inspectedWindow.tabId;
+    setTabId(_tabId);
+
+    const tabData = (await chrome.storage.local.get([_tabId.toString()]))[
+      _tabId.toString()
+    ];
+
+    if (tabData && tabData.cookies) {
+      setTabCookies(tabData.cookies);
+    }
+
+    chrome.devtools.inspectedWindow.eval(
+      'window.location.href',
+      (result, isException) => {
+        if (!isException && typeof result === 'string') {
+          setTabUrl(result);
+        }
+      }
+    );
+  }, []);
+
+  const storeChangeListener = useCallback(
+    (changes: { [key: string]: chrome.storage.StorageChange }) => {
+      if (
+        tabId &&
+        Object.keys(changes).includes(tabId.toString()) &&
+        changes[tabId.toString()]?.newValue?.cookies
+      ) {
+        setTabCookies(changes[tabId.toString()].newValue.cookies);
+      }
+    },
+    [tabId]
+  );
+
+  const tabUpdateListener = useCallback(
+    (_tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => {
+      if (tabId === _tabId && changeInfo.url) {
+        setTabUrl(changeInfo.url);
+      }
+    },
+    [tabId]
+  );
+
+  useEffect(() => {
+    intitialSync();
+    chrome.storage.local.onChanged.addListener(storeChangeListener);
+    chrome.tabs.onUpdated.addListener(tabUpdateListener);
+    return () => {
+      chrome.storage.local.onChanged.removeListener(storeChangeListener);
+      chrome.tabs.onUpdated.removeListener(tabUpdateListener);
+    };
+  }, [intitialSync, storeChangeListener, tabUpdateListener]);
+
+  return (
+    <Context.Provider value={{ state: { tabCookies, tabUrl }, actions: {} }}>
+      {children}
+    </Context.Provider>
+  );
+};
+
+export function useCookieStore(): CookieStoreContext;
+export function useCookieStore<T>(
+  selector: (state: CookieStoreContext) => T
+): T;
+
+/**
+ * Cookie store hook.
+ * @param selector Selector function to partially select state.
+ * @returns selected part of the state
+ */
+export function useCookieStore<T>(
+  selector: (state: CookieStoreContext) => T | CookieStoreContext = (state) =>
+    state
+) {
+  return useContextSelector(Context, selector);
+}
diff --git a/packages/extension/src/view/popup/app.tsx b/packages/extension/src/view/popup/app.tsx
index b13bf3e1..44d11409 100644
--- a/packages/extension/src/view/popup/app.tsx
+++ b/packages/extension/src/view/popup/app.tsx
@@ -24,18 +24,34 @@ import React from 'react';
  */
 import './app.css';
 import { PieChart, Legend } from './components';
-import { useCookieStore } from '../stateProviders/syncCookieStore';
-import countCookiesByCategory from '../../utils/countCookiesByCategory';
+import { useCookieStore } from './stateProviders/syncCookieStore';
 import { COLOR_MAP } from './const';
 
 const App: React.FC = () => {
-  const { cookies, tabURL } = useCookieStore(({ state }) => ({
-    cookies: state?.cookies,
-    tabURL: state?.url,
+  const { cookieStats } = useCookieStore(({ state }) => ({
+    cookieStats: state.tabCookieStats,
   }));
 
-  const cookieStats = countCookiesByCategory(cookies, tabURL || '');
+  if (!cookieStats) {
+    return (
+      <div className="w-96 h-80 flex justify-center items-center flex-col">
+        <p className="font-bold text-lg">
+          Please refresh this page to view cookies
+        </p>
+      </div>
+    );
+  }
 
+  if (
+    cookieStats.firstParty.total === 0 &&
+    cookieStats.thirdParty.total === 0
+  ) {
+    return (
+      <div className="w-96 h-80 flex justify-center items-center flex-col">
+        <p className="font-bold text-lg">No cookies found on this page</p>
+      </div>
+    );
+  }
   const legendData = [
     {
       label: 'Functional',
@@ -103,68 +119,52 @@ const App: React.FC = () => {
   ];
 
   return (
-    <>
-      {cookieStats?.firstParty.total && cookieStats?.thirdParty.total ? (
-        <div className="w-96 h-80 flex justify-center items-center flex-col">
-          <div className="w-full flex-1 flex gap-16 pt-6 px-12">
-            <div className="w-full h-full flex flex-col justify-center items-center">
-              <div className="flex-1 w-full">
-                {cookieStats.firstParty.total ? (
-                  <PieChart
-                    centerCount={cookieStats.firstParty.total}
-                    data={firstPartyPiechartData}
-                  />
-                ) : (
-                  <div className="w-full h-full flex justify-center items-center">
-                    <h1 className="text-center">
-                      First Party Cookies Not Found
-                    </h1>
-                  </div>
-                )}
+    <div className="w-96 h-80 flex justify-center items-center flex-col">
+      <div className="w-full flex-1 flex gap-16 pt-6 px-12">
+        <div className="w-full h-full flex flex-col justify-center items-center">
+          <div className="flex-1 w-full">
+            {cookieStats.firstParty.total ? (
+              <PieChart
+                centerCount={cookieStats.firstParty.total}
+                data={firstPartyPiechartData}
+              />
+            ) : (
+              <div className="w-full h-full flex justify-center items-center">
+                <h1 className="text-center">First Party Cookies Not Found</h1>
               </div>
-              {cookieStats.firstParty.total ? (
-                <p className="font-bold text-xs">1st Party Cookies</p>
-              ) : null}
-            </div>
-            <div className="w-full h-full flex flex-col justify-center items-center">
-              <div className="flex-1 w-full h-full">
-                {cookieStats.thirdParty.total ? (
-                  <PieChart
-                    centerCount={cookieStats.thirdParty.total}
-                    data={thirdPartyPiechartData}
-                  />
-                ) : (
-                  <div className="w-full h-full flex justify-center items-center">
-                    <h1 className="text-center">
-                      Third Party Cookies Not Found
-                    </h1>
-                  </div>
-                )}
-              </div>
-              {cookieStats.thirdParty.total ? (
-                <p className="font-bold text-xs">3rd Party Cookies</p>
-              ) : null}
-            </div>
-          </div>
-          <div className="mt-3">
-            <Legend legendItemList={legendData} />
-          </div>
-          <div className="w-full text-center mt-5 px-3 mb-3">
-            <p className="text-chart-label text-xs">
-              {'Inspect cookies in the "Privacy Sandbox" panel of DevTools'}
-            </p>
+            )}
           </div>
+          {cookieStats.firstParty.total ? (
+            <p className="font-bold text-xs">1st Party Cookies</p>
+          ) : null}
         </div>
-      ) : (
-        <div className="w-96 h-80 flex justify-center items-center flex-col">
-          <p className="font-bold text-lg">
-            {cookies === null
-              ? 'Please refresh this page to view cookies'
-              : 'No cookies found on this page'}
-          </p>
+        <div className="w-full h-full flex flex-col justify-center items-center">
+          <div className="flex-1 w-full h-full">
+            {cookieStats.thirdParty.total ? (
+              <PieChart
+                centerCount={cookieStats.thirdParty.total}
+                data={thirdPartyPiechartData}
+              />
+            ) : (
+              <div className="w-full h-full flex justify-center items-center">
+                <h1 className="text-center">Third Party Cookies Not Found</h1>
+              </div>
+            )}
+          </div>
+          {cookieStats.thirdParty.total ? (
+            <p className="font-bold text-xs">3rd Party Cookies</p>
+          ) : null}
         </div>
-      )}
-    </>
+      </div>
+      <div className="mt-3">
+        <Legend legendItemList={legendData} />
+      </div>
+      <div className="w-full text-center mt-5 px-3 mb-3">
+        <p className="text-chart-label text-xs">
+          {'Inspect cookies in the "Privacy Sandbox" panel of DevTools'}
+        </p>
+      </div>
+    </div>
   );
 };
 
diff --git a/packages/extension/src/view/popup/index.tsx b/packages/extension/src/view/popup/index.tsx
index 24f5061c..d7162833 100644
--- a/packages/extension/src/view/popup/index.tsx
+++ b/packages/extension/src/view/popup/index.tsx
@@ -24,7 +24,7 @@ import { createRoot } from 'react-dom/client';
  * Internal dependencies.
  */
 import App from './app';
-import { Provider as ExternalStoreProvider } from '../stateProviders/syncCookieStore';
+import { Provider as ExternalStoreProvider } from './stateProviders/syncCookieStore';
 
 const root = document.getElementById('root');
 
diff --git a/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx
new file mode 100644
index 00000000..28fb9c00
--- /dev/null
+++ b/packages/extension/src/view/popup/stateProviders/syncCookieStore/index.tsx
@@ -0,0 +1,154 @@
+/*
+ * 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, createContext } from 'use-context-selector';
+import React, {
+  type PropsWithChildren,
+  useEffect,
+  useState,
+  useCallback,
+} from 'react';
+
+/**
+ * Internal dependencies.
+ */
+import { getCurrentTab } from '../../../../utils/getCurrentTabId';
+import type { CookieStats } from '../../types';
+import countCookiesByCategory from '../../../../utils/countCookiesByCategory';
+
+export interface CookieStoreContext {
+  state: {
+    tabCookieStats: CookieStats | null;
+  };
+  actions: object;
+}
+
+const initialState: CookieStoreContext = {
+  state: {
+    tabCookieStats: {
+      total: 0,
+      firstParty: {
+        total: 0,
+        functional: 0,
+        marketing: 0,
+        analytics: 0,
+        uncategorised: 0,
+      },
+      thirdParty: {
+        total: 0,
+        functional: 0,
+        marketing: 0,
+        analytics: 0,
+        uncategorised: 0,
+      },
+    },
+  },
+  actions: {},
+};
+
+export const Context = createContext<CookieStoreContext>(initialState);
+
+export const Provider = ({ children }: PropsWithChildren) => {
+  const [tabId, setTabId] = useState<number | null>(null);
+
+  const [tabUrl, setTabUrl] = useState<string | null>(null);
+
+  const [tabCookieStats, setTabCookieStats] =
+    useState<CookieStoreContext['state']['tabCookieStats']>(null);
+
+  const intitialSync = useCallback(async () => {
+    const [tab] = await getCurrentTab();
+    if (!tab.id || !tab.url) {
+      return;
+    }
+    const _tabId = tab.id;
+    const _tabUrl = tab.url;
+
+    setTabId(tab.id);
+    setTabUrl(tab.url);
+
+    const tabData = (await chrome.storage.local.get([_tabId.toString()]))[
+      _tabId.toString()
+    ];
+
+    if (tabData && tabData.cookies) {
+      setTabCookieStats(countCookiesByCategory(tabData.cookies, _tabUrl));
+    }
+  }, []);
+
+  const storeChangeListener = useCallback(
+    (changes: { [key: string]: chrome.storage.StorageChange }) => {
+      if (
+        tabId &&
+        tabUrl &&
+        Object.keys(changes).includes(tabId.toString()) &&
+        changes[tabId.toString()]?.newValue?.cookies
+      ) {
+        setTabCookieStats(
+          countCookiesByCategory(
+            changes[tabId.toString()].newValue.cookies,
+            tabUrl
+          )
+        );
+      }
+    },
+    [tabId, tabUrl]
+  );
+
+  const tabUpdateListener = useCallback(
+    (_tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => {
+      if (tabId === _tabId && changeInfo.url) {
+        setTabUrl(changeInfo.url);
+      }
+    },
+    [tabId]
+  );
+
+  useEffect(() => {
+    intitialSync();
+    chrome.storage.local.onChanged.addListener(storeChangeListener);
+    chrome.tabs.onUpdated.addListener(tabUpdateListener);
+    return () => {
+      chrome.storage.local.onChanged.removeListener(storeChangeListener);
+      chrome.tabs.onUpdated.removeListener(tabUpdateListener);
+    };
+  }, [intitialSync, storeChangeListener, tabUpdateListener]);
+
+  return (
+    <Context.Provider value={{ state: { tabCookieStats }, actions: {} }}>
+      {children}
+    </Context.Provider>
+  );
+};
+
+export function useCookieStore(): CookieStoreContext;
+export function useCookieStore<T>(
+  selector: (state: CookieStoreContext) => T
+): T;
+
+/**
+ * Cookie store hook.
+ * @param selector Selector function to partially select state.
+ * @returns selected part of the state
+ */
+export function useCookieStore<T>(
+  selector: (state: CookieStoreContext) => T | CookieStoreContext = (state) =>
+    state
+) {
+  return useContextSelector(Context, selector);
+}
diff --git a/packages/extension/src/view/stateProviders/syncCookieStore/index.tsx b/packages/extension/src/view/stateProviders/syncCookieStore/index.tsx
deleted file mode 100644
index d2d69d3b..00000000
--- a/packages/extension/src/view/stateProviders/syncCookieStore/index.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2023 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * External dependencies.
- */
-import { useContextSelector, createContext } from 'use-context-selector';
-import React, {
-  type PropsWithChildren,
-  useEffect,
-  useState,
-  useCallback,
-} from 'react';
-
-/**
- * Internal dependencies.
- */
-import { type TabData, EMPTY_TAB_DATA } from '../../../localStore';
-import { getCurrentTabId } from '../../../utils/getCurrentTabId';
-
-export interface ICookieStoreContext {
-  state: TabData;
-  actions: object;
-}
-
-const initialState: ICookieStoreContext = {
-  state: EMPTY_TAB_DATA,
-  actions: {},
-};
-
-export const Context = createContext<ICookieStoreContext>(initialState);
-
-export const Provider = ({ children }: PropsWithChildren) => {
-  const [state, setState] = useState<TabData>(EMPTY_TAB_DATA);
-
-  const syncState = useCallback(async () => {
-    const tabId = await getCurrentTabId();
-
-    if (!tabId) {
-      return;
-    }
-
-    const tabData = await chrome.storage.local.get();
-
-    const tabInfo = tabData?.[tabId];
-
-    if (!tabInfo) {
-      setState({
-        ...EMPTY_TAB_DATA,
-        cookies: null,
-      });
-    } else if (tabInfo?.cookies && tabInfo?.url) {
-      setState({ ...EMPTY_TAB_DATA, ...tabInfo });
-    }
-  }, []);
-
-  useEffect(() => {
-    syncState();
-
-    chrome.storage.onChanged.addListener(syncState);
-
-    return () => {
-      chrome.storage.onChanged.removeListener(syncState);
-    };
-  }, [syncState]);
-
-  return (
-    <Context.Provider value={{ state, actions: {} }}>
-      {children}
-    </Context.Provider>
-  );
-};
-
-export function useCookieStore(): ICookieStoreContext;
-export function useCookieStore<T>(
-  selector: (state: ICookieStoreContext) => T
-): T;
-
-/**
- * Cookie store hook.
- * @param selector Selector function to partially select state.
- * @returns selected part of the state
- */
-export function useCookieStore<T>(
-  selector: (state: ICookieStoreContext) => T | ICookieStoreContext = (state) =>
-    state
-) {
-  return useContextSelector(Context, selector);
-}
diff --git a/packages/extension/src/worker/service-worker.ts b/packages/extension/src/worker/service-worker.ts
index bdcf3624..113712a4 100644
--- a/packages/extension/src/worker/service-worker.ts
+++ b/packages/extension/src/worker/service-worker.ts
@@ -25,9 +25,6 @@ import {
   fetchDictionary,
 } from '../utils/fetchCookieDictionary';
 
-//clear data from previous sessions or version of the extension ( or when extension is refreshed )
-chrome.storage.local.clear();
-
 let cookieDB: CookieDatabase | null = null;
 
 /**
@@ -119,20 +116,9 @@ chrome.webRequest.onBeforeSendHeaders.addListener(
   ['extraHeaders', 'requestHeaders']
 );
 
-/**
- * Update tab metadata when the browser is about to navigate to a new page.
- * @see https://developer.chrome.com/docs/extensions/reference/webNavigation/
- */
-chrome.webNavigation.onBeforeNavigate.addListener((details) => {
-  const { tabId, url, frameType } = details;
-
-  if (url && frameType === 'outermost_frame') {
-    // Updates the location of the tab in the cookies object.
-    CookieStore.updateTabLocation(
-      tabId.toString(),
-      new URL(url).origin,
-      Date.now()
-    );
+chrome.tabs.onCreated.addListener(async (tab) => {
+  if (tab.id) {
+    await CookieStore.addTabData(tab.id.toString());
   }
 });
 


Test patch:
diff --git a/packages/extension/src/localStore/tests/cookieStore.ts b/packages/extension/src/localStore/tests/cookieStore.ts
index 60dd03a1..4d6484fb 100644
--- a/packages/extension/src/localStore/tests/cookieStore.ts
+++ b/packages/extension/src/localStore/tests/cookieStore.ts
@@ -108,7 +108,7 @@ describe('local store: CookieStore', () => {
     chrome.storage.local.clear();
 
     //mock navigation to a URL
-    CookieStore.updateTabLocation('123', 'https://example.com', Date.now());
+    CookieStore.addTabData('123');
   });
 
   it('should add/update tab data', async () => {
@@ -119,15 +119,6 @@ describe('local store: CookieStore', () => {
     });
   });
 
-  it('should update tab location and clear cookies', async () => {
-    await CookieStore.update('123', cookieArray);
-
-    const now = Date.now();
-    await CookieStore.updateTabLocation('123', 'https://new_site.com', now);
-
-    expect(storage['123'].cookies).toStrictEqual({});
-  });
-
   it('should update tab foucusedAt value', async () => {
     await CookieStore.update('123', cookieArray);
     const prevFocusedAt = storage['123'].focusedAt;
diff --git a/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx b/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
index f7f4d44c..a9d9d36d 100644
--- a/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
+++ b/packages/extension/src/view/devtools/components/tabContent/cookies/tests/cookieTab.tsx
@@ -25,7 +25,7 @@ import { type Cookie as ParsedCookie } from 'simple-cookie';
  * External dependencies.
  */
 import CookieTab from '..';
-import type { TabData } from '../../../../../../localStore';
+import type { CookieStoreContext } from '../../../../stateProviders/syncCookieStore';
 
 const uncategorised1pCookie: ParsedCookie = {
   name: '_cb',
@@ -52,11 +52,10 @@ const known3pCookie: ParsedCookie = {
 };
 
 const mockResponse: {
-  cookies: NonNullable<TabData['cookies']>;
-  url: NonNullable<TabData['url']>;
-  focusedAt: NonNullable<TabData['focusedAt']>;
+  tabCookies: NonNullable<CookieStoreContext['state']['tabCookies']>;
+  tabUrl: NonNullable<CookieStoreContext['state']['tabUrl']>;
 } = {
-  cookies: {
+  tabCookies: {
     [uncategorised1pCookie.name]: {
       parsedCookie: uncategorised1pCookie,
       analytics: null,
@@ -104,14 +103,13 @@ const mockResponse: {
       headerType: 'response',
     },
   },
-  url: 'https://edition.cnn.com/',
-  focusedAt: 123,
+  tabUrl: 'https://edition.cnn.com/',
 };
 
-jest.mock('../../../../../stateProviders/syncCookieStore', () => {
+jest.mock('../../../../stateProviders/syncCookieStore', () => {
   return {
     useCookieStore: () => {
-      return { cookies: mockResponse.cookies, tabUrl: mockResponse.url };
+      return { cookies: mockResponse.tabCookies, tabUrl: mockResponse.tabUrl };
     },
   };
 });
@@ -136,7 +134,7 @@ describe('CookieTab', () => {
     expect(card).toBeInTheDocument();
 
     const firstCookie =
-      mockResponse.cookies[Object.keys(mockResponse.cookies)[0]];
+      mockResponse.tabCookies[Object.keys(mockResponse.tabCookies)[0]];
 
     expect(
       within(card).getByText(firstCookie.parsedCookie.name)
@@ -154,7 +152,7 @@ describe('CookieTab', () => {
     expect(card).toBeInTheDocument();
 
     const thirdCookie =
-      mockResponse.cookies[Object.keys(mockResponse.cookies)[2]];
+      mockResponse.tabCookies[Object.keys(mockResponse.tabCookies)[2]];
 
     expect(
       within(card).getByText(`${thirdCookie.parsedCookie.name}`)