LCOV - code coverage report
Current view: top level - pkg/shell - session.ts Coverage Total Hit
Test: cockpit Lines: 80.7 % 83 67
Test Date: 2026-07-17 12:03:54

            Line data    Source code
       1          341 : // SPDX-License-Identifier: LGPL-2.1-or-later
       2              : 
       3          341 : import cockpit from "cockpit";
       4              : import { EventEmitter } from "cockpit/event";
       5              : import { Channel } from "cockpit/channel";
       6              : 
       7          341 : const _ = cockpit.gettext;
       8              : 
       9              : /* SessionController for handling idle timeouts
      10              :  */
      11              : 
      12              : interface SessionControllerEvents {
      13              :     changed: () => void;
      14              : }
      15              : 
      16          341 : export class SessionController extends EventEmitter<SessionControllerEvents> {
      17              :     active: boolean;
      18          341 :     countdown: number = -1;
      19          341 :     problem: string | null = null;
      20              : 
      21          341 :     #channel: Channel | null = null;
      22          341 :     #timeout: number = -1;
      23              : 
      24          341 :     constructor() {
      25          341 :         super();
      26           63 :         this.active = (window.parent === window || window.name.indexOf("cockpit1:") !== 0);
      27          341 :         if (this.active) {
      28          341 :             console.debug("session controller active for", window.name);
      29          341 :             this.add_window(window);
      30          341 :             this.#open_channel();
      31          341 :         }
      32          341 :     }
      33              : 
      34          341 :     add_window(win: Window) {
      35          341 :         if (!this.active)
      36          341 :             return;
      37              : 
      38              :         // NOTE: This function will be called many many times for a
      39              :         // given window, not just once. Calling addEventListener
      40              :         // multiple times is ok here, however, since we always pass
      41              :         // the exact same listener.
      42          341 :         win.addEventListener("mousemove", this.#record_activity, false);
      43          341 :         win.addEventListener("mousedown", this.#record_activity, false);
      44          341 :         win.addEventListener("keypress", this.#record_activity, false);
      45          341 :         win.addEventListener("touchmove", this.#record_activity, false);
      46          341 :         win.addEventListener("scroll", this.#record_activity, false);
      47          341 :     }
      48              : 
      49          341 :     #open_channel() {
      50          341 :         const channel = new Channel({ payload: "session-control" });
      51              : 
      52          338 :         channel.on("ready", (data) => {
      53          338 :             if (typeof data.timeout === "number")
      54          338 :                 this.#timeout = data.timeout;
      55          338 :         });
      56              : 
      57            0 :         channel.on("control", (message) => {
      58            0 :             if (message.command === "countdown" && typeof message.counter === "number") {
      59            0 :                 this.countdown = message.counter;
      60            0 :                 this.emit("changed");
      61            0 :             } else if (message.command === "logout") {
      62            0 :                 cockpit.logout(true, _("You have been logged out due to inactivity."));
      63            0 :             }
      64            0 :         });
      65              : 
      66            6 :         channel.on("close", (options) => {
      67            0 :             const problem = (typeof options.problem === "string" ? options.problem : "") || "disconnected";
      68            6 :             this.#channel = null;
      69            6 :             console.warn("session terminated: " + problem);
      70            6 :             this.problem = problem;
      71            6 :             this.emit("changed");
      72            6 :         });
      73              : 
      74          341 :         this.#channel = channel;
      75          341 :     }
      76              : 
      77          338 :     inhibit_activity_reporting(flag: boolean) {
      78          338 :         this.#inhibit_activity_reporting = flag;
      79          338 :     }
      80              : 
      81            0 :     continue_session() {
      82            0 :         if (this.countdown != -1) {
      83            0 :             this.#send_activity_notification();
      84            0 :             this.countdown = -1;
      85            0 :             this.emit("changed");
      86            0 :         }
      87            0 :     }
      88              : 
      89          341 :     #last_activity_report_time: number = 0;
      90          341 :     #inhibit_activity_reporting: boolean = false;
      91              : 
      92          295 :     #record_activity = () => {
      93          295 :         const now = Date.now();
      94          295 :         if (!this.#inhibit_activity_reporting && Math.abs(now - this.#last_activity_report_time) > 10 * 1000) {
      95          295 :             this.#last_activity_report_time = now;
      96          295 :             this.#send_activity_notification();
      97           27 :             if (this.countdown !== -1) {
      98           27 :                 this.countdown = -1;
      99           27 :                 this.emit("changed");
     100           27 :             }
     101          295 :         }
     102          295 :     };
     103              : 
     104          295 :     #send_activity_notification() {
     105          295 :         if (this.#channel && this.#timeout > 0)
     106           27 :             this.#channel.send_control({ command: "active" });
     107          295 :     }
     108          341 : }
     109              : 
     110          341 : let controller : SessionController | undefined;
     111              : 
     112          341 : export function get_session_controller(): SessionController {
     113          341 :     if (!controller)
     114          341 :         controller = new SessionController();
     115          341 :     return controller;
     116          341 : }
        

Generated by: LCOV version 2.0-1