Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@
"selector": "BinaryExpression[operator='instanceof'][right.name='MouseEvent']",
"message": "Use DOM.isMouseEvent() to support multi-window scenarios."
},
{
"selector": "BinaryExpression[operator='instanceof'][right.name=/^HTML\\w+/]",
"message": "Use DOM.isHTMLElement() and related methods to support multi-window scenarios."
},
{
"selector": "BinaryExpression[operator='instanceof'][right.name='KeyboardEvent']",
"message": "Use DOM.isKeyboardEvent() to support multi-window scenarios."
Expand Down
47 changes: 41 additions & 6 deletions src/vs/base/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ export function isAncestorUsingFlowTo(testChild: Node, testAncestor: Node): bool
return true;
}

if (node instanceof HTMLElement) {
if (isHTMLElement(node)) {
const flowToParentElement = getParentFlowToElement(node);
if (flowToParentElement) {
node = flowToParentElement;
Expand Down Expand Up @@ -1148,6 +1148,41 @@ function isCSSStyleRule(rule: CSSRule): rule is CSSStyleRule {
return typeof (rule as CSSStyleRule).selectorText === 'string';
}

export function isHTMLElement(e: unknown): e is HTMLElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLElement || e instanceof getWindow(e as Node).HTMLElement;
}

export function isHTMLAnchorElement(e: unknown): e is HTMLAnchorElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLAnchorElement || e instanceof getWindow(e as Node).HTMLAnchorElement;
}

export function isHTMLSpanElement(e: unknown): e is HTMLSpanElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLSpanElement || e instanceof getWindow(e as Node).HTMLSpanElement;
}

export function isHTMLTextAreaElement(e: unknown): e is HTMLTextAreaElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLTextAreaElement || e instanceof getWindow(e as Node).HTMLTextAreaElement;
}

export function isHTMLInputElement(e: unknown): e is HTMLInputElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLInputElement || e instanceof getWindow(e as Node).HTMLInputElement;
}

export function isHTMLButtonElement(e: unknown): e is HTMLButtonElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLButtonElement || e instanceof getWindow(e as Node).HTMLButtonElement;
}

export function isHTMLDivElement(e: unknown): e is HTMLDivElement {
// eslint-disable-next-line no-restricted-syntax
return e instanceof HTMLDivElement || e instanceof getWindow(e as Node).HTMLDivElement;
}

export function isMouseEvent(e: unknown): e is MouseEvent {
// eslint-disable-next-line no-restricted-syntax
return e instanceof MouseEvent || e instanceof getWindow(e as UIEvent).MouseEvent;
Expand Down Expand Up @@ -1286,7 +1321,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
private _refreshStateHandler: () => void;

private static hasFocusWithin(element: HTMLElement | Window): boolean {
if (element instanceof HTMLElement) {
if (isHTMLElement(element)) {
const shadowRoot = getShadowRoot(element);
const activeElement = (shadowRoot ? shadowRoot.activeElement : element.ownerDocument.activeElement);
return isAncestor(activeElement, element);
Expand All @@ -1312,7 +1347,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
const onBlur = () => {
if (hasFocus) {
loosingFocus = true;
(element instanceof HTMLElement ? getWindow(element) : element).setTimeout(() => {
(isHTMLElement(element) ? getWindow(element) : element).setTimeout(() => {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
Expand All @@ -1335,7 +1370,7 @@ class FocusTracker extends Disposable implements IFocusTracker {

this._register(addDisposableListener(element, EventType.FOCUS, onFocus, true));
this._register(addDisposableListener(element, EventType.BLUR, onBlur, true));
if (element instanceof HTMLElement) {
if (isHTMLElement(element)) {
this._register(addDisposableListener(element, EventType.FOCUS_IN, () => this._refreshStateHandler()));
this._register(addDisposableListener(element, EventType.FOCUS_OUT, () => this._refreshStateHandler()));
}
Expand Down Expand Up @@ -1488,7 +1523,7 @@ export function hide(...elements: HTMLElement[]): void {

function findParentWithAttribute(node: Node | null, attribute: string): HTMLElement | null {
while (node && node.nodeType === node.ELEMENT_NODE) {
if (node instanceof HTMLElement && node.hasAttribute(attribute)) {
if (isHTMLElement(node) && node.hasAttribute(attribute)) {
return node;
}

Expand Down Expand Up @@ -2304,7 +2339,7 @@ export function h(tag: string, ...args: [] | [attributes: { $: string } & Partia

if (children) {
for (const c of children) {
if (c instanceof HTMLElement) {
if (isHTMLElement(c)) {
el.appendChild(c);
} else if (typeof c === 'string') {
el.append(c);
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/actionbar/actionbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ export class ActionBar extends Disposable implements IActionRunner {
}

// by element
if (indexOrElement instanceof HTMLElement) {
if (DOM.isHTMLElement(indexOrElement)) {
while (indexOrElement.parentElement !== this.actionsList) {
if (!indexOrElement.parentElement) {
return undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/contextview/contextview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export class ContextView extends Disposable {
let around: IView;

// Get the element's position and size (to anchor the view)
if (anchor instanceof HTMLElement) {
if (DOM.isHTMLElement(anchor)) {
const elementPosition = DOM.getDomNodePagePosition(anchor);

// In areas where zoom is applied to the element or its ancestors, we need to adjust the size of the element
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/iconLabel/iconLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class IconLabel extends Disposable {
const existingIconNode = this.domNode.element.querySelector('.monaco-icon-label-iconpath');
if (options?.iconPath) {
let iconNode;
if (!existingIconNode || !(existingIconNode instanceof HTMLElement)) {
if (!existingIconNode || !(dom.isHTMLElement(existingIconNode))) {
iconNode = dom.$('.monaco-icon-label-iconpath');
this.domNode.element.prepend(iconNode);
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/list/listView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, isAncestor, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
Expand Down Expand Up @@ -1384,7 +1384,7 @@ export class ListView<T> implements IListView<T> {
const scrollableElement = this.scrollableElement.getDomNode();
let element: HTMLElement | null = target as (HTMLElement | null);

while (element instanceof HTMLElement && element !== this.rowsContainer && scrollableElement.contains(element)) {
while (isHTMLElement(element) && element !== this.rowsContainer && scrollableElement.contains(element)) {
const rawIndex = element.getAttribute('data-index');

if (rawIndex) {
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/list/listWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { IDragAndDropData } from 'vs/base/browser/dnd';
import { asCssValueWithDefault, createStyleSheet, Dimension, EventHelper, getActiveElement, getWindow, isActiveElement, isMouseEvent } from 'vs/base/browser/dom';
import { asCssValueWithDefault, createStyleSheet, Dimension, EventHelper, getActiveElement, getWindow, isActiveElement, isHTMLElement, isMouseEvent } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Gesture } from 'vs/base/browser/touch';
Expand Down Expand Up @@ -635,7 +635,7 @@ class DOMFocusController<T> implements IDisposable {

const tabIndexElement = focusedDomElement.querySelector('[tabIndex]');

if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.tabIndex === -1) {
if (!tabIndexElement || !(isHTMLElement(tabIndexElement)) || tabIndexElement.tabIndex === -1) {
return;
}

Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/sash/sash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { $, append, createStyleSheet, EventHelper, EventLike, getWindow } from 'vs/base/browser/dom';
import { $, append, createStyleSheet, EventHelper, EventLike, getWindow, isHTMLElement } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { EventType, Gesture } from 'vs/base/browser/touch';
import { Delayer } from 'vs/base/common/async';
Expand Down Expand Up @@ -670,7 +670,7 @@ export class Sash extends Disposable {
private getOrthogonalSash(e: PointerEvent): Sash | undefined {
const target = e.initialTarget ?? e.target;

if (!target || !(target instanceof HTMLElement)) {
if (!target || !(isHTMLElement(target))) {
return undefined;
}

Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/splitview/paneview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { isFirefox } from 'vs/base/browser/browser';
import { DataTransfers } from 'vs/base/browser/dnd';
import { $, addDisposableListener, append, clearNode, EventHelper, EventType, getWindow, trackFocus } from 'vs/base/browser/dom';
import { $, addDisposableListener, append, clearNode, EventHelper, EventType, getWindow, isHTMLElement, trackFocus } from 'vs/base/browser/dom';
import { DomEmitter } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Gesture, EventType as TouchEventType } from 'vs/base/browser/touch';
Expand Down Expand Up @@ -513,7 +513,7 @@ export class PaneView extends Disposable {

const eventDisposables = this._register(new DisposableStore());
const onKeyDown = this._register(new DomEmitter(this.element, 'keydown'));
const onHeaderKeyDown = Event.map(Event.filter(onKeyDown.event, e => e.target instanceof HTMLElement && e.target.classList.contains('pane-header'), eventDisposables), e => new StandardKeyboardEvent(e), eventDisposables);
const onHeaderKeyDown = Event.map(Event.filter(onKeyDown.event, e => isHTMLElement(e.target) && e.target.classList.contains('pane-header'), eventDisposables), e => new StandardKeyboardEvent(e), eventDisposables);

this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.UpArrow, eventDisposables)(() => this.focusPrevious()));
this._register(Event.filter(onHeaderKeyDown, e => e.keyCode === KeyCode.DownArrow, eventDisposables)(() => this.focusNext()));
Expand Down
14 changes: 7 additions & 7 deletions src/vs/base/test/browser/dom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
import { $, asCssValueWithDefault, h, multibyteAwareBtoa, trackAttributes, copyAttributes, disposableWindowInterval, getWindows, getWindowsCount, getWindowId, getWindowById, hasWindow, getWindow, getDocument } from 'vs/base/browser/dom';
import { $, asCssValueWithDefault, h, multibyteAwareBtoa, trackAttributes, copyAttributes, disposableWindowInterval, getWindows, getWindowsCount, getWindowId, getWindowById, hasWindow, getWindow, getDocument, isHTMLElement } from 'vs/base/browser/dom';
import { ensureCodeWindow, isAuxiliaryWindow, mainWindow } from 'vs/base/browser/window';
import { DeferredPromise, timeout } from 'vs/base/common/async';
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
Expand Down Expand Up @@ -85,23 +85,23 @@ suite('dom', () => {
test('should build simple nodes', () => {
const div = $('div');
assert(div);
assert(div instanceof HTMLElement);
assert(isHTMLElement(div));
assert.strictEqual(div.tagName, 'DIV');
assert(!div.firstChild);
});

test('should build nodes with id', () => {
const div = $('div#foo');
assert(div);
assert(div instanceof HTMLElement);
assert(isHTMLElement(div));
assert.strictEqual(div.tagName, 'DIV');
assert.strictEqual(div.id, 'foo');
});

test('should build nodes with class-name', () => {
const div = $('div.foo');
assert(div);
assert(div instanceof HTMLElement);
assert(isHTMLElement(div));
assert.strictEqual(div.tagName, 'DIV');
assert.strictEqual(div.className, 'foo');
});
Expand Down Expand Up @@ -136,15 +136,15 @@ suite('dom', () => {
suite('h', () => {
test('should build simple nodes', () => {
const div = h('div');
assert(div.root instanceof HTMLElement);
assert(isHTMLElement(div.root));
assert.strictEqual(div.root.tagName, 'DIV');

const span = h('span');
assert(span.root instanceof HTMLElement);
assert(isHTMLElement(span.root));
assert.strictEqual(span.root.tagName, 'SPAN');

const img = h('img');
assert(img.root instanceof HTMLElement);
assert(isHTMLElement(img.root));
assert.strictEqual(img.root.tagName, 'IMG');
});

Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/test/browser/iconLabels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
import { isHTMLElement } from 'vs/base/browser/dom';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';

Expand Down Expand Up @@ -47,7 +48,7 @@ suite('renderLabelWithIcons', () => {

const elementsToString = (elements: Array<HTMLElement | string>): string => {
return elements
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
.map(elem => isHTMLElement(elem) ? elem.outerHTML : elem)
.reduce((a, b) => a + b, '');
};

Expand Down
8 changes: 4 additions & 4 deletions src/vs/editor/browser/services/hoverService/hoverService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { HoverWidget } from 'vs/editor/browser/services/hoverService/hoverWidget';
import { IContextViewProvider, IDelegate } from 'vs/base/browser/ui/contextview/contextview';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { addDisposableListener, EventType, getActiveElement, isAncestorOfActiveElement, isAncestor, getWindow } from 'vs/base/browser/dom';
import { addDisposableListener, EventType, getActiveElement, isAncestorOfActiveElement, isAncestor, getWindow, isHTMLElement } from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
Expand Down Expand Up @@ -90,7 +90,7 @@ export class HoverService extends Disposable implements IHoverService {
}, undefined, hoverDisposables);
// Set the container explicitly to enable aux window support
if (!options.container) {
const targetElement = options.target instanceof HTMLElement ? options.target : options.target.targetElements[0];
const targetElement = isHTMLElement(options.target) ? options.target : options.target.targetElements[0];
options.container = this._layoutService.getContainer(getWindow(targetElement));
}

Expand Down Expand Up @@ -259,7 +259,7 @@ export class HoverService extends Disposable implements IHoverService {
// track the mouse position
const onMouseMove = (e: MouseEvent) => {
target.x = e.x + 10;
if ((e.target instanceof HTMLElement) && getHoverTargetElement(e.target, htmlElement) !== htmlElement) {
if ((isHTMLElement(e.target)) && getHoverTargetElement(e.target, htmlElement) !== htmlElement) {
hideHover(true, true);
}
};
Expand All @@ -268,7 +268,7 @@ export class HoverService extends Disposable implements IHoverService {

hoverPreparation = toDispose;

if ((e.target instanceof HTMLElement) && getHoverTargetElement(e.target as HTMLElement, htmlElement) !== htmlElement) {
if ((isHTMLElement(e.target)) && getHoverTargetElement(e.target as HTMLElement, htmlElement) !== htmlElement) {
return; // Do not show hover when the mouse is over another hover target
}

Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/browser/services/hoverService/hoverWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class HoverWidget extends Widget implements IHoverWidget {
contentsElement.textContent = options.content;
contentsElement.style.whiteSpace = 'pre-wrap';

} else if (options.content instanceof HTMLElement) {
} else if (dom.isHTMLElement(options.content)) {
contentsElement.appendChild(options.content);
contentsElement.classList.add('html-hover-contents');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { isHTMLElement } from 'vs/base/browser/dom';
import type { IHoverWidget, IUpdatableHoverContent, IUpdatableHoverOptions } from 'vs/base/browser/ui/hover/hover';
import type { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from 'vs/base/browser/ui/hover/hoverDelegate';
import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget';
Expand Down Expand Up @@ -33,7 +34,7 @@ export class UpdatableHoverWidget implements IDisposable {
}

let resolvedContent;
if (content === undefined || isString(content) || content instanceof HTMLElement) {
if (content === undefined || isString(content) || isHTMLElement(content)) {
resolvedContent = content;
} else if (!isFunction(content.markdown)) {
resolvedContent = content.markdown ?? content.markdownNotSupportedFallback;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { ModifierKeyEmitter } from 'vs/base/browser/dom';
import { isHTMLElement, ModifierKeyEmitter } from 'vs/base/browser/dom';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
Expand Down Expand Up @@ -358,7 +358,7 @@ export class InlayHintsController implements IEditorContribution {

private _installContextMenu(): IDisposable {
return this._editor.onContextMenu(async e => {
if (!(e.event.target instanceof HTMLElement)) {
if (!(isHTMLElement(e.event.target))) {
return;
}
const part = this._getInlayHintLabelPart(e);
Expand Down
4 changes: 2 additions & 2 deletions src/vs/platform/clipboard/browser/clipboardService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { isSafari, isWebkitWebView } from 'vs/base/browser/browser';
import { $, addDisposableListener, getActiveDocument, getActiveWindow, onDidRegisterWindow } from 'vs/base/browser/dom';
import { $, addDisposableListener, getActiveDocument, getActiveWindow, isHTMLElement, onDidRegisterWindow } from 'vs/base/browser/dom';
import { mainWindow } from 'vs/base/browser/window';
import { DeferredPromise } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
Expand Down Expand Up @@ -130,7 +130,7 @@ export class BrowserClipboardService extends Disposable implements IClipboardSer

activeDocument.execCommand('copy');

if (activeElement instanceof HTMLElement) {
if (isHTMLElement(activeElement)) {
activeElement.focus();
}

Expand Down
Loading