2020-10-28 04:53:39 +01:00
|
|
|
const TESTING = (typeof chrome === 'undefined');
|
|
|
|
|
2018-11-12 01:18:21 +01:00
|
|
|
const unix = {
|
|
|
|
EPERM: 1,
|
|
|
|
ENOENT: 2,
|
|
|
|
ESRCH: 3,
|
|
|
|
EINTR: 4,
|
|
|
|
EIO: 5,
|
|
|
|
ENXIO: 6,
|
2019-02-28 08:38:12 +01:00
|
|
|
ENOTSUP: 45,
|
2018-11-12 01:18:21 +01:00
|
|
|
|
|
|
|
// Unix file types
|
|
|
|
S_IFMT: 0170000, // type of file mask
|
|
|
|
S_IFIFO: 010000, // named pipe (fifo)
|
|
|
|
S_IFCHR: 020000, // character special
|
|
|
|
S_IFDIR: 040000, // directory
|
|
|
|
S_IFBLK: 060000, // block special
|
|
|
|
S_IFREG: 0100000, // regular
|
|
|
|
S_IFLNK: 0120000, // symbolic link
|
|
|
|
S_IFSOCK: 0140000, // socket
|
|
|
|
}
|
|
|
|
|
2020-12-02 20:58:37 +01:00
|
|
|
class UnixError extends Error {
|
2020-12-02 22:45:20 +01:00
|
|
|
constructor(error) { super(); this.name = "UnixError"; this.error = error; }
|
2018-11-22 11:52:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-24 07:15:29 +01:00
|
|
|
function pathComponent(path, i) {
|
|
|
|
const components = path.split('/');
|
|
|
|
return components[i >= 0 ? i : components.length + i];
|
|
|
|
}
|
2020-12-02 22:45:20 +01:00
|
|
|
function sanitize(s) { return s.replace(/[^A-Za-z0-9_\-\.]/gm, '_'); }
|
2020-12-12 06:25:03 +01:00
|
|
|
function utf8(str) {
|
2020-12-04 18:58:09 +01:00
|
|
|
var utf8 = [];
|
|
|
|
for (var i=0; i < str.length; i++) {
|
|
|
|
var charcode = str.charCodeAt(i);
|
|
|
|
if (charcode < 0x80) utf8.push(charcode);
|
|
|
|
else if (charcode < 0x800) {
|
|
|
|
utf8.push(0xc0 | (charcode >> 6),
|
|
|
|
0x80 | (charcode & 0x3f));
|
|
|
|
}
|
|
|
|
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
|
|
|
utf8.push(0xe0 | (charcode >> 12),
|
|
|
|
0x80 | ((charcode>>6) & 0x3f),
|
|
|
|
0x80 | (charcode & 0x3f));
|
|
|
|
}
|
|
|
|
// surrogate pair
|
|
|
|
else {
|
|
|
|
i++;
|
|
|
|
charcode = ((charcode&0x3ff)<<10)|(str.charCodeAt(i)&0x3ff)
|
|
|
|
utf8.push(0xf0 | (charcode >>18),
|
|
|
|
0x80 | ((charcode>>12) & 0x3f),
|
|
|
|
0x80 | ((charcode>>6) & 0x3f),
|
|
|
|
0x80 | (charcode & 0x3f));
|
|
|
|
}
|
|
|
|
}
|
2020-12-12 06:25:03 +01:00
|
|
|
return utf8;
|
2020-10-30 07:16:06 +01:00
|
|
|
}
|
2018-11-24 07:15:29 +01:00
|
|
|
|
2020-12-12 00:18:26 +01:00
|
|
|
const TabManager = {
|
|
|
|
tabState: {},
|
2020-12-02 20:58:37 +01:00
|
|
|
|
2020-12-12 00:18:26 +01:00
|
|
|
debugTab: async function(tabId) {
|
|
|
|
this.tabState[tabId] = this.tabState[tabId] || {};
|
|
|
|
if (this.tabState[tabId].debugging) {
|
|
|
|
this.tabState[tabId].debugging += 1;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
await new Promise((resolve, reject) => chrome.debugger.attach({tabId}, "1.3", async () => {
|
|
|
|
if (chrome.runtime.lastError) {
|
|
|
|
if (chrome.runtime.lastError.message.indexOf('Another debugger is already attached') !== -1) {
|
2020-12-12 11:36:37 +01:00
|
|
|
chrome.debugger.detach({tabId}, async () => {
|
|
|
|
await TabManager.debugTab(tabId);
|
|
|
|
resolve();
|
|
|
|
});
|
2020-12-12 00:18:26 +01:00
|
|
|
} else {
|
|
|
|
reject(chrome.runtime.lastError); return;
|
|
|
|
}
|
|
|
|
return;
|
2020-12-05 02:40:03 +01:00
|
|
|
}
|
2020-12-12 00:18:26 +01:00
|
|
|
this.tabState[tabId].debugging = 1; resolve();
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
},
|
|
|
|
enableDomainForTab: async function(tabId, domain) {
|
|
|
|
this.tabState[tabId] = this.tabState[tabId] || {};
|
|
|
|
if (this.tabState[tabId][domain]) { this.tabState[tabId][domain] += 1;
|
|
|
|
} else {
|
|
|
|
await sendDebuggerCommand(tabId, `${domain}.enable`, {});
|
|
|
|
this.tabState[tabId][domain] = 1;
|
|
|
|
}
|
2020-11-22 13:30:57 +01:00
|
|
|
}
|
2020-12-12 00:18:26 +01:00
|
|
|
};
|
|
|
|
|
2020-11-22 13:30:57 +01:00
|
|
|
function sendDebuggerCommand(tabId, method, commandParams) {
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
chrome.debugger.sendCommand({tabId}, method, commandParams, result => {
|
2020-12-05 02:40:03 +01:00
|
|
|
if (result) { resolve(result); } else { reject(chrome.runtime.lastError); }
|
2020-11-22 13:30:57 +01:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-12-12 11:36:37 +01:00
|
|
|
const BrowserState = { lastFocusedWindowId: null, scriptsForTab: {} };
|
2020-12-12 00:18:26 +01:00
|
|
|
(function() {
|
|
|
|
browser.windows.getLastFocused().then(window => { BrowserState.lastFocusedWindowId = window.id; });
|
|
|
|
browser.windows.onFocusChanged.addListener(windowId => {
|
|
|
|
if (windowId !== -1) BrowserState.lastFocusedWindowId = windowId;
|
|
|
|
});
|
2020-12-12 11:36:37 +01:00
|
|
|
|
|
|
|
chrome.debugger.onEvent.addListener((source, method, params) => {
|
|
|
|
console.log(source, method, params);
|
|
|
|
if (method === "Debugger.scriptParsed") {
|
|
|
|
BrowserState.scriptsForTab[source.tabId] = BrowserState.scriptsForTab[source.tabId] || [];
|
|
|
|
BrowserState.scriptsForTab[source.tabId].push(params);
|
2020-12-13 14:21:43 +01:00
|
|
|
// FIXME: clear these out when page changes in tab (how?)
|
2020-12-12 11:36:37 +01:00
|
|
|
}
|
|
|
|
});
|
2020-12-12 00:18:26 +01:00
|
|
|
})();
|
2020-11-22 22:14:54 +01:00
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
const router = {};
|
|
|
|
|
2020-12-12 00:45:19 +01:00
|
|
|
const Cache = {
|
|
|
|
// used when you open a file to cache the content we got from the browser
|
|
|
|
// until you close that file.
|
|
|
|
store: {}, nextHandle: 0,
|
|
|
|
storeObject(object) {
|
|
|
|
const handle = ++this.nextHandle;
|
|
|
|
this.store[handle] = object;
|
|
|
|
return handle;
|
|
|
|
},
|
|
|
|
getObjectForHandle(handle) { return this.store[handle]; },
|
|
|
|
removeObjectForHandle(handle) { delete this.store[handle]; }
|
|
|
|
};
|
2020-12-12 06:25:03 +01:00
|
|
|
function toArray(stringOrArray) {
|
|
|
|
if (typeof stringOrArray == 'string') { return utf8(stringOrArray); }
|
|
|
|
else { return stringOrArray; }
|
|
|
|
}
|
2020-12-13 14:21:43 +01:00
|
|
|
const defineFile = (getData, setData) => ({
|
2020-12-12 06:25:03 +01:00
|
|
|
async getattr({path}) {
|
|
|
|
return {
|
2020-12-13 14:21:43 +01:00
|
|
|
st_mode: unix.S_IFREG | 0444 | (setData ? 0222 : 0),
|
2020-12-12 06:25:03 +01:00
|
|
|
st_nlink: 1,
|
2020-12-13 14:21:43 +01:00
|
|
|
st_size: toArray(await getData(path)).length
|
2020-12-12 06:25:03 +01:00
|
|
|
};
|
|
|
|
},
|
2020-12-13 14:21:43 +01:00
|
|
|
async open({path}) { return { fh: Cache.storeObject(toArray(await getData(path))) }; },
|
2020-12-12 06:25:03 +01:00
|
|
|
async read({path, fh, size, offset}) {
|
|
|
|
return { buf: String.fromCharCode(...Cache.getObjectForHandle(fh).slice(offset, offset + size)) }
|
|
|
|
},
|
2020-12-13 14:21:43 +01:00
|
|
|
async write({path, buf}) {
|
|
|
|
// FIXME: patch
|
|
|
|
setData(path, buf); return {size: utf8(buf).length};
|
|
|
|
},
|
2020-12-12 06:25:03 +01:00
|
|
|
async release({fh}) { Cache.removeObjectForHandle(fh); return {}; }
|
|
|
|
});
|
2020-12-12 00:45:19 +01:00
|
|
|
|
|
|
|
router["/tabs/by-id"] = {
|
|
|
|
async readdir() {
|
|
|
|
const tabs = await browser.tabs.query({});
|
2020-12-14 07:02:29 +01:00
|
|
|
return { entries: [".", "..", ...tabs.map(tab => String(tab.id))] };
|
2020-12-12 00:45:19 +01:00
|
|
|
}
|
|
|
|
};
|
2020-12-12 11:36:37 +01:00
|
|
|
// (should these have .txt extensions?)
|
|
|
|
// title
|
|
|
|
// url
|
|
|
|
// text
|
|
|
|
// TODO: document.html
|
|
|
|
|
|
|
|
// TODO: console
|
|
|
|
// TODO: mem (?)
|
|
|
|
// TODO: cpu (?)
|
|
|
|
|
2020-12-14 07:02:29 +01:00
|
|
|
// screenshot.png (FIXME: how to keep from blocking when unfocused?)
|
2020-12-12 11:36:37 +01:00
|
|
|
// TODO: archive.mhtml ?
|
|
|
|
// TODO: printed.pdf
|
|
|
|
// control
|
|
|
|
// resources/
|
|
|
|
// TODO: scripts/
|
|
|
|
|
2020-12-12 00:45:19 +01:00
|
|
|
(function() {
|
2020-12-13 14:21:43 +01:00
|
|
|
const withTab = handler => defineFile(async path => {
|
2020-12-12 05:01:11 +01:00
|
|
|
const tabId = parseInt(pathComponent(path, -2));
|
|
|
|
const tab = await browser.tabs.get(tabId);
|
|
|
|
return handler(tab);
|
2020-12-12 00:45:19 +01:00
|
|
|
});
|
2020-12-13 14:21:43 +01:00
|
|
|
const fromScript = code => defineFile(async path => {
|
2020-12-12 05:01:11 +01:00
|
|
|
const tabId = parseInt(pathComponent(path, -2));
|
|
|
|
return (await browser.tabs.executeScript(tabId, {code}))[0];
|
|
|
|
});
|
|
|
|
|
2020-12-12 00:45:19 +01:00
|
|
|
router["/tabs/by-id/*/url"] = withTab(tab => tab.url + "\n");
|
|
|
|
router["/tabs/by-id/*/title"] = withTab(tab => tab.title + "\n");
|
|
|
|
router["/tabs/by-id/*/text"] = fromScript(`document.body.innerText`);
|
|
|
|
})();
|
2020-12-13 14:21:43 +01:00
|
|
|
router["/tabs/by-id/*/screenshot.png"] = defineFile(async path => {
|
2020-12-12 07:32:46 +01:00
|
|
|
const tabId = parseInt(pathComponent(path, -2));
|
|
|
|
await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
|
2020-12-03 05:24:20 +01:00
|
|
|
|
2020-12-12 07:32:46 +01:00
|
|
|
const {data} = await sendDebuggerCommand(tabId, "Page.captureScreenshot");
|
|
|
|
return Uint8Array.from(atob(data), c => c.charCodeAt(0));
|
|
|
|
});
|
2020-11-22 13:30:57 +01:00
|
|
|
router["/tabs/by-id/*/resources"] = {
|
2020-12-01 01:01:56 +01:00
|
|
|
async readdir({path}) {
|
2020-11-22 13:30:57 +01:00
|
|
|
const tabId = parseInt(pathComponent(path, -2));
|
2020-12-12 11:36:37 +01:00
|
|
|
await TabManager.debugTab(tabId); await TabManager.enableDomainForTab(tabId, "Page");
|
2020-11-22 13:30:57 +01:00
|
|
|
const {frameTree} = await sendDebuggerCommand(tabId, "Page.getResourceTree", {});
|
2020-12-14 07:02:29 +01:00
|
|
|
return { entries: [".", "..", ...frameTree.resources.map(r => sanitize(String(r.url).slice(0, 200)))] };
|
2020-11-22 13:30:57 +01:00
|
|
|
}
|
2020-11-22 13:42:16 +01:00
|
|
|
};
|
2020-12-13 14:21:43 +01:00
|
|
|
router["/tabs/by-id/*/resources/*"] = defineFile(async path => {
|
2020-12-12 06:25:03 +01:00
|
|
|
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;
|
2020-11-22 13:42:16 +01:00
|
|
|
}
|
|
|
|
}
|
2020-12-12 06:25:03 +01:00
|
|
|
throw new UnixError(unix.ENOENT);
|
|
|
|
});
|
2020-12-12 11:36:37 +01:00
|
|
|
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));
|
2020-12-14 07:02:29 +01:00
|
|
|
return { entries: [".", "..", ...BrowserState.scriptsForTab[tabId].map(params => sanitize(params.url).slice(0, 200) + "_" + params.scriptId)] };
|
2020-12-12 11:36:37 +01:00
|
|
|
}
|
|
|
|
};
|
2020-12-13 14:21:43 +01:00
|
|
|
router["/tabs/by-id/*/scripts/*"] = defineFile(async path => {
|
2020-12-12 11:51:09 +01:00
|
|
|
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];
|
|
|
|
const {scriptSource} = await sendDebuggerCommand(tabId, "Debugger.getScriptSource", {scriptId});
|
|
|
|
return scriptSource;
|
2020-12-13 14:21:43 +01:00
|
|
|
|
|
|
|
}, 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 {};
|
2020-12-12 11:51:09 +01:00
|
|
|
});
|
2020-11-22 13:42:16 +01:00
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
router["/tabs/by-id/*/control"] = {
|
2020-11-06 04:09:44 +01:00
|
|
|
// echo remove >> mnt/tabs/by-id/1644/control
|
2020-12-01 01:01:56 +01:00
|
|
|
async write({path, buf}) {
|
2020-10-28 04:53:39 +01:00
|
|
|
const tabId = parseInt(pathComponent(path, -2));
|
2020-11-06 04:09:44 +01:00
|
|
|
const command = buf.trim();
|
|
|
|
// can use `discard`, `remove`, `reload`, `goForward`, `goBack`...
|
|
|
|
// see https://developer.chrome.com/extensions/tabs
|
|
|
|
await new Promise(resolve => chrome.tabs[command](tabId, resolve));
|
2020-12-13 14:21:43 +01:00
|
|
|
return {size: utf8(buf).length};
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
router["/tabs/by-title"] = {
|
2020-12-14 07:02:29 +01:00
|
|
|
getattr() {
|
|
|
|
return {
|
|
|
|
st_mode: unix.S_IFDIR | 0777,
|
|
|
|
st_nlink: 3,
|
|
|
|
st_size: 0,
|
|
|
|
};
|
|
|
|
},
|
2020-12-01 01:01:56 +01:00
|
|
|
async readdir() {
|
2020-10-28 04:53:39 +01:00
|
|
|
const tabs = await browser.tabs.query({});
|
2020-12-14 07:02:29 +01:00
|
|
|
return { entries: [".", "..", ...tabs.map(tab => sanitize(String(tab.title).slice(0, 200)) + "_" + String(tab.id))] };
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
router["/tabs/by-title/*"] = {
|
2020-10-29 23:56:57 +01:00
|
|
|
// a symbolic link to /tabs/by-id/[id for this tab]
|
2020-12-01 01:01:56 +01:00
|
|
|
async readlink({path}) {
|
2020-12-12 11:51:09 +01:00
|
|
|
const parts = path.split("_"); const id = parts[parts.length - 1];
|
2020-12-01 01:01:56 +01:00
|
|
|
return { buf: "../by-id/" + id };
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|
|
|
|
};
|
2019-02-25 22:02:25 +01:00
|
|
|
|
2020-11-22 20:52:08 +01:00
|
|
|
router["/tabs/last-focused"] = {
|
|
|
|
// a symbolic link to /tabs/by-id/[id for this tab]
|
2020-12-01 01:01:56 +01:00
|
|
|
async readlink({path}) {
|
2020-12-12 00:18:26 +01:00
|
|
|
const id = (await browser.tabs.query({ active: true, windowId: BrowserState.lastFocusedWindowId }))[0].id;
|
2020-12-01 01:01:56 +01:00
|
|
|
return { buf: "by-id/" + id };
|
2020-11-22 20:52:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-29 23:56:57 +01:00
|
|
|
// 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.
|
2020-10-28 04:53:39 +01:00
|
|
|
for (let key in router) {
|
|
|
|
let path = key;
|
|
|
|
while (path !== "/") { // walk upward through the path
|
|
|
|
path = path.substr(0, path.lastIndexOf("/"));
|
2020-10-28 22:14:57 +01:00
|
|
|
if (path == '') path = '/';
|
2020-10-28 04:53:39 +01:00
|
|
|
|
|
|
|
if (!router[path]) {
|
|
|
|
// find all direct children
|
2020-10-28 22:14:57 +01:00
|
|
|
let children = Object.keys(router)
|
|
|
|
.filter(k => k.startsWith(path) &&
|
|
|
|
(k.match(/\//g) || []).length ===
|
|
|
|
(path.match(/\//g) || []).length + 1)
|
|
|
|
.map(k => k.substr((path === '/' ? 0 : path.length) + 1).split('/')[0]);
|
2020-12-14 07:02:29 +01:00
|
|
|
children = [".", "..", ...new Set(children)];
|
2020-10-28 04:53:39 +01:00
|
|
|
|
2020-12-02 22:45:20 +01:00
|
|
|
router[path] = { readdir() { return { entries: children }; } };
|
2018-11-15 08:52:26 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|
2020-11-06 04:09:44 +01:00
|
|
|
if (TESTING) { // I wish I could color this section with... a pink background, or something.
|
2020-10-28 04:53:39 +01:00
|
|
|
const assert = require('assert');
|
|
|
|
(async () => {
|
2020-12-01 01:01:56 +01:00
|
|
|
assert.deepEqual(await router['/tabs/by-id/*'].readdir(), ['url', 'title', 'text', 'control']);
|
|
|
|
assert.deepEqual(await router['/'].readdir(), ['tabs']);
|
|
|
|
assert.deepEqual(await router['/tabs'].readdir(), ['by-id', 'by-title']);
|
2020-10-28 22:14:57 +01:00
|
|
|
|
|
|
|
assert.deepEqual(findRoute('/tabs/by-id/TABID/url'), router['/tabs/by-id/*/url']);
|
2020-10-28 04:53:39 +01:00
|
|
|
})()
|
|
|
|
}
|
2018-11-12 01:31:02 +01:00
|
|
|
|
2020-12-02 22:45:20 +01:00
|
|
|
|
|
|
|
// fill in default implementations of fs ops
|
|
|
|
for (let key in router) {
|
|
|
|
// if readdir -> directory -> add getattr, opendir, releasedir
|
|
|
|
if (router[key].readdir) {
|
|
|
|
router[key] = {
|
|
|
|
getattr() {
|
|
|
|
return {
|
|
|
|
st_mode: unix.S_IFDIR | 0755,
|
|
|
|
st_nlink: 3,
|
|
|
|
st_size: 0,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
opendir({path}) { return { fh: 0 }; },
|
|
|
|
releasedir({path}) { return {}; },
|
|
|
|
...router[key]
|
|
|
|
};
|
|
|
|
|
|
|
|
} else if (router[key].readlink) {
|
|
|
|
router[key] = {
|
|
|
|
async getattr({path}) {
|
2020-12-14 07:02:29 +01:00
|
|
|
const st_size = (await this.readlink({path})).buf.length + 1;
|
2020-12-02 22:45:20 +01:00
|
|
|
return {
|
|
|
|
st_mode: unix.S_IFLNK | 0444,
|
|
|
|
st_nlink: 1,
|
|
|
|
// You _must_ return correct linkee path length from getattr!
|
|
|
|
st_size
|
|
|
|
};
|
|
|
|
},
|
|
|
|
...router[key]
|
|
|
|
};
|
|
|
|
|
|
|
|
} else if (router[key].read || router[key].write) {
|
|
|
|
router[key] = {
|
|
|
|
async getattr() {
|
|
|
|
return {
|
|
|
|
st_mode: unix.S_IFREG | ((router[key].read && 0444) || (router[key].write && 0222)),
|
|
|
|
st_nlink: 1,
|
|
|
|
st_size: 100 // FIXME
|
|
|
|
};
|
|
|
|
},
|
|
|
|
open() {
|
|
|
|
return { fh: 0 };
|
|
|
|
},
|
|
|
|
release() {
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
...router[key]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
console.log(router);
|
2018-11-15 08:52:26 +01:00
|
|
|
function findRoute(path) {
|
2019-03-02 10:48:56 +01:00
|
|
|
let pathSegments = path.split("/");
|
2020-10-28 04:53:39 +01:00
|
|
|
|
2019-03-02 10:48:56 +01:00
|
|
|
if (pathSegments[pathSegments.length - 1].startsWith("._")) {
|
|
|
|
throw new UnixError(unix.ENOTSUP); // Apple Double file for xattrs
|
|
|
|
}
|
2018-11-12 01:31:02 +01:00
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
let routingPath = "";
|
|
|
|
for (let segment of pathSegments) {
|
2020-10-28 22:14:57 +01:00
|
|
|
if (routingPath === "/") { routingPath = ""; }
|
|
|
|
|
|
|
|
if (router[routingPath + "/*"]) {
|
|
|
|
routingPath += "/*";
|
|
|
|
} else if (router[routingPath + "/" + segment]) {
|
2020-10-28 04:53:39 +01:00
|
|
|
routingPath += "/" + segment;
|
|
|
|
} else {
|
2020-10-28 22:14:57 +01:00
|
|
|
throw new UnixError(unix.ENOENT);
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|
2018-11-12 01:31:02 +01:00
|
|
|
}
|
2020-10-28 04:53:39 +01:00
|
|
|
return router[routingPath];
|
2018-11-22 11:52:54 +01:00
|
|
|
}
|
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
let port;
|
|
|
|
async function onMessage(req) {
|
2020-12-03 01:57:06 +01:00
|
|
|
if (req.buf) req.buf = atob(req.buf);
|
2020-12-12 11:36:37 +01:00
|
|
|
console.log('req', req);
|
2018-11-29 18:53:03 +01:00
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
let response = { op: req.op, error: unix.EIO };
|
|
|
|
/* console.time(req.op + ':' + req.path);*/
|
|
|
|
try {
|
2020-12-01 01:01:56 +01:00
|
|
|
response = await findRoute(req.path)[req.op](req);
|
2020-10-28 04:53:39 +01:00
|
|
|
response.op = req.op;
|
2020-12-04 18:58:09 +01:00
|
|
|
if (response.buf) { response.buf = btoa(response.buf); }
|
2018-11-29 18:53:03 +01:00
|
|
|
|
2018-11-22 11:52:54 +01:00
|
|
|
} catch (e) {
|
2019-02-26 08:08:52 +01:00
|
|
|
console.error(e);
|
2018-11-11 20:44:36 +01:00
|
|
|
response = {
|
2018-11-22 11:52:54 +01:00
|
|
|
op: req.op,
|
|
|
|
error: e instanceof UnixError ? e.error : unix.EIO
|
|
|
|
}
|
2018-11-12 01:18:21 +01:00
|
|
|
}
|
2018-11-24 19:58:33 +01:00
|
|
|
/* console.timeEnd(req.op + ':' + req.path);*/
|
2018-11-11 14:32:44 +01:00
|
|
|
|
2020-12-12 11:36:37 +01:00
|
|
|
console.log('resp', response);
|
2020-10-22 15:47:00 +02:00
|
|
|
port.postMessage(response);
|
2018-11-11 14:32:44 +01:00
|
|
|
};
|
2018-11-29 18:53:03 +01:00
|
|
|
|
|
|
|
function tryConnect() {
|
2020-10-24 07:03:13 +02:00
|
|
|
port = chrome.runtime.connectNative('com.rsnous.tabfs');
|
2020-10-17 06:56:49 +02:00
|
|
|
port.onMessage.addListener(onMessage);
|
2020-10-28 04:53:39 +01:00
|
|
|
port.onDisconnect.addListener(p => {console.log('disconnect', p)});
|
2018-11-29 18:53:03 +01:00
|
|
|
}
|
|
|
|
|
2020-10-28 04:53:39 +01:00
|
|
|
if (!TESTING) {
|
2018-11-29 18:53:03 +01:00
|
|
|
tryConnect();
|
2020-10-28 04:53:39 +01:00
|
|
|
}
|