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-06-25 09:20:42

            Line data    Source code
       1          339 : // SPDX-License-Identifier: LGPL-2.1-or-later
       2              : 
       3          339 : import cockpit from "cockpit";
       4              : import { EventEmitter } from "cockpit/event";
       5              : import { Channel } from "cockpit/channel";
       6              : 
       7          339 : const _ = cockpit.gettext;
       8              : 
       9              : /* SessionController for handling idle timeouts
      10              :  */
      11              : 
      12              : interface SessionControllerEvents {
      13              :     changed: () => void;
      14              : }
      15              : 
      16          339 : export class SessionController extends EventEmitter<SessionControllerEvents> {
      17              :     active: boolean;
      18          339 :     countdown: number = -1;
      19          339 :     problem: string | null = null;
      20              : 
      21          339 :     #channel: Channel | null = null;
      22          339 :     #timeout: number = -1;
      23              : 
      24          339 :     constructor() {
      25          339 :         super();
      26           62 :         this.active = (window.parent === window || window.name.indexOf("cockpit1:") !== 0);
      27          339 :         if (this.active) {
      28          339 :             console.debug("session controller active for", window.name);
      29          339 :             this.add_window(window);
      30          339 :             this.#open_channel();
      31          339 :         }
      32          339 :     }
      33              : 
      34          339 :     add_window(win: Window) {
      35          339 :         if (!this.active)
      36          339 :             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          339 :         win.addEventListener("mousemove", this.#record_activity, false);
      43          339 :         win.addEventListener("mousedown", this.#record_activity, false);
      44          339 :         win.addEventListener("keypress", this.#record_activity, false);
      45          339 :         win.addEventListener("touchmove", this.#record_activity, false);
      46          339 :         win.addEventListener("scroll", this.#record_activity, false);
      47          339 :     }
      48              : 
      49          339 :     #open_channel() {
      50          339 :         const channel = new Channel({ payload: "session-control" });
      51              : 
      52          336 :         channel.on("ready", (data) => {
      53          336 :             if (typeof data.timeout === "number")
      54          336 :                 this.#timeout = data.timeout;
      55          336 :         });
      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          339 :         this.#channel = channel;
      75          339 :     }
      76              : 
      77          336 :     inhibit_activity_reporting(flag: boolean) {
      78          336 :         this.#inhibit_activity_reporting = flag;
      79          336 :     }
      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          339 :     #last_activity_report_time: number = 0;
      90          339 :     #inhibit_activity_reporting: boolean = false;
      91              : 
      92          293 :     #record_activity = () => {
      93          293 :         const now = Date.now();
      94          293 :         if (!this.#inhibit_activity_reporting && Math.abs(now - this.#last_activity_report_time) > 10 * 1000) {
      95          293 :             this.#last_activity_report_time = now;
      96          293 :             this.#send_activity_notification();
      97           26 :             if (this.countdown !== -1) {
      98           26 :                 this.countdown = -1;
      99           26 :                 this.emit("changed");
     100           26 :             }
     101          293 :         }
     102          293 :     };
     103              : 
     104          293 :     #send_activity_notification() {
     105          293 :         if (this.#channel && this.#timeout > 0)
     106           26 :             this.#channel.send_control({ command: "active" });
     107          293 :     }
     108          339 : }
     109              : 
     110          339 : let controller : SessionController | undefined;
     111              : 
     112          339 : export function get_session_controller(): SessionController {
     113          339 :     if (!controller)
     114          339 :         controller = new SessionController();
     115          339 :     return controller;
     116          339 : }
        

Generated by: LCOV version 2.0-1