Skip to content

Instantly share code, notes, and snippets.

@feliperohdee
Created February 8, 2020 18:04
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 feliperohdee/e41a96fa95ca206e00f825f46cda93f3 to your computer and use it in GitHub Desktop.
Save feliperohdee/e41a96fa95ca206e00f825f46cda93f3 to your computer and use it in GitHub Desktop.
chrome.debugger intercept scripts
(async () => {
const scripts = new Map([
['app', chrome.runtime.getURL('static/app.js')],
['app2', chrome.runtime.getURL('static/app2.js')],
['progress', chrome.runtime.getURL('static/progress.js')]
]);
const p = async (fn, ...args) => {
return new Promise((resolve, reject) => {
return fn(...[...args, (...response) => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError.message);
}
resolve(...response);
}]);
});
};
const wait = async time => {
return new Promise(resolve => {
setTimeout(resolve, time);
});
};
const getScript = async (key, request) => {
const headers = [{
name: 'ii',
value: 'true'
}];
const response = await fetch(request.url, {
headers: new Headers(request.headers)
});
response.headers.forEach((value, name) => {
headers.push({
name,
value
});
});
if (!scripts.has(key)) {
return null;
}
await wait(1500);
const script = await fetch(scripts.get(key));
return {
body: btoa(unescape(encodeURIComponent(await script.text()))),
headers
};
};
class Tab {
constructor(id) {
this.id = id;
this.attached = false;
this.intercepting = false;
this.scripts = 0;
this.status = 'loading';
}
close() {
chrome.tabs.remove(this.id);
}
async attach() {
if (this.attached) {
return;
}
await p(chrome.debugger.attach, {
tabId: this.id
}, '1.0');
this.attached = true;
}
async detach() {
if (!this.attached) {
return;
}
await p(chrome.debugger.detach, {
tabId: this.id
});
this.attached = false;
this.intercepting = false;
}
async fullFillRequest(requestId, script) {
if (!this.attached) {
return;
}
await p(chrome.debugger.sendCommand, {
tabId: this.id
}, 'Fetch.fulfillRequest', {
requestId,
responseCode: 200,
responseHeaders: script.headers,
body: script.body
});
return scripts.size === ++this.scripts;
}
async intercept() {
if (this.intercepting) {
return;
}
await p(chrome.debugger.sendCommand, {
tabId: this.id
}, 'Fetch.enable', {
patterns: [{
urlPattern: '*/progress*.js'
}, {
urlPattern: '*/app*.js'
}]
});
this.intercepting = true;
}
}
class Tabs extends Map {
add(id) {
const tab = new Tab(id);
super.set(id, tab);
return tab;
}
}
const tabs = new Tabs();
const init = async () => {
const {
id
} = await p(chrome.tabs.create, {
url: 'https://web.whatsapp.com'
});
const tab = tabs.add(id);
await tab.attach();
await tab.intercept();
};
chrome.browserAction.onClicked.addListener(init);
chrome.tabs.onUpdated.addListener(async (tabId, info) => {
const tab = tabs.get(tabId);
if (tab && info.status) {
if (
tab.status === 'complete' &&
info.status === 'loading'
) {
tab.close();
init();
} else {
tab.status = info.status;
}
}
});
chrome.tabs.onRemoved.addListener(tabId => {
tabs.delete(tabId);
});
chrome.debugger.onEvent.addListener(async (source, method, params) => {
if (tabs.has(source.tabId)) {
if (method === 'Fetch.requestPaused') {
const {
request,
requestId
} = params;
let key;
if (request.url.includes('/progress.')) {
key = 'progress';
} else if (request.url.includes('/app.')) {
key = 'app';
} else if (request.url.includes('/app2.')) {
key = 'app2';
}
const script = await getScript(key, request);
const tab = tabs.get(source.tabId);
if (script && tab) {
const ready = await tab.fullFillRequest(requestId, script);
if (ready) {
await tab.detach();
}
}
}
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment