From d574012871e58e503801f869fab5e3d3a4052390 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 12 Feb 2020 16:49:34 -0600 Subject: [PATCH] Fix duplicate files opening with folder parameter When using a query parameter without a scheme, the scheme defaults to `file`. This results in the files in the explorer being technically different from the file picker files because they are file:// instead of vscode-remote://, causing the same file to open twice and causing numerous issues. Normally the file explorer wouldn't even load at all in this case but we provide a file service for file:// URLs as a failsafe for certain files that wouldn't load correctly in the past. These files load fine now using the vscode-remote scheme, so I'm also removing that service. Related: #1351. Fixes #1294. --- scripts/vscode.patch | 50 ++++++++++++++++++++++++++++++++++++-------- src/node/server.ts | 29 +++++++++++++++---------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/scripts/vscode.patch b/scripts/vscode.patch index f301d4a7d..1f111ad82 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -76,6 +76,46 @@ index 2c64061da7..c0ef8faedd 100644 } catch (err) { // Do nothing. If we can't read the file we have no // language pack config. +diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts +index a599f5a7eb..ec7ccd43f8 100644 +--- a/src/vs/code/browser/workbench/workbench.ts ++++ b/src/vs/code/browser/workbench/workbench.ts +@@ -298,35 +298,6 @@ class WorkspaceProvider implements IWorkspaceProvider { + let workspace: IWorkspace; + let payload = Object.create(null); + +- const query = new URL(document.location.href).searchParams; +- query.forEach((value, key) => { +- switch (key) { +- +- // Folder +- case WorkspaceProvider.QUERY_PARAM_FOLDER: +- workspace = { folderUri: URI.parse(value) }; +- foundWorkspace = true; +- break; +- +- // Workspace +- case WorkspaceProvider.QUERY_PARAM_WORKSPACE: +- workspace = { workspaceUri: URI.parse(value) }; +- foundWorkspace = true; +- break; +- +- // Empty +- case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: +- workspace = undefined; +- foundWorkspace = true; +- break; +- +- // Payload +- case WorkspaceProvider.QUERY_PARAM_PAYLOAD: +- payload = JSON.parse(value); +- break; +- } +- }); +- + // If no workspace is provided through the URL, check for config attribute from server + if (!foundWorkspace) { + if (config.folderUri) { diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index abd1e33b18..bf75952ce1 100644 --- a/src/vs/platform/environment/common/environment.ts @@ -680,7 +720,7 @@ index 4781f22676..25143a97c0 100644 throw new Error(`Cannot load module '${request}'`); } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts -index 94e7052574..4219adda2c 100644 +index 94e7052574..7e5563b417 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -49,6 +49,7 @@ import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedD @@ -699,14 +739,6 @@ index 94e7052574..4219adda2c 100644 } private registerListeners(workbench: Workbench, storageService: BrowserStorageService): void { -@@ -245,6 +247,7 @@ class BrowserMain extends Disposable { - // Remote file system - const remoteFileSystemProvider = this._register(new RemoteFileSystemProvider(remoteAgentService)); - fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); -+ fileService.registerProvider(Schemas.file, remoteFileSystemProvider); - - if (!this.configuration.userDataProvider) { - const remoteUserDataUri = this.getRemoteUserDataUri(); diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index c509716fc4..e416413084 100644 --- a/src/vs/workbench/common/resources.ts diff --git a/src/node/server.ts b/src/node/server.ts index e3b10fb96..5db95f7a2 100644 --- a/src/node/server.ts +++ b/src/node/server.ts @@ -10,7 +10,6 @@ import * as tls from "tls"; import * as url from "url"; import * as util from "util"; import { Emitter } from "vs/base/common/event"; -import { sanitizeFilePath } from "vs/base/common/extpath"; import { Schemas } from "vs/base/common/network"; import { URI, UriComponents } from "vs/base/common/uri"; import { generateUuid } from "vs/base/common/uuid"; @@ -574,6 +573,7 @@ export class MainServer extends Server { } private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise { + const remoteAuthority = request.headers.host as string; const filePath = path.join(this.serverRoot, "browser/workbench.html"); let [content, startPath] = await Promise.all([ util.promisify(fs.readFile)(filePath, "utf8"), @@ -582,14 +582,14 @@ export class MainServer extends Server { { path: parsedUrl.query.folder, workspace: false }, (await this.readSettings()).lastVisited, { path: this.options.openUri } - ]), + ], remoteAuthority), this.servicesPromise, ]); if (startPath) { this.writeSettings({ lastVisited: { - path: startPath.uri.fsPath, + path: startPath.uri, workspace: startPath.workspace }, }); @@ -598,14 +598,13 @@ export class MainServer extends Server { const logger = this.services.get(ILogService) as ILogService; logger.info("request.url", `"${request.url}"`); - const remoteAuthority = request.headers.host as string; const transformer = getUriTransformer(remoteAuthority); const environment = this.services.get(IEnvironmentService) as IEnvironmentService; const options: Options = { WORKBENCH_WEB_CONFIGURATION: { - workspaceUri: startPath && startPath.workspace ? transformer.transformOutgoing(startPath.uri) : undefined, - folderUri: startPath && !startPath.workspace ? transformer.transformOutgoing(startPath.uri) : undefined, + workspaceUri: startPath && startPath.workspace ? URI.parse(startPath.uri) : undefined, + folderUri: startPath && !startPath.workspace ? URI.parse(startPath.uri) : undefined, remoteAuthority, logLevel: getLogLevel(environment), }, @@ -631,9 +630,8 @@ export class MainServer extends Server { * workspace or a directory are acceptable. Otherwise it must be a file if a * workspace or a directory otherwise. */ - private async getFirstValidPath(startPaths: Array): Promise<{ uri: URI, workspace?: boolean} | undefined> { + private async getFirstValidPath(startPaths: Array, remoteAuthority: string): Promise<{ uri: string, workspace?: boolean} | undefined> { const logger = this.services.get(ILogService) as ILogService; - const cwd = process.env.VSCODE_CWD || process.cwd(); for (let i = 0; i < startPaths.length; ++i) { const startPath = startPaths[i]; if (!startPath) { @@ -641,11 +639,20 @@ export class MainServer extends Server { } const paths = typeof startPath.path === "string" ? [startPath.path] : (startPath.path || []); for (let j = 0; j < paths.length; ++j) { - const uri = URI.file(sanitizeFilePath(paths[j], cwd)); + const uri = url.parse(paths[j]); try { - const stat = await util.promisify(fs.stat)(uri.fsPath); + if (!uri.pathname) { + throw new Error(`${paths[j]} is not valid`); + } + const stat = await util.promisify(fs.stat)(uri.pathname); if (typeof startPath.workspace === "undefined" || startPath.workspace !== stat.isDirectory()) { - return { uri, workspace: !stat.isDirectory() }; + return { uri: url.format({ + protocol: uri.protocol || "vscode-remote", + hostname: remoteAuthority.split(":")[0], + port: remoteAuthority.split(":")[1], + pathname: uri.pathname, + slashes: true, + }), workspace: !stat.isDirectory() }; } } catch (error) { logger.warn(error.message);