Skip to content

Commit

Permalink
🚚 [RUMF-1449] move addEventListener functions to a dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitZugmeyer committed Dec 7, 2022
1 parent f7e38a4 commit ca6b03d
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 100 deletions.
95 changes: 95 additions & 0 deletions packages/core/src/browser/addEventListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { monitor } from '@datadog/browser-core'

export const enum DOM_EVENT {
BEFORE_UNLOAD = 'beforeunload',
CLICK = 'click',
DBL_CLICK = 'dblclick',
KEY_DOWN = 'keydown',
LOAD = 'load',
POP_STATE = 'popstate',
SCROLL = 'scroll',
TOUCH_START = 'touchstart',
TOUCH_END = 'touchend',
TOUCH_MOVE = 'touchmove',
VISIBILITY_CHANGE = 'visibilitychange',
DOM_CONTENT_LOADED = 'DOMContentLoaded',
POINTER_DOWN = 'pointerdown',
POINTER_UP = 'pointerup',
POINTER_CANCEL = 'pointercancel',
HASH_CHANGE = 'hashchange',
PAGE_HIDE = 'pagehide',
MOUSE_DOWN = 'mousedown',
MOUSE_UP = 'mouseup',
MOUSE_MOVE = 'mousemove',
FOCUS = 'focus',
BLUR = 'blur',
CONTEXT_MENU = 'contextmenu',
RESIZE = 'resize',
CHANGE = 'change',
INPUT = 'input',
PLAY = 'play',
PAUSE = 'pause',
SECURITY_POLICY_VIOLATION = 'securitypolicyviolation',
SELECTION_CHANGE = 'selectionchange',
}

interface AddEventListenerOptions {
once?: boolean
capture?: boolean
passive?: boolean
}

/**
* Add an event listener to an event target object (Window, Element, mock object...). This provides
* a few conveniences compared to using `element.addEventListener` directly:
*
* * supports IE11 by: using an option object only if needed and emulating the `once` option
*
* * wraps the listener with a `monitor` function
*
* * returns a `stop` function to remove the listener
*/
export function addEventListener<E extends Event>(
eventTarget: EventTarget,
event: DOM_EVENT,
listener: (event: E) => void,
options?: AddEventListenerOptions
) {
return addEventListeners(eventTarget, [event], listener, options)
}

/**
* Add event listeners to an event target object (Window, Element, mock object...). This provides
* a few conveniences compared to using `element.addEventListener` directly:
*
* * supports IE11 by: using an option object only if needed and emulating the `once` option
*
* * wraps the listener with a `monitor` function
*
* * returns a `stop` function to remove the listener
*
* * with `once: true`, the listener will be called at most once, even if different events are listened
*/
export function addEventListeners<E extends Event>(
eventTarget: EventTarget,
events: DOM_EVENT[],
listener: (event: E) => void,
{ once, capture, passive }: AddEventListenerOptions = {}
) {
const wrappedListener = monitor(
once
? (event: Event) => {
stop()
listener(event as E)
}
: (listener as (event: Event) => void)
)

const options = passive ? { capture, passive } : capture
events.forEach((event) => eventTarget.addEventListener(event, wrappedListener, options))
const stop = () => events.forEach((event) => eventTarget.removeEventListener(event, wrappedListener, options))

return {
stop,
}
}
2 changes: 1 addition & 1 deletion packages/core/src/browser/pageExitObservable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Observable } from '../tools/observable'
import { addEventListener, DOM_EVENT } from '../tools/utils'
import { addEventListener, DOM_EVENT } from './addEventListener'

export const enum PageExitReason {
HIDDEN = 'visibility_hidden',
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/domain/report/reportObservable.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { toStackTraceString } from '../../tools/error'
import { monitor } from '../../tools/monitor'
import { mergeObservables, Observable } from '../../tools/observable'
import { DOM_EVENT, includes, addEventListener, safeTruncate } from '../../tools/utils'
import { includes, safeTruncate } from '../../tools/utils'
import { addEventListener, DOM_EVENT } from '../../browser/addEventListener'
import type { Report, BrowserWindow, ReportType } from './browser.types'

export const RawReportType = {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/domain/session/sessionManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { CookieOptions } from '../../browser/cookie'
import { COOKIE_ACCESS_DELAY, getCookie, setCookie } from '../../browser/cookie'
import type { Clock } from '../../../test/specHelper'
import { mockClock, restorePageVisibility, setPageVisibility, createNewEvent } from '../../../test/specHelper'
import { ONE_HOUR, DOM_EVENT, ONE_SECOND } from '../../tools/utils'
import { ONE_HOUR, ONE_SECOND } from '../../tools/utils'
import type { RelativeTime } from '../../tools/timeUtils'
import { isIE } from '../../tools/browserDetection'
import { DOM_EVENT } from '../../browser/addEventListener'
import type { SessionManager } from './sessionManager'
import { startSessionManager, stopSessionManager, VISIBILITY_CHECK_DELAY } from './sessionManager'
import { SESSION_COOKIE_NAME } from './sessionCookieStore'
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/domain/session/sessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ContextHistory } from '../../tools/contextHistory'
import type { RelativeTime } from '../../tools/timeUtils'
import { relativeNow, clocksOrigin } from '../../tools/timeUtils'
import { monitor } from '../../tools/monitor'
import { DOM_EVENT, addEventListener, addEventListeners } from '../../browser/addEventListener'
import { tryOldCookiesMigration } from './oldCookiesMigration'
import { startSessionStore } from './sessionStore'
import { SESSION_TIME_OUT_DELAY } from './sessionConstants'
Expand Down Expand Up @@ -70,9 +71,9 @@ export function stopSessionManager() {
}

function trackActivity(expandOrRenewSession: () => void) {
const { stop } = utils.addEventListeners(
const { stop } = addEventListeners(
window,
[utils.DOM_EVENT.CLICK, utils.DOM_EVENT.TOUCH_START, utils.DOM_EVENT.KEY_DOWN, utils.DOM_EVENT.SCROLL],
[DOM_EVENT.CLICK, DOM_EVENT.TOUCH_START, DOM_EVENT.KEY_DOWN, DOM_EVENT.SCROLL],
expandOrRenewSession,
{ capture: true, passive: true }
)
Expand All @@ -86,7 +87,7 @@ function trackVisibility(expandSession: () => void) {
}
})

const { stop } = utils.addEventListener(document, utils.DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible)
const { stop } = addEventListener(document, DOM_EVENT.VISIBILITY_CHANGE, expandSessionWhenVisible)
stopCallbacks.push(stop)

const visibilityCheckInterval = setInterval(expandSessionWhenVisible, VISIBILITY_CHECK_DELAY)
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export { areCookiesAuthorized, getCookie, setCookie, deleteCookie, COOKIE_ACCESS
export { initXhrObservable, XhrCompleteContext, XhrStartContext } from './browser/xhrObservable'
export { initFetchObservable, FetchResolveContext, FetchStartContext, FetchContext } from './browser/fetchObservable'
export { createPageExitObservable, PageExitEvent, PageExitReason } from './browser/pageExitObservable'
export * from './browser/addEventListener'
export { initConsoleObservable, ConsoleLog } from './domain/console/consoleObservable'
export { BoundedBuffer } from './tools/boundedBuffer'
export { catchUserErrors } from './tools/catchUserErrors'
Expand Down
95 changes: 1 addition & 94 deletions packages/core/src/tools/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DOM_EVENT, addEventListener } from '../browser/addEventListener'
import { display } from './display'
import { monitor } from './monitor'

Expand All @@ -9,39 +10,6 @@ export const ONE_YEAR = 365 * ONE_DAY
export const ONE_KIBI_BYTE = 1024
export const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE

export const enum DOM_EVENT {
BEFORE_UNLOAD = 'beforeunload',
CLICK = 'click',
DBL_CLICK = 'dblclick',
KEY_DOWN = 'keydown',
LOAD = 'load',
POP_STATE = 'popstate',
SCROLL = 'scroll',
TOUCH_START = 'touchstart',
TOUCH_END = 'touchend',
TOUCH_MOVE = 'touchmove',
VISIBILITY_CHANGE = 'visibilitychange',
DOM_CONTENT_LOADED = 'DOMContentLoaded',
POINTER_DOWN = 'pointerdown',
POINTER_UP = 'pointerup',
POINTER_CANCEL = 'pointercancel',
HASH_CHANGE = 'hashchange',
PAGE_HIDE = 'pagehide',
MOUSE_DOWN = 'mousedown',
MOUSE_UP = 'mouseup',
MOUSE_MOVE = 'mousemove',
FOCUS = 'focus',
BLUR = 'blur',
CONTEXT_MENU = 'contextmenu',
RESIZE = 'resize',
CHANGE = 'change',
INPUT = 'input',
PLAY = 'play',
PAUSE = 'pause',
SECURITY_POLICY_VIOLATION = 'securitypolicyviolation',
SELECTION_CHANGE = 'selectionchange',
}

export const enum ResourceType {
DOCUMENT = 'document',
XHR = 'xhr',
Expand Down Expand Up @@ -357,67 +325,6 @@ export function safeTruncate(candidate: string, length: number, suffix = '') {
return `${candidate.slice(0, correctedLength)}${suffix}`
}

interface AddEventListenerOptions {
once?: boolean
capture?: boolean
passive?: boolean
}

/**
* Add an event listener to an event target object (Window, Element, mock object...). This provides
* a few conveniences compared to using `element.addEventListener` directly:
*
* * supports IE11 by: using an option object only if needed and emulating the `once` option
*
* * wraps the listener with a `monitor` function
*
* * returns a `stop` function to remove the listener
*/
export function addEventListener<E extends Event>(
eventTarget: EventTarget,
event: DOM_EVENT,
listener: (event: E) => void,
options?: AddEventListenerOptions
) {
return addEventListeners(eventTarget, [event], listener, options)
}

/**
* Add event listeners to an event target object (Window, Element, mock object...). This provides
* a few conveniences compared to using `element.addEventListener` directly:
*
* * supports IE11 by: using an option object only if needed and emulating the `once` option
*
* * wraps the listener with a `monitor` function
*
* * returns a `stop` function to remove the listener
*
* * with `once: true`, the listener will be called at most once, even if different events are listened
*/
export function addEventListeners<E extends Event>(
eventTarget: EventTarget,
events: DOM_EVENT[],
listener: (event: E) => void,
{ once, capture, passive }: AddEventListenerOptions = {}
) {
const wrappedListener = monitor(
once
? (event: Event) => {
stop()
listener(event as E)
}
: (listener as (event: Event) => void)
)

const options = passive ? { capture, passive } : capture
events.forEach((event) => eventTarget.addEventListener(event, wrappedListener, options))
const stop = () => events.forEach((event) => eventTarget.removeEventListener(event, wrappedListener, options))

return {
stop,
}
}

export function elementMatches(element: Element & { msMatchesSelector?(selector: string): boolean }, selector: string) {
if (element.matches) {
return element.matches(selector)
Expand Down

0 comments on commit ca6b03d

Please sign in to comment.