const form = document.getElementById('control-row'); const input = document.getElementById('input'); const message = document.getElementById('message'); const getCookieButton = document.getElementById('getCookiesButton'); const setCookieButton = document.getElementById('saveCookiesButton'); const cookieTextArea = document.getElementById('cookieInput'); const cookieDisplayArea = document.getElementById('cookieJson'); // The async IIFE is necessary because Chrome <89 does not support top level await. (async function initPopupWindow() { let [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (tab?.url) { try { let url = new URL(tab.url); input.value = url.hostname; } catch { // ignore } } input.focus(); })(); setCookieButton.addEventListener('click', async () => { const cookieData = JSON.parse(cookieTextArea.value) cookieData.map(async (cookie) => { try { let url = stringToUrl(input.value); cookie.domain = url.hostname cookie.url = `https://${url.hostname}${url.pathname}` console.log('cookie', cookie) delete cookie.hostOnly delete cookie.session await chrome.cookies.set(cookie) } catch (e) { console.log('e', e) } console.log('set this thing') }) }) getCookieButton.addEventListener('click', async () => { try { let url = stringToUrl(input.value); const cookies = await chrome.cookies.getAll({ domain: url.hostname }); if (cookies.length === 0) { return 'No cookies found'; } cookieDisplayArea.innerHTML = JSON.stringify(cookies, undefined, 2); } catch (error) { return `Unexpected error: ${error.message}`; } }) form.addEventListener('submit', handleFormSubmit); async function handleFormSubmit(event) { event.preventDefault(); clearMessage(); let url = stringToUrl(input.value); if (!url) { setMessage('Invalid URL'); return; } let message = await deleteDomainCookies(url.hostname); setMessage(message); } function stringToUrl(input) { // Start with treating the provided value as a URL try { return new URL(input); } catch { // ignore } // If that fails, try assuming the provided input is an HTTP host try { return new URL('http://' + input); } catch { // ignore } // If that fails ¯\_(ツ)_/¯ return null; } async function deleteDomainCookies(domain) { let cookiesDeleted = 0; try { const cookies = await chrome.cookies.getAll({ domain }); if (cookies.length === 0) { return 'No cookies found'; } let pending = cookies.map(deleteCookie); await Promise.all(pending); cookiesDeleted = pending.length; } catch (error) { return `Unexpected error: ${error.message}`; } return `Deleted ${cookiesDeleted} cookie(s).`; } function deleteCookie(cookie) { // Cookie deletion is largely modeled off of how deleting cookies works when using HTTP headers. // Specific flags on the cookie object like `secure` or `hostOnly` are not exposed for deletion // purposes. Instead, cookies are deleted by URL, name, and storeId. Unlike HTTP headers, though, // we don't have to delete cookies by setting Max-Age=0; we have a method for that ;) // // To remove cookies set with a Secure attribute, we must provide the correct protocol in the // details object's `url` property. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Secure const protocol = cookie.secure ? 'https:' : 'http:'; // Note that the final URL may not be valid. The domain value for a standard cookie is prefixed // with a period (invalid) while cookies that are set to `cookie.hostOnly == true` do not have // this prefix (valid). // https://developer.chrome.com/docs/extensions/reference/cookies/#type-Cookie const cookieUrl = `${protocol}//${cookie.domain}${cookie.path}`; return chrome.cookies.remove({ url: cookieUrl, name: cookie.name, storeId: cookie.storeId }); } function setMessage(str) { message.textContent = str; message.hidden = false; } function clearMessage() { message.hidden = true; message.textContent = ''; }