提交 2433e293 编写于 作者: B Benjamin Pasero

sandbox - use IPC communication to read bundles

上级 ec5da2bd
......@@ -127,14 +127,11 @@
* @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined}
*/
function setupNLS() {
if (!path || !fs || typeof process === 'undefined') {
console.warn('setupNLS() is only available in node.js environments');
return { availableLanguages: {} }; // TODO@sandbox NLS is currently non-sandboxed only
}
// Get the nls configuration into the process.env as early as possible.
// Get the nls configuration as early as possible.
const process = safeProcess();
let nlsConfig = { availableLanguages: {} };
if (process.env['VSCODE_NLS_CONFIG']) {
if (process && process.env['VSCODE_NLS_CONFIG']) {
try {
nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']);
} catch (e) {
......@@ -153,8 +150,7 @@
return;
}
const bundleFile = path.join(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`);
fs.promises.readFile(bundleFile, 'utf8').then(function (content) {
safeReadNlsFile(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`).then(function (content) {
const json = JSON.parse(content);
bundles[bundle] = json;
......@@ -162,7 +158,7 @@
}).catch((error) => {
try {
if (nlsConfig._corruptedFile) {
fs.promises.writeFile(nlsConfig._corruptedFile, 'corrupted', 'utf8').catch(function (error) { console.error(error); });
safeWriteNlsFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); });
}
} finally {
cb(error, undefined);
......@@ -267,14 +263,69 @@
global['diagnosticsSource'] = {}; // Prevents diagnostic channel (which patches "require") from initializing entirely
}
function safeProcess() {
function safeGlobals() {
const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {});
return globals.vscode;
}
/**
* @returns {NodeJS.Process | undefined}
*/
function safeProcess() {
if (typeof process !== 'undefined') {
return process; // Native environment (non-sandboxed)
} else if (typeof globals.vscode !== 'undefined') {
return globals.vscode.process; // Native environment (sandboxed)
}
const globals = safeGlobals();
if (globals) {
return globals.process; // Native environment (sandboxed)
}
}
/**
* @returns {Electron.IpcRenderer | undefined}
*/
function safeIpcRenderer() {
const globals = safeGlobals();
if (globals) {
return globals.ipcRenderer;
}
}
/**
* @param {string[]} pathSegments
* @returns {Promise<string>}
*/
async function safeReadNlsFile(...pathSegments) {
const ipcRenderer = safeIpcRenderer();
if (ipcRenderer) {
return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments);
}
if (fs && path) {
return (await fs.promises.readFile(path.join(...pathSegments))).toString();
}
throw new Error('Unsupported operation (read NLS files)');
}
/**
* @param {string} path
* @param {string} content
* @returns {Promise<void>}
*/
function safeWriteNlsFile(path, content) {
const ipcRenderer = safeIpcRenderer();
if (ipcRenderer) {
return ipcRenderer.invoke('vscode:writeNlsFile', path, content);
}
if (fs) {
return fs.promises.writeFile(path, content);
}
throw new Error('Unsupported operation (write NLS files)');
}
//#endregion
......
......@@ -35,6 +35,17 @@
}
},
/**
* @param {string} channel
* @param {any[]} args
* @returns {Promise<any> | undefined}
*/
invoke(channel, ...args) {
if (validateIPC(channel)) {
return ipcRenderer.invoke(channel, ...args);
}
},
/**
* @param {string} channel
* @param {(event: import('electron').IpcRendererEvent, ...args: any[]) => void} listener
......@@ -108,33 +119,30 @@
get type() { return 'renderer'; },
get execPath() { return process.execPath; },
resolveEnv:
/**
* @param userEnv {{[key: string]: string}}
* @returns {Promise<void>}
*/
function (userEnv) {
return resolveEnv(userEnv);
},
getProcessMemoryInfo:
/**
* @returns {Promise<import('electron').ProcessMemoryInfo>}
*/
function () {
return process.getProcessMemoryInfo();
},
on:
/**
* @param {string} type
* @param {() => void} callback
*/
function (type, callback) {
if (validateProcessEventType(type)) {
process.on(type, callback);
}
/**
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
resolveEnv(userEnv) {
return resolveEnv(userEnv);
},
/**
* @returns {Promise<import('electron').ProcessMemoryInfo>}
*/
getProcessMemoryInfo() {
return process.getProcessMemoryInfo();
},
/**
* @param {string} type
* @param {() => void} callback
*/
on(type, callback) {
if (validateProcessEventType(type)) {
process.on(type, callback);
}
}
},
/**
......@@ -168,6 +176,7 @@
/**
* @param {string} channel
* @returns {true | never}
*/
function validateIPC(channel) {
if (!channel || !channel.startsWith('vscode:')) {
......@@ -198,7 +207,7 @@
* all development related environment variables. We do this from the
* main process because it may involve spawning a shell.
*
* @param userEnv {{[key: string]: string}}
* @param {{[key: string]: string}} userEnv
* @returns {Promise<void>}
*/
function resolveEnv(userEnv) {
......
......@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, IpcMainEvent, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
import { app, ipcMain as ipc, systemPreferences, contentTracing, protocol, BrowserWindow, dialog, session } from 'electron';
import { IProcessEnvironment, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform';
import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService';
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
import { OpenContext } from 'vs/platform/windows/node/window';
......@@ -52,7 +52,7 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu';
import { sep, posix } from 'vs/base/common/path';
import { sep, posix, join, isAbsolute } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
......@@ -86,6 +86,7 @@ import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platfo
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { DisplayMainService, IDisplayMainService } from 'vs/platform/display/electron-main/displayMainService';
import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper';
import { isEqualOrParent } from 'vs/base/common/extpath';
export class CodeApplication extends Disposable {
private windowsMainService: IWindowsMainService | undefined;
......@@ -100,7 +101,8 @@ export class CodeApplication extends Disposable {
@IEnvironmentMainService private readonly environmentService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStateService private readonly stateService: IStateService
@IStateService private readonly stateService: IStateService,
@IFileService private readonly fileService: IFileService
) {
super();
......@@ -264,7 +266,9 @@ export class CodeApplication extends Disposable {
this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
});
ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => {
//#region Bootstrap IPC Handlers
ipc.on('vscode:fetchShellEnv', async event => {
const webContents = event.sender;
const window = this.windowsMainService?.getWindowByWebContents(event.sender);
......@@ -312,10 +316,50 @@ export class CodeApplication extends Disposable {
acceptShellEnv(shellEnv);
});
ipc.on('vscode:toggleDevTools', (event: IpcMainEvent) => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', (event: IpcMainEvent) => event.sender.openDevTools());
ipc.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => {
const uri = this.validateNlsPath([path]);
if (!uri || typeof data !== 'string') {
return Promise.reject('Invalid operation (vscode:writeNlsFile)');
}
return this.fileService.writeFile(uri, VSBuffer.fromString(data));
});
ipc.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => {
const uri = this.validateNlsPath(paths);
if (!uri) {
return Promise.reject('Invalid operation (vscode:readNlsFile)');
}
return (await this.fileService.readFile(uri)).value.toString();
});
ipc.on('vscode:toggleDevTools', event => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', event => event.sender.openDevTools());
ipc.on('vscode:reloadWindow', event => event.sender.reload());
//#endregion
}
private validateNlsPath(pathSegments: unknown[]): URI | undefined {
let path: string | undefined = undefined;
for (const pathSegment of pathSegments) {
if (typeof pathSegment === 'string') {
if (typeof path !== 'string') {
path = pathSegment;
} else {
path = join(path, pathSegment);
}
}
}
if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentService.cachedLanguagesPath, !isLinux)) {
return undefined;
}
ipc.on('vscode:reloadWindow', (event: IpcMainEvent) => event.sender.reload());
return URI.file(path);
}
private onUnexpectedError(err: Error): void {
......@@ -856,8 +900,7 @@ export class CodeApplication extends Disposable {
// based on telemetry.enableCrashreporter settings, generate a UUID which
// will be used as crash reporter id and also update the json file.
try {
const fileService = accessor.get(IFileService);
const argvContent = await fileService.readFile(this.environmentService.argvResource);
const argvContent = await this.fileService.readFile(this.environmentService.argvResource);
const argvString = argvContent.value.toString();
const argvJSON = JSON.parse(stripComments(argvString));
if (argvJSON['enable-crash-reporter'] === undefined) {
......@@ -874,7 +917,7 @@ export class CodeApplication extends Disposable {
'}'
];
const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n'));
await fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString));
await this.fileService.writeFile(this.environmentService.argvResource, VSBuffer.fromString(newArgvString));
}
} catch (error) {
this.logService.error(error);
......
......@@ -19,6 +19,9 @@ export const IEnvironmentMainService = createDecorator<IEnvironmentMainService>(
*/
export interface IEnvironmentMainService extends INativeEnvironmentService {
// --- NLS cache path
cachedLanguagesPath: string;
// --- backup paths
backupHome: string;
backupWorkspacesPath: string;
......@@ -37,6 +40,9 @@ export interface IEnvironmentMainService extends INativeEnvironmentService {
export class EnvironmentMainService extends NativeEnvironmentService implements IEnvironmentMainService {
@memoize
get cachedLanguagesPath(): string { return join(this.userDataPath, 'clp'); }
@memoize
get backupHome(): string { return join(this.userDataPath, 'Backups'); }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册