prevent explosion of scriptsForTab!

This commit is contained in:
Omar Rizwan 2020-12-29 03:52:17 -08:00
parent 44c8dfcec6
commit 705b245be5
3 changed files with 83 additions and 49 deletions

View file

@ -52,24 +52,41 @@ async function detachDebugger(tabId) {
else { resolve(); } else { resolve(); }
})); }));
} }
const TabManager = { const TabManager = (function() {
debugTab: async function(tabId) { if (TESTING) return;
// meant to be higher-level wrapper for raw attach/detach chrome.debugger.onEvent.addListener((source, method, params) => {
// TODO: could we remember if we're already attached? idk if it's worth it console.log(source, method, params);
try { await attachDebugger(tabId); } if (method === "Page.frameStartedLoading") {
catch (e) { // we're gonna assume we're always plugged into both Page and Debugger.
if (e.message.indexOf('Another debugger is already attached') !== -1) { TabManager.scriptsForTab[source.tabId] = [];
await detachDebugger(tabId);
await attachDebugger(tabId); } else if (method === "Debugger.scriptParsed") {
} TabManager.scriptsForTab[source.tabId] = TabManager.scriptsForTab[source.tabId] || [];
TabManager.scriptsForTab[source.tabId].push(params);
} }
// TODO: detach automatically? some kind of reference counting thing? });
},
enableDomainForTab: async function(tabId, domain) { return {
// TODO: could we remember if we're already enabled? idk if it's worth it scriptsForTab: {},
await sendDebuggerCommand(tabId, `${domain}.enable`, {}); debugTab: async function(tabId) {
} // meant to be higher-level wrapper for raw attach/detach
}; // TODO: could we remember if we're already attached? idk if it's worth it
try { await attachDebugger(tabId); }
catch (e) {
if (e.message.indexOf('Another debugger is already attached') !== -1) {
await detachDebugger(tabId);
await attachDebugger(tabId);
}
}
// TODO: detach automatically? some kind of reference counting thing?
},
enableDomainForTab: async function(tabId, domain) {
// TODO: could we remember if we're already enabled? idk if it's worth it
if (domain === 'Debugger') { TabManager.scriptsForTab[tabId] = []; }
await sendDebuggerCommand(tabId, `${domain}.enable`, {});
}
};
})();
function sendDebuggerCommand(tabId, method, commandParams) { function sendDebuggerCommand(tabId, method, commandParams) {
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
chrome.debugger.sendCommand({tabId}, method, commandParams, result => { chrome.debugger.sendCommand({tabId}, method, commandParams, result => {
@ -78,23 +95,6 @@ function sendDebuggerCommand(tabId, method, commandParams) {
); );
} }
const BrowserState = { scriptsForTab: {} };
(function() {
if (TESTING) return;
chrome.debugger.onEvent.addListener((source, method, params) => {
console.log(source, method, params);
if (method === "Page.frameStartedLoading") {
// we're gonna assume we're always plugged into both Page and Debugger.
BrowserState.scriptsForTab[source.tabId] = [];
} else if (method === "Debugger.scriptParsed") {
BrowserState.scriptsForTab[source.tabId] = BrowserState.scriptsForTab[source.tabId] || [];
BrowserState.scriptsForTab[source.tabId].push(params);
}
});
})();
const router = {}; const router = {};
const Cache = { const Cache = {
@ -288,14 +288,16 @@ router["/tabs/by-id/*/control"] = {
}, },
async readdir({path}) { async readdir({path}) {
const tabId = parseInt(pathComponent(path, -3)); const tabId = parseInt(pathComponent(path, -3));
return { entries: [".", "..", ...BrowserState.scriptsForTab[tabId].map(params => sanitize(params.url).slice(0, 200) + "_" + params.scriptId)] }; return { entries: [".", "..", ...TabManager.scriptsForTab[tabId].map(params => sanitize(params.url).slice(0, 200) + "_" + params.scriptId)] };
} }
}; };
router["/tabs/by-id/*/debugger/scripts/*"] = defineFile(async path => { router["/tabs/by-id/*/debugger/scripts/*"] = defineFile(async path => {
const [tabId, suffix] = [parseInt(pathComponent(path, -4)), pathComponent(path, -1)]; const [tabId, suffix] = [parseInt(pathComponent(path, -4)), pathComponent(path, -1)];
await TabManager.debugTab(tabId); await TabManager.debugTab(tabId);
console.log('BEFORE', TabManager.scriptsForTab[tabId].length);
await TabManager.enableDomainForTab(tabId, "Page"); await TabManager.enableDomainForTab(tabId, "Page");
await TabManager.enableDomainForTab(tabId, "Debugger"); await TabManager.enableDomainForTab(tabId, "Debugger");
console.log('AFTER', TabManager.scriptsForTab[tabId].length)
const parts = path.split("_"); const scriptId = parts[parts.length - 1]; const parts = path.split("_"); const scriptId = parts[parts.length - 1];
const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId}); const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId});

View file

@ -358,7 +358,18 @@ TODO: make diagrams?
GPLv3 GPLv3
## things that would be cool to do ## things that could/should be done
- add more synthetic files!! view DOM nodes, snapshot current HTML of
page, spelunk into living objects. see what your code is doing. make
more files writable also
- build more (GUI and CLI) tools on top, on both sides
- more persistence stuff
- why can't Preview open images? GUI programs often struggle with the
filesystem for some reason. CLI more reliable
- multithreading. the key constraint is that I pass `-s` to - multithreading. the key constraint is that I pass `-s` to
`fuse_main` in `tabfs.c`, which makes everything `fuse_main` in `tabfs.c`, which makes everything
@ -379,18 +390,20 @@ GPLv3
filesystem, even ones you'd assume are reasonably battle-tested and filesystem, even ones you'd assume are reasonably battle-tested and
well-engineered like sshfs? well-engineered like sshfs?
- other performance stuff -- remembering when we're already attached
to things, reference counting, minimizing browser roundtrips. not
sure impact of these
- TypeScript (how to do with the minimum amount of build system and
package manager nonsense?)
- look into support for Firefox / Windows / Safari / etc. best FUSE - look into support for Firefox / Windows / Safari / etc. best FUSE
equiv for Windows? can you bridge to the remote debugging APIs that equiv for Windows? can you bridge to the remote debugging APIs that
all of them already have to get the augmented functionality? or just all of them already have to get the augmented functionality? or just
implement it all with JS monkey patching? implement it all with JS monkey patching?
- window management. tab management where you can move tabs - window management. tab management where you can move tabs. 'merge
all windows'
- snapshotting (snapshot.html / snapshot.mhtml file)
- fix leaks
- elim unnecessary round-trips / browser API calls
## hmm ## hmm
@ -423,14 +436,30 @@ suggests making an extension is a whole Thing, a whole Project. like,
why can't I just take a minute to ask my browser a question or tell it why can't I just take a minute to ask my browser a question or tell it
to automate something? lightness to automate something? lightness
- a lot of existing uses of these APIs are in an automation context, - a lot of existing uses of these APIs are in an automation context:
if you want to test your code on an automated browser. I'm much more testing your code on a robotic browser as part of some pipeline. I'm
interested in an interactive, end-user context. augmenting the way I much more interested in an interactive, end-user context. augmenting
use my normal browser. that's why this is an extension. it doesn't the way I use my everyday browser. that's why this is an
require your browser to run in some weird remote debugging mode that extension. it doesn't require your browser to run in some weird
you'd always forget to turn on. it just [stays remote debugging mode that you'd always forget to turn on. it just
[stays
running](https://twitter.com/rsnous/status/1340150818553561094) running](https://twitter.com/rsnous/status/1340150818553561094)
- system call tracing (dtruss or strace) super useful when anything is
going wrong. (need to disable SIP on macOS, though.) the
combination of dtruss (application side) & console logging fs
request/response (filesystem side) gives a huge amount of insight
into basically any problem, end to end
- for a lot of things in the extension API, the browser can notify you
of updates but there's no apparent way to query the full current
state. so we'd need to sit in a lot of these places from the
beginning and accumulate the incoming events to know, like, the last
time a tab was updated, or the list of scripts currently running on
a tab
- async/await was absolutely vital to making this readable
- open input space -- filesystem. (it reminds me of Screenotate.) - open input space -- filesystem. (it reminds me of Screenotate.)
- now you have this whole 'language', this whole toolset, to control - now you have this whole 'language', this whole toolset, to control

View file

@ -41,6 +41,9 @@ int main() {
assert(system("echo file://$(pwd)/test-page.html > ../fs/mnt/tabs/create") == 0); assert(system("echo file://$(pwd)/test-page.html > ../fs/mnt/tabs/create") == 0);
assert(file_contents_equal("../fs/mnt/tabs/last-focused/title.txt", "Title of Test Page")); assert(file_contents_equal("../fs/mnt/tabs/last-focused/title.txt", "Title of Test Page"));
assert(file_contents_equal("../fs/mnt/tabs/last-focused/text.txt", "Body Text of Test Page")); assert(file_contents_equal("../fs/mnt/tabs/last-focused/text.txt", "Body Text of Test Page"));
assert(system("ls ../fs/mnt/tabs/last-focused/debugger/scripts") == 0);
assert(system("echo remove > ../fs/mnt/tabs/last-focused/control") == 0); assert(system("echo remove > ../fs/mnt/tabs/last-focused/control") == 0);
} }