Summary: Moved all logic per device type we support to its own dir, including tools and utilities around it, which makes it easier to consolidate logic and decouple in turn per device type. Per type, all logic can be found in `server/devices/(desktop|metro|android|ios|webapp)` Reviewed By: timur-valiev Differential Revision: D30277817 fbshipit-source-id: 2b5339c363d5d31ceeba07cec03826fc67cf3748
212 lines
5.8 KiB
TypeScript
212 lines
5.8 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 {
|
|
ClientConnection,
|
|
ConnectionStatus,
|
|
ConnectionStatusChange,
|
|
ResponseType,
|
|
} from '../../comms/ClientConnection';
|
|
import {ipcRenderer, remote, IpcRendererEvent} from 'electron';
|
|
import JSDevice from './JSDevice';
|
|
import {Store} from '../../../reducers';
|
|
import {Logger} from '../../../fb-interfaces/Logger';
|
|
import ServerController from '../../comms/ServerController';
|
|
import {buildClientId} from '../../../utils/clientUtils';
|
|
import {destroyDevice} from '../../../reducers/connections';
|
|
|
|
const connections: Map<number, JSClientFlipperConnection> = 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: ServerController,
|
|
flipperConnections: Map<
|
|
string,
|
|
{
|
|
connection: ClientConnection | null | undefined;
|
|
client: Client;
|
|
}
|
|
>,
|
|
) {
|
|
ipcRenderer.on(
|
|
'from-js-emulator-init-client',
|
|
(_event: IpcRendererEvent, message: any) => {
|
|
const {windowId} = message;
|
|
const {plugins, appName} = message.payload;
|
|
const device = new JSDevice(jsDeviceId(windowId), 'jsEmulator', windowId);
|
|
store.dispatch({
|
|
type: 'REGISTER_DEVICE',
|
|
payload: device,
|
|
});
|
|
|
|
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,
|
|
device,
|
|
);
|
|
|
|
flipperConnections.set(clientId, {
|
|
connection: connection,
|
|
client: client,
|
|
});
|
|
|
|
connection.subscribeToEvents((status) => {
|
|
if (
|
|
status == ConnectionStatus.ERROR ||
|
|
status == ConnectionStatus.CLOSED
|
|
) {
|
|
console.debug(`Device disconnected ${client.id}`, 'server');
|
|
flipperServer.removeConnection(client.id);
|
|
destroyDevice(store, logger, jsDeviceId(windowId));
|
|
connections.delete(windowId);
|
|
availablePlugins.delete(windowId);
|
|
}
|
|
});
|
|
|
|
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',
|
|
}),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
})
|
|
.catch((_) => {});
|
|
},
|
|
);
|
|
}
|
|
|
|
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 implements ClientConnection {
|
|
webContentsId: number;
|
|
connStatusSubscribers: Set<ConnectionStatusChange> = new Set();
|
|
connStatus: ConnectionStatus;
|
|
|
|
constructor(webContentsId: number) {
|
|
this.webContentsId = webContentsId;
|
|
this.connStatus = ConnectionStatus.CONNECTED;
|
|
}
|
|
subscribeToEvents(subscriber: ConnectionStatusChange): void {
|
|
this.connStatusSubscribers.add(subscriber);
|
|
}
|
|
send(data: any): void {
|
|
ipcRenderer.sendTo(
|
|
this.webContentsId,
|
|
'message-to-plugin',
|
|
JSON.parse(data != null ? data : '{}'),
|
|
);
|
|
}
|
|
// TODO: fully implement and return actual result
|
|
sendExpectResponse(data: any): Promise<ResponseType> {
|
|
return new Promise((resolve, _) => {
|
|
const method = data != null ? JSON.parse(data).method : 'not-defined';
|
|
|
|
if (method !== 'getPlugins') {
|
|
this.send(data);
|
|
}
|
|
|
|
if (method === 'getPlugins') {
|
|
resolve({
|
|
success: {
|
|
plugins: availablePlugins.get(this.webContentsId),
|
|
},
|
|
length: 0,
|
|
});
|
|
} else {
|
|
resolve({
|
|
success: undefined,
|
|
length: 0,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
close(): void {
|
|
this.connStatus = ConnectionStatus.CLOSED;
|
|
this.connStatusSubscribers.forEach((subscriber) => {
|
|
subscriber(this.connStatus);
|
|
});
|
|
}
|
|
}
|