diff --git a/injected/src/features.js b/injected/src/features.js index 96a2b07728..2804a5ad85 100644 --- a/injected/src/features.js +++ b/injected/src/features.js @@ -28,6 +28,7 @@ const otherFeatures = /** @type {const} */ ([ 'breakageReporting', 'autofillPasswordImport', 'favicon', + 'telemetry', ]); /** @typedef {baseFeatures[number]|otherFeatures[number]} FeatureName */ @@ -46,7 +47,16 @@ export const platformSupport = { android: [...baseFeatures, 'webCompat', 'breakageReporting', 'duckPlayer', 'messageBridge'], 'android-broker-protection': ['brokerProtection'], 'android-autofill-password-import': ['autofillPasswordImport'], - windows: ['cookie', ...baseFeatures, 'windowsPermissionUsage', 'duckPlayer', 'brokerProtection', 'breakageReporting', 'messageBridge'], + windows: [ + 'cookie', + ...baseFeatures, + 'telemetry', + 'windowsPermissionUsage', + 'duckPlayer', + 'brokerProtection', + 'breakageReporting', + 'messageBridge', + ], firefox: ['cookie', ...baseFeatures, 'clickToLoad'], chrome: ['cookie', ...baseFeatures, 'clickToLoad'], 'chrome-mv3': ['cookie', ...baseFeatures, 'clickToLoad'], diff --git a/injected/src/features/telemetry.js b/injected/src/features/telemetry.js new file mode 100644 index 0000000000..6a47e34ec2 --- /dev/null +++ b/injected/src/features/telemetry.js @@ -0,0 +1,62 @@ +import ContentFeature from '../content-feature.js'; + +export class Telemetry extends ContentFeature { + init() { + if (this.getFeatureSettingEnabled('videoPlayback')) { + this.videoPlaybackObserve(); + } + } + + videoPlaybackObserve() { + if (document.readyState === 'loading') { + // if the document is not ready wait until it is + document.addEventListener('DOMContentLoaded', () => this.videoPlaybackObserveInner()); + } else { + this.videoPlaybackObserveInner(); + } + } + + videoPlaybackObserveInner() { + const seenVideoElements = new WeakSet(); + function addPlayObserver(video) { + if (seenVideoElements.has(video)) { + return; // already observed + } + seenVideoElements.add(video); + video.addEventListener('play', () => { + if (navigator.userActivation.isActive) { + console.log('user interaction'); + } + }); + } + + function addListenersToAllVideos(node) { + const videos = node.querySelectorAll('video'); + videos.forEach((video) => { + addPlayObserver(video); + }); + } + addListenersToAllVideos(document.body); + const observerCallback = (mutationsList) => { + for (const mutation of mutationsList) { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === Node.ELEMENT_NODE) { + if (node.tagName === 'VIDEO') { + addPlayObserver(node); + } + addListenersToAllVideos(node); + } + }); + } + } + }; + const observer = new MutationObserver(observerCallback); + observer.observe(document.body, { + childList: true, + subtree: true, + }); + } +} + +export default Telemetry;