protocol links - add more logging (#171053)

* protocol links - add more logging

* more logging
This commit is contained in:
Benjamin Pasero 2023-01-11 13:10:26 +01:00 committed by GitHub
parent 2dcfd9dc83
commit 545e47e7cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 28 deletions

View file

@ -821,24 +821,32 @@ export class CodeApplication extends Disposable {
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
const urlService = accessor.get(IURLService);
const nativeHostMainService = accessor.get(INativeHostMainService);
const logService = this.logService;
// Signal phase: ready (services set)
this.lifecycleMainService.phase = LifecycleMainPhase.Ready;
// Check for initial URLs to handle from protocol link invocations
const protocolLinksFromCommandLine = this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : []; // Windows/Linux: protocol handler invokes CLI with --open-url
if (protocolLinksFromCommandLine.length) {
logService.trace('app#openFirstWindow() protocol links from command line:', protocolLinksFromCommandLine);
}
const protocolLinksFromEvent = ((<any>global).getOpenUrls() || []) as string[]; // macOS: open-url events
if (protocolLinksFromEvent.length) {
logService.trace(`app#openFirstWindow() protocol links from macOS 'open-url' event:`, protocolLinksFromEvent);
}
const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = [];
const pendingProtocolLinksToHandle = [
// Windows/Linux: protocol handler invokes CLI with --open-url
...this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : [],
// macOS: open-url events
...((<any>global).getOpenUrls() || []) as string[]
...protocolLinksFromCommandLine,
...protocolLinksFromEvent
].map(url => {
try {
return { uri: URI.parse(url), url };
} catch {
logService.trace('app#openFirstWindow() protocol link failed to parse:', url);
return undefined;
}
}).filter((obj): obj is { uri: URI; url: string } => {
@ -848,6 +856,8 @@ export class CodeApplication extends Disposable {
// If URI should be blocked, filter it out
if (this.shouldBlockURI(obj.uri)) {
logService.trace('app#openFirstWindow() protocol link was blocked:', obj.uri.toString(true));
return false;
}
@ -856,11 +866,15 @@ export class CodeApplication extends Disposable {
// previous workspace too.
const windowOpenable = this.getWindowOpenableFromProtocolLink(obj.uri);
if (windowOpenable) {
logService.trace('app#openFirstWindow() protocol link will be handled as window to open:', obj.uri.toString(true), windowOpenable);
pendingWindowOpenablesFromProtocolLinks.push(windowOpenable);
return false;
}
logService.trace('app#openFirstWindow() protocol link will be passed to active window for handling:', obj.uri.toString(true));
return true;
});
@ -870,11 +884,11 @@ export class CodeApplication extends Disposable {
const app = this;
const environmentService = this.environmentMainService;
const productService = this.productService;
const logService = this.logService;
urlService.registerHandler({
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
logService.trace('app#handleURL: ', uri.toString(true), options);
logService.trace('app#handleURL():', uri.toString(true), options);
// Support 'workspace' URLs (https://github.com/microsoft/vscode/issues/124263)
if (uri.scheme === productService.urlProtocol && uri.path === 'workspace') {
uri = uri.with({
authority: 'file',
@ -885,6 +899,8 @@ export class CodeApplication extends Disposable {
// If URI should be blocked, behave as if it's handled
if (app.shouldBlockURI(uri)) {
logService.trace('app#handleURL() protocol link was blocked:', uri.toString(true));
return true;
}
@ -893,26 +909,37 @@ export class CodeApplication extends Disposable {
// We should handle the URI in a new window if the URL contains `windowId=_blank`
const params = new URLSearchParams(uri.query);
if (params.get('windowId') === '_blank') {
logService.trace(`app#handleURL() found 'windowId=_blank' as parameter, setting shouldOpenInNewWindow=true:`, uri.toString(true));
params.delete('windowId');
uri = uri.with({ query: params.toString() });
shouldOpenInNewWindow = true;
}
// or if no window is open (macOS only)
shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0;
else if (isMacintosh && windowsMainService.getWindowCount() === 0) {
logService.trace(`app#handleURL() running on macOS with no window open, setting shouldOpenInNewWindow=true:`, uri.toString(true));
shouldOpenInNewWindow = true;
}
// Pass along whether the application is being opened via a Continue On flow
const continueOn = params.get('continueOn');
if (continueOn !== null) {
environmentService.continueOn = continueOn ?? undefined;
logService.trace(`app#handleURL() found 'continueOn' as parameter:`, uri.toString(true));
params.delete('continueOn');
uri = uri.with({ query: params.toString() });
environmentService.continueOn = continueOn ?? undefined;
}
// Check for URIs to open in window
// Check if the protocol link is a window openable to open...
const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri);
logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink);
if (windowOpenableFromProtocolLink) {
logService.trace('app#handleURL() opening protocol link as window:', windowOpenableFromProtocolLink, uri.toString(true));
const [window] = await windowsMainService.open({
context: OpenContext.API,
cli: { ...environmentService.args },
@ -927,7 +954,10 @@ export class CodeApplication extends Disposable {
return true;
}
// ...or if we should open in a new window and then handle it within that window
if (shouldOpenInNewWindow) {
logService.trace('app#handleURL() opening empty window and passing in protocol link:', uri.toString(true));
const [window] = await windowsMainService.open({
context: OpenContext.API,
cli: { ...environmentService.args },
@ -958,7 +988,7 @@ export class CodeApplication extends Disposable {
urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel));
// Watch Electron URLs and forward them to the UrlService
this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentMainService, this.productService));
this._register(new ElectronURLListener(pendingProtocolLinksToHandle, urlService, windowsMainService, this.environmentMainService, this.productService, this.logService));
// Open our first window
const args = this.environmentMainService.args;

View file

@ -10,18 +10,11 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IURLService } from 'vs/platform/url/common/url';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
function uriFromRawUrl(url: string): URI | null {
try {
return URI.parse(url);
} catch (e) {
return null;
}
}
/**
* A listener for URLs that are opened from the OS and handled by VSCode.
* Depending on the platform, this works differently:
@ -37,15 +30,17 @@ export class ElectronURLListener {
private uris: { uri: URI; url: string }[] = [];
private retryCount = 0;
private flushDisposable: IDisposable = Disposable.None;
private disposables = new DisposableStore();
private readonly disposables = new DisposableStore();
constructor(
initialUrisToHandle: { uri: URI; url: string }[],
private readonly urlService: IURLService,
windowsMainService: IWindowsMainService,
environmentMainService: IEnvironmentMainService,
productService: IProductService
productService: IProductService,
private readonly logService: ILogService
) {
logService.trace('ElectronURLListener initialUrisToHandle:', initialUrisToHandle.map(initialUri => initialUri.url));
// the initial set of URIs we need to handle once the window is ready
this.uris = initialUrisToHandle;
@ -62,12 +57,12 @@ export class ElectronURLListener {
Event.fromNodeEventEmitter(app, 'open-url', (event: ElectronEvent, url: string) => ({ event, url })),
({ event, url }) => {
event.preventDefault(); // always prevent default and return the url as string
return url;
});
this.disposables.add(onOpenElectronUrl(url => {
const uri = uriFromRawUrl(url);
const uri = this.uriFromRawUrl(url);
if (!uri) {
return;
}
@ -77,27 +72,46 @@ export class ElectronURLListener {
// Send initial links to the window once it has loaded
const isWindowReady = windowsMainService.getWindows()
.filter(w => w.isReady)
.filter(window => window.isReady)
.length > 0;
if (isWindowReady) {
logService.trace('ElectronURLListener: window is ready to handle URLs');
this.flush();
} else {
logService.trace('ElectronURLListener: waiting for window to be ready to handle URLs...');
Event.once(windowsMainService.onDidSignalReadyWindow)(this.flush, this, this.disposables);
}
}
private uriFromRawUrl(url: string): URI | undefined {
try {
return URI.parse(url);
} catch (e) {
return undefined;
}
}
private async flush(): Promise<void> {
if (this.retryCount++ > 10) {
this.logService.trace('ElectronURLListener#flush(): giving up after 10 retries');
return;
}
this.logService.trace('ElectronURLListener#flush(): flushing URLs');
const uris: { uri: URI; url: string }[] = [];
for (const obj of this.uris) {
const handled = await this.urlService.open(obj.uri, { originalUrl: obj.url });
if (handled) {
this.logService.trace('ElectronURLListener#flush(): URL was handled', obj.url);
} else {
this.logService.trace('ElectronURLListener#flush(): URL was not yet handled', obj.url);
if (!handled) {
uris.push(obj);
}
}