blob: 2f2c5bc8062306d995b1c1cf28aa97e500ac2781 [file] [log] [blame]
// 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 * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import type * as Protocol from '../../generated/protocol.js';
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
import {AffectedItem, AffectedResourcesView} from './AffectedResourcesView.js';
import * as IssuesComponents from './components/components.js';
const UIStrings = {
/**
*@description Singular or plural label for number of affected CSP (content security policy,
* see https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) directives in issue view.
*/
nDirectives: '{n, plural, =1 {# directive} other {# directives}}',
/**
*@description Indicates that a CSP error should be treated as a warning
*/
reportonly: 'report-only',
/**
*@description The kind of resolution for a mixed content issue
*/
blocked: 'blocked',
/**
*@description Tooltip for button linking to the Elements panel
*/
clickToRevealTheViolatingDomNode: 'Click to reveal the violating DOM node in the Elements panel',
/**
*@description Header for the section listing affected directives
*/
directiveC: 'Directive',
/**
*@description Label for the column in the element list in the CSS overview report
*/
element: 'Element',
/**
*@description Header for the source location column
*/
sourceLocation: 'Source location',
/**
*@description Text for the status of something
*/
status: 'Status',
/**
*@description Text that refers to the resources of the web page
*/
resourceC: 'Resource',
};
const str_ = i18n.i18n.registerUIStrings('panels/issues/AffectedDirectivesView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class AffectedDirectivesView extends AffectedResourcesView {
#appendStatus(element: Element, isReportOnly: boolean): void {
const status = document.createElement('td');
if (isReportOnly) {
status.classList.add('affected-resource-report-only-status');
status.textContent = i18nString(UIStrings.reportonly);
} else {
status.classList.add('affected-resource-blocked-status');
status.textContent = i18nString(UIStrings.blocked);
}
element.appendChild(status);
}
protected getResourceNameWithCount(count: number): Platform.UIString.LocalizedString {
return i18nString(UIStrings.nDirectives, {n: count});
}
#appendViolatedDirective(element: Element, directive: string): void {
const violatedDirective = document.createElement('td');
violatedDirective.textContent = directive;
element.appendChild(violatedDirective);
}
#appendBlockedURL(element: Element, url: Platform.DevToolsPath.UrlString): void {
const info = document.createElement('td');
info.classList.add('affected-resource-directive-info');
info.textContent = url;
element.appendChild(info);
}
#appendBlockedElement(
element: Element, nodeId: Protocol.DOM.BackendNodeId|undefined, model: SDK.IssuesModel.IssuesModel): void {
const elementsPanelLinkComponent = new IssuesComponents.ElementsPanelLink.ElementsPanelLink();
if (nodeId) {
const violatingNodeId = nodeId;
elementsPanelLinkComponent.title = i18nString(UIStrings.clickToRevealTheViolatingDomNode);
const onElementRevealIconClick: (arg0?: Event|undefined) => void = () => {
const target = model.getTargetIfNotDisposed();
if (target) {
Host.userMetrics.issuesPanelResourceOpened(this.issue.getCategory(), AffectedItem.ELEMENT);
const deferredDOMNode = new SDK.DOMModel.DeferredDOMNode(target, violatingNodeId);
void Common.Revealer.reveal(deferredDOMNode);
}
};
const onElementRevealIconMouseEnter: (arg0?: Event|undefined) => void = () => {
const target = model.getTargetIfNotDisposed();
if (target) {
const deferredDOMNode = new SDK.DOMModel.DeferredDOMNode(target, violatingNodeId);
if (deferredDOMNode) {
deferredDOMNode.highlight();
}
}
};
const onElementRevealIconMouseLeave: (arg0?: Event|undefined) => void = () => {
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
};
elementsPanelLinkComponent
.data = {onElementRevealIconClick, onElementRevealIconMouseEnter, onElementRevealIconMouseLeave};
}
const violatingNode = document.createElement('td');
violatingNode.classList.add('affected-resource-csp-info-node');
violatingNode.appendChild(elementsPanelLinkComponent);
element.appendChild(violatingNode);
}
#appendAffectedContentSecurityPolicyDetails(
cspIssues: Iterable<IssuesManager.ContentSecurityPolicyIssue.ContentSecurityPolicyIssue>): void {
const header = document.createElement('tr');
if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.inlineViolationCode) {
this.appendColumnTitle(header, i18nString(UIStrings.directiveC));
this.appendColumnTitle(header, i18nString(UIStrings.element));
this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation));
this.appendColumnTitle(header, i18nString(UIStrings.status));
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.urlViolationCode) {
this.appendColumnTitle(header, i18nString(UIStrings.resourceC), 'affected-resource-directive-info-header');
this.appendColumnTitle(header, i18nString(UIStrings.status));
this.appendColumnTitle(header, i18nString(UIStrings.directiveC));
this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation));
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.evalViolationCode) {
this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation));
this.appendColumnTitle(header, i18nString(UIStrings.directiveC));
this.appendColumnTitle(header, i18nString(UIStrings.status));
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.trustedTypesSinkViolationCode) {
this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation));
this.appendColumnTitle(header, i18nString(UIStrings.status));
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.trustedTypesPolicyViolationCode) {
this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation));
this.appendColumnTitle(header, i18nString(UIStrings.directiveC));
this.appendColumnTitle(header, i18nString(UIStrings.status));
} else {
this.updateAffectedResourceCount(0);
return;
}
this.affectedResources.appendChild(header);
let count = 0;
for (const cspIssue of cspIssues) {
count++;
this.#appendAffectedContentSecurityPolicyDetail(cspIssue);
}
this.updateAffectedResourceCount(count);
}
#appendAffectedContentSecurityPolicyDetail(
cspIssue: IssuesManager.ContentSecurityPolicyIssue.ContentSecurityPolicyIssue): void {
const element = document.createElement('tr');
element.classList.add('affected-resource-directive');
const cspIssueDetails = cspIssue.details();
const location = IssuesManager.Issue.toZeroBasedLocation(cspIssueDetails.sourceCodeLocation);
const model = cspIssue.model();
const maybeTarget = cspIssue.model()?.getTargetIfNotDisposed();
if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.inlineViolationCode && model) {
this.#appendViolatedDirective(element, cspIssueDetails.violatedDirective);
this.#appendBlockedElement(element, cspIssueDetails.violatingNodeId, model);
this.appendSourceLocation(element, location, maybeTarget);
this.#appendStatus(element, cspIssueDetails.isReportOnly);
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.urlViolationCode) {
const url = cspIssueDetails.blockedURL ? cspIssueDetails.blockedURL as Platform.DevToolsPath.UrlString :
Platform.DevToolsPath.EmptyUrlString;
this.#appendBlockedURL(element, url);
this.#appendStatus(element, cspIssueDetails.isReportOnly);
this.#appendViolatedDirective(element, cspIssueDetails.violatedDirective);
this.appendSourceLocation(element, location, maybeTarget);
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.evalViolationCode) {
this.appendSourceLocation(element, location, maybeTarget);
this.#appendViolatedDirective(element, cspIssueDetails.violatedDirective);
this.#appendStatus(element, cspIssueDetails.isReportOnly);
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.trustedTypesSinkViolationCode) {
this.appendSourceLocation(element, location, maybeTarget);
this.#appendStatus(element, cspIssueDetails.isReportOnly);
} else if (this.issue.code() === IssuesManager.ContentSecurityPolicyIssue.trustedTypesPolicyViolationCode) {
this.appendSourceLocation(element, location, maybeTarget);
this.#appendViolatedDirective(element, cspIssueDetails.violatedDirective);
this.#appendStatus(element, cspIssueDetails.isReportOnly);
} else {
return;
}
this.affectedResources.appendChild(element);
}
update(): void {
this.clear();
this.#appendAffectedContentSecurityPolicyDetails(this.issue.getCspIssues());
}
}