blob: cd69253c3155ebe6a46e389eb3e6b526b4d31798 [file] [log] [blame]
// Copyright 2020 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.
import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import 'chrome://resources/cr_elements/icons.m.js';
import '../icons.js';
import '../settings_shared_css.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
import {html, microTask, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BaseMixin, BaseMixinInterface} from '../base_mixin.js';
import {loadTimeData} from '../i18n_setup.js';
import {PrefsBehavior, PrefsBehaviorInterface} from '../prefs/prefs_behavior.js';
import {Route, Router} from '../router.js';
import {ContentSetting, ContentSettingsTypes, NotificationSetting} from '../site_settings/constants.js';
import {SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl} from '../site_settings/site_settings_prefs_browser_proxy.js';
/**
* @typedef{{
* route: !Route,
* id: ContentSettingsTypes,
* label: string,
* icon: (string|undefined),
* enabledLabel: (string|undefined),
* disabledLabel: (string|undefined),
* otherLabel: (string|undefined),
* shouldShow: function():boolean,
* }}
*/
export let CategoryListItem;
/**
* @param {string} setting Value from ContentSetting.
* @param {string} enabled Non-block label ('feature X not allowed').
* @param {string} disabled Block label (likely just, 'Blocked').
* @param {?string} other Tristate value (maybe, 'session only').
*/
export function defaultSettingLabel(setting, enabled, disabled, other) {
if (setting === ContentSetting.BLOCK) {
return disabled;
}
if (setting === ContentSetting.ALLOW) {
return enabled;
}
return other || enabled;
}
/**
* @constructor
* @extends {PolymerElement}
* @implements {BaseMixinInterface}
* @implements {I18nBehaviorInterface}
* @implements {PrefsBehaviorInterface}
* @implements {WebUIListenerBehaviorInterface}
*/
const SettingsSiteSettingsListElementBase = mixinBehaviors(
[WebUIListenerBehavior, I18nBehavior, PrefsBehavior],
BaseMixin(PolymerElement));
/** @polymer */
class SettingsSiteSettingsListElement extends
SettingsSiteSettingsListElementBase {
static get is() {
return 'settings-site-settings-list';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/** @type {!Array<!CategoryListItem>} */
categoryList: Array,
/** @type {!Map<string, (string|Function)>} */
focusConfig: {
type: Object,
observer: 'focusConfigChanged_',
},
};
}
static get observers() {
return [
// The prefs object is only populated for the instance of this element
// which contains the notifications link row, avoiding non-actionable
// firing of the observer.
'updateNotificationsLabel_(prefs.generated.notification.*)'
];
}
constructor() {
super();
/** @private {!SiteSettingsPrefsBrowserProxy} */
this.browserProxy_ = SiteSettingsPrefsBrowserProxyImpl.getInstance();
}
/**
* @param {!Map<string, string>} newConfig
* @param {?Map<string, string>} oldConfig
* @private
*/
focusConfigChanged_(newConfig, oldConfig) {
// focusConfig is set only once on the parent, so this observer should
// only fire once.
assert(!oldConfig);
// Populate the |focusConfig| map of the parent <settings-animated-pages>
// element, with additional entries that correspond to subpage trigger
// elements residing in this element's Shadow DOM.
for (const item of this.categoryList) {
this.focusConfig.set(item.route.path, () => microTask.run(() => {
focusWithoutInk(assert(this.shadowRoot.querySelector(`#${item.id}`)));
}));
}
}
/** @override */
ready() {
super.ready();
Promise
.all(this.categoryList.map(
item => this.refreshDefaultValueLabel_(item.id)))
.then(() => {
this.fire('site-settings-list-labels-updated-for-testing');
});
this.addWebUIListener(
'contentSettingCategoryChanged',
this.refreshDefaultValueLabel_.bind(this));
const hasProtocolHandlers = this.categoryList.some(item => {
return item.id === ContentSettingsTypes.PROTOCOL_HANDLERS;
});
if (hasProtocolHandlers) {
// The protocol handlers have a separate enabled/disabled notifier.
this.addWebUIListener('setHandlersEnabled', enabled => {
this.updateDefaultValueLabel_(
ContentSettingsTypes.PROTOCOL_HANDLERS,
enabled ? ContentSetting.ALLOW : ContentSetting.BLOCK);
});
this.browserProxy_.observeProtocolHandlersEnabledState();
}
const hasCookies = this.categoryList.some(item => {
return item.id === ContentSettingsTypes.COOKIES;
});
if (hasCookies) {
// The cookies sub-label is provided by an update from C++.
this.browserProxy_.getCookieSettingDescription().then(
this.updateCookiesLabel_.bind(this));
this.addWebUIListener(
'cookieSettingDescriptionChanged',
this.updateCookiesLabel_.bind(this));
}
}
/**
* @param {!ContentSettingsTypes} category The category to refresh
* (fetch current value + update UI)
* @return {!Promise<void>} A promise firing after the label has been
* updated.
* @private
*/
refreshDefaultValueLabel_(category) {
// Default labels are not applicable to ZOOM_LEVELS, PDF or
// PROTECTED_CONTENT
if (category === ContentSettingsTypes.ZOOM_LEVELS ||
category === ContentSettingsTypes.PROTECTED_CONTENT ||
category === 'pdfDocuments') {
return Promise.resolve();
}
if (category === ContentSettingsTypes.COOKIES) {
// Updates to the cookies label are handled by the
// cookieSettingDescriptionChanged event listener.
return Promise.resolve();
}
if (category === ContentSettingsTypes.NOTIFICATIONS) {
// Updates to the notifications label are handled by a preference
// observer.
return Promise.resolve();
}
return this.browserProxy_.getDefaultValueForContentType(category).then(
defaultValue => {
this.updateDefaultValueLabel_(category, defaultValue.setting);
});
}
/**
* Updates the DOM for the given |category| to display a label that
* corresponds to the given |setting|.
* @param {!ContentSettingsTypes} category
* @param {!ContentSetting} setting
* @private
*/
updateDefaultValueLabel_(category, setting) {
const element = this.shadowRoot.querySelector(`#${category}`);
if (!element) {
// |category| is not part of this list.
return;
}
const index =
this.shadowRoot.querySelector('dom-repeat').indexForElement(element);
const dataItem = this.categoryList[index];
this.set(
`categoryList.${index}.subLabel`,
defaultSettingLabel(
setting,
dataItem.enabledLabel ? this.i18n(dataItem.enabledLabel) : '',
dataItem.disabledLabel ? this.i18n(dataItem.disabledLabel) : '',
dataItem.otherLabel ? this.i18n(dataItem.otherLabel) : null));
}
/**
* Update the cookies link row label when the cookies setting description
* changes.
* @param {string} label
* @private
*/
updateCookiesLabel_(label) {
const index =
this.shadowRoot.querySelector('dom-repeat')
.indexForElement(this.shadowRoot.querySelector('#cookies'));
this.set(`categoryList.${index}.subLabel`, label);
}
/**
* Update the notifications link row label when the notifications setting
* description changes.
* @private
*/
updateNotificationsLabel_() {
const redesignEnabled =
loadTimeData.getBoolean('enableContentSettingsRedesign');
const state = this.getPref('generated.notification').value;
const index = this.categoryList.map(e => e.id).indexOf(
ContentSettingsTypes.NOTIFICATIONS);
// updateNotificationsLabel_ should only be called for the
// site-settings-list instance which contains notifications.
assert(index !== -1);
let label = redesignEnabled ? 'siteSettingsNotificationsBlocked' :
'siteSettingsBlocked';
if (state === NotificationSetting.ASK) {
label = redesignEnabled ? 'siteSettingsNotificationsAllowed' :
'siteSettingsAskBeforeSending';
} else if (state === NotificationSetting.QUIETER_MESSAGING) {
label = redesignEnabled ? 'siteSettingsNotificationsPartial' :
'siteSettingsAskBeforeSending';
}
this.set(`categoryList.${index}.subLabel`, this.i18n(label));
}
/**
* @param {!Event} event
* @private
*/
onClick_(event) {
Router.getInstance().navigateTo(this.categoryList[event.model.index].route);
}
}
customElements.define(
SettingsSiteSettingsListElement.is, SettingsSiteSettingsListElement);