|
| 1 | +// Copyright 2024 The Chromium Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +import '../../ui/components/switch/switch.js'; |
| 6 | + |
| 7 | +import * as Common from '../../core/common/common.js'; |
| 8 | +import * as i18n from '../../core/i18n/i18n.js'; |
| 9 | +import * as Cards from '../../ui/components/cards/cards.js'; // eslint-disable-line @typescript-eslint/no-unused-vars |
| 10 | +import * as Input from '../../ui/components/input/input.js'; |
| 11 | +import * as UI from '../../ui/legacy/legacy.js'; |
| 12 | +import * as LitHtml from '../../ui/lit-html/lit-html.js'; |
| 13 | +import * as VisualLogging from '../../ui/visual_logging/visual_logging.js'; |
| 14 | + |
| 15 | +import cookieControlsViewStyles from './cookieControlsView.css.js'; |
| 16 | + |
| 17 | +const {render, html} = LitHtml; |
| 18 | + |
| 19 | +const UIStrings = { |
| 20 | + /** |
| 21 | + *@description Title in the view's header for the controls tool in the Privacy & Security panel |
| 22 | + */ |
| 23 | + viewTitle: 'Controls', |
| 24 | + /** |
| 25 | + *@description Explanation in the view's header about the purpose of this controls tool |
| 26 | + */ |
| 27 | + viewExplanation: 'Test how this site will perform if a user chooses to restrict third-party cookies in Chrome', |
| 28 | + /** |
| 29 | + *@description Title in the card within the controls tool |
| 30 | + */ |
| 31 | + cardTitle: 'Temporarily restrict third-party cookies', |
| 32 | + /** |
| 33 | + *@description Disclaimer beneath the card title to tell the user that the controls will only persist while devtools is open |
| 34 | + */ |
| 35 | + cardDisclaimer: 'Only when DevTools is open', |
| 36 | + /** |
| 37 | + *@description Message as part of the banner that prompts the user to reload the page to see the changes take effect. This appears when the user makes any change within the tool |
| 38 | + */ |
| 39 | + siteReloadMessage: 'To apply your updated controls, reload the page', |
| 40 | + /** |
| 41 | + *@description Title of controls section. These are exceptions that the user will be able to override to test their site |
| 42 | + */ |
| 43 | + exceptions: 'Exceptions', |
| 44 | + /** |
| 45 | + *@description Explanation of what exceptions are in this context |
| 46 | + */ |
| 47 | + exceptionsExplanation: 'Scenarios that grant access to third-party cookies', |
| 48 | + /** |
| 49 | + *@description Title for the grace period exception control |
| 50 | + */ |
| 51 | + gracePeriodTitle: 'Third-party cookie grace period', |
| 52 | + /** |
| 53 | + *@description Explanation of the grace period and a link to learn more |
| 54 | + *@example {grace period} PH1 |
| 55 | + */ |
| 56 | + gracePeriodExplanation: |
| 57 | + 'If this site or a site embedded on it is enrolled in the {PH1}, then the site can access third-party cookies', |
| 58 | + /** |
| 59 | + *@description Text used for link within the gracePeriodExplanation to let the user learn more about the grace period |
| 60 | + */ |
| 61 | + gracePeriod: 'grace period', |
| 62 | + /** |
| 63 | + *@description Title for the heuristic exception control |
| 64 | + */ |
| 65 | + heuristicTitle: 'Heuristics based exception', |
| 66 | + /** |
| 67 | + *@description Explanation of the heuristics with a link to learn more about the scenarios in which they apply |
| 68 | + *@example {predefined scenarios} PH1 |
| 69 | + */ |
| 70 | + heuristicExplanation: |
| 71 | + 'In {PH1} like pop-ups or redirects, a site embedded on this site can access third-party cookies', |
| 72 | + /** |
| 73 | + *@description Text used for link within the heuristicExplanation to let the user learn more about the heuristic exception |
| 74 | + */ |
| 75 | + scenarios: 'predefined scenarios', |
| 76 | +}; |
| 77 | + |
| 78 | +const str_ = i18n.i18n.registerUIStrings('panels/security/CookieControlsView.ts', UIStrings); |
| 79 | +export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| 80 | +export const i18nFormatString = i18n.i18n.getFormatLocalizedString.bind(undefined, str_); |
| 81 | + |
| 82 | +export interface ViewInput { |
| 83 | + inputChanged: (newValue: boolean, setting: Common.Settings.Setting<boolean>) => void; |
| 84 | +} |
| 85 | +export interface ViewOutput {} |
| 86 | + |
| 87 | +export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void; |
| 88 | + |
| 89 | +export class CookieControlsView extends UI.Widget.VBox { |
| 90 | + #view: View; |
| 91 | + |
| 92 | + constructor(element?: HTMLElement, view: View = (input, output, target) => { |
| 93 | + const toggleSetting = Common.Settings.Settings.instance().moduleSetting('cookie-control-override-enabled'); |
| 94 | + const gracePeriodSetting = Common.Settings.Settings.instance().moduleSetting('grace-period-mitigation-disabled'); |
| 95 | + const heuristicSetting = Common.Settings.Settings.instance().moduleSetting('heuristic-mitigation-disabled'); |
| 96 | + |
| 97 | + // clang-format off |
| 98 | + const cardHeader = html ` |
| 99 | + <div class="card-header"> |
| 100 | + <div class="lhs"> |
| 101 | + <div class="text"> |
| 102 | + <div class="card-title">${i18nString(UIStrings.cardTitle)}</div> |
| 103 | + <div class="body">${i18nString(UIStrings.cardDisclaimer)}</div> |
| 104 | + </div> |
| 105 | + </div> |
| 106 | + <div> |
| 107 | + <devtools-switch |
| 108 | + .checked=${Boolean(toggleSetting.get())} |
| 109 | + @switchchange=${(e: Event)=>{ |
| 110 | + input.inputChanged((e.target as HTMLInputElement).checked, toggleSetting); |
| 111 | + }}> |
| 112 | + </devtools-switch> |
| 113 | + </div> |
| 114 | + </div> |
| 115 | + `; |
| 116 | + |
| 117 | + const gracePeriodControl = html` |
| 118 | + <div class="card-row"> |
| 119 | + <label class='checkbox-label'> |
| 120 | + <input type='checkbox' |
| 121 | + ?disabled=${!Boolean(toggleSetting.get())} |
| 122 | + ?checked=${!Boolean(gracePeriodSetting.get())} |
| 123 | + @change=${(e: Event)=>{ |
| 124 | + input.inputChanged(!(e.target as HTMLInputElement).checked, gracePeriodSetting); |
| 125 | + }} |
| 126 | + > |
| 127 | + <div class="text"> |
| 128 | + <div class="body">${i18nString(UIStrings.gracePeriodTitle)}</div> |
| 129 | + <div class="body"> |
| 130 | + ${i18nFormatString(UIStrings.gracePeriodExplanation, { |
| 131 | + PH1: UI.Fragment.html`<x-link class="x-link" href="https://developers.google.com/privacy-sandbox/cookies/temporary-exceptions/grace-period" jslog=${VisualLogging.link('grace-period-link').track({click: true})}>${i18nString(UIStrings.gracePeriod)}</x-link>`, |
| 132 | + })} |
| 133 | + </div> |
| 134 | + </div> |
| 135 | + </label> |
| 136 | + </div> |
| 137 | + `; |
| 138 | + |
| 139 | + const heuristicControl = html` |
| 140 | + <div class="card-row"> |
| 141 | + <label class='checkbox-label'> |
| 142 | + <input type='checkbox' |
| 143 | + ?disabled=${!Boolean(toggleSetting.get())} |
| 144 | + ?checked=${!Boolean(heuristicSetting.get())} |
| 145 | + @change=${(e: Event)=>{ |
| 146 | + input.inputChanged(!(e.target as HTMLInputElement).checked, heuristicSetting); |
| 147 | + }} |
| 148 | + > |
| 149 | + <div class="text"> |
| 150 | + <div class="body">${i18nString(UIStrings.heuristicTitle)}</div> |
| 151 | + <div class="body"> |
| 152 | + ${i18nFormatString(UIStrings.heuristicExplanation, { |
| 153 | + PH1: UI.Fragment.html`<x-link class="x-link" href="https://developers.google.com/privacy-sandbox/cookies/temporary-exceptions/heuristics-based-exceptions" jslog=${VisualLogging.link('heuristic-link').track({click: true})}>${i18nString(UIStrings.scenarios)}</x-link>`, |
| 154 | + })} |
| 155 | + </div> |
| 156 | + </div> |
| 157 | + </label> |
| 158 | + </div> |
| 159 | + `; |
| 160 | + |
| 161 | + render(html ` |
| 162 | + <div class="overflow-auto"> |
| 163 | + <div class="controls"> |
| 164 | + <div class="header"> |
| 165 | + <div class="title">${i18nString(UIStrings.viewTitle)}</div> |
| 166 | + <div class="body">${i18nString(UIStrings.viewExplanation)}</div> |
| 167 | + </div> |
| 168 | + <devtools-card> |
| 169 | + <div slot="content" class='card'> |
| 170 | + ${cardHeader} |
| 171 | + <div> |
| 172 | + <div class="card-row text"> |
| 173 | + <div class="card-row-title">${i18nString(UIStrings.exceptions)}</div> |
| 174 | + <div class="body">${i18nString(UIStrings.exceptionsExplanation)}</div> |
| 175 | + </div> |
| 176 | + ${gracePeriodControl} |
| 177 | + ${heuristicControl} |
| 178 | + </div> |
| 179 | + </div> |
| 180 | + </devtools-card> |
| 181 | + </div> |
| 182 | + </div> |
| 183 | + `, target, {host: this}); |
| 184 | + // clang-format on |
| 185 | + }) { |
| 186 | + super(true, undefined, element); |
| 187 | + this.#view = view; |
| 188 | + this.update(); |
| 189 | + } |
| 190 | + |
| 191 | + override async doUpdate(): Promise<void> { |
| 192 | + this.#view(this, this, this.contentElement); |
| 193 | + } |
| 194 | + |
| 195 | + inputChanged(newValue: boolean, setting: Common.Settings.Setting<boolean>): void { |
| 196 | + setting.set(newValue); |
| 197 | + UI.InspectorView.InspectorView.instance().displayDebuggedTabReloadRequiredWarning( |
| 198 | + i18nString(UIStrings.siteReloadMessage)); |
| 199 | + this.update(); |
| 200 | + } |
| 201 | + |
| 202 | + override wasShown(): void { |
| 203 | + super.wasShown(); |
| 204 | + this.registerCSSFiles([Input.checkboxStyles, cookieControlsViewStyles]); |
| 205 | + } |
| 206 | +} |
0 commit comments