mirror of
https://github.com/Microsoft/vscode
synced 2024-10-30 09:05:19 +00:00
Enable IPC API for web (#138054)
* wip: ipc api * wip: send message ports upfront * address both inside and outside iframe * fix build * relay MessagePort to worker * address api discussion feedback * check for proposed api * fix layer breakage Co-authored-by: Alex Dima <alexdima@microsoft.com>
This commit is contained in:
parent
898073bcdb
commit
5e630c145f
11 changed files with 97 additions and 20 deletions
|
@ -52,7 +52,9 @@ const CORE_TYPES = [
|
|||
'trimEnd',
|
||||
'trimLeft',
|
||||
'trimRight',
|
||||
'queueMicrotask'
|
||||
'queueMicrotask',
|
||||
'MessageChannel',
|
||||
'MessagePort'
|
||||
];
|
||||
// Types that are defined in a common layer but are known to be only
|
||||
// available in native environments should not be allowed in browser
|
||||
|
|
|
@ -53,7 +53,9 @@ const CORE_TYPES = [
|
|||
'trimEnd',
|
||||
'trimLeft',
|
||||
'trimRight',
|
||||
'queueMicrotask'
|
||||
'queueMicrotask',
|
||||
'MessageChannel',
|
||||
'MessagePort'
|
||||
];
|
||||
|
||||
// Types that are defined in a common layer but are known to be only
|
||||
|
|
|
@ -98,6 +98,12 @@ export interface IWorkspaceData extends IStaticWorkspaceData {
|
|||
folders: { uri: UriComponents, name: string, index: number; }[];
|
||||
}
|
||||
|
||||
export interface MessagePortLike {
|
||||
postMessage(message: any, transfer?: any[]): void;
|
||||
addEventListener(type: 'message', listener: (e: any) => any): void;
|
||||
removeEventListener(type: 'message', listener: (e: any) => any): void;
|
||||
}
|
||||
|
||||
export interface IInitData {
|
||||
version: string;
|
||||
commit?: string;
|
||||
|
@ -114,6 +120,7 @@ export interface IInitData {
|
|||
autoStart: boolean;
|
||||
remote: { isRemote: boolean; authority: string | undefined; connectionData: IRemoteConnectionData | null; };
|
||||
uiKind: UIKind;
|
||||
messagePorts?: ReadonlyMap<string, MessagePortLike>;
|
||||
}
|
||||
|
||||
export interface IConfigurationInitData extends IConfigurationData {
|
||||
|
@ -2260,7 +2267,7 @@ export const MainContext = {
|
|||
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
|
||||
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
|
||||
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline'),
|
||||
MainThreadTesting: createMainId<MainThreadTestingShape>('MainThreadTesting')
|
||||
MainThreadTesting: createMainId<MainThreadTestingShape>('MainThreadTesting'),
|
||||
};
|
||||
|
||||
export const ExtHostContext = {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
|
|||
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import type * as vscode from 'vscode';
|
||||
|
@ -424,6 +424,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
|||
const that = this;
|
||||
let extension: vscode.Extension<any> | undefined;
|
||||
|
||||
const messagePort = isProposedApiEnabled(extensionDescription, 'ipc')
|
||||
? this._initData.messagePorts?.get(ExtensionIdentifier.toKey(extensionDescription.identifier))
|
||||
: undefined;
|
||||
|
||||
return Object.freeze<vscode.ExtensionContext>({
|
||||
globalState,
|
||||
workspaceState,
|
||||
|
@ -449,7 +453,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
|||
checkProposedApiEnabled(extensionDescription, 'extensionRuntime');
|
||||
return that.extensionRuntime;
|
||||
},
|
||||
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
|
||||
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); },
|
||||
messagePassingProtocol: messagePort && {
|
||||
onDidReceiveMessage: Event.fromDOMEventEmitter(messagePort, 'message', e => e.data),
|
||||
postMessage: messagePort.postMessage.bind(messagePort) as any
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -187,6 +187,10 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
|
|||
throw barrierError;
|
||||
}
|
||||
|
||||
// Send over message ports for extension API
|
||||
const messagePorts = this._environmentService.options?.messagePorts ?? new Map();
|
||||
iframe.contentWindow!.postMessage(messagePorts, '*', [...messagePorts.values()]);
|
||||
|
||||
port.onmessage = (event) => {
|
||||
const { data } = event;
|
||||
if (!(data instanceof ArrayBuffer)) {
|
||||
|
|
|
@ -45,7 +45,8 @@ export class ExtensionHostMain {
|
|||
protocol: IMessagePassingProtocol,
|
||||
initData: IInitData,
|
||||
hostUtils: IHostUtils,
|
||||
uriTransformer: IURITransformer | null
|
||||
uriTransformer: IURITransformer | null,
|
||||
messagePorts?: ReadonlyMap<string, MessagePort>
|
||||
) {
|
||||
this._isTerminating = false;
|
||||
this._hostUtils = hostUtils;
|
||||
|
@ -56,7 +57,7 @@ export class ExtensionHostMain {
|
|||
|
||||
// bootstrap services
|
||||
const services = new ServiceCollection(...getSingletonServiceDescriptors());
|
||||
services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData });
|
||||
services.set(IExtHostInitDataService, { _serviceBrand: undefined, ...initData, messagePorts });
|
||||
services.set(IExtHostRpcService, new ExtHostRpcService(this._rpcProtocol));
|
||||
services.set(IURITransformerService, new URITransformerService(uriTransformer));
|
||||
services.set(IHostUtils, hostUtils);
|
||||
|
|
|
@ -25,6 +25,7 @@ export const allApiProposals = Object.freeze({
|
|||
fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts',
|
||||
inlayHints: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlayHints.d.ts',
|
||||
inlineCompletions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts',
|
||||
ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts',
|
||||
languageIcon: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageIcon.d.ts',
|
||||
languageStatus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatus.d.ts',
|
||||
notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts',
|
||||
|
|
|
@ -216,18 +216,24 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
|
|||
|
||||
let onTerminate = (reason: string) => nativeClose();
|
||||
|
||||
export function create(): void {
|
||||
const res = new ExtensionWorker();
|
||||
export function create(): { onmessage: (message: any) => void } {
|
||||
performance.mark(`code/extHost/willConnectToRenderer`);
|
||||
connectToRenderer(res.protocol).then(data => {
|
||||
performance.mark(`code/extHost/didWaitForInitData`);
|
||||
const extHostMain = new ExtensionHostMain(
|
||||
data.protocol,
|
||||
data.initData,
|
||||
hostUtil,
|
||||
null,
|
||||
);
|
||||
const res = new ExtensionWorker();
|
||||
|
||||
onTerminate = (reason: string) => extHostMain.terminate(reason);
|
||||
});
|
||||
return {
|
||||
onmessage(messagePorts: ReadonlyMap<string, MessagePort>) {
|
||||
connectToRenderer(res.protocol).then(data => {
|
||||
performance.mark(`code/extHost/didWaitForInitData`);
|
||||
const extHostMain = new ExtensionHostMain(
|
||||
data.protocol,
|
||||
data.initData,
|
||||
hostUtil,
|
||||
null,
|
||||
messagePorts
|
||||
);
|
||||
|
||||
onTerminate = (reason: string) => extHostMain.terminate(reason);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'none';
|
||||
child-src 'self' data: blob:;
|
||||
script-src 'self' 'unsafe-eval' 'sha256-cb2sg39EJV8ABaSNFfWu/ou8o1xVXYK7jp90oZ9vpcg=' https:;
|
||||
script-src 'self' 'unsafe-eval' 'sha256-7r+WjLnkogQ49YJMiebuJrtdmXlsN8evaIGRDcHnFCo=' https:;
|
||||
connect-src 'self' https: wss: http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*;"/>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -59,6 +59,8 @@
|
|||
console.error(event.message, event.error);
|
||||
sendError(event.error);
|
||||
};
|
||||
|
||||
self.onmessage = (event) => worker.postMessage(event.data, event.ports);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
sendError(err);
|
||||
|
|
|
@ -540,6 +540,13 @@ interface IWorkbenchConstructionOptions {
|
|||
//#endregion
|
||||
|
||||
|
||||
//#region IPC
|
||||
|
||||
readonly messagePorts?: ReadonlyMap<ExtensionId, MessagePort>;
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Development options
|
||||
|
||||
readonly developmentOptions?: IDevelopmentOptions;
|
||||
|
|
37
src/vscode-dts/vscode.proposed.ipc.d.ts
vendored
Normal file
37
src/vscode-dts/vscode.proposed.ipc.d.ts
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
/**
|
||||
* A message passing protocol, which enables sending and receiving messages
|
||||
* between two parties.
|
||||
*/
|
||||
export interface MessagePassingProtocol {
|
||||
|
||||
/**
|
||||
* Fired when a message is received from the other party.
|
||||
*/
|
||||
readonly onDidReceiveMessage: Event<any>;
|
||||
|
||||
/**
|
||||
* Post a message to the other party.
|
||||
*
|
||||
* @param message Body of the message. This must be a JSON serializable object.
|
||||
* @param transfer A collection of `ArrayBuffer` instances which can be transferred
|
||||
* to the other party, saving costly memory copy operations.
|
||||
*/
|
||||
postMessage(message: any, transfer?: ArrayBuffer[]): void;
|
||||
}
|
||||
|
||||
export interface ExtensionContext {
|
||||
|
||||
/**
|
||||
* When not `undefined`, this is an instance of {@link MessagePassingProtocol} in
|
||||
* which the other party is owned by the web embedder.
|
||||
*/
|
||||
readonly messagePassingProtocol: MessagePassingProtocol | undefined;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue