| // Copyright 2025 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 * as Protocol from '../../generated/protocol.js'; |
| import * as IssuesManager from '../../models/issues_manager/issues_manager.js'; |
| |
| const UIStrings = { |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formLabelForNameError: 'Incorrect use of <label for=FORM_ELEMENT>', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formDuplicateIdForInputError: 'Duplicate form field id in the same form', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formInputWithNoLabelError: 'Form field without valid aria-labelledby attribute or associated label', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formAutocompleteAttributeEmptyError: 'Incorrect use of autocomplete attribute', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formEmptyIdAndNameAttributesForInputError: 'A form field element should have an id or name attribute', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formAriaLabelledByToNonExistingId: 'An aria-labelledby attribute doesn\'t match any element id', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formInputAssignedAutocompleteValueToIdOrNameAttributeError: 'An element doesn\'t have an autocomplete attribute', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formLabelHasNeitherForNorNestedInput: 'No label associated with a form field', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formLabelForMatchesNonExistingIdError: 'Incorrect use of <label for=FORM_ELEMENT>', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| formInputHasWrongButWellIntendedAutocompleteValueError: 'Non-standard autocomplete attribute value', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| disallowedSelectChild: 'Invalid element or text node within <select>', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| disallowedOptGroupChild: 'Invalid element or text node within <optgroup>', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| nonPhrasingContentOptionChild: 'Non-phrasing content used within an <option> element', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| interactiveContentOptionChild: 'Interactive element inside of an <option> element', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| interactiveContentLegendChild: 'Interactive element inside of a <legend> element', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| interactiveContentAttributesSelectDescendant: 'Element with invalid attributes within a <select> element', |
| /** |
| * @description Tooltip text shown in the Elements panel when an element has an error. |
| */ |
| interactiveContentSummaryDescendant: 'Interactive element inside of a <summary> element', |
| } as const; |
| |
| const str_ = i18n.i18n.registerUIStrings('panels/elements/ElementIssueUtils.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| export interface ElementIssueDetails { |
| tooltip: string; |
| nodeId?: Protocol.DOM.BackendNodeId; |
| attribute?: string; |
| } |
| |
| export function getElementIssueDetails(issue: IssuesManager.Issue.Issue): ElementIssueDetails|undefined { |
| if (issue instanceof IssuesManager.GenericIssue.GenericIssue) { |
| const issueDetails = issue.details(); |
| return { |
| tooltip: getTooltipFromGenericIssue(issueDetails.errorType), |
| nodeId: issueDetails.violatingNodeId, |
| attribute: issueDetails.violatingNodeAttribute, |
| }; |
| } |
| if (issue instanceof IssuesManager.ElementAccessibilityIssue.ElementAccessibilityIssue) { |
| const issueDetails = issue.details(); |
| if (issue.isInteractiveContentAttributesSelectDescendantIssue()) { |
| return { |
| tooltip: i18nString(UIStrings.interactiveContentAttributesSelectDescendant), |
| nodeId: issueDetails.nodeId, |
| }; |
| } |
| return { |
| tooltip: getTooltipFromElementAccessibilityIssue(issueDetails.elementAccessibilityIssueReason), |
| nodeId: issueDetails.nodeId, |
| }; |
| } |
| |
| return undefined; |
| } |
| |
| function getTooltipFromGenericIssue(errorType: Protocol.Audits.GenericIssueErrorType): string { |
| switch (errorType) { |
| case Protocol.Audits.GenericIssueErrorType.FormLabelForNameError: |
| return i18nString(UIStrings.formLabelForNameError); |
| case Protocol.Audits.GenericIssueErrorType.FormDuplicateIdForInputError: |
| return i18nString(UIStrings.formDuplicateIdForInputError); |
| case Protocol.Audits.GenericIssueErrorType.FormInputWithNoLabelError: |
| return i18nString(UIStrings.formInputWithNoLabelError); |
| case Protocol.Audits.GenericIssueErrorType.FormAutocompleteAttributeEmptyError: |
| return i18nString(UIStrings.formAutocompleteAttributeEmptyError); |
| case Protocol.Audits.GenericIssueErrorType.FormEmptyIdAndNameAttributesForInputError: |
| return i18nString(UIStrings.formEmptyIdAndNameAttributesForInputError); |
| case Protocol.Audits.GenericIssueErrorType.FormAriaLabelledByToNonExistingIdError: |
| return i18nString(UIStrings.formAriaLabelledByToNonExistingId); |
| case Protocol.Audits.GenericIssueErrorType.FormInputAssignedAutocompleteValueToIdOrNameAttributeError: |
| return i18nString(UIStrings.formInputAssignedAutocompleteValueToIdOrNameAttributeError); |
| case Protocol.Audits.GenericIssueErrorType.FormLabelHasNeitherForNorNestedInputError: |
| return i18nString(UIStrings.formLabelHasNeitherForNorNestedInput); |
| case Protocol.Audits.GenericIssueErrorType.FormLabelForMatchesNonExistingIdError: |
| return i18nString(UIStrings.formLabelForMatchesNonExistingIdError); |
| case Protocol.Audits.GenericIssueErrorType.FormInputHasWrongButWellIntendedAutocompleteValueError: |
| return i18nString(UIStrings.formInputHasWrongButWellIntendedAutocompleteValueError); |
| default: |
| return ''; |
| } |
| } |
| |
| function getTooltipFromElementAccessibilityIssue(reason: Protocol.Audits.ElementAccessibilityIssueReason): string { |
| switch (reason) { |
| case Protocol.Audits.ElementAccessibilityIssueReason.DisallowedSelectChild: |
| return i18nString(UIStrings.disallowedSelectChild); |
| case Protocol.Audits.ElementAccessibilityIssueReason.DisallowedOptGroupChild: |
| return i18nString(UIStrings.disallowedOptGroupChild); |
| case Protocol.Audits.ElementAccessibilityIssueReason.NonPhrasingContentOptionChild: |
| return i18nString(UIStrings.nonPhrasingContentOptionChild); |
| case Protocol.Audits.ElementAccessibilityIssueReason.InteractiveContentOptionChild: |
| return i18nString(UIStrings.interactiveContentOptionChild); |
| case Protocol.Audits.ElementAccessibilityIssueReason.InteractiveContentLegendChild: |
| return i18nString(UIStrings.interactiveContentLegendChild); |
| case Protocol.Audits.ElementAccessibilityIssueReason.InteractiveContentSummaryDescendant: |
| return i18nString(UIStrings.interactiveContentSummaryDescendant); |
| default: |
| return ''; |
| } |
| } |