UNPKG

chrome-devtools-frontend

Version:
340 lines (301 loc) • 13.5 kB
// Copyright 2024 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. /* eslint-disable rulesdir/no-imperative-dom-api */ import * as Common from '../../core/common/common.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Platform from '../../core/platform/platform.js'; import * as Root from '../../core/root/root.js'; import * as Protocol from '../../generated/protocol.js'; import type * as IconButton from '../../ui/components/icon_button/icon_button.js'; import * as UI from '../../ui/legacy/legacy.js'; import {CookieControlsTreeElement} from './CookieControlsTreeElement.js'; import {CookieReportTreeElement} from './CookieReportTreeElement.js'; import lockIconStyles from './lockIcon.css.js'; import {OriginTreeElement} from './OriginTreeElement.js'; import { createHighlightedUrl, getSecurityStateIconForDetailedView, getSecurityStateIconForOverview, OriginGroup, } from './SecurityPanel.js'; import type {SecurityPanelSidebarTreeElement} from './SecurityPanelSidebarTreeElement.js'; import sidebarStyles from './sidebar.css.js'; const UIStrings = { /** *@description Section title for the the Security Panel's sidebar */ security: 'Security', /** *@description Section title for the the Security Panel's sidebar */ privacy: 'Privacy', /** *@description Sidebar element text in the Security panel */ cookieReport: 'Third-party cookies', /** *@description Sidebar element text in the Security panel */ flagControls: 'Controls', /** *@description Text in Security Panel of the Security panel */ mainOrigin: 'Main origin', /** *@description Text in Security Panel of the Security panel */ nonsecureOrigins: 'Non-secure origins', /** *@description Text in Security Panel of the Security panel */ secureOrigins: 'Secure origins', /** *@description Text in Security Panel of the Security panel */ unknownCanceled: 'Unknown / canceled', /** *@description Title text content in Security Panel of the Security panel */ overview: 'Overview', /** *@description Text in Security Panel of the Security panel */ reloadToViewDetails: 'Reload to view details', } as const; const str_ = i18n.i18n.registerUIStrings('panels/security/SecurityPanelSidebar.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class SecurityPanelSidebar extends UI.Widget.VBox { readonly #securitySidebarLastItemSetting: Common.Settings.Setting<string>; readonly sidebarTree: UI.TreeOutline.TreeOutlineInShadow; readonly #originGroupTitles: Map<OriginGroup, {title: string, icon?: IconButton.Icon.Icon}>; #originGroups: Map<OriginGroup, UI.TreeOutline.TreeElement>; securityOverviewElement: OriginTreeElement; readonly #cookieControlsTreeElement: CookieControlsTreeElement|undefined; readonly cookieReportTreeElement: CookieReportTreeElement|undefined; readonly #elementsByOrigin: Map<string, OriginTreeElement>; readonly #mainViewReloadMessage: UI.TreeOutline.TreeElement; #mainOrigin: string|null; constructor(element?: HTMLElement) { super(undefined, undefined, element); this.#securitySidebarLastItemSetting = Common.Settings.Settings.instance().createSetting('security-last-selected-element-path', ''); this.#mainOrigin = null; this.sidebarTree = new UI.TreeOutline.TreeOutlineInShadow(UI.TreeOutline.TreeVariant.NAVIGATION_TREE); this.sidebarTree.registerRequiredCSS(lockIconStyles, sidebarStyles); this.sidebarTree.element.classList.add('security-sidebar'); this.contentElement.appendChild(this.sidebarTree.element); if (Root.Runtime.hostConfig.devToolsPrivacyUI?.enabled) { const privacyTreeSection = this.#addSidebarSection(i18nString(UIStrings.privacy), 'privacy'); this.#cookieControlsTreeElement = new CookieControlsTreeElement(i18nString(UIStrings.flagControls), 'cookie-flag-controls'); privacyTreeSection.appendChild(this.#cookieControlsTreeElement); this.cookieReportTreeElement = new CookieReportTreeElement(i18nString(UIStrings.cookieReport), 'cookie-report'); privacyTreeSection.appendChild(this.cookieReportTreeElement); // If this if the first time this setting is set, go to the controls tool if (this.#securitySidebarLastItemSetting.get() === '') { this.#securitySidebarLastItemSetting.set(this.#cookieControlsTreeElement.elemId); } } const securitySectionTitle = i18nString(UIStrings.security); const securityTreeSection = this.#addSidebarSection(securitySectionTitle, 'security'); this.securityOverviewElement = new OriginTreeElement('security-main-view-sidebar-tree-item', this.#renderTreeElement); this.securityOverviewElement.tooltip = i18nString(UIStrings.overview); securityTreeSection.appendChild(this.securityOverviewElement); this.#originGroupTitles = new Map([ [OriginGroup.MainOrigin, {title: i18nString(UIStrings.mainOrigin)}], [ OriginGroup.NonSecure, { title: i18nString(UIStrings.nonsecureOrigins), icon: getSecurityStateIconForDetailedView( Protocol.Security.SecurityState.Insecure, `lock-icon lock-icon-${Protocol.Security.SecurityState.Insecure}`), }, ], [ OriginGroup.Secure, { title: i18nString(UIStrings.secureOrigins), icon: getSecurityStateIconForDetailedView( Protocol.Security.SecurityState.Secure, `lock-icon lock-icon-${Protocol.Security.SecurityState.Secure}`), }, ], [ OriginGroup.Unknown, { title: i18nString(UIStrings.unknownCanceled), icon: getSecurityStateIconForDetailedView( Protocol.Security.SecurityState.Unknown, `lock-icon lock-icon-${Protocol.Security.SecurityState.Unknown}`), }, ], ]); this.#originGroups = new Map(); for (const group of Object.values(OriginGroup)) { const element = this.#createOriginGroupElement( this.#originGroupTitles.get(group)?.title as string, this.#originGroupTitles.get(group)?.icon); this.#originGroups.set(group, element); securityTreeSection.appendChild(element); } this.#mainViewReloadMessage = new UI.TreeOutline.TreeElement(i18nString(UIStrings.reloadToViewDetails)); this.#mainViewReloadMessage.selectable = false; this.#mainViewReloadMessage.listItemElement.classList.add('security-main-view-reload-message'); const treeElement = this.#originGroups.get(OriginGroup.MainOrigin); (treeElement as UI.TreeOutline.TreeElement).appendChild(this.#mainViewReloadMessage); this.#clearOriginGroups(); this.#elementsByOrigin = new Map(); this.element.addEventListener('update-sidebar-selection', (event: Event) => { const id: string = (event as CustomEvent).detail.id; this.#securitySidebarLastItemSetting.set(id); }); this.showLastSelectedElement(); } showLastSelectedElement(): void { if (this.#cookieControlsTreeElement && this.#securitySidebarLastItemSetting.get() === this.#cookieControlsTreeElement.elemId) { this.#cookieControlsTreeElement.select(); this.#cookieControlsTreeElement.showElement(); } else if ( this.cookieReportTreeElement && this.#securitySidebarLastItemSetting.get() === this.cookieReportTreeElement.elemId) { this.cookieReportTreeElement.select(); this.cookieReportTreeElement.showElement(); } else { this.securityOverviewElement.select(); this.securityOverviewElement.showElement(); } } #addSidebarSection(title: string, jslogContext: string): UI.TreeOutline.TreeElement { const treeElement = new UI.TreeOutline.TreeElement(title, true, jslogContext); treeElement.listItemElement.classList.add('security-group-list-item'); treeElement.setCollapsible(false); treeElement.selectable = false; this.sidebarTree.appendChild(treeElement); UI.ARIAUtils.markAsHeading(treeElement.listItemElement, 3); UI.ARIAUtils.setLabel(treeElement.childrenListElement, title); return treeElement; } #originGroupTitle(originGroup: OriginGroup): string { return this.#originGroupTitles.get(originGroup)?.title as string; } #originGroupElement(originGroup: OriginGroup): UI.TreeOutline.TreeElement { return this.#originGroups.get(originGroup) as UI.TreeOutline.TreeElement; } #createOriginGroupElement(originGroupTitle: string, originGroupIcon?: IconButton.Icon.Icon): UI.TreeOutline.TreeElement { const originGroup = new UI.TreeOutline.TreeElement(originGroupTitle, true); originGroup.selectable = false; originGroup.expand(); originGroup.listItemElement.classList.add('security-sidebar-origins'); if (originGroupIcon) { originGroup.setLeadingIcons([originGroupIcon]); } UI.ARIAUtils.setLabel(originGroup.childrenListElement, originGroupTitle); return originGroup; } toggleOriginsList(hidden: boolean): void { for (const element of this.#originGroups.values()) { element.hidden = hidden; } } addOrigin(origin: Platform.DevToolsPath.UrlString, securityState: Protocol.Security.SecurityState): void { this.#mainViewReloadMessage.hidden = true; const originElement = new OriginTreeElement('security-sidebar-tree-item', this.#renderTreeElement, origin); originElement.tooltip = origin; this.#elementsByOrigin.set(origin, originElement); this.updateOrigin(origin, securityState); } setMainOrigin(origin: string): void { this.#mainOrigin = origin; } get mainOrigin(): string|null { return this.#mainOrigin; } get originGroups(): Map<OriginGroup, UI.TreeOutline.TreeElement> { return this.#originGroups; } updateOrigin(origin: string, securityState: Protocol.Security.SecurityState): void { const originElement = this.#elementsByOrigin.get(origin) as OriginTreeElement; originElement.setSecurityState(securityState); let newParent: UI.TreeOutline.TreeElement; if (origin === this.#mainOrigin) { newParent = this.#originGroups.get(OriginGroup.MainOrigin) as UI.TreeOutline.TreeElement; newParent.title = i18nString(UIStrings.mainOrigin); if (securityState === Protocol.Security.SecurityState.Secure) { newParent.setLeadingIcons( [getSecurityStateIconForOverview(securityState, `lock-icon lock-icon-${securityState}`)]); } else { newParent.setLeadingIcons( [getSecurityStateIconForOverview(securityState, `lock-icon lock-icon-${securityState}`)]); } UI.ARIAUtils.setLabel(newParent.childrenListElement, newParent.title); } else { switch (securityState) { case Protocol.Security.SecurityState.Secure: newParent = this.#originGroupElement(OriginGroup.Secure); break; case Protocol.Security.SecurityState.Unknown: newParent = this.#originGroupElement(OriginGroup.Unknown); break; default: newParent = this.#originGroupElement(OriginGroup.NonSecure); break; } } const oldParent = originElement.parent; if (oldParent !== newParent) { if (oldParent) { oldParent.removeChild(originElement); if (oldParent.childCount() === 0) { oldParent.hidden = true; } } newParent.appendChild(originElement); newParent.hidden = false; } } #clearOriginGroups(): void { for (const [originGroup, originGroupElement] of this.#originGroups) { if (originGroup === OriginGroup.MainOrigin) { for (let i = originGroupElement.childCount() - 1; i > 0; i--) { originGroupElement.removeChildAtIndex(i); } originGroupElement.title = this.#originGroupTitle(OriginGroup.MainOrigin); originGroupElement.hidden = false; this.#mainViewReloadMessage.hidden = false; } else { originGroupElement.removeChildren(); originGroupElement.hidden = true; } } } clearOrigins(): void { this.#clearOriginGroups(); this.#elementsByOrigin.clear(); } override focus(): void { this.sidebarTree.focus(); } #renderTreeElement(element: SecurityPanelSidebarTreeElement): void { if (element instanceof OriginTreeElement) { const securityState = element.securityState() ?? Protocol.Security.SecurityState.Unknown; const isOverviewElement = element.listItemElement.classList.contains('security-main-view-sidebar-tree-item'); const icon = isOverviewElement ? getSecurityStateIconForOverview(securityState, `lock-icon lock-icon-${securityState}`) : getSecurityStateIconForDetailedView(securityState, `security-property security-property-${securityState}`); const elementTitle = isOverviewElement ? ((): Element => { const title = document.createElement('span'); title.classList.add('title'); title.textContent = i18nString(UIStrings.overview); return title; })() : createHighlightedUrl(element.origin() ?? Platform.DevToolsPath.EmptyUrlString, securityState); element.setLeadingIcons([icon]); if (element.listItemElement.lastChild) { element.listItemElement.removeChild(element.listItemElement.lastChild); } element.listItemElement.appendChild(elementTitle); } } }