mirror of
https://github.com/vale981/tridactyl
synced 2025-03-04 17:11:40 -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 */
|
/* tslint:disable:import-spacing */
|
||||||
|
|
||||||
import * as proxy_background from "@src/lib/browser_proxy_background"
|
import * as proxy_background from "@src/lib/browser_proxy_background"
|
||||||
|
|
||||||
import * as controller from "@src/lib/controller"
|
import * as controller from "@src/lib/controller"
|
||||||
import * as perf from "@src/perf"
|
import * as perf from "@src/perf"
|
||||||
import { listenForCounters } 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 meta from "@src/background/meta"
|
||||||
import * as Logging from "@src/lib/logging"
|
import * as Logging from "@src/lib/logging"
|
||||||
import * as Proxy from "@src/lib/proxy"
|
import * as Proxy from "@src/lib/proxy"
|
||||||
|
import { tabsProxy } from "@src/lib/tabs"
|
||||||
|
|
||||||
// Add various useful modules to the window for debugging
|
// Add various useful modules to the window for debugging
|
||||||
;(window as any).tri = Object.assign(Object.create(null), {
|
;(window as any).tri = Object.assign(Object.create(null), {
|
||||||
|
@ -52,6 +52,7 @@ import * as Proxy from "@src/lib/proxy"
|
||||||
R,
|
R,
|
||||||
perf,
|
perf,
|
||||||
meta,
|
meta,
|
||||||
|
tabs: tabsProxy,
|
||||||
})
|
})
|
||||||
|
|
||||||
import { HintingCmds } from "@src/background/hinting"
|
import { HintingCmds } from "@src/background/hinting"
|
||||||
|
|
|
@ -83,6 +83,7 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = await import("@src/lib/controller")
|
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 excmds_content = await import("@src/.excmds_content.generated")
|
||||||
const hinting_content = await import("@src/content/hinting")
|
const hinting_content = await import("@src/content/hinting")
|
||||||
// Hook the keyboard up to the controller
|
// Hook the keyboard up to the controller
|
||||||
|
@ -124,6 +125,7 @@ messaging.addListener(
|
||||||
"controller_content",
|
"controller_content",
|
||||||
messaging.attributeCaller(controller),
|
messaging.attributeCaller(controller),
|
||||||
)
|
)
|
||||||
|
messaging.addListener("omniscient_content", messaging.attributeCaller(omniscient_controller))
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/require-await
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
messaging.addListener("alive", async () => true)
|
messaging.addListener("alive", async () => true)
|
||||||
|
|
|
@ -9,6 +9,7 @@ export type TabMessageType =
|
||||||
| "controller_content"
|
| "controller_content"
|
||||||
| "commandline_content"
|
| "commandline_content"
|
||||||
| "finding_content"
|
| "finding_content"
|
||||||
|
| "omniscient_content"
|
||||||
| "commandline_cmd"
|
| "commandline_cmd"
|
||||||
| "commandline_frame"
|
| "commandline_frame"
|
||||||
| "state"
|
| "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