debug-server-next
Version:
Dev server for hippy-core.
174 lines (173 loc) • 7.09 kB
JavaScript
// Copyright (c) 2021 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.
import * as i18n from '../../../core/i18n/i18n.js';
import * as Common from '../../../core/common/common.js';
import * as NetworkForward from '../../../panels/network/forward/forward.js';
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
import * as Coordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
import requestLinkIconStyles from './requestLinkIcon.css.js';
const UIStrings = {
/**
* @description Title for a link to show a request in the network panel
* @example {https://example.org/index.html} url
*/
clickToShowRequestInTheNetwork: 'Click to open the network panel and show request for URL: {url}',
/**
* @description Title for an link to show a request that is unavailable because the request couldn't be resolved
*/
requestUnavailableInTheNetwork: 'Request unavailable in the network panel, try reloading the inspected page',
/**
* @description Label for the shortened URL displayed in a link to show a request in the network panel
*/
shortenedURL: 'Shortened URL',
};
const str_ = i18n.i18n.registerUIStrings('ui/components/request_link_icon/RequestLinkIcon.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export const extractShortPath = (path) => {
// 1st regex matches everything after last '/'
// if path ends with '/', 2nd regex returns everything between the last two '/'
return (/[^/]+$/.exec(path) || /[^/]+\/$/.exec(path) || [''])[0];
};
const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();
export class RequestLinkIcon extends HTMLElement {
static litTagName = LitHtml.literal `devtools-request-link-icon`;
shadow = this.attachShadow({ mode: 'open' });
linkToPreflight;
// The value `null` indicates that the request is not available,
// `undefined` that it is still being resolved.
request;
highlightHeader;
requestResolver;
displayURL = false;
networkTab;
affectedRequest;
additionalOnClickAction;
reveal = Common.Revealer.reveal;
requestResolvedPromise = Promise.resolve(undefined);
set data(data) {
this.linkToPreflight = data.linkToPreflight;
this.request = data.request;
if (data.affectedRequest) {
this.affectedRequest = { ...data.affectedRequest };
}
this.highlightHeader = data.highlightHeader;
this.networkTab = data.networkTab;
this.requestResolver = data.requestResolver;
this.displayURL = data.displayURL ?? false;
this.additionalOnClickAction = data.additionalOnClickAction;
if (data.revealOverride) {
this.reveal = data.revealOverride;
}
if (!this.request && data.affectedRequest) {
this.requestResolvedPromise = this.resolveRequest(data.affectedRequest.requestId);
}
this.render();
}
connectedCallback() {
this.shadow.adoptedStyleSheets = [requestLinkIconStyles];
}
resolveRequest(requestId) {
if (!this.requestResolver) {
throw new Error('A `RequestResolver` must be provided if an `affectedRequest` is provided.');
}
return this.requestResolver.waitFor(requestId)
.then(request => {
this.request = request;
})
.catch(() => {
this.request = null;
});
}
get data() {
return {
linkToPreflight: this.linkToPreflight,
request: this.request,
affectedRequest: this.affectedRequest,
highlightHeader: this.highlightHeader,
networkTab: this.networkTab,
requestResolver: this.requestResolver,
displayURL: this.displayURL,
additionalOnClickAction: this.additionalOnClickAction,
revealOverride: this.reveal !== Common.Revealer.reveal ? this.reveal : undefined,
};
}
iconColor() {
if (!this.request) {
return '--issue-color-yellow';
}
return '--color-link';
}
iconData() {
return {
iconName: 'network_panel_icon',
color: `var(${this.iconColor()})`,
width: '16px',
height: '16px',
};
}
handleClick(event) {
if (event.button !== 0) {
return; // Only handle left-click for now.
}
const linkedRequest = this.linkToPreflight ? this.request?.preflightRequest() : this.request;
if (!linkedRequest) {
return;
}
if (this.highlightHeader) {
const requestLocation = NetworkForward.UIRequestLocation.UIRequestLocation.header(linkedRequest, this.highlightHeader.section, this.highlightHeader.name);
this.reveal(requestLocation);
}
else {
const requestLocation = NetworkForward.UIRequestLocation.UIRequestLocation.tab(linkedRequest, this.networkTab ?? NetworkForward.UIRequestLocation.UIRequestTabs.Headers);
this.reveal(requestLocation);
}
this.additionalOnClickAction?.();
}
getTooltip() {
if (this.request) {
return i18nString(UIStrings.clickToShowRequestInTheNetwork, { url: this.request.url() });
}
return i18nString(UIStrings.requestUnavailableInTheNetwork);
}
getUrlForDisplaying() {
if (!this.request) {
return this.affectedRequest?.url;
}
return this.request.url();
}
maybeRenderURL() {
if (!this.displayURL) {
return LitHtml.nothing;
}
const url = this.getUrlForDisplaying();
if (!url) {
return LitHtml.nothing;
}
const filename = extractShortPath(url);
return LitHtml.html `<span aria-label="${i18nString(UIStrings.shortenedURL)}" title="${url}">${filename}</span>`;
}
render() {
return coordinator.write(() => {
// clang-format off
LitHtml.render(LitHtml.html `
${LitHtml.Directives.until(this.requestResolvedPromise.then(() => this.renderComponent()), this.renderComponent())}
`, this.shadow, { host: this });
// clang-format on
});
}
renderComponent() {
// clang-format off
return LitHtml.html `
<span class=${LitHtml.Directives.classMap({ 'link': Boolean(this.request) })}
=${this.handleClick}>
<${IconButton.Icon.Icon.litTagName} .data=${this.iconData()}
title=${this.getTooltip()}></${IconButton.Icon.Icon.litTagName}>
${this.maybeRenderURL()}
</span>`;
// clang-format on
}
}
ComponentHelpers.CustomElements.defineComponent('devtools-request-link-icon', RequestLinkIcon);