@@ -24,7 +24,7 @@ import { ILogService } from 'vs/platform/log/common/log';
2424import { IStateService } from 'vs/platform/state/node/state' ;
2525import { IEnvironmentService } from 'vs/platform/environment/common/environment' ;
2626import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
27- import { IURLService , IOpenURLOptions } from 'vs/platform/url/common/url' ;
27+ import { IURLService } from 'vs/platform/url/common/url' ;
2828import { URLHandlerChannelClient , URLHandlerRouter } from 'vs/platform/url/common/urlIpc' ;
2929import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
3030import { NullTelemetryService , combinedAppender , LogAppender } from 'vs/platform/telemetry/common/telemetryUtils' ;
@@ -73,10 +73,10 @@ import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsSer
7373import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc' ;
7474import { IElectronMainService , ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService' ;
7575import { ISharedProcessMainService , SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService' ;
76- import { assign } from 'vs/base/common/objects' ;
7776import { IDialogMainService , DialogMainService } from 'vs/platform/dialogs/electron-main/dialogs' ;
7877import { withNullAsUndefined } from 'vs/base/common/types' ;
7978import { parseArgs , OPTIONS } from 'vs/platform/environment/node/argv' ;
79+ import { coalesce } from 'vs/base/common/arrays' ;
8080
8181export class CodeApplication extends Disposable {
8282
@@ -395,7 +395,7 @@ export class CodeApplication extends Disposable {
395395 const windows = appInstantiationService . invokeFunction ( accessor => this . openFirstWindow ( accessor , electronIpcServer , sharedProcessClient ) ) ;
396396
397397 // Post Open Windows Tasks
398- appInstantiationService . invokeFunction ( this . afterWindowOpen . bind ( this ) ) ;
398+ appInstantiationService . invokeFunction ( accessor => this . afterWindowOpen ( accessor ) ) ;
399399
400400 // Tracing: Stop tracing after windows are ready if enabled
401401 if ( this . environmentService . args . trace ) {
@@ -575,9 +575,8 @@ export class CodeApplication extends Disposable {
575575 electronIpcServer . registerChannel ( 'logger' , loggerChannel ) ;
576576 sharedProcessClient . then ( client => client . registerChannel ( 'logger' , loggerChannel ) ) ;
577577
578- const windowsMainService = this . windowsMainService = accessor . get ( IWindowsMainService ) ;
579-
580578 // ExtensionHost Debug broadcast service
579+ const windowsMainService = this . windowsMainService = accessor . get ( IWindowsMainService ) ;
581580 electronIpcServer . registerChannel ( ExtensionHostDebugBroadcastChannel . ChannelName , new ElectronExtensionHostDebugBroadcastChannel ( windowsMainService ) ) ;
582581
583582 // Signal phase: ready (services set)
@@ -586,47 +585,67 @@ export class CodeApplication extends Disposable {
586585 // Propagate to clients
587586 this . dialogMainService = accessor . get ( IDialogMainService ) ;
588587
589- // Create a URL handler to open file URIs in the active window
588+ // Check for initial URLs to handle from protocol link invocations
590589 const environmentService = accessor . get ( IEnvironmentService ) ;
590+ const pendingWindowOpenablesFromProtocolLinks : IWindowOpenable [ ] = [ ] ;
591+ const pendingProtocolLinksToHandle = coalesce ( [
592+
593+ // Windows/Linux: protocol handler invokes CLI with --open-url
594+ ...environmentService . args [ 'open-url' ] ? environmentService . args . _urls || [ ] : [ ] ,
595+
596+ // macOS: open-url events
597+ ...( ( < any > global ) . getOpenUrls ( ) || [ ] ) as string [ ]
598+ ] . map ( pendingUrlToHandle => {
599+ try {
600+ return URI . parse ( pendingUrlToHandle ) ;
601+ } catch ( error ) {
602+ return undefined ;
603+ }
604+ } ) ) . filter ( pendingUriToHandle => {
605+ // filter out any protocol link that wants to open as window so that
606+ // we open the right set of windows on startup and not restore the
607+ // previous workspace too.
608+ const windowOpenable = this . getWindowOpenableFromProtocolLink ( pendingUriToHandle ) ;
609+ if ( windowOpenable ) {
610+ pendingWindowOpenablesFromProtocolLinks . push ( windowOpenable ) ;
611+
612+ return false ;
613+ }
614+
615+ return true ;
616+ } ) ;
617+
618+ // Create a URL handler to open file URIs in the active window
619+ const app = this ;
591620 urlService . registerHandler ( {
592- async handleURL ( uri : URI , options ?: IOpenURLOptions ) : Promise < boolean > {
593-
594- // Catch file/remote URLs
595- if ( ( uri . authority === Schemas . file || uri . authority === Schemas . vscodeRemote ) && ! ! uri . path ) {
596- const cli = assign ( Object . create ( null ) , environmentService . args ) ;
597- const urisToOpen : IWindowOpenable [ ] = [ ] ;
598-
599- // File path
600- if ( uri . authority === Schemas . file ) {
601- // we configure as fileUri, but later validation will
602- // make sure to open as folder or workspace if possible
603- urisToOpen . push ( { fileUri : URI . file ( uri . fsPath ) } ) ;
604- }
621+ async handleURL ( uri : URI ) : Promise < boolean > {
622+
623+ // Check for URIs to open in window
624+ const windowOpenableFromProtocolLink = app . getWindowOpenableFromProtocolLink ( uri ) ;
625+ if ( windowOpenableFromProtocolLink ) {
626+ windowsMainService . open ( {
627+ context : OpenContext . API ,
628+ cli : { ...environmentService . args } ,
629+ urisToOpen : [ windowOpenableFromProtocolLink ] ,
630+ gotoLineMode : true
631+ } ) ;
605632
606- // Remote path
607- else {
608- // Example conversion:
609- // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco
610- // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco
611- const secondSlash = uri . path . indexOf ( posix . sep , 1 /* skip over the leading slash */ ) ;
612- if ( secondSlash !== - 1 ) {
613- const authority = uri . path . substring ( 1 , secondSlash ) ;
614- const path = uri . path . substring ( secondSlash ) ;
615- const remoteUri = URI . from ( { scheme : Schemas . vscodeRemote , authority, path, query : uri . query , fragment : uri . fragment } ) ;
616-
617- if ( hasWorkspaceFileExtension ( path ) ) {
618- urisToOpen . push ( { workspaceUri : remoteUri } ) ;
619- } else {
620- urisToOpen . push ( { folderUri : remoteUri } ) ;
621- }
622- }
623- }
633+ return true ;
634+ }
624635
625- if ( urisToOpen . length > 0 ) {
626- windowsMainService . open ( { context : OpenContext . API , cli, urisToOpen, gotoLineMode : true } ) ;
636+ // If we have not yet handled the URI and we have no window opened (macOS only)
637+ // we first open a window and then try to open that URI within that window
638+ if ( isMacintosh && windowsMainService . getWindowCount ( ) === 0 ) {
639+ const [ window ] = windowsMainService . open ( {
640+ context : OpenContext . API ,
641+ cli : { ...environmentService . args } ,
642+ forceEmpty : true ,
643+ gotoLineMode : true
644+ } ) ;
627645
628- return true ;
629- }
646+ await window . ready ( ) ;
647+
648+ return urlService . open ( uri ) ;
630649 }
631650
632651 return false ;
@@ -638,37 +657,13 @@ export class CodeApplication extends Disposable {
638657 const activeWindowRouter = new StaticRouter ( ctx => activeWindowManager . getActiveClientId ( ) . then ( id => ctx === id ) ) ;
639658 const urlHandlerRouter = new URLHandlerRouter ( activeWindowRouter ) ;
640659 const urlHandlerChannel = electronIpcServer . getChannel ( 'urlHandler' , urlHandlerRouter ) ;
641- const multiplexURLHandler = new URLHandlerChannelClient ( urlHandlerChannel ) ;
642-
643- // On Mac, Code can be running without any open windows, so we must create a window to handle urls,
644- // if there is none
645- if ( isMacintosh ) {
646- urlService . registerHandler ( {
647- async handleURL ( uri : URI , options ?: IOpenURLOptions ) : Promise < boolean > {
648- if ( windowsMainService . getWindowCount ( ) === 0 ) {
649- const cli = { ...environmentService . args } ;
650- const [ window ] = windowsMainService . open ( { context : OpenContext . API , cli, forceEmpty : true , gotoLineMode : true } ) ;
651-
652- await window . ready ( ) ;
653-
654- return urlService . open ( uri ) ;
655- }
656-
657- return false ;
658- }
659- } ) ;
660- }
661-
662- // Register the multiple URL handler
663- urlService . registerHandler ( multiplexURLHandler ) ;
660+ urlService . registerHandler ( new URLHandlerChannelClient ( urlHandlerChannel ) ) ;
664661
665662 // Watch Electron URLs and forward them to the UrlService
666- const args = this . environmentService . args ;
667- const urls = args [ 'open-url' ] ? args . _urls : [ ] ;
668- const urlListener = new ElectronURLListener ( urls || [ ] , urlService , windowsMainService , this . environmentService ) ;
669- this . _register ( urlListener ) ;
663+ this . _register ( new ElectronURLListener ( pendingProtocolLinksToHandle , urlService , windowsMainService , this . environmentService ) ) ;
670664
671665 // Open our first window
666+ const args = this . environmentService . args ;
672667 const macOpenFiles : string [ ] = ( < any > global ) . macOpenFiles ;
673668 const context = ! ! process . env [ 'VSCODE_CLI' ] ? OpenContext . CLI : OpenContext . DESKTOP ;
674669 const hasCliArgs = args . _ . length ;
@@ -677,6 +672,19 @@ export class CodeApplication extends Disposable {
677672 const noRecentEntry = args [ 'skip-add-to-recently-opened' ] === true ;
678673 const waitMarkerFileURI = args . wait && args . waitMarkerFilePath ? URI . file ( args . waitMarkerFilePath ) : undefined ;
679674
675+ // check for a pending window to open from URI
676+ // e.g. when running code with --open-uri from
677+ // a protocol handler
678+ if ( pendingWindowOpenablesFromProtocolLinks . length > 0 ) {
679+ return windowsMainService . open ( {
680+ context,
681+ cli : args ,
682+ urisToOpen : pendingWindowOpenablesFromProtocolLinks ,
683+ gotoLineMode : true ,
684+ initialStartup : true
685+ } ) ;
686+ }
687+
680688 // new window if "-n" or "--remote" was used without paths
681689 if ( ( args [ 'new-window' ] || args . remote ) && ! hasCliArgs && ! hasFolderURIs && ! hasFileURIs ) {
682690 return windowsMainService . open ( {
@@ -698,7 +706,6 @@ export class CodeApplication extends Disposable {
698706 urisToOpen : macOpenFiles . map ( file => this . getWindowOpenableFromPathSync ( file ) ) ,
699707 noRecentEntry,
700708 waitMarkerFileURI,
701- gotoLineMode : false ,
702709 initialStartup : true
703710 } ) ;
704711 }
@@ -716,6 +723,40 @@ export class CodeApplication extends Disposable {
716723 } ) ;
717724 }
718725
726+ private getWindowOpenableFromProtocolLink ( uri : URI ) : IWindowOpenable | undefined {
727+ if ( ! uri . path ) {
728+ return undefined ;
729+ }
730+
731+ // File path
732+ if ( uri . authority === Schemas . file ) {
733+ // we configure as fileUri, but later validation will
734+ // make sure to open as folder or workspace if possible
735+ return { fileUri : URI . file ( uri . fsPath ) } ;
736+ }
737+
738+ // Remote path
739+ else if ( uri . authority === Schemas . vscodeRemote ) {
740+ // Example conversion:
741+ // From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco
742+ // To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco
743+ const secondSlash = uri . path . indexOf ( posix . sep , 1 /* skip over the leading slash */ ) ;
744+ if ( secondSlash !== - 1 ) {
745+ const authority = uri . path . substring ( 1 , secondSlash ) ;
746+ const path = uri . path . substring ( secondSlash ) ;
747+ const remoteUri = URI . from ( { scheme : Schemas . vscodeRemote , authority, path, query : uri . query , fragment : uri . fragment } ) ;
748+
749+ if ( hasWorkspaceFileExtension ( path ) ) {
750+ return { workspaceUri : remoteUri } ;
751+ } else {
752+ return { folderUri : remoteUri } ;
753+ }
754+ }
755+ }
756+
757+ return undefined ;
758+ }
759+
719760 private getWindowOpenableFromPathSync ( path : string ) : IWindowOpenable {
720761 try {
721762 const fileStat = statSync ( path ) ;
@@ -734,6 +775,7 @@ export class CodeApplication extends Disposable {
734775 }
735776
736777 private afterWindowOpen ( accessor : ServicesAccessor ) : void {
778+
737779 // Signal phase: after window open
738780 this . lifecycleMainService . phase = LifecycleMainPhase . AfterWindowOpen ;
739781
@@ -763,7 +805,7 @@ class ElectronExtensionHostDebugBroadcastChannel<TContext> extends ExtensionHost
763805 super ( ) ;
764806 }
765807
766- call ( ctx : TContext , command : string , arg ?: any ) : Promise < any > {
808+ async call ( ctx : TContext , command : string , arg ?: any ) : Promise < any > {
767809 if ( command === 'openExtensionDevelopmentHostWindow' ) {
768810 const env = arg [ 1 ] ;
769811 const pargs = parseArgs ( arg [ 0 ] , OPTIONS ) ;
@@ -775,7 +817,6 @@ class ElectronExtensionHostDebugBroadcastChannel<TContext> extends ExtensionHost
775817 userEnv : Object . keys ( env ) . length > 0 ? env : undefined
776818 } ) ;
777819 }
778- return Promise . resolve ( ) ;
779820 } else {
780821 return super . call ( ctx , command , arg ) ;
781822 }
0 commit comments