mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 13:29:44 +00:00
Extracting common webview elements
Minimizing diff with the iframe based webview branch
This commit is contained in:
parent
39947c7250
commit
8de74d9255
|
@ -197,17 +197,17 @@ export class MarkdownContentProvider {
|
|||
private getCspForResource(resource: vscode.Uri, nonce: string): string {
|
||||
switch (this.cspArbiter.getSecurityLevelForResource(resource)) {
|
||||
case MarkdownPreviewSecurityLevel.AllowInsecureContent:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: http: https: data:; media-src vscode-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' http: https: data:; font-src vscode-resource: http: https: data:;">`;
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: http: https: data:; media-src vscode-resource: http: https: data:; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' http: https: data:; font-src vscode-resource: http: https: data:;">`;
|
||||
|
||||
case MarkdownPreviewSecurityLevel.AllowInsecureLocalContent:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; media-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*;">`;
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; media-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src vscode-resource: https: data: http://localhost:* http://127.0.0.1:*;">`;
|
||||
|
||||
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
|
||||
return '';
|
||||
|
||||
case MarkdownPreviewSecurityLevel.Strict:
|
||||
default:
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data:; media-src vscode-resource: https: data:; script-src 'nonce-${nonce}'; style-src vscode-resource: 'unsafe-inline' https: data:; font-src vscode-resource: https: data:;">`;
|
||||
return `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https: data:; media-src vscode-resource: https: data:; script-src 'nonce-${nonce}'; style-src 'self' vscode-resource: 'unsafe-inline' https: data:; font-src vscode-resource: https: data:;">`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,13 +94,19 @@ const defaultCssRules = `
|
|||
}`;
|
||||
|
||||
/**
|
||||
* @typedef {{ postMessage: (channel: string, data?: any) => void, onMessage: (channel: string, handler: any) => void }} HostCommunications
|
||||
* @typedef {{
|
||||
* postMessage: (channel: string, data?: any) => void,
|
||||
* onMessage: (channel: string, handler: any) => void,
|
||||
* injectHtml?: (document: HTMLDocument) => void,
|
||||
* preProcessHtml?: (text: string) => void,
|
||||
* focusIframeOnCreate?: boolean
|
||||
* }} HostCommunications
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {HostCommunications} host
|
||||
*/
|
||||
module.exports = function createWebviewManager(host) {
|
||||
function createWebviewManager(host) {
|
||||
// state
|
||||
let firstLoad = true;
|
||||
let loadTimeout;
|
||||
|
@ -212,9 +218,9 @@ module.exports = function createWebviewManager(host) {
|
|||
return;
|
||||
}
|
||||
|
||||
host.onMessage('styles', (_event, variables, activeTheme) => {
|
||||
initData.styles = variables;
|
||||
initData.activeTheme = activeTheme;
|
||||
host.onMessage('styles', (_event, data) => {
|
||||
initData.styles = data.styles;
|
||||
initData.activeTheme = data.activeTheme;
|
||||
|
||||
const target = getActiveFrame();
|
||||
if (!target) {
|
||||
|
@ -238,7 +244,7 @@ module.exports = function createWebviewManager(host) {
|
|||
host.onMessage('content', (_event, data) => {
|
||||
const options = data.options;
|
||||
|
||||
const text = data.contents;
|
||||
const text = host.preProcessHtml ? host.preProcessHtml(data.contents) : data.contents;
|
||||
const newDocument = new DOMParser().parseFromString(text, 'text/html');
|
||||
|
||||
newDocument.querySelectorAll('a').forEach(a => {
|
||||
|
@ -293,6 +299,10 @@ module.exports = function createWebviewManager(host) {
|
|||
|
||||
applyStyles(newDocument, newDocument.body);
|
||||
|
||||
if (host.injectHtml) {
|
||||
host.injectHtml(newDocument);
|
||||
}
|
||||
|
||||
const frame = getActiveFrame();
|
||||
const wasFirstLoad = firstLoad;
|
||||
// keep current scrollY around and use later
|
||||
|
@ -372,7 +382,9 @@ module.exports = function createWebviewManager(host) {
|
|||
applyStyles(newFrame.contentDocument, newFrame.contentDocument.body);
|
||||
newFrame.setAttribute('id', 'active-frame');
|
||||
newFrame.style.visibility = 'visible';
|
||||
newFrame.contentWindow.focus();
|
||||
if (host.focusIframeOnCreate) {
|
||||
newFrame.contentWindow.focus();
|
||||
}
|
||||
|
||||
contentWindow.addEventListener('scroll', handleInnerScroll);
|
||||
|
||||
|
@ -441,6 +453,10 @@ module.exports = function createWebviewManager(host) {
|
|||
window.onmessage = onMessage;
|
||||
|
||||
// signal ready
|
||||
host.postMessage('webview-ready', process.pid);
|
||||
host.postMessage('webview-ready', {});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = createWebviewManager;
|
||||
}
|
26
src/vs/workbench/contrib/webview/common/mimeTypes.ts
Normal file
26
src/vs/workbench/contrib/webview/common/mimeTypes.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime';
|
||||
import { extname } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const webviewMimeTypes = {
|
||||
'.svg': 'image/svg+xml',
|
||||
'.txt': 'text/plain',
|
||||
'.css': 'text/css',
|
||||
'.js': 'application/javascript',
|
||||
'.json': 'application/json',
|
||||
'.html': 'text/html',
|
||||
'.htm': 'text/html',
|
||||
'.xhtml': 'application/xhtml+xml',
|
||||
'.oft': 'font/otf',
|
||||
'.xml': 'application/xml',
|
||||
};
|
||||
|
||||
export function getWebviewContentMimeType(normalizedPath: URI): string {
|
||||
const ext = extname(normalizedPath.fsPath).toLowerCase();
|
||||
return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN;
|
||||
}
|
63
src/vs/workbench/contrib/webview/common/themeing.ts
Normal file
63
src/vs/workbench/contrib/webview/common/themeing.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
interface WebviewThemeData {
|
||||
readonly activeTheme: string;
|
||||
readonly styles: { readonly [key: string]: string | number };
|
||||
}
|
||||
|
||||
export function getWebviewThemeData(
|
||||
theme: ITheme,
|
||||
configurationService: IConfigurationService
|
||||
): WebviewThemeData {
|
||||
const configuration = configurationService.getValue<IEditorOptions>('editor');
|
||||
const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
|
||||
const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight;
|
||||
const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize;
|
||||
|
||||
const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => {
|
||||
const color = theme.getColor(entry.id);
|
||||
if (color) {
|
||||
colors['vscode-' + entry.id.replace('.', '-')] = color.toString();
|
||||
}
|
||||
return colors;
|
||||
}, {} as { [key: string]: string });
|
||||
|
||||
const styles = {
|
||||
'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", ans-serif',
|
||||
'vscode-font-weight': 'normal',
|
||||
'vscode-font-size': '13px',
|
||||
'vscode-editor-font-family': editorFontFamily,
|
||||
'vscode-editor-font-weight': editorFontWeight,
|
||||
'vscode-editor-font-size': editorFontSize,
|
||||
...exportedColors
|
||||
};
|
||||
|
||||
const activeTheme = ApiThemeClassName.fromTheme(theme);
|
||||
return { styles, activeTheme };
|
||||
}
|
||||
|
||||
enum ApiThemeClassName {
|
||||
light = 'vscode-light',
|
||||
dark = 'vscode-dark',
|
||||
highContrast = 'vscode-high-contrast'
|
||||
}
|
||||
|
||||
namespace ApiThemeClassName {
|
||||
export function fromTheme(theme: ITheme): ApiThemeClassName {
|
||||
if (theme.type === LIGHT) {
|
||||
return ApiThemeClassName.light;
|
||||
} else if (theme.type === DARK) {
|
||||
return ApiThemeClassName.dark;
|
||||
} else {
|
||||
return ApiThemeClassName.highContrast;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,12 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
/**
|
||||
* Set when the find widget in a webview is visible.
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
},
|
||||
onMessage: (channel, handler) => {
|
||||
ipcRenderer.on(channel, handler);
|
||||
}
|
||||
},
|
||||
focusIframeOnCreate: true
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
|
|
@ -11,7 +11,6 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { endsWith } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
@ -21,12 +20,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
|
||||
import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService';
|
||||
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
|
||||
import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
|
||||
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
|
||||
import { WebviewFindWidget } from '../browser/webviewFindWidget';
|
||||
import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing';
|
||||
|
||||
export interface WebviewPortMapping {
|
||||
readonly port: number;
|
||||
|
@ -554,11 +553,11 @@ export class WebviewElement extends Disposable implements Webview {
|
|||
private readonly _onMessage = this._register(new Emitter<any>());
|
||||
public readonly onMessage = this._onMessage.event;
|
||||
|
||||
private _send(channel: string, ...args: any[]): void {
|
||||
private _send(channel: string, data?: any): void {
|
||||
this._ready
|
||||
.then(() => {
|
||||
if (this._webview) {
|
||||
this._webview.send(channel, ...args);
|
||||
this._webview.send(channel, data);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
|
@ -647,31 +646,8 @@ export class WebviewElement extends Disposable implements Webview {
|
|||
}
|
||||
|
||||
private style(theme: ITheme): void {
|
||||
const configuration = this._configurationService.getValue<IEditorOptions>('editor');
|
||||
const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily;
|
||||
const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight;
|
||||
const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize;
|
||||
|
||||
const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => {
|
||||
const color = theme.getColor(entry.id);
|
||||
if (color) {
|
||||
colors['vscode-' + entry.id.replace('.', '-')] = color.toString();
|
||||
}
|
||||
return colors;
|
||||
}, {} as { [key: string]: string });
|
||||
|
||||
const styles = {
|
||||
'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif',
|
||||
'vscode-font-weight': 'normal',
|
||||
'vscode-font-size': '13px',
|
||||
'vscode-editor-font-family': editorFontFamily,
|
||||
'vscode-editor-font-weight': editorFontWeight,
|
||||
'vscode-editor-font-size': editorFontSize,
|
||||
...exportedColors
|
||||
};
|
||||
|
||||
const activeTheme = ApiThemeClassName.fromTheme(theme);
|
||||
this._send('styles', styles, activeTheme);
|
||||
const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService);
|
||||
this._send('styles', { styles, activeTheme });
|
||||
|
||||
if (this._webviewFindWidget) {
|
||||
this._webviewFindWidget.updateTheme(theme);
|
||||
|
@ -805,22 +781,3 @@ export class WebviewElement extends Disposable implements Webview {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum ApiThemeClassName {
|
||||
light = 'vscode-light',
|
||||
dark = 'vscode-dark',
|
||||
highContrast = 'vscode-high-contrast'
|
||||
}
|
||||
|
||||
namespace ApiThemeClassName {
|
||||
export function fromTheme(theme: ITheme): ApiThemeClassName {
|
||||
if (theme.type === LIGHT) {
|
||||
return ApiThemeClassName.light;
|
||||
} else if (theme.type === DARK) {
|
||||
return ApiThemeClassName.dark;
|
||||
} else {
|
||||
return ApiThemeClassName.highContrast;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime';
|
||||
import { extname, sep } from 'vs/base/common/path';
|
||||
import * as electron from 'electron';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import * as electron from 'electron';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { getWebviewContentMimeType } from 'vs/workbench/contrib/webview/common/mimeTypes';
|
||||
|
||||
type BufferProtocolCallback = (buffer?: Buffer | electron.MimeTypedBuffer | { error: number }) => void;
|
||||
|
||||
|
@ -54,10 +54,10 @@ export function registerFileProtocol(
|
|||
requestResourcePath: requestUri.path
|
||||
})
|
||||
});
|
||||
resolveContent(fileService, redirectedUri, getMimeType(requestUri), callback);
|
||||
resolveContent(fileService, redirectedUri, getWebviewContentMimeType(requestUri), callback);
|
||||
return;
|
||||
} else {
|
||||
resolveContent(fileService, normalizedPath, getMimeType(normalizedPath), callback);
|
||||
resolveContent(fileService, normalizedPath, getWebviewContentMimeType(normalizedPath), callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -70,20 +70,3 @@ export function registerFileProtocol(
|
|||
});
|
||||
}
|
||||
|
||||
const webviewMimeTypes = {
|
||||
'.svg': 'image/svg+xml',
|
||||
'.txt': 'text/plain',
|
||||
'.css': 'text/css',
|
||||
'.js': 'application/javascript',
|
||||
'.json': 'application/json',
|
||||
'.html': 'text/html',
|
||||
'.htm': 'text/html',
|
||||
'.xhtml': 'application/xhtml+xml',
|
||||
'.oft': 'font/otf',
|
||||
'.xml': 'application/xml',
|
||||
};
|
||||
|
||||
function getMimeType(normalizedPath: URI): string {
|
||||
const ext = extname(normalizedPath.fsPath).toLowerCase();
|
||||
return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue