Add communication with Webnative
Closed this issue · 1 comments
Summary
Problem
The extension does not communicate with Webnative.
Impact
The extension does not fulfill its primary purpose of debugging Webnative without this feature.
Solution
Implement a communication layer to interact with Webnative.
Detail
Communication methods
The extension can communicate with Webnative by a couple of methods:
- Call a function added by Webnative to some global scope (e.g.
window.navigator
) - Read data added to some global scope by Webnative
- Send a window post to Webnative
Webnative can only communicate with the extension using window post messages. We can check that the post messages are from the page where the extension's content script was injected by checking the event.source
when receiving a window post message.
Side note: Chrome supports an externally_connectable
API that allows a page to send messages directly using the extension runtime. Firefox does not support this feature yet so we can discard it as an option for now.
Connect
We will start the interaction between the extension and Webnative when a developer opens the Webnative devtools panel. The extension will signal to Webnative that it is ready to connect.
We could call a function put onto the global scope by Webnative or send a window post message to Webnative. I think calling a function would be preferable because it is a direct communication between the extension and Webnative. A window post message could be received elsewhere.
Webnative should send a connected
window post message to the extension to confirm the connection. We can't directly target the extension with a window post message, but we can tag messages so the extension can ignore any other window post noise.
The connected
message could look like:
{
id: 'webnative-message',
type: 'connected'
}
We could go further configure Webnative with a unique id
sent by the extension when it connects. For example
{
id: 'webnative-61728e0e-d3ee-4eea-93ff-c4d9a9ab493a',
type: 'connected'
}
The main benefit here would be to ignore spoofed messages sent by another window poster. Not actually sure if we have a privacy or security concern, so a unique id
might be overkill.
Disconnect
We should add a disconnect as well. The extension might disconnect when the developer is not viewing the Webnative devtools panel. Also, Webnative may want to signal to the extension that it has disconnected because something went wrong.
Disconnect could be implemented in roughly the same way as connect. Webnative adds a disconnect function to the global scope that the extension can call. When called, Webnative responds with a disconnect
message like:
{
id: 'webnative-message',
type: 'disconnected'
}
Data
Data messages are sent from Webnative to the extension. See #2 for a list of data we would like to send.
We will send data messages as window post messages, and we can use a similar format to the other messages with an added data
field:
{
id: 'webnative-message',
type: 'data',
data: {
// various bits of data
}
}
The data
that we send must be JSON serializable because the message will pass through extension messaging APIs.
The message protocol has changed to send Webnative state on connect and disconnect messages. In addition, data messages have been have been broken into filesystem and session messages.
Messages
Messages are sent from Webnative to the extension as window post messages. When the extension initially connects, it configures Webnative with an extension ID which is used to tag all messages sent to the extension. The tag lets the extension filter window post messages to only receive messages intended for it.
State
Each message includes some information about the state of Webnative:
type State = {
app: {
namespace: AppInfo | string
capabilities?: Permissions
}
fileSystem: {
dataRootCID: string | null
}
user: {
username: string | null
accountDID: string | null
agentDID: string
}
odd: {
version: string
}
}
Updated on April 21st, 2023.
Connect message
The extension calls a connect function that is added to the global object (e.g. window
) by Webnative. When the connect function is called, Webnative sends a connect message to the extension:
globalThis.postMessage({
id: extensionId,
type: "connect",
timestamp: 1678487975,
state
})
Disconnect message
The extension calls a disconnect function when it does not need messages to be sent from Webnative:
globalThis.postMessage({
id: extensionId,
type: "disconnect",
timestamp: 1678487975,
state
})
Session messages
Webnative sends session create and destroy messages to the extension. Each message contains a detail field that includes the username and indicates the message type:
globalThis.postMessage({
id: extensionId,
type: "session",
timestamp: 1678487975,
state,
detail: {
type: "create",
username
}
})
globalThis.postMessage({
id: extensionId,
type: "session",
timestamp: 1678487975,
state,
detail: {
type: "destroy",
username
}
})
File system messages
Webnative sends filesystem local-change and publish messages to the extension. Each message contains a detail field that includes the root CID, path on local-change, and indicates the message type:
globalThis.postMessage({
id: extensionId,
type: "filesystem",
timestamp: 1678487975,
state,
detail: {
type: "local-change",
root: "<root-cid>",
path
}
})
globalThis.postMessage({
id: extensionId,
type: "filesystem",
timestamp: 1678487975,
state,
detail: {
type: "publish",
root: "<root-cid>"
}
})