blob: ae32867baedf493b8ed05a3ebc29d5e656b9ea55 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chrome://resources/js/assert.js';
import {addWebUiListener} from 'chrome://resources/js/cr.js';
import {getRequiredElement} from 'chrome://resources/js/util.js';
import {html, render} from 'chrome://resources/lit/v3_0/lit.rollup.js';
let instance: TrafficLogTag|null = null;
// See components/sync/engine/events/protocol_event.h
export interface ProtocolEvent {
details: string;
proto: any;
time: string;
type: string;
}
class TrafficLogTag {
container: HTMLElement|null = null;
protocolEvents: ProtocolEvent[] = [];
knownEventTimestamps: Set<string> = new Set();
/**
* Helper to determine if the window is scrolled to its bottom limit.
* @return true if the container is scrolled to the bottom
*/
private isScrolledToBottom_(): boolean {
return (window.innerHeight + window.scrollY) >= document.body.offsetHeight;
}
/**
* Helper to scroll the window to its bottom.
*/
private scrollToBottom_() {
window.scrollTo(0, document.body.scrollHeight);
}
/**
* Callback for incoming protocol events.
* @param details The protocol event.
*/
private onReceivedProtocolEvent_(details: ProtocolEvent) {
if (this.knownEventTimestamps.has(details.time)) {
return;
}
this.knownEventTimestamps.add(details.time);
this.protocolEvents.push(details);
const shouldScrollDown = this.isScrolledToBottom_();
assert(this.container);
render(this.getTrafficLogHtml_(), this.container);
if (shouldScrollDown) {
this.scrollToBottom_();
}
}
/**
* Toggles the given traffic event entry div's "expanded" state.
* @param e the click event that triggered the toggle.
*/
private onClick_(e: Event) {
if ((e.target as HTMLElement).classList.contains('proto')) {
// We ignore proto clicks to keep it copyable.
return;
}
let trafficEventDiv = e.target as HTMLElement;
// Click might be on div's child.
if (trafficEventDiv.nodeName !== 'DIV' && trafficEventDiv.parentNode) {
trafficEventDiv = trafficEventDiv.parentNode as HTMLElement;
}
trafficEventDiv.classList.toggle('traffic-event-entry-expanded-fullscreen');
}
private getTrafficLogHtml_() {
// clang-format off
return html`
${this.protocolEvents.map(item => html`
<div class="traffic-event-entry-fullscreen" @click="${this.onClick_}">
<span class="time">${(new Date(item.time)).toLocaleString()}</span>
<span class="type">${item.type}</span>
<pre class="details">${item.details}</pre>
<pre class="proto">${JSON.stringify(item.proto, null, 2)}</pre>
</div>
`)}
`;
// clang-format on
}
onLoad() {
this.container = getRequiredElement('traffic-event-fullscreen-container');
addWebUiListener(
'onProtocolEvent', this.onReceivedProtocolEvent_.bind(this));
}
static getInstance(): TrafficLogTag {
return instance || (instance = new TrafficLogTag());
}
}
document.addEventListener('DOMContentLoaded', () => {
TrafficLogTag.getInstance().onLoad();
});