UNPKG

chrome-devtools-frontend

Version:
169 lines (140 loc) 5.29 kB
// Copyright 2024 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* eslint-disable rulesdir/no-lit-render-outside-of-view */ import * as i18n from '../../../../core/i18n/i18n.js'; import * as Platform from '../../../../core/platform/platform.js'; import * as SDK from '../../../../core/sdk/sdk.js'; import * as Trace from '../../../../models/trace/trace.js'; import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js'; import * as Lit from '../../../../ui/lit/lit.js'; import * as Utils from '../../utils/utils.js'; import baseInsightComponentStyles from './baseInsightComponent.css.js'; const {html} = Lit; export class EventReferenceClick extends Event { static readonly eventName = 'eventreferenceclick'; constructor(public event: Trace.Types.Events.Event) { super(EventReferenceClick.eventName, {bubbles: true, composed: true}); } } class EventRef extends HTMLElement { readonly #shadow = this.attachShadow({mode: 'open'}); #text: string|null = null; #event: Trace.Types.Events.Event|null = null; set text(text: string) { this.#text = text; void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render); } set event(event: Trace.Types.Events.Event) { this.#event = event; void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render); } #render(): void { if (!this.#text || !this.#event) { return; } // clang-format off Lit.render(html` <style>${baseInsightComponentStyles}</style> <button type="button" class="timeline-link" @click=${(e: Event) => { e.stopPropagation(); if (this.#event) { this.dispatchEvent(new EventReferenceClick(this.#event)); } }}>${this.#text}</button> `, this.#shadow, {host: this}); // clang-format on } } type EventRefSupportedEvents = Trace.Types.Events.SyntheticNetworkRequest; export function eventRef( event: EventRefSupportedEvents, options?: {text?: string, title?: string}): Lit.TemplateResult { let title = options?.title; let text = options?.text; if (Trace.Types.Events.isSyntheticNetworkRequest(event)) { text = text ?? Utils.Helpers.shortenUrl(new URL(event.args.data.url)); title = title ?? event.args.data.url; } else { Platform.TypeScriptUtilities.assertNever( event, `unsupported event in eventRef: ${(event as Trace.Types.Events.Event).name}`); } return html`<devtools-performance-event-ref .event=${event as Trace.Types.Events.Event} .text=${text} title=${title} ></devtools-performance-event-ref>`; } class ImageRef extends HTMLElement { readonly #shadow = this.attachShadow({mode: 'open'}); #request?: Trace.Types.Events.SyntheticNetworkRequest; #imageDataUrl?: string|null; set request(request: Trace.Types.Events.SyntheticNetworkRequest) { this.#request = request; this.#imageDataUrl = undefined; void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render); } /** * This only returns a data url if the resource is currently present from the active * inspected page. */ async #getOrCreateImageDataUrl(): Promise<string|null> { if (!this.#request) { return null; } if (this.#imageDataUrl !== undefined) { return this.#imageDataUrl; } const originalUrl = this.#request.args.data.url as Platform.DevToolsPath.UrlString; const resource = SDK.ResourceTreeModel.ResourceTreeModel.resourceForURL(originalUrl); if (!resource) { this.#imageDataUrl = null; return this.#imageDataUrl; } const content = await resource.requestContentData(); if ('error' in content) { this.#imageDataUrl = null; return this.#imageDataUrl; } this.#imageDataUrl = content.asDataUrl(); return this.#imageDataUrl; } async #render(): Promise<void> { if (!this.#request) { return; } const url = this.#request.args.data.mimeType.includes('image') ? await this.#getOrCreateImageDataUrl() : null; const img = url ? html`<img src=${url} class="element-img"/>` : Lit.nothing; // clang-format off Lit.render(html` <style>${baseInsightComponentStyles}</style> <div class="image-ref"> ${img} <span class="element-img-details"> ${eventRef(this.#request)} <span class="element-img-details-size">${ i18n.ByteUtilities.bytesToString(this.#request.args.data.decodedBodyLength ?? 0) }</span> </span> </div> `, this.#shadow, {host: this}); // clang-format on } } export function imageRef(request: Trace.Types.Events.SyntheticNetworkRequest): Lit.TemplateResult { return html` <devtools-performance-image-ref .request=${request} ></devtools-performance-image-ref> `; } declare global { interface GlobalEventHandlersEventMap { [EventReferenceClick.eventName]: EventReferenceClick; } interface HTMLElementTagNameMap { 'devtools-performance-event-ref': EventRef; 'devtools-performance-image-ref': ImageRef; } } customElements.define('devtools-performance-event-ref', EventRef); customElements.define('devtools-performance-image-ref', ImageRef);