chromium/chrome/browser/resources/privacy_sandbox/internals/mojo_timestamp.ts

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {CustomElement} from 'chrome://resources/js/custom_element.js';

import {getTemplate} from './mojo_timestamp.html.js';

class MojoTimestampElement extends CustomElement {
  static observedAttributes = ['ts'];

  static override get template() {
    return getTemplate();
  }

  mojoTsToJsDate(mojoTs: bigint) {
    // The Javascript `Date()` is based off of the number of milliseconds since
    // the UNIX epoch (1970-01-01 00::00:00 UTC), while `internalValue``
    // of the `base::Time` (represented in mojom.Time) represents the
    // number of microseconds since the Windows FILETIME epoch
    // (1601-01-01 00:00:00 UTC). This computes the final Javascript time by
    // computing the epoch delta and the conversion from microseconds to
    // milliseconds.
    const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
    const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
    // `epochDeltaInMs` is equal to `base::Time::kTimeTToMicrosecondsOffset`.
    const epochDeltaInMs = unixEpoch - windowsEpoch;
    const timeInMs = Number(mojoTs) / 1000;
    return new Date(timeInMs - epochDeltaInMs);
  }

  attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
    if (name === 'ts') {
      const elem = this.shadowRoot!.querySelector<HTMLElement>('#time')!;
      const ts: bigint = BigInt(newValue);
      if (ts === BigInt(0)) {
        elem.textContent = 'epoch';
        elem.classList.add('none');
      } else {
        const date = this.mojoTsToJsDate(ts);
        elem.textContent = date.toUTCString();
        elem.classList.remove('none');
      }
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'mojo-timestamp': MojoTimestampElement;
  }
}

customElements.define('mojo-timestamp', MojoTimestampElement);