Convert messaging to use ports (#38)

* Convert messageing to use ports

* Small listeneing cleanups
This commit is contained in:
Jaco Greeff 2019-05-30 14:28:41 +02:00 committed by GitHub
parent dc155556d4
commit 01f2f16b79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 38 deletions

View File

@ -6,19 +6,45 @@ import { AuthorizeRequest, MessageTypes, SigningRequest } from '@polkadot/extens
import { KeyringJson } from '@polkadot/ui-keyring/types';
import { KeypairType } from '@polkadot/util-crypto/types';
import extension from 'extensionizer';
import { PORT_POPUP } from '@polkadot/extension/defaults';
type Handlers = {
[index: number]: {
resolve: (data: any) => void,
reject: (error: Error) => void
}
};
const port = extension.runtime.connect({ name: PORT_POPUP });
const handlers: Handlers = {};
let idCounter = 0;
// setup a listener for messages, any incoming resolves the promise
port.onMessage.addListener((data) => {
const handler = handlers[data.id];
if (!handler) {
console.error(`Uknown response: ${JSON.stringify(data)}`);
return;
}
delete handlers[data.id];
if (data.error) {
handler.reject(new Error(data.error));
} else {
handler.resolve(data.response);
}
});
function sendMessage (message: MessageTypes, request: any = {}): Promise<any> {
return new Promise((resolve, reject) => {
const id = ++idCounter;
chrome.runtime.sendMessage({ id, message, request }, ({ error, response }) => {
if (error) {
reject(new Error(error));
} else {
resolve(response);
}
});
handlers[id] = { resolve, reject };
port.postMessage({ id, message, request });
});
}

View File

@ -4,6 +4,7 @@
import { MessageRequest } from '../types';
import { PORT_POPUP } from '../../defaults';
import Extension from './Extension';
import State from './State';
import Tabs from './Tabs';
@ -12,27 +13,34 @@ const state = new State();
const extension = new Extension(state);
const tabs = new Tabs(state);
export default function handler ({ id, message, request }: MessageRequest, { tab }: chrome.runtime.MessageSender, sendResponse: (response?: any) => void): boolean {
const source = `${tab ? tab.url : 'extension'}: ${id}: ${message}`;
export default function handler ({ id, message, request }: MessageRequest, port: chrome.runtime.Port): boolean {
const isPopup = port.name === PORT_POPUP;
const sender = port.sender as chrome.runtime.MessageSender;
const from = isPopup
? 'popup'
: sender.tab
? sender.tab.url
: '<unknown>';
const source = `${from}: ${id}: ${message}`;
console.log(` [in] ${source}`); // :: ${JSON.stringify(request)}`);
const promise = tab
? tabs.handle(message, request, tab.url)
: extension.handle(message, request);
const promise = isPopup
? extension.handle(message, request)
: tabs.handle(message, request, from);
promise
.then((response) => {
console.log(`[out] ${source}`); // :: ${JSON.stringify(response)}`);
sendResponse({ id, response });
port.postMessage({ id, response });
})
.catch((error) => {
console.log(`[err] ${source}:: ${error.message}`);
sendResponse({ id, error: error.message });
port.postMessage({ id, error: error.message });
});
// return true to indicate we are sending the respionse async
// return true to indicate we are sending the response async
return true;
}

View File

@ -7,14 +7,27 @@ import extension from 'extensionizer';
// Runs in the extension background, handling all keyring access
import keyring from '@polkadot/ui-keyring';
import { assert } from '@polkadot/util';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { PORT_CONTENT, PORT_POPUP } from '../defaults';
import ChromeStore from './ChromeStore';
import handlers from './handlers';
// setup the notification
extension.browserAction.setBadgeBackgroundColor({ color: '#ff0000' });
extension.runtime.onMessage.addListener(handlers);
// listen to all messages and handle appropriately
extension.runtime.onConnect.addListener((port) => {
// shouldn't happen, however... only listen to what we know about
assert([PORT_CONTENT, PORT_POPUP].includes(port.name), `Unknown connection from ${port.name}`);
// message and disconnect handlers
port.onMessage.addListener((data) => handlers(data, port));
port.onDisconnect.addListener(() => console.log(`Disconnected from ${port.name}`));
});
// initial setup
cryptoWaitReady()
.then(() => {
console.log('crypto initialized');

View File

@ -4,27 +4,24 @@
import extension from 'extensionizer';
function sendResponse (data: any): void {
window.postMessage({ ...data, origin: 'content' }, '*');
}
import { PORT_CONTENT } from './defaults';
// Handle all messages, passing messages to the extension
// connect to the extension
const port = extension.runtime.connect({ name: PORT_CONTENT });
// send any messages from the extension back to the page
port.onMessage.addListener((data) => {
window.postMessage({ ...data, origin: 'content' }, '*');
});
// all messages from the page, pass them to the extension
window.addEventListener('message', ({ data, source }) => {
// only allow messages from our window, by the inject
if (source !== window || data.origin !== 'page') {
return;
}
// pass the detail as-is as a message to the extension
extension.runtime.sendMessage(data, (response) => {
if (!response) {
// no data, send diconnected/unknown error
sendResponse({ id: data.id, error: 'Unknown response' });
} else {
// handle the response, passing as a custom event
sendResponse(response);
}
});
port.postMessage(data);
});
// inject our data injector

View File

@ -0,0 +1,11 @@
// Copyright 2019 @polkadot/extension authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
const PORT_CONTENT = 'content';
const PORT_POPUP = 'popup';
export {
PORT_CONTENT,
PORT_POPUP
};

View File

@ -14,7 +14,7 @@ import Injected from './Injected';
// - this injector, reads listends on the events, maps it to the original
// - resolves/rejects the promise with the result
type Callbacks = {
type Handlers = {
[index: number]: {
resolve: (data: any) => void,
reject: (error: Error) => void
@ -23,7 +23,7 @@ type Callbacks = {
// small helper with the typescript types, just cast window
const windowInject = window as InjectedWindow;
const callbacks: Callbacks = {};
const handlers: Handlers = {};
let idCounter = 0;
// a generic message sender that creates an event, returning a promise that will
@ -32,7 +32,7 @@ function sendMessage (message: MessageTypes, request: any = null): Promise<any>
return new Promise((resolve, reject) => {
const id = ++idCounter;
callbacks[id] = { resolve, reject };
handlers[id] = { resolve, reject };
window.postMessage({ id, message, origin: 'page', request }, '*');
});
@ -52,19 +52,19 @@ window.addEventListener('message', ({ data, source }) => {
return;
}
const promise = callbacks[data.id];
const handler = handlers[data.id];
if (!promise) {
if (!handler) {
console.error(`Uknown response: ${JSON.stringify(data)}`);
return;
}
delete callbacks[data.id];
delete handlers[data.id];
if (data.error) {
promise.reject(new Error(data.error));
handler.reject(new Error(data.error));
} else {
promise.resolve(data.response);
handler.resolve(data.response);
}
});