diff --git a/CHANGELOG.md b/CHANGELOG.md index 4befde2743..cb7fb6d910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Avoid setting marker opacity twice. ([#5441](https://github.com/maplibre/maplibre-gl-js/pull/5441)) - Fix rendering Japanese symbols which are accidentally ignored. ([#5421](https://github.com/maplibre/maplibre-gl-js/pull/5421) +- Add a check for MouseEvent, to avoid errors when bot were crawling on website using Event instance instead of MouseEvent instance for types like mouseover, mouseout etc.. ([#5466](https://github.com/maplibre/maplibre-gl-js/pull/5466)). - _...Add new stuff here..._ ### 🐞 Bug fixes diff --git a/src/ui/events.ts b/src/ui/events.ts index 54f94ccaf5..1dfcf6808c 100644 --- a/src/ui/events.ts +++ b/src/ui/events.ts @@ -536,6 +536,7 @@ export class MapMouseEvent extends Event implements MapLibreEvent { _defaultPrevented: boolean; constructor(type: string, map: Map, originalEvent: MouseEvent, data: any = {}) { + originalEvent = originalEvent instanceof MouseEvent ? originalEvent : new MouseEvent(type, originalEvent); const point = DOM.mousePos(map.getCanvas(), originalEvent); const lngLat = map.unproject(point); super(type, extend({point, lngLat, originalEvent}, data)); diff --git a/src/ui/handler/map_event.test.ts b/src/ui/handler/map_event.test.ts index c91ab01479..d101dcf963 100644 --- a/src/ui/handler/map_event.test.ts +++ b/src/ui/handler/map_event.test.ts @@ -156,4 +156,29 @@ describe('map events', () => { simulate.contextmenu(map.getCanvas(), {target, button: 2, clientX: 10, clientY: 10}); // triggered only after mouseup expect(contextmenu).toHaveBeenCalledTimes(0); }); + + test('MapMouseEvent constructor does not throw error with Event instance instead of MouseEvent as originalEvent param', () => { + const map = createMap(); + const target = map.getCanvasContainer(); + + expect(()=> { + target.dispatchEvent(new Event('mousedown')); + target.dispatchEvent(new Event('mouseup')); + target.dispatchEvent(new Event('click')); + target.dispatchEvent(new Event('dblclick')); + target.dispatchEvent(new Event('mousemove')); + target.dispatchEvent(new Event('mouseover')); + target.dispatchEvent(new Event('mouseenter')); + target.dispatchEvent(new Event('mouseleave')); + target.dispatchEvent(new Event('mouseout')); + target.dispatchEvent(new Event('contextmenu')); + target.dispatchEvent(new Event('wheel')); + + target.dispatchEvent(new Event('touchstart')); + target.dispatchEvent(new Event('touchmove')); + target.dispatchEvent(new Event('touchmoveWindow')); + target.dispatchEvent(new Event('touchend')); + target.dispatchEvent(new Event('touchcancel')); + }).not.toThrow(); + }); }); diff --git a/src/ui/handler_manager.ts b/src/ui/handler_manager.ts index 9898fb0901..d640a76f4f 100644 --- a/src/ui/handler_manager.ts +++ b/src/ui/handler_manager.ts @@ -17,7 +17,7 @@ import {DragPanHandler} from './handler/shim/drag_pan'; import {DragRotateHandler} from './handler/shim/drag_rotate'; import {TwoFingersTouchZoomRotateHandler} from './handler/shim/two_fingers_touch'; import {CooperativeGesturesHandler} from './handler/cooperative_gestures'; -import {extend} from '../util/util'; +import {extend, isPointableEvent, isTouchableEvent, isTouchableOrPointableType} from '../util/util'; import {browser} from '../util/browser'; import Point from '@mapbox/point-geometry'; import {type MapControlsDeltas} from '../geo/projection/camera_helper'; @@ -377,12 +377,6 @@ export class HandlerManager { const mergedHandlerResult: HandlerResult = {needsRenderFrame: false}; const eventsInProgress: EventsInProgress = {}; const activeHandlers = {}; - const eventTouches = (e as TouchEvent).touches; - - const mapTouches = eventTouches ? this._getMapTouches(eventTouches) : undefined; - const points = mapTouches ? - DOM.touchPos(this._map.getCanvas(), mapTouches) : - DOM.mousePos(this._map.getCanvas(), ((e as MouseEvent))); for (const {handlerName, handler, allowed} of this._handlers) { if (!handler.isEnabled()) continue; @@ -393,7 +387,17 @@ export class HandlerManager { } else { if (handler[eventName || e.type]) { - data = handler[eventName || e.type](e, points, mapTouches); + if (isPointableEvent(e, eventName || e.type)){ + const point = DOM.mousePos(this._map.getCanvas(), e); + data = handler[eventName || e.type](e, point); + } else if (isTouchableEvent(e, eventName || e.type)) { + const eventTouches = (e as TouchEvent).touches; + const mapTouches = this._getMapTouches(eventTouches); + const points = DOM.touchPos(this._map.getCanvas(), mapTouches); + data = handler[eventName || e.type](e, points, mapTouches); + } else if (!isTouchableOrPointableType(eventName || e.type)) { + data = handler[eventName || e.type](e); + } this.mergeHandlerResult(mergedHandlerResult, eventsInProgress, data, handlerName, inputEvent); if (data && data.needsRenderFrame) { this._triggerRenderFrame(); diff --git a/src/util/util.ts b/src/util/util.ts index 7ff47d50a4..776f314f95 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -6,6 +6,7 @@ import type {WorkerGlobalScopeInterface} from './web_worker'; import {mat3, mat4, quat, vec2, type vec3, type vec4} from 'gl-matrix'; import {pixelsToTileUnits} from '../source/pixels_to_tile_units'; import {type OverscaledTileID} from '../source/tile_id'; +import type {Event} from './evented'; /** * Returns a new 64 bit float vec4 of zeroes. @@ -1035,3 +1036,37 @@ export const MAX_TILE_ZOOM = 25; export const MIN_TILE_ZOOM = 0; export const MAX_VALID_LATITUDE = 85.051129; + +const touchableEvents = { + touchstart: true, + touchmove: true, + touchmoveWindow: true, + touchend: true, + touchcancel: true +}; + +const pointableEvents = { + dblclick: true, + click: true, + mouseover: true, + mouseout: true, + mousedown: true, + mousemove: true, + mousemoveWindow: true, + mouseup: true, + mouseupWindow: true, + contextmenu: true, + wheel: true +}; + +export function isTouchableEvent(event: Event, eventType: string): boolean { + return touchableEvents[eventType] && 'touches' in event; +} + +export function isPointableEvent(event: Event, eventType: string): event is MouseEvent { + return pointableEvents[eventType] && (event instanceof MouseEvent || event instanceof WheelEvent); +} + +export function isTouchableOrPointableType(eventType: string): boolean { + return touchableEvents[eventType] || pointableEvents[eventType]; +}