Files
flipper/desktop/app/src/utils/js-client-server-utils/serverUtils.tsx
Michel Weststrate 54d36876b6 Upgrade to Electron 10 (#1532)
Summary:
Pull Request resolved: https://github.com/facebook/flipper/pull/1532

Upgrade to Electron 10, in an attempt to fix random hard Electron crashes. Didn't fix it, but given the changelog of Electron with all the bug fixes, seems to be a good idea nonetheless :)

Changelog: Flipper now uses Electron 10

Reviewed By: nikoant

Differential Revision: D23565000

fbshipit-source-id: 3f07b8cf8c438147bde5314698d20046ac9f94cf
2020-09-17 09:44:27 -07:00

222 lines
6.3 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import Client, {ClientQuery} from '../../Client';
import {FlipperClientConnection} from '../../Client';
import {ipcRenderer, remote, IpcRendererEvent} from 'electron';
import JSDevice from '../../devices/JSDevice';
import {Store} from '../../reducers';
import {Logger} from '../../fb-interfaces/Logger';
import {Payload, ConnectionStatus, ISubscriber} from 'rsocket-types';
import {Flowable, Single} from 'rsocket-flowable';
import Server from '../../server';
import {buildClientId} from '../clientUtils';
const connections: Map<number, JSClientFlipperConnection<any>> = new Map();
const availablePlugins: Map<number, Array<string>> = new Map();
function jsDeviceId(windowId: number): string {
return 'test_js_device' + windowId;
}
export function initJsEmulatorIPC(
store: Store,
logger: Logger,
flipperServer: Server,
flipperConnections: Map<
string,
{
connection: FlipperClientConnection<any, any> | null | undefined;
client: Client;
}
>,
) {
ipcRenderer.on(
'from-js-emulator-init-client',
(_event: IpcRendererEvent, message: any) => {
const {windowId} = message;
const {plugins, appName} = message.payload;
store.dispatch({
type: 'REGISTER_DEVICE',
payload: new JSDevice(jsDeviceId(windowId), 'jsEmulator', windowId),
});
const connection = new JSClientFlipperConnection(windowId);
connections.set(windowId, connection);
availablePlugins.set(windowId, plugins);
const query: ClientQuery = {
app: appName,
os: 'JSWebApp',
device: 'jsEmulator',
device_id: jsDeviceId(windowId),
sdk_version: 2, // hack to bybass callbacks in Client, will be fixed when JS Connection will be fully implemented
};
const clientId = buildClientId(query);
const client = new Client(
clientId,
query,
connection,
logger,
store,
plugins,
);
flipperConnections.set(clientId, {
connection: connection,
client: client,
});
connection.connectionStatus().subscribe({
onNext(payload) {
if (payload.kind == 'ERROR' || payload.kind == 'CLOSED') {
console.debug(`Device disconnected ${client.id}`, 'server');
flipperServer.removeConnection(client.id);
const toUnregister = new Set<string>();
toUnregister.add(jsDeviceId(windowId));
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: toUnregister,
});
connections.delete(windowId);
availablePlugins.delete(windowId);
}
},
onSubscribe(subscription) {
subscription.request(Number.MAX_SAFE_INTEGER);
},
});
client.init().then(() => {
console.log(client);
flipperServer.emit('new-client', client);
flipperServer.emit('clients-change');
client.emit('plugins-change');
ipcRenderer.on(
'from-js-emulator',
(_event: IpcRendererEvent, message: any) => {
const {command, payload} = message;
if (command === 'sendFlipperObject') {
client.onMessage(
JSON.stringify({
params: {
api: payload.api,
method: payload.method,
params: JSON.parse(payload.params),
},
method: 'execute',
}),
);
}
},
);
});
},
);
}
export function launchJsEmulator(url: string, height: number, width: number) {
const BrowserWindow = remote.BrowserWindow;
const win = new BrowserWindow({
height: height,
width: width,
webPreferences: {
enableRemoteModule: true,
preload: require('path').join(
remote.app.getAppPath(),
'SupportJSClientPreload.js',
),
nodeIntegration: false,
contextIsolation: false,
allowRunningInsecureContent: true,
},
});
win.webContents.on('preload-error', (_event, path, error) => {
console.log(path, error);
});
win.loadURL(url);
win.webContents.on('did-finish-load', () => {
win.webContents.send('parent-window-id', remote.getCurrentWebContents().id);
const childWindowId = win.webContents.id;
win.on('closed', () => {
connections.get(childWindowId)?.close();
});
});
}
export class JSClientFlipperConnection<M>
implements FlipperClientConnection<string, M> {
webContentsId: number;
connStatusSubscribers: Set<ISubscriber<ConnectionStatus>> = new Set();
connStatus: ConnectionStatus;
constructor(webContentsId: number) {
this.webContentsId = webContentsId;
this.connStatus = {kind: 'CONNECTED'};
}
connectionStatus(): Flowable<ConnectionStatus> {
return new Flowable<ConnectionStatus>((subscriber) => {
subscriber.onSubscribe({
cancel: () => {
this.connStatusSubscribers.delete(subscriber);
},
request: (_) => {
this.connStatusSubscribers.add(subscriber);
subscriber.onNext(this.connStatus);
},
});
});
}
close(): void {
this.connStatus = {kind: 'CLOSED'};
this.connStatusSubscribers.forEach((subscriber) => {
subscriber.onNext(this.connStatus);
});
}
fireAndForget(payload: Payload<string, M>): void {
ipcRenderer.sendTo(
this.webContentsId,
'message-to-plugin',
JSON.parse(payload.data != null ? payload.data : '{}'),
);
}
// TODO: fully implement and return actual result
requestResponse(payload: Payload<string, M>): Single<Payload<string, M>> {
return new Single((subscriber) => {
const method =
payload.data != null ? JSON.parse(payload.data).method : 'not-defined';
if (method != 'getPlugins') {
this.fireAndForget(payload);
}
subscriber.onSubscribe(() => {});
subscriber.onComplete(
method == 'getPlugins'
? {
data: JSON.stringify({
success: {plugins: availablePlugins.get(this.webContentsId)},
}),
}
: {data: JSON.stringify({success: null})},
);
});
}
}