JS apps support 1/n
Summary: ### Connecting Flipper with JS apps by using electron's BrowserWindow and IPC 1. UI: there is a menu item in Devices tab which opens JS Emulator Launcher Sheet. Here we can configure URL to open and initial size of the window. 2. BrowserWindow, preloaded js: there is SupportJSClientPreload.js which initialize communication between flipper and app via electron's ipc 3. On flipper's side there is src/utils/js-client/serverUtils.tsx which contains most of JS emulator related code 4. Extracting of FlipperClientConnection: since we don't use RScocket to communicate with JS app I extracted needed methods to FlipperClientConnection (located in Client) and partly implemented them in JSClientFlipperConnection (requestResponse is just send a message now, doesn't return actual result) Reviewed By: jknoxville Differential Revision: D18572882 fbshipit-source-id: 56d1ca1a60ed2e51329b917021a09382cbb1ceec
This commit is contained in:
committed by
Facebook Github Bot
parent
e7ad713df8
commit
c685493db0
@@ -19,6 +19,7 @@ import ShareSheetExportUrl from './chrome/ShareSheetExportUrl';
|
||||
import SignInSheet from './chrome/SignInSheet';
|
||||
import ExportDataPluginSheet from './chrome/ExportDataPluginSheet';
|
||||
import ShareSheetExportFile from './chrome/ShareSheetExportFile';
|
||||
import JSEmulatorLauncherSheet from './chrome/JSEmulatorLauncherSheet';
|
||||
import PluginContainer from './PluginContainer';
|
||||
import Sheet from './chrome/Sheet';
|
||||
import {ipcRenderer, remote} from 'electron';
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
ACTIVE_SHEET_SHARE_DATA_IN_FILE,
|
||||
ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT,
|
||||
ACTIVE_SHEET_PLUGIN_SHEET,
|
||||
ACTIVE_SHEET_JS_EMULATOR_LAUNCHER,
|
||||
} from './reducers/application';
|
||||
import {Logger} from './fb-interfaces/Logger';
|
||||
import BugReporter from './fb-stubs/BugReporter';
|
||||
@@ -122,6 +124,8 @@ export class App extends React.Component<Props> {
|
||||
case ACTIVE_SHEET_PLUGIN_SHEET:
|
||||
// Currently unused.
|
||||
return null;
|
||||
case ACTIVE_SHEET_JS_EMULATOR_LAUNCHER:
|
||||
return <JSEmulatorLauncherSheet onHide={onHide} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ import {App} from './App.js';
|
||||
import {Logger} from './fb-interfaces/Logger';
|
||||
import {Store} from './reducers/index';
|
||||
import {setPluginState} from './reducers/pluginStates';
|
||||
import {RSocketClientSocket} from 'rsocket-core/RSocketClient';
|
||||
import {Payload, ConnectionStatus} from 'rsocket-types';
|
||||
import {Flowable, Single} from 'rsocket-flowable';
|
||||
import {performance} from 'perf_hooks';
|
||||
import {reportPlatformFailures, reportPluginFailures} from './utils/metrics';
|
||||
import {notNull} from './utils/typeUtils';
|
||||
@@ -97,6 +98,13 @@ const handleError = (
|
||||
}
|
||||
};
|
||||
|
||||
export interface FlipperClientConnection<D, M> {
|
||||
connectionStatus(): Flowable<ConnectionStatus>;
|
||||
close(): void;
|
||||
fireAndForget(payload: Payload<D, M>): void;
|
||||
requestResponse(payload: Payload<D, M>): Single<Payload<D, M>>;
|
||||
}
|
||||
|
||||
export default class Client extends EventEmitter {
|
||||
app: App | undefined;
|
||||
connected: boolean;
|
||||
@@ -105,7 +113,7 @@ export default class Client extends EventEmitter {
|
||||
sdkVersion: number;
|
||||
messageIdCounter: number;
|
||||
plugins: Plugins;
|
||||
connection: RSocketClientSocket<any, any> | null | undefined;
|
||||
connection: FlipperClientConnection<any, any> | null | undefined;
|
||||
store: Store;
|
||||
activePlugins: Set<string>;
|
||||
device: Promise<BaseDevice>;
|
||||
@@ -129,7 +137,7 @@ export default class Client extends EventEmitter {
|
||||
constructor(
|
||||
id: string,
|
||||
query: ClientQuery,
|
||||
conn: RSocketClientSocket<any, any> | null | undefined,
|
||||
conn: FlipperClientConnection<any, any> | null | undefined,
|
||||
logger: Logger,
|
||||
store: Store,
|
||||
plugins?: Plugins | null | undefined,
|
||||
|
||||
@@ -12,11 +12,17 @@ import {connect, ReactReduxContext} from 'react-redux';
|
||||
import {spawn} from 'child_process';
|
||||
import {dirname} from 'path';
|
||||
import {selectDevice, preferDevice} from '../reducers/connections';
|
||||
import {
|
||||
setActiveSheet,
|
||||
ActiveSheet,
|
||||
ACTIVE_SHEET_JS_EMULATOR_LAUNCHER,
|
||||
} from '../reducers/application';
|
||||
import {default as which} from 'which';
|
||||
import {showOpenDialog} from '../utils/exportData';
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import React, {Component} from 'react';
|
||||
import {State} from '../reducers';
|
||||
import GK from '../fb-stubs/GK';
|
||||
|
||||
type StateFromProps = {
|
||||
selectedDevice: BaseDevice | null | undefined;
|
||||
@@ -27,6 +33,7 @@ type StateFromProps = {
|
||||
type DispatchFromProps = {
|
||||
selectDevice: (device: BaseDevice) => void;
|
||||
preferDevice: (device: string) => void;
|
||||
setActiveSheet: (sheet: ActiveSheet) => void;
|
||||
};
|
||||
|
||||
type OwnProps = {};
|
||||
@@ -154,17 +161,30 @@ class DevicesButton extends Component<Props> {
|
||||
label: name,
|
||||
click: () => this.launchEmulator(name),
|
||||
}));
|
||||
if (emulators.length > 0) {
|
||||
|
||||
// Launch JS emulator
|
||||
if (GK.get('flipper_js_client_emulator')) {
|
||||
if (emulators.length > 0) {
|
||||
dropdown.push(
|
||||
{type: 'separator' as 'separator'},
|
||||
{
|
||||
label: 'Launch Android emulators',
|
||||
enabled: false,
|
||||
},
|
||||
...emulators,
|
||||
);
|
||||
}
|
||||
dropdown.push(
|
||||
{type: 'separator' as 'separator'},
|
||||
{
|
||||
label: 'Launch Android emulators',
|
||||
enabled: false,
|
||||
label: 'Launch JS Web App',
|
||||
click: () =>
|
||||
this.props.setActiveSheet(ACTIVE_SHEET_JS_EMULATOR_LAUNCHER),
|
||||
},
|
||||
...emulators,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (dropdown.length > 0) {
|
||||
dropdown.push({type: 'separator' as 'separator'});
|
||||
}
|
||||
@@ -196,5 +216,6 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, State>(
|
||||
{
|
||||
selectDevice,
|
||||
preferDevice,
|
||||
setActiveSheet,
|
||||
},
|
||||
)(DevicesButton);
|
||||
|
||||
122
src/chrome/JSEmulatorLauncherSheet.tsx
Normal file
122
src/chrome/JSEmulatorLauncherSheet.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 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 {
|
||||
FlexColumn,
|
||||
Button,
|
||||
styled,
|
||||
Text,
|
||||
FlexRow,
|
||||
Spacer,
|
||||
Input,
|
||||
Label,
|
||||
} from 'flipper';
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {State as Store} from '../reducers';
|
||||
import {launchJsEmulator} from '../utils/js-client/serverUtils';
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
padding: 20,
|
||||
width: 800,
|
||||
});
|
||||
|
||||
const Title = styled(Text)({
|
||||
marginBottom: 18,
|
||||
marginRight: 10,
|
||||
fontWeight: 100,
|
||||
fontSize: '40px',
|
||||
});
|
||||
|
||||
const textareaStyle = {
|
||||
margin: 0,
|
||||
marginBottom: 10,
|
||||
};
|
||||
|
||||
const TitleInput = styled(Input)({
|
||||
...textareaStyle,
|
||||
height: 30,
|
||||
});
|
||||
|
||||
type OwnProps = {
|
||||
onHide: () => void;
|
||||
};
|
||||
|
||||
type StateFromProps = {};
|
||||
|
||||
type DispatchFromProps = {};
|
||||
|
||||
type State = {
|
||||
url: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type Props = OwnProps & StateFromProps & DispatchFromProps;
|
||||
class JSEmulatorLauncherSheet extends Component<Props, State> {
|
||||
state: State = {
|
||||
url: 'http://localhost:8888',
|
||||
width: 800,
|
||||
height: 600,
|
||||
};
|
||||
|
||||
onUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({url: e.target.value});
|
||||
};
|
||||
|
||||
onHeightChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({height: Number(e.target.value)});
|
||||
};
|
||||
|
||||
onWidthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({width: Number(e.target.value)});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {url, height, width} = this.state;
|
||||
return (
|
||||
<Container>
|
||||
<Title>Launch Web App</Title>
|
||||
<Label>Url</Label>
|
||||
<TitleInput value={url} onChange={this.onUrlChange} />
|
||||
<Label>Height</Label>
|
||||
<TitleInput value={height} onChange={this.onHeightChange} />
|
||||
<Label>Width</Label>
|
||||
<TitleInput value={width} onChange={this.onWidthChange} />
|
||||
|
||||
<br />
|
||||
<FlexRow>
|
||||
<Spacer />
|
||||
<Button compact padded onClick={this.props.onHide}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
compact
|
||||
padded
|
||||
onClick={() => {
|
||||
launchJsEmulator(
|
||||
this.state.url,
|
||||
this.state.height,
|
||||
this.state.width,
|
||||
);
|
||||
this.props.onHide();
|
||||
}}>
|
||||
Launch
|
||||
</Button>
|
||||
</FlexRow>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
() => ({}),
|
||||
{},
|
||||
)(JSEmulatorLauncherSheet);
|
||||
@@ -52,7 +52,7 @@ export type DeviceExport = {
|
||||
logs: Array<DeviceLogEntry>;
|
||||
};
|
||||
|
||||
export type OS = 'iOS' | 'Android' | 'Windows' | 'MacOS';
|
||||
export type OS = 'iOS' | 'Android' | 'Windows' | 'MacOS' | 'JSWebApp';
|
||||
|
||||
export default class BaseDevice {
|
||||
constructor(serial: string, deviceType: DeviceType, title: string, os: OS) {
|
||||
|
||||
20
src/devices/JSDevice.tsx
Normal file
20
src/devices/JSDevice.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 BaseDevice from './BaseDevice';
|
||||
|
||||
export default class JSDevice extends BaseDevice {
|
||||
webContentsId: number;
|
||||
|
||||
constructor(serial: string, title: string, webContentsId: number) {
|
||||
super(serial, 'emulator', title, 'JSWebApp');
|
||||
this.devicePlugins = [];
|
||||
this.webContentsId = webContentsId;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' =
|
||||
export const SET_EXPORT_STATUS_MESSAGE: 'SET_EXPORT_STATUS_MESSAGE' =
|
||||
'SET_EXPORT_STATUS_MESSAGE';
|
||||
export const UNSET_SHARE: 'UNSET_SHARE' = 'UNSET_SHARE';
|
||||
export const ACTIVE_SHEET_JS_EMULATOR_LAUNCHER: 'ACTIVE_SHEET_JS_EMULATOR_LAUNCHER' =
|
||||
'ACTIVE_SHEET_JS_EMULATOR_LAUNCHER';
|
||||
|
||||
export type ActiveSheet =
|
||||
| typeof ACTIVE_SHEET_PLUGIN_SHEET
|
||||
@@ -37,6 +39,7 @@ export type ActiveSheet =
|
||||
| typeof ACTIVE_SHEET_DOCTOR
|
||||
| typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE
|
||||
| typeof ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT
|
||||
| typeof ACTIVE_SHEET_JS_EMULATOR_LAUNCHER
|
||||
| null;
|
||||
|
||||
export type LauncherMsg = {
|
||||
|
||||
@@ -16,17 +16,19 @@ import {RSocketServer} from 'rsocket-core';
|
||||
import RSocketTCPServer from 'rsocket-tcp-server';
|
||||
import {Single} from 'rsocket-flowable';
|
||||
import Client from './Client';
|
||||
import {FlipperClientConnection} from './Client';
|
||||
import {UninitializedClient} from './UninitializedClient';
|
||||
import {reportPlatformFailures} from './utils/metrics';
|
||||
import EventEmitter from 'events';
|
||||
import invariant from 'invariant';
|
||||
import tls from 'tls';
|
||||
import net, {Socket} from 'net';
|
||||
import {RSocketClientSocket} from 'rsocket-core/RSocketClient';
|
||||
import {Responder, Payload, ReactiveSocket} from 'rsocket-types';
|
||||
import GK from './fb-stubs/GK';
|
||||
import {initJsEmulatorIPC} from './utils/js-client/serverUtils';
|
||||
|
||||
type ClientInfo = {
|
||||
connection: RSocketClientSocket<any, any> | null | undefined;
|
||||
connection: FlipperClientConnection<any, any> | null | undefined;
|
||||
client: Client;
|
||||
};
|
||||
|
||||
@@ -84,6 +86,11 @@ class Server extends EventEmitter {
|
||||
return;
|
||||
});
|
||||
reportPlatformFailures(this.initialisePromise, 'initializeServer');
|
||||
|
||||
if (GK.get('flipper_js_client_emulator')) {
|
||||
initJsEmulatorIPC(this.store, this.logger, this, this.connections);
|
||||
}
|
||||
|
||||
return this.initialisePromise;
|
||||
}
|
||||
|
||||
@@ -304,7 +311,7 @@ class Server extends EventEmitter {
|
||||
}
|
||||
|
||||
async addConnection(
|
||||
conn: RSocketClientSocket<any, any>,
|
||||
conn: FlipperClientConnection<any, any>,
|
||||
query: ClientQuery,
|
||||
csrQuery: ClientCsrQuery,
|
||||
): Promise<Client> {
|
||||
|
||||
210
src/utils/js-client/serverUtils.tsx
Normal file
210
src/utils/js-client/serverUtils.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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} from 'electron';
|
||||
import JSDevice from '../../devices/JSDevice';
|
||||
import {Store} from 'src/reducers';
|
||||
import {Logger} from 'src/fb-interfaces/Logger';
|
||||
|
||||
import {Payload, ConnectionStatus, ISubscriber} from 'rsocket-types';
|
||||
import {Flowable, Single} from 'rsocket-flowable';
|
||||
import Server from 'src/server';
|
||||
|
||||
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, message) => {
|
||||
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 = `${query.app}#${query.os}#${query.device}#${query.device_id}`;
|
||||
|
||||
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, message) => {
|
||||
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: {
|
||||
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})},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
90
static/SupportJSClientPreload.js
Normal file
90
static/SupportJSClientPreload.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
// ==============
|
||||
// Preload script
|
||||
// ==============
|
||||
const {remote, ipcRenderer} = require('electron');
|
||||
|
||||
let FlipperMainWindowId = 0;
|
||||
|
||||
ipcRenderer.on('parent-window-id', (event, message) => {
|
||||
FlipperMainWindowId = message;
|
||||
});
|
||||
|
||||
let FlipperIsClientInit = false;
|
||||
let FlipperMemoizedPlugins;
|
||||
|
||||
function initClient(plugins) {
|
||||
if (FlipperIsClientInit) {
|
||||
return;
|
||||
}
|
||||
if (plugins) {
|
||||
FlipperMemoizedPlugins = plugins;
|
||||
}
|
||||
if (FlipperMainWindowId != 0) {
|
||||
ipcRenderer.sendTo(FlipperMainWindowId, 'from-js-emulator-init-client', {
|
||||
command: 'initClient',
|
||||
windowId: remote.getCurrentWebContents().id,
|
||||
payload: {
|
||||
plugins: plugins ? plugins : FlipperMemoizedPlugins,
|
||||
appName: 'kite/weblite',
|
||||
},
|
||||
});
|
||||
FlipperIsClientInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.FlipperWebviewBridge = {
|
||||
registerPlugins: function(plugins) {
|
||||
console.log(plugins);
|
||||
if (FlipperMainWindowId != 0) {
|
||||
ipcRenderer.sendTo(FlipperMainWindowId, 'from-js-emulator', {
|
||||
command: 'registerPlugins',
|
||||
payload: plugins,
|
||||
});
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
console.log('start');
|
||||
|
||||
if (FlipperMainWindowId != 0) {
|
||||
ipcRenderer.sendTo(FlipperMainWindowId, 'from-js-emulator', {
|
||||
command: 'start',
|
||||
payload: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
sendFlipperObject: function(plugin, method, data) {
|
||||
console.log(plugin, method, data);
|
||||
initClient();
|
||||
if (FlipperMainWindowId != 0) {
|
||||
ipcRenderer.sendTo(FlipperMainWindowId, 'from-js-emulator', {
|
||||
command: 'sendFlipperObject',
|
||||
payload: {
|
||||
api: plugin,
|
||||
method: method,
|
||||
params: data,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
isFlipperSupported: true,
|
||||
initClient: initClient,
|
||||
};
|
||||
|
||||
ipcRenderer.on('message-to-plugin', (event, message) => {
|
||||
const flipper = window.flipper;
|
||||
if (!flipper) {
|
||||
return;
|
||||
}
|
||||
const receiver = flipper.FlipperWebviewMessageReceiver.receive;
|
||||
const {api, method, params} = message.params;
|
||||
receiver(api, method, JSON.stringify(params));
|
||||
});
|
||||
Reference in New Issue
Block a user