mirror of
https://github.com/vale981/tridactyl
synced 2025-03-04 09:01:39 -05:00
Enable accessorless access to tab processes
This commit enables accessing content script values directly, like this: (await tri.tabs[3].document.location.href) tri.tabs[6].document.title = "New title!" tri.tabs[9].tri.excmds.js("document.getElementById('blah').textContent").then(console.log) Note that setting values, as shown in the above document.title example above, cannot be synchronously, i.e. there is no way to wait for the value to have been written is the content process before the background process moves on to the next instruction. This is considered okay as writing to the content process synchronously can be done with (await tri.excmds.js("document.title = 'New title!'")) instead. At the moment, accessing all tabs by not specifying an index in tri.tabs is still not supported.
This commit is contained in:
parent
3806445d85
commit
37d841df2c
1 changed files with 71 additions and 32 deletions
103
src/lib/tabs.ts
103
src/lib/tabs.ts
|
@ -1,51 +1,90 @@
|
|||
import * as Messaging from "@src/lib/messaging"
|
||||
|
||||
const allTabs = -1;
|
||||
const allTabs = -1
|
||||
|
||||
// Small wrapper meant to enable sending a message either to a single tab or
|
||||
// multiple ones. Note that for now, sending messages to all tabs does not
|
||||
// work, for reasons unknown.
|
||||
const msg = (tabId, ...args) => {
|
||||
if (tabId === allTabs) {
|
||||
return Messaging.messageAllTabs("omniscient_content", ...args)
|
||||
} else {
|
||||
return Messaging.messageTab(tabId, "omniscient_content", ...args)
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used to generate proxies. We use a function rather than an
|
||||
// object created through Object.create(null) in order to make our proxy
|
||||
// callable. As all proxy calls should be handled through the proxy's "apply"
|
||||
// function, we make ItWouldBeAMistakeToCallThis throw an error, to make bugs
|
||||
// as obvious as possible.
|
||||
const ItWouldBeAMistakeToCallThis = () => {
|
||||
throw Error("Error, base function ItWouldBeAMistakeToCallThis was called!")
|
||||
}
|
||||
|
||||
// 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}]);
|
||||
},
|
||||
})
|
||||
const tabProxy = (tabId, props) =>
|
||||
new Proxy(ItWouldBeAMistakeToCallThis, {
|
||||
get(target, p) {
|
||||
if (p === Symbol.toPrimitive) {
|
||||
// Symbol.toPrimitive will be accessed when the user attempts
|
||||
// to use a content process value without awaiting it first,
|
||||
// e.g.:
|
||||
//
|
||||
// tri.tabs[3].document.title + "!"
|
||||
//
|
||||
// It is a mistake to do this, so we throw an error - see the
|
||||
// if condition checking for "then" for more details.
|
||||
throw Error(
|
||||
`TypeError: tabs[${tabId}].${props.join(".")} is a Promise, you need to await its value.`,
|
||||
)
|
||||
}
|
||||
if (p === "then") {
|
||||
// Because we can only access content process values by
|
||||
// messaging a tab, we can only get values as a promise. We
|
||||
// take advantage of this fact to wait until we get an access
|
||||
// to a "then" property before fetching anything - this enables
|
||||
// rapid traversal of objects instead of having to perform slow
|
||||
// back and forths for every property.
|
||||
//
|
||||
// This works with the "await" keyword too as awaits are turned
|
||||
// into calls to "then" by the javascript engine.
|
||||
// One drawback of this approach is that properties named
|
||||
//
|
||||
// "then" in the content process cannot be directly accessed.
|
||||
// We consider this an okay trade-off, as there exists an
|
||||
// alternative: using tri.tabs[3].eval("x.then") instead.
|
||||
const promise = msg(tabId, "get", [
|
||||
{ target: props, value: undefined },
|
||||
])
|
||||
return promise.then.bind(promise)
|
||||
}
|
||||
return tabProxy(tabId, props.concat(p))
|
||||
},
|
||||
set(target, p, value) {
|
||||
msg(tabId, "set", [{ target: props.concat(p), value }])
|
||||
return true
|
||||
},
|
||||
apply(target, thisArg, argArray) {
|
||||
return msg(tabId, "apply", [{ target: props, value: argArray }])
|
||||
},
|
||||
})
|
||||
|
||||
export const tabsProxy = new Proxy(Object.create(null), {
|
||||
get(target, p, receiver) {
|
||||
let id = Number(p);
|
||||
get(target, p) {
|
||||
const 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.";
|
||||
throw Error("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