Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions src/vs/platform/protocol/electron-main/protocolMainService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { session } from 'electron';
import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
import { COI, FileAccess, Schemas, CacheControlheaders, DocumentPolicyheaders } from '../../../base/common/network.js';
import { basename, extname, normalize } from '../../../base/common/path.js';
import { isLinux } from '../../../base/common/platform.js';
import { isLinux, isWindows } from '../../../base/common/platform.js';
import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
import { URI } from '../../../base/common/uri.js';
import { generateUuid } from '../../../base/common/uuid.js';
Expand All @@ -19,6 +19,48 @@ import { IUserDataProfilesService } from '../../userDataProfile/common/userDataP

type ProtocolCallback = { (result: string | Electron.FilePathWithHeaders | { error: number }): void };

/**
* On Windows, the default Win32 file APIs enforce a `MAX_PATH` limit of 259
* characters. When a resource path exceeds this limit, Electron's
* `registerFileProtocol` handler fails to load the file (see
* https://github.com/microsoft/vscode/issues/261880 and
* https://github.com/electron/electron/issues/49101).
*
* Prefixing an absolute path with `\\?\` (or `\\?\UNC\` for UNC paths)
* opts the Win32 APIs into extended-length path mode which bypasses
* `MAX_PATH`.
*/
const WINDOWS_LONG_PATH_THRESHOLD = 248; // conservative: Win32 directory limit is 248 while file limit is 259
const WINDOWS_EXTENDED_PATH_PREFIX = '\\\\?\\';
const WINDOWS_EXTENDED_UNC_PREFIX = '\\\\?\\UNC\\';

function toWindowsLongPathIfNeeded(path: string): string {
if (!isWindows) {
return path;
}

if (path.length < WINDOWS_LONG_PATH_THRESHOLD) {
return path;
}

// Already using the extended-length prefix
if (path.startsWith(WINDOWS_EXTENDED_PATH_PREFIX)) {
return path;
}

// UNC path: \\server\share\... -> \\?\UNC\server\share\...
if (path.length >= 2 && path.charCodeAt(0) === 92 /* \ */ && path.charCodeAt(1) === 92 /* \ */) {
return WINDOWS_EXTENDED_UNC_PREFIX + path.substring(2);
}

// Drive-letter absolute path: C:\... -> \\?\C:\...
if (path.length >= 3 && path.charCodeAt(1) === 58 /* : */) {
return WINDOWS_EXTENDED_PATH_PREFIX + path;
}

return path;
}

export class ProtocolMainService extends Disposable implements IProtocolMainService {

declare readonly _serviceBrand: undefined;
Expand Down Expand Up @@ -123,14 +165,20 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ
};
}

// On Windows, paths longer than MAX_PATH (259) cause Electron's file
// protocol handler to fail. Apply the `\\?\` extended-length prefix so
// Win32 APIs bypass the limit (e.g. markdown image hover previews for
// deeply nested files). See https://github.com/microsoft/vscode/issues/261880.
const resolvedPath = toWindowsLongPathIfNeeded(path);

// first check by validRoots
if (this.validRoots.findSubstr(path)) {
return callback({ path, headers });
return callback({ path: resolvedPath, headers });
}

// then check by validExtensions
if (this.validExtensions.has(extname(path).toLowerCase())) {
return callback({ path, headers });
return callback({ path: resolvedPath, headers });
}

// finally block to load the resource
Expand Down