diff options
-rw-r--r-- | extension/background.js | 42 | ||||
-rw-r--r-- | fs/Makefile | 1 | ||||
-rw-r--r-- | fs/tabfs.c | 34 | ||||
-rw-r--r-- | tabfs.md | 7 | ||||
-rw-r--r-- | test/test-textarea.html | 10 | ||||
-rw-r--r-- | test/test.c | 24 |
6 files changed, 102 insertions, 16 deletions
diff --git a/extension/background.js b/extension/background.js index 129b956..5e487cf 100644 --- a/extension/background.js +++ b/extension/background.js @@ -19,6 +19,9 @@ const unix = { S_IFREG: 0100000, // regular S_IFLNK: 0120000, // symbolic link S_IFSOCK: 0140000, // socket + + // Open flags + O_TRUNC: 01000, } class UnixError extends Error { @@ -110,6 +113,7 @@ const Cache = { return handle; }, getObjectForHandle(handle) { return this.store[handle]; }, + setObjectForHandle(handle, object) { this.store[handle] = object; }, removeObjectForHandle(handle) { delete this.store[handle]; } }; function toUtf8Array(stringOrArray) { @@ -141,7 +145,10 @@ const defineFile = (getData, setData) => ({ // We call getData() once when the file is opened, then cache that // data for all subsequent reads from that application. - async open({path}) { return { fh: Cache.storeObject(toUtf8Array(await getData(path))) }; }, + async open({path, flags}) { + const data = !(flags & unix.O_TRUNC) ? await getData(path) : ""; + return { fh: Cache.storeObject(toUtf8Array(data)) }; + }, async read({path, fh, size, offset}) { return { buf: String.fromCharCode(...Cache.getObjectForHandle(fh).slice(offset, offset + size)) } }, @@ -150,9 +157,11 @@ const defineFile = (getData, setData) => ({ const bufarr = stringToUtf8Array(buf); if (offset + bufarr.length > arr.length) { const newArr = new Uint8Array(offset + bufarr.length); - newArr.set(arr); arr = newArr; + newArr.set(arr.slice(0, Math.min(offset, arr.length))); + arr = newArr; + Cache.setObjectForHandle(fh, arr); } - for (let i = 0; i < bufarr.length; i++) { arr[offset + i] = bufarr[i]; } + arr.set(bufarr, offset); // I guess caller should override write() if they want to actually // patch and not just re-set the whole string (for example, // if they want to hot-reload just one function the user modified) @@ -165,11 +174,12 @@ const defineFile = (getData, setData) => ({ // (but `echo hi > foo.txt`, the main thing I care about, uses // O_TRUNC which thankfully doesn't do that) let arr = toUtf8Array(await getData(path)); - if (size > arr.length) { + if (size !== arr.length) { const newArr = new Uint8Array(size); - newArr.set(arr); arr = newArr; + newArr.set(arr.slice(0, Math.min(size, arr.length))); + arr = newArr; } - await setData(path, utf8ArrayToString(arr.slice(0, size))); return {}; + await setData(path, utf8ArrayToString(arr)); return {}; } }); @@ -439,6 +449,26 @@ router["/tabs/by-id/*/active"] = { }); })(); +router["/tabs/by-id/*/textareas"] = { + async readdir({path}) { + const tabId = parseInt(pathComponent(path, -2)); + // TODO: assign new IDs to textareas without them? + const code = `Array.from(document.querySelectorAll('textarea')).map(e => e.id).filter(id => id)` + const ids = (await browser.tabs.executeScript(tabId, {code}))[0]; + return { entries: [".", "..", ...ids.map(id => `${id}.txt`)] }; + } +}; +router["/tabs/by-id/*/textareas/*"] = defineFile(async path => { + const [tabId, textareaId] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1).slice(0, -4)]; + const code = `document.getElementById('${textareaId}').value`; + const textareaValue = (await browser.tabs.executeScript(tabId, {code}))[0]; + return textareaValue; +}, async (path, buf) => { + const [tabId, textareaId] = [parseInt(pathComponent(path, -3)), pathComponent(path, -1).slice(0, -4)]; + const code = `document.getElementById('${textareaId}').value = unescape('${escape(buf)}')`; + await browser.tabs.executeScript(tabId, {code}); +}); + router["/tabs/by-title"] = { getattr() { return { diff --git a/fs/Makefile b/fs/Makefile index 658adb7..622c2df 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -36,3 +36,4 @@ unmount: killall -9 tabfs || true diskutil unmount force mnt || true fusermount -u mnt || true + umount -f mnt || true @@ -261,7 +261,7 @@ static int tabfs_open(const char *path, struct fuse_file_info *fi) { "open", path, fi->flags); parse_and_free_response(data, size, - "fh: %d", + "fh: %llu", &fi->fh); return 0; @@ -275,7 +275,7 @@ static int tabfs_read(const char *path, char *rdata; size_t rsize; exchange_json(&rdata, &rsize, - "op: %Q, path: %Q, size: %d, offset: %d, fh: %d, flags: %d", + "op: %Q, path: %Q, size: %d, offset: %lld, fh: %llu, flags: %d", "read", path, size, offset, fi->fh, fi->flags); char *scan_buf; int scan_len; @@ -298,7 +298,7 @@ static int tabfs_write(const char *path, char *rdata; size_t rsize; exchange_json(&rdata, &rsize, - "op: %Q, path: %Q, buf: %V, offset: %d, fh: %d, flags: %d", + "op: %Q, path: %Q, buf: %V, offset: %lld, fh: %llu, flags: %d", "write", path, data, size, offset, fi->fh, fi->flags); int ret; @@ -313,7 +313,7 @@ static int tabfs_release(const char *path, struct fuse_file_info *fi) { char *data; size_t size; exchange_json(&data, &size, - "op: %Q, path: %Q, fh: %d", + "op: %Q, path: %Q, fh: %llu", "release", path, fi->fh); parse_and_free_response(data, size, ""); @@ -329,7 +329,7 @@ static int tabfs_opendir(const char *path, struct fuse_file_info *fi) { "opendir", path, fi->flags); parse_and_free_response(rdata, rsize, - "fh: %d", + "fh: %llu", &fi->fh); return 0; @@ -345,7 +345,7 @@ static int tabfs_readdir(const char *path, char *rdata; size_t rsize; exchange_json(&rdata, &rsize, - "op: %Q, path: %Q, offset: %d", + "op: %Q, path: %Q, offset: %lld", "readdir", path, offset); struct json_token t; @@ -365,7 +365,7 @@ static int tabfs_releasedir(const char *path, struct fuse_file_info *fi) { char *rdata; size_t rsize; exchange_json(&rdata, &rsize, - "op: %Q, path: %Q, fh: %d", + "op: %Q, path: %Q, fh: %llu", "releasedir", path, fi->fh); parse_and_free_response(rdata, rsize, ""); @@ -377,7 +377,7 @@ static int tabfs_truncate(const char *path, off_t size) { char *rdata; size_t rsize; exchange_json(&rdata, &rsize, - "op: %Q, path: %Q, size: %d", + "op: %Q, path: %Q, size: %lld", "truncate", path, size); parse_and_free_response(rdata, rsize, ""); @@ -423,6 +423,20 @@ static int tabfs_create(const char *path, mode_t mode, struct fuse_file_info *fi return 0; } +#define want_capability(conn, flag) \ + do { \ + if (conn->capable & flag) { \ + conn->want |= flag; \ + } else { \ + eprintln("warning: " #flag " not supported"); \ + } \ + } while (0) + +static void *tabfs_init(struct fuse_conn_info *conn) { + want_capability(conn, FUSE_CAP_ATOMIC_O_TRUNC); + return NULL; +} + static const struct fuse_operations tabfs_oper = { .getattr = tabfs_getattr, .readlink = tabfs_readlink, @@ -441,6 +455,8 @@ static const struct fuse_operations tabfs_oper = { .mkdir = tabfs_mkdir, .create = tabfs_create, + + .init = tabfs_init, }; int main(int argc, char **argv) { @@ -479,8 +495,10 @@ int main(int argc, char **argv) { argv[0], "-f", #if !defined(__APPLE__) +#if !defined(__FreeBSD__) "-oauto_unmount", #endif +#endif "-odirect_io", getenv("TABFS_MOUNT_DIR"), NULL, @@ -487,6 +487,13 @@ TODO: make diagrams? GPLv3 +## Sponsors + +Thanks to [all the project sponsors](https://github.com/sponsors/osnr). Special +thanks to: + +<a href="https://oss.capital/"><img style="max-width: 50%" src="oss-capital.png"></a> + ## things that could/should be done (maybe you can do these? lots of people are [already pitching in on diff --git a/test/test-textarea.html b/test/test-textarea.html new file mode 100644 index 0000000..9c460fa --- /dev/null +++ b/test/test-textarea.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Textarea Test Page</title> + </head> + <body> + <textarea id="ta">initial text</textarea> + </body> +</html> diff --git a/test/test.c b/test/test.c index d16c724..6f4537d 100644 --- a/test/test.c +++ b/test/test.c @@ -13,7 +13,7 @@ int file_contents_equal(char* path, char* contents) { // hehe: https://twitter.com/ianh_/status/1340450349065244675 setenv("path", path, 1); setenv("contents", contents, 1); - return system("[ \"$contents\" == \"$(cat \"$path\")\" ]") == 0; + return system("bash -c '[ \"$contents\" == \"$(cat \"$path\")\" ]'") == 0; } char* expand(char* phrase) { // expand path with wildcard @@ -77,7 +77,27 @@ int main() { assert(strcmp(hi, "hi") == 0); fclose(console); } - + + // try to shorten the URL (#40) + /* assert(system("echo about:blank > ../fs/mnt/tabs/last-focused/url.txt") == 0); */ + /* assert(file_contents_equal("../fs/mnt/tabs/last-focused/url.txt", "about:blank")); */ + + assert(system("echo remove > ../fs/mnt/tabs/last-focused/control") == 0); + } + + { + assert(system("echo file://$(pwd)/test-textarea.html > ../fs/mnt/tabs/create") == 0); + { + FILE* console = fopen("../fs/mnt/tabs/last-focused/console", "r"); + assert(system("echo \"console.log(document.getElementById('ta').value)\" > ../fs/mnt/tabs/last-focused/execute-script") == 0); + + char ta[100] = {0}; fread(ta, 1, sizeof(ta), console); + assert(strcmp(ta, "initial text") == 0); + + assert(file_contents_equal("../fs/mnt/tabs/last-focused/textareas/ta.txt", ta)); + + fclose(console); + } assert(system("echo remove > ../fs/mnt/tabs/last-focused/control") == 0); } |