Archived
1
0

Add support for running extensions in the browser

This commit is contained in:
Asher
2019-10-04 18:14:07 -05:00
parent 846dcbb947
commit 548d095611
24 changed files with 727 additions and 51 deletions

View File

@ -1,5 +1,5 @@
import * as vscode from "vscode";
import { CoderApi, VSCodeApi } from "../typings/api";
import { CoderApi, VSCodeApi } from "../../typings/api";
import { createCSSRule } from "vs/base/browser/dom";
import { Emitter, Event } from "vs/base/common/event";
import { IDisposable } from "vs/base/common/lifecycle";

View File

@ -1,13 +1,18 @@
import { Emitter } from "vs/base/common/event";
import { URI } from "vs/base/common/uri";
import { registerSingleton } from "vs/platform/instantiation/common/extensions";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { ILocalizationsService } from "vs/platform/localizations/common/localizations";
import { LocalizationsService } from "vs/platform/localizations/electron-browser/localizationsService";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { IUpdateService } from "vs/platform/update/common/update";
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
import { TelemetryChannelClient } from "vs/server/src/telemetry";
import { IUploadService, UploadService } from 'vs/server/src/upload';
import { coderApi, vscodeApi } from "vs/server/src/browser/api";
import { IUploadService, UploadService } from "vs/server/src/browser/upload";
import { INodeProxyService, NodeProxyChannelClient } from "vs/server/src/common/nodeProxy";
import { TelemetryChannelClient } from "vs/server/src/common/telemetry";
import "vs/workbench/contrib/localizations/browser/localizations.contribution";
import "vs/workbench/contrib/update/electron-browser/update.contribution";
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
class TelemetryService extends TelemetryChannelClient {
@ -18,16 +23,28 @@ class TelemetryService extends TelemetryChannelClient {
}
}
class NodeProxyService extends NodeProxyChannelClient implements INodeProxyService {
private readonly _onClose = new Emitter<void>();
public readonly onClose = this._onClose.event;
private readonly _onDown = new Emitter<void>();
public readonly onDown = this._onDown.event;
private readonly _onUp = new Emitter<void>();
public readonly onUp = this._onUp.event;
public constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
) {
// TODO: up/down/close
super(remoteAgentService.getConnection()!.getChannel("nodeProxy"));
}
}
registerSingleton(ILocalizationsService, LocalizationsService);
registerSingleton(INodeProxyService, NodeProxyService);
registerSingleton(ITelemetryService, TelemetryService);
registerSingleton(IUpdateService, UpdateService);
registerSingleton(IUploadService, UploadService, true);
import "vs/workbench/contrib/update/electron-browser/update.contribution";
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
import { coderApi, vscodeApi } from "vs/server/src/api";
/**
* This is called by vs/workbench/browser/web.main.ts after the workbench has
* been initialized so we can initialize our own client-side code.

View File

@ -0,0 +1,46 @@
import { Emitter } from "vs/base/common/event";
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ExtHostNodeProxyShape, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol";
import { IExtHostRpcService } from "vs/workbench/api/common/extHostRpcService";
export class ExtHostNodeProxy implements ExtHostNodeProxyShape {
_serviceBrand: any;
private readonly _onMessage = new Emitter<string>();
public readonly onMessage = this._onMessage.event;
private readonly _onClose = new Emitter<void>();
public readonly onClose = this._onClose.event;
private readonly _onDown = new Emitter<void>();
public readonly onDown = this._onDown.event;
private readonly _onUp = new Emitter<void>();
public readonly onUp = this._onUp.event;
private readonly proxy: MainThreadNodeProxyShape;
constructor(@IExtHostRpcService rpc: IExtHostRpcService) {
this.proxy = rpc.getProxy(MainContext.MainThreadNodeProxy);
}
public $onMessage(message: string): void {
this._onMessage.fire(message);
}
public $onClose(): void {
this._onClose.fire();
}
public $onUp(): void {
this._onUp.fire();
}
public $onDown(): void {
this._onDown.fire();
}
public send(message: string): void {
this.proxy.$send(message);
}
}
export interface IExtHostNodeProxy extends ExtHostNodeProxy { }
export const IExtHostNodeProxy = createDecorator<IExtHostNodeProxy>('IExtHostNodeProxy');

View File

@ -0,0 +1,37 @@
import { IDisposable } from "vs/base/common/lifecycle";
import { INodeProxyService } from "vs/server/src/common/nodeProxy";
import { ExtHostContext, IExtHostContext, MainContext, MainThreadNodeProxyShape } from "vs/workbench/api/common/extHost.protocol";
import { extHostNamedCustomer } from "vs/workbench/api/common/extHostCustomers";
@extHostNamedCustomer(MainContext.MainThreadNodeProxy)
export class MainThreadNodeProxy implements MainThreadNodeProxyShape {
private disposed = false;
private disposables = <IDisposable[]>[];
constructor(
extHostContext: IExtHostContext,
@INodeProxyService private readonly proxyService: INodeProxyService,
) {
if (!extHostContext.remoteAuthority) { // HACK: A terrible way to detect if running in the worker.
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostNodeProxy);
this.disposables = [
this.proxyService.onMessage((message: string) => proxy.$onMessage(message)),
this.proxyService.onClose(() => proxy.$onClose()),
this.proxyService.onDown(() => proxy.$onDown()),
this.proxyService.onUp(() => proxy.$onUp()),
];
}
}
$send(message: string): void {
if (!this.disposed) {
this.proxyService.send(message);
}
}
dispose(): void {
this.disposables.forEach((d) => d.dispose());
this.disposables = [];
this.disposed = true;
}
}

47
src/common/nodeProxy.ts Normal file
View File

@ -0,0 +1,47 @@
import { Event } from "vs/base/common/event";
import { IChannel, IServerChannel } from "vs/base/parts/ipc/common/ipc";
import { createDecorator } from "vs/platform/instantiation/common/instantiation";
import { ReadWriteConnection } from "vs/server/node_modules/@coder/node-browser/out/common/connection";
export const INodeProxyService = createDecorator<INodeProxyService>("nodeProxyService");
export interface INodeProxyService extends ReadWriteConnection {
_serviceBrand: any;
send(message: string): void;
onMessage: Event<string>;
onUp: Event<void>;
onClose: Event<void>;
onDown: Event<void>;
}
export class NodeProxyChannel implements IServerChannel {
constructor(private service: INodeProxyService) {}
listen(_: unknown, event: string): Event<any> {
switch (event) {
case "onMessage": return this.service.onMessage;
}
throw new Error(`Invalid listen ${event}`);
}
async call(_: unknown, command: string, args?: any): Promise<any> {
switch (command) {
case "send": return this.service.send(args[0]);
}
throw new Error(`Invalid call ${command}`);
}
}
export class NodeProxyChannelClient {
_serviceBrand: any;
public readonly onMessage: Event<string>;
constructor(private readonly channel: IChannel) {
this.onMessage = this.channel.listen<string>("onMessage");
}
public send(data: string): void {
this.channel.call("send", [data]);
}
}

View File

@ -16,9 +16,11 @@ import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
import { getTranslations } from "vs/server/src/nls";
import { getUriTransformer } from "vs/server/src/util";
import { INodeProxyService } from "vs/server/src/common/nodeProxy";
import { getTranslations } from "vs/server/src/node/nls";
import { getUriTransformer } from "vs/server/src/node/util";
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
import { Server } from "vs/server/node_modules/@coder/node-browser/out/server/server";
/**
* Extend the file provider to allow unwatching.
@ -274,3 +276,37 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
this.telemetry.setEnabled(false);
}
}
export class NodeProxyService implements INodeProxyService {
public _serviceBrand = undefined;
public readonly server: Server;
private readonly _onMessage = new Emitter<string>();
public readonly onMessage = this._onMessage.event;
private readonly _$onMessage = new Emitter<string>();
public readonly $onMessage = this._$onMessage.event;
private readonly _onClose = new Emitter<void>();
public readonly onClose = this._onClose.event;
private readonly _onDown = new Emitter<void>();
public readonly onDown = this._onDown.event;
private readonly _onUp = new Emitter<void>();
public readonly onUp = this._onUp.event;
public constructor() {
// TODO: close/down/up
this.server = new Server({
onMessage: this.$onMessage,
onClose: this.onClose,
onDown: this.onDown,
onUp: this.onUp,
send: (message: string): void => {
this._onMessage.fire(message);
}
});
}
public send(message: string): void {
this._$onMessage.fire(message);
}
}

View File

@ -9,10 +9,10 @@ import { buildHelpMessage, buildVersionMessage, Option as VsOption, options as v
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
import pkg from "vs/platform/product/node/package";
import product from "vs/platform/product/node/product";
import { ipcMain } from "vs/server/src/ipc";
import { enableCustomMarketplace } from "vs/server/src/marketplace";
import { MainServer } from "vs/server/src/server";
import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/util";
import { ipcMain } from "vs/server/src/node/ipc";
import { enableCustomMarketplace } from "vs/server/src/node/marketplace";
import { MainServer } from "vs/server/src/node/server";
import { AuthType, buildAllowedMessage, enumToArray, FormatType, generateCertificate, generatePassword, localRequire, open, unpackExecutables } from "vs/server/src/node/util";
const { logger } = localRequire<typeof import("@coder/logger/out/index")>("@coder/logger/out/index");
setUnexpectedErrorHandler((error) => logger.warn(error.message));

View File

@ -6,9 +6,9 @@ import { ISocket } from "vs/base/parts/ipc/common/ipc.net";
import { NodeSocket } from "vs/base/parts/ipc/node/ipc.net";
import { IEnvironmentService } from "vs/platform/environment/common/environment";
import { ILogService } from "vs/platform/log/common/log";
import { getNlsConfiguration } from "vs/server/src/nls";
import { Protocol } from "vs/server/src/protocol";
import { uriTransformerPath } from "vs/server/src/util";
import { getNlsConfiguration } from "vs/server/src/node/nls";
import { Protocol } from "vs/server/src/node/protocol";
import { uriTransformerPath } from "vs/server/src/node/util";
import { IExtHostReadyMessage } from "vs/workbench/services/extensions/common/extensionHostProtocol";
export abstract class Connection {

View File

@ -6,7 +6,7 @@ import { mkdirp } from "vs/base/node/pfs";
import * as vszip from "vs/base/node/zip";
import * as nls from "vs/nls";
import product from "vs/platform/product/node/product";
import { localRequire } from "vs/server/src/util";
import { localRequire } from "vs/server/src/node/util";
const tarStream = localRequire<typeof import("tar-stream")>("tar-stream/index");

View File

@ -57,14 +57,15 @@ import { combinedAppender, LogAppender, NullTelemetryService } from "vs/platform
import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender";
import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProperties";
import { UpdateChannel } from "vs/platform/update/node/updateIpc";
import { ExtensionEnvironmentChannel, FileProviderChannel } from "vs/server/src/channel";
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/connection";
import { TelemetryClient } from "vs/server/src/insights";
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/nls";
import { Protocol } from "vs/server/src/protocol";
import { TelemetryChannel } from "vs/server/src/telemetry";
import { UpdateService } from "vs/server/src/update";
import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/util";
import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from "vs/server/src/node/channel";
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/node/connection";
import { TelemetryClient } from "vs/server/src/node/insights";
import { getLocaleFromConfig, getNlsConfiguration } from "vs/server/src/node/nls";
import { NodeProxyChannel } from "vs/server/src/common/nodeProxy";
import { Protocol } from "vs/server/src/node/protocol";
import { TelemetryChannel } from "vs/server/src/common/telemetry";
import { UpdateService } from "vs/server/src/node/update";
import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from "vs/server/src/node/util";
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
@ -125,7 +126,7 @@ export interface ServerOptions {
export abstract class Server {
protected readonly server: http.Server | https.Server;
protected rootPath = path.resolve(__dirname, "../../../..");
protected rootPath = path.resolve(__dirname, "../../../../..");
protected serverRoot = path.join(this.rootPath, "/out/vs/server/src");
protected readonly allowedRequestPaths: string[] = [this.rootPath];
private listenPromise: Promise<string> | undefined;
@ -707,11 +708,13 @@ export class MainServer extends Server {
const requestChannel = new RequestChannel(this.services.get(IRequestService) as IRequestService);
const telemetryChannel = new TelemetryChannel(telemetryService);
const updateChannel = new UpdateChannel(instantiationService.createInstance(UpdateService));
const nodeProxyChannel = new NodeProxyChannel(instantiationService.createInstance(NodeProxyService));
this.ipc.registerChannel("extensions", extensionsChannel);
this.ipc.registerChannel("remoteextensionsenvironment", extensionsEnvironmentChannel);
this.ipc.registerChannel("request", requestChannel);
this.ipc.registerChannel("telemetry", telemetryChannel);
this.ipc.registerChannel("nodeProxy", nodeProxyChannel);
this.ipc.registerChannel("update", updateChannel);
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, fileChannel);
resolve(new ErrorTelemetry(telemetryService));

View File

@ -15,9 +15,9 @@ import pkg from "vs/platform/product/node/package";
import { asJson, IRequestService } from "vs/platform/request/common/request";
import { AvailableForDownload, State, StateType, UpdateType } from "vs/platform/update/common/update";
import { AbstractUpdateService } from "vs/platform/update/electron-main/abstractUpdateService";
import { ipcMain } from "vs/server/src/ipc";
import { extract } from "vs/server/src/marketplace";
import { tmpdir } from "vs/server/src/util";
import { ipcMain } from "vs/server/src/node/ipc";
import { extract } from "vs/server/src/node/marketplace";
import { tmpdir } from "vs/server/src/node/util";
import * as zlib from "zlib";
interface IUpdate {

View File

@ -5,20 +5,20 @@ module.exports = (remoteAuthority) => {
transformIncoming: (uri) => {
switch (uri.scheme) {
case "code-server": return { scheme: "file", path: uri.path };
case "file": return { scheme: "code-server-local", path: uri.path };
case "file": return { scheme: "code-server", path: uri.path };
default: return uri;
}
},
transformOutgoing: (uri) => {
switch (uri.scheme) {
case "code-server-local": return { scheme: "file", path: uri.path };
case "code-server": return { scheme: "file", path: uri.path };
case "file": return { scheme: "code-server", authority: remoteAuthority, path: uri.path };
default: return uri;
}
},
transformOutgoingScheme: (scheme) => {
switch (scheme) {
case "code-server-local": return "file";
case "code-server": return "file";
case "file": return "code-server";
default: return scheme;
}

View File

@ -53,7 +53,7 @@ export const generateCertificate = async (): Promise<{ cert: string, certKey: st
return paths;
};
export const uriTransformerPath = getPathFromAmdModule(require, "vs/server/src/uriTransformer");
export const uriTransformerPath = getPathFromAmdModule(require, "vs/server/src/node/uriTransformer");
export const getUriTransformer = (remoteAuthority: string): URITransformer => {
const rawURITransformerFactory = <any>require.__$__nodeRequire(uriTransformerPath);
const rawURITransformer = <IRawURITransformer>rawURITransformerFactory(remoteAuthority);
@ -135,5 +135,5 @@ export const buildAllowedMessage = (t: any): string => {
* at the root for Node modules.
*/
export const localRequire = <T>(modulePath: string): T => {
return require.__$__nodeRequire(path.resolve(__dirname, "../node_modules", modulePath));
return require.__$__nodeRequire(path.resolve(__dirname, "../../node_modules", modulePath));
};