| // Copyright 2020 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 Common from '../../core/common/common.js'; |
| import * as Bindings from '../../models/bindings/bindings.js'; |
| import * as Workspace from '../../models/workspace/workspace.js'; |
| |
| import { |
| ContentSecurityPolicyIssue, |
| trustedTypesPolicyViolationCode, |
| trustedTypesSinkViolationCode, |
| } from './ContentSecurityPolicyIssue.js'; |
| import {type Issue, type IssueKind, toZeroBasedLocation} from './Issue.js'; |
| import type {IssueAddedEvent, IssuesManager} from './IssuesManager.js'; |
| import {Events} from './IssuesManagerEvents.js'; |
| import {getIssueTitleFromMarkdownDescription} from './MarkdownIssueDescription.js'; |
| import {PropertyRuleIssue} from './PropertyRuleIssue.js'; |
| import {lateImportStylesheetLoadingCode, type StylesheetLoadingIssue} from './StylesheetLoadingIssue.js'; |
| |
| export class SourceFrameIssuesManager { |
| #sourceFrameMessageManager = new Bindings.PresentationConsoleMessageHelper.PresentationSourceFrameMessageManager(); |
| constructor(private readonly issuesManager: IssuesManager) { |
| this.issuesManager.addEventListener(Events.ISSUE_ADDED, this.#onIssueAdded, this); |
| this.issuesManager.addEventListener(Events.FULL_UPDATE_REQUIRED, this.#onFullUpdateRequired, this); |
| } |
| |
| #onIssueAdded(event: Common.EventTarget.EventTargetEvent<IssueAddedEvent>): void { |
| const {issue} = event.data; |
| void this.#addIssue(issue); |
| } |
| |
| async #addIssue(issue: Issue): Promise<void> { |
| if (!this.#isTrustedTypeIssue(issue) && !this.#isLateImportIssue(issue) && !this.#isPropertyRuleIssue(issue)) { |
| return; |
| } |
| const issuesModel = issue.model(); |
| if (!issuesModel) { |
| return; |
| } |
| const srcLocation = toZeroBasedLocation(issue.details().sourceCodeLocation); |
| const description = issue.getDescription(); |
| if (!description || !srcLocation) { |
| return; |
| } |
| const messageText = await getIssueTitleFromMarkdownDescription(description); |
| if (!messageText) { |
| return; |
| } |
| const clickHandler = (): void => { |
| void Common.Revealer.reveal(issue); |
| }; |
| this.#sourceFrameMessageManager.addMessage( |
| new IssueMessage(messageText, issue.getKind(), clickHandler), { |
| line: srcLocation.lineNumber, |
| column: srcLocation.columnNumber ?? -1, |
| url: srcLocation.url, |
| scriptId: srcLocation.scriptId, |
| }, |
| issuesModel.target()); |
| } |
| |
| #onFullUpdateRequired(): void { |
| this.#resetMessages(); |
| const issues = this.issuesManager.issues(); |
| for (const issue of issues) { |
| void this.#addIssue(issue); |
| } |
| } |
| |
| #isTrustedTypeIssue(issue: Issue): issue is ContentSecurityPolicyIssue { |
| return issue instanceof ContentSecurityPolicyIssue && issue.code() === trustedTypesSinkViolationCode || |
| issue.code() === trustedTypesPolicyViolationCode; |
| } |
| |
| #isPropertyRuleIssue(issue: Issue): issue is PropertyRuleIssue { |
| return issue instanceof PropertyRuleIssue; |
| } |
| |
| #isLateImportIssue(issue: Issue): issue is StylesheetLoadingIssue { |
| return issue.code() === lateImportStylesheetLoadingCode; |
| } |
| |
| #resetMessages(): void { |
| this.#sourceFrameMessageManager.clear(); |
| } |
| } |
| |
| export class IssueMessage extends Workspace.UISourceCode.Message { |
| #kind: IssueKind; |
| constructor(title: string, kind: IssueKind, clickHandler: () => void) { |
| super(Workspace.UISourceCode.Message.Level.ISSUE, title, clickHandler); |
| this.#kind = kind; |
| } |
| |
| getIssueKind(): IssueKind { |
| return this.#kind; |
| } |
| } |