mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)
* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 * Fix breaks * Extension management fixes * Fix breaks in windows bundling * Fix/skip failing tests * Update distro * Add clear to nuget.config * Add hygiene task * Bump distro * Fix hygiene issue * Add build to hygiene exclusion * Update distro * Update hygiene * Hygiene exclusions * Update tsconfig * Bump distro for server breaks * Update build config * Update darwin path * Add done calls to notebook tests * Skip failing tests * Disable smoke tests
This commit is contained in:
@@ -10,11 +10,13 @@ import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas, RemoteAuthorities } from 'vs/base/common/network';
|
||||
import { FileAccess, RemoteAuthorities } from 'vs/base/common/network';
|
||||
import { BrowserFeatures } from 'vs/base/browser/canIUse';
|
||||
import { insane, InsaneOptions } from 'vs/base/common/insane/insane';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
||||
export function clearNode(node: HTMLElement): void {
|
||||
while (node.firstChild) {
|
||||
@@ -31,6 +33,13 @@ export function removeNode(node: HTMLElement): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function trustedInnerHTML(node: Element, value: TrustedHTML): void {
|
||||
// this is a workaround for innerHTML not allowing for "asymetric" accessors
|
||||
// see https://github.com/microsoft/vscode/issues/106396#issuecomment-692625393
|
||||
// and https://github.com/microsoft/TypeScript/issues/30024
|
||||
node.innerHTML = value as unknown as string;
|
||||
}
|
||||
|
||||
export function isInDOM(node: Node | null): boolean {
|
||||
while (node) {
|
||||
if (node === document.body) {
|
||||
@@ -517,6 +526,26 @@ export class Dimension implements IDimension {
|
||||
public readonly height: number,
|
||||
) { }
|
||||
|
||||
with(width: number = this.width, height: number = this.height): Dimension {
|
||||
if (width !== this.width || height !== this.height) {
|
||||
return new Dimension(width, height);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static is(obj: unknown): obj is IDimension {
|
||||
return typeof obj === 'object' && typeof (<IDimension>obj).height === 'number' && typeof (<IDimension>obj).width === 'number';
|
||||
}
|
||||
|
||||
static lift(obj: IDimension): Dimension {
|
||||
if (obj instanceof Dimension) {
|
||||
return obj;
|
||||
} else {
|
||||
return new Dimension(obj.width, obj.height);
|
||||
}
|
||||
}
|
||||
|
||||
static equals(a: Dimension | undefined, b: Dimension | undefined): boolean {
|
||||
if (a === b) {
|
||||
return true;
|
||||
@@ -702,15 +731,57 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b
|
||||
return false;
|
||||
}
|
||||
|
||||
const parentFlowToDataKey = 'parentFlowToElementId';
|
||||
|
||||
/**
|
||||
* Set an explicit parent to use for nodes that are not part of the
|
||||
* regular dom structure.
|
||||
*/
|
||||
export function setParentFlowTo(fromChildElement: HTMLElement, toParentElement: Element): void {
|
||||
fromChildElement.dataset[parentFlowToDataKey] = toParentElement.id;
|
||||
}
|
||||
|
||||
function getParentFlowToElement(node: HTMLElement): HTMLElement | null {
|
||||
const flowToParentId = node.dataset[parentFlowToDataKey];
|
||||
if (typeof flowToParentId === 'string') {
|
||||
return document.getElementById(flowToParentId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `testAncestor` is an ancessor of `testChild`, observing the explicit
|
||||
* parents set by `setParentFlowTo`.
|
||||
*/
|
||||
export function isAncestorUsingFlowTo(testChild: Node, testAncestor: Node): boolean {
|
||||
let node: Node | null = testChild;
|
||||
while (node) {
|
||||
if (node === testAncestor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node instanceof HTMLElement) {
|
||||
const flowToParentElement = getParentFlowToElement(node);
|
||||
if (flowToParentElement) {
|
||||
node = flowToParentElement;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null {
|
||||
while (node && node.nodeType === node.ELEMENT_NODE) {
|
||||
if (hasClass(node, clazz)) {
|
||||
if (node.classList.contains(clazz)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (stopAtClazzOrNode) {
|
||||
if (typeof stopAtClazzOrNode === 'string') {
|
||||
if (hasClass(node, stopAtClazzOrNode)) {
|
||||
if (node.classList.contains(stopAtClazzOrNode)) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
@@ -1015,8 +1086,15 @@ export function prepend<T extends Node>(parent: HTMLElement, child: T): T {
|
||||
/**
|
||||
* Removes all children from `parent` and appends `children`
|
||||
*/
|
||||
export function reset(parent: HTMLElement, ...children: Array<Node | string>) {
|
||||
export function reset(parent: HTMLElement, ...children: Array<Node | string>): void {
|
||||
parent.innerText = '';
|
||||
appendChildren(parent, ...children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends `children` to `parent`
|
||||
*/
|
||||
export function appendChildren(parent: HTMLElement, ...children: Array<Node | string>): void {
|
||||
for (const child of children) {
|
||||
if (child instanceof Node) {
|
||||
parent.appendChild(child);
|
||||
@@ -1196,7 +1274,7 @@ export function computeScreenAwareSize(cssPx: number): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://github.com/Microsoft/monaco-editor/issues/601
|
||||
* See https://github.com/microsoft/monaco-editor/issues/601
|
||||
* To protect against malicious code in the linked site, particularly phishing attempts,
|
||||
* the window.opener should be set to null to prevent the linked site from having access
|
||||
* to change the location of the current page.
|
||||
@@ -1205,7 +1283,7 @@ export function computeScreenAwareSize(cssPx: number): number {
|
||||
export function windowOpenNoOpener(url: string): void {
|
||||
if (platform.isNative || browser.isEdgeWebView) {
|
||||
// In VSCode, window.open() always returns null...
|
||||
// The same is true for a WebView (see https://github.com/Microsoft/monaco-editor/issues/628)
|
||||
// The same is true for a WebView (see https://github.com/microsoft/monaco-editor/issues/628)
|
||||
window.open(url);
|
||||
} else {
|
||||
let newTab = window.open();
|
||||
@@ -1228,16 +1306,6 @@ export function animate(fn: () => void): IDisposable {
|
||||
|
||||
RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? 'https' : 'http');
|
||||
|
||||
export function asDomUri(uri: URI): URI {
|
||||
if (!uri) {
|
||||
return uri;
|
||||
}
|
||||
if (Schemas.vscodeRemote === uri.scheme) {
|
||||
return RemoteAuthorities.rewrite(uri);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns url('...')
|
||||
*/
|
||||
@@ -1245,10 +1313,9 @@ export function asCSSUrl(uri: URI): string {
|
||||
if (!uri) {
|
||||
return `url('')`;
|
||||
}
|
||||
return `url('${asDomUri(uri).toString(true).replace(/'/g, '%27')}')`;
|
||||
return `url('${FileAccess.asBrowserUri(uri).toString(true).replace(/'/g, '%27')}')`;
|
||||
}
|
||||
|
||||
|
||||
export function triggerDownload(dataOrUri: Uint8Array | URI, name: string): void {
|
||||
|
||||
// If the data is provided as Buffer, we create a
|
||||
@@ -1340,3 +1407,261 @@ export function detectFullscreen(): IDetectedFullscreen | null {
|
||||
// Not in fullscreen
|
||||
return null;
|
||||
}
|
||||
|
||||
// -- sanitize and trusted html
|
||||
|
||||
function _extInsaneOptions(opts: InsaneOptions, allowedAttributesForAll: string[]): InsaneOptions {
|
||||
|
||||
let allowedAttributes: Record<string, string[]> = opts.allowedAttributes ?? {};
|
||||
|
||||
if (opts.allowedTags) {
|
||||
for (let tag of opts.allowedTags) {
|
||||
let array = allowedAttributes[tag];
|
||||
if (!array) {
|
||||
array = allowedAttributesForAll;
|
||||
} else {
|
||||
array = array.concat(allowedAttributesForAll);
|
||||
}
|
||||
allowedAttributes[tag] = array;
|
||||
}
|
||||
}
|
||||
|
||||
return { ...opts, allowedAttributes };
|
||||
}
|
||||
|
||||
const _ttpSafeInnerHtml = window.trustedTypes?.createPolicy('safeInnerHtml', {
|
||||
createHTML(value, options: InsaneOptions) {
|
||||
return insane(value, options);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Sanitizes the given `value` and reset the given `node` with it.
|
||||
*/
|
||||
export function safeInnerHtml(node: HTMLElement, value: string): void {
|
||||
|
||||
const options = _extInsaneOptions({
|
||||
allowedTags: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
|
||||
allowedAttributes: {
|
||||
'a': ['href', 'x-dispatch'],
|
||||
'button': ['data-href', 'x-dispatch'],
|
||||
'input': ['type', 'placeholder', 'checked', 'required'],
|
||||
'label': ['for'],
|
||||
'select': ['required'],
|
||||
'span': ['data-command', 'role'],
|
||||
'textarea': ['name', 'placeholder', 'required'],
|
||||
},
|
||||
allowedSchemes: ['http', 'https', 'command']
|
||||
}, ['class', 'id', 'role', 'tabindex']);
|
||||
|
||||
const html = _ttpSafeInnerHtml?.createHTML(value, options) ?? insane(value, options);
|
||||
node.innerHTML = html as unknown as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Unicode string to a string in which each 16-bit unit occupies only one byte
|
||||
*
|
||||
* From https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa
|
||||
*/
|
||||
function toBinary(str: string): string {
|
||||
const codeUnits = new Uint16Array(str.length);
|
||||
for (let i = 0; i < codeUnits.length; i++) {
|
||||
codeUnits[i] = str.charCodeAt(i);
|
||||
}
|
||||
return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of the global `btoa` function that handles multi-byte characters instead
|
||||
* of throwing an exception.
|
||||
*/
|
||||
export function multibyteAwareBtoa(str: string): string {
|
||||
return btoa(toBinary(str));
|
||||
}
|
||||
|
||||
/**
|
||||
* Typings for the https://wicg.github.io/file-system-access
|
||||
*
|
||||
* Use `supported(window)` to find out if the browser supports this kind of API.
|
||||
*/
|
||||
export namespace WebFileSystemAccess {
|
||||
|
||||
// https://wicg.github.io/file-system-access/#dom-window-showdirectorypicker
|
||||
export interface FileSystemAccess {
|
||||
showDirectoryPicker: () => Promise<FileSystemDirectoryHandle>;
|
||||
}
|
||||
|
||||
// https://wicg.github.io/file-system-access/#api-filesystemdirectoryhandle
|
||||
export interface FileSystemDirectoryHandle {
|
||||
readonly kind: 'directory',
|
||||
readonly name: string,
|
||||
|
||||
getFileHandle: (name: string, options?: { create?: boolean }) => Promise<FileSystemFileHandle>;
|
||||
getDirectoryHandle: (name: string, options?: { create?: boolean }) => Promise<FileSystemDirectoryHandle>;
|
||||
}
|
||||
|
||||
// https://wicg.github.io/file-system-access/#api-filesystemfilehandle
|
||||
export interface FileSystemFileHandle {
|
||||
readonly kind: 'file',
|
||||
readonly name: string,
|
||||
|
||||
createWritable: (options?: { keepExistingData?: boolean }) => Promise<FileSystemWritableFileStream>;
|
||||
}
|
||||
|
||||
// https://wicg.github.io/file-system-access/#api-filesystemwritablefilestream
|
||||
export interface FileSystemWritableFileStream {
|
||||
write: (buffer: Uint8Array) => Promise<void>;
|
||||
close: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function supported(obj: any & Window): obj is FileSystemAccess {
|
||||
const candidate = obj as FileSystemAccess;
|
||||
if (typeof candidate?.showDirectoryPicker === 'function') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
type ModifierKey = 'alt' | 'ctrl' | 'shift' | 'meta';
|
||||
|
||||
export interface IModifierKeyStatus {
|
||||
altKey: boolean;
|
||||
shiftKey: boolean;
|
||||
ctrlKey: boolean;
|
||||
metaKey: boolean;
|
||||
lastKeyPressed?: ModifierKey;
|
||||
lastKeyReleased?: ModifierKey;
|
||||
event?: KeyboardEvent;
|
||||
}
|
||||
|
||||
export class ModifierKeyEmitter extends Emitter<IModifierKeyStatus> {
|
||||
|
||||
private readonly _subscriptions = new DisposableStore();
|
||||
private _keyStatus: IModifierKeyStatus;
|
||||
private static instance: ModifierKeyEmitter;
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
|
||||
this._keyStatus = {
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
|
||||
if (e.altKey && !this._keyStatus.altKey) {
|
||||
this._keyStatus.lastKeyPressed = 'alt';
|
||||
} else if (e.ctrlKey && !this._keyStatus.ctrlKey) {
|
||||
this._keyStatus.lastKeyPressed = 'ctrl';
|
||||
} else if (e.metaKey && !this._keyStatus.metaKey) {
|
||||
this._keyStatus.lastKeyPressed = 'meta';
|
||||
} else if (e.shiftKey && !this._keyStatus.shiftKey) {
|
||||
this._keyStatus.lastKeyPressed = 'shift';
|
||||
} else if (event.keyCode !== KeyCode.Alt) {
|
||||
this._keyStatus.lastKeyPressed = undefined;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._keyStatus.altKey = e.altKey;
|
||||
this._keyStatus.ctrlKey = e.ctrlKey;
|
||||
this._keyStatus.metaKey = e.metaKey;
|
||||
this._keyStatus.shiftKey = e.shiftKey;
|
||||
|
||||
if (this._keyStatus.lastKeyPressed) {
|
||||
this._keyStatus.event = e;
|
||||
this.fire(this._keyStatus);
|
||||
}
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'keyup', true)(e => {
|
||||
if (!e.altKey && this._keyStatus.altKey) {
|
||||
this._keyStatus.lastKeyReleased = 'alt';
|
||||
} else if (!e.ctrlKey && this._keyStatus.ctrlKey) {
|
||||
this._keyStatus.lastKeyReleased = 'ctrl';
|
||||
} else if (!e.metaKey && this._keyStatus.metaKey) {
|
||||
this._keyStatus.lastKeyReleased = 'meta';
|
||||
} else if (!e.shiftKey && this._keyStatus.shiftKey) {
|
||||
this._keyStatus.lastKeyReleased = 'shift';
|
||||
} else {
|
||||
this._keyStatus.lastKeyReleased = undefined;
|
||||
}
|
||||
|
||||
if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) {
|
||||
this._keyStatus.lastKeyPressed = undefined;
|
||||
}
|
||||
|
||||
this._keyStatus.altKey = e.altKey;
|
||||
this._keyStatus.ctrlKey = e.ctrlKey;
|
||||
this._keyStatus.metaKey = e.metaKey;
|
||||
this._keyStatus.shiftKey = e.shiftKey;
|
||||
|
||||
if (this._keyStatus.lastKeyReleased) {
|
||||
this._keyStatus.event = e;
|
||||
this.fire(this._keyStatus);
|
||||
}
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'mousedown', true)(e => {
|
||||
this._keyStatus.lastKeyPressed = undefined;
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'mouseup', true)(e => {
|
||||
this._keyStatus.lastKeyPressed = undefined;
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(document.body, 'mousemove', true)(e => {
|
||||
if (e.buttons) {
|
||||
this._keyStatus.lastKeyPressed = undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
this._subscriptions.add(domEvent(window, 'blur')(e => {
|
||||
this.resetKeyStatus();
|
||||
}));
|
||||
}
|
||||
|
||||
get keyStatus(): IModifierKeyStatus {
|
||||
return this._keyStatus;
|
||||
}
|
||||
|
||||
get isModifierPressed(): boolean {
|
||||
return this._keyStatus.altKey || this._keyStatus.ctrlKey || this._keyStatus.metaKey || this._keyStatus.shiftKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to explicitly reset the key status based on more knowledge (#109062)
|
||||
*/
|
||||
resetKeyStatus(): void {
|
||||
this.doResetKeyStatus();
|
||||
this.fire(this._keyStatus);
|
||||
}
|
||||
|
||||
private doResetKeyStatus(): void {
|
||||
this._keyStatus = {
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
metaKey: false
|
||||
};
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (!ModifierKeyEmitter.instance) {
|
||||
ModifierKeyEmitter.instance = new ModifierKeyEmitter();
|
||||
}
|
||||
|
||||
return ModifierKeyEmitter.instance;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
this._subscriptions.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user