提交 57c79f4c 编写于 作者: R Rob Lourens

Move data URL handling code out of preload script. Guess extension based on...

Move data URL handling code out of preload script. Guess extension based  on mimetype if 'download' isn't provided
上级 2c137b4e
......@@ -335,3 +335,13 @@ export function getMediaMime(path: string): string | undefined {
const ext = extname(path);
return mapExtToMediaMimes[ext.toLowerCase()];
}
export function getExtensionForMimeType(mimeType: string): string | undefined {
for (const extension in mapExtToMediaMimes) {
if (mapExtToMediaMimes[extension] === mimeType) {
return extension;
}
}
return undefined;
}
......@@ -18,7 +18,7 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IWebviewService, WebviewElement, IDataLinkClickEvent } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import { asWebviewUri } from 'vs/workbench/contrib/webview/common/webviewUri';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname, joinPath } from 'vs/base/common/resources';
......@@ -27,6 +27,7 @@ import { Schemas } from 'vs/base/common/network';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IFileService } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { getExtensionForMimeType } from 'vs/base/common/mime';
export interface WebviewIntialized {
__vscode_notebook_message: boolean;
......@@ -73,6 +74,13 @@ export interface IBlurOutputMessage {
focusNext?: boolean;
}
export interface IClickedDataUrlMessage {
__vscode_notebook_message: string;
type: 'clicked-data-url';
data: string;
downloadName?: string;
}
export interface IClearMessage {
type: 'clear';
}
......@@ -134,7 +142,7 @@ function html(strings: TemplateStringsArray, ...values: any[]): string {
return str;
}
type IMessage = IDimensionMessage | IScrollAckMessage | IWheelMessage | IMouseEnterMessage | IMouseLeaveMessage | IBlurOutputMessage | WebviewIntialized;
type IMessage = IDimensionMessage | IScrollAckMessage | IWheelMessage | IMouseEnterMessage | IMouseLeaveMessage | IBlurOutputMessage | WebviewIntialized | IClickedDataUrlMessage;
let version = 0;
export class BackLayerWebView extends Disposable {
......@@ -241,6 +249,50 @@ ${loaderJs}
<div id='container' class="widgetarea" style="position: absolute;width:100%;top: 0px"></div>
<script>
(function () {
const handleInnerClick = (event) => {
if (!event || !event.view || !event.view.document) {
return;
}
/** @type {any} */
let node = event.target;
while (node) {
if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) {
if (node.href.startsWith('blob:')) {
handleBlobUrlClick(node.href, node.download);
}
event.preventDefault();
break;
}
node = node.parentNode;
}
};
const handleBlobUrlClick = async (url, downloadName) => {
try {
const response = await fetch(url);
const blob = await response.blob();
const reader = new FileReader();
reader.addEventListener('load', () => {
const data = reader.result;
vscode.postMessage({
__vscode_notebook_message: true,
type: 'clicked-data-url',
data,
downloadName
});
});
reader.readAsDataURL(blob);
} catch (e) {
console.error(e.message);
}
};
document.body.addEventListener('click', handleInnerClick);
// eslint-disable-next-line no-undef
const vscode = acquireVsCodeApi();
......@@ -521,10 +573,6 @@ ${loaderJs}
}
}));
this._register(this.webview.onDidClickDataLink(event => {
this._onDidClickDataLink(event);
}));
this._register(this.webview.onDidReload(() => {
this.preloadsCache.clear();
for (const [output, inset] of this.insetMapping.entries()) {
......@@ -587,6 +635,8 @@ ${loaderJs}
this.notebookEditor.focusNotebookCell(info.cell, 'editor');
}
}
} else if (data.type === 'clicked-data-url') {
this._onDidClickDataLink(data);
}
return;
}
......@@ -595,10 +645,23 @@ ${loaderJs}
}));
}
private async _onDidClickDataLink(event: IDataLinkClickEvent): Promise<void> {
private async _onDidClickDataLink(event: IClickedDataUrlMessage): Promise<void> {
const [splitStart, splitData] = event.data.split(';base64,');
if (!splitData || !splitStart) {
return;
}
const defaultDir = dirname(this.documentUri);
const defaultUri = joinPath(defaultDir, event.downloadName || 'download.png');
let defaultName: string;
if (event.downloadName) {
defaultName = event.downloadName;
} else {
const mimeType = splitStart.replace(/^data:/, '');
const candidateExtension = mimeType && getExtensionForMimeType(mimeType);
defaultName = candidateExtension ? `download${candidateExtension}` : 'download';
}
const defaultUri = joinPath(defaultDir, defaultName);
const newFileUri = await this.fileDialogService.showSaveDialog({
defaultUri
});
......@@ -606,11 +669,6 @@ ${loaderJs}
return;
}
const splitData = event.dataURL.split(';base64,')[1];
if (!splitData) {
return;
}
const decoded = atob(splitData);
const typedArray = new Uint8Array(decoded.length);
for (let i = 0; i < decoded.length; i++) {
......
......@@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IDataLinkClickEvent, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
......@@ -26,7 +26,6 @@ export const enum WebviewMessageChannels {
doUpdateState = 'do-update-state',
doReload = 'do-reload',
loadResource = 'load-resource',
saveResource = 'save-resource',
loadLocalhost = 'load-localhost',
webviewReady = 'webview-ready',
wheel = 'did-scroll-wheel'
......@@ -137,10 +136,6 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
this.handleKeyDown(data);
}));
this._register(this.on(WebviewMessageChannels.saveResource, (event: IDataLinkClickEvent) => {
this._onDidClickDataLink.fire(event);
}));
this.style();
this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this));
}
......@@ -160,9 +155,6 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
private readonly _onDidClickLink = this._register(new Emitter<string>());
public readonly onDidClickLink = this._onDidClickLink.event;
private readonly _onDidClickDataLink = this._register(new Emitter<IDataLinkClickEvent>());
public readonly onDidClickDataLink = this._onDidClickDataLink.event;
private readonly _onDidReload = this._register(new Emitter<void>());
public readonly onDidReload = this._onDidReload.event;
......
......@@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay, IDataLinkClickEvent } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
/**
......@@ -188,9 +188,6 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv
private readonly _onDidClickLink = this._register(new Emitter<string>());
public readonly onDidClickLink: Event<string> = this._onDidClickLink.event;
private readonly _onDidClickDataLink = this._register(new Emitter<IDataLinkClickEvent>());
public readonly onDidClickDataLink: Event<IDataLinkClickEvent> = this._onDidClickDataLink.event;
private readonly _onDidReload = this._register(new Emitter<void>());
public readonly onDidReload = this._onDidReload.event;
......
......@@ -240,10 +240,6 @@
if (scrollTarget) {
scrollTarget.scrollIntoView();
}
} else if (node.href.startsWith('blob:')) {
handleBlobUrlClick(node.href, node.download);
} else if (node.href.startsWith('data:')) {
handleDataUrlClick(node.href, node.download);
} else {
host.postMessage('did-click-link', node.href.baseVal || node.href);
}
......@@ -254,31 +250,6 @@
}
};
const handleDataUrlClick = async (url, downloadName) => {
host.postMessage('save-resource', {
data: url,
downloadName
});
};
const handleBlobUrlClick = async (url, downloadName) => {
try {
const response = await fetch(url);
const blob = await response.blob();
const reader = new FileReader();
reader.addEventListener('load', () => {
const data = reader.result;
host.postMessage('save-resource', {
data,
downloadName
});
});
reader.readAsDataURL(blob);
} catch (e) {
console.error(e.message);
}
};
/**
* @param {MouseEvent} event
*/
......
......@@ -89,7 +89,6 @@ export interface Webview extends IDisposable {
readonly onDidFocus: Event<void>;
readonly onDidBlur: Event<void>;
readonly onDidClickLink: Event<string>;
readonly onDidClickDataLink: Event<IDataLinkClickEvent>;
readonly onDidScroll: Event<{ scrollYPercentage: number }>;
readonly onDidWheel: Event<IMouseWheelEvent>;
readonly onDidUpdateState: Event<string | undefined>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册