| // Copyright 2026 The Chromium Authors |
| // 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 type * as Platform from '../../core/platform/platform.js'; |
| import type * as SDK from '../../core/sdk/sdk.js'; |
| import type * as Protocol from '../../generated/protocol.js'; |
| import * as Bindings from '../../models/bindings/bindings.js'; |
| import type * as IssuesManager from '../../models/issues_manager/issues_manager.js'; |
| import * as Components from '../../ui/legacy/components/utils/utils.js'; |
| import * as UI from '../../ui/legacy/legacy.js'; |
| import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js'; |
| |
| import {AffectedResourcesView} from './AffectedResourcesView.js'; |
| |
| const UIStrings = { |
| /** |
| * @description Label for number of affected resources indication in issue view |
| */ |
| nViolations: '{n, plural, =1 {# violation} other {# violations}}', |
| /** |
| * @description Title for the API column in the Selective Permissions Intervention affected resources list |
| */ |
| api: 'API', |
| /** |
| * @description Title for the Script column in the Selective Permissions Intervention affected resources list |
| */ |
| script: 'Script', |
| /** |
| * @description Title for the Ad Ancestry column in the Selective Permissions Intervention affected resources list |
| */ |
| adAncestry: 'Ad Ancestry', |
| /** |
| * @description Text for unknown value |
| */ |
| unknown: 'unknown', |
| /** |
| * @description Text for loading state |
| */ |
| loading: 'loading…', |
| } as const; |
| const str_ = i18n.i18n.registerUIStrings('panels/issues/AffectedSelectivePermissionsInterventionView.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| const {widget} = UI.Widget; |
| |
| export class AffectedSelectivePermissionsInterventionView extends AffectedResourcesView { |
| readonly #linkifier = new Components.Linkifier.Linkifier(); |
| |
| protected getResourceNameWithCount(count: number): string { |
| return i18nString(UIStrings.nViolations, {n: count}); |
| } |
| |
| #render(): void { |
| const issues = Array.from(this.issue.getSelectivePermissionsInterventionIssues()); |
| // eslint-disable-next-line @devtools/no-lit-render-outside-of-view |
| render( |
| html` |
| <tr> |
| <td class="affected-resource-header">${i18nString(UIStrings.api)}</td> |
| <td class="affected-resource-header">${i18nString(UIStrings.script)}</td> |
| <td class="affected-resource-header">${i18nString(UIStrings.adAncestry)}</td> |
| </tr> |
| ${issues.map(issue => this.#renderDetail(issue))} |
| `, |
| this.affectedResources, {host: this}); |
| this.updateAffectedResourceCount(issues.length); |
| } |
| |
| #renderDetail(issue: IssuesManager.SelectivePermissionsInterventionIssue.SelectivePermissionsInterventionIssue): |
| LitTemplate { |
| const details = issue.details(); |
| const issuesModel = issue.model(); |
| |
| const stackTracePromise = (details.stackTrace && issuesModel) ? |
| this.#resolveStackTrace(details.stackTrace, issuesModel) : |
| Promise.resolve(html`<span>${i18nString(UIStrings.unknown)}</span>`); |
| |
| const target = issuesModel ? issuesModel.target() : null; |
| |
| return html` |
| <tr class="affected-resource-directive"> |
| <td>${details.apiName}</td> |
| <td>${Directives.until(stackTracePromise, html`<span>${i18nString(UIStrings.loading)}</span>`)}</td> |
| <td class="affected-resource-cell"> |
| <div class="ad-ancestry-list"> |
| ${(details.adAncestry?.ancestryChain || []).map(script => { |
| const link = |
| this.#linkifier.linkifyScriptLocation(target, script.scriptId, '' as Platform.DevToolsPath.UrlString, 0); |
| return html`<div>${link}</div>`; |
| })} |
| ${ |
| details.adAncestry?.rootScriptFilterlistRule ? |
| html`<div>Rule: ${details.adAncestry.rootScriptFilterlistRule}</div>` : |
| nothing} |
| </div> |
| </td> |
| </tr> |
| `; |
| } |
| |
| async #resolveStackTrace(stackTrace: Protocol.Runtime.StackTrace, issuesModel: SDK.IssuesModel.IssuesModel): |
| Promise<LitTemplate> { |
| const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance(); |
| const stackTraceTranslated = |
| await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime(stackTrace, issuesModel.target()); |
| return html`${widget(Components.JSPresentationUtils.StackTracePreviewContent, { |
| stackTrace: stackTraceTranslated, |
| options: {expandable: true}, |
| })}`; |
| } |
| |
| update(): void { |
| this.requestResolver.clear(); |
| this.#linkifier.reset(); |
| this.#render(); |
| } |
| } |