<commit_before>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import type { Event } from 'vs/base/common/event';
import type { IDisposable } from 'vs/base/common/lifecycle';
import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import type { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, IDimensionMessage, IClickMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage, IOutputFocusMessage, IOutputBlurMessage, DimensionUpdate, IContextMenuMarkdownPreviewMessage, ITelemetryFoundRenderedMarkdownMath, ITelemetryFoundUnrenderedMarkdownMath, IMarkdownCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';

// !! IMPORTANT !! everything must be in-line within the webviewPreloads
// function. Imports are not allowed. This is stringified and injected into
// the webview.

declare module globalThis {
	const acquireVsCodeApi: () => ({
		getState(): { [key: string]: unknown; };
		setState(data: { [key: string]: unknown; }): void;
		postMessage: (msg: unknown) => void;
	});
}

declare class ResizeObserver {
	constructor(onChange: (entries: { target: HTMLElement, contentRect?: ClientRect; }[]) => void);
	observe(element: Element): void;
	disconnect(): void;
}


type Listener<T> = { fn: (evt: T) => void; thisArg: unknown; };

interface EmitterLike<T> {
	fire(data: T): void;
	event: Event<T>;
}

interface PreloadStyles {
	readonly outputNodePadding: number;
	readonly outputNodeLeftPadding: number;
}

declare function __import(path: string): Promise<any>;

async function webviewPreloads(style: PreloadStyles, rendererData: readonly RendererMetadata[]) {
	const acquireVsCodeApi = globalThis.acquireVsCodeApi;
	const vscode = acquireVsCodeApi();
	delete (globalThis as any).acquireVsCodeApi;

	const handleInnerClick = (event: MouseEvent) => {
		if (!event || !event.view || !event.view.document) {
			return;
		}

		for (const node of event.composedPath()) {
			if (node instanceof HTMLAnchorElement && node.href) {
				if (node.href.startsWith('blob:')) {
					handleBlobUrlClick(node.href, node.download);
				} else if (node.href.startsWith('data:')) {
					handleDataUrl(node.href, node.download);
				}
				event.preventDefault();
				return;
			}
		}
	};

	const handleDataUrl = async (data: string | ArrayBuffer | null, downloadName: string) => {
		postNotebookMessage<IClickedDataUrlMessage>('clicked-data-url', {
			data,
			downloadName
		});
	};

	const handleBlobUrlClick = async (url: string, downloadName: string) => {
		try {
			const response = await fetch(url);
			const blob = await response.blob();
			const reader = new FileReader();
			reader.addEventListener('load', () => {
				handleDataUrl(reader.result, downloadName);
			});
			reader.readAsDataURL(blob);
		} catch (e) {
			console.error(e.message);
		}
	};

	document.body.addEventListener('click', handleInnerClick);

	const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [
		'type', 'src', 'nonce', 'noModule', 'async',
	];

	// derived from https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/core/DOMEval.js
	const domEval = (container: Element) => {
		const arr = Array.from(container.getElementsByTagName('script'));
		for (let n = 0; n < arr.length; n++) {
			const node = arr[n];
			const scriptTag = document.createElement('script');
			const trustedScript = ttPolicy?.createScript(node.innerText) ?? node.innerText;
			scriptTag.text = trustedScript as string;
			for (const key of preservedScriptAttributes) {
				const val = node[key] || node.getAttribute && node.getAttribute(key);
				if (val) {
					scriptTag.setAttribute(key, val as any);
				}
			}

			// TODO@connor4312: should script with src not be removed?
			container.appendChild(scriptTag).parentNode!.removeChild(scriptTag);
		}
	};

	async function loadScriptSource(url: string, originalUri = url): Promise<string> {
		const res = await fetch(url);
		const text = await res.text();
		if (!res.ok) {
			throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`);
		}

		return text;
	}

	interface RendererContext {
		getState<T>(): T | undefined;
		setState<T>(newState: T): void;

		getRenderer(id: string): any | undefined;
	}

	function createRendererContext(rendererId: string): RendererContext {
		return {
			setState: newState => vscode.setState({ ...vscode.getState(), [rendererId]: newState }),
			getState: <T>() => {
				const state = vscode.getState();
				return typeof state === 'object' && state ? state[rendererId] as T : undefined;
			},
			getRenderer: (id: string) => renderers.getRenderer(id),
		};
	}

	interface ScriptModule {
		activate: (ctx?: RendererContext) => any;
	}

	const invokeSourceWithGlobals = (functionSrc: string, globals: { [name: string]: unknown }) => {
		const args = Object.entries(globals);
		return new Function(...args.map(([k]) => k), functionSrc)(...args.map(([, v]) => v));
	};

	const runPreload = async (url: string, originalUri: string): Promise<ScriptModule> => {
		const text = await loadScriptSource(url, originalUri);
		return {
			activate: () => {
				return invokeSourceWithGlobals(text, kernelPreloadGlobals);
			}
		};
	};

	const runRenderScript = async (url: string, rendererId: string): Promise<ScriptModule> => {
		const text = await loadScriptSource(url);
		// TODO: Support both the new module based renderers and the old style global renderers
		const isModule = /\bexport\b.*\bactivate\b/.test(text);
		if (isModule) {
			return __import(url);
		} else {
			return createBackCompatModule(rendererId, text);
		}
	};

	const createBackCompatModule = (rendererId: string, scriptText: string): ScriptModule => ({
		activate: (): RendererApi => {
			const onDidCreateOutput = createEmitter<ICreateCellInfo>();
			const onWillDestroyOutput = createEmitter<undefined | IDestroyCellInfo>();

			const globals = {
				acquireNotebookRendererApi: <T>(): GlobalNotebookRendererApi<T> => ({
					onDidCreateOutput: onDidCreateOutput.event,
					onWillDestroyOutput: onWillDestroyOutput.event,
					setState: newState => vscode.setState({ ...vscode.getState(), [rendererId]: newState }),
					getState: () => {
						const state = vscode.getState();
						return typeof state === 'object' && state ? state[rendererId] as T : undefined;
					},
				}),
			};

			invokeSourceWithGlobals(scriptText, globals);

			return {
				renderCell(id, context) {
					onDidCreateOutput.fire({ ...context, outputId: id });
				},
				destroyCell(id) {
					onWillDestroyOutput.fire(id ? { outputId: id } : undefined);
				}
			};
		}
	});

	const dimensionUpdater = new class {
		private readonly pending = new Map<string, DimensionUpdate>();

		update(id: string, height: number, options: { init?: boolean; isOutput?: boolean }) {
			if (!this.pending.size) {<commit_msg>notebook: include script url in back compat patch<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import type { Event } from 'vs/base/common/event';
import type { IDisposable } from 'vs/base/common/lifecycle';
import { RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import type { FromWebviewMessage, IBlurOutputMessage, ICellDropMessage, ICellDragMessage, ICellDragStartMessage, IClickedDataUrlMessage, IDimensionMessage, IClickMarkdownPreviewMessage, IMouseEnterMarkdownPreviewMessage, IMouseEnterMessage, IMouseLeaveMarkdownPreviewMessage, IMouseLeaveMessage, IToggleMarkdownPreviewMessage, IWheelMessage, ToWebviewMessage, ICellDragEndMessage, IOutputFocusMessage, IOutputBlurMessage, DimensionUpdate, IContextMenuMarkdownPreviewMessage, ITelemetryFoundRenderedMarkdownMath, ITelemetryFoundUnrenderedMarkdownMath, IMarkdownCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';

// !! IMPORTANT !! everything must be in-line within the webviewPreloads
// function. Imports are not allowed. This is stringified and injected into
// the webview.

declare module globalThis {
	const acquireVsCodeApi: () => ({
		getState(): { [key: string]: unknown; };
		setState(data: { [key: string]: unknown; }): void;
		postMessage: (msg: unknown) => void;
	});
}

declare class ResizeObserver {
	constructor(onChange: (entries: { target: HTMLElement, contentRect?: ClientRect; }[]) => void);
	observe(element: Element): void;
	disconnect(): void;
}


type Listener<T> = { fn: (evt: T) => void; thisArg: unknown; };

interface EmitterLike<T> {
	fire(data: T): void;
	event: Event<T>;
}

interface PreloadStyles {
	readonly outputNodePadding: number;
	readonly outputNodeLeftPadding: number;
}

declare function __import(path: string): Promise<any>;

async function webviewPreloads(style: PreloadStyles, rendererData: readonly RendererMetadata[]) {
	const acquireVsCodeApi = globalThis.acquireVsCodeApi;
	const vscode = acquireVsCodeApi();
	delete (globalThis as any).acquireVsCodeApi;

	const handleInnerClick = (event: MouseEvent) => {
		if (!event || !event.view || !event.view.document) {
			return;
		}

		for (const node of event.composedPath()) {
			if (node instanceof HTMLAnchorElement && node.href) {
				if (node.href.startsWith('blob:')) {
					handleBlobUrlClick(node.href, node.download);
				} else if (node.href.startsWith('data:')) {
					handleDataUrl(node.href, node.download);
				}
				event.preventDefault();
				return;
			}
		}
	};

	const handleDataUrl = async (data: string | ArrayBuffer | null, downloadName: string) => {
		postNotebookMessage<IClickedDataUrlMessage>('clicked-data-url', {
			data,
			downloadName
		});
	};

	const handleBlobUrlClick = async (url: string, downloadName: string) => {
		try {
			const response = await fetch(url);
			const blob = await response.blob();
			const reader = new FileReader();
			reader.addEventListener('load', () => {
				handleDataUrl(reader.result, downloadName);
			});
			reader.readAsDataURL(blob);
		} catch (e) {
			console.error(e.message);
		}
	};

	document.body.addEventListener('click', handleInnerClick);

	const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [
		'type', 'src', 'nonce', 'noModule', 'async',
	];

	// derived from https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/core/DOMEval.js
	const domEval = (container: Element) => {
		const arr = Array.from(container.getElementsByTagName('script'));
		for (let n = 0; n < arr.length; n++) {
			const node = arr[n];
			const scriptTag = document.createElement('script');
			const trustedScript = ttPolicy?.createScript(node.innerText) ?? node.innerText;
			scriptTag.text = trustedScript as string;
			for (const key of preservedScriptAttributes) {
				const val = node[key] || node.getAttribute && node.getAttribute(key);
				if (val) {
					scriptTag.setAttribute(key, val as any);
				}
			}

			// TODO@connor4312: should script with src not be removed?
			container.appendChild(scriptTag).parentNode!.removeChild(scriptTag);
		}
	};

	async function loadScriptSource(url: string, originalUri = url): Promise<string> {
		const res = await fetch(url);
		const text = await res.text();
		if (!res.ok) {
			throw new Error(`Unexpected ${res.status} requesting ${originalUri}: ${text || res.statusText}`);
		}

		return text;
	}

	interface RendererContext {
		getState<T>(): T | undefined;
		setState<T>(newState: T): void;

		getRenderer(id: string): any | undefined;
	}

	function createRendererContext(rendererId: string): RendererContext {
		return {
			setState: newState => vscode.setState({ ...vscode.getState(), [rendererId]: newState }),
			getState: <T>() => {
				const state = vscode.getState();
				return typeof state === 'object' && state ? state[rendererId] as T : undefined;
			},
			getRenderer: (id: string) => renderers.getRenderer(id),
		};
	}

	interface ScriptModule {
		activate: (ctx?: RendererContext) => any;
	}

	const invokeSourceWithGlobals = (functionSrc: string, globals: { [name: string]: unknown }) => {
		const args = Object.entries(globals);
		return new Function(...args.map(([k]) => k), functionSrc)(...args.map(([, v]) => v));
	};

	const runPreload = async (url: string, originalUri: string): Promise<ScriptModule> => {
		const text = await loadScriptSource(url, originalUri);
		return {
			activate: () => {
				return invokeSourceWithGlobals(text, kernelPreloadGlobals);
			}
		};
	};

	const runRenderScript = async (url: string, rendererId: string): Promise<ScriptModule> => {
		const text = await loadScriptSource(url);
		// TODO: Support both the new module based renderers and the old style global renderers
		const isModule = /\bexport\b.*\bactivate\b/.test(text);
		if (isModule) {
			return __import(url);
		} else {
			return createBackCompatModule(rendererId, url, text);
		}
	};

	const createBackCompatModule = (rendererId: string, scriptUrl: string, scriptText: string): ScriptModule => ({
		activate: (): RendererApi => {
			const onDidCreateOutput = createEmitter<ICreateCellInfo>();
			const onWillDestroyOutput = createEmitter<undefined | IDestroyCellInfo>();

			const globals = {
				scriptUrl,
				acquireNotebookRendererApi: <T>(): GlobalNotebookRendererApi<T> => ({
					onDidCreateOutput: onDidCreateOutput.event,
					onWillDestroyOutput: onWillDestroyOutput.event,
					setState: newState => vscode.setState({ ...vscode.getState(), [rendererId]: newState }),
					getState: () => {
						const state = vscode.getState();
						return typeof state === 'object' && state ? state[rendererId] as T : undefined;
					},
				}),
			};

			invokeSourceWithGlobals(scriptText, globals);

			return {
				renderCell(id, context) {
					onDidCreateOutput.fire({ ...context, outputId: id });
				},
				destroyCell(id) {
					onWillDestroyOutput.fire(id ? { outputId: id } : undefined);
				}
			};
		}
	});

	const dimensionUpdater = new class {
		private readonly pending = new Map<string, DimensionUpdate>();

		update(id: string, height: number, options: { init?: boolean; isOutput?: boolean }) {
			if (!this.pending.size) {
{'fraction_non_alphanumeric': 0.15562445238255712, 'fraction_numerical': 0.0037743479139987868, 'mean_word_length': 4.029830508474577, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 83, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2279264', 'n_tokens_mistral': 4909, 'n_tokens_neox': 4423, 'n_words': 1059}