diff options
author | Omar Rizwan <omar@omar.website> | 2020-12-29 03:52:17 -0800 |
---|---|---|
committer | Omar Rizwan <omar@omar.website> | 2020-12-29 03:52:17 -0800 |
commit | 705b245be5ea7e69f04c1ab370962de054800bb0 (patch) | |
tree | d2ffb73fcb548855d45aae87cf79ee3e71829502 | |
parent | 44c8dfcec6e04a19ef78e2de38c97f49f9fcb8d5 (diff) |
prevent explosion of scriptsForTab!
-rw-r--r-- | extension/background.js | 68 | ||||
-rw-r--r-- | tabfs.md | 57 | ||||
-rw-r--r-- | test/test.c | 3 |
3 files changed, 81 insertions, 47 deletions
diff --git a/extension/background.js b/extension/background.js index 4de9b67..51b4043 100644 --- a/extension/background.js +++ b/extension/background.js @@ -52,48 +52,48 @@ async function detachDebugger(tabId) { else { resolve(); } })); } -const TabManager = { - 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 - await sendDebuggerCommand(tabId, `${domain}.enable`, {}); - } -}; -function sendDebuggerCommand(tabId, method, commandParams) { - return new Promise((resolve, reject) => - chrome.debugger.sendCommand({tabId}, method, commandParams, result => { - if (result) { resolve(result); } else { reject(chrome.runtime.lastError); } - }) - ); -} - -const BrowserState = { scriptsForTab: {} }; -(function() { +const TabManager = (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] = []; + TabManager.scriptsForTab[source.tabId] = []; } else if (method === "Debugger.scriptParsed") { - BrowserState.scriptsForTab[source.tabId] = BrowserState.scriptsForTab[source.tabId] || []; - BrowserState.scriptsForTab[source.tabId].push(params); + TabManager.scriptsForTab[source.tabId] = TabManager.scriptsForTab[source.tabId] || []; + TabManager.scriptsForTab[source.tabId].push(params); } }); + + return { + scriptsForTab: {}, + 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) { + return new Promise((resolve, reject) => + chrome.debugger.sendCommand({tabId}, method, commandParams, result => { + if (result) { resolve(result); } else { reject(chrome.runtime.lastError); } + }) + ); +} const router = {}; @@ -288,14 +288,16 @@ router["/tabs/by-id/*/control"] = { }, async readdir({path}) { 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 => { const [tabId, suffix] = [parseInt(pathComponent(path, -4)), pathComponent(path, -1)]; await TabManager.debugTab(tabId); + console.log('BEFORE', TabManager.scriptsForTab[tabId].length); await TabManager.enableDomainForTab(tabId, "Page"); await TabManager.enableDomainForTab(tabId, "Debugger"); + console.log('AFTER', TabManager.scriptsForTab[tabId].length) const parts = path.split("_"); const scriptId = parts[parts.length - 1]; const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId}); @@ -358,7 +358,18 @@ TODO: make diagrams? 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 `fuse_main` in `tabfs.c`, which makes everything @@ -379,18 +390,20 @@ GPLv3 filesystem, even ones you'd assume are reasonably battle-tested and 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 equiv for Windows? can you bridge to the remote debugging APIs that all of them already have to get the augmented functionality? or just implement it all with JS monkey patching? -- window management. tab management where you can move tabs - -- snapshotting (snapshot.html / snapshot.mhtml file) - -- fix leaks - -- elim unnecessary round-trips / browser API calls +- window management. tab management where you can move tabs. 'merge + all windows' ## 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 to automate something? lightness -- 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 - interested in an interactive, end-user context. augmenting the way I - use my normal browser. that's why this is an extension. it doesn't - require your browser to run in some weird remote debugging mode that - you'd always forget to turn on. it just [stays +- a lot of existing uses of these APIs are in an automation context: + testing your code on a robotic browser as part of some pipeline. I'm + much more interested in an interactive, end-user context. augmenting + the way I use my everyday browser. that's why this is an + extension. it doesn't require your browser to run in some weird + remote debugging mode that you'd always forget to turn on. it just + [stays 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.) - now you have this whole 'language', this whole toolset, to control diff --git a/test/test.c b/test/test.c index b4fe5b4..edb1c99 100644 --- a/test/test.c +++ b/test/test.c @@ -41,6 +41,9 @@ int main() { 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/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); } |