Chrome拡張で「現在のウィンドウの全タブURLをワンクリックでコピー」する最小構成コードです。
manifest.json / background.js / offscreen.html / offscreen.js の4ファイルを同一フォルダに保存し、
chrome://extensions から「パッケージ化されていない拡張機能を読み込む」で導入してください。
フォルダ構成
tab-url-copier/
manifest.json
background.js
offscreen.html
offscreen.js1) manifest.json
{
"manifest_version": 3,
"name": "Tab URL Copier (1-click, stable)",
"version": "1.0.1",
"description": "Copy all tab URLs in the current Chrome window to clipboard with one click (stable via offscreen).",
"permissions": ["tabs", "offscreen", "clipboardWrite"],
"background": {
"service_worker": "background.js",
"type": "module"
},
"action": {
"default_title": "Copy tab URLs"
}
}2) background.js
async function ensureOffscreen() {
const exists = await chrome.offscreen.hasDocument?.();
if (exists) return;
await chrome.offscreen.createDocument({
url: "offscreen.html",
reasons: ["CLIPBOARD"],
justification: "Write tab URLs to clipboard."
});
}
async function copyTextToClipboard(text) {
await ensureOffscreen();
const res = await chrome.runtime.sendMessage({ type: "COPY_TO_CLIPBOARD", text });
if (!res?.ok) throw new Error(res?.error || "Clipboard copy failed");
}
function uniqKeepOrder(arr) {
const seen = new Set();
const out = [];
for (const x of arr) {
if (!seen.has(x)) {
seen.add(x);
out.push(x);
}
}
return out;
}
async function badge(text) {
await chrome.action.setBadgeText({ text });
if (text) setTimeout(() => chrome.action.setBadgeText({ text: "" }), 1200);
}
chrome.action.onClicked.addListener(async () => {
try {
const tabs = await chrome.tabs.query({ currentWindow: true });
const urls = uniqKeepOrder(
tabs.map(t => t.url).filter(u => typeof u === "string" && u.length > 0)
);
await copyTextToClipboard(urls.join("\n"));
await badge(String(urls.length));
} catch (e) {
console.error(e);
await badge("ERR");
}
});3) offscreen.html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Offscreen</title>
</head>
<body>
<script src="offscreen.js"></script>
</body>
</html>4) offscreen.js
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
(async () => {
if (msg?.type !== "COPY_TO_CLIPBOARD") return;
try {
await navigator.clipboard.writeText(msg.text ?? "");
sendResponse({ ok: true });
} catch (e) {
// fallback for restricted environments
try {
const ta = document.createElement("textarea");
ta.value = msg.text ?? "";
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
ta.remove();
sendResponse({ ok: true, fallback: true });
} catch (e2) {
sendResponse({ ok: false, error: String(e2) });
}
}
})();
return true;
});