UNPKG

chrome-devtools-frontend

Version:
898 lines (844 loc) 39.8 kB
// Copyright 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 {StackTrace, type StackTraceData} from './StackTrace.js'; import { PermissionsPolicySection, renderIconLink, type PermissionsPolicySectionData, } from './PermissionsPolicySection.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 NetworkForward from '../../../panels/network/forward/forward.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'; import * as LitHtml from '../../../ui/lit-html/lit-html.js'; import * as ExpandableList from '../../../ui/components/expandable_list/expandable_list.js'; import * as ReportView from '../../../ui/components/report_view/report_view.js'; import * as IconButton from '../../../ui/components/icon_button/icon_button.js'; import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js'; import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wrapper.js'; import type * as UI from '../../../ui/legacy/legacy.js'; import * as Workspace from '../../../models/workspace/workspace.js'; import * as Components from '../../../ui/legacy/components/utils/utils.js'; import * as Protocol from '../../../generated/protocol.js'; import {OriginTrialTreeView, type OriginTrialTreeViewData} from './OriginTrialTreeView.js'; import * as Coordinator from '../../../ui/components/render_coordinator/render_coordinator.js'; import frameDetailsReportViewStyles from './frameDetailsReportView.css.js'; import {Prerender2ReasonDescription} from './Prerender2.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 A web URL (for a lot of languages this does not need to be translated, please translate only where necessary) */ 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 The origin of a URL (https://web.dev/same-site-same-origin/#origin) *(for a lot of languages this does not need to be translated, please translate only where necessary) */ 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 */ rootDescription: '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 */ childDescription: '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 Label for whether a frame is cross-origin isolated *(https://developer.chrome.com/docs/extensions/mv3/cross-origin-isolation/) *(for a lot of languages this does not need to be translated, please translate only where necessary) */ 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 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 Explanation of why cross-origin isolation is important *(https://web.dev/why-coop-coep/) *(for a lot of languages 'cross-origin isolation' does not need to be translated, please translate only where necessary) */ 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 why SharedArrayBuffer will not be available in the future *(https://developer.chrome.com/docs/extensions/mv3/cross-origin-isolation/) *(for a lot of languages 'cross-origin isolation' does not need to be translated, please translate only where necessary) */ willRequireCrossoriginIsolated: '⚠️ will require cross-origin isolated context in the future', /** *@description Explanation why SharedArrayBuffer is not available *(https://developer.chrome.com/docs/extensions/mv3/cross-origin-isolation/) *(for a lot of languages 'cross-origin isolation' does not need to be translated, please translate only where necessary). */ requiresCrossoriginIsolated: 'requires cross-origin isolated context', /** *@description Explanation for the SharedArrayBuffer availability status in case the transfer of a SAB requires the * permission policy `cross-origin-isolated` to be enabled (e.g. because the message refers to the situation in an iframe). */ transferRequiresCrossoriginIsolatedPermission: '`SharedArrayBuffer` transfer requires enabling the permission policy:', /** *@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 Label for a stack trace. If a frame is created programmatically (i.e. via JavaScript), there is a * stack trace for the line of code which caused the creation of the iframe. This is the stack trace we are showing here. */ 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 Text descripting why a frame has been indentified as an advertisement. */ parentIsAdExplanation: 'This frame is considered an ad frame because its parent frame is an ad frame.', /** *@description Text descripting why a frame has been indentified as an advertisement. */ matchedBlockingRuleExplanation: 'This frame is considered an ad frame because its current (or previous) main document is an ad resource.', /** *@description Text descripting why a frame has been indentified as an advertisement. */ createdByAdScriptExplanation: 'There was an ad script in the `(async) stack` when this frame was created. Examining the creation `stack trace` of this frame might provide more insight.', /** *@description Label for a button which when clicked causes some information to be refreshed/updated. */ refresh: 'Refresh', /** *@description Label for section of frame details view */ prerendering: 'Prerendering', /** *@description Label for subtitle of frame details view */ prerenderingStatus: 'Prerendering Status', /** *@description Label for a link to an ad script, which created the current iframe. */ creatorAdScript: 'Creator Ad Script', }; const str_ = i18n.i18n.registerUIStrings('panels/application/components/FrameDetailsView.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance(); export interface FrameDetailsReportViewData { frame: SDK.ResourceTreeModel.ResourceTreeFrame; target?: SDK.Target.Target; prerenderedUrl?: string; adScriptId: Protocol.Page.AdScriptId|null; } export class FrameDetailsReportView extends LegacyWrapper.LegacyWrapper.WrappableComponent<UI.ThrottledWidget.ThrottledWidget> { static readonly litTagName = LitHtml.literal`devtools-resources-frame-details-view`; readonly #shadow = this.attachShadow({mode: 'open'}); #frame?: SDK.ResourceTreeModel.ResourceTreeFrame; #target?: SDK.Target.Target; #prerenderedUrl?: string; #protocolMonitorExperimentEnabled = false; #permissionsPolicies: Promise<Protocol.Page.PermissionsPolicyFeatureState[]|null>|null = null; #permissionsPolicySectionData: PermissionsPolicySectionData = {policies: [], showDetails: false}; #originTrialTreeView: OriginTrialTreeView = new OriginTrialTreeView(); #linkifier = new Components.Linkifier.Linkifier(); #adScriptId: Protocol.Page.AdScriptId|null = null; constructor(frame: SDK.ResourceTreeModel.ResourceTreeFrame) { super(); this.#frame = frame; this.#prerenderedUrl = ''; this.classList.add('overflow-auto'); this.update(); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.ChildTargetManager.ChildTargetManager, SDK.ChildTargetManager.Events.TargetInfoChanged, this.targetChanged, this); frame.resourceTreeModel().addEventListener( SDK.ResourceTreeModel.Events.PrerenderingStatusUpdated, this.update, this); } update(): void { this.wrapper?.update(); } targetChanged(event: Common.EventTarget.EventTargetEvent<Protocol.Target.TargetInfo>): void { const targetInfo = event.data; if (targetInfo.subtype === 'prerender') { this.#prerenderedUrl = targetInfo.url; this.update(); } } connectedCallback(): void { this.#protocolMonitorExperimentEnabled = Root.Runtime.experiments.isEnabled('protocolMonitor'); this.#shadow.adoptedStyleSheets = [frameDetailsReportViewStyles]; } override async render(): Promise<void> { this.#adScriptId = (await this.#frame?.parentFrame()?.getAdScriptId(this.#frame?.id)) || null; const debuggerModel = this.#adScriptId?.debuggerId ? await SDK.DebuggerModel.DebuggerModel.modelForDebuggerId(this.#adScriptId?.debuggerId) : null; this.#target = debuggerModel?.target(); if (!this.#permissionsPolicies && this.#frame) { this.#permissionsPolicies = this.#frame.getPermissionsPolicyState(); } await coordinator.write('FrameDetailsView render', () => { if (!this.#frame) { return; } // Disabled until https://crbug.com/1079231 is fixed. // clang-format off LitHtml.render(LitHtml.html` <${ReportView.ReportView.Report.litTagName} .data=${{reportTitle: this.#frame.displayName()} as ReportView.ReportView.ReportData}> ${this.#renderDocumentSection()} ${this.#renderIsolationSection()} ${this.#renderApiAvailabilitySection()} ${this.#renderOriginTrial()} ${LitHtml.Directives.until(this.#permissionsPolicies?.then(policies => { this.#permissionsPolicySectionData.policies = policies || []; return LitHtml.html` <${PermissionsPolicySection.litTagName} .data=${this.#permissionsPolicySectionData as PermissionsPolicySectionData} > </${PermissionsPolicySection.litTagName}> `; }), LitHtml.nothing)} ${this.#renderPrerenderingSection()} ${this.#protocolMonitorExperimentEnabled ? this.#renderAdditionalInfoSection() : LitHtml.nothing} </${ReportView.ReportView.Report.litTagName}> `, this.#shadow, {host: this}); // clang-format on }); } #renderOriginTrial(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } this.#originTrialTreeView.classList.add('span-cols'); const frame = this.#frame; const refreshOriginTrials: () => void = () => { void frame.getOriginTrials().then(trials => { this.#originTrialTreeView.data = {trials} as OriginTrialTreeViewData; }); }; refreshOriginTrials(); return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}> ${i18n.i18n.lockedString('Origin Trials')} <${IconButton.IconButton.IconButton.litTagName} class="inline-button" .data=${{ clickHandler: refreshOriginTrials, groups: [ { iconName: 'refresh', text: i18nString(UIStrings.refresh), iconColor: 'var(--icon-default-hover)', iconWidth: '14px', iconHeight: '14px', } as IconButton.IconButton.IconWithTextData, ], } as IconButton.IconButton.IconButtonData}> </${IconButton.IconButton.IconButton.litTagName}> </${ReportView.ReportView.ReportSectionHeader.litTagName}> ${this.#originTrialTreeView} <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}> `; } #renderDocumentSection(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}>${i18nString(UIStrings.document)}</${ ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.url)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="inline-items"> ${this.#maybeRenderSourcesLinkForURL()} ${this.#maybeRenderNetworkLinkForURL()} <div class="text-ellipsis" title=${this.#frame.url}>${this.#frame.url}</div> </div> </${ReportView.ReportView.ReportValue.litTagName}> ${this.#maybeRenderUnreachableURL()} ${this.#maybeRenderOrigin()} ${LitHtml.Directives.until(this.#renderOwnerElement(), LitHtml.nothing)} ${this.#maybeRenderCreationStacktrace()} ${this.#maybeRenderAdStatus()} <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}> `; } #maybeRenderSourcesLinkForURL(): LitHtml.LitTemplate { if (!this.#frame || this.#frame.unreachableUrl()) { return LitHtml.nothing; } const sourceCode = this.#uiSourceCodeForFrame(this.#frame); return renderIconLink( 'breakpoint-circle', i18nString(UIStrings.clickToRevealInSourcesPanel), (): Promise<void> => Common.Revealer.reveal(sourceCode), ); } #maybeRenderNetworkLinkForURL(): LitHtml.LitTemplate { if (this.#frame) { const resource = this.#frame.resourceForURL(this.#frame.url); if (resource && resource.request) { const request = resource.request; return renderIconLink( 'arrow-up-down-circle', i18nString(UIStrings.clickToRevealInNetworkPanel), (): Promise<void> => { const headersTab = Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.HEADER_OVERRIDES) ? NetworkForward.UIRequestLocation.UIRequestTabs.HeadersComponent : NetworkForward.UIRequestLocation.UIRequestTabs.Headers; const requestLocation = NetworkForward.UIRequestLocation.UIRequestLocation.tab(request, headersTab); return Common.Revealer.reveal(requestLocation); }); } } return LitHtml.nothing; } #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; } #maybeRenderUnreachableURL(): LitHtml.LitTemplate { if (!this.#frame || !this.#frame.unreachableUrl()) { return LitHtml.nothing; } return LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.unreachableUrl)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="inline-items"> ${this.#renderNetworkLinkForUnreachableURL()} <div class="text-ellipsis" title=${this.#frame.unreachableUrl()}>${this.#frame.unreachableUrl()}</div> </div> </${ReportView.ReportView.ReportValue.litTagName}> `; } #renderNetworkLinkForUnreachableURL(): LitHtml.LitTemplate { if (this.#frame) { const unreachableUrl = Common.ParsedURL.ParsedURL.fromString(this.#frame.unreachableUrl()); if (unreachableUrl) { return renderIconLink( 'arrow-up-down-circle', i18nString(UIStrings.clickToRevealInNetworkPanelMight), (): void => { void Common.Revealer.reveal(NetworkForward.UIFilter.UIRequestFilter.filters([ { filterType: NetworkForward.UIFilter.FilterType.Domain, filterValue: unreachableUrl.domain(), }, { filterType: null, filterValue: unreachableUrl.path, }, ])); }, ); } } return LitHtml.nothing; } #maybeRenderOrigin(): LitHtml.LitTemplate { if (this.#frame && this.#frame.securityOrigin && this.#frame.securityOrigin !== '://') { return LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.origin)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${this.#frame.securityOrigin}>${this.#frame.securityOrigin}</div> </${ReportView.ReportView.ReportValue.litTagName}> `; } return LitHtml.nothing; } async #renderOwnerElement(): Promise<LitHtml.LitTemplate> { 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` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.ownerElement)}</${ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName} class="without-min-width"> <div class="inline-items"> <button class="link" role="link" tabindex=0 @mouseenter=${(): Promise<void>|undefined => this.#frame?.highlight()} @mouseleave=${(): void => SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight()} @click=${(): Promise<void> => Common.Revealer.reveal(linkTargetDOMNode)} title=${i18nString(UIStrings.clickToRevealInElementsPanel)} > <${IconButton.Icon.Icon.litTagName} .data=${{ iconName: 'code-circle', color: 'var(--icon-link)', width: '16px', height: '16px', } as IconButton.Icon.IconData}> </${IconButton.Icon.Icon.litTagName}> </button> <button class="link text-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)} > &lt;${linkTargetDOMNode.nodeName().toLocaleLowerCase()}&gt; </button> </div> </${ReportView.ReportView.ReportValue.litTagName}> `; // clang-format on } } return LitHtml.nothing; } #maybeRenderCreationStacktrace(): LitHtml.LitTemplate { const creationStackTraceData = this.#frame?.getCreationStackTraceData(); if (creationStackTraceData && creationStackTraceData.creationStackTrace) { // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName} title=${i18nString(UIStrings.creationStackTraceExplanation)}>${ i18nString(UIStrings.creationStackTrace)}</${ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <${StackTrace.litTagName} .data=${{ frame: this.#frame, buildStackTraceRows: Components.JSPresentationUtils.buildStackTraceRows, } as StackTraceData}> </${StackTrace.litTagName}> </${ReportView.ReportView.ReportValue.litTagName}> `; // clang-format on } return LitHtml.nothing; } #getAdFrameTypeStrings(type: Protocol.Page.AdFrameType.Child|Protocol.Page.AdFrameType.Root): {value: Platform.UIString.LocalizedString, description: Platform.UIString.LocalizedString} { switch (type) { case Protocol.Page.AdFrameType.Child: return {value: i18nString(UIStrings.child), description: i18nString(UIStrings.childDescription)}; case Protocol.Page.AdFrameType.Root: return {value: i18nString(UIStrings.root), description: i18nString(UIStrings.rootDescription)}; } } #getAdFrameExplanationString(explanation: Protocol.Page.AdFrameExplanation): Platform.UIString.LocalizedString { switch (explanation) { case Protocol.Page.AdFrameExplanation.CreatedByAdScript: return i18nString(UIStrings.createdByAdScriptExplanation); case Protocol.Page.AdFrameExplanation.MatchedBlockingRule: return i18nString(UIStrings.matchedBlockingRuleExplanation); case Protocol.Page.AdFrameExplanation.ParentIsAd: return i18nString(UIStrings.parentIsAdExplanation); } } #maybeRenderAdStatus(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } const adFrameType = this.#frame.adFrameType(); if (adFrameType === Protocol.Page.AdFrameType.None) { return LitHtml.nothing; } const typeStrings = this.#getAdFrameTypeStrings(adFrameType); const rows = [LitHtml.html`<div title=${typeStrings.description}>${typeStrings.value}</div>`]; for (const explanation of this.#frame.adFrameStatus()?.explanations || []) { rows.push(LitHtml.html`<div>${this.#getAdFrameExplanationString(explanation)}</div>`); } const adScriptLinkElement = this.#target ? this.#linkifier.linkifyScriptLocation( this.#target, this.#adScriptId?.scriptId || null, Platform.DevToolsPath.EmptyUrlString, undefined, undefined) : null; // Disabled until https://crbug.com/1079231 is fixed. // clang-format off return LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.adStatus)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <${ExpandableList.ExpandableList.ExpandableList.litTagName} .data=${ {rows} as ExpandableList.ExpandableList.ExpandableListData}></${ ExpandableList.ExpandableList.ExpandableList.litTagName}></${ReportView.ReportView.ReportValue.litTagName}> ${this.#target ? LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.creatorAdScript)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName} class="ad-script-link">${adScriptLinkElement}</${ ReportView.ReportView.ReportValue.litTagName}> ` : LitHtml.nothing} `; // clang-format on } #renderIsolationSection(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}>${i18nString(UIStrings.securityIsolation)}</${ ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.secureContext)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> ${this.#frame.isSecureContext() ? i18nString(UIStrings.yes) : i18nString(UIStrings.no)}\xA0${ this.#maybeRenderSecureContextExplanation()} </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.crossoriginIsolated)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> ${this.#frame.isCrossOriginIsolated() ? i18nString(UIStrings.yes) : i18nString(UIStrings.no)} </${ReportView.ReportView.ReportValue.litTagName}> ${LitHtml.Directives.until(this.#maybeRenderCoopCoepStatus(), LitHtml.nothing)} <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}> `; } #maybeRenderSecureContextExplanation(): LitHtml.LitTemplate { const explanation = this.#getSecureContextExplanation(); if (explanation) { return LitHtml.html`<span class="inline-comment">${explanation}</span>`; } return LitHtml.nothing; } #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; } async #maybeRenderCoopCoepStatus(): Promise<LitHtml.LitTemplate> { 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, i18n.i18n.lockedString('Cross-Origin Embedder Policy (COEP)'), Protocol.Network.CrossOriginEmbedderPolicyValue.None)} ${ this.#maybeRenderCrossOriginStatus( info.coop, i18n.i18n.lockedString('Cross-Origin Opener Policy (COOP)'), Protocol.Network.CrossOriginOpenerPolicyValue.UnsafeNone)} `; } } return LitHtml.nothing; } #maybeRenderCrossOriginStatus( info: Protocol.Network.CrossOriginEmbedderPolicyStatus|Protocol.Network.CrossOriginOpenerPolicyStatus|undefined, policyName: string, noneValue: Protocol.Network.CrossOriginEmbedderPolicyValue| Protocol.Network.CrossOriginOpenerPolicyValue): LitHtml.LitTemplate { 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` <${ReportView.ReportView.ReportKey.litTagName}>${policyName}</${ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> ${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} </${ReportView.ReportView.ReportValue.litTagName}> `; } #renderApiAvailabilitySection(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}>${i18nString(UIStrings.apiAvailability)}</${ ReportView.ReportView.ReportSectionHeader.litTagName}> <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()} <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}> `; } #renderSharedArrayBufferAvailability(): LitHtml.LitTemplate { 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) : ''); function renderHint(frame: SDK.ResourceTreeModel.ResourceTreeFrame): LitHtml.LitTemplate { switch (frame.getCrossOriginIsolatedContextType()) { case Protocol.Page.CrossOriginIsolatedContextType.Isolated: return LitHtml.nothing; case Protocol.Page.CrossOriginIsolatedContextType.NotIsolated: if (sabAvailable) { return LitHtml.html`<span class="inline-comment">${ i18nString(UIStrings.willRequireCrossoriginIsolated)}</span>`; } return LitHtml.html`<span class="inline-comment">${ i18nString(UIStrings.requiresCrossoriginIsolated)}</span>`; case Protocol.Page.CrossOriginIsolatedContextType.NotIsolatedFeatureDisabled: if (!sabTransferAvailable) { return LitHtml.html`<span class="inline-comment">${ i18nString( UIStrings .transferRequiresCrossoriginIsolatedPermission)} <code>cross-origin-isolated</code></span>`; } break; } return LitHtml.nothing; } // SharedArrayBuffer is an API name, so we don't translate it. return LitHtml.html` <${ReportView.ReportView.ReportKey.litTagName}>SharedArrayBuffers</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName} title=${tooltipText}> ${availabilityText}\xA0${renderHint(this.#frame)} </${ReportView.ReportView.ReportValue.litTagName}> `; } } return LitHtml.nothing; } #renderMeasureMemoryAvailability(): LitHtml.LitTemplate { 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` <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.measureMemory)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <span title=${tooltipText}>${ availabilityText}</span>\xA0<x-link class="link" href="https://web.dev/monitor-total-page-memory-usage/">${ i18nString(UIStrings.learnMore)}</x-link> </${ReportView.ReportView.ReportValue.litTagName}> `; } return LitHtml.nothing; } #renderPrerenderingSection(): LitHtml.LitTemplate { if (this.#prerenderedUrl && this.#prerenderedUrl !== '') { const status = Prerender2ReasonDescription['PrerenderingOngoing'].name() + ' ' + this.#prerenderedUrl; return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}> ${i18nString(UIStrings.prerendering)}</${ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.prerenderingStatus)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${status}>${status}</div> </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}>`; } if (!this.#frame || !this.#frame.prerenderFinalStatus) { return LitHtml.nothing; } const finalStatus = Prerender2ReasonDescription[this.#frame.prerenderFinalStatus].name(); if (this.#frame.prerenderDisallowedApiMethod) { const detailSection = Prerender2ReasonDescription['DisallowedApiMethod'].name(); return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}> ${i18nString(UIStrings.prerendering)}</${ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.prerenderingStatus)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${finalStatus}>${finalStatus}</div> </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${detailSection}</${ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${this.#frame.prerenderDisallowedApiMethod}> ${this.#frame.prerenderDisallowedApiMethod} </div> </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}>`; } return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName}> ${i18nString(UIStrings.prerendering)}</${ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.prerenderingStatus)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${finalStatus}>${finalStatus}</div> </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}>`; } #renderAdditionalInfoSection(): LitHtml.LitTemplate { if (!this.#frame) { return LitHtml.nothing; } return LitHtml.html` <${ReportView.ReportView.ReportSectionHeader.litTagName} title=${i18nString(UIStrings.thisAdditionalDebugging)} >${i18nString(UIStrings.additionalInformation)}</${ReportView.ReportView.ReportSectionHeader.litTagName}> <${ReportView.ReportView.ReportKey.litTagName}>${i18nString(UIStrings.frameId)}</${ ReportView.ReportView.ReportKey.litTagName}> <${ReportView.ReportView.ReportValue.litTagName}> <div class="text-ellipsis" title=${this.#frame.id}>${this.#frame.id}</div> </${ReportView.ReportView.ReportValue.litTagName}> <${ReportView.ReportView.ReportSectionDivider.litTagName}></${ ReportView.ReportView.ReportSectionDivider.litTagName}> `; } } ComponentHelpers.CustomElements.defineComponent('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; } }