import { Disposable } from '../utils/dispose' ; import * as vscode from 'vscode' ; import { ConnectionManager } from '../infoManagers/connectionManager' ; import { INIT_PANEL_TITLE } from '../utils/constants' ; import { NavEditCommands , PageHistory } from './pageHistoryTracker' ; import { getNonce , isFileInjectable } from '../utils/utils' ; import { PathUtil } from '../utils/pathUtil' ; description export class WebviewComm extends Disposable { private readonly _pageHistory : PageHistory ; public currentAddress : string ; private readonly _onPanelTitleChange = this . _register ( new vscode . EventEmitter < { title ? : string ; pathname ? : string ; } > ( ) ) ; public readonly onPanelTitleChange = this . _onPanelTitleChange . event ; constructor ( initialFile , private readonly _panel , private readonly _extensionUri , private readonly _connectionManager ) { super ( ) ; this . _pageHistory = this . _register ( new PageHistory ( ) ) ; this . updateForwardBackArrows ( ) ; this . goToFile ( initialFile , false ) ; this . _pageHistory ?. addHistory ( initialFile ) ; this . currentAddress = initialFile ; } returns { Promise < vscode . Uri > } public async resolveHost ( ) { return this . _connectionManager . resolveExternalHTTPUri ( ) ; } returns { Promise < vscode . Uri > } private async resolveWsHost ( ) { return this . _connectionManager . resolveExternalWSUri ( ) ; } param { string } URLExt param { string } hostURI returns { Promise < string > } public async constructAddress ( URLExt , hostURI ? ) { if ( URLExt . length > 0 && URLExt [ 0 ] == '/' ) { URLExt = URLExt . substring ( 1 ) ; } URLExt = PathUtil . ConvertToUnixPath ( URLExt ) ; URLExt = URLExt . startsWith ( '/' ) ? URLExt . substr ( 1 ) : URLExt ; if ( ! hostURI ) { hostURI = await this . resolveHost ( ) ; } return `${ hostURI . toString ( ) }${ URLExt }` ; } description param { string } URLExt param { boolean } updateHistory public async goToFile ( URLExt , updateHistory = true ) { this . setHtml ( this . _panel . webview , URLExt , updateHistory ) ; this . currentAddress = URLExt ; } description param { vscode . Webview } webview param { string } URLExt param { boolean } updateHistory public async setHtml ( webview , URLExt , updateHistory ) { const httpHost = await this . resolveHost ( ) ; const url = await this . constructAddress ( URLExt , httpHost ) ; const wsURI = await this . resolveWsHost ( ) ; this . _panel . webview . html = this . getHtmlForWebview ( webview , url , `ws://${ wsURI . authority }${ wsURI . path }` , `${ httpHost . scheme }://${ httpHost . authority }` ) ; if ( ! isFileInjectable ( URLExt ) ) { this . _onPanelTitleChange . fire ( { title : '' , pathname : URLExt } ) ; this . _panel . webview . postMessage ( { command : 'set-url' , text : JSON . stringify ( { fullPath : url , pathname : URLExt } ) , } ) ; } if ( updateHistory ) { this . handleNewPageLoad ( URLExt ) ; } } description param { vscode . Webview } webview param { string } httpURL param { string } wsServerAddr param { string } httpServerAddr returns { string } private getHtmlForWebview ( webview , httpURL , wsServerAddr , httpServerAddr ) { const scriptPathOnDisk = vscode . Uri . joinPath ( this . _extensionUri , 'media' , 'main.js' ) ; const scriptUri = webview . asWebviewUri ( scriptPathOnDisk ) ; const stylesPathMainPath = vscode . Uri . joinPath ( this . _extensionUri , 'media' , 'vscode.css' ) ; const codiconsPathMainPath = vscode . Uri . joinPath ( this . _extensionUri , 'media' , 'codicon.css' ) ; const stylesMainUri = webview . asWebviewUri ( stylesPathMainPath ) ; const codiconsUri = webview . asWebviewUri ( codiconsPathMainPath ) ; const nonce = getNonce ( ) ; return `<!DOCTYPE html>
		<html lang="en">
			<head>
				<meta http-equiv="Content-type" content="text/html;charset=UTF-8">

				<!--
					Use a content security policy to only allow loading images from https or from our extension directory,
					and only allow scripts that have a specific nonce.
				-->
				<meta http-equiv="Content-Security-Policy" content="
					default-src 'none';
					connect-src ${ wsServerAddr };
					font-src ${ this . _panel . webview . cspSource };
					style-src ${ this . _panel . webview . cspSource };
					script-src 'nonce-${ nonce }';
					frame-src ${ httpServerAddr };
				">
				<meta name="viewport" content="width=device-width, initial-scale=1.0">

				<link href="${ stylesMainUri }" rel="stylesheet">
				<link rel="stylesheet" type="text/css" href="${ codiconsUri }">

				<title>${ INIT_PANEL_TITLE }</title>
			</head>
			<body>
			<div class="displayContents">
				<div class="header">
					<div class="headercontent">
						<nav class="controls">
							<button
								id="back"
								title="Back"
								class="back-button icon leftmost-nav"><i class="codicon codicon-arrow-left"></i></button>
							<button
								id="forward"
								title="Forward"
								class="forward-button icon leftmost-nav"><i class="codicon codicon-arrow-right"></i></button>
							<button
								id="reload"
								title="Reload"
								class="reload-button icon leftmost-nav"><i class="codicon codicon-refresh"></i></button>
							<input 
								id="url-input"
								class="url-input" 
								type="text">
							<button
								id="more"
								title="More Browser Actions"
								class="more-button icon"><i class="codicon codicon-list-flat"></i></button>
						</nav>
						<div class="find-container" id="find-box" hidden=true>
							<nav class="find">
								<input 
									id="find-input"
									class="find-input" 
									type="text">
								<div
									id="find-result"
									class="find-result icon" hidden=true><i id="find-result-icon" class="codicon" ></i></div>
								<button
									id="find-prev"
									title="Previous"
									class="find-prev-button icon find-nav"><i class="codicon codicon-chevron-up"></i></button>
								<button
									id="find-next"
									tabIndex=-1
									title="Next"
									class="find-next-button icon find-nav"><i class="codicon codicon-chevron-down"></i></button>
								<button
									id="find-x"
									tabIndex=-1
									title="Close"
									class="find-x-button icon find-nav"><i class="codicon codicon-chrome-close"></i></button>
							</nav>
						</div>
					</div>
					<div class="extras-menu" id="extras-menu-pane" hidden=true;>
						<table cellspacing="0" cellpadding="0">
							<tr>
								<td>
									<button tabIndex=-1 
										id="browser-open" class="extra-menu-nav">Open in Browser</button>
								</td>
							</tr>
							<tr>
								<td>
									<button tabIndex=-1 
										id="find" class="extra-menu-nav">Find in Page</button>
								</td>
							</tr>
							<tr>
								<td>
									<button tabIndex=-1 
										id="devtools-open" class="extra-menu-nav">Open Devtools Pane</button>
								</td>
							</tr>
						</table>
					</div>
				</div>
				<div class="content">
					<iframe id="hostedContent" src="${ httpURL }"></iframe>
				</div>
			</div>
			<div id="link-preview"></div>
				<script nonce="${ nonce }">
					const WS_URL= "${ wsServerAddr }";
				</script>
				<script nonce="${ nonce }" src="${ scriptUri }"></script>
			</body>
		</html>` ; } description param { string } pathname public async setUrlBar ( pathname ) { this . _panel . webview . postMessage ( { command : 'set-url' , text : JSON . stringify ( { fullPath : await this . constructAddress ( pathname ) , pathname : pathname , } ) , } ) ; this . handleNewPageLoad ( pathname ) ; } param { NavEditCommands } command public handleNavAction ( command ) { let text = { } ; switch ( command ) { case NavEditCommands . DISABLE_BACK : text = { element : 'back' , disabled : true } ; break ; case NavEditCommands . ENABLE_BACK : text = { element : 'back' , disabled : false } ; break ; case NavEditCommands . DISABLE_FORWARD : text = { element : 'forward' , disabled : true } ; break ; case NavEditCommands . ENABLE_FORWARD : text = { element : 'forward' , disabled : false } ; break ; } this . _panel . webview . postMessage ( { command : 'changed-history' , text : JSON . stringify ( text ) , } ) ; } description param { string } pathname param { string } panelTitle public handleNewPageLoad ( pathname , panelTitle = '' ) { if ( pathname . length > 0 && pathname [ 0 ] != '/' ) { pathname = '/' + pathname ; } this . _onPanelTitleChange . fire ( { title : panelTitle , pathname : pathname } ) ; this . currentAddress = pathname ; const response = this . _pageHistory ?. addHistory ( pathname ) ; if ( response ) { for ( const i in response . actions ) { this . handleNavAction ( response . actions [ i ] ) ; } } } description public updateForwardBackArrows ( ) { const navigationStatus = this . _pageHistory . currentCommands ; for ( const i in navigationStatus ) { this . handleNavAction ( navigationStatus [ i ] ) ; } } description public goForwards ( ) { const response = this . _pageHistory . goForward ( ) ; const pagename = response . address ; if ( pagename != undefined ) { this . goToFile ( pagename , false ) ; } for ( const i in response . actions ) { this . handleNavAction ( response . actions [ i ] ) ; } } description public goBack ( ) { const response = this . _pageHistory . goBackward ( ) ; const pagename = response . address ; if ( pagename != undefined ) { this . goToFile ( pagename , false ) ; } for ( const i in response . actions ) { this . handleNavAction ( response . actions [ i ] ) ; } } }