mirror of
https://github.com/osnr/TabFS.git
synced 2024-05-04 06:43:14 +02:00
make debugger/ subfolder. remove lastFocused storage (didn't need?)
make test reload on start. test seems to work
This commit is contained in:
parent
b64c0f7016
commit
cc53bdd98d
|
@ -1,3 +1,3 @@
|
||||||
# TabFS
|
# TabFS
|
||||||
|
|
||||||
See [https://omar.website/tabfs/].
|
See <https://omar.website/tabfs/>.
|
||||||
|
|
|
@ -8,7 +8,7 @@ const unix = {
|
||||||
EIO: 5,
|
EIO: 5,
|
||||||
ENXIO: 6,
|
ENXIO: 6,
|
||||||
ENOTSUP: 45,
|
ENOTSUP: 45,
|
||||||
ETIMEDOUT: 110,
|
ETIMEDOUT: 110, // not on macOS (?)
|
||||||
|
|
||||||
// Unix file types
|
// Unix file types
|
||||||
S_IFMT: 0170000, // type of file mask
|
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() {
|
(function() {
|
||||||
if (TESTING) return;
|
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) => {
|
chrome.debugger.onEvent.addListener((source, method, params) => {
|
||||||
console.log(source, method, params);
|
console.log(source, method, params);
|
||||||
|
@ -178,10 +174,9 @@ router["/tabs/by-id"] = {
|
||||||
return { entries: [".", "..", ...tabs.map(tab => String(tab.id))] };
|
return { entries: [".", "..", ...tabs.map(tab => String(tab.id))] };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// (should these have .txt extensions?)
|
// title.txt
|
||||||
// title
|
// url.txt
|
||||||
// url
|
// text.txt
|
||||||
// text
|
|
||||||
// TODO: document.html
|
// TODO: document.html
|
||||||
|
|
||||||
// TODO: console
|
// TODO: console
|
||||||
|
@ -229,67 +224,14 @@ router["/tabs/by-id"] = {
|
||||||
// }
|
// }
|
||||||
})();
|
})();
|
||||||
router["/tabs/by-id/*/screenshot.png"] = defineFile(async path => {
|
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));
|
const tabId = parseInt(pathComponent(path, -2));
|
||||||
await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
|
await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
|
||||||
|
|
||||||
const {data} = await sendDebuggerCommand(tabId, "Page.captureScreenshot");
|
const {data} = await sendDebuggerCommand(tabId, "Page.captureScreenshot");
|
||||||
return Uint8Array.from(atob(data), c => c.charCodeAt(0));
|
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"] = {
|
router["/tabs/by-id/*/control"] = {
|
||||||
// echo remove >> mnt/tabs/by-id/1644/control
|
// echo remove >> mnt/tabs/by-id/1644/control
|
||||||
async write({path, buf}) {
|
async write({path, buf}) {
|
||||||
|
@ -303,6 +245,65 @@ router["/tabs/by-id/*/control"] = {
|
||||||
async truncate({path, size}) { return {}; }
|
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"] = {
|
router["/tabs/by-title"] = {
|
||||||
getattr() {
|
getattr() {
|
||||||
return {
|
return {
|
||||||
|
@ -332,7 +333,7 @@ router["/tabs/by-title/*"] = {
|
||||||
router["/tabs/last-focused"] = {
|
router["/tabs/last-focused"] = {
|
||||||
// a symbolic link to /tabs/by-id/[id for this tab]
|
// a symbolic link to /tabs/by-id/[id for this tab]
|
||||||
async readlink({path}) {
|
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 };
|
return { buf: "by-id/" + id };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,10 +365,11 @@ router["/runtime/reload"] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure that there are routes for all ancestors. This algorithm is
|
// Ensure that there are routes for all ancestors. This algorithm is
|
||||||
// probably not correct, but whatever. I also think it would be
|
// probably not correct, but whatever. Basically, you need to start at
|
||||||
// better to compute this stuff on the fly, so you could patch more
|
// the deepest level, fill in all the parents 1 level up that don't
|
||||||
// routes in at runtime, but I need to think a bit about how to make
|
// exist yet, then walk up one level at a time. It's important to go
|
||||||
// that work with wildcards.
|
// 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 i = 10; i >= 0; i--) {
|
||||||
for (let path of Object.keys(router).filter(key => key.split("/").length === i)) {
|
for (let path of Object.keys(router).filter(key => key.split("/").length === i)) {
|
||||||
path = path.substr(0, path.lastIndexOf("/"));
|
path = path.substr(0, path.lastIndexOf("/"));
|
||||||
|
@ -385,6 +387,9 @@ for (let i = 10; i >= 0; i--) {
|
||||||
router[path] = { readdir() { return { entries }; } };
|
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.
|
if (TESTING) { // I wish I could color this section with... a pink background, or something.
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
8
test.c
8
test.c
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
|
|
||||||
|
@ -15,13 +17,17 @@ int file_contents_equal(char* path, char* contents) {
|
||||||
return system("[ \"$contents\" == \"$(cat \"$path\")\" ]") == 0;
|
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);
|
wordexp_t result; assert(wordexp(phrase, &result, 0) == 0);
|
||||||
return result.we_wordv[0];
|
return result.we_wordv[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// integration tests
|
// integration tests
|
||||||
int main() {
|
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(system("echo about:blank > fs/mnt/tabs/create") == 0);
|
||||||
assert(file_contents_equal("fs/mnt/tabs/last-focused/url.txt", "about:blank"));
|
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
|
assert(system("file fs/mnt/tabs/last-focused/screenshot.png") == 0); // slow
|
||||||
|
|
Loading…
Reference in a new issue