From e5e9452e273c5305bf2bebad524fa37d1b349ca3 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 04:30:01 +0900 Subject: [PATCH 01/14] change: use es modules --- src/@types/index.d.ts | 5 + src/{background.js => background.mjs} | 22 +---- src/manifest.json | 3 +- src/modules/cookie_formatter.mjs | 38 +++++++ src/modules/get_all_cookies.mjs | 6 ++ src/popup.html | 2 +- src/popup.js | 136 -------------------------- src/popup.mjs | 106 ++++++++++++++++++++ src/types.d.ts | 19 ---- 9 files changed, 159 insertions(+), 178 deletions(-) create mode 100644 src/@types/index.d.ts rename src/{background.js => background.mjs} (69%) create mode 100644 src/modules/cookie_formatter.mjs create mode 100644 src/modules/get_all_cookies.mjs delete mode 100644 src/popup.js create mode 100644 src/popup.mjs delete mode 100644 src/types.d.ts diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts new file mode 100644 index 0000000..d4574b0 --- /dev/null +++ b/src/@types/index.d.ts @@ -0,0 +1,5 @@ +type Format = { + ext: string; + mimeType: string; + serializer: (cookies: chrome.cookies.Cookie[]) => string; +}; diff --git a/src/background.js b/src/background.mjs similarity index 69% rename from src/background.js rename to src/background.mjs index b1fd98f..e50dd73 100644 --- a/src/background.js +++ b/src/background.mjs @@ -1,24 +1,4 @@ -/** - * Retrieves both cookies with and without a partition key and returns the merged list - * @param {chrome.cookies.GetAllDetails} details - * @returns {chrome.cookies.Cookie[]} - */ -const getAllCookies = async (details) => { - const { partitionKey, ...detailsWithoutPartitionKey } = details; - const cookiesWithPartitionKey = partitionKey ? await chrome.cookies.getAll(details) : []; - const cookies = await chrome.cookies.getAll(detailsWithoutPartitionKey); - return [...cookies, ...cookiesWithPartitionKey]; -}; - -// Listen for messages from popup.js -chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - if (message.action === 'requestAllCookies') { - const { details } = message; - getAllCookies(details).then(sendResponse); - return true; - } - return false; -}); +import getAllCookies from './modules/get_all_cookies.mjs'; /** * Update icon badge counter on active page diff --git a/src/manifest.json b/src/manifest.json index d8bd58f..49e2778 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -22,6 +22,7 @@ }, "incognito": "split", "background": { - "service_worker": "background.js" + "service_worker": "background.mjs", + "type": "module" } } diff --git a/src/modules/cookie_formatter.mjs b/src/modules/cookie_formatter.mjs new file mode 100644 index 0000000..267bdc9 --- /dev/null +++ b/src/modules/cookie_formatter.mjs @@ -0,0 +1,38 @@ +/** + * Convert Chrome's JSON format cookies data to a string array for Netscape format + * @param {chrome.cookies.Cookie[]} cookies + * @returns {string[][]} + */ +export const jsonToNetscapeMapper = (cookies) => { + return cookies.map(({ domain, expirationDate, path, secure, name, value }) => { + const includeSubDomain = !!domain?.startsWith('.'); + const expiry = expirationDate?.toFixed() ?? '0'; + const arr = [domain, includeSubDomain, path, secure, expiry, name, value]; + return arr.map((v) => (typeof v === 'boolean' ? v.toString().toUpperCase() : v)); + }); +}; + +/** @type {Record} */ +export const FormatMap = { + netscape: { + ext: '.txt', + mimeType: 'text/plain', + serializer: (cookies) => { + const netscapeTable = jsonToNetscapeMapper(cookies); + const text = [ + '# Netscape HTTP Cookie File', + '# http://curl.haxx.se/rfc/cookie_spec.html', + '# This is a generated file! Do not edit.', + '', + ...netscapeTable.map((row) => row.join('\t')), + '' // Add a new line at the end + ].join('\n'); + return text; + } + }, + json: { + ext: '.json', + mimeType: 'application/json', + serializer: JSON.stringify + } +}; diff --git a/src/modules/get_all_cookies.mjs b/src/modules/get_all_cookies.mjs new file mode 100644 index 0000000..528a077 --- /dev/null +++ b/src/modules/get_all_cookies.mjs @@ -0,0 +1,6 @@ +export default async function getAllCookies(details) { + const { partitionKey, ...detailsWithoutPartitionKey } = details; + const cookiesWithPartitionKey = partitionKey ? await chrome.cookies.getAll(details) : []; + const cookies = await chrome.cookies.getAll(detailsWithoutPartitionKey); + return [...cookies, ...cookiesWithPartitionKey]; +} diff --git a/src/popup.html b/src/popup.html index fc5eaac..039b92e 100644 --- a/src/popup.html +++ b/src/popup.html @@ -7,7 +7,7 @@ - + diff --git a/src/popup.js b/src/popup.js deleted file mode 100644 index 3a8be60..0000000 --- a/src/popup.js +++ /dev/null @@ -1,136 +0,0 @@ -/** @typedef {import('./types.d.ts').*} * */ - -/** Promise to get URL of Active Tab */ -const getUrlPromise = chrome.tabs.query({ active: true, currentWindow: true }).then(([{ url }]) => new URL(url)); - -/** - * Convert Chrome's JSON format cookies data to a string array for Netscape format - * @param {CookieJson[]} jsonData - * @returns {string[][7]} - */ -const jsonToNetscapeMapper = (jsonData) => { - return jsonData.map(({ domain, expirationDate, path, secure, name, value }) => { - const includeSubDomain = !!domain?.startsWith('.'); - const expiry = expirationDate?.toFixed() ?? '0'; - const arr = [domain, includeSubDomain, path, secure, expiry, name, value]; - return arr.map((v) => (typeof v === 'boolean' ? v.toString().toUpperCase() : v)); - }); -}; - -/** @type {Record} */ -const FormatMap = { - netscape: { - ext: '.txt', - mimeType: 'text/plain', - serializer: (jsonData) => { - const netscapeTable = jsonToNetscapeMapper(jsonData); - const nsText = [ - '# Netscape HTTP Cookie File', - '# http://curl.haxx.se/rfc/cookie_spec.html', - '# This is a generated file! Do not edit.', - '', - ...netscapeTable.map((row) => row.join('\t')), - '' // Add a new line at the end - ].join('\n'); - return nsText; - } - }, - json: { - ext: '.json', - mimeType: 'application/json', - serializer: JSON.stringify - } -}; - -/** - * Save text data as a file - * @param {string} text - * @param {string} name - * @param {Format} format - * @param {boolean} saveAs - */ -const save = async (text, name, { ext, mimeType }, saveAs = false) => { - const blob = new Blob([text], { type: mimeType }); - const filename = name + ext; - const url = URL.createObjectURL(blob); - await chrome.downloads.download({ url, filename, saveAs }).finally(() => URL.revokeObjectURL(url)); -}; - -/** - * Copy text data to the clipboard - * @param {string} text - */ -const setClipboard = async (text) => { - await navigator.clipboard.writeText(text); - document.getElementById('copy').innerText = 'Copied!'; -}; - -/** - * Retrieves both cookies with and without a partition key and returns the merged list - * @param {chrome.cookies.GetAllDetails} details - * @returns {Promise} - */ -const requestAllCookies = async (details) => chrome.runtime.sendMessage({ action: 'requestAllCookies', details }); - -/** - * Serialize and retrieve Cookies data into text data in a specific format - * @param {Format} format - * @param {chrome.cookies.GetAllDetails} details - * @returns {string} - */ -const getCookieText = async (format, details) => { - const cookies = await requestAllCookies(details); - return format.serializer(cookies); -}; - -/** Set URL in the header */ -getUrlPromise.then((url) => { - const location = document.querySelector('#location'); - location.textContent = location.href = url.href; -}); - -/** Set Cookies data to the table */ -getUrlPromise - .then((url) => requestAllCookies({ url: url.href, partitionKey: { topLevelSite: url.origin } })) - .then((cookies) => { - const netscape = jsonToNetscapeMapper(cookies); - const tableRows = netscape.map((row) => { - const tr = document.createElement('tr'); - tr.replaceChildren( - ...row.map((v) => { - const td = document.createElement('td'); - td.textContent = v; - return td; - }) - ); - return tr; - }); - document.querySelector('table tbody').replaceChildren(...tableRows); - }); - -document.querySelector('#export').addEventListener('click', async () => { - const format = FormatMap[document.querySelector('#format').value]; - const url = await getUrlPromise; - const text = await getCookieText(format, { url: url.href, partitionKey: { topLevelSite: url.origin } }); - save(text, url.hostname + '_cookies', format); -}); - -document.querySelector('#exportAs').addEventListener('click', async () => { - const format = FormatMap[document.querySelector('#format').value]; - const url = await getUrlPromise; - const text = await getCookieText(format, { url: url.href, partitionKey: { topLevelSite: url.origin } }); - save(text, url.hostname + '_cookies', format, true); -}); - -document.querySelector('#copy').addEventListener('click', async () => { - const format = FormatMap[document.querySelector('#format').value]; - const url = await getUrlPromise; - const text = await getCookieText(format, { url: url.href, partitionKey: { topLevelSite: url.origin } }); - setClipboard(text); -}); - -document.querySelector('#exportAll').addEventListener('click', async () => { - const format = FormatMap[document.querySelector('#format').value]; - const text = await getCookieText(format, { partitionKey: {} }); - save(text, 'cookies', format); -}); diff --git a/src/popup.mjs b/src/popup.mjs new file mode 100644 index 0000000..f8fc846 --- /dev/null +++ b/src/popup.mjs @@ -0,0 +1,106 @@ +import getAllCookies from './modules/get_all_cookies.mjs'; +import { jsonToNetscapeMapper, FormatMap } from './modules/cookie_formatter.mjs'; + +/** Promise to get URL of Active Tab */ +const getUrlPromise = chrome.tabs.query({ active: true, currentWindow: true }).then(([{ url }]) => new URL(url)); + + +// ---------------------------------------------- +// Actions after resolving the promise +// ---------------------------------------------- + +/** Set URL in the header */ +getUrlPromise.then((url) => { + const location = document.querySelector('#location'); + location.textContent = location.href = url.href; +}); + +/** Set Cookies data to the table */ +getUrlPromise + .then((url) => getAllCookies({ url: url.href, partitionKey: { topLevelSite: url.origin } })) + .then((cookies) => { + const netscape = jsonToNetscapeMapper(cookies); + const tableRows = netscape.map((row) => { + const tr = document.createElement('tr'); + tr.replaceChildren( + ...row.map((v) => { + const td = document.createElement('td'); + td.textContent = v; + return td; + }) + ); + return tr; + }); + document.querySelector('table tbody').replaceChildren(...tableRows); + }); + +// ---------------------------------------------- +// Event Listeners +// ---------------------------------------------- + +document.querySelector('#export').addEventListener('click', async () => { + const url = await getUrlPromise; + const details = { url: url.href, partitionKey: { topLevelSite: url.origin } }; + const { text, format } = await getCookieText(details); + saveToFile(text, url.hostname + '_cookies', format); +}); + +document.querySelector('#exportAs').addEventListener('click', async () => { + const url = await getUrlPromise; + const details = { url: url.href, partitionKey: { topLevelSite: url.origin } }; + const { text, format } = await getCookieText(details); + saveToFile(text, url.hostname + '_cookies', format, true); +}); + +document.querySelector('#copy').addEventListener('click', async () => { + const url = await getUrlPromise; + const details = { url: url.href, partitionKey: { topLevelSite: url.origin } }; + const { text } = await getCookieText(details); + setClipboard(text); +}); + +document.querySelector('#exportAll').addEventListener('click', async () => { + const { text, format } = await getCookieText({ partitionKey: {} }); + saveToFile(text, 'cookies', format); +}); + + +// ---------------------------------------------- +// Functions +// ---------------------------------------------- + +/** + * Get Stringified Cookies Text and Format Data + * @param {chrome.cookies.GetAllDetails} details + * @returns {Promise<{ text: string, format: Format }>} + */ +const getCookieText = async (details) => { + const cookies = await getAllCookies(details); + const format = FormatMap[document.querySelector('#format').value]; + if (!format) throw new Error('Invalid format'); + const text = format.serializer(cookies); + return { text, format }; +}; + +/** + * Save text data as a file + * @param {string} text + * @param {string} name + * @param {Format} format + * @param {boolean} saveAs + */ +export const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { + const blob = new Blob([text], { type: mimeType }); + const filename = name + ext; + const url = URL.createObjectURL(blob); + await chrome.downloads.download({ url, filename, saveAs }).finally(() => URL.revokeObjectURL(url)); +}; + +/** + * Copy text data to the clipboard + * @param {string} text + */ +export const setClipboard = async (text) => { + await navigator.clipboard.writeText(text); + document.getElementById('copy').innerText = 'Copied!'; +}; diff --git a/src/types.d.ts b/src/types.d.ts deleted file mode 100644 index cfbd709..0000000 --- a/src/types.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -type Format = { - ext: string; - mimeType: string; - serializer: (jsonData: CookieJson) => string; -}; -type CookieJson = { - domain: string; - expirationDate: number | undefined; - hostOnly: boolean; - httpOnly: boolean; - name: string; - path: string; - sameSite: string; - secure: boolean; - session: boolean; - storeId: string; - value: string; -}; -type CookieNetscape = string[][7]; From 41d6966482aa8679b22a5479886343d7e853c11f Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 04:38:26 +0900 Subject: [PATCH 02/14] Update name case --- src/modules/{cookie_formatter.mjs => cookie_format.mjs} | 2 +- src/popup.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/modules/{cookie_formatter.mjs => cookie_format.mjs} (97%) diff --git a/src/modules/cookie_formatter.mjs b/src/modules/cookie_format.mjs similarity index 97% rename from src/modules/cookie_formatter.mjs rename to src/modules/cookie_format.mjs index 267bdc9..f092e65 100644 --- a/src/modules/cookie_formatter.mjs +++ b/src/modules/cookie_format.mjs @@ -13,7 +13,7 @@ export const jsonToNetscapeMapper = (cookies) => { }; /** @type {Record} */ -export const FormatMap = { +export const formatMap = { netscape: { ext: '.txt', mimeType: 'text/plain', diff --git a/src/popup.mjs b/src/popup.mjs index f8fc846..e951a79 100644 --- a/src/popup.mjs +++ b/src/popup.mjs @@ -1,5 +1,5 @@ import getAllCookies from './modules/get_all_cookies.mjs'; -import { jsonToNetscapeMapper, FormatMap } from './modules/cookie_formatter.mjs'; +import { jsonToNetscapeMapper, formatMap } from './modules/cookie_format.mjs'; /** Promise to get URL of Active Tab */ const getUrlPromise = chrome.tabs.query({ active: true, currentWindow: true }).then(([{ url }]) => new URL(url)); @@ -76,7 +76,7 @@ document.querySelector('#exportAll').addEventListener('click', async () => { */ const getCookieText = async (details) => { const cookies = await getAllCookies(details); - const format = FormatMap[document.querySelector('#format').value]; + const format = formatMap[document.querySelector('#format').value]; if (!format) throw new Error('Invalid format'); const text = format.serializer(cookies); return { text, format }; From 146f620dfbed158b02bb4f2dc6862fc3e124d98c Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 04:41:05 +0900 Subject: [PATCH 03/14] rename file --- src/popup.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/popup.mjs b/src/popup.mjs index e951a79..ec1d8b1 100644 --- a/src/popup.mjs +++ b/src/popup.mjs @@ -4,7 +4,6 @@ import { jsonToNetscapeMapper, formatMap } from './modules/cookie_format.mjs'; /** Promise to get URL of Active Tab */ const getUrlPromise = chrome.tabs.query({ active: true, currentWindow: true }).then(([{ url }]) => new URL(url)); - // ---------------------------------------------- // Actions after resolving the promise // ---------------------------------------------- @@ -64,7 +63,6 @@ document.querySelector('#exportAll').addEventListener('click', async () => { saveToFile(text, 'cookies', format); }); - // ---------------------------------------------- // Functions // ---------------------------------------------- From d59d959a5522abc353b0e62b7983d379d199464c Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 04:59:33 +0900 Subject: [PATCH 04/14] fix build script --- build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.js b/build.js index ce8e770..971f246 100644 --- a/build.js +++ b/build.js @@ -18,7 +18,7 @@ const getBranch = () => new Promise((resolve, reject) => { const build = async (name) => { const dt = new Date().toLocaleString('sv').replace(/\D/g, ''); - const zipPath = `${name}_${dt}.zip`; + const zipPath = `${name.replace('/', '_')}_${dt}.zip`; const output = fs.createWriteStream(path.join(__dirname, zipPath)); process.chdir(path.join(__dirname, 'src')); From a0520415021ccffe2f121424d0f53d82f8692472 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 09:24:32 +0900 Subject: [PATCH 05/14] Update to support firefox again --- README.md | 7 ++++++ build.js | 41 ++++++++++++++++++--------------- package-lock.json | 19 +++++++++++---- package.json | 5 +++- src/background.html | 2 ++ src/manifest-firefox.json | 11 +++++++++ src/modules/get_all_cookies.mjs | 23 ++++++++++++++++++ 7 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 src/background.html create mode 100644 src/manifest-firefox.json diff --git a/README.md b/README.md index 0e735e4..e4eb2ae 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,13 @@ Specifically, it now needs "Download management" to execute a more appropriate f 3. Enable "Developer mode". 4. Click on "Load Unpacked" and open the directory `Get-cookies.txt-LOCALLY/src`. +## Build for Firefox +1. Download and unzip this repository. +2. Merge `src/manifest.json` and `src/manifest-firefox.json` using one of the following methods: + - `npm install && npm run build:firfox` and install generated `.zip` file. + - `jq -s '.[0] + .[1]' src/manifest.json src/manifest.firefox.json > src/manifest.json` and install the extension from the `src` rectory. + - Merge manually. + ## Example of extension installation directory (Google Chrome) diff --git a/build.js b/build.js index 971f246..6e4dfeb 100644 --- a/build.js +++ b/build.js @@ -1,24 +1,23 @@ +#!/usr/bin/env node + const fs = require('fs'); const process = require('process'); const path = require('path'); const archiver = require('archiver'); -const { exec } = require('child_process'); - +const { execSync } = require('child_process'); +const { program } = require('commander'); -const getBranch = () => new Promise((resolve, reject) => { - exec('git rev-parse --abbrev-ref HEAD', (err, stdout, stderr) => { - if (typeof stdout === 'string') { - resolve(stdout.trim()); - } else { - reject(TypeError); - } - }); -}); +const options = program.option('-f --firefox', 'Build for Firefox').parse(process.argv).opts(); +const getGitInfo = () => { + const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); + const commitHash = execSync('git rev-parse HEAD').toString().trim(); + return { branch, commitHash }; +}; -const build = async (name) => { - const dt = new Date().toLocaleString('sv').replace(/\D/g, ''); - const zipPath = `${name.replace('/', '_')}_${dt}.zip`; +const build = async ({ branch, commitHash }) => { + const mode = options.firefox ? 'firefox' : 'chrome'; + const zipPath = `${branch.replace('/', '_')}_${commitHash.slice(0,5)}_${mode}.zip`; const output = fs.createWriteStream(path.join(__dirname, zipPath)); process.chdir(path.join(__dirname, 'src')); @@ -28,11 +27,17 @@ const build = async (name) => { }); archive.pipe(output); - archive.glob('**/*'); + archive.glob('**/*', { ignore: ['**/*.json'] }); + + const manifest = JSON.parse(fs.readFileSync('manifest.json')); + if (options.firefox) { + const manifestFirefox = JSON.parse(fs.readFileSync('manifest-firefox.json')); + Object.assign(manifest, manifestFirefox); + } + archive.append(JSON.stringify(manifest, null, 2), { name: 'manifest.json' }); await archive.finalize(); console.log('BUILD', zipPath); -} - +}; -getBranch().then(build); +build(getGitInfo()); diff --git a/package-lock.json b/package-lock.json index d4e4efe..566cf14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,11 +4,11 @@ "requires": true, "packages": { "": { - "name": "Get-cookies.txt-LOCALLY", "devDependencies": { "@types/chrome": "^0.0.218", "@types/wicg-file-system-access": "^2020.9.5", "archiver": "^5.3.1", + "commander": "^12.1.0", "icon-gen": "^3.0.1", "prettier": "^3.2.5" } @@ -331,12 +331,12 @@ } }, "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "engines": { - "node": ">= 12" + "node": ">=18" } }, "node_modules/compress-commons": { @@ -609,6 +609,15 @@ "node": ">= 12" } }, + "node_modules/icon-gen/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", diff --git a/package.json b/package.json index 909197c..e6f8638 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,14 @@ "@types/chrome": "^0.0.218", "@types/wicg-file-system-access": "^2020.9.5", "archiver": "^5.3.1", + "commander": "^12.1.0", "icon-gen": "^3.0.1", "prettier": "^3.2.5" }, "scripts": { "format": "npx prettier --write src", - "build": "npm run format && node build.js" + "build": "npm run build:chrome && npm run build:firefox", + "build:chrome": "node build.js", + "build:firefox": "node build.js --firefox" } } diff --git a/src/background.html b/src/background.html new file mode 100644 index 0000000..27dd81b --- /dev/null +++ b/src/background.html @@ -0,0 +1,2 @@ + + diff --git a/src/manifest-firefox.json b/src/manifest-firefox.json new file mode 100644 index 0000000..fb5c7fc --- /dev/null +++ b/src/manifest-firefox.json @@ -0,0 +1,11 @@ +{ + "incognito": "spanning", + "background": { + "page": "background.html" + }, + "browser_specific_settings": { + "gecko": { + "id": "{ac87cfd8-47b1-4401-b32e-f033af5ed96b}" + } + } +} diff --git a/src/modules/get_all_cookies.mjs b/src/modules/get_all_cookies.mjs index 528a077..8a871f7 100644 --- a/src/modules/get_all_cookies.mjs +++ b/src/modules/get_all_cookies.mjs @@ -1,6 +1,29 @@ +/** + * Get all cookies that match the given criteria. + * @param {chrome.cookies.GetAllDetails} details + * @returns {Promise} + */ export default async function getAllCookies(details) { + details.storeId ??= await getCurrentCookieStoreId(); const { partitionKey, ...detailsWithoutPartitionKey } = details; const cookiesWithPartitionKey = partitionKey ? await chrome.cookies.getAll(details) : []; const cookies = await chrome.cookies.getAll(detailsWithoutPartitionKey); return [...cookies, ...cookiesWithPartitionKey]; } + +/** + * Get the current cookie store ID. + * @returns {Promise} + */ +const getCurrentCookieStoreId = async () => { + // If the extension is in split incognito mode, return undefined to choose the default store. + if (chrome.runtime.getManifest().incognito === 'split') return undefined; + + // Firefox supports the `tab.cookieStoreId` property. + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (tab.cookieStoreId) return tab.cookieStoreId; + + // Chrome does not support the `tab.cookieStoreId` property. + const stores = await chrome.cookies.getAllCookieStores(); + return stores.find((store) => store.tabIds.includes(tab.id))?.id; +}; From 38e1f24c7ea3076dc626f2fbb5d2e6ea1b3ab2ad Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 09:24:54 +0900 Subject: [PATCH 06/14] Update version to 0.6.0 --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index 49e2778..8a79519 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "name": "Get cookies.txt LOCALLY", "description": "Get cookies.txt, NEVER send information outside with open-source", - "version": "0.5.7", + "version": "0.6.0", "manifest_version": 3, "permissions": ["activeTab", "cookies", "downloads", "notifications"], "host_permissions": [""], From 05de0f1a8d0563b0eba091eb6f64be68dc3822ff Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 09:32:13 +0900 Subject: [PATCH 07/14] fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4eb2ae..8929052 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Specifically, it now needs "Download management" to execute a more appropriate f 1. Download and unzip this repository. 2. Merge `src/manifest.json` and `src/manifest-firefox.json` using one of the following methods: - `npm install && npm run build:firfox` and install generated `.zip` file. - - `jq -s '.[0] + .[1]' src/manifest.json src/manifest.firefox.json > src/manifest.json` and install the extension from the `src` rectory. + - `jq -s '.[0] + .[1]' src/manifest.json src/manifest-firefox.json > src/manifest.json` and install the extension from the `src` rectory. - Merge manually. From e5fcfff50a00703f380ddf2869f76cf1211d5be3 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 09:40:13 +0900 Subject: [PATCH 08/14] Update: use dst dir --- .gitignore | 1 + README.md | 2 +- build.js | 9 +++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 41a73c3..b1802e4 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,5 @@ dist # TernJS port file .tern-port +dst/ *.zip diff --git a/README.md b/README.md index 8929052..940a2f3 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Specifically, it now needs "Download management" to execute a more appropriate f ## Build for Firefox 1. Download and unzip this repository. 2. Merge `src/manifest.json` and `src/manifest-firefox.json` using one of the following methods: - - `npm install && npm run build:firfox` and install generated `.zip` file. + - `npm install && npm run build:firfox` and install generated `.zip` file from the `dist` directory. - `jq -s '.[0] + .[1]' src/manifest.json src/manifest-firefox.json > src/manifest.json` and install the extension from the `src` rectory. - Merge manually. diff --git a/build.js b/build.js index 6e4dfeb..eb914af 100644 --- a/build.js +++ b/build.js @@ -17,8 +17,9 @@ const getGitInfo = () => { const build = async ({ branch, commitHash }) => { const mode = options.firefox ? 'firefox' : 'chrome'; - const zipPath = `${branch.replace('/', '_')}_${commitHash.slice(0,5)}_${mode}.zip`; - const output = fs.createWriteStream(path.join(__dirname, zipPath)); + const zipName = `${branch.replace('/', '_')}_${commitHash.slice(0,5)}_${mode}.zip`; + fs.mkdirSync(path.join(__dirname, 'dst'), { recursive: true }); + const output = fs.createWriteStream(path.join(__dirname, 'dst', zipName)); process.chdir(path.join(__dirname, 'src')); @@ -36,8 +37,8 @@ const build = async ({ branch, commitHash }) => { } archive.append(JSON.stringify(manifest, null, 2), { name: 'manifest.json' }); - await archive.finalize(); - console.log('BUILD', zipPath); + archive.finalize(); + console.log('BUILD', zipName); }; build(getGitInfo()); From b78e3a38ec82727527d80ea60fd07db56f1f9a01 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 10:21:47 +0900 Subject: [PATCH 09/14] fix --- src/popup.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/popup.mjs b/src/popup.mjs index ec1d8b1..87c5924 100644 --- a/src/popup.mjs +++ b/src/popup.mjs @@ -87,7 +87,7 @@ const getCookieText = async (details) => { * @param {Format} format * @param {boolean} saveAs */ -export const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { +const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { const blob = new Blob([text], { type: mimeType }); const filename = name + ext; const url = URL.createObjectURL(blob); @@ -98,7 +98,7 @@ export const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) * Copy text data to the clipboard * @param {string} text */ -export const setClipboard = async (text) => { +const setClipboard = async (text) => { await navigator.clipboard.writeText(text); document.getElementById('copy').innerText = 'Copied!'; }; From 52e27097feb11c98d3ed0b476bfce5ede438adc3 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 11:24:26 +0900 Subject: [PATCH 10/14] Fix: Firefox fails to download if revoke URL is done before completion --- src/popup.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/popup.mjs b/src/popup.mjs index 87c5924..205ad35 100644 --- a/src/popup.mjs +++ b/src/popup.mjs @@ -82,6 +82,7 @@ const getCookieText = async (details) => { /** * Save text data as a file + * Firefox fails if revoked during download. * @param {string} text * @param {string} name * @param {Format} format @@ -91,7 +92,17 @@ const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { const blob = new Blob([text], { type: mimeType }); const filename = name + ext; const url = URL.createObjectURL(blob); - await chrome.downloads.download({ url, filename, saveAs }).finally(() => URL.revokeObjectURL(url)); + const id = await chrome.downloads.download({ url, filename, saveAs }); + + /** @param {chrome.downloads.DownloadDelta} delta */ + const onChange = (delta) => { + if (delta.id === id && delta.state?.current !== 'in_progress') { + chrome.downloads.onChanged.removeListener(onChange); + URL.revokeObjectURL(url); + } + }; + + chrome.downloads.onChanged.addListener(onChange); }; /** From 39e06b66c6fabbc7cbf1250395a657afc9764a7b Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 11:25:12 +0900 Subject: [PATCH 11/14] update version --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index 8a79519..7a5cd7e 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "name": "Get cookies.txt LOCALLY", "description": "Get cookies.txt, NEVER send information outside with open-source", - "version": "0.6.0", + "version": "0.6.1", "manifest_version": 3, "permissions": ["activeTab", "cookies", "downloads", "notifications"], "host_permissions": [""], From 77b471aa8bbde570dfc666fe3fab3d8c9cf70857 Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 19:54:09 +0900 Subject: [PATCH 12/14] Fix table header wrap style --- src/popup-options.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup-options.css b/src/popup-options.css index c6d7645..e77ffa1 100644 --- a/src/popup-options.css +++ b/src/popup-options.css @@ -12,6 +12,6 @@ gap: 0.5em; } -.netscape-table.nowrap { +.netscape-table.nowrap tbody { white-space: nowrap; } From 44b022b7138e6b86cbb25d52a116f7a13784d0fd Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 19:54:29 +0900 Subject: [PATCH 13/14] update patch version --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index 7a5cd7e..516e6a4 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "name": "Get cookies.txt LOCALLY", "description": "Get cookies.txt, NEVER send information outside with open-source", - "version": "0.6.1", + "version": "0.6.2", "manifest_version": 3, "permissions": ["activeTab", "cookies", "downloads", "notifications"], "host_permissions": [""], From e07a9793aeded6846070f0dbed36a26106d0954a Mon Sep 17 00:00:00 2001 From: kairi003 Date: Sun, 19 May 2024 21:18:20 +0900 Subject: [PATCH 14/14] Refactor: Modification of Order Structure --- src/popup.mjs | 102 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/popup.mjs b/src/popup.mjs index 205ad35..a1d67a2 100644 --- a/src/popup.mjs +++ b/src/popup.mjs @@ -4,6 +4,57 @@ import { jsonToNetscapeMapper, formatMap } from './modules/cookie_format.mjs'; /** Promise to get URL of Active Tab */ const getUrlPromise = chrome.tabs.query({ active: true, currentWindow: true }).then(([{ url }]) => new URL(url)); +// ---------------------------------------------- +// Functions +// ---------------------------------------------- + +/** + * Get Stringified Cookies Text and Format Data + * @param {chrome.cookies.GetAllDetails} details + * @returns {Promise<{ text: string, format: Format }>} + */ +const getCookieText = async (details) => { + const cookies = await getAllCookies(details); + const format = formatMap[document.querySelector('#format').value]; + if (!format) throw new Error('Invalid format'); + const text = format.serializer(cookies); + return { text, format }; +}; + +/** + * Save text data as a file + * Firefox fails if revoked during download. + * @param {string} text + * @param {string} name + * @param {Format} format + * @param {boolean} saveAs + */ +const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { + const blob = new Blob([text], { type: mimeType }); + const filename = name + ext; + const url = URL.createObjectURL(blob); + const id = await chrome.downloads.download({ url, filename, saveAs }); + + /** @param {chrome.downloads.DownloadDelta} delta */ + const onChange = (delta) => { + if (delta.id === id && delta.state?.current !== 'in_progress') { + chrome.downloads.onChanged.removeListener(onChange); + URL.revokeObjectURL(url); + } + }; + + chrome.downloads.onChanged.addListener(onChange); +}; + +/** + * Copy text data to the clipboard + * @param {string} text + */ +const setClipboard = async (text) => { + await navigator.clipboard.writeText(text); + document.getElementById('copy').innerText = 'Copied!'; +}; + // ---------------------------------------------- // Actions after resolving the promise // ---------------------------------------------- @@ -62,54 +113,3 @@ document.querySelector('#exportAll').addEventListener('click', async () => { const { text, format } = await getCookieText({ partitionKey: {} }); saveToFile(text, 'cookies', format); }); - -// ---------------------------------------------- -// Functions -// ---------------------------------------------- - -/** - * Get Stringified Cookies Text and Format Data - * @param {chrome.cookies.GetAllDetails} details - * @returns {Promise<{ text: string, format: Format }>} - */ -const getCookieText = async (details) => { - const cookies = await getAllCookies(details); - const format = formatMap[document.querySelector('#format').value]; - if (!format) throw new Error('Invalid format'); - const text = format.serializer(cookies); - return { text, format }; -}; - -/** - * Save text data as a file - * Firefox fails if revoked during download. - * @param {string} text - * @param {string} name - * @param {Format} format - * @param {boolean} saveAs - */ -const saveToFile = async (text, name, { ext, mimeType }, saveAs = false) => { - const blob = new Blob([text], { type: mimeType }); - const filename = name + ext; - const url = URL.createObjectURL(blob); - const id = await chrome.downloads.download({ url, filename, saveAs }); - - /** @param {chrome.downloads.DownloadDelta} delta */ - const onChange = (delta) => { - if (delta.id === id && delta.state?.current !== 'in_progress') { - chrome.downloads.onChanged.removeListener(onChange); - URL.revokeObjectURL(url); - } - }; - - chrome.downloads.onChanged.addListener(onChange); -}; - -/** - * Copy text data to the clipboard - * @param {string} text - */ -const setClipboard = async (text) => { - await navigator.clipboard.writeText(text); - document.getElementById('copy').innerText = 'Copied!'; -};