blob: 79b2dcc79da3c68a8b5c8fc7924843d58cfc8848 [file] [log] [blame]
// Copyright 2020 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 type {StackTraceData} from './StackTrace.js';
import * as Bindings from '../../models/bindings/bindings.js';
import * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Network from '../network/network.js';
import * as Platform from '../../core/platform/platform.js';
import * as Root from '../../core/root/root.js';
import * as SDK from '../../core/sdk/sdk.js'; // eslint-disable-line no-unused-vars
import * as LitHtml from '../../third_party/lit-html/lit-html.js';
import * as WebComponents from '../../ui/components/components.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as Workspace from '../../workspace/workspace.js';
import * as Components from '../../components/components.js';
const UIStrings = {
/**
*@description Section header in the Frame Details view
*/
additionalInformation: 'Additional Information',
/**
*@description Explanation for why the additional information section is being shown
*/
thisAdditionalDebugging:
'This additional (debugging) information is shown because the \'Protocol Monitor\' experiment is enabled.',
/**
*@description Label for subtitle of frame details view
*/
frameId: 'Frame ID',
/**
*@description Name of a network resource type
*/
document: 'Document',
/**
*@description Text for web URLs
*/
url: 'URL',
/**
*@description Title for a link to the Sources panel
*/
clickToRevealInSourcesPanel: 'Click to reveal in Sources panel',
/**
*@description Title for a link to the Network panel
*/
clickToRevealInNetworkPanel: 'Click to reveal in Network panel',
/**
*@description Title for unreachable URL field
*/
unreachableUrl: 'Unreachable URL',
/**
*@description Title for a link that applies a filter to the network panel
*/
clickToRevealInNetworkPanelMight: 'Click to reveal in Network panel (might require page reload)',
/**
*@description Text for the origin of something
*/
origin: 'Origin',
/**
*@description Related node label in Timeline UIUtils of the Performance panel
*/
ownerElement: 'Owner Element',
/**
*@description Title for a link to the Elements panel
*/
clickToRevealInElementsPanel: 'Click to reveal in Elements panel',
/**
*@description Title for ad frame type field
*/
adStatus: 'Ad Status',
/**
*@description Description for ad frame type
*/
thisFrameHasBeenIdentifiedAsThe: 'This frame has been identified as the root frame of an ad',
/**
*@description Value for ad frame type
*/
root: 'root',
/**
*@description Description for ad frame type
*/
thisFrameHasBeenIdentifiedAsTheA: 'This frame has been identified as a child frame of an ad',
/**
*@description Value for ad frame type
*/
child: 'child',
/**
*@description Section header in the Frame Details view
*/
securityIsolation: 'Security & Isolation',
/**
*@description Row title for in the Frame Details view
*/
secureContext: 'Secure Context',
/**
*@description Text in Timeline indicating that input has happened recently
*/
yes: 'Yes',
/**
*@description Text in Timeline indicating that input has not happened recently
*/
no: 'No',
/**
*@description Row title for in the Frame Details view
*/
crossoriginIsolated: 'Cross-Origin Isolated',
/**
*@description Explanatory text in the Frame Details view
*/
localhostIsAlwaysASecureContext: 'Localhost is always a secure context',
/**
*@description Explanatory text in the Frame Details view
*/
aFrameAncestorIsAnInsecure: 'A frame ancestor is an insecure context',
/**
*@description Explanatory text in the Frame Details view
*/
theFramesSchemeIsInsecure: 'The frame\'s scheme is insecure',
/**
*@description Row title in the Frame Details view
*/
crossoriginEmbedderPolicy: 'Cross-Origin Embedder Policy',
/**
*@description Row title in the Frame Details view
*/
crossoriginOpenerPolicy: 'Cross-Origin Opener Policy',
/**
*@description This label specifies the server endpoints to which the server is reporting errors
*and warnings through the Report-to API. Following this label will be the URL of the server.
*/
reportingTo: 'reporting to',
/**
*@description Section header in the Frame Details view
*/
apiAvailability: 'API availability',
/**
*@description Explanatory text in the Frame Details view for the API availability section
*/
availabilityOfCertainApisDepends: 'Availability of certain APIs depends on the document being cross-origin isolated.',
/**
*@description Description of the SharedArrayBuffer status
*/
availableTransferable: 'available, transferable',
/**
*@description Description of the SharedArrayBuffer status
*/
availableNotTransferable: 'available, not transferable',
/**
*@description Explanation for the SharedArrayBuffer availability status
*/
unavailable: 'unavailable',
/**
*@description Tooltip for the SharedArrayBuffer availability status
*/
sharedarraybufferConstructorIs:
'SharedArrayBuffer constructor is available and SABs can be transferred via postMessage',
/**
*@description Tooltip for the SharedArrayBuffer availability status
*/
sharedarraybufferConstructorIsAvailable:
'SharedArrayBuffer constructor is available but SABs cannot be transferred via postMessage',
/**
*@description Explanation for the SharedArrayBuffer availability status
*/
WillRequireCrossoriginIsolated: '⚠️ will require cross-origin isolated context in the future',
/**
*@description Explanation for the SharedArrayBuffer availability status
*/
requiresCrossoriginIsolated: 'requires cross-origin isolated context',
/**
*@description Explanation for the Measure Memory availability status
*/
available: 'available',
/**
*@description Tooltip for the Measure Memory availability status
*/
thePerformanceAPI: 'The performance.measureUserAgentSpecificMemory() API is available',
/**
*@description Tooltip for the Measure Memory availability status
*/
thePerformancemeasureuseragentspecificmemory: 'The performance.measureUserAgentSpecificMemory() API is not available',
/**
*@description Entry in the API availability section of the frame details view
*/
measureMemory: 'Measure Memory',
/**
*@description Text that is usually a hyperlink to more documentation
*/
learnMore: 'Learn more',
/**
*@description Entry in the document section of the frame details view
*/
creationStackTrace: 'Frame Creation Stack Trace',
/**
*@description Tooltip for 'Frame Creation Stack Trace' explaining that the stack
*trace shows where in the code the frame has been created programmatically
*/
creationStackTraceExplanation: 'This frame was created programmatically. The stack trace shows where this happened.',
/**
*@description Label for a button. When clicked more details (for the content this button refers to) will be shown.
*/
showDetails: 'Show details',
/**
*@description Label for a button. When clicked some details (for the content this button refers to) will be hidden.
*/
hideDetails: 'Hide details',
/**
*@description Permissions policy is a mechanism that allows developers to enable/disable browser features and APIs
*(e.g. camera, geolocation, autoplay). In some languages, this might not need to be translated.
*/
permissionsPolicy: 'Permissions Policy',
/**
*@description Label for a list of features which are allowed according to the current Permissions policy
*(a mechanism that allows developers to enable/disable browser features and APIs (e.g. camera, geolocation, autoplay))
*/
allowedFeatures: 'Allowed Features',
/**
*@description Label for a list of features which are disabled according to the current Permissions policy
*(a mechanism that allows developers to enable/disable browser features and APIs (e.g. camera, geolocation, autoplay))
*/
disabledFeatures: 'Disabled Features',
/**
*@description Tooltip text for a link to a specific request's headers in the Network panel.
*/
clickToShowHeader: 'Click to reveal the request whose "`Permissions-Policy`" HTTP header disables this feature.',
/**
*@description Tooltip text for a link to a specific iframe in the Elements panel.
*/
clickToShowIframe: 'Click to reveal the top-most iframe which does not allow this feature in the elements panel.',
/**
*@description Text describing that a specific feature is blocked by not being included in the iframe's "allow" attribute.
*/
disabledByIframe: 'missing in iframe "`allow`" attribute',
/**
*@description Text describing that a specific feature is blocked by a Permissions Policy specified in a request header.
*/
disabledByHeader: 'disabled by "`Permissions-Policy`" header',
};
const str_ = i18n.i18n.registerUIStrings('panels/application/FrameDetailsView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class FrameDetailsView extends UI.ThrottledWidget.ThrottledWidget {
private readonly reportView = new FrameDetailsReportView();
private readonly frame: SDK.ResourceTreeModel.ResourceTreeFrame;
constructor(frame: SDK.ResourceTreeModel.ResourceTreeFrame) {
super();
this.frame = frame;
this.contentElement.classList.add('overflow-auto');
this.contentElement.appendChild(this.reportView);
this.update();
}
async doUpdate(): Promise<void> {
this.reportView.data = {frame: this.frame};
}
}
export interface FrameDetailsReportViewData {
frame: SDK.ResourceTreeModel.ResourceTreeFrame;
}
export class FrameDetailsReportView extends HTMLElement {
private readonly shadow = this.attachShadow({mode: 'open'});
private frame?: SDK.ResourceTreeModel.ResourceTreeFrame;
private protocolMonitorExperimentEnabled = false;
private showPermissionsDisallowedDetails = false;
connectedCallback(): void {
this.protocolMonitorExperimentEnabled = Root.Runtime.experiments.isEnabled('protocolMonitor');
}
set data(data: FrameDetailsReportViewData) {
this.frame = data.frame;
this.render();
}
private async render(): Promise<void> {
if (!this.frame) {
return;
}
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
LitHtml.render(LitHtml.html`
<style>
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
button ~ .text-ellipsis {
padding-left: 2px;
}
.link,
.devtools-link {
color: var(--color-link);
text-decoration: underline;
cursor: pointer;
padding: 2px 0; /* adjust focus ring size */
}
button.link {
border: none;
background: none;
font-family: inherit;
font-size: inherit;
}
.inline-comment {
padding-left: 1ex;
white-space: pre-line;
}
.inline-comment::before {
content: "(";
}
.inline-comment::after {
content: ")";
}
.inline-name {
color: var(--color-text-secondary);
padding-left: 2ex;
user-select: none;
white-space: pre-line;
}
.inline-name::after {
content: ':\u00a0';
}
.inline-items {
display: flex;
}
.span-cols {
grid-column-start: span 2;
margin: 0 0 8px 30px;
line-height: 28px;
}
.policies-list {
padding-top: 3px;
}
</style>
<devtools-report .data=${{reportTitle: this.frame.displayName()} as WebComponents.ReportView.ReportData}>
${this.renderDocumentSection()}
${this.renderIsolationSection()}
${this.renderApiAvailabilitySection()}
${LitHtml.Directives.until(this.renderPermissionPolicy(), LitHtml.nothing)}
${this.protocolMonitorExperimentEnabled ? this.renderAdditionalInfoSection() : LitHtml.nothing}
</devtools-report>
`, this.shadow);
// clang-format on
}
private async renderPermissionPolicy(): Promise<LitHtml.TemplateResult|{}> {
const policies = await (this.frame && this.frame.getPermissionsPolicyState());
if (!policies) {
return LitHtml.nothing;
}
const toggleShowPermissionsDisallowedDetails = (): void => {
this.showPermissionsDisallowedDetails = !this.showPermissionsDisallowedDetails;
this.render();
};
const renderAllowed = (): LitHtml.TemplateResult|{} => {
const allowed = policies.filter(p => p.allowed).map(p => p.feature).sort();
if (!allowed.length) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.allowedFeatures)}</devtools-report-key>
<devtools-report-value>
${allowed.join(', ')}
</devtools-report-value>
`;
};
const renderDisallowed = async(): Promise<LitHtml.TemplateResult|{}> => {
const disallowed = policies.filter(p => !p.allowed).sort((a, b) => a.feature.localeCompare(b.feature));
if (!disallowed.length) {
return LitHtml.nothing;
}
if (!this.showPermissionsDisallowedDetails) {
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.disabledFeatures)}</devtools-report-key>
<devtools-report-value>
${disallowed.map(p => p.feature).join(', ')}
<button class="link" @click=${(): void => toggleShowPermissionsDisallowedDetails()}>
${i18nString(UIStrings.showDetails)}
</button>
</devtools-report-value>
`;
}
const frameManager = SDK.FrameManager.FrameManager.instance();
const featureRows = await Promise.all(disallowed.map(async policy => {
const frame = policy.locator ? frameManager.getFrame(policy.locator.frameId) : null;
const blockReason = policy.locator?.blockReason;
const linkTargetDOMNode = await (
blockReason === Protocol.Page.PermissionsPolicyBlockReason.IframeAttribute && frame &&
frame.getOwnerDOMNodeOrDocument());
const resource = frame && frame.resourceForURL(frame.url);
const linkTargetRequest =
blockReason === Protocol.Page.PermissionsPolicyBlockReason.Header && resource && resource.request;
const blockReasonText = blockReason === Protocol.Page.PermissionsPolicyBlockReason.IframeAttribute ?
i18nString(UIStrings.disabledByIframe) :
blockReason === Protocol.Page.PermissionsPolicyBlockReason.Header ? i18nString(UIStrings.disabledByHeader) :
'';
const revealHeader = async(): Promise<void> => {
if (!linkTargetRequest) {
return;
}
const headerName =
linkTargetRequest.responseHeaderValue('permissions-policy') ? 'permissions-policy' : 'feature-policy';
const requestLocation = Network.NetworkSearchScope.UIRequestLocation.responseHeaderMatch(
linkTargetRequest,
{name: headerName, value: ''},
);
// TODO(crbug.com/1196676) Refactor to use Common.Revealer
await Network.NetworkPanel.RequestLocationRevealer.instance().reveal(requestLocation);
};
return LitHtml.html`
<div class="permissions-row">
<div>
<devtools-icon class="allowed-icon"
.data=${{color: '', iconName: 'error_icon', width: '14px'} as WebComponents.Icon.IconData}>
</devtools-icon>
</div>
<div class="feature-name text-ellipsis">
${policy.feature}
</div>
<div class="block-reason">${blockReasonText}</div>
<div>
${
linkTargetDOMNode ? this.renderIconLink(
'elements_panel_icon',
i18nString(UIStrings.clickToShowIframe),
(): Promise<void> => Common.Revealer.reveal(linkTargetDOMNode),
) :
LitHtml.nothing}
${
linkTargetRequest ? this.renderIconLink(
'network_panel_icon',
i18nString(UIStrings.clickToShowHeader),
revealHeader,
) :
LitHtml.nothing}
</div>
</div>
`;
}));
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.disabledFeatures)}</devtools-report-key>
<devtools-report-value class="policies-list">
<style>
.permissions-row {
display: flex;
line-height: 22px;
}
.permissions-row div {
padding-right: 5px;
}
.feature-name {
width: 135px;
}
.allowed-icon {
padding: 2.5px 0;
}
.block-reason {
width: 215px;
}
</style>
${featureRows}
<div class="permissions-row">
<button class="link" @click=${(): void => toggleShowPermissionsDisallowedDetails()}>
${i18nString(UIStrings.hideDetails)}
</button>
</div>
</devtools-report-value>
`;
};
return LitHtml.html`
<devtools-report-section-header>${i18nString(UIStrings.permissionsPolicy)}</devtools-report-section-header>
${renderAllowed()}
${LitHtml.Directives.until(renderDisallowed(), LitHtml.nothing)}
<devtools-report-divider></devtools-report-divider>
`;
}
private renderDocumentSection(): LitHtml.TemplateResult|{} {
if (!this.frame) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-section-header>${i18nString(UIStrings.document)}</devtools-report-section-header>
<devtools-report-key>${i18nString(UIStrings.url)}</devtools-report-key>
<devtools-report-value>
<div class="inline-items">
${this.maybeRenderSourcesLinkForURL()}
${this.maybeRenderNetworkLinkForURL()}
<div class="text-ellipsis" title=${this.frame.url}>${this.frame.url}</div>
</div>
</devtools-report-value>
${this.maybeRenderUnreachableURL()}
${this.maybeRenderOrigin()}
${LitHtml.Directives.until(this.renderOwnerElement(), LitHtml.nothing)}
${this.maybeRenderCreationStacktrace()}
${this.maybeRenderAdStatus()}
<devtools-report-divider></devtools-report-divider>
`;
}
private maybeRenderSourcesLinkForURL(): LitHtml.TemplateResult|{} {
if (!this.frame || this.frame.unreachableUrl()) {
return LitHtml.nothing;
}
const sourceCode = this.uiSourceCodeForFrame(this.frame);
return this.renderIconLink(
'sources_panel_icon',
i18nString(UIStrings.clickToRevealInSourcesPanel),
(): Promise<void> => Common.Revealer.reveal(sourceCode),
);
}
private maybeRenderNetworkLinkForURL(): LitHtml.TemplateResult|{} {
if (this.frame) {
const resource = this.frame.resourceForURL(this.frame.url);
if (resource && resource.request) {
const request = resource.request;
return this.renderIconLink(
'network_panel_icon',
i18nString(UIStrings.clickToRevealInNetworkPanel),
(): Promise<void> =>
Network.NetworkPanel.NetworkPanel.selectAndShowRequest(request, Network.NetworkItemView.Tabs.Headers),
);
}
}
return LitHtml.nothing;
}
private renderIconLink(
iconName: string, title: Platform.UIString.LocalizedString,
clickHandler: (() => void)|(() => Promise<void>)): LitHtml.TemplateResult {
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
return LitHtml.html`
<button class="link" role="link" tabindex=0 @click=${clickHandler} title=${title}>
<devtools-icon .data=${{
iconName: iconName,
color: 'var(--color-primary)',
width: '16px',
height: '16px',
} as WebComponents.Icon.IconData}>
</button>
`;
// clang-format on
}
private uiSourceCodeForFrame(frame: SDK.ResourceTreeModel.ResourceTreeFrame): Workspace.UISourceCode.UISourceCode
|null {
for (const project of Workspace.Workspace.WorkspaceImpl.instance().projects()) {
const projectTarget = Bindings.NetworkProject.NetworkProject.getTargetForProject(project);
if (projectTarget && projectTarget === frame.resourceTreeModel().target()) {
const uiSourceCode = project.uiSourceCodeForURL(frame.url);
if (uiSourceCode) {
return uiSourceCode;
}
}
}
return null;
}
private maybeRenderUnreachableURL(): LitHtml.TemplateResult|{} {
if (!this.frame || !this.frame.unreachableUrl()) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.unreachableUrl)}</devtools-report-key>
<devtools-report-value>
<div class="inline-items">
${this.renderNetworkLinkForUnreachableURL()}
<div class="text-ellipsis" title=${this.frame.unreachableUrl()}>${this.frame.unreachableUrl()}</div>
</div>
</devtools-report-value>
`;
}
private renderNetworkLinkForUnreachableURL(): LitHtml.TemplateResult|{} {
if (this.frame) {
const unreachableUrl = Common.ParsedURL.ParsedURL.fromString(this.frame.unreachableUrl());
if (unreachableUrl) {
return this.renderIconLink(
'network_panel_icon',
i18nString(UIStrings.clickToRevealInNetworkPanelMight),
():
void => {
Network.NetworkPanel.NetworkPanel.revealAndFilter([
{
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
// @ts-expect-error
filterType: 'domain',
filterValue: unreachableUrl.domain(),
},
{
filterType: null,
filterValue: unreachableUrl.path,
},
]);
},
);
}
}
return LitHtml.nothing;
}
private maybeRenderOrigin(): LitHtml.TemplateResult|{} {
if (this.frame && this.frame.securityOrigin && this.frame.securityOrigin !== '://') {
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.origin)}</devtools-report-key>
<devtools-report-value>
<div class="text-ellipsis" title=${this.frame.securityOrigin}>${this.frame.securityOrigin}</div>
</devtools-report-value>
`;
}
return LitHtml.nothing;
}
private async renderOwnerElement(): Promise<LitHtml.TemplateResult|{}> {
if (this.frame) {
const linkTargetDOMNode = await this.frame.getOwnerDOMNodeOrDocument();
if (linkTargetDOMNode) {
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
return LitHtml.html`
<style>
.button-icon-with-text {
vertical-align: sub;
}
.without-min-width {
min-width: auto;
}
</style>
<devtools-report-key>${i18nString(UIStrings.ownerElement)}</devtools-report-key>
<devtools-report-value class="without-min-width">
<button class="link" role="link" tabindex=0 title=${i18nString(UIStrings.clickToRevealInElementsPanel)}
@mouseenter=${(): Promise<void>|undefined => this.frame?.highlight()}
@mouseleave=${(): void => SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight()}
@click=${(): Promise<void> => Common.Revealer.reveal(linkTargetDOMNode)}
>
<devtools-icon class="button-icon-with-text" .data=${{
iconName: 'elements_panel_icon',
color: 'var(--color-primary)',
width: '16px',
height: '16px',
} as WebComponents.Icon.IconData}></devtools-icon>
<${linkTargetDOMNode.nodeName().toLocaleLowerCase()}>
</button>
</devtools-report-value>
`;
// clang-format on
}
}
return LitHtml.nothing;
}
private maybeRenderCreationStacktrace(): LitHtml.TemplateResult|{} {
if (this.frame && this.frame._creationStackTrace) {
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
return LitHtml.html`
<devtools-report-key title=${i18nString(UIStrings.creationStackTraceExplanation)}>${
i18nString(UIStrings.creationStackTrace)}</devtools-report-key>
<devtools-report-value>
<devtools-resources-stack-trace .data=${{
frame: this.frame,
buildStackTraceRows: Components.JSPresentationUtils.buildStackTraceRows,
} as StackTraceData}>
</devtools-resources-stack-trace>
</devtools-report-value>
`;
// clang-format on
}
return LitHtml.nothing;
}
private maybeRenderAdStatus(): LitHtml.TemplateResult|{} {
if (this.frame) {
if (this.frame.adFrameType() === Protocol.Page.AdFrameType.Root) {
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.adStatus)}</devtools-report-key>
<devtools-report-value title=${i18nString(UIStrings.thisFrameHasBeenIdentifiedAsThe)}>${
i18nString(UIStrings.root)}</devtools-report-value>
`;
}
if (this.frame.adFrameType() === Protocol.Page.AdFrameType.Child) {
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.adStatus)}</devtools-report-key>
<devtools-report-value title=${i18nString(UIStrings.thisFrameHasBeenIdentifiedAsTheA)}>${
i18nString(UIStrings.child)}</devtools-report-value>
`;
}
}
return LitHtml.nothing;
}
private renderIsolationSection(): LitHtml.TemplateResult|{} {
if (!this.frame) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-section-header>${i18nString(UIStrings.securityIsolation)}</devtools-report-section-header>
<devtools-report-key>${i18nString(UIStrings.secureContext)}</devtools-report-key>
<devtools-report-value>
${this.frame.isSecureContext() ? i18nString(UIStrings.yes) : i18nString(UIStrings.no)}
${this.maybeRenderSecureContextExplanation()}
</devtools-report-value>
<devtools-report-key>${i18nString(UIStrings.crossoriginIsolated)}</devtools-report-key>
<devtools-report-value>
${this.frame.isCrossOriginIsolated() ? i18nString(UIStrings.yes) : i18nString(UIStrings.no)}
</devtools-report-value>
${LitHtml.Directives.until(this.maybeRenderCoopCoepStatus(), LitHtml.nothing)}
<devtools-report-divider></devtools-report-divider>
`;
}
private maybeRenderSecureContextExplanation(): LitHtml.TemplateResult|{} {
const explanation = this.getSecureContextExplanation();
if (explanation) {
return LitHtml.html`
<span class="inline-comment">${explanation}</span>
`;
}
return LitHtml.nothing;
}
private getSecureContextExplanation(): Platform.UIString.LocalizedString|null {
switch (this.frame?.getSecureContextType()) {
case Protocol.Page.SecureContextType.Secure:
return null;
case Protocol.Page.SecureContextType.SecureLocalhost:
return i18nString(UIStrings.localhostIsAlwaysASecureContext);
case Protocol.Page.SecureContextType.InsecureAncestor:
return i18nString(UIStrings.aFrameAncestorIsAnInsecure);
case Protocol.Page.SecureContextType.InsecureScheme:
return i18nString(UIStrings.theFramesSchemeIsInsecure);
}
return null;
}
private async maybeRenderCoopCoepStatus(): Promise<LitHtml.TemplateResult|{}> {
if (this.frame) {
const model = this.frame.resourceTreeModel().target().model(SDK.NetworkManager.NetworkManager);
const info = model && await model.getSecurityIsolationStatus(this.frame.id);
if (info) {
return LitHtml.html`
${
this.maybeRenderCrossOriginStatus(
info.coep, i18nString(UIStrings.crossoriginEmbedderPolicy),
Protocol.Network.CrossOriginEmbedderPolicyValue.None)}
${
this.maybeRenderCrossOriginStatus(
info.coop, i18nString(UIStrings.crossoriginOpenerPolicy),
Protocol.Network.CrossOriginOpenerPolicyValue.UnsafeNone)}
`;
}
}
return LitHtml.nothing;
}
private maybeRenderCrossOriginStatus(
info: Protocol.Network.CrossOriginEmbedderPolicyStatus|Protocol.Network.CrossOriginOpenerPolicyStatus|undefined,
policyName: string,
noneValue: Protocol.Network.CrossOriginEmbedderPolicyValue|
Protocol.Network.CrossOriginOpenerPolicyValue): LitHtml.TemplateResult|{} {
if (!info) {
return LitHtml.nothing;
}
const isEnabled = info.value !== noneValue;
const isReportOnly = (!isEnabled && info.reportOnlyValue !== noneValue);
const endpoint = isEnabled ? info.reportingEndpoint : info.reportOnlyReportingEndpoint;
return LitHtml.html`
<devtools-report-key>${policyName}</devtools-report-key>
<devtools-report-value>
${isEnabled ? info.value : info.reportOnlyValue}
${isReportOnly ? LitHtml.html`<span class="inline-comment">report-only</span>` : LitHtml.nothing}
${
endpoint ? LitHtml.html`<span class="inline-name">${i18nString(UIStrings.reportingTo)}</span>${endpoint}` :
LitHtml.nothing}
</devtools-report-value>
`;
}
private renderApiAvailabilitySection(): LitHtml.TemplateResult|{} {
if (!this.frame) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-section-header>${i18nString(UIStrings.apiAvailability)}</devtools-report-section-header>
<div class="span-cols">
${i18nString(UIStrings.availabilityOfCertainApisDepends)}
<x-link href="https://web.dev/why-coop-coep/" class="link">${i18nString(UIStrings.learnMore)}</x-link>
</div>
${this.renderSharedArrayBufferAvailability()}
${this.renderMeasureMemoryAvailability()}
<devtools-report-divider></devtools-report-divider>
`;
}
private renderSharedArrayBufferAvailability(): LitHtml.TemplateResult|{} {
if (this.frame) {
const features = this.frame.getGatedAPIFeatures();
if (features) {
const sabAvailable = features.includes(Protocol.Page.GatedAPIFeatures.SharedArrayBuffers);
const sabTransferAvailable =
sabAvailable && features.includes(Protocol.Page.GatedAPIFeatures.SharedArrayBuffersTransferAllowed);
const availabilityText = sabTransferAvailable ?
i18nString(UIStrings.availableTransferable) :
(sabAvailable ? i18nString(UIStrings.availableNotTransferable) : i18nString(UIStrings.unavailable));
const tooltipText = sabTransferAvailable ?
i18nString(UIStrings.sharedarraybufferConstructorIs) :
(sabAvailable ? i18nString(UIStrings.sharedarraybufferConstructorIsAvailable) : '');
// SharedArrayBuffer is an API name, so we don't translate it.
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
return LitHtml.html`
<devtools-report-key>SharedArrayBuffers</devtools-report-key>
<devtools-report-value title=${tooltipText}>
${availabilityText}
${!this.frame.isCrossOriginIsolated() ?
(sabAvailable ?
LitHtml.html`<span class="inline-comment">${
i18nString(UIStrings.WillRequireCrossoriginIsolated)}</span>` :
LitHtml.html`<span class="inline-comment">${i18nString(UIStrings.requiresCrossoriginIsolated)}</span>`) :
LitHtml.nothing}
</devtools-report-value>
`;
// clang-format on
}
}
return LitHtml.nothing;
}
private renderMeasureMemoryAvailability(): LitHtml.TemplateResult|{} {
if (this.frame) {
const measureMemoryAvailable = this.frame.isCrossOriginIsolated();
const availabilityText =
measureMemoryAvailable ? i18nString(UIStrings.available) : i18nString(UIStrings.unavailable);
const tooltipText = measureMemoryAvailable ? i18nString(UIStrings.thePerformanceAPI) :
i18nString(UIStrings.thePerformancemeasureuseragentspecificmemory);
return LitHtml.html`
<devtools-report-key>${i18nString(UIStrings.measureMemory)}</devtools-report-key>
<devtools-report-value>
<span title=${tooltipText}>${availabilityText}</span>
<x-link class="link" href="https://web.dev/monitor-total-page-memory-usage/">${
i18nString(UIStrings.learnMore)}</x-link>
</devtools-report-value>
`;
}
return LitHtml.nothing;
}
private renderAdditionalInfoSection(): LitHtml.TemplateResult|{} {
if (!this.frame) {
return LitHtml.nothing;
}
return LitHtml.html`
<devtools-report-section-header
title=${i18nString(UIStrings.thisAdditionalDebugging)}
>${i18nString(UIStrings.additionalInformation)}</devtools-report-section-header>
<devtools-report-key>${i18nString(UIStrings.frameId)}</devtools-report-key>
<devtools-report-value>
<div class="text-ellipsis" title=${this.frame.id}>${this.frame.id}</div>
</devtools-report-value>
<devtools-report-divider></devtools-report-divider>
`;
}
}
customElements.define('devtools-resources-frame-details-view', FrameDetailsReportView);
declare global {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface HTMLElementTagNameMap {
'devtools-resources-frame-details-view': FrameDetailsReportView;
}
}