import { isStandalone } from "vs/base/browser/browser" ; import { streamToBuffer } from "vs/base/common/buffer" ; import { CancellationToken } from "vs/base/common/cancellation" ; import { Emitter , Event } from "vs/base/common/event" ; import { Disposable } from "vs/base/common/lifecycle" ; import { Schemas } from "vs/base/common/network" ; import { isEqual } from "vs/base/common/resources" ; import { URI , UriComponents } from "vs/base/common/uri" ; import { generateUuid } from "vs/base/common/uuid" ; import { request } from "vs/base/parts/request/browser/request" ; import { localize } from "vs/nls" ; import { parseLogLevel } from "vs/platform/log/common/log" ; import product from "vs/platform/product/common/product" ; import { isFolderToOpen , isWorkspaceToOpen , } from "vs/platform/windows/common/windows" ; import { create , ICredentialsProvider , IHomeIndicator , IProductQualityChangeHandler , ISettingsSyncOptions , IURLCallbackProvider , IWelcomeBanner , IWindowIndicator , IWorkbenchConstructionOptions , IWorkspace , IWorkspaceProvider , } from "vs/workbench/workbench.web.api" ; function doCreateUri ( path , queryValues ) { let query = undefined ; if ( queryValues ) { let index = 0 ; queryValues . forEach ( ( value , key ) => { if ( ! query ) { query = "" ; } const prefix = index ++ === 0 ? "" : "&" ; query += `${ prefix }${ key }=${ encodeURIComponent ( value ) }` ; } ) ; } return URI . parse ( window . location . href ) . with ( { path , query } ) ; } interface ICredential { service : string ; account : string ; password : string ; } class LocalStorageCredentialsProvider implements ICredentialsProvider { static readonly CREDENTIALS_OPENED_KEY = "credentials.provider" ; private readonly authService : string | undefined ; constructor ( ) { let authSessionInfo ; const authSessionElement = document . getElementById ( "vscode-workbench-auth-session" ) ; const authSessionElementAttribute = authSessionElement ? authSessionElement . getAttribute ( "data-settings" ) : undefined ; if ( authSessionElementAttribute ) { try { authSessionInfo = JSON . parse ( authSessionElementAttribute ) ; } catch ( error ) { } } if ( authSessionInfo ) { this . setPassword ( `${ product . urlProtocol }.login` , "account" , JSON . stringify ( authSessionInfo ) ) ; this . authService = `${ product . urlProtocol }-${ authSessionInfo . providerId }.login` ; this . setPassword ( this . authService , "account" , JSON . stringify ( authSessionInfo . scopes . map ( ( scopes ) => ( { id : authSessionInfo ! . id , scopes , accessToken : authSessionInfo ! . accessToken , } ) ) ) ) ; } } private _credentials : ICredential [ ] | undefined ; private get credentials ( ) : ICredential [ ] { if ( ! this . _credentials ) { try { const serializedCredentials = window . localStorage . getItem ( LocalStorageCredentialsProvider . CREDENTIALS_OPENED_KEY ) ; if ( serializedCredentials ) { this . _credentials = JSON . parse ( serializedCredentials ) ; } } catch ( error ) { } if ( ! Array . isArray ( this . _credentials ) ) { this . _credentials = [ ] ; } } return this . _credentials ; } private save ( ) { window . localStorage . setItem ( LocalStorageCredentialsProvider . CREDENTIALS_OPENED_KEY , JSON . stringify ( this . credentials ) ) ; } async getPassword ( service , account ) { return this . doGetPassword ( service , account ) ; } private async doGetPassword ( service , account ? ) { for ( const credential of this . credentials ) { if ( credential . service === service ) { if ( typeof account !== "string" || account === credential . account ) { return credential . password ; } } } return null ; } async setPassword ( service , account , password ) { this . doDeletePassword ( service , account ) ; this . credentials . push ( { service , account , password } ) ; this . save ( ) ; try { if ( password && service === this . authService ) { const value = JSON . parse ( password ) ; if ( Array . isArray ( value ) && value . length === 0 ) { await this . logout ( service ) ; } } } catch ( error ) { console . log ( error ) ; } } async deletePassword ( service , account ) { const result = await this . doDeletePassword ( service , account ) ; if ( result && service === this . authService ) { try { await this . logout ( service ) ; } catch ( error ) { console . log ( error ) ; } } return result ; } private async doDeletePassword ( service , account ) { let found = false ; this . _credentials = this . credentials . filter ( ( credential ) => { if ( credential . service === service && credential . account === account ) { found = true ; return false ; } return true ; } ) ; if ( found ) { this . save ( ) ; } return found ; } async findPassword ( service ) { return this . doGetPassword ( service ) ; } async findCredentials ( service ) { return this . credentials . filter ( ( credential ) => credential . service === service ) . map ( ( { account , password } ) => ( { account , password } ) ) ; } private async logout ( service ) { const queryValues = new Map ( ) ; queryValues . set ( "logout" , String ( true ) ) ; queryValues . set ( "service" , service ) ; await request ( { url : doCreateUri ( "/auth/logout" , queryValues ) . toString ( true ) , } , CancellationToken . None ) ; } async clear ( ) { window . localStorage . removeItem ( LocalStorageCredentialsProvider . CREDENTIALS_OPENED_KEY ) ; } } class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider { static readonly FETCH_INTERVAL = 500 ; static readonly FETCH_TIMEOUT = 5 * 60 * 1000 ; f ( ) { } static readonly QUERY_KEYS = { REQUEST_ID : "vscode-requestId" , SCHEME : "vscode-scheme" , AUTHORITY : "vscode-authority" , PATH : "vscode-path" , QUERY : "vscode-query" , FRAGMENT : "vscode-fragment" , } ; private readonly _onCallback = this . _register ( new Emitter < URI > ( ) ) ; readonly onCallback = this . _onCallback . event ; create ( options ? ) { const queryValues = new Map ( ) ; const requestId = generateUuid ( ) ; queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . REQUEST_ID , requestId ) ; const { scheme , authority , path , query , fragment } = options ? options : { scheme : undefined , authority : undefined , path : undefined , query : undefined , fragment : undefined , } ; if ( scheme ) { queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . SCHEME , scheme ) ; } if ( authority ) { queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . AUTHORITY , authority ) ; } if ( path ) { queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . PATH , path ) ; } if ( query ) { queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . QUERY , query ) ; } if ( fragment ) { queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . FRAGMENT , fragment ) ; } this . periodicFetchCallback ( requestId , Date . now ( ) ) ; return doCreateUri ( "/callback" , queryValues ) ; } private async periodicFetchCallback ( requestId , startTime ) { const queryValues = new Map ( ) ; queryValues . set ( PollingURLCallbackProvider . QUERY_KEYS . REQUEST_ID , requestId ) ; const result = await request ( { url : doCreateUri ( "/fetch-callback" , queryValues ) . toString ( true ) , } , CancellationToken . None ) ; const content = await streamToBuffer ( result . stream ) ; if ( content . byteLength > 0 ) { try { this . _onCallback . fire ( URI . revive ( JSON . parse ( content . toString ( ) ) ) ) ; } catch ( error ) { console . error ( error ) ; } return ; } if ( Date . now ( ) - startTime < PollingURLCallbackProvider . FETCH_TIMEOUT ) { setTimeout ( ( ) => this . periodicFetchCallback ( requestId , startTime ) , PollingURLCallbackProvider . FETCH_INTERVAL ) ; } } } class WorkspaceProvider implements IWorkspaceProvider { static QUERY_PARAM_EMPTY_WINDOW = "ew" ; static QUERY_PARAM_FOLDER = "folder" ; static QUERY_PARAM_WORKSPACE = "workspace" ; static QUERY_PARAM_PAYLOAD = "payload" ; readonly trusted = true ; constructor ( ) { } async open ( workspace , options ? ) { if ( options ?. reuse && ! options . payload && this . isSame ( this . workspace , workspace ) ) { return true ; } } async open ( workspace , options ? ) { if ( options ?. reuse && ! options . payload && this . isSame ( this . workspace , workspace ) ) { return true ; } const targetHref = this . createTargetUrl ( workspace , options ) ; if ( targetHref ) { if ( options ?. reuse ) { window . location . href = targetHref ; return true ; } else { let result ; if ( isStandalone ) { result = window . open ( targetHref , "_blank" , "toolbar=no" ) ; } else { result = window . open ( targetHref ) ; } return ! ! result ; } } return false ; } private createTargetUrl ( workspace , options ? ) { let targetHref = undefined ; if ( ! workspace ) { targetHref = `${ document . location . origin }${ document . location . pathname }?${ WorkspaceProvider . QUERY_PARAM_EMPTY_WINDOW }=true` ; } else if ( isFolderToOpen ( workspace ) ) { targetHref = `${ document . location . origin }${ document . location . pathname }?${ WorkspaceProvider . QUERY_PARAM_FOLDER }=${ encodeURIComponent ( workspace . folderUri . toString ( ) ) }` ; } else if ( isWorkspaceToOpen ( workspace ) ) { targetHref = `${ document . location . origin }${ document . location . pathname }?${ WorkspaceProvider . QUERY_PARAM_WORKSPACE }=${ encodeURIComponent ( workspace . workspaceUri . toString ( ) ) }` ; } if ( options ?. payload ) { targetHref += `&${ WorkspaceProvider . QUERY_PARAM_PAYLOAD }=${ encodeURIComponent ( JSON . stringify ( options . payload ) ) }` ; } return targetHref ; } private isSame ( workspaceA , workspaceB ) { if ( ! workspaceA || ! workspaceB ) { return workspaceA === workspaceB ; } if ( isFolderToOpen ( workspaceA ) && isFolderToOpen ( workspaceB ) ) { return isEqual ( workspaceA . folderUri , workspaceB . folderUri ) ; } if ( isWorkspaceToOpen ( workspaceA ) && isWorkspaceToOpen ( workspaceB ) ) { return isEqual ( workspaceA . workspaceUri , workspaceB . workspaceUri ) ; } return false ; } hasRemote ( ) { if ( this . workspace ) { if ( isFolderToOpen ( this . workspace ) ) { return this . workspace . folderUri . scheme === Schemas . vscodeRemote ; } if ( isWorkspaceToOpen ( this . workspace ) ) { return this . workspace . workspaceUri . scheme === Schemas . vscodeRemote ; } } return true ; } } class WindowIndicator implements IWindowIndicator { readonly onDidChange = Event . None ; readonly label : string ; readonly tooltip : string ; readonly command : string | undefined ; constructor ( workspace ) { let repositoryOwner = undefined ; let repositoryName = undefined ; if ( workspace ) { let uri = undefined ; if ( isFolderToOpen ( workspace ) ) { uri = workspace . folderUri ; } else if ( isWorkspaceToOpen ( workspace ) ) { uri = workspace . workspaceUri ; } if ( uri ?. scheme === "github" || uri ?. scheme === "codespace" ) { [ repositoryOwner , repositoryName ] = uri . authority . split ( "+" ) ; } } if ( repositoryName && repositoryOwner ) { this . label = localize ( "playgroundLabelRepository" , "$(remote) Visual Studio Code Playground: {0}/{1}" , repositoryOwner , repositoryName ) ; this . tooltip = localize ( "playgroundRepositoryTooltip" , "Visual Studio Code Playground: {0}/{1}" , repositoryOwner , repositoryName ) ; } else { this . label = localize ( "playgroundLabel" , "$(remote) Visual Studio Code Playground" ) ; this . tooltip = localize ( "playgroundTooltip" , "Visual Studio Code Playground" ) ; } } } ( function ( ) { const configElement = document . getElementById ( "vscode-workbench-web-configuration" ) ; const configElementAttribute = configElement ? configElement . getAttribute ( "data-settings" ) : undefined ; if ( ! configElement || ! configElementAttribute ) { throw new Error ( "Missing web configuration element" ) ; } const config = JSON . parse ( configElementAttribute ) ; let foundWorkspace = false ; let workspace ; let payload = Object . create ( null ) ; let logLevel = undefined ; const query = new URL ( document . location . href ) . searchParams ; query . forEach ( ( value , key ) => { switch ( key ) { case WorkspaceProvider . QUERY_PARAM_FOLDER : workspace = { folderUri : URI . parse ( value ) } ; foundWorkspace = true ; break ; case WorkspaceProvider . QUERY_PARAM_WORKSPACE : workspace = { workspaceUri : URI . parse ( value ) } ; foundWorkspace = true ; break ; case WorkspaceProvider . QUERY_PARAM_EMPTY_WINDOW : workspace = undefined ; foundWorkspace = true ; break ; case WorkspaceProvider . QUERY_PARAM_PAYLOAD : try { payload = JSON . parse ( value ) ; } catch ( error ) { console . error ( error ) ; } break ; case "logLevel" : logLevel = value ; break ; } } ) ; if ( ! foundWorkspace ) { if ( config . folderUri ) { workspace = { folderUri : URI . revive ( config . folderUri ) } ; } else if ( config . workspaceUri ) { workspace = { workspaceUri : URI . revive ( config . workspaceUri ) } ; } else { workspace = undefined ; } } const workspaceProvider = new WorkspaceProvider ( workspace , payload ) ; const homeIndicator = { href : "https://github.com/microsoft/vscode" , icon : "code" , title : localize ( "home" , "Home" ) , } ; const welcomeBanner = { message : localize ( "welcomeBannerMessage" , "{0} Web. Browser based playground for testing." , product . nameShort ) , actions : [ { href : "https://github.com/microsoft/vscode" , label : localize ( "learnMore" , "Learn More" ) , } , ] , } ; let windowIndicator = undefined ; if ( ! workspaceProvider . hasRemote ( ) ) { windowIndicator = new WindowIndicator ( workspace ) ; } const productQualityChangeHandler = ( quality ) => { let queryString = `quality=${ quality }` ; const query = new URL ( document . location . href ) . searchParams ; query . forEach ( ( value , key ) => { if ( key !== "quality" ) { queryString += `&${ key }=${ value }` ; } } ) ; window . location . href = `${ window . location . origin }?${ queryString }` ; } ; const settingsSyncOptions = config . settingsSyncOptions ? { enabled : config . settingsSyncOptions . enabled , } : undefined ; create ( document . body , { ... config , developmentOptions : { logLevel : logLevel ? parseLogLevel ( logLevel ) : undefined , ... config . developmentOptions , } , settingsSyncOptions , homeIndicator , windowIndicator , welcomeBanner , productQualityChangeHandler , workspaceProvider , urlCallbackProvider : new PollingURLCallbackProvider ( ) , credentialsProvider : new LocalStorageCredentialsProvider ( ) , } ) ; } ) ( ) ;