// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import '../../../ui/components/report_view/report_view.js'; import '../../../ui/legacy/components/data_grid/data_grid.js'; import '../../../ui/kit/kit.js'; import * as i18n from '../../../core/i18n/i18n.js'; import * as SDK from '../../../core/sdk/sdk.js'; import * as Buttons from '../../../ui/components/buttons/buttons.js'; import * as UI from '../../../ui/legacy/legacy.js'; import * as Lit from '../../../ui/lit/lit.js'; import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js'; import bounceTrackingMitigationsViewStyles from './bounceTrackingMitigationsView.css.js'; const {html} = Lit; const UIStrings = { /** * @description Title text in bounce tracking mitigations view of the Application panel. */ bounceTrackingMitigationsTitle: 'Bounce tracking mitigations', /** * @description Label for the button to force bounce tracking mitigations to run. */ forceRun: 'Force run', /** * @description Label for the disabled button while bounce tracking mitigations are running */ runningMitigations: 'Running', /** * @description Heading of table which displays sites whose state was deleted by bounce tracking mitigations. */ stateDeletedFor: 'State was deleted for the following sites:', /** * @description Text shown once the deletion command has been sent to the browser process. */ checkingPotentialTrackers: 'Checking for potential bounce tracking sites.', /** * @description Link text about explanation of Bounce Tracking Mitigations. */ learnMore: 'Learn more: Bounce Tracking Mitigations', /** * @description Text shown when bounce tracking mitigations have been forced to run and * identified no potential bounce tracking sites to delete state for. This may also * indicate that bounce tracking mitigations are disabled or third-party cookies aren't being blocked. */ noPotentialBounceTrackersIdentified: 'State was not cleared for any potential bounce tracking sites. Either none were identified or third-party cookies are not blocked.', /** * @description Text shown when bounce tracking mitigations are disabled. */ featureDisabled: 'Bounce tracking mitigations are disabled.', } as const; const str_ = i18n.i18n.registerUIStrings('panels/application/components/BounceTrackingMitigationsView.ts', UIStrings); export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export const enum ScreenStatusType { INITIALIZING = 'Initializing', RUNNING = 'Running', RESULT = 'Result', DISABLED = 'Disabled', } export interface BounceTrackingMitigationsViewData { trackingSites: string[]; } export interface ViewInput { screenStatus: ScreenStatusType; trackingSites: string[]; seenButtonClick: boolean; runMitigations: () => Promise; } const renderForceRunButton = (input: ViewInput): Lit.TemplateResult => { const isMitigationRunning = (input.screenStatus === ScreenStatusType.RUNNING); // clang-format off return html` ${isMitigationRunning ? html` ${i18nString(UIStrings.runningMitigations)}`:` ${i18nString(UIStrings.forceRun)} `} `; // clang-format on }; const renderDeletedSitesOrNoSitesMessage = (input: ViewInput): Lit.LitTemplate => { if (!input.seenButtonClick) { return Lit.nothing; } if (input.trackingSites.length === 0) { // clang-format off return html` ${(input.screenStatus === ScreenStatusType.RUNNING) ? html` ${i18nString(UIStrings.checkingPotentialTrackers)}`:` ${i18nString(UIStrings.noPotentialBounceTrackersIdentified)} `} `; // clang-format on } // clang-format off return html` ${input.trackingSites.map(site => html` `)}
${i18nString(UIStrings.stateDeletedFor)}
${site}
`; // clang-format on }; const renderMainFrameInformation = (input: ViewInput): Lit.LitTemplate => { if (input.screenStatus === ScreenStatusType.INITIALIZING) { return Lit.nothing; } if (input.screenStatus === ScreenStatusType.DISABLED) { // clang-format off return html` ${i18nString(UIStrings.featureDisabled)} `; // clang-format on } // clang-format off return html` ${renderForceRunButton(input)} ${renderDeletedSitesOrNoSitesMessage(input)} ${i18nString(UIStrings.learnMore)} `; // clang-format on }; export const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement): void => { // clang-format off Lit.render(html` ${renderMainFrameInformation(input)} `, target); // clang-format on }; type ViewFunction = typeof DEFAULT_VIEW; export class BounceTrackingMitigationsView extends UI.Widget.Widget { #trackingSites: string[] = []; #screenStatus = ScreenStatusType.INITIALIZING; #seenButtonClick = false; #view: ViewFunction; constructor(element?: HTMLElement, view: ViewFunction = DEFAULT_VIEW) { super(element, {useShadowDom: true, classes: ['overflow-auto']}); this.#view = view; const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget(); if (!mainTarget) { this.#screenStatus = ScreenStatusType.RESULT; } else { void mainTarget.systemInfo().invoke_getFeatureState({featureState: 'DIPS'}).then(state => { this.#screenStatus = state.featureEnabled ? ScreenStatusType.RESULT : ScreenStatusType.DISABLED; this.requestUpdate(); }); } } override wasShown(): void { super.wasShown(); this.requestUpdate(); } override performUpdate(): void { this.#view( { screenStatus: this.#screenStatus, trackingSites: this.#trackingSites, seenButtonClick: this.#seenButtonClick, runMitigations: this.#runMitigations.bind(this), }, undefined, this.contentElement); } async #runMitigations(): Promise { const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget(); if (!mainTarget) { return; } this.#seenButtonClick = true; this.#screenStatus = ScreenStatusType.RUNNING; this.requestUpdate(); const response = await mainTarget.storageAgent().invoke_runBounceTrackingMitigations(); this.#trackingSites = []; response.deletedSites.forEach(element => { this.#trackingSites.push(element); }); this.#renderMitigationsResult(); } #renderMitigationsResult(): void { this.#screenStatus = ScreenStatusType.RESULT; this.requestUpdate(); } }