summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extension/background.js42
-rw-r--r--fs/Makefile1
-rw-r--r--fs/tabfs.c34
-rw-r--r--tabfs.md7
-rw-r--r--test/test-textarea.html10
-rw-r--r--test/test.c24
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
diff --git a/fs/tabfs.c b/fs/tabfs.c
index 8c28dab..fd1a15d 100644
--- a/fs/tabfs.c
+++ b/fs/tabfs.c
@@ -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,
diff --git a/tabfs.md b/tabfs.md
index 8fa9c87..81c05f1 100644
--- a/tabfs.md
+++ b/tabfs.md
@@ -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);
}