chrome-devtools-frontend
Version:
Chrome DevTools UI
290 lines (265 loc) • 10.7 kB
text/typescript
// 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/kit/kit.js';
import * as i18n from '../../../../core/i18n/i18n.js';
import * as Platform from '../../../../core/platform/platform.js';
import * as Buttons from '../../../../ui/components/buttons/buttons.js';
import * as Dialogs from '../../../../ui/components/dialogs/dialogs.js';
import * as UI from '../../../../ui/legacy/legacy.js';
import {html, i18nTemplate, type LitTemplate, nothing, render} from '../../../../ui/lit/lit.js';
import * as VisualLogging from '../../../../ui/visual_logging/visual_logging.js';
import preloadingDisabledInfobarStyles from './preloadingDisabledInfobar.css.js';
const {urlString} = Platform.DevToolsPath;
const UIStrings = {
/**
* @description Infobar text for disabled case
*/
infobarPreloadingIsDisabled: 'Speculative loading is disabled',
/**
* @description Infobar text for force-enabled case
*/
infobarPreloadingIsForceEnabled: 'Speculative loading is force-enabled',
/**
* @description Title for dialog
*/
titleReasonsPreventingPreloading: 'Reasons preventing speculative loading',
/**
* @description Header in dialog
*/
headerDisabledByPreference: 'User settings or extensions',
/**
* @description Description in dialog
* @example {Preload pages settings (linked to chrome://settings/performance)} PH1
* @example {Extensions settings (linked to chrome://extensions)} PH2
*/
descriptionDisabledByPreference:
'Speculative loading is disabled because of user settings or an extension. Go to {PH1} to update your preference. Go to {PH2} to disable any extension that blocks speculative loading.',
/**
* @description Text of link
*/
preloadingPagesSettings: 'Preload pages settings',
/**
* @description Text of link
*/
extensionsSettings: 'Extensions settings',
/**
* @description Header in dialog
*/
headerDisabledByDataSaver: 'Data Saver',
/**
* @description Description in dialog
*/
descriptionDisabledByDataSaver: 'Speculative loading is disabled because of the operating system\'s Data Saver mode.',
/**
* @description Header in dialog
*/
headerDisabledByBatterySaver: 'Battery Saver',
/**
* @description Description in dialog
*/
descriptionDisabledByBatterySaver:
'Speculative loading is disabled because of the operating system\'s Battery Saver mode.',
/**
* @description Header in dialog
*/
headerDisabledByHoldbackPrefetchSpeculationRules: 'Prefetch was disabled, but is force-enabled now',
/**
* @description Description in infobar
*/
descriptionDisabledByHoldbackPrefetchSpeculationRules:
'Prefetch is forced-enabled because DevTools is open. When DevTools is closed, prefetch will be disabled because this browser session is part of a holdback group used for performance comparisons.',
/**
* @description Header in dialog
*/
headerDisabledByHoldbackPrerenderSpeculationRules: 'Prerendering was disabled, but is force-enabled now',
/**
* @description Description in infobar
*/
descriptionDisabledByHoldbackPrerenderSpeculationRules:
'Prerendering is forced-enabled because DevTools is open. When DevTools is closed, prerendering will be disabled because this browser session is part of a holdback group used for performance comparisons.',
/**
* @description Footer link for more details
*/
footerLearnMore: 'Learn more',
} as const;
const str_ =
i18n.i18n.registerUIStrings('panels/application/preloading/components/PreloadingDisabledInfobar.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
const LINK = 'https://developer.chrome.com/blog/prerender-pages/';
export interface ViewInput {
header: Platform.UIString.LocalizedString|null;
warnings: Array<{
key: Platform.UIString.LocalizedString,
valueId: string,
placeholders?: Record<string, {
title: Platform.UIString.LocalizedString,
href: Platform.DevToolsPath.UrlString,
}>,
}>;
}
type ViewOutput = unknown;
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement|DocumentFragment) => void;
export const DEFAULT_VIEW: View = (input, _output, target) => {
let template: LitTemplate = nothing;
if (input.header !== null) {
// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
template = html`
<style>${preloadingDisabledInfobarStyles}</style>
<div id="container">
<span id="header">${input.header}</span>
<devtools-button-dialog .data=${{
iconName: 'info',
variant: Buttons.Button.Variant.ICON,
closeButton: true,
position: Dialogs.Dialog.DialogVerticalPosition.AUTO,
horizontalAlignment: Dialogs.Dialog.DialogHorizontalAlignment.AUTO,
closeOnESC: true,
closeOnScroll: false,
dialogTitle: i18nString(UIStrings.titleReasonsPreventingPreloading),
} as Dialogs.ButtonDialog.ButtonDialogData}
jslog=${VisualLogging.dialog('preloading-disabled').track({resize: true, keydown: 'Escape'})}>
<div id="contents">
<devtools-report>
${input.warnings.map(({key, valueId, placeholders = {}}) => {
const value = i18nTemplate(
str_, valueId,
Object.fromEntries(Object.entries(placeholders).map(
([key, {title, href}]) =>
[key, html`<devtools-link href=${href}>${title}</devtools-link>`])));
return html`
<div class="key">${key}</div>
<div class="value">${value}</div>
`;
})}
</devtools-report>
<div id="footer">
<devtools-link href=${LINK} jslogcontext="learn-more">
${i18nString(UIStrings.footerLearnMore)}
</devtools-link>
</div>
</div>
</devtools-button-dialog>
</div>`;
// clang-format on
}
render(template, target);
};
export class PreloadingDisabledInfobar extends UI.Widget.VBox {
#view: View;
#disabledByPreference = false;
#disabledByDataSaver = false;
#disabledByBatterySaver = false;
#disabledByHoldbackPrefetchSpeculationRules = false;
#disabledByHoldbackPrerenderSpeculationRules = false;
constructor(view: View = DEFAULT_VIEW) {
super({useShadowDom: true});
this.#view = view;
}
get disabledByPreference(): boolean {
return this.#disabledByPreference;
}
set disabledByPreference(value: boolean) {
if (this.#disabledByPreference !== value) {
this.#disabledByPreference = value;
this.requestUpdate();
}
}
get disabledByDataSaver(): boolean {
return this.#disabledByDataSaver;
}
set disabledByDataSaver(value: boolean) {
if (this.#disabledByDataSaver !== value) {
this.#disabledByDataSaver = value;
this.requestUpdate();
}
}
get disabledByBatterySaver(): boolean {
return this.#disabledByBatterySaver;
}
set disabledByBatterySaver(value: boolean) {
if (this.#disabledByBatterySaver !== value) {
this.#disabledByBatterySaver = value;
this.requestUpdate();
}
}
get disabledByHoldbackPrefetchSpeculationRules(): boolean {
return this.#disabledByHoldbackPrefetchSpeculationRules;
}
set disabledByHoldbackPrefetchSpeculationRules(value: boolean) {
if (this.#disabledByHoldbackPrefetchSpeculationRules !== value) {
this.#disabledByHoldbackPrefetchSpeculationRules = value;
this.requestUpdate();
}
}
get disabledByHoldbackPrerenderSpeculationRules(): boolean {
return this.#disabledByHoldbackPrerenderSpeculationRules;
}
set disabledByHoldbackPrerenderSpeculationRules(value: boolean) {
if (this.#disabledByHoldbackPrerenderSpeculationRules !== value) {
this.#disabledByHoldbackPrerenderSpeculationRules = value;
this.requestUpdate();
}
}
override wasShown(): void {
super.wasShown();
this.requestUpdate();
}
override performUpdate(): void {
let header: Platform.UIString.LocalizedString|null = null;
if (this.#disabledByPreference || this.#disabledByDataSaver || this.#disabledByBatterySaver) {
header = i18nString(UIStrings.infobarPreloadingIsDisabled);
} else if (this.#disabledByHoldbackPrefetchSpeculationRules || this.#disabledByHoldbackPrerenderSpeculationRules) {
header = i18nString(UIStrings.infobarPreloadingIsForceEnabled);
}
const warnings = [];
if (this.#disabledByPreference) {
warnings.push({
key: i18nString(UIStrings.headerDisabledByPreference),
valueId: UIStrings.descriptionDisabledByPreference,
placeholders: {
PH1: {
title: i18nString(UIStrings.preloadingPagesSettings),
href: urlString`chrome://settings/performance`,
},
PH2: {
title: i18nString(UIStrings.extensionsSettings),
href: urlString`chrome://extensions`,
},
},
});
}
if (this.#disabledByDataSaver) {
warnings.push({
key: i18nString(UIStrings.headerDisabledByDataSaver),
valueId: UIStrings.descriptionDisabledByDataSaver,
});
}
if (this.#disabledByBatterySaver) {
warnings.push({
key: i18nString(UIStrings.headerDisabledByBatterySaver),
valueId: UIStrings.descriptionDisabledByBatterySaver,
});
}
if (this.#disabledByHoldbackPrefetchSpeculationRules) {
warnings.push({
key: i18nString(UIStrings.headerDisabledByHoldbackPrefetchSpeculationRules),
valueId: UIStrings.descriptionDisabledByHoldbackPrefetchSpeculationRules,
});
}
if (this.#disabledByHoldbackPrerenderSpeculationRules) {
warnings.push({
key: i18nString(UIStrings.headerDisabledByHoldbackPrerenderSpeculationRules),
valueId: UIStrings.descriptionDisabledByHoldbackPrerenderSpeculationRules,
});
}
const input: ViewInput = {
header,
warnings,
};
const output: ViewOutput = undefined;
this.#view(input, output, this.contentElement);
}
}