Socket
Socket
Sign inDemoInstall

@datadog/browser-rum-core

Package Overview
Dependencies
Maintainers
1
Versions
179
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@datadog/browser-rum-core - npm Package Compare versions

Comparing version 4.34.0 to 4.34.1

2

cjs/browser/htmlDomUtils.js

@@ -22,3 +22,3 @@ "use strict";

var shadowRoot = node;
return !!shadowRoot.host && isElementNode(shadowRoot.host);
return !!shadowRoot.host && shadowRoot.nodeType === Node.DOCUMENT_FRAGMENT_NODE && isElementNode(shadowRoot.host);
}

@@ -25,0 +25,0 @@ exports.isNodeShadowRoot = isNodeShadowRoot;

@@ -48,3 +48,3 @@ "use strict";

},
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.34.0" : undefined,
browser_sdk_version: (0, browser_core_1.canUseEventBridge)() ? "4.34.1" : undefined,
},

@@ -51,0 +51,0 @@ application: {

@@ -47,3 +47,2 @@ "use strict";

position: action.position,
pointer_up_delay: action.pointerUpDelay,
},

@@ -50,0 +49,0 @@ },

@@ -50,2 +50,5 @@ "use strict";

'select,' +
// contenteditable and their descendants don't always trigger meaningful changes when manipulated
'[contenteditable],' +
'[contenteditable] *,' +
// canvas, as there is no good way to detect activity occurring on them

@@ -61,8 +64,5 @@ 'canvas,' +

}
return !(0, browser_core_1.elementMatches)(click.event.target, (0, browser_core_1.isExperimentalFeatureEnabled)('dead_click_fixes')
? // contenteditable and their descendants don't always trigger meaningful changes when manipulated
"".concat(DEAD_CLICK_EXCLUDE_SELECTOR, ",[contenteditable],[contenteditable] *")
: DEAD_CLICK_EXCLUDE_SELECTOR);
return !(0, browser_core_1.elementMatches)(click.event.target, DEAD_CLICK_EXCLUDE_SELECTOR);
}
exports.isDead = isDead;
//# sourceMappingURL=computeFrustration.js.map

@@ -1,3 +0,2 @@

import type { TimeStamp } from '@datadog/browser-core';
export declare type MouseEventOnElement = MouseEvent & {
export declare type MouseEventOnElement = PointerEvent & {
target: Element;

@@ -11,6 +10,6 @@ };

onPointerDown: (event: MouseEventOnElement) => ClickContext | undefined;
onStartEvent: (context: ClickContext, event: MouseEventOnElement, getUserActivity: () => UserActivity, getClickEventTimeStamp: () => TimeStamp | undefined) => void;
onPointerUp: (context: ClickContext, event: MouseEventOnElement, getUserActivity: () => UserActivity) => void;
}
export declare function listenActionEvents<ClickContext>({ onPointerDown, onStartEvent }: ActionEventsHooks<ClickContext>): {
export declare function listenActionEvents<ClickContext>({ onPointerDown, onPointerUp }: ActionEventsHooks<ClickContext>): {
stop: () => void;
};

@@ -6,3 +6,3 @@ "use strict";

function listenActionEvents(_a) {
var onPointerDown = _a.onPointerDown, onStartEvent = _a.onStartEvent;
var onPointerDown = _a.onPointerDown, onPointerUp = _a.onPointerUp;
var selectionEmptyAtPointerDown;

@@ -16,11 +16,7 @@ var userActivity = {

(0, browser_core_1.addEventListener)(window, "pointerdown" /* POINTER_DOWN */, function (event) {
if (isValidMouseEvent(event)) {
if (isValidPointerEvent(event)) {
selectionEmptyAtPointerDown = isSelectionEmpty();
userActivity = {
selection: false,
input: (0, browser_core_1.isExperimentalFeatureEnabled)('dead_click_fixes')
? false
: // Mimics the issue that was fixed in https://github.com/DataDog/browser-sdk/pull/1968
// The goal is to release all dead click fixes at the same time
userActivity.input,
input: false,
};

@@ -35,14 +31,8 @@ clickContext = onPointerDown(event);

}, { capture: true }),
(0, browser_core_1.addEventListener)(window, (0, browser_core_1.isExperimentalFeatureEnabled)('dead_click_fixes') ? "pointerup" /* POINTER_UP */ : "click" /* CLICK */, function (startEvent) {
if (isValidMouseEvent(startEvent) && clickContext) {
(0, browser_core_1.addEventListener)(window, "pointerup" /* POINTER_UP */, function (event) {
if (isValidPointerEvent(event) && clickContext) {
// Use a scoped variable to make sure the value is not changed by other clicks
var localUserActivity_1 = userActivity;
var clickEventTimeStamp_1;
onStartEvent(clickContext, startEvent, function () { return localUserActivity_1; }, function () { return clickEventTimeStamp_1; });
onPointerUp(clickContext, event, function () { return localUserActivity_1; });
clickContext = undefined;
if ((0, browser_core_1.isExperimentalFeatureEnabled)('dead_click_fixes')) {
(0, browser_core_1.addEventListener)(window, "click" /* CLICK */, function () {
clickEventTimeStamp_1 = (0, browser_core_1.timeStampNow)();
}, { capture: true, once: true });
}
}

@@ -65,11 +55,8 @@ }, { capture: true }),

}
function isValidMouseEvent(event) {
function isValidPointerEvent(event) {
return (event.target instanceof Element &&
// Only consider 'primary' pointer events for now. Multi-touch support could be implemented in
// the future.
// On Chrome, click events are PointerEvent with `isPrimary = false`, but we should still
// consider them valid. This could be removed when we enable the `click-action-on-pointerup`
// flag, since we won't rely on click events anymore.
(event.type === 'click' || event.isPrimary !== false));
event.isPrimary !== false);
}
//# sourceMappingURL=listenActionEvents.js.map

@@ -33,3 +33,2 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core';

events: Event[];
pointerUpDelay?: Duration;
}

@@ -48,3 +47,3 @@ export interface ActionContexts {

export declare type Click = ReturnType<typeof newClick>;
declare function newClick(lifeCycle: LifeCycle, history: ClickActionIdHistory, getUserActivity: () => UserActivity, getClickEventTimeStamp: () => TimeStamp | undefined, clickActionBase: ClickActionBase, startEvent: MouseEventOnElement): {
declare function newClick(lifeCycle: LifeCycle, history: ClickActionIdHistory, getUserActivity: () => UserActivity, clickActionBase: ClickActionBase, startEvent: MouseEventOnElement): {
event: MouseEventOnElement;

@@ -51,0 +50,0 @@ stop: (newActivityEndTime?: TimeStamp | undefined) => void;

@@ -27,5 +27,5 @@ "use strict";

},
onStartEvent: function (_a, startEvent, getUserActivity, getClickEventTimeStamp) {
onPointerUp: function (_a, startEvent, getUserActivity) {
var clickActionBase = _a.clickActionBase, hadActivityOnPointerDown = _a.hadActivityOnPointerDown;
return startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown, getClickEventTimeStamp);
return startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown);
},

@@ -74,14 +74,12 @@ }).stop;

var hadActivityOnPointerDown = false;
if ((0, browser_core_1.isExperimentalFeatureEnabled)('dead_click_fixes')) {
(0, waitPageActivityEnd_1.waitPageActivityEnd)(lifeCycle, domMutationObservable, configuration, function (pageActivityEndEvent) {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity;
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
waitPageActivityEnd_1.PAGE_ACTIVITY_VALIDATION_DELAY);
}
(0, waitPageActivityEnd_1.waitPageActivityEnd)(lifeCycle, domMutationObservable, configuration, function (pageActivityEndEvent) {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity;
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
waitPageActivityEnd_1.PAGE_ACTIVITY_VALIDATION_DELAY);
return { clickActionBase: clickActionBase, hadActivityOnPointerDown: function () { return hadActivityOnPointerDown; } };
}
function startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown, getClickEventTimeStamp) {
var click = newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent);
function startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown) {
var click = newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent);
if (configuration.trackFrustrations) {

@@ -158,3 +156,3 @@ appendClickToClickChain(click);

}
function newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent) {
function newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent) {
var id = (0, browser_core_1.generateUUID)();

@@ -205,3 +203,3 @@ var startClocks = (0, browser_core_1.clocksNow)();

isStopped: function () { return status === 1 /* STOPPED */ || status === 2 /* FINALIZED */; },
clone: function () { return newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent); },
clone: function () { return newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent); },
validate: function (domEvents) {

@@ -213,3 +211,2 @@ stop();

var _a = eventCountsSubscription.eventCounts, resourceCount = _a.resourceCount, errorCount = _a.errorCount, longTaskCount = _a.longTaskCount;
var clickEventTimeStamp = getClickEventTimeStamp();
var clickAction = (0, browser_core_1.assign)({

@@ -228,3 +225,2 @@ type: "click" /* CLICK */,

event: startEvent,
pointerUpDelay: clickEventTimeStamp && (0, browser_core_1.elapsed)(startClocks.timeStamp, clickEventTimeStamp),
}, clickActionBase);

@@ -231,0 +227,0 @@ lifeCycle.notify(1 /* AUTO_ACTION_COMPLETED */, clickAction);

@@ -42,3 +42,3 @@ "use strict";

lifeCycle.subscribe(9 /* PAGE_EXITED */, function (pageExitEvent) {
if (pageExitEvent.reason === "before_unload" /* UNLOADING */) {
if (pageExitEvent.reason === browser_core_1.PageExitReason.UNLOADING) {
currentView.end();

@@ -45,0 +45,0 @@ currentView.triggerUpdate();

@@ -15,3 +15,3 @@ export function isTextNode(node) {

var shadowRoot = node;
return !!shadowRoot.host && isElementNode(shadowRoot.host);
return !!shadowRoot.host && shadowRoot.nodeType === Node.DOCUMENT_FRAGMENT_NODE && isElementNode(shadowRoot.host);
}

@@ -18,0 +18,0 @@ export function getChildNodes(node) {

@@ -45,3 +45,3 @@ import { combine, isEmptyObject, limitModification, timeStampNow, currentDrift, display, createEventRateLimiter, canUseEventBridge, } from '@datadog/browser-core';

},
browser_sdk_version: canUseEventBridge() ? "4.34.0" : undefined,
browser_sdk_version: canUseEventBridge() ? "4.34.1" : undefined,
},

@@ -48,0 +48,0 @@ application: {

@@ -43,3 +43,2 @@ import { noop, assign, combine, toServerDuration, generateUUID } from '@datadog/browser-core';

position: action.position,
pointer_up_delay: action.pointerUpDelay,
},

@@ -46,0 +45,0 @@ },

@@ -1,2 +0,2 @@

import { elementMatches, isExperimentalFeatureEnabled, ONE_SECOND } from '@datadog/browser-core';
import { elementMatches, ONE_SECOND } from '@datadog/browser-core';
var MIN_CLICKS_PER_SECOND_TO_CONSIDER_RAGE = 3;

@@ -45,2 +45,5 @@ export function computeFrustration(clicks, rageClick) {

'select,' +
// contenteditable and their descendants don't always trigger meaningful changes when manipulated
'[contenteditable],' +
'[contenteditable] *,' +
// canvas, as there is no good way to detect activity occurring on them

@@ -56,7 +59,4 @@ 'canvas,' +

}
return !elementMatches(click.event.target, isExperimentalFeatureEnabled('dead_click_fixes')
? // contenteditable and their descendants don't always trigger meaningful changes when manipulated
"".concat(DEAD_CLICK_EXCLUDE_SELECTOR, ",[contenteditable],[contenteditable] *")
: DEAD_CLICK_EXCLUDE_SELECTOR);
return !elementMatches(click.event.target, DEAD_CLICK_EXCLUDE_SELECTOR);
}
//# sourceMappingURL=computeFrustration.js.map

@@ -1,3 +0,2 @@

import type { TimeStamp } from '@datadog/browser-core';
export declare type MouseEventOnElement = MouseEvent & {
export declare type MouseEventOnElement = PointerEvent & {
target: Element;

@@ -11,6 +10,6 @@ };

onPointerDown: (event: MouseEventOnElement) => ClickContext | undefined;
onStartEvent: (context: ClickContext, event: MouseEventOnElement, getUserActivity: () => UserActivity, getClickEventTimeStamp: () => TimeStamp | undefined) => void;
onPointerUp: (context: ClickContext, event: MouseEventOnElement, getUserActivity: () => UserActivity) => void;
}
export declare function listenActionEvents<ClickContext>({ onPointerDown, onStartEvent }: ActionEventsHooks<ClickContext>): {
export declare function listenActionEvents<ClickContext>({ onPointerDown, onPointerUp }: ActionEventsHooks<ClickContext>): {
stop: () => void;
};

@@ -1,4 +0,4 @@

import { addEventListener, isExperimentalFeatureEnabled, timeStampNow } from '@datadog/browser-core';
import { addEventListener } from '@datadog/browser-core';
export function listenActionEvents(_a) {
var onPointerDown = _a.onPointerDown, onStartEvent = _a.onStartEvent;
var onPointerDown = _a.onPointerDown, onPointerUp = _a.onPointerUp;
var selectionEmptyAtPointerDown;

@@ -12,11 +12,7 @@ var userActivity = {

addEventListener(window, "pointerdown" /* POINTER_DOWN */, function (event) {
if (isValidMouseEvent(event)) {
if (isValidPointerEvent(event)) {
selectionEmptyAtPointerDown = isSelectionEmpty();
userActivity = {
selection: false,
input: isExperimentalFeatureEnabled('dead_click_fixes')
? false
: // Mimics the issue that was fixed in https://github.com/DataDog/browser-sdk/pull/1968
// The goal is to release all dead click fixes at the same time
userActivity.input,
input: false,
};

@@ -31,14 +27,8 @@ clickContext = onPointerDown(event);

}, { capture: true }),
addEventListener(window, isExperimentalFeatureEnabled('dead_click_fixes') ? "pointerup" /* POINTER_UP */ : "click" /* CLICK */, function (startEvent) {
if (isValidMouseEvent(startEvent) && clickContext) {
addEventListener(window, "pointerup" /* POINTER_UP */, function (event) {
if (isValidPointerEvent(event) && clickContext) {
// Use a scoped variable to make sure the value is not changed by other clicks
var localUserActivity_1 = userActivity;
var clickEventTimeStamp_1;
onStartEvent(clickContext, startEvent, function () { return localUserActivity_1; }, function () { return clickEventTimeStamp_1; });
onPointerUp(clickContext, event, function () { return localUserActivity_1; });
clickContext = undefined;
if (isExperimentalFeatureEnabled('dead_click_fixes')) {
addEventListener(window, "click" /* CLICK */, function () {
clickEventTimeStamp_1 = timeStampNow();
}, { capture: true, once: true });
}
}

@@ -60,11 +50,8 @@ }, { capture: true }),

}
function isValidMouseEvent(event) {
function isValidPointerEvent(event) {
return (event.target instanceof Element &&
// Only consider 'primary' pointer events for now. Multi-touch support could be implemented in
// the future.
// On Chrome, click events are PointerEvent with `isPrimary = false`, but we should still
// consider them valid. This could be removed when we enable the `click-action-on-pointerup`
// flag, since we won't rely on click events anymore.
(event.type === 'click' || event.isPrimary !== false));
event.isPrimary !== false);
}
//# sourceMappingURL=listenActionEvents.js.map

@@ -33,3 +33,2 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core';

events: Event[];
pointerUpDelay?: Duration;
}

@@ -48,3 +47,3 @@ export interface ActionContexts {

export declare type Click = ReturnType<typeof newClick>;
declare function newClick(lifeCycle: LifeCycle, history: ClickActionIdHistory, getUserActivity: () => UserActivity, getClickEventTimeStamp: () => TimeStamp | undefined, clickActionBase: ClickActionBase, startEvent: MouseEventOnElement): {
declare function newClick(lifeCycle: LifeCycle, history: ClickActionIdHistory, getUserActivity: () => UserActivity, clickActionBase: ClickActionBase, startEvent: MouseEventOnElement): {
event: MouseEventOnElement;

@@ -51,0 +50,0 @@ stop: (newActivityEndTime?: TimeStamp | undefined) => void;

@@ -24,5 +24,5 @@ import { includes, timeStampNow, isExperimentalFeatureEnabled, Observable, assign, getRelativeTime, ONE_MINUTE, ContextHistory, generateUUID, clocksNow, ONE_SECOND, elapsed, } from '@datadog/browser-core';

},
onStartEvent: function (_a, startEvent, getUserActivity, getClickEventTimeStamp) {
onPointerUp: function (_a, startEvent, getUserActivity) {
var clickActionBase = _a.clickActionBase, hadActivityOnPointerDown = _a.hadActivityOnPointerDown;
return startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown, getClickEventTimeStamp);
return startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown);
},

@@ -70,14 +70,12 @@ }).stop;

var hadActivityOnPointerDown = false;
if (isExperimentalFeatureEnabled('dead_click_fixes')) {
waitPageActivityEnd(lifeCycle, domMutationObservable, configuration, function (pageActivityEndEvent) {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity;
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
PAGE_ACTIVITY_VALIDATION_DELAY);
}
waitPageActivityEnd(lifeCycle, domMutationObservable, configuration, function (pageActivityEndEvent) {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity;
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
PAGE_ACTIVITY_VALIDATION_DELAY);
return { clickActionBase: clickActionBase, hadActivityOnPointerDown: function () { return hadActivityOnPointerDown; } };
}
function startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown, getClickEventTimeStamp) {
var click = newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent);
function startClickAction(configuration, lifeCycle, domMutationObservable, history, stopObservable, appendClickToClickChain, clickActionBase, startEvent, getUserActivity, hadActivityOnPointerDown) {
var click = newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent);
if (configuration.trackFrustrations) {

@@ -154,3 +152,3 @@ appendClickToClickChain(click);

}
function newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent) {
function newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent) {
var id = generateUUID();

@@ -201,3 +199,3 @@ var startClocks = clocksNow();

isStopped: function () { return status === 1 /* STOPPED */ || status === 2 /* FINALIZED */; },
clone: function () { return newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent); },
clone: function () { return newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent); },
validate: function (domEvents) {

@@ -209,3 +207,2 @@ stop();

var _a = eventCountsSubscription.eventCounts, resourceCount = _a.resourceCount, errorCount = _a.errorCount, longTaskCount = _a.longTaskCount;
var clickEventTimeStamp = getClickEventTimeStamp();
var clickAction = assign({

@@ -224,3 +221,2 @@ type: "click" /* CLICK */,

event: startEvent,
pointerUpDelay: clickEventTimeStamp && elapsed(startClocks.timeStamp, clickEventTimeStamp),
}, clickActionBase);

@@ -227,0 +223,0 @@ lifeCycle.notify(1 /* AUTO_ACTION_COMPLETED */, clickAction);

@@ -1,2 +0,2 @@

import { shallowClone, assign, elapsed, generateUUID, monitor, ONE_MINUTE, throttle, clocksNow, clocksOrigin, timeStampNow, display, looksLikeRelativeTime, } from '@datadog/browser-core';
import { PageExitReason, shallowClone, assign, elapsed, generateUUID, monitor, ONE_MINUTE, throttle, clocksNow, clocksOrigin, timeStampNow, display, looksLikeRelativeTime, } from '@datadog/browser-core';
import { trackInitialViewTimings } from './trackInitialViewTimings';

@@ -39,3 +39,3 @@ import { trackViewMetrics } from './trackViewMetrics';

lifeCycle.subscribe(9 /* PAGE_EXITED */, function (pageExitEvent) {
if (pageExitEvent.reason === "before_unload" /* UNLOADING */) {
if (pageExitEvent.reason === PageExitReason.UNLOADING) {
currentView.end();

@@ -42,0 +42,0 @@ currentView.triggerUpdate();

{
"name": "@datadog/browser-rum-core",
"version": "4.34.0",
"version": "4.34.1",
"license": "Apache-2.0",

@@ -15,3 +15,3 @@ "main": "cjs/index.js",

"dependencies": {
"@datadog/browser-core": "4.34.0"
"@datadog/browser-core": "4.34.1"
},

@@ -26,3 +26,3 @@ "devDependencies": {

},
"gitHead": "e592cccb3f008ad1bffb649b586f6ca802c46558"
"gitHead": "963c6de2a0ecd461f6926a873bf905e3185808e2"
}

@@ -62,17 +62,38 @@ import { isIE } from '@datadog/browser-core'

describe('isShadowRoot', () => {
const parent = document.createElement('div')
parent.attachShadow({ mode: 'open' })
const parameters: Array<[Node, boolean]> = [
[parent.shadowRoot!, true],
[parent, false],
[document.body, false],
[document.createTextNode('hello'), false],
[document.createComment('hello'), false],
const notShadowDomNodes: Node[] = [
document,
document.head,
document.body,
document.createElement('div'),
document.createTextNode('hello'),
document.createComment('hello'),
]
parameters.forEach(([element, result]) => {
it(`should return ${String(result)} for "${String(element)}"`, () => {
expect(isNodeShadowRoot(element)).toBe(result)
notShadowDomNodes.forEach((element) => {
it(`should return false for "${String(element.nodeName)}"`, () => {
expect(isNodeShadowRoot(element)).toBe(false)
})
})
it('should return true for shadow root but not its host', () => {
const parent = document.createElement('div')
const shadowRoot = parent.attachShadow({ mode: 'open' })
expect(isNodeShadowRoot(parent)).toBe(false)
expect(isNodeShadowRoot(shadowRoot)).toBe(true)
})
it('should return false for a[href] despite it has a host property', () => {
const link = document.createElement('a')
link.setAttribute('href', 'http://localhost/some/path')
expect(link.host).toBeTruthy()
expect(isNodeShadowRoot(link)).toBe(false)
})
it('should return false for a form with an input[name="host"] despite it has a host property', () => {
const form = document.createElement('form')
const input = document.createElement('input')
input.setAttribute('name', 'host')
form.appendChild(input)
expect(isNodeShadowRoot(form)).toBe(false)
})
})

@@ -79,0 +100,0 @@ }

@@ -19,3 +19,3 @@ export function isTextNode(node: Node): node is Text {

const shadowRoot = node as ShadowRoot
return !!shadowRoot.host && isElementNode(shadowRoot.host)
return !!shadowRoot.host && shadowRoot.nodeType === Node.DOCUMENT_FRAGMENT_NODE && isElementNode(shadowRoot.host)
}

@@ -22,0 +22,0 @@

@@ -29,3 +29,3 @@ import type { Duration, RelativeTime, ServerDuration, TimeStamp } from '@datadog/browser-core'

const event = createNewEvent('click', { target: document.createElement('button') })
const event = createNewEvent('pointerup', { target: document.createElement('button') })
lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, {

@@ -91,3 +91,2 @@ counts: {

},
pointer_up_delay: undefined,
},

@@ -94,0 +93,0 @@ },

@@ -82,3 +82,2 @@ import type { ClocksState, Context, Observable } from '@datadog/browser-core'

position: action.position,
pointer_up_delay: action.pointerUpDelay,
},

@@ -85,0 +84,0 @@ },

@@ -1,2 +0,2 @@

import { ONE_SECOND, resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core'
import { ONE_SECOND } from '@datadog/browser-core'
import { FrustrationType } from '../../../rawRumEvent.types'

@@ -140,3 +140,2 @@ import type { Clock } from '../../../../../core/test/specHelper'

isolatedDom = createIsolatedDom()
updateExperimentalFeatures(['dead_click_fixes'])
})

@@ -146,3 +145,2 @@

isolatedDom.clear()
resetExperimentalFeatures()
})

@@ -149,0 +147,0 @@

@@ -1,2 +0,2 @@

import { elementMatches, isExperimentalFeatureEnabled, ONE_SECOND } from '@datadog/browser-core'
import { elementMatches, ONE_SECOND } from '@datadog/browser-core'
import { FrustrationType } from '../../../rawRumEvent.types'

@@ -56,2 +56,5 @@ import type { Click } from './trackClickActions'

'select,' +
// contenteditable and their descendants don't always trigger meaningful changes when manipulated
'[contenteditable],' +
'[contenteditable] *,' +
// canvas, as there is no good way to detect activity occurring on them

@@ -68,9 +71,3 @@ 'canvas,' +

}
return !elementMatches(
click.event.target,
isExperimentalFeatureEnabled('dead_click_fixes')
? // contenteditable and their descendants don't always trigger meaningful changes when manipulated
`${DEAD_CLICK_EXCLUDE_SELECTOR},[contenteditable],[contenteditable] *`
: DEAD_CLICK_EXCLUDE_SELECTOR
)
return !elementMatches(click.event.target, DEAD_CLICK_EXCLUDE_SELECTOR)
}

@@ -1,4 +0,2 @@

import { resetExperimentalFeatures, updateExperimentalFeatures } from '@datadog/browser-core'
import type { Clock } from '../../../../../core/test/specHelper'
import { createNewEvent, mockClock } from '../../../../../core/test/specHelper'
import { createNewEvent } from '../../../../../core/test/specHelper'
import type { ActionEventsHooks } from './listenActionEvents'

@@ -9,3 +7,3 @@ import { listenActionEvents } from './listenActionEvents'

let actionEventsHooks: {
onStartEvent: jasmine.Spy<ActionEventsHooks<object>['onStartEvent']>
onPointerUp: jasmine.Spy<ActionEventsHooks<object>['onPointerUp']>
onPointerDown: jasmine.Spy<ActionEventsHooks<object>['onPointerDown']>

@@ -17,3 +15,3 @@ }

actionEventsHooks = {
onStartEvent: jasmine.createSpy(),
onPointerUp: jasmine.createSpy(),
onPointerDown: jasmine.createSpy().and.returnValue({}),

@@ -42,8 +40,7 @@ }

it('listen to click events', () => {
it('listen to pointerup events', () => {
emulateClick()
expect(actionEventsHooks.onStartEvent).toHaveBeenCalledOnceWith(
expect(actionEventsHooks.onPointerUp).toHaveBeenCalledOnceWith(
{},
jasmine.objectContaining({ type: 'click' }),
jasmine.any(Function),
jasmine.objectContaining({ type: 'pointerup' }),
jasmine.any(Function)

@@ -53,13 +50,2 @@ )

it('listen to non-primary click events', () => {
// This emulates a Chrome behavior where all click events are non-primary
emulateClick({ clickEventIsPrimary: false })
expect(actionEventsHooks.onStartEvent).toHaveBeenCalledOnceWith(
{},
jasmine.objectContaining({ type: 'click' }),
jasmine.any(Function),
jasmine.any(Function)
)
})
it('aborts click lifecycle if the pointerdown event occurs on a non-element', () => {

@@ -73,10 +59,10 @@ emulateClick({ target: document.createTextNode('foo') })

emulateClick()
expect(actionEventsHooks.onStartEvent).not.toHaveBeenCalled()
expect(actionEventsHooks.onPointerUp).not.toHaveBeenCalled()
})
it('passes the context created in onPointerDown to onStartEvent', () => {
it('passes the context created in onPointerDown to onPointerUp', () => {
const context = {}
actionEventsHooks.onPointerDown.and.returnValue(context)
emulateClick()
expect(actionEventsHooks.onStartEvent.calls.mostRecent().args[0]).toBe(context)
expect(actionEventsHooks.onPointerUp.calls.mostRecent().args[0]).toBe(context)
})

@@ -86,32 +72,9 @@

emulateClick()
actionEventsHooks.onStartEvent.calls.reset()
actionEventsHooks.onPointerUp.calls.reset()
window.dispatchEvent(createNewEvent('click', { target: document.body }))
expect(actionEventsHooks.onStartEvent).not.toHaveBeenCalled()
expect(actionEventsHooks.onPointerUp).not.toHaveBeenCalled()
})
describe('dead_click_fixes experimental feature', () => {
beforeEach(() => {
stopListenEvents()
updateExperimentalFeatures(['dead_click_fixes'])
;({ stop: stopListenEvents } = listenActionEvents(actionEventsHooks))
})
afterEach(() => {
resetExperimentalFeatures()
})
it('listen to pointerup events', () => {
emulateClick()
expect(actionEventsHooks.onStartEvent).toHaveBeenCalledOnceWith(
{},
jasmine.objectContaining({ type: 'pointerup' }),
jasmine.any(Function),
jasmine.any(Function)
)
})
})
describe('selection change', () => {

@@ -186,3 +149,3 @@ let text: Text

function hasSelectionChanged() {
return actionEventsHooks.onStartEvent.calls.mostRecent().args[2]().selection
return actionEventsHooks.onPointerUp.calls.mostRecent().args[2]().selection
}

@@ -208,12 +171,2 @@

describe('input user activity', () => {
let clock: Clock
beforeEach(() => {
clock = mockClock()
})
afterEach(() => {
clock.cleanup()
})
it('click that do not trigger an input input event should not report input user activity', () => {

@@ -236,31 +189,9 @@ emulateClick()

emulateInputEvent()
clock.tick(1) // run immediate timeouts
expect(hasInputUserActivity()).toBe(true)
})
describe('with dead_click_fixes flag', () => {
beforeEach(() => {
stopListenEvents()
updateExperimentalFeatures(['dead_click_fixes'])
;({ stop: stopListenEvents } = listenActionEvents(actionEventsHooks))
})
afterEach(() => {
resetExperimentalFeatures()
})
it('input events that precede clicks should not be taken into account', () => {
emulateInputEvent()
emulateClick()
clock.tick(1) // run immediate timeouts
expect(hasInputUserActivity()).toBe(false)
})
})
it('without dead_click_fixes, input events that precede clicks should still be taken into account', () => {
it('input events that precede clicks should not be taken into account', () => {
emulateInputEvent()
emulateClick()
clock.tick(1) // run immediate timeouts
expect(hasInputUserActivity()).toBe(true)
expect(hasInputUserActivity()).toBe(false)
})

@@ -281,3 +212,3 @@

function hasInputUserActivity() {
return actionEventsHooks.onStartEvent.calls.mostRecent().args[2]().input
return actionEventsHooks.onPointerUp.calls.mostRecent().args[2]().input
}

@@ -284,0 +215,0 @@ })

@@ -1,5 +0,4 @@

import type { TimeStamp } from '@datadog/browser-core'
import { addEventListener, DOM_EVENT, isExperimentalFeatureEnabled, timeStampNow } from '@datadog/browser-core'
import { addEventListener, DOM_EVENT } from '@datadog/browser-core'
export type MouseEventOnElement = MouseEvent & { target: Element }
export type MouseEventOnElement = PointerEvent & { target: Element }

@@ -12,11 +11,6 @@ export interface UserActivity {

onPointerDown: (event: MouseEventOnElement) => ClickContext | undefined
onStartEvent: (
context: ClickContext,
event: MouseEventOnElement,
getUserActivity: () => UserActivity,
getClickEventTimeStamp: () => TimeStamp | undefined
) => void
onPointerUp: (context: ClickContext, event: MouseEventOnElement, getUserActivity: () => UserActivity) => void
}
export function listenActionEvents<ClickContext>({ onPointerDown, onStartEvent }: ActionEventsHooks<ClickContext>) {
export function listenActionEvents<ClickContext>({ onPointerDown, onPointerUp }: ActionEventsHooks<ClickContext>) {
let selectionEmptyAtPointerDown: boolean

@@ -34,11 +28,7 @@ let userActivity: UserActivity = {

(event: PointerEvent) => {
if (isValidMouseEvent(event)) {
if (isValidPointerEvent(event)) {
selectionEmptyAtPointerDown = isSelectionEmpty()
userActivity = {
selection: false,
input: isExperimentalFeatureEnabled('dead_click_fixes')
? false
: // Mimics the issue that was fixed in https://github.com/DataDog/browser-sdk/pull/1968
// The goal is to release all dead click fixes at the same time
userActivity.input,
input: false,
}

@@ -64,25 +54,9 @@ clickContext = onPointerDown(event)

window,
isExperimentalFeatureEnabled('dead_click_fixes') ? DOM_EVENT.POINTER_UP : DOM_EVENT.CLICK,
(startEvent: MouseEvent) => {
if (isValidMouseEvent(startEvent) && clickContext) {
DOM_EVENT.POINTER_UP,
(event: PointerEvent) => {
if (isValidPointerEvent(event) && clickContext) {
// Use a scoped variable to make sure the value is not changed by other clicks
const localUserActivity = userActivity
let clickEventTimeStamp: TimeStamp | undefined
onStartEvent(
clickContext,
startEvent,
() => localUserActivity,
() => clickEventTimeStamp
)
onPointerUp(clickContext, event, () => localUserActivity)
clickContext = undefined
if (isExperimentalFeatureEnabled('dead_click_fixes')) {
addEventListener(
window,
DOM_EVENT.CLICK,
() => {
clickEventTimeStamp = timeStampNow()
},
{ capture: true, once: true }
)
}
}

@@ -115,3 +89,3 @@ },

function isValidMouseEvent(event: MouseEvent): event is MouseEventOnElement {
function isValidPointerEvent(event: PointerEvent): event is MouseEventOnElement {
return (

@@ -121,7 +95,4 @@ event.target instanceof Element &&

// the future.
// On Chrome, click events are PointerEvent with `isPrimary = false`, but we should still
// consider them valid. This could be removed when we enable the `click-action-on-pointerup`
// flag, since we won't rely on click events anymore.
(event.type === 'click' || (event as PointerEvent).isPrimary !== false)
event.isPrimary !== false
)
}

@@ -90,3 +90,3 @@ import type { Context, Duration } from '@datadog/browser-core'

clock.tick(EXPIRE_DELAY)
const domEvent = createNewEvent('click', { target: document.createElement('button') })
const domEvent = createNewEvent('pointerup', { target: document.createElement('button') })
expect(events).toEqual([

@@ -112,3 +112,2 @@ {

events: [domEvent],
pointerUpDelay: undefined,
},

@@ -436,51 +435,30 @@ ])

describe('dead_click_fixes experimental feature', () => {
beforeEach(() => {
updateExperimentalFeatures(['dead_click_fixes'])
})
it('does not consider a click with activity happening on pointerdown as a dead click', () => {
const { clock } = setupBuilder.build()
afterEach(() => {
resetExperimentalFeatures()
})
emulateClick({ activity: { on: 'pointerdown' } })
it('does not consider a click with activity happening on pointerdown as a dead click', () => {
const { clock } = setupBuilder.build()
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].frustrationTypes).toEqual([])
})
emulateClick({ activity: { on: 'pointerdown' } })
it('activity happening on pointerdown is not taken into account for the action duration', () => {
const { clock } = setupBuilder.build()
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].frustrationTypes).toEqual([])
})
emulateClick({ activity: { on: 'pointerdown' } })
it('activity happening on pointerdown is not taken into account for the action duration', () => {
const { clock } = setupBuilder.build()
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].duration).toBe(0 as Duration)
})
emulateClick({ activity: { on: 'pointerdown' } })
it('does not consider a click with activity happening on pointerup as a dead click', () => {
const { clock } = setupBuilder.build()
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].duration).toBe(0 as Duration)
})
emulateClick({ activity: { on: 'pointerup' } })
it('does not consider a click with activity happening on pointerup as a dead click', () => {
const { clock } = setupBuilder.build()
emulateClick({ activity: { on: 'pointerup' } })
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].frustrationTypes).toEqual([])
})
it('reports the delay between pointerup and click event', () => {
const { clock } = setupBuilder.build()
const pointerUpActivityDelay = 5 as Duration
emulateClick({ activity: { on: 'pointerup', delay: pointerUpActivityDelay } })
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].pointerUpDelay).toBe(pointerUpActivityDelay)
})
clock.tick(EXPIRE_DELAY)
expect(events.length).toBe(1)
expect(events[0].frustrationTypes).toEqual([])
})

@@ -487,0 +465,0 @@ })

@@ -54,3 +54,2 @@ import type { Duration, ClocksState, RelativeTime, TimeStamp } from '@datadog/browser-core'

events: Event[]
pointerUpDelay?: Duration
}

@@ -89,8 +88,3 @@

processPointerDown(configuration, lifeCycle, domMutationObservable, history, pointerDownEvent),
onStartEvent: (
{ clickActionBase, hadActivityOnPointerDown },
startEvent,
getUserActivity,
getClickEventTimeStamp
) =>
onPointerUp: ({ clickActionBase, hadActivityOnPointerDown }, startEvent, getUserActivity) =>
startClickAction(

@@ -106,4 +100,3 @@ configuration,

getUserActivity,
hadActivityOnPointerDown,
getClickEventTimeStamp
hadActivityOnPointerDown
),

@@ -164,15 +157,13 @@ })

if (isExperimentalFeatureEnabled('dead_click_fixes')) {
waitPageActivityEnd(
lifeCycle,
domMutationObservable,
configuration,
(pageActivityEndEvent) => {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
PAGE_ACTIVITY_VALIDATION_DELAY
)
}
waitPageActivityEnd(
lifeCycle,
domMutationObservable,
configuration,
(pageActivityEndEvent) => {
hadActivityOnPointerDown = pageActivityEndEvent.hadActivity
},
// We don't care about the activity duration, we just want to know whether an activity did happen
// within the "validation delay" or not. Limit the duration so the callback is called sooner.
PAGE_ACTIVITY_VALIDATION_DELAY
)

@@ -192,6 +183,5 @@ return { clickActionBase, hadActivityOnPointerDown: () => hadActivityOnPointerDown }

getUserActivity: () => UserActivity,
hadActivityOnPointerDown: () => boolean,
getClickEventTimeStamp: () => TimeStamp | undefined
hadActivityOnPointerDown: () => boolean
) {
const click = newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent)
const click = newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent)

@@ -297,3 +287,2 @@ if (configuration.trackFrustrations) {

getUserActivity: () => UserActivity,
getClickEventTimeStamp: () => TimeStamp | undefined,
clickActionBase: ClickActionBase,

@@ -350,3 +339,3 @@ startEvent: MouseEventOnElement

clone: () => newClick(lifeCycle, history, getUserActivity, getClickEventTimeStamp, clickActionBase, startEvent),
clone: () => newClick(lifeCycle, history, getUserActivity, clickActionBase, startEvent),

@@ -360,3 +349,2 @@ validate: (domEvents?: Event[]) => {

const { resourceCount, errorCount, longTaskCount } = eventCountsSubscription.eventCounts
const clickEventTimeStamp = getClickEventTimeStamp()
const clickAction: ClickAction = assign(

@@ -376,3 +364,2 @@ {

event: startEvent,
pointerUpDelay: clickEventTimeStamp && elapsed(startClocks.timeStamp, clickEventTimeStamp),
},

@@ -379,0 +366,0 @@ clickActionBase

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc