Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08b1db2c9d |
@@ -351,3 +351,96 @@ describe('MouseService mouseEventsRequireAlt', () => {
|
|||||||
assert.isFalse(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS));
|
assert.isFalse(element.classList.contains(MouseEventCssClasses.ENABLE_MOUSE_EVENTS));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('MouseService multi-window', () => {
|
||||||
|
interface IRecordingDocument {
|
||||||
|
doc: Document;
|
||||||
|
added: string[];
|
||||||
|
removed: string[];
|
||||||
|
listeners: { [type: string]: EventListener[] };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRecordingDocument(): IRecordingDocument {
|
||||||
|
const added: string[] = [];
|
||||||
|
const removed: string[] = [];
|
||||||
|
const listeners: { [type: string]: EventListener[] } = {};
|
||||||
|
const doc = {
|
||||||
|
addEventListener: (type: string, listener: EventListener) => {
|
||||||
|
added.push(type);
|
||||||
|
(listeners[type] ??= []).push(listener);
|
||||||
|
},
|
||||||
|
removeEventListener: (type: string) => removed.push(type)
|
||||||
|
} as any;
|
||||||
|
return { doc, added, removed, listeners };
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should manage protocol mouseup/mousemove listeners on the element\'s current document, not the document the terminal was opened with', () => {
|
||||||
|
const mouseStateService = new MouseStateService();
|
||||||
|
const optionsService = new OptionsService({});
|
||||||
|
const mouseService = new MouseService(
|
||||||
|
new MockRenderService(),
|
||||||
|
{
|
||||||
|
getMouseReportCoords: () => ({ col: 0, row: 0, x: 0, y: 0 })
|
||||||
|
} as any,
|
||||||
|
mouseStateService,
|
||||||
|
{
|
||||||
|
triggerDataEvent: () => {},
|
||||||
|
triggerBinaryEvent: () => {},
|
||||||
|
decPrivateModes: { applicationCursorKeys: false }
|
||||||
|
} as any,
|
||||||
|
bufferService,
|
||||||
|
optionsService,
|
||||||
|
new TestSelectionService(),
|
||||||
|
logService,
|
||||||
|
new MockCoreBrowserService()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Simulate a terminal that was opened with one document but whose element now lives in
|
||||||
|
// another document (eg. it was moved to a child window)
|
||||||
|
const openDocument = createRecordingDocument();
|
||||||
|
const childDocument = createRecordingDocument();
|
||||||
|
const elementListeners: { [type: string]: EventListener[] } = {};
|
||||||
|
const element = {
|
||||||
|
classList: {
|
||||||
|
add: () => {},
|
||||||
|
remove: () => {},
|
||||||
|
contains: () => false
|
||||||
|
},
|
||||||
|
addEventListener: (type: string, listener: EventListener) => {
|
||||||
|
(elementListeners[type] ??= []).push(listener);
|
||||||
|
},
|
||||||
|
removeEventListener: () => {},
|
||||||
|
ownerDocument: childDocument.doc
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
mouseService.bindMouse({
|
||||||
|
element,
|
||||||
|
screenElement: createTestMouseTargetElement(),
|
||||||
|
document: openDocument.doc
|
||||||
|
}, disposable => disposable, () => {});
|
||||||
|
mouseStateService.activeProtocol = 'ANY';
|
||||||
|
|
||||||
|
elementListeners['mousedown'][0]({
|
||||||
|
type: 'mousedown',
|
||||||
|
button: 0,
|
||||||
|
buttons: 1,
|
||||||
|
altKey: false,
|
||||||
|
ctrlKey: false,
|
||||||
|
shiftKey: false,
|
||||||
|
preventDefault: () => {}
|
||||||
|
} as any);
|
||||||
|
assert.deepEqual(childDocument.added, ['mouseup', 'mousemove']);
|
||||||
|
assert.deepEqual(openDocument.added, []);
|
||||||
|
|
||||||
|
childDocument.listeners['mouseup'][0]({
|
||||||
|
type: 'mouseup',
|
||||||
|
button: 0,
|
||||||
|
buttons: 0,
|
||||||
|
altKey: false,
|
||||||
|
ctrlKey: false,
|
||||||
|
shiftKey: false
|
||||||
|
} as any);
|
||||||
|
assert.deepEqual(childDocument.removed, ['mouseup', 'mousemove']);
|
||||||
|
assert.deepEqual(openDocument.removed, []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ interface IMouseBindContext {
|
|||||||
readonly target: IMouseServiceTarget;
|
readonly target: IMouseServiceTarget;
|
||||||
readonly focus: () => void;
|
readonly focus: () => void;
|
||||||
readonly requestedEvents: RequestedMouseEvents;
|
readonly requestedEvents: RequestedMouseEvents;
|
||||||
|
listenerDocument: Document | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MouseService implements IMouseService {
|
export class MouseService implements IMouseService {
|
||||||
@@ -61,7 +62,7 @@ export class MouseService implements IMouseService {
|
|||||||
mousedrag: null,
|
mousedrag: null,
|
||||||
mousemove: null
|
mousemove: null
|
||||||
};
|
};
|
||||||
const ctx: IMouseBindContext = { target, focus, requestedEvents };
|
const ctx: IMouseBindContext = { target, focus, requestedEvents, listenerDocument: null };
|
||||||
const eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener> = {
|
const eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener> = {
|
||||||
mouseup: (ev: Event) => this._handleMouseUp(ctx, ev as MouseEvent),
|
mouseup: (ev: Event) => this._handleMouseUp(ctx, ev as MouseEvent),
|
||||||
wheel: (ev: Event) => this._handleWheel(ctx, ev as WheelEvent),
|
wheel: (ev: Event) => this._handleWheel(ctx, ev as WheelEvent),
|
||||||
@@ -87,11 +88,12 @@ export class MouseService implements IMouseService {
|
|||||||
|
|
||||||
// Ensure document-level listeners are removed on dispose
|
// Ensure document-level listeners are removed on dispose
|
||||||
register(toDisposable(() => {
|
register(toDisposable(() => {
|
||||||
|
const listenerDocument = ctx.listenerDocument ?? document;
|
||||||
if (requestedEvents.mouseup) {
|
if (requestedEvents.mouseup) {
|
||||||
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
listenerDocument.removeEventListener('mouseup', requestedEvents.mouseup);
|
||||||
}
|
}
|
||||||
if (requestedEvents.mousedrag) {
|
if (requestedEvents.mousedrag) {
|
||||||
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
listenerDocument.removeEventListener('mousemove', requestedEvents.mousedrag);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -199,12 +201,14 @@ export class MouseService implements IMouseService {
|
|||||||
this._sendEvent(ctx, ev);
|
this._sendEvent(ctx, ev);
|
||||||
if (!ev.buttons) {
|
if (!ev.buttons) {
|
||||||
// if no other button is held remove global handlers
|
// if no other button is held remove global handlers
|
||||||
|
const listenerDocument = ctx.listenerDocument ?? ctx.target.document;
|
||||||
if (ctx.requestedEvents.mouseup) {
|
if (ctx.requestedEvents.mouseup) {
|
||||||
ctx.target.document.removeEventListener('mouseup', ctx.requestedEvents.mouseup);
|
listenerDocument.removeEventListener('mouseup', ctx.requestedEvents.mouseup);
|
||||||
}
|
}
|
||||||
if (ctx.requestedEvents.mousedrag) {
|
if (ctx.requestedEvents.mousedrag) {
|
||||||
ctx.target.document.removeEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
listenerDocument.removeEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
||||||
}
|
}
|
||||||
|
ctx.listenerDocument = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,12 +250,15 @@ export class MouseService implements IMouseService {
|
|||||||
// of the terminal element.
|
// of the terminal element.
|
||||||
// Note: Other emulators also do this for 'mousedown' while a button
|
// Note: Other emulators also do this for 'mousedown' while a button
|
||||||
// is held, we currently limit 'mousedown' to the terminal only.
|
// is held, we currently limit 'mousedown' to the terminal only.
|
||||||
|
// Use the element's current document in case it moved to another window after open.
|
||||||
|
const listenerDocument = ctx.target.element.ownerDocument ?? ctx.target.document;
|
||||||
if (ctx.requestedEvents.mouseup) {
|
if (ctx.requestedEvents.mouseup) {
|
||||||
ctx.target.document.addEventListener('mouseup', ctx.requestedEvents.mouseup);
|
listenerDocument.addEventListener('mouseup', ctx.requestedEvents.mouseup);
|
||||||
}
|
}
|
||||||
if (ctx.requestedEvents.mousedrag) {
|
if (ctx.requestedEvents.mousedrag) {
|
||||||
ctx.target.document.addEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
listenerDocument.addEventListener('mousemove', ctx.requestedEvents.mousedrag);
|
||||||
}
|
}
|
||||||
|
ctx.listenerDocument = listenerDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handlePassiveWheel(ctx: IMouseBindContext, ev: WheelEvent): false | void {
|
private _handlePassiveWheel(ctx: IMouseBindContext, ev: WheelEvent): false | void {
|
||||||
@@ -398,8 +405,9 @@ export class MouseService implements IMouseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
|
private _handleProtocolChange(ctx: IMouseBindContext, eventListeners: Record<'mouseup' | 'wheel' | 'mousedrag' | 'mousemove', EventListener>, events: CoreMouseEventType): void {
|
||||||
const { element, document } = ctx.target;
|
const { element } = ctx.target;
|
||||||
const { requestedEvents } = ctx;
|
const { requestedEvents } = ctx;
|
||||||
|
const listenerDocument = ctx.listenerDocument ?? ctx.target.document;
|
||||||
// apply global changes on events
|
// apply global changes on events
|
||||||
if (events) {
|
if (events) {
|
||||||
if (this._optionsService.rawOptions.logLevel === 'debug') {
|
if (this._optionsService.rawOptions.logLevel === 'debug') {
|
||||||
@@ -434,7 +442,7 @@ export class MouseService implements IMouseService {
|
|||||||
|
|
||||||
if (!(events & CoreMouseEventType.UP)) {
|
if (!(events & CoreMouseEventType.UP)) {
|
||||||
if (requestedEvents.mouseup) {
|
if (requestedEvents.mouseup) {
|
||||||
document.removeEventListener('mouseup', requestedEvents.mouseup);
|
listenerDocument.removeEventListener('mouseup', requestedEvents.mouseup);
|
||||||
}
|
}
|
||||||
requestedEvents.mouseup = null;
|
requestedEvents.mouseup = null;
|
||||||
} else {
|
} else {
|
||||||
@@ -443,7 +451,7 @@ export class MouseService implements IMouseService {
|
|||||||
|
|
||||||
if (!(events & CoreMouseEventType.DRAG)) {
|
if (!(events & CoreMouseEventType.DRAG)) {
|
||||||
if (requestedEvents.mousedrag) {
|
if (requestedEvents.mousedrag) {
|
||||||
document.removeEventListener('mousemove', requestedEvents.mousedrag);
|
listenerDocument.removeEventListener('mousemove', requestedEvents.mousedrag);
|
||||||
}
|
}
|
||||||
requestedEvents.mousedrag = null;
|
requestedEvents.mousedrag = null;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user