mirror of
https://github.com/vale981/tridactyl
synced 2025-03-04 09:01:39 -05:00
Implement basic proxy providing easy access to tabs from background
This commit implements a basic proxy enabling access to arbitrary values/functions in tabs from the background script, like this: tri.tabs[3].document.title.get().then(console.log) tri.tabs[3].document.title.set("New title!").then(console.log) tri.tabs[9].alert.apply("Hello world!") tri.tabs[12].tri.excmds.js.apply("alert('Hello world!')") Ease of implementation was chosen above ease of use. Enabling reading, writing and calling directly through property accesses instead of forcing the use of .get()/.set()/.apply() will be attempted in another commit.
This commit is contained in:
parent
d59a9d96bb
commit
3806445d85
5 changed files with 94 additions and 1 deletions
|
@ -3,7 +3,6 @@
|
|||
/* tslint:disable:import-spacing */
|
||||
|
||||
import * as proxy_background from "@src/lib/browser_proxy_background"
|
||||
|
||||
import * as controller from "@src/lib/controller"
|
||||
import * as perf from "@src/perf"
|
||||
import { listenForCounters } from "@src/perf"
|
||||
|
@ -30,6 +29,7 @@ import * as commands from "@src/background/commands"
|
|||
import * as meta from "@src/background/meta"
|
||||
import * as Logging from "@src/lib/logging"
|
||||
import * as Proxy from "@src/lib/proxy"
|
||||
import { tabsProxy } from "@src/lib/tabs"
|
||||
|
||||
// Add various useful modules to the window for debugging
|
||||
;(window as any).tri = Object.assign(Object.create(null), {
|
||||
|
@ -52,6 +52,7 @@ import * as Proxy from "@src/lib/proxy"
|
|||
R,
|
||||
perf,
|
||||
meta,
|
||||
tabs: tabsProxy,
|
||||
})
|
||||
|
||||
import { HintingCmds } from "@src/background/hinting"
|
||||
|
|
|
@ -83,6 +83,7 @@ try {
|
|||
}
|
||||
|
||||
const controller = await import("@src/lib/controller")
|
||||
const { omniscient_controller } = await import("@src/lib/omniscient_controller")
|
||||
const excmds_content = await import("@src/.excmds_content.generated")
|
||||
const hinting_content = await import("@src/content/hinting")
|
||||
// Hook the keyboard up to the controller
|
||||
|
@ -124,6 +125,7 @@ messaging.addListener(
|
|||
"controller_content",
|
||||
messaging.attributeCaller(controller),
|
||||
)
|
||||
messaging.addListener("omniscient_content", messaging.attributeCaller(omniscient_controller))
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
messaging.addListener("alive", async () => true)
|
||||
|
|
|
@ -9,6 +9,7 @@ export type TabMessageType =
|
|||
| "controller_content"
|
||||
| "commandline_content"
|
||||
| "finding_content"
|
||||
| "omniscient_content"
|
||||
| "commandline_cmd"
|
||||
| "commandline_frame"
|
||||
| "state"
|
||||
|
|
38
src/lib/omniscient_controller.ts
Normal file
38
src/lib/omniscient_controller.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
export const omniscient_controller = {
|
||||
set: ({ target, value }) => {
|
||||
let result
|
||||
try {
|
||||
const the_last_one = target[target.length - 1]
|
||||
const everything_except_the_last_one = target.slice(
|
||||
0,
|
||||
target.length - 1,
|
||||
)
|
||||
const second_to_last = everything_except_the_last_one.reduce(
|
||||
(acc, prop) => acc[prop],
|
||||
window,
|
||||
)
|
||||
result = second_to_last[the_last_one] = value
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
return result
|
||||
},
|
||||
get: ({ target, _ }) => {
|
||||
let result
|
||||
try {
|
||||
result = target.reduce((acc, prop) => acc[prop], window)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
return result
|
||||
},
|
||||
apply: ({ target, value }) => {
|
||||
let result
|
||||
try {
|
||||
result = target.reduce((acc, prop) => acc[prop], window)(...value)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
return result
|
||||
},
|
||||
}
|
51
src/lib/tabs.ts
Normal file
51
src/lib/tabs.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import * as Messaging from "@src/lib/messaging"
|
||||
|
||||
const allTabs = -1;
|
||||
|
||||
// tabProxy accumulates all accesses to a tab's properties and then, when the
|
||||
// value of said properties are read, set or called, sends a request to the
|
||||
// corresponding tab to either retrieve or set the value.
|
||||
const tabProxy = (tabId, props) => new Proxy(()=>undefined, {
|
||||
get(target, p, receiver) {
|
||||
if (p === Symbol.toPrimitive) {
|
||||
const prop = `tabs[${tabId}].${props.join(".")}`;
|
||||
throw `${prop} cannot be used directly - use ${prop}.get() instead`;
|
||||
}
|
||||
return tabProxy(tabId, props.concat(p));
|
||||
},
|
||||
apply(target, thisArg, argArray) {
|
||||
const last = props[props.length -1];
|
||||
switch (last) {
|
||||
case "get":
|
||||
case "set":
|
||||
case "apply":
|
||||
break;
|
||||
default:
|
||||
const call = `tabs[${tabId}].${props.join(".")}`;
|
||||
const args = `(${argArray.join(", ")})`;
|
||||
throw `${call}${args} cannot be called directly, use ${call}.apply${args} instead`;
|
||||
};
|
||||
let msg = Messaging.messageAllTabs;
|
||||
if (tabId !== allTabs) {
|
||||
msg = (...args) => Messaging.messageTab(tabId, ...args);
|
||||
}
|
||||
return msg("omniscient_content", last, [{target: props.slice(0, props.length - 1), value: argArray}]);
|
||||
},
|
||||
})
|
||||
|
||||
export const tabsProxy = new Proxy(Object.create(null), {
|
||||
get(target, p, receiver) {
|
||||
let id = Number(p);
|
||||
if (typeof id === "number" && isFinite(id)) {
|
||||
// We're accessing tabs[i] - meaning that we should return a proxy
|
||||
// for a single tab
|
||||
return tabProxy(id, [])
|
||||
}
|
||||
throw "Foreground tabs proxy needs to be indexed by tab ID.";
|
||||
// Ideally, if p is a string, we should construct a proxy for all
|
||||
// existing tabs. This unfortunately does not seem to work:
|
||||
// Messaging.messageAllTab seems to return an array of undefined when
|
||||
// running e.g. `tabs.document.title`.
|
||||
// return tabProxy(allTabs, []);
|
||||
}
|
||||
})
|
Loading…
Add table
Reference in a new issue