chore(vscode): update to 1.53.2
These conflicts will be resolved in the following commits. We do it this way so that PR review is possible.
This commit is contained in:
@@ -7,11 +7,10 @@ import 'vs/css!./media/issueReporter';
|
||||
import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded
|
||||
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
|
||||
import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService';
|
||||
import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
|
||||
import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window';
|
||||
import { $, reset, safeInnerHtml, windowOpenNoOpener } from 'vs/base/browser/dom';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
|
||||
import * as collections from 'vs/base/common/collections';
|
||||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -23,9 +22,11 @@ import BaseHtml from 'vs/code/electron-sandbox/issue/issueReporterPage';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { IMainProcessService, ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/common/issue';
|
||||
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
|
||||
const MAX_URL_LENGTH = 2045;
|
||||
|
||||
@@ -82,14 +83,12 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
this.initServices(configuration);
|
||||
|
||||
const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION;
|
||||
|
||||
const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id === configuration.data.extensionId) : undefined;
|
||||
this.issueReporterModel = new IssueReporterModel({
|
||||
issueType: configuration.data.issueType || IssueType.Bug,
|
||||
versionInfo: {
|
||||
vscodeVersion: `${configuration.product.nameShort} ${configuration.product.version} (${configuration.product.commit || 'Commit unknown'}, ${configuration.product.date || 'Date unknown'})`,
|
||||
os: `${this.configuration.os.type} ${this.configuration.os.arch} ${this.configuration.os.release}${isSnap ? ' snap' : ''}`
|
||||
os: `${this.configuration.os.type} ${this.configuration.os.arch} ${this.configuration.os.release}${platform.isLinuxSnap ? ' snap' : ''}`
|
||||
},
|
||||
extensionsDisabled: !!configuration.disableExtensions,
|
||||
fileOnExtension: configuration.data.extensionId ? !targetExtension?.isBuiltin : undefined,
|
||||
@@ -267,7 +266,7 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
private initServices(configuration: IssueReporterConfiguration): void {
|
||||
const serviceCollection = new ServiceCollection();
|
||||
const mainProcessService = new MainProcessService(configuration.windowId);
|
||||
const mainProcessService = new ElectronIPCMainProcessService(configuration.windowId);
|
||||
serviceCollection.set(IMainProcessService, mainProcessService);
|
||||
|
||||
this.nativeHostService = new NativeHostService(configuration.windowId, mainProcessService) as INativeHostService;
|
||||
@@ -442,7 +441,11 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
private updatePreviewButtonState() {
|
||||
if (this.isPreviewEnabled()) {
|
||||
this.previewButton.label = localize('previewOnGitHub', "Preview on GitHub");
|
||||
if (this.configuration.data.githubAccessToken) {
|
||||
this.previewButton.label = localize('createOnGitHub', "Create on GitHub");
|
||||
} else {
|
||||
this.previewButton.label = localize('previewOnGitHub', "Preview on GitHub");
|
||||
}
|
||||
this.previewButton.enabled = true;
|
||||
} else {
|
||||
this.previewButton.enabled = false;
|
||||
@@ -598,8 +601,7 @@ export class IssueReporter extends Disposable {
|
||||
issueState = $('span.issue-state');
|
||||
|
||||
const issueIcon = $('span.issue-icon');
|
||||
const codicon = new CodiconLabel(issueIcon);
|
||||
codicon.text = issue.state === 'open' ? '$(issue-opened)' : '$(issue-closed)';
|
||||
issueIcon.appendChild(renderIcon(issue.state === 'open' ? Codicon.issueOpened : Codicon.issueClosed));
|
||||
|
||||
const issueStateLabel = $('span.issue-state.label');
|
||||
issueStateLabel.textContent = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed");
|
||||
@@ -778,6 +780,35 @@ export class IssueReporter extends Disposable {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private async submitToGitHub(issueTitle: string, issueBody: string, gitHubDetails: { owner: string, repositoryName: string }): Promise<boolean> {
|
||||
const url = `https://api.github.com/repos/${gitHubDetails.owner}/${gitHubDetails.repositoryName}/issues`;
|
||||
const init = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
title: issueTitle,
|
||||
body: issueBody
|
||||
}),
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.configuration.data.githubAccessToken}`
|
||||
})
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
window.fetch(url, init).then((response) => {
|
||||
if (response.ok) {
|
||||
response.json().then(result => {
|
||||
ipcRenderer.send('vscode:openExternal', result.html_url);
|
||||
ipcRenderer.send('vscode:closeIssueReporter');
|
||||
resolve(true);
|
||||
});
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async createIssue(): Promise<boolean> {
|
||||
if (!this.validateInputs()) {
|
||||
// If inputs are invalid, set focus to the first one and add listeners on them
|
||||
@@ -810,8 +841,16 @@ export class IssueReporter extends Disposable {
|
||||
|
||||
this.hasBeenSubmitted = true;
|
||||
|
||||
const baseUrl = this.getIssueUrlWithTitle((<HTMLInputElement>this.getElementById('issue-title')).value);
|
||||
const issueTitle = (<HTMLInputElement>this.getElementById('issue-title')).value;
|
||||
const issueBody = this.issueReporterModel.serialize();
|
||||
|
||||
const issueUrl = this.issueReporterModel.fileOnExtension() ? this.getExtensionGitHubUrl() : this.configuration.product.reportIssueUrl!;
|
||||
const gitHubDetails = this.parseGitHubUrl(issueUrl);
|
||||
if (this.configuration.data.githubAccessToken && gitHubDetails) {
|
||||
return this.submitToGitHub(issueTitle, issueBody, gitHubDetails);
|
||||
}
|
||||
|
||||
const baseUrl = this.getIssueUrlWithTitle((<HTMLInputElement>this.getElementById('issue-title')).value);
|
||||
let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`;
|
||||
|
||||
if (url.length > MAX_URL_LENGTH) {
|
||||
@@ -841,6 +880,20 @@ export class IssueReporter extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private parseGitHubUrl(url: string): undefined | { repositoryName: string, owner: string } {
|
||||
// Assumes a GitHub url to a particular repo, https://github.com/repositoryName/owner.
|
||||
// Repository name and owner cannot contain '/'
|
||||
const match = /^https?:\/\/github\.com\/([^\/]*)\/([^\/]*).*/.exec(url);
|
||||
if (match && match.length) {
|
||||
return {
|
||||
owner: match[1],
|
||||
repositoryName: match[2]
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getExtensionGitHubUrl(): string {
|
||||
let repositoryUrl = '';
|
||||
const bugsUrl = this.getExtensionBugsUrl();
|
||||
|
||||
@@ -49,28 +49,12 @@ body {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.process-item {
|
||||
line-height: 22px;
|
||||
.monaco-list-row:first-of-type {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
th[scope='col'] {
|
||||
vertical-align: bottom;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
padding: .5rem;
|
||||
border-top: 1px solid #cccccc;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: .25rem;
|
||||
vertical-align: top;
|
||||
cursor: default;
|
||||
.row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.centered {
|
||||
@@ -79,6 +63,9 @@ td {
|
||||
|
||||
.nameLabel{
|
||||
text-align: left;
|
||||
width: calc(100% - 185px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.data {
|
||||
@@ -93,15 +80,3 @@ td {
|
||||
padding-left: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
tbody > tr:hover {
|
||||
background-color: #2A2D2E;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
|
||||
</head>
|
||||
<body aria-label="">
|
||||
<table id="process-list"></table>
|
||||
<div id="process-list"></div>
|
||||
</body>
|
||||
|
||||
<!-- Init Bootstrap Helpers -->
|
||||
|
||||
@@ -14,38 +14,226 @@ import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox
|
||||
import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu';
|
||||
import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu';
|
||||
import { ProcessItem } from 'vs/base/common/processes';
|
||||
import { addDisposableListener, $ } from 'vs/base/browser/dom';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { isRemoteDiagnosticError, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
|
||||
import { MainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { CodiconLabel } from 'vs/base/browser/ui/codicons/codiconLabel';
|
||||
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
|
||||
import { ByteSize } from 'vs/platform/files/common/files';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
|
||||
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
|
||||
|
||||
const DEBUG_FLAGS_PATTERN = /\s--(inspect|debug)(-brk|port)?=(\d+)?/;
|
||||
const DEBUG_PORT_PATTERN = /\s--(inspect|debug)-port=(\d+)/;
|
||||
|
||||
interface FormattedProcessItem {
|
||||
cpu: number;
|
||||
memory: number;
|
||||
pid: string;
|
||||
class ProcessListDelegate implements IListVirtualDelegate<MachineProcessInformation | ProcessItem | IRemoteDiagnosticError> {
|
||||
getHeight(element: MachineProcessInformation | ProcessItem | IRemoteDiagnosticError) {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(element: ProcessInformation | MachineProcessInformation | ProcessItem | IRemoteDiagnosticError) {
|
||||
if (isProcessItem(element)) {
|
||||
return 'process';
|
||||
}
|
||||
|
||||
if (isMachineProcessInformation(element)) {
|
||||
return 'machine';
|
||||
}
|
||||
|
||||
if (isRemoteDiagnosticError(element)) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
if (isProcessInformation(element)) {
|
||||
return 'header';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
interface IProcessItemTemplateData extends IProcessRowTemplateData {
|
||||
CPU: HTMLElement;
|
||||
memory: HTMLElement;
|
||||
PID: HTMLElement;
|
||||
}
|
||||
|
||||
interface IProcessRowTemplateData {
|
||||
name: HTMLElement;
|
||||
}
|
||||
|
||||
class ProcessTreeDataSource implements IDataSource<ProcessTree, ProcessInformation | MachineProcessInformation | ProcessItem | IRemoteDiagnosticError> {
|
||||
hasChildren(element: ProcessTree | ProcessInformation | MachineProcessInformation | ProcessItem | IRemoteDiagnosticError): boolean {
|
||||
if (isRemoteDiagnosticError(element)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isProcessItem(element)) {
|
||||
return !!element.children?.length;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
getChildren(element: ProcessTree | ProcessInformation | MachineProcessInformation | ProcessItem | IRemoteDiagnosticError) {
|
||||
if (isProcessItem(element)) {
|
||||
return element.children ? element.children : [];
|
||||
}
|
||||
|
||||
if (isRemoteDiagnosticError(element)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isProcessInformation(element)) {
|
||||
// If there are multiple process roots, return these, otherwise go directly to the root process
|
||||
if (element.processRoots.length > 1) {
|
||||
return element.processRoots;
|
||||
} else {
|
||||
return [element.processRoots[0].rootProcess];
|
||||
}
|
||||
}
|
||||
|
||||
if (isMachineProcessInformation(element)) {
|
||||
return [element.rootProcess];
|
||||
}
|
||||
|
||||
return [element.processes];
|
||||
}
|
||||
}
|
||||
|
||||
class ProcessHeaderTreeRenderer implements ITreeRenderer<ProcessInformation, void, IProcessItemTemplateData> {
|
||||
templateId: string = 'header';
|
||||
renderTemplate(container: HTMLElement): IProcessItemTemplateData {
|
||||
const data = Object.create(null);
|
||||
const row = dom.append(container, dom.$('.row'));
|
||||
data.name = dom.append(row, dom.$('.nameLabel'));
|
||||
data.CPU = dom.append(row, dom.$('.cpu'));
|
||||
data.memory = dom.append(row, dom.$('.memory'));
|
||||
data.PID = dom.append(row, dom.$('.pid'));
|
||||
return data;
|
||||
}
|
||||
renderElement(node: ITreeNode<ProcessInformation, void>, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void {
|
||||
templateData.name.textContent = localize('name', "Process Name");
|
||||
templateData.CPU.textContent = localize('cpu', "CPU %");
|
||||
templateData.PID.textContent = localize('pid', "PID");
|
||||
templateData.memory.textContent = localize('memory', "Memory (MB)");
|
||||
|
||||
}
|
||||
disposeTemplate(templateData: any): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
class MachineRenderer implements ITreeRenderer<MachineProcessInformation, void, IProcessRowTemplateData> {
|
||||
templateId: string = 'machine';
|
||||
renderTemplate(container: HTMLElement): IProcessRowTemplateData {
|
||||
const data = Object.create(null);
|
||||
const row = dom.append(container, dom.$('.row'));
|
||||
data.name = dom.append(row, dom.$('.nameLabel'));
|
||||
return data;
|
||||
}
|
||||
renderElement(node: ITreeNode<MachineProcessInformation, void>, index: number, templateData: IProcessRowTemplateData, height: number | undefined): void {
|
||||
templateData.name.textContent = node.element.name;
|
||||
}
|
||||
disposeTemplate(templateData: IProcessRowTemplateData): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorRenderer implements ITreeRenderer<IRemoteDiagnosticError, void, IProcessRowTemplateData> {
|
||||
templateId: string = 'error';
|
||||
renderTemplate(container: HTMLElement): IProcessRowTemplateData {
|
||||
const data = Object.create(null);
|
||||
const row = dom.append(container, dom.$('.row'));
|
||||
data.name = dom.append(row, dom.$('.nameLabel'));
|
||||
return data;
|
||||
}
|
||||
renderElement(node: ITreeNode<IRemoteDiagnosticError, void>, index: number, templateData: IProcessRowTemplateData, height: number | undefined): void {
|
||||
templateData.name.textContent = node.element.errorMessage;
|
||||
}
|
||||
disposeTemplate(templateData: IProcessRowTemplateData): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ProcessRenderer implements ITreeRenderer<ProcessItem, void, IProcessItemTemplateData> {
|
||||
constructor(private platform: string, private totalMem: number, private mapPidToWindowTitle: Map<number, string>) { }
|
||||
|
||||
templateId: string = 'process';
|
||||
renderTemplate(container: HTMLElement): IProcessItemTemplateData {
|
||||
const data = <IProcessItemTemplateData>Object.create(null);
|
||||
const row = dom.append(container, dom.$('.row'));
|
||||
|
||||
data.name = dom.append(row, dom.$('.nameLabel'));
|
||||
data.CPU = dom.append(row, dom.$('.cpu'));
|
||||
data.memory = dom.append(row, dom.$('.memory'));
|
||||
data.PID = dom.append(row, dom.$('.pid'));
|
||||
|
||||
return data;
|
||||
}
|
||||
renderElement(node: ITreeNode<ProcessItem, void>, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void {
|
||||
const { element } = node;
|
||||
|
||||
let name = element.name;
|
||||
if (name === 'window') {
|
||||
const windowTitle = this.mapPidToWindowTitle.get(element.pid);
|
||||
name = windowTitle !== undefined ? `${name} (${this.mapPidToWindowTitle.get(element.pid)})` : name;
|
||||
}
|
||||
|
||||
templateData.name.textContent = name;
|
||||
templateData.name.title = element.cmd;
|
||||
|
||||
templateData.CPU.textContent = element.load.toFixed(0);
|
||||
templateData.PID.textContent = element.pid.toFixed(0);
|
||||
|
||||
const memory = this.platform === 'win32' ? element.mem : (this.totalMem * (element.mem / 100));
|
||||
templateData.memory.textContent = (memory / ByteSize.MB).toFixed(0);
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IProcessItemTemplateData): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
interface MachineProcessInformation {
|
||||
name: string;
|
||||
formattedName: string;
|
||||
cmd: string;
|
||||
rootProcess: ProcessItem | IRemoteDiagnosticError
|
||||
}
|
||||
|
||||
interface ProcessInformation {
|
||||
processRoots: MachineProcessInformation[];
|
||||
}
|
||||
|
||||
interface ProcessTree {
|
||||
processes: ProcessInformation;
|
||||
}
|
||||
|
||||
function isMachineProcessInformation(item: any): item is MachineProcessInformation {
|
||||
return !!item.name && !!item.rootProcess;
|
||||
}
|
||||
|
||||
function isProcessInformation(item: any): item is ProcessInformation {
|
||||
return !!item.processRoots;
|
||||
}
|
||||
|
||||
function isProcessItem(item: any): item is ProcessItem {
|
||||
return !!item.pid;
|
||||
}
|
||||
|
||||
class ProcessExplorer {
|
||||
private lastRequestTime: number;
|
||||
|
||||
private collapsedStateCache: Map<string, boolean> = new Map<string, boolean>();
|
||||
|
||||
private mapPidToWindowTitle = new Map<number, string>();
|
||||
|
||||
private listeners = new DisposableStore();
|
||||
|
||||
private nativeHostService: INativeHostService;
|
||||
|
||||
private tree: DataTree<any, ProcessTree | MachineProcessInformation | ProcessItem | ProcessInformation | IRemoteDiagnosticError, any> | undefined;
|
||||
|
||||
constructor(windowId: number, private data: ProcessExplorerData) {
|
||||
const mainProcessService = new MainProcessService(windowId);
|
||||
const mainProcessService = new ElectronIPCMainProcessService(windowId);
|
||||
this.nativeHostService = new NativeHostService(windowId, mainProcessService) as INativeHostService;
|
||||
|
||||
this.applyStyles(data.styles);
|
||||
@@ -56,8 +244,19 @@ class ProcessExplorer {
|
||||
windows.forEach(window => this.mapPidToWindowTitle.set(window.pid, window.title));
|
||||
});
|
||||
|
||||
ipcRenderer.on('vscode:listProcessesResponse', (event: unknown, processRoots: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]) => {
|
||||
this.updateProcessInfo(processRoots);
|
||||
ipcRenderer.on('vscode:listProcessesResponse', async (event: unknown, processRoots: MachineProcessInformation[]) => {
|
||||
processRoots.forEach((info, index) => {
|
||||
if (isProcessItem(info.rootProcess)) {
|
||||
info.rootProcess.name = index === 0 ? `${this.data.applicationName} main` : 'remote agent';
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.tree) {
|
||||
await this.createProcessTree(processRoots);
|
||||
} else {
|
||||
this.tree.setInput({ processes: { processRoots } });
|
||||
}
|
||||
|
||||
this.requestProcessList(0);
|
||||
});
|
||||
|
||||
@@ -66,54 +265,58 @@ class ProcessExplorer {
|
||||
ipcRenderer.send('vscode:listProcesses');
|
||||
}
|
||||
|
||||
private getProcessList(rootProcess: ProcessItem, isLocal: boolean, totalMem: number): FormattedProcessItem[] {
|
||||
const processes: FormattedProcessItem[] = [];
|
||||
const handledProcesses = new Set<number>();
|
||||
|
||||
if (rootProcess) {
|
||||
this.getProcessItem(processes, rootProcess, 0, isLocal, totalMem, handledProcesses);
|
||||
private async createProcessTree(processRoots: MachineProcessInformation[]): Promise<void> {
|
||||
const container = document.getElementById('process-list');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
return processes;
|
||||
}
|
||||
const { totalmem } = await this.nativeHostService.getOSStatistics();
|
||||
|
||||
private getProcessItem(processes: FormattedProcessItem[], item: ProcessItem, indent: number, isLocal: boolean, totalMem: number, handledProcesses: Set<number>): void {
|
||||
const isRoot = (indent === 0);
|
||||
const renderers = [
|
||||
new ProcessRenderer(this.data.platform, totalmem, this.mapPidToWindowTitle),
|
||||
new ProcessHeaderTreeRenderer(),
|
||||
new MachineRenderer(),
|
||||
new ErrorRenderer()
|
||||
];
|
||||
|
||||
handledProcesses.add(item.pid);
|
||||
this.tree = new DataTree('processExplorer',
|
||||
container,
|
||||
new ProcessListDelegate(),
|
||||
renderers,
|
||||
new ProcessTreeDataSource(),
|
||||
{
|
||||
identityProvider:
|
||||
{
|
||||
getId: (element: ProcessTree | ProcessItem | MachineProcessInformation | ProcessInformation | IRemoteDiagnosticError) => {
|
||||
if (isProcessItem(element)) {
|
||||
return element.pid.toString();
|
||||
}
|
||||
|
||||
let name = item.name;
|
||||
if (isRoot) {
|
||||
name = isLocal ? `${this.data.applicationName} main` : 'remote agent';
|
||||
}
|
||||
if (isRemoteDiagnosticError(element)) {
|
||||
return element.hostName;
|
||||
}
|
||||
|
||||
if (name === 'window') {
|
||||
const windowTitle = this.mapPidToWindowTitle.get(item.pid);
|
||||
name = windowTitle !== undefined ? `${name} (${this.mapPidToWindowTitle.get(item.pid)})` : name;
|
||||
}
|
||||
if (isProcessInformation(element)) {
|
||||
return 'processes';
|
||||
}
|
||||
|
||||
// Format name with indent
|
||||
const formattedName = isRoot ? name : `${' '.repeat(indent)} ${name}`;
|
||||
const memory = this.data.platform === 'win32' ? item.mem : (totalMem * (item.mem / 100));
|
||||
processes.push({
|
||||
cpu: item.load,
|
||||
memory: (memory / ByteSize.MB),
|
||||
pid: item.pid.toFixed(0),
|
||||
name,
|
||||
formattedName,
|
||||
cmd: item.cmd
|
||||
});
|
||||
if (isMachineProcessInformation(element)) {
|
||||
return element.name;
|
||||
}
|
||||
|
||||
// Recurse into children if any
|
||||
if (Array.isArray(item.children)) {
|
||||
item.children.forEach(child => {
|
||||
if (!child || handledProcesses.has(child.pid)) {
|
||||
return; // prevent loops
|
||||
return 'header';
|
||||
}
|
||||
}
|
||||
|
||||
this.getProcessItem(processes, child, indent + 1, isLocal, totalMem, handledProcesses);
|
||||
});
|
||||
}
|
||||
|
||||
this.tree.setInput({ processes: { processRoots } });
|
||||
this.tree.layout(window.innerHeight, window.innerWidth);
|
||||
this.tree.onContextMenu(e => {
|
||||
if (isProcessItem(e.element)) {
|
||||
this.showContextMenu(e.element, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private isDebuggable(cmd: string): boolean {
|
||||
@@ -121,7 +324,7 @@ class ProcessExplorer {
|
||||
return (matches && matches.length >= 2) || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0;
|
||||
}
|
||||
|
||||
private attachTo(item: FormattedProcessItem) {
|
||||
private attachTo(item: ProcessItem) {
|
||||
const config: any = {
|
||||
type: 'node',
|
||||
request: 'attach',
|
||||
@@ -150,179 +353,16 @@ class ProcessExplorer {
|
||||
ipcRenderer.send('vscode:workbenchCommand', { id: 'debug.startFromConfig', from: 'processExplorer', args: [config] });
|
||||
}
|
||||
|
||||
private getProcessIdWithHighestProperty(processList: any[], propertyName: string) {
|
||||
let max = 0;
|
||||
let maxProcessId;
|
||||
processList.forEach(process => {
|
||||
if (process[propertyName] > max) {
|
||||
max = process[propertyName];
|
||||
maxProcessId = process.pid;
|
||||
}
|
||||
});
|
||||
|
||||
return maxProcessId;
|
||||
}
|
||||
|
||||
private updateSectionCollapsedState(shouldExpand: boolean, body: HTMLElement, twistie: CodiconLabel, sectionName: string) {
|
||||
if (shouldExpand) {
|
||||
body.classList.remove('hidden');
|
||||
this.collapsedStateCache.set(sectionName, false);
|
||||
twistie.text = '$(chevron-down)';
|
||||
} else {
|
||||
body.classList.add('hidden');
|
||||
this.collapsedStateCache.set(sectionName, true);
|
||||
twistie.text = '$(chevron-right)';
|
||||
}
|
||||
}
|
||||
|
||||
private renderProcessFetchError(sectionName: string, errorMessage: string) {
|
||||
const container = document.getElementById('process-list');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const body = document.createElement('tbody');
|
||||
|
||||
this.renderProcessGroupHeader(sectionName, body, container);
|
||||
|
||||
const errorRow = document.createElement('tr');
|
||||
const data = document.createElement('td');
|
||||
data.textContent = errorMessage;
|
||||
data.className = 'error';
|
||||
data.colSpan = 4;
|
||||
errorRow.appendChild(data);
|
||||
|
||||
body.appendChild(errorRow);
|
||||
container.appendChild(body);
|
||||
}
|
||||
|
||||
private renderProcessGroupHeader(sectionName: string, body: HTMLElement, container: HTMLElement) {
|
||||
const headerRow = document.createElement('tr');
|
||||
|
||||
const headerData = document.createElement('td');
|
||||
headerData.colSpan = 4;
|
||||
headerRow.appendChild(headerData);
|
||||
|
||||
const headerContainer = document.createElement('div');
|
||||
headerContainer.className = 'header';
|
||||
headerData.appendChild(headerContainer);
|
||||
|
||||
const twistieContainer = document.createElement('div');
|
||||
const twistieCodicon = new CodiconLabel(twistieContainer);
|
||||
this.updateSectionCollapsedState(!this.collapsedStateCache.get(sectionName), body, twistieCodicon, sectionName);
|
||||
headerContainer.appendChild(twistieContainer);
|
||||
|
||||
const headerLabel = document.createElement('span');
|
||||
headerLabel.textContent = sectionName;
|
||||
headerContainer.appendChild(headerLabel);
|
||||
|
||||
this.listeners.add(addDisposableListener(headerData, 'click', (e) => {
|
||||
const isHidden = body.classList.contains('hidden');
|
||||
this.updateSectionCollapsedState(isHidden, body, twistieCodicon, sectionName);
|
||||
}));
|
||||
|
||||
container.appendChild(headerRow);
|
||||
}
|
||||
|
||||
private renderTableSection(sectionName: string, processList: FormattedProcessItem[], renderManySections: boolean, sectionIsLocal: boolean): void {
|
||||
const container = document.getElementById('process-list');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const highestCPUProcess = this.getProcessIdWithHighestProperty(processList, 'cpu');
|
||||
const highestMemoryProcess = this.getProcessIdWithHighestProperty(processList, 'memory');
|
||||
|
||||
const body = document.createElement('tbody');
|
||||
|
||||
if (renderManySections) {
|
||||
this.renderProcessGroupHeader(sectionName, body, container);
|
||||
}
|
||||
|
||||
processList.forEach(p => {
|
||||
const row = document.createElement('tr');
|
||||
row.id = p.pid.toString();
|
||||
|
||||
const cpu = document.createElement('td');
|
||||
p.pid === highestCPUProcess
|
||||
? cpu.classList.add('centered', 'highest')
|
||||
: cpu.classList.add('centered');
|
||||
cpu.textContent = p.cpu.toFixed(0);
|
||||
|
||||
const memory = document.createElement('td');
|
||||
p.pid === highestMemoryProcess
|
||||
? memory.classList.add('centered', 'highest')
|
||||
: memory.classList.add('centered');
|
||||
memory.textContent = p.memory.toFixed(0);
|
||||
|
||||
const pid = document.createElement('td');
|
||||
pid.classList.add('centered');
|
||||
pid.textContent = p.pid;
|
||||
|
||||
const name = document.createElement('th');
|
||||
name.scope = 'row';
|
||||
name.classList.add('data');
|
||||
name.title = p.cmd;
|
||||
name.textContent = p.formattedName;
|
||||
|
||||
row.append(cpu, memory, pid, name);
|
||||
|
||||
this.listeners.add(addDisposableListener(row, 'contextmenu', (e) => {
|
||||
this.showContextMenu(e, p, sectionIsLocal);
|
||||
}));
|
||||
|
||||
body.appendChild(row);
|
||||
});
|
||||
|
||||
container.appendChild(body);
|
||||
}
|
||||
|
||||
private async updateProcessInfo(processLists: [{ name: string, rootProcess: ProcessItem | IRemoteDiagnosticError }]): Promise<void> {
|
||||
const container = document.getElementById('process-list');
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerText = '';
|
||||
this.listeners.clear();
|
||||
|
||||
const tableHead = $('thead', undefined);
|
||||
const row = $('tr');
|
||||
tableHead.append(row);
|
||||
|
||||
row.append($('th.cpu', { scope: 'col' }, localize('cpu', "CPU %")));
|
||||
row.append($('th.memory', { scope: 'col' }, localize('memory', "Memory (MB)")));
|
||||
row.append($('th.pid', { scope: 'col' }, localize('pid', "PID")));
|
||||
row.append($('th.nameLabel', { scope: 'col' }, localize('name', "Name")));
|
||||
|
||||
container.append(tableHead);
|
||||
|
||||
const hasMultipleMachines = Object.keys(processLists).length > 1;
|
||||
const { totalmem } = await this.nativeHostService.getOSStatistics();
|
||||
processLists.forEach((remote, i) => {
|
||||
const isLocal = i === 0;
|
||||
if (isRemoteDiagnosticError(remote.rootProcess)) {
|
||||
this.renderProcessFetchError(remote.name, remote.rootProcess.errorMessage);
|
||||
} else {
|
||||
this.renderTableSection(remote.name, this.getProcessList(remote.rootProcess, isLocal, totalmem), hasMultipleMachines, isLocal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private applyStyles(styles: ProcessExplorerStyles): void {
|
||||
const styleTag = document.createElement('style');
|
||||
const content: string[] = [];
|
||||
|
||||
if (styles.hoverBackground) {
|
||||
content.push(`tbody > tr:hover, table > tr:hover { background-color: ${styles.hoverBackground}; }`);
|
||||
content.push(`.monaco-list-row:hover { background-color: ${styles.hoverBackground}; }`);
|
||||
}
|
||||
|
||||
if (styles.hoverForeground) {
|
||||
content.push(`tbody > tr:hover, table > tr:hover { color: ${styles.hoverForeground}; }`);
|
||||
}
|
||||
|
||||
if (styles.highlightForeground) {
|
||||
content.push(`.highest { color: ${styles.highlightForeground}; }`);
|
||||
content.push(`.monaco-list-row:hover { color: ${styles.hoverForeground}; }`);
|
||||
}
|
||||
|
||||
styleTag.textContent = content.join('\n');
|
||||
@@ -334,9 +374,7 @@ class ProcessExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
private showContextMenu(e: MouseEvent, item: FormattedProcessItem, isLocal: boolean) {
|
||||
e.preventDefault();
|
||||
|
||||
private showContextMenu(item: ProcessItem, isLocal: boolean) {
|
||||
const items: IContextMenuItem[] = [];
|
||||
const pid = Number(item.pid);
|
||||
|
||||
@@ -417,8 +455,6 @@ class ProcessExplorer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function startup(windowId: number, data: ProcessExplorerData): void {
|
||||
const platformClass = data.platform === 'win32' ? 'windows' : data.platform === 'linux' ? 'linux' : 'mac';
|
||||
document.body.classList.add(platformClass); // used by our fonts
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'none'; img-src 'self' https: data:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif;
|
||||
font-size: 10pt;
|
||||
background-color: #F3F3F3;
|
||||
}
|
||||
|
||||
#main {
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
background-color: #555;
|
||||
color: #f0f0f0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#username,
|
||||
#password {
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#buttons {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", system-ui, "Ubuntu", "Droid Sans", sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 id="title"></h1>
|
||||
<section id="main">
|
||||
<p id="message"></p>
|
||||
<form id="form">
|
||||
<p><input type="text" id="username" placeholder="Username" required /></p>
|
||||
<p><input type="password" id="password" placeholder="Password" required /></p>
|
||||
<p id="buttons">
|
||||
<input id="ok" type="submit" value="OK" />
|
||||
<input id="cancel" type="button" value="Cancel" />
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
<script src="auth.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -1,48 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { ipcRenderer } = window.vscode;
|
||||
|
||||
function promptForCredentials(data) {
|
||||
return new Promise((c, e) => {
|
||||
const $title = document.getElementById('title');
|
||||
const $username = document.getElementById('username');
|
||||
const $password = document.getElementById('password');
|
||||
const $form = document.getElementById('form');
|
||||
const $cancel = document.getElementById('cancel');
|
||||
const $message = document.getElementById('message');
|
||||
|
||||
function submit() {
|
||||
c({ username: $username.value, password: $password.value });
|
||||
return false;
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
c({ username: '', password: '' });
|
||||
return false;
|
||||
}
|
||||
|
||||
$form.addEventListener('submit', submit);
|
||||
$cancel.addEventListener('click', cancel);
|
||||
|
||||
document.body.addEventListener('keydown', function (e) {
|
||||
switch (e.keyCode) {
|
||||
case 27: e.preventDefault(); e.stopPropagation(); return cancel();
|
||||
case 13: e.preventDefault(); e.stopPropagation(); return submit();
|
||||
}
|
||||
});
|
||||
|
||||
$title.textContent = data.title;
|
||||
$message.textContent = data.message;
|
||||
$username.focus();
|
||||
});
|
||||
}
|
||||
|
||||
ipcRenderer.on('vscode:openProxyAuthDialog', async (event, data) => {
|
||||
const response = await promptForCredentials(data);
|
||||
ipcRenderer.send('vscode:proxyAuthResponse', response);
|
||||
});
|
||||
@@ -3,7 +3,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview: https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' vscode-webview: https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https: ws:; font-src 'self' https: vscode-remote-resource:;">
|
||||
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'; trusted-types default TrustedFunctionWorkaround ExtensionScripts amdLoader cellRendererEditorText defaultWorkerFactory diffEditorWidget domLineBreaksComputer editorViewLayer diffReview extensionHostWorker insane notebookOutputRenderer safeInnerHtml standaloneColorizer tokenizeToString webNestedWorkerExtensionHost webWorkerExtensionHost;">
|
||||
</head>
|
||||
<body aria-label="">
|
||||
</body>
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
const bootstrapWindow = bootstrapWindowLib();
|
||||
|
||||
// Add a perf entry right from the top
|
||||
const perf = bootstrapWindow.perfLib();
|
||||
perf.mark('renderer/started');
|
||||
performance.mark('code/didStartRenderer');
|
||||
|
||||
// Load workbench main JS, CSS and NLS all in parallel. This is an
|
||||
// optimization to prevent a waterfall of loading to happen, because
|
||||
@@ -24,10 +23,10 @@
|
||||
'vs/nls!vs/workbench/workbench.desktop.main',
|
||||
'vs/css!vs/workbench/workbench.desktop.main'
|
||||
],
|
||||
async function (workbench, configuration) {
|
||||
function (_, configuration) {
|
||||
|
||||
// Mark start of workbench
|
||||
perf.mark('didLoadWorkbenchMain');
|
||||
performance.mark('code/didLoadWorkbenchMain');
|
||||
|
||||
// @ts-ignore
|
||||
return require('vs/workbench/electron-sandbox/desktop.main').main(configuration);
|
||||
@@ -41,19 +40,34 @@
|
||||
loaderConfig.recordStats = true;
|
||||
},
|
||||
beforeRequire: function () {
|
||||
perf.mark('willLoadWorkbenchMain');
|
||||
performance.mark('code/willLoadWorkbenchMain');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// add default trustedTypes-policy for logging and to workaround
|
||||
// lib/platform limitations
|
||||
window.trustedTypes?.createPolicy('default', {
|
||||
createHTML(value) {
|
||||
// see https://github.com/electron/electron/issues/27211
|
||||
// Electron webviews use a static innerHTML default value and
|
||||
// that isn't trusted. We use a default policy to check for the
|
||||
// exact value of that innerHTML-string and only allow that.
|
||||
if (value === '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>') {
|
||||
return value;
|
||||
}
|
||||
throw new Error('UNTRUSTED html usage, default trusted types policy should NEVER be reached');
|
||||
// console.trace('UNTRUSTED html usage, default trusted types policy should NEVER be reached');
|
||||
// return value;
|
||||
}
|
||||
});
|
||||
|
||||
//region Helpers
|
||||
|
||||
/**
|
||||
* @returns {{
|
||||
* load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown,
|
||||
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals'),
|
||||
* perfLib: () => { mark: (name: string) => void }
|
||||
* load: (modules: string[], resultCallback: (result, configuration: import('../../../platform/windows/common/windows').INativeWindowConfiguration) => any, options: object) => unknown,
|
||||
* globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals')
|
||||
* }}
|
||||
*/
|
||||
function bootstrapWindowLib() {
|
||||
|
||||
Reference in New Issue
Block a user