Skip to content

Commit

Permalink
wallet-ext: dapp <-> content script messaging
Browse files Browse the repository at this point in the history
* use window message events to pass messages between the web page and the content script
  • Loading branch information
pchrysochoidis committed Jun 28, 2022
1 parent dc47252 commit 393d212
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 5 deletions.
5 changes: 4 additions & 1 deletion wallet/configs/ts/tsconfig.common.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
"_hooks": ["./src/ui/app/hooks/"],
"_components/*": ["./src/ui/app/components/*"],
"_messaging/*": ["./src/shared/messaging/*"],
"_messages/*": ["./src/shared/messaging/messages/*"]
"_messages": ["./src/shared/messaging/messages/"],
"_messages/*": ["./src/shared/messaging/messages/*"],
"_payloads": ["./src/shared/messaging/messages/payloads/"],
"_payloads/*": ["./src/shared/messaging/messages/payloads/*"]
}
},
"include": ["../../src"],
Expand Down
27 changes: 27 additions & 0 deletions wallet/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@types/node": "^17.0.31",
"@types/react": "^18.0.8",
"@types/react-dom": "^18.0.3",
"@types/uuid": "^8.3.4",
"@types/webextension-polyfill": "^0.8.3",
"@types/webpack": "^5.28.0",
"concurrently": "^7.1.0",
Expand Down Expand Up @@ -86,6 +87,7 @@
"rxjs": "^7.5.5",
"stream-browserify": "^3.0.0",
"tweetnacl": "^1.0.3",
"uuid": "^8.3.2",
"webextension-polyfill": "^0.9.0",
"yup": "^0.32.11"
}
Expand Down
2 changes: 2 additions & 0 deletions wallet/src/content-script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import { injectDappInterface } from './interface-inject';
import { setupMessagesProxy } from './messages-proxy';

injectDappInterface();
setupMessagesProxy();
16 changes: 16 additions & 0 deletions wallet/src/content-script/messages-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { WindowMessageStream } from '_messaging/WindowMessageStream';

export function setupMessagesProxy() {
const windowMsgStream = new WindowMessageStream(
'sui_content-script',
'sui_in-page'
);
windowMsgStream.messages.subscribe((msg) => {
// TODO implement
// eslint-disable-next-line no-console
console.log('[ContentScriptProxy] message from inPage', msg);
});
}
52 changes: 49 additions & 3 deletions wallet/src/dapp-interface/DAppInterface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { filter, lastValueFrom, map, take } from 'rxjs';

import { createMessage } from '_messages';
import { WindowMessageStream } from '_messaging/WindowMessageStream';
import { isErrorPayload } from '_payloads';

import type { SuiAddress } from '@mysten/sui.js';
import type { Payload } from '_payloads';
import type { GetAccount } from '_payloads/account/GetAccount';
import type { GetAccountResponse } from '_payloads/account/GetAccountResponse';
import type { Observable } from 'rxjs';

export class DAppInterface {
private _window: Window;
private _messagesStream: WindowMessageStream;

constructor() {
this._messagesStream = new WindowMessageStream(
'sui_in-page',
'sui_content-script'
);
}

public getAccounts(): Promise<SuiAddress[]> {
const stream = this.send<GetAccount, GetAccountResponse>({
type: 'get-account',
}).pipe(
take(1),
map((response) => {
if (isErrorPayload(response)) {
// TODO: throw proper error
throw new Error(response.message);
}
return response.accounts;
})
);
return lastValueFrom(stream);
}

constructor(theWindow: Window) {
this._window = window;
private send<
RequestPayload extends Payload,
ResponsePayload extends Payload | void = void
>(
payload: RequestPayload,
responseForID?: string
): Observable<ResponsePayload> {
const msg = createMessage(payload, responseForID);
this._messagesStream.send(msg);
return this._messagesStream.messages.pipe(
filter(({ id }) => id === msg.id),
map((msg) => msg.payload as ResponsePayload)
);
}
}
2 changes: 1 addition & 1 deletion wallet/src/dapp-interface/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import { DAppInterface } from './DAppInterface';
Object.defineProperty(window, 'suiWallet', {
enumerable: false,
configurable: false,
value: new DAppInterface(window),
value: new DAppInterface(),
});
49 changes: 49 additions & 0 deletions wallet/src/shared/messaging/WindowMessageStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { filter, fromEvent, map } from 'rxjs';

import type { Message } from '_messages';
import type { Observable } from 'rxjs';

export type ClientType = 'sui_in-page' | 'sui_content-script';

type WindowMessage = {
target: ClientType;
payload: Message;
};

export class WindowMessageStream {
public readonly messages: Observable<Message>;
private _name: ClientType;
private _target: ClientType;

constructor(name: ClientType, target: ClientType) {
if (name === target) {
throw new Error(
'[WindowMessageStream] name and target must be different'
);
}
this._name = name;
this._target = target;
this.messages = fromEvent<MessageEvent<WindowMessage>>(
window,
'message'
).pipe(
filter(
(message) =>
message.source === window &&
message.data.target === this._name
),
map((message) => message.data.payload)
);
}

public send(payload: Message) {
const msg: WindowMessage = {
target: this._target,
payload,
};
window.postMessage(msg);
}
}
21 changes: 21 additions & 0 deletions wallet/src/shared/messaging/messages/Message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { v4 as uuidV4 } from 'uuid';

import type { Payload } from './payloads/Payload';

export type Message = {
id: string;
payload: Payload;
};

export function createMessage<MsgPayload extends Payload>(
payload: MsgPayload,
id?: string
): Message {
return {
id: id || uuidV4(),
payload,
};
}
4 changes: 4 additions & 0 deletions wallet/src/shared/messaging/messages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

export * from './Message';
19 changes: 19 additions & 0 deletions wallet/src/shared/messaging/messages/payloads/BasePayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { Payload } from './Payload';

export type PayloadType =
| 'permission-request'
| 'permission-response'
| 'get-permission-requests'
| 'get-account'
| 'get-account-response';

export interface BasePayload {
type: PayloadType;
}

export function isBasePayload(payload: Payload): payload is BasePayload {
return 'type' in payload && typeof payload.type !== 'undefined';
}
14 changes: 14 additions & 0 deletions wallet/src/shared/messaging/messages/payloads/ErrorPayload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { Payload } from './Payload';

export interface ErrorPayload {
error: true;
code: number;
message: string;
}

export function isErrorPayload(payload: Payload): payload is ErrorPayload {
return 'error' in payload && payload.error === true;
}
7 changes: 7 additions & 0 deletions wallet/src/shared/messaging/messages/payloads/Payload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { BasePayload } from './BasePayload';
import type { ErrorPayload } from './ErrorPayload';

export type Payload = BasePayload | ErrorPayload;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { BasePayload } from '_payloads';

export interface GetAccount extends BasePayload {
type: 'get-account';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { SuiAddress } from '@mysten/sui.js';
import type { BasePayload } from '_payloads';

export interface GetAccountResponse extends BasePayload {
type: 'get-account-response';
accounts: SuiAddress[];
}
6 changes: 6 additions & 0 deletions wallet/src/shared/messaging/messages/payloads/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

export * from './BasePayload';
export * from './ErrorPayload';
export * from './Payload';

0 comments on commit 393d212

Please sign in to comment.