src/welcome.ts: add welcome webview

This change is the initial change for adding a welcome page
for the extension. This adds the basic structure and a link to
open the release notes, which uses the communication channel
between the webview and the extension.

The webview is based on the microsoft webview example:
https://github.com/microsoft/vscode-extension-samples/tree/master/webview-sample

Updates golang/vscode-go#949

Change-Id: I08f63346781dcf9afb24b8d482394a0db7cf6492
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/280596
Trust: Suzy Mueller <suzmue@golang.org>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/docs/commands.md b/docs/commands.md
index f1450e9..6914126 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -95,6 +95,10 @@
 
 Extract logs in the `gopls (server)` output channel to the editor.
 
+### `Go: Welcome`
+
+Open the welcome page for the Go extension.
+
 ### `Go: Toggle gc details`
 
 Toggle the display of compiler optimization choices
diff --git a/images/go-logo-blue.png b/media/go-logo-blue.png
similarity index 100%
rename from images/go-logo-blue.png
rename to media/go-logo-blue.png
Binary files differ
diff --git a/images/gutter-blockblue.svg b/media/gutter-blockblue.svg
similarity index 100%
rename from images/gutter-blockblue.svg
rename to media/gutter-blockblue.svg
diff --git a/images/gutter-blockgreen.svg b/media/gutter-blockgreen.svg
similarity index 100%
rename from images/gutter-blockgreen.svg
rename to media/gutter-blockgreen.svg
diff --git a/images/gutter-blockred.svg b/media/gutter-blockred.svg
similarity index 100%
rename from images/gutter-blockred.svg
rename to media/gutter-blockred.svg
diff --git a/images/gutter-blockyellow.svg b/media/gutter-blockyellow.svg
similarity index 100%
rename from images/gutter-blockyellow.svg
rename to media/gutter-blockyellow.svg
diff --git a/images/gutter-slashblue.svg b/media/gutter-slashblue.svg
similarity index 100%
rename from images/gutter-slashblue.svg
rename to media/gutter-slashblue.svg
diff --git a/images/gutter-slashgreen.svg b/media/gutter-slashgreen.svg
similarity index 100%
rename from images/gutter-slashgreen.svg
rename to media/gutter-slashgreen.svg
diff --git a/images/gutter-slashred.svg b/media/gutter-slashred.svg
similarity index 100%
rename from images/gutter-slashred.svg
rename to media/gutter-slashred.svg
diff --git a/images/gutter-slashyellow.svg b/media/gutter-slashyellow.svg
similarity index 100%
rename from images/gutter-slashyellow.svg
rename to media/gutter-slashyellow.svg
diff --git a/images/gutter-vertblue.svg b/media/gutter-vertblue.svg
similarity index 100%
rename from images/gutter-vertblue.svg
rename to media/gutter-vertblue.svg
diff --git a/images/gutter-vertgreen.svg b/media/gutter-vertgreen.svg
similarity index 100%
rename from images/gutter-vertgreen.svg
rename to media/gutter-vertgreen.svg
diff --git a/images/gutter-vertred.svg b/media/gutter-vertred.svg
similarity index 100%
rename from images/gutter-vertred.svg
rename to media/gutter-vertred.svg
diff --git a/images/gutter-vertyellow.svg b/media/gutter-vertyellow.svg
similarity index 100%
rename from images/gutter-vertyellow.svg
rename to media/gutter-vertyellow.svg
diff --git a/media/welcome.css b/media/welcome.css
new file mode 100644
index 0000000..caee018
--- /dev/null
+++ b/media/welcome.css
@@ -0,0 +1,4 @@
+/*---------------------------------------------------------
+ * Copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
diff --git a/media/welcome.js b/media/welcome.js
new file mode 100644
index 0000000..8457bbe
--- /dev/null
+++ b/media/welcome.js
@@ -0,0 +1,22 @@
+/*---------------------------------------------------------
+ * Copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+// This script will be run within the webview itself
+// It cannot access the main VS Code APIs directly.
+(function () {
+	const vscode = acquireVsCodeApi();
+
+	function showReleaseNotes() {
+		vscode.postMessage({
+			command: 'showReleaseNotes',
+		});
+	}
+
+	document.querySelector(".release-notes").addEventListener('click', () => {
+		showReleaseNotes();
+	});
+
+}());
+
diff --git a/package.json b/package.json
index d530bc5..e1a1188 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "name": "Go Team at Google"
   },
   "license": "MIT",
-  "icon": "images/go-logo-blue.png",
+  "icon": "media/go-logo-blue.png",
   "categories": [
     "Programming Languages",
     "Snippets",
@@ -97,7 +97,8 @@
     "onCommand:go.locate.tools",
     "onCommand:go.show.commands",
     "onDebugInitialConfigurations",
-    "onDebugResolve:go"
+    "onDebugResolve:go",
+    "onWebviewPanel:welcomeGo"
   ],
   "main": "./dist/goMain.js",
   "contributes": {
@@ -251,6 +252,11 @@
         "description": "Extract logs in the `gopls (server)` output channel to the editor."
       },
       {
+        "command": "go.welcome",
+        "title": "Go: Welcome",
+        "description": "Open the welcome page for the Go extension."
+      },
+      {
         "command": "go.toggle.gc_details",
         "title": "Go: Toggle gc details",
         "description": "Toggle the display of compiler optimization choices"
diff --git a/src/goCover.ts b/src/goCover.ts
index f1e96a5..2d525b3 100644
--- a/src/goCover.ts
+++ b/src/goCover.ts
@@ -54,18 +54,18 @@
 export function initCoverageDecorators(ctx: vscode.ExtensionContext) {
 	// Initialize gutter svgs
 	gutterSvgs = {
-		blockred: ctx.asAbsolutePath('images/gutter-blockred.svg'),
-		blockgreen: ctx.asAbsolutePath('images/gutter-blockgreen.svg'),
-		blockblue: ctx.asAbsolutePath('images/gutter-blockblue.svg'),
-		blockyellow: ctx.asAbsolutePath('images/gutter-blockyellow.svg'),
-		slashred: ctx.asAbsolutePath('images/gutter-slashred.svg'),
-		slashgreen: ctx.asAbsolutePath('images/gutter-slashgreen.svg'),
-		slashblue: ctx.asAbsolutePath('images/gutter-slashblue.svg'),
-		slashyellow: ctx.asAbsolutePath('images/gutter-slashyellow.svg'),
-		verticalred: ctx.asAbsolutePath('images/gutter-vertred.svg'),
-		verticalgreen: ctx.asAbsolutePath('images/gutter-vertgreen.svg'),
-		verticalblue: ctx.asAbsolutePath('images/gutter-vertblue.svg'),
-		verticalyellow: ctx.asAbsolutePath('images/gutter-vertyellow.svg')
+		blockred: ctx.asAbsolutePath('media/gutter-blockred.svg'),
+		blockgreen: ctx.asAbsolutePath('media/gutter-blockgreen.svg'),
+		blockblue: ctx.asAbsolutePath('media/gutter-blockblue.svg'),
+		blockyellow: ctx.asAbsolutePath('media/gutter-blockyellow.svg'),
+		slashred: ctx.asAbsolutePath('media/gutter-slashred.svg'),
+		slashgreen: ctx.asAbsolutePath('media/gutter-slashgreen.svg'),
+		slashblue: ctx.asAbsolutePath('media/gutter-slashblue.svg'),
+		slashyellow: ctx.asAbsolutePath('media/gutter-slashyellow.svg'),
+		verticalred: ctx.asAbsolutePath('media/gutter-vertred.svg'),
+		verticalgreen: ctx.asAbsolutePath('media/gutter-vertgreen.svg'),
+		verticalblue: ctx.asAbsolutePath('media/gutter-vertblue.svg'),
+		verticalyellow: ctx.asAbsolutePath('media/gutter-vertyellow.svg')
 	};
 
 	const goConfig = getGoConfig();
diff --git a/src/goMain.ts b/src/goMain.ts
index 197363f..c7cd808 100644
--- a/src/goMain.ts
+++ b/src/goMain.ts
@@ -70,6 +70,7 @@
 	resolvePath,
 } from './util';
 import { clearCacheForTools, fileExists, getCurrentGoRoot, setCurrentGoRoot } from './utils/pathUtils';
+import { WelcomePanel } from './welcome';
 
 export let buildDiagnosticCollection: vscode.DiagnosticCollection;
 export let lintDiagnosticCollection: vscode.DiagnosticCollection;
@@ -91,6 +92,15 @@
 	setWorkspaceState(ctx.workspaceState);
 	setEnvironmentVariableCollection(ctx.environmentVariableCollection);
 
+	if (vscode.window.registerWebviewPanelSerializer) {
+		// Make sure we register a serializer in activation event
+		vscode.window.registerWebviewPanelSerializer(WelcomePanel.viewType, {
+			async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
+				WelcomePanel.revive(webviewPanel, ctx.extensionUri);
+			}
+		});
+	}
+
 	if (isNightly()) {
 		promptForLanguageServerDefaultChange(cfg);
 
@@ -470,6 +480,11 @@
 		showServerOutputChannel();
 	}));
 
+	ctx.subscriptions.push(
+		vscode.commands.registerCommand('go.welcome', () => {
+			WelcomePanel.createOrShow(ctx.extensionUri);
+		}));
+
 	ctx.subscriptions.push(vscode.commands.registerCommand('go.toggle.gc_details', () => {
 		if (!languageServerIsRunning) {
 			vscode.window.showErrorMessage('"Go: Toggle gc details" command is available only when the language server is running');
diff --git a/src/welcome.ts b/src/welcome.ts
new file mode 100644
index 0000000..3300888
--- /dev/null
+++ b/src/welcome.ts
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------
+ * Copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+// This code is modified from:
+// https://github.com/microsoft/vscode-extension-samples/tree/master/webview-sample
+
+import vscode = require('vscode');
+
+export class WelcomePanel {
+	public static currentPanel: WelcomePanel | undefined;
+
+	public static readonly viewType = 'welcomeGo';
+
+	public static createOrShow(extensionUri: vscode.Uri) {
+		const column = vscode.window.activeTextEditor
+			? vscode.window.activeTextEditor.viewColumn
+			: undefined;
+
+		// If we already have a panel, show it.
+		if (WelcomePanel.currentPanel) {
+			WelcomePanel.currentPanel.panel.reveal(column);
+			return;
+		}
+
+		// Otherwise, create a new panel.
+		const panel = vscode.window.createWebviewPanel(
+			WelcomePanel.viewType,
+			'Go - Welcome',
+			column || vscode.ViewColumn.One,
+			{
+				// Enable javascript in the webview
+				enableScripts: true,
+
+				// And restrict the webview to only loading content from our extension's directory.
+				localResourceRoots: [vscode.Uri.joinPath(extensionUri)],
+			}
+		);
+		panel.iconPath = vscode.Uri.joinPath(extensionUri, 'media', 'go-logo-blue.png');
+
+		WelcomePanel.currentPanel = new WelcomePanel(panel, extensionUri);
+	}
+
+	public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
+		WelcomePanel.currentPanel = new WelcomePanel(panel, extensionUri);
+	}
+
+	private readonly panel: vscode.WebviewPanel;
+	private readonly extensionUri: vscode.Uri;
+	private readonly dataroot: vscode.Uri;
+	private disposables: vscode.Disposable[] = [];
+
+	private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
+		this.panel = panel;
+		this.extensionUri = extensionUri;
+		this.dataroot = vscode.Uri.joinPath(this.extensionUri, 'media');
+
+		// Set the webview's initial html content
+		this.update();
+
+		// Listen for when the panel is disposed
+		// This happens when the user closes the panel or when the panel is closed programatically
+		this.panel.onDidDispose(() => this.dispose(), null, this.disposables);
+
+		// Handle messages from the webview
+		this.panel.webview.onDidReceiveMessage(
+			(message) => {
+				console.log(message);
+				switch (message.command) {
+					case 'alert':
+						vscode.window.showErrorMessage(message.text);
+						return;
+					case 'showReleaseNotes':
+						const uri = vscode.Uri.joinPath(this.extensionUri, 'CHANGELOG.md');
+						vscode.commands.executeCommand('markdown.showPreviewToSide', uri);
+						return;
+				}
+			},
+			null,
+			this.disposables
+		);
+	}
+
+	public dispose() {
+		WelcomePanel.currentPanel = undefined;
+
+		// Clean up our resources
+		this.panel.dispose();
+
+		while (this.disposables.length) {
+			const x = this.disposables.pop();
+			if (x) {
+				x.dispose();
+			}
+		}
+	}
+
+	private update() {
+		const webview = this.panel.webview;
+		this.panel.webview.html = this.getHtmlForWebview(webview);
+	}
+
+	private getHtmlForWebview(webview: vscode.Webview) {
+		// Local path to css styles and images
+		const scriptPathOnDisk = vscode.Uri.joinPath(this.dataroot, 'welcome.js');
+		const stylePath = vscode.Uri.joinPath(this.dataroot, 'welcome.css');
+		const gopherPath = vscode.Uri.joinPath(this.dataroot, 'go-logo-blue.png');
+
+		// Uri to load styles and images into webview
+		const scriptURI = webview.asWebviewUri(scriptPathOnDisk);
+		const stylesURI = webview.asWebviewUri(stylePath);
+		const gopherURI = webview.asWebviewUri(gopherPath);
+
+		// Use a nonce to only allow specific scripts to be run
+		const nonce = getNonce();
+		return `<!DOCTYPE html>
+			<html lang="en">
+			<head>
+				<meta 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'; style-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';">
+
+				<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+				<link href="${stylesURI}" rel="stylesheet">
+
+				<title>Go - Welcome</title>
+			</head>
+			<body>
+			<div class="header">
+				<img src="${gopherURI}" alt="logo"/>
+				<h1>Go - Welcome</h1>
+			</div>
+
+			<p>Rich Go language support for Visual Studio Code</p>
+			<!-- linking to a document does not actually work, but is used here to give it the appearance
+			of a link -->
+			<p><a class="release-notes"">Release Notes</a></p>
+
+			<script nonce="${nonce}" src="${scriptURI}"></script>
+			</body>
+			</html>`;
+	}
+}
+
+function getNonce() {
+	let text = '';
+	const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+	for (let i = 0; i < 32; i++) {
+		text += possible.charAt(Math.floor(Math.random() * possible.length));
+	}
+	return text;
+}