/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ import { actionCreators as ac, actionTypes as at, } from "common/Actions.sys.mjs"; import { perfService as perfSvc } from "content-src/lib/perf-service"; const VISIBLE = "visible"; const VISIBILITY_CHANGE_EVENT = "visibilitychange"; export class DetectUserSessionStart { constructor(store, options = {}) { this._store = store; // Overrides for testing this.document = options.document || global.document; this._perfService = options.perfService || perfSvc; this._onVisibilityChange = this._onVisibilityChange.bind(this); } /** * sendEventOrAddListener - Notify immediately if the page is already visible, * or else set up a listener for when visibility changes. * This is needed for accurate session tracking for telemetry, * because tabs are pre-loaded. */ sendEventOrAddListener() { if (this.document.visibilityState === VISIBLE) { // If the document is already visible, to the user, send a notification // immediately that a session has started. this._sendEvent(); } else { // If the document is not visible, listen for when it does become visible. this.document.addEventListener( VISIBILITY_CHANGE_EVENT, this._onVisibilityChange ); } } /** * _sendEvent - Sends a message to the main process to indicate the current * tab is now visible to the user, includes the * visibility_event_rcvd_ts time in ms from the UNIX epoch. */ _sendEvent() { this._perfService.mark("visibility_event_rcvd_ts"); try { let visibility_event_rcvd_ts = this._perfService.getMostRecentAbsMarkStartByName( "visibility_event_rcvd_ts" ); this._store.dispatch( ac.AlsoToMain({ type: at.SAVE_SESSION_PERF_DATA, data: { visibility_event_rcvd_ts }, }) ); } catch (ex) { // If this failed, it's likely because the `privacy.resistFingerprinting` // pref is true. We should at least not blow up. } } /** * _onVisibilityChange - If the visibility has changed to visible, sends a notification * and removes the event listener. This should only be called once per tab. */ _onVisibilityChange() { if (this.document.visibilityState === VISIBLE) { this._sendEvent(); this.document.removeEventListener( VISIBILITY_CHANGE_EVENT, this._onVisibilityChange ); } } }