aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--extension/background.js145
-rwxr-xr-xtest.c8
3 files changed, 83 insertions, 72 deletions
diff --git a/README.md b/README.md
index 035bf09..f002452 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
# TabFS
-See [https://omar.website/tabfs/].
+See <https://omar.website/tabfs/>.
diff --git a/extension/background.js b/extension/background.js
index 8c09ff9..d863858 100644
--- a/extension/background.js
+++ b/extension/background.js
@@ -8,7 +8,7 @@ const unix = {
EIO: 5,
ENXIO: 6,
ENOTSUP: 45,
- ETIMEDOUT: 110,
+ ETIMEDOUT: 110, // not on macOS (?)
// Unix file types
S_IFMT: 0170000, // type of file mask
@@ -82,13 +82,9 @@ function sendDebuggerCommand(tabId, method, commandParams) {
);
}
-const BrowserState = { lastFocusedWindowId: null, scriptsForTab: {} };
+const BrowserState = { scriptsForTab: {} };
(function() {
if (TESTING) return;
- browser.windows.getLastFocused().then(window => { BrowserState.lastFocusedWindowId = window.id; });
- browser.windows.onFocusChanged.addListener(windowId => {
- if (windowId !== -1) BrowserState.lastFocusedWindowId = windowId;
- });
chrome.debugger.onEvent.addListener((source, method, params) => {
console.log(source, method, params);
@@ -178,10 +174,9 @@ router["/tabs/by-id"] = {
return { entries: [".", "..", ...tabs.map(tab => String(tab.id))] };
}
};
-// (should these have .txt extensions?)
-// title
-// url
-// text
+// title.txt
+// url.txt
+// text.txt
// TODO: document.html
// TODO: console
@@ -229,67 +224,14 @@ router["/tabs/by-id"] = {
// }
})();
router["/tabs/by-id/*/screenshot.png"] = defineFile(async path => {
+ // FIXME: replace with captureTab
+ // FIXME: hide if tab is not focused
const tabId = parseInt(pathComponent(path, -2));
await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
const {data} = await sendDebuggerCommand(tabId, "Page.captureScreenshot");
return Uint8Array.from(atob(data), c => c.charCodeAt(0));
});
-router["/tabs/by-id/*/resources"] = {
- async readdir({path}) {
- const tabId = parseInt(pathComponent(path, -2));
- await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
- const {frameTree} = await sendDebuggerCommand(tabId, "Page.getResourceTree", {});
- return { entries: [".", "..", ...frameTree.resources.map(r => sanitize(String(r.url).slice(0, 200)))] };
- }
-};
-router["/tabs/by-id/*/resources/*"] = defineFile(async path => {
- const [tabId, suffix] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1)];
- await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
-
- const {frameTree} = await sendDebuggerCommand(tabId, "Page.getResourceTree", {});
- for (let resource of frameTree.resources) {
- const resourceSuffix = sanitize(String(resource.url).slice(0, 200));
- if (resourceSuffix === suffix) {
- let {base64Encoded, content} = await sendDebuggerCommand(tabId, "Page.getResourceContent", {
- frameId: frameTree.frame.id,
- url: resource.url
- });
- if (base64Encoded) { return Uint8Array.from(atob(content), c => c.charCodeAt(0)); }
- return content;
- }
- }
- throw new UnixError(unix.ENOENT);
-});
-router["/tabs/by-id/*/scripts"] = {
- async opendir({path}) {
- const tabId = parseInt(pathComponent(path, -2));
- await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Debugger");
- return { fh: 0 };
- },
- async readdir({path}) {
- const tabId = parseInt(pathComponent(path, -2));
- return { entries: [".", "..", ...BrowserState.scriptsForTab[tabId].map(params => sanitize(params.url).slice(0, 200) + "_" + params.scriptId)] };
- }
-};
-router["/tabs/by-id/*/scripts/*"] = defineFile(async path => {
- const [tabId, suffix] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1)];
- await TabManager.debugTab(tabId);
- await TabManager.enableDomainForTab(tabId, "Page");
- await TabManager.enableDomainForTab(tabId, "Debugger");
-
- const parts = path.split("_"); const scriptId = parts[parts.length - 1];
- const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId});
- return scriptSource;
-
-}, async (path, buf) => {
- const [tabId, suffix] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1)];
- await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Debugger");
-
- const parts = path.split("_"); const scriptId = parts[parts.length - 1];
- await sendDebuggerCommand(tabId, "Debugger.setScriptSource", {scriptId, scriptSource: buf});
- return {};
-});
router["/tabs/by-id/*/control"] = {
// echo remove >> mnt/tabs/by-id/1644/control
async write({path, buf}) {
@@ -303,6 +245,65 @@ router["/tabs/by-id/*/control"] = {
async truncate({path, size}) { return {}; }
};
+// debugger-API-dependent (Chrome-only)
+(function() {
+ router["/tabs/by-id/*/debugger/resources"] = {
+ async readdir({path}) {
+ const tabId = parseInt(pathComponent(path, -2));
+ await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
+ const {frameTree} = await sendDebuggerCommand(tabId, "Page.getResourceTree", {});
+ return { entries: [".", "..", ...frameTree.resources.map(r => sanitize(String(r.url).slice(0, 200)))] };
+ }
+ };
+ router["/tabs/by-id/*/debugger/resources/*"] = defineFile(async path => {
+ const [tabId, suffix] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1)];
+ await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
+
+ const {frameTree} = await sendDebuggerCommand(tabId, "Page.getResourceTree", {});
+ for (let resource of frameTree.resources) {
+ const resourceSuffix = sanitize(String(resource.url).slice(0, 200));
+ if (resourceSuffix === suffix) {
+ let {base64Encoded, content} = await sendDebuggerCommand(tabId, "Page.getResourceContent", {
+ frameId: frameTree.frame.id,
+ url: resource.url
+ });
+ if (base64Encoded) { return Uint8Array.from(atob(content), c => c.charCodeAt(0)); }
+ return content;
+ }
+ }
+ throw new UnixError(unix.ENOENT);
+ });
+ router["/tabs/by-id/*/debugger/scripts"] = {
+ async opendir({path}) {
+ const tabId = parseInt(pathComponent(path, -2));
+ await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Debugger");
+ return { fh: 0 };
+ },
+ async readdir({path}) {
+ const tabId = parseInt(pathComponent(path, -2));
+ return { entries: [".", "..", ...BrowserState.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, -3)), pathComponent(path, -1)];
+ await TabManager.debugTab(tabId);
+ await TabManager.enableDomainForTab(tabId, "Page");
+ await TabManager.enableDomainForTab(tabId, "Debugger");
+
+ const parts = path.split("_"); const scriptId = parts[parts.length - 1];
+ const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId});
+ return scriptSource;
+
+ }, async (path, buf) => {
+ const [tabId, suffix] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1)];
+ await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Debugger");
+
+ const parts = path.split("_"); const scriptId = parts[parts.length - 1];
+ await sendDebuggerCommand(tabId, "Debugger.setScriptSource", {scriptId, scriptSource: buf});
+ return {};
+ });
+})();
+
router["/tabs/by-title"] = {
getattr() {
return {
@@ -332,7 +333,7 @@ router["/tabs/by-title/*"] = {
router["/tabs/last-focused"] = {
// a symbolic link to /tabs/by-id/[id for this tab]
async readlink({path}) {
- const id = (await browser.tabs.query({ active: true, windowId: BrowserState.lastFocusedWindowId }))[0].id;
+ const id = (await browser.tabs.query({ active: true, currentWindow: true }))[0].id;
return { buf: "by-id/" + id };
}
}
@@ -364,10 +365,11 @@ router["/runtime/reload"] = {
};
// Ensure that there are routes for all ancestors. This algorithm is
-// probably not correct, but whatever. I also think it would be
-// better to compute this stuff on the fly, so you could patch more
-// routes in at runtime, but I need to think a bit about how to make
-// that work with wildcards.
+// probably not correct, but whatever. Basically, you need to start at
+// the deepest level, fill in all the parents 1 level up that don't
+// exist yet, then walk up one level at a time. It's important to go
+// one level at a time so you know (for each parent) what all the
+// children will be.
for (let i = 10; i >= 0; i--) {
for (let path of Object.keys(router).filter(key => key.split("/").length === i)) {
path = path.substr(0, path.lastIndexOf("/"));
@@ -385,6 +387,9 @@ for (let i = 10; i >= 0; i--) {
router[path] = { readdir() { return { entries }; } };
}
}
+ // I also think it would be better to compute this stuff on the fly,
+ // so you could patch more routes in at runtime, but I need to think
+ // a bit about how to make that work with wildcards.
}
if (TESTING) { // I wish I could color this section with... a pink background, or something.
const assert = require('assert');
diff --git a/test.c b/test.c
index cb1580a..a02c9d7 100755
--- a/test.c
+++ b/test.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <stdlib.h>
+
+#include <unistd.h>
#include <assert.h>
#include <wordexp.h>
@@ -15,13 +17,17 @@ int file_contents_equal(char* path, char* contents) {
return system("[ \"$contents\" == \"$(cat \"$path\")\" ]") == 0;
}
-char* expand(char* phrase) {
+char* expand(char* phrase) { // expand path with wildcard
wordexp_t result; assert(wordexp(phrase, &result, 0) == 0);
return result.we_wordv[0];
}
// integration tests
int main() {
+ // reload the extension so we know it's the latest code.
+ system("echo reload > fs/mnt/runtime/reload"); // this may error, but it should still have effect
+ sleep(2);
+
assert(system("echo about:blank > fs/mnt/tabs/create") == 0);
assert(file_contents_equal("fs/mnt/tabs/last-focused/url.txt", "about:blank"));
assert(system("file fs/mnt/tabs/last-focused/screenshot.png") == 0); // slow