Skip to content

Instantly share code, notes, and snippets.

@guest271314
Created November 4, 2022 01:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guest271314/a06ebd8bf455effa2f58a774b61ea9a9 to your computer and use it in GitHub Desktop.
Save guest271314/a06ebd8bf455effa2f58a774b61ea9a9 to your computer and use it in GitHub Desktop.
Stream same data to all non-chrome tabs
const start = new Date();
let streams = [];
let data = [...Array(1000)].map((_, i) => i);
let encoder = new TextEncoder();
let resolve;
let promise = new Promise((_) => (resolve = _));
let readable = new ReadableStream({
start: async (_) => {
// console.log('Original stream');
return promise;
},
pull: async (c) => {
while (data.length) {
c.enqueue(encoder.encode(data.splice(0, 1)));
await new Promise((resolve) => setTimeout(resolve, 1000));
}
c.close();
},
cancel: (reason) => {
console.log(reason);
},
});
function parallelStream() {
let controller;
const stream = new ReadableStream({
start: (c) => {
controller = c;
return;
},
});
streams.push({
controller,
stream,
});
return stream;
}
async function readStream(rs) {
return rs
.pipeTo(
new WritableStream({
start(_) {
// console.log('Starting tab streaming');
},
write(value) {
for (const { controller, stream } of streams) {
controller.enqueue(value);
}
},
close() {
console.log('Original stream closed');
try {
for (const { controller, stream } of streams) {
controller.close();
}
} catch (e) {
console.error(e);
}
},
})
)
.catch(console.error);
}
async function createStream({ id, url }) {
if (!/^(chrome|chrome-extension):/.test(url)) {
return await chrome.scripting.executeScript({
target: {
tabId: id,
},
world: 'MAIN',
args: [`${chrome.runtime.getURL('')}index.html`],
func: (src) => {
const url = new URL(src);
for (const iframe of document.querySelectorAll(
`iframe[src*="${url.host}"]`
)) {
iframe.remove();
}
onmessage = async (e) => {
if (e.origin === url.origin) {
let bool = false;
e.data
.pipeThrough(new TextDecoderStream())
.pipeTo(
new WritableStream({
start() {
console.log('start');
},
write(value) {
console.log(value);
},
close() {
console.log(`Stream closed`);
},
})
)
.catch(console.error);
}
};
const transferableWindow = document.createElement('iframe');
transferableWindow.style.display = 'none';
transferableWindow.name = location.href;
transferableWindow.src = url.href;
document.body.appendChild(transferableWindow);
},
});
}
}
async function onClicked(tab) {
const tabs = await chrome.tabs.query({});
for (const { id, url } of tabs) {
await createStream({ id, url });
}
try {
readStream(readable); resolve();
chrome.tabs.onUpdated.addListener(onUpdated);
} catch (e) {
console.error(e);
}
}
async function onUpdated(id, { status }, tab) {
if (status === 'complete') {
const { url } = tab;
try {
await createStream({ id, url });
} catch (e) {
console.error(e);
}
}
}
async function onMessage(e) {
if (e.data === 'stream') {
try {
const rs = parallelStream();
e.source.postMessage(rs, [rs]);
} catch (e) {
console.error(e);
}
} else {
console.log({time: ~~((new Date() - start) / 60000)});
}
}
function onInstalled(reason) {
console.log(reason);
}
function onInstall(e) {
e.waitUntil(self.skipWaiting());
}
function onActivate(e) {
e.waitUntil(self.clients.claim());
}
chrome.action.onClicked.addListener(onClicked);
chrome.runtime.onInstalled.addListener(onInstalled);
addEventListener('message', onMessage);
addEventListener('install', onInstall);
addEventListener('activate', onActivate);
{
"name": "Parallel stream",
"description": "Stream same data to all non-chrome tabs",
"version": "1.0",
"manifest_version": 3,
"host_permissions": ["<all_urls>"],
"permissions": ["tabs", "activeTab", "scripting"],
"background": {
"service_worker": "background.js",
"type": "module"
},
"web_accessible_resources": [{
"resources": ["*.html", "*.js"],
"matches": ["<all_urls>"],
"extensions": []
}],
"action": {},
"author": "guest271314"
}
const { active } = await navigator.serviceWorker.ready;
async function handleMessage(e) {
console.log(e.data);
if (e.data instanceof ReadableStream) {
const readable = e.data;
parent.postMessage(readable, name, [readable]);
}
}
navigator.serviceWorker.addEventListener('message', handleMessage);
active.postMessage('stream');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment