Skip to content

Commit 461ff9b

Browse files
committed
Move Webview HTML generation out of interface-utils.ts
This moves the Webview HTML generation used by `AbstractWebview` out of `interface-utils.ts` and into a new file `webview-html.ts` in the `common/vscode` directory.
1 parent a7a24fc commit 461ff9b

File tree

3 files changed

+102
-107
lines changed

3 files changed

+102
-107
lines changed

extensions/ql-vscode/src/common/vscode/abstract-webview.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import { join } from "path";
1111

1212
import { DisposableObject, DisposeHandler } from "../../pure/disposable-object";
1313
import { tmpDir } from "../../helpers";
14-
import {
15-
getHtmlForWebview,
16-
WebviewMessage,
17-
WebviewView,
18-
} from "../../interface-utils";
14+
import { getHtmlForWebview, WebviewMessage, WebviewView } from "./webview-html";
1915

2016
export type WebviewPanelConfig = {
2117
viewId: string;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ExtensionContext, Uri, Webview } from "vscode";
2+
import { randomBytes } from "crypto";
3+
import { EOL } from "os";
4+
5+
export type WebviewView =
6+
| "results"
7+
| "compare"
8+
| "variant-analysis"
9+
| "data-flow-paths"
10+
| "data-extensions-editor";
11+
12+
export interface WebviewMessage {
13+
t: string;
14+
}
15+
16+
/**
17+
* Returns HTML to populate the given webview.
18+
* Uses a content security policy that only loads the given script.
19+
*/
20+
export function getHtmlForWebview(
21+
ctx: ExtensionContext,
22+
webview: Webview,
23+
view: WebviewView,
24+
{
25+
allowInlineStyles,
26+
allowWasmEval,
27+
}: {
28+
allowInlineStyles?: boolean;
29+
allowWasmEval?: boolean;
30+
} = {
31+
allowInlineStyles: false,
32+
allowWasmEval: false,
33+
},
34+
): string {
35+
const scriptUriOnDisk = Uri.file(ctx.asAbsolutePath("out/webview.js"));
36+
37+
const stylesheetUrisOnDisk = [
38+
Uri.file(ctx.asAbsolutePath("out/webview.css")),
39+
];
40+
41+
// Convert the on-disk URIs into webview URIs.
42+
const scriptWebviewUri = webview.asWebviewUri(scriptUriOnDisk);
43+
const stylesheetWebviewUris = stylesheetUrisOnDisk.map(
44+
(stylesheetUriOnDisk) => webview.asWebviewUri(stylesheetUriOnDisk),
45+
);
46+
47+
// Use a nonce in the content security policy to uniquely identify the above resources.
48+
const nonce = getNonce();
49+
50+
const stylesheetsHtmlLines = allowInlineStyles
51+
? stylesheetWebviewUris.map((uri) => createStylesLinkWithoutNonce(uri))
52+
: stylesheetWebviewUris.map((uri) => createStylesLinkWithNonce(nonce, uri));
53+
54+
const styleSrc = allowInlineStyles
55+
? `${webview.cspSource} vscode-file: 'unsafe-inline'`
56+
: `'nonce-${nonce}'`;
57+
58+
const fontSrc = webview.cspSource;
59+
60+
/*
61+
* Content security policy:
62+
* default-src: allow nothing by default.
63+
* script-src:
64+
* - allow the given script, using the nonce.
65+
* - 'wasm-unsafe-eval: allow loading WebAssembly modules if necessary.
66+
* style-src: allow only the given stylesheet, using the nonce.
67+
* connect-src: only allow fetch calls to webview resource URIs
68+
* (this is used to load BQRS result files).
69+
*/
70+
return `
71+
<html>
72+
<head>
73+
<meta http-equiv="Content-Security-Policy"
74+
content="default-src 'none'; script-src 'nonce-${nonce}'${
75+
allowWasmEval ? " 'wasm-unsafe-eval'" : ""
76+
}; font-src ${fontSrc}; style-src ${styleSrc}; connect-src ${
77+
webview.cspSource
78+
};">
79+
${stylesheetsHtmlLines.join(` ${EOL}`)}
80+
</head>
81+
<body>
82+
<div id=root data-view="${view}">
83+
</div>
84+
<script nonce="${nonce}" src="${scriptWebviewUri}">
85+
</script>
86+
</body>
87+
</html>`;
88+
}
89+
90+
/** Gets a nonce string created with 128 bits of entropy. */
91+
function getNonce(): string {
92+
return randomBytes(16).toString("base64");
93+
}
94+
95+
function createStylesLinkWithNonce(nonce: string, uri: Uri): string {
96+
return `<link nonce="${nonce}" rel="stylesheet" href="${uri}">`;
97+
}
98+
99+
function createStylesLinkWithoutNonce(uri: Uri): string {
100+
return `<link rel="stylesheet" href="${uri}">`;
101+
}

extensions/ql-vscode/src/interface-utils.ts

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import { randomBytes } from "crypto";
2-
import { EOL } from "os";
31
import {
42
Uri,
53
Location,
64
Range,
7-
ExtensionContext,
85
WebviewPanel,
9-
Webview,
106
workspace,
117
window as Window,
128
ViewColumn,
@@ -30,11 +26,6 @@ import {
3026
* interface.ts and compare-interface.ts.
3127
*/
3228

33-
/** Gets a nonce string created with 128 bits of entropy. */
34-
export function getNonce(): string {
35-
return randomBytes(16).toString("base64");
36-
}
37-
3829
/**
3930
* Whether to force webview to reveal
4031
*/
@@ -109,91 +100,6 @@ export function tryResolveLocation(
109100
}
110101
}
111102

112-
export type WebviewView =
113-
| "results"
114-
| "compare"
115-
| "variant-analysis"
116-
| "data-flow-paths"
117-
| "data-extensions-editor";
118-
119-
export interface WebviewMessage {
120-
t: string;
121-
}
122-
123-
/**
124-
* Returns HTML to populate the given webview.
125-
* Uses a content security policy that only loads the given script.
126-
*/
127-
export function getHtmlForWebview(
128-
ctx: ExtensionContext,
129-
webview: Webview,
130-
view: WebviewView,
131-
{
132-
allowInlineStyles,
133-
allowWasmEval,
134-
}: {
135-
allowInlineStyles?: boolean;
136-
allowWasmEval?: boolean;
137-
} = {
138-
allowInlineStyles: false,
139-
allowWasmEval: false,
140-
},
141-
): string {
142-
const scriptUriOnDisk = Uri.file(ctx.asAbsolutePath("out/webview.js"));
143-
144-
const stylesheetUrisOnDisk = [
145-
Uri.file(ctx.asAbsolutePath("out/webview.css")),
146-
];
147-
148-
// Convert the on-disk URIs into webview URIs.
149-
const scriptWebviewUri = webview.asWebviewUri(scriptUriOnDisk);
150-
const stylesheetWebviewUris = stylesheetUrisOnDisk.map(
151-
(stylesheetUriOnDisk) => webview.asWebviewUri(stylesheetUriOnDisk),
152-
);
153-
154-
// Use a nonce in the content security policy to uniquely identify the above resources.
155-
const nonce = getNonce();
156-
157-
const stylesheetsHtmlLines = allowInlineStyles
158-
? stylesheetWebviewUris.map((uri) => createStylesLinkWithoutNonce(uri))
159-
: stylesheetWebviewUris.map((uri) => createStylesLinkWithNonce(nonce, uri));
160-
161-
const styleSrc = allowInlineStyles
162-
? `${webview.cspSource} vscode-file: 'unsafe-inline'`
163-
: `'nonce-${nonce}'`;
164-
165-
const fontSrc = webview.cspSource;
166-
167-
/*
168-
* Content security policy:
169-
* default-src: allow nothing by default.
170-
* script-src:
171-
* - allow the given script, using the nonce.
172-
* - 'wasm-unsafe-eval: allow loading WebAssembly modules if necessary.
173-
* style-src: allow only the given stylesheet, using the nonce.
174-
* connect-src: only allow fetch calls to webview resource URIs
175-
* (this is used to load BQRS result files).
176-
*/
177-
return `
178-
<html>
179-
<head>
180-
<meta http-equiv="Content-Security-Policy"
181-
content="default-src 'none'; script-src 'nonce-${nonce}'${
182-
allowWasmEval ? " 'wasm-unsafe-eval'" : ""
183-
}; font-src ${fontSrc}; style-src ${styleSrc}; connect-src ${
184-
webview.cspSource
185-
};">
186-
${stylesheetsHtmlLines.join(` ${EOL}`)}
187-
</head>
188-
<body>
189-
<div id=root data-view="${view}">
190-
</div>
191-
<script nonce="${nonce}" src="${scriptWebviewUri}">
192-
</script>
193-
</body>
194-
</html>`;
195-
}
196-
197103
export async function showResolvableLocation(
198104
loc: ResolvableLocationValue,
199105
databaseItem: DatabaseItem,
@@ -279,11 +185,3 @@ export async function jumpToLocation(
279185
}
280186
}
281187
}
282-
283-
function createStylesLinkWithNonce(nonce: string, uri: Uri): string {
284-
return `<link nonce="${nonce}" rel="stylesheet" href="${uri}">`;
285-
}
286-
287-
function createStylesLinkWithoutNonce(uri: Uri): string {
288-
return `<link rel="stylesheet" href="${uri}">`;
289-
}

0 commit comments

Comments
 (0)