globalMouseMoveMonitor.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as dom from './dom.js';
  6. import { IframeUtils } from './iframe.js';
  7. import { StandardMouseEvent } from './mouseEvent.js';
  8. import { DisposableStore } from '../common/lifecycle.js';
  9. import { isIOS } from '../common/platform.js';
  10. export function standardMouseMoveMerger(lastEvent, currentEvent) {
  11. let ev = new StandardMouseEvent(currentEvent);
  12. ev.preventDefault();
  13. return {
  14. leftButton: ev.leftButton,
  15. buttons: ev.buttons,
  16. posx: ev.posx,
  17. posy: ev.posy
  18. };
  19. }
  20. export class GlobalMouseMoveMonitor {
  21. constructor() {
  22. this._hooks = new DisposableStore();
  23. this._mouseMoveEventMerger = null;
  24. this._mouseMoveCallback = null;
  25. this._onStopCallback = null;
  26. }
  27. dispose() {
  28. this.stopMonitoring(false);
  29. this._hooks.dispose();
  30. }
  31. stopMonitoring(invokeStopCallback, browserEvent) {
  32. if (!this.isMonitoring()) {
  33. // Not monitoring
  34. return;
  35. }
  36. // Unhook
  37. this._hooks.clear();
  38. this._mouseMoveEventMerger = null;
  39. this._mouseMoveCallback = null;
  40. const onStopCallback = this._onStopCallback;
  41. this._onStopCallback = null;
  42. if (invokeStopCallback && onStopCallback) {
  43. onStopCallback(browserEvent);
  44. }
  45. }
  46. isMonitoring() {
  47. return !!this._mouseMoveEventMerger;
  48. }
  49. startMonitoring(initialElement, initialButtons, mouseMoveEventMerger, mouseMoveCallback, onStopCallback) {
  50. if (this.isMonitoring()) {
  51. // I am already hooked
  52. return;
  53. }
  54. this._mouseMoveEventMerger = mouseMoveEventMerger;
  55. this._mouseMoveCallback = mouseMoveCallback;
  56. this._onStopCallback = onStopCallback;
  57. const windowChain = IframeUtils.getSameOriginWindowChain();
  58. const mouseMove = isIOS ? 'pointermove' : 'mousemove'; // Safari sends wrong event, workaround for #122653
  59. const mouseUp = 'mouseup';
  60. const listenTo = windowChain.map(element => element.window.document);
  61. const shadowRoot = dom.getShadowRoot(initialElement);
  62. if (shadowRoot) {
  63. listenTo.unshift(shadowRoot);
  64. }
  65. for (const element of listenTo) {
  66. this._hooks.add(dom.addDisposableThrottledListener(element, mouseMove, (data) => {
  67. if (data.buttons !== initialButtons) {
  68. // Buttons state has changed in the meantime
  69. this.stopMonitoring(true);
  70. return;
  71. }
  72. this._mouseMoveCallback(data);
  73. }, (lastEvent, currentEvent) => this._mouseMoveEventMerger(lastEvent, currentEvent)));
  74. this._hooks.add(dom.addDisposableListener(element, mouseUp, (e) => this.stopMonitoring(true)));
  75. }
  76. if (IframeUtils.hasDifferentOriginAncestor()) {
  77. let lastSameOriginAncestor = windowChain[windowChain.length - 1];
  78. // We might miss a mouse up if it happens outside the iframe
  79. // This one is for Chrome
  80. this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseout', (browserEvent) => {
  81. let e = new StandardMouseEvent(browserEvent);
  82. if (e.target.tagName.toLowerCase() === 'html') {
  83. this.stopMonitoring(true);
  84. }
  85. }));
  86. // This one is for FF
  87. this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseover', (browserEvent) => {
  88. let e = new StandardMouseEvent(browserEvent);
  89. if (e.target.tagName.toLowerCase() === 'html') {
  90. this.stopMonitoring(true);
  91. }
  92. }));
  93. // This one is for IE
  94. this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document.body, 'mouseleave', (browserEvent) => {
  95. this.stopMonitoring(true);
  96. }));
  97. }
  98. }
  99. }