import { OPEN_EXTERNALLY } from '../utils/constants' ; import * as vscode from 'vscode' ; import { Disposable } from '../utils/dispose' ; import { PathUtil } from '../utils/pathUtil' ; import TelemetryReporter from 'vscode-extension-telemetry' ; import { WorkspaceManager } from '../infoManagers/workspaceManager' ; import { ConnectionManager } from '../infoManagers/connectionManager' ; import { WebviewComm } from './webviewComm' ; import { FormatDateTime } from '../utils/utils' ; import { SETTINGS_SECTION_ID , SettingUtil } from '../utils/settingsUtil' ; import * as path from 'path' ; description export class BrowserPreview extends Disposable { public static readonly viewType = 'browserPreview' ; private readonly _webviewComm : WebviewComm ; private readonly _onDisposeEmitter = this . _register ( new vscode . EventEmitter < void > ( ) ) ; public readonly onDispose = this . _onDisposeEmitter . event ; description public close ( ) { this . _panel . dispose ( ) ; } param column param file public reveal ( column , file = '/' ) { this . _webviewComm . goToFile ( file ) ; this . _panel . reveal ( column ) ; } constructor ( initialFile , private readonly _panel , private readonly _extensionUri , private readonly _reporter , private readonly _workspaceManager , private readonly _connectionManager , private readonly _outputChannel ) { super ( ) ; this . _panel . iconPath = { light : vscode . Uri . joinPath ( this . _extensionUri , 'media' , 'preview-light.svg' ) , dark : vscode . Uri . joinPath ( this . _extensionUri , 'media' , 'preview-dark.svg' ) , } ; this . _webviewComm = this . _register ( new WebviewComm ( initialFile , _panel , _extensionUri , _connectionManager ) ) ; this . _register ( this . _panel . onDidDispose ( ( ) => { this . dispose ( ) ; } ) ) ; this . _register ( this . _connectionManager . onConnected ( ( e ) => { this . reloadWebview ( ) ; } ) ) ; this . _register ( this . _webviewComm . onPanelTitleChange ( ( e ) => { this . setPanelTitle ( e . title , e . pathname ) ; } ) ) ; this . _register ( this . _panel . webview . onDidReceiveMessage ( ( message ) => this . handleWebviewMessage ( message ) ) ) ; } description param { any } message private handleWebviewMessage ( message ) { switch ( message . command ) { case 'alert' : vscode . window . showErrorMessage ( message . text ) ; return ; case 'update-path' : { const msgJSON = JSON . parse ( message . text ) ; this . _webviewComm . handleNewPageLoad ( msgJSON . path . pathname , msgJSON . title ) ; return ; } case 'go-back' : this . _webviewComm . goBack ( ) ; return ; case 'go-forward' : this . _webviewComm . goForwards ( ) ; return ; case 'open-browser' : this . handleOpenBrowser ( message . text ) ; return ; case 'add-history' : { this . _webviewComm . setUrlBar ( message . text ) ; return ; } case 'refresh-back-forward-buttons' : this . _webviewComm . updateForwardBackArrows ( ) ; return ; case 'go-to-file' : this . goToFullAddress ( message . text ) ; return ; case 'console' : { const msgJSON = JSON . parse ( message . text ) ; this . handleConsole ( msgJSON . type , msgJSON . data ) ; return ; } case 'devtools-open' : vscode . commands . executeCommand ( 'workbench.action.webview.openDeveloperTools' ) ; return ; } } description param { string } type param { string } log private handleConsole ( type , log ) { if ( type == 'CLEAR' ) { this . _outputChannel . clear ( ) ; } else { const date = new Date ( ) ; this . _outputChannel . appendLine ( `[${ type } - ${ FormatDateTime ( date , ' ' ) }] ${ log }` ) ; } } dispose ( ) { this . _onDisposeEmitter . fire ( ) ; this . _panel . dispose ( ) ; super . dispose ( ) ; } public get panel ( ) : vscode . WebviewPanel { return this . _panel ; } description private reloadWebview ( ) { this . _webviewComm . goToFile ( this . _webviewComm . currentAddress , false ) ; } param { string } givenURL private async handleOpenBrowser ( givenURL ) { if ( givenURL == '' ) { const givenURI = await this . _webviewComm . constructAddress ( this . _webviewComm . currentAddress ) ; const uri = vscode . Uri . parse ( givenURI . toString ( ) ) ; vscode . commands . executeCommand ( `${ SETTINGS_SECTION_ID }.start.${ SettingUtil . GetExternalPreviewType ( this . _extensionUri ) }.atFile` , uri , false ) ; } else { const uri = vscode . Uri . parse ( givenURL ) ; vscode . window . showInformationMessage ( `Externally hosted links are not supported in the embedded preview. Do you want to open ${ givenURL } in an external browser?` , { modal : true } , OPEN_EXTERNALLY ) . then ( ( selection ) => { if ( selection ) { if ( selection === OPEN_EXTERNALLY ) { vscode . env . openExternal ( uri ) ; } } } ) ; } this . _reporter . sendTelemetryEvent ( 'preview.openExternalBrowser' ) ; this . _webviewComm . goToFile ( this . _webviewComm . currentAddress , false ) ; this . _webviewComm . updateForwardBackArrows ( ) ; } param { string } address public async goToFullAddress ( address ) { const host = await this . _webviewComm . resolveHost ( ) ; let hostString = host . toString ( ) ; if ( hostString . endsWith ( '/' ) ) { hostString = hostString . substr ( 0 , hostString . length - 1 ) ; } if ( address . startsWith ( hostString ) ) { const file = address . substr ( host . toString ( ) . length ) ; this . _webviewComm . goToFile ( file ) ; } else { this . handleOpenBrowser ( address ) ; } } param { string } title param { string } pathname private setPanelTitle ( title = '' , pathname = 'Preview' ) { if ( title == '' ) { pathname = unescape ( pathname ) ; if ( pathname . length > 0 && pathname [ 0 ] == '/' ) { if ( this . _workspaceManager . pathExistsRelativeToDefaultWorkspace ( pathname ) ) { this . _panel . title = PathUtil . GetFileName ( pathname ) ; } else { this . _panel . title = path . basename ( pathname . substr ( 1 ) ) ; } } else { this . _panel . title = pathname ; } } else { this . _panel . title = title ; } } }