blob: 2f62cea75206367fcd0519f8f8c5762ff20cba08 [file] [log] [blame]
// Copyright 2015 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.
/**
* @fileoverview
* 'site-details' show the details (permissions and usage) for a given origin
* under Site Settings.
*/
import 'chrome://resources/js/action_link.js';
import 'chrome://resources/cr_elements/action_link_css.m.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/cr_elements/shared_style_css.m.js';
import 'chrome://resources/cr_elements/shared_vars_css.m.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import '../icons.js';
import '../settings_shared_css.js';
import './all_sites_icons.js';
import './clear_storage_dialog_css.js';
import './site_details_permission.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} from 'chrome://resources/js/i18n_behavior.m.js';
import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {loadTimeData} from '../i18n_setup.js';
import {MetricsBrowserProxyImpl, PrivacyElementInteractions} from '../metrics_browser_proxy.js';
import {routes} from '../route.js';
import {Route, RouteObserverMixin, RouteObserverMixinInterface, Router} from '../router.js';
import {ContentSetting, ContentSettingsTypes} from './constants.js';
import {SiteDetailsPermissionElement} from './site_details_permission.js';
import {SiteSettingsMixin, SiteSettingsMixinInterface} from './site_settings_mixin.js';
import {WebsiteUsageBrowserProxy, WebsiteUsageBrowserProxyImpl} from './website_usage_browser_proxy.js';
/**
* @constructor
* @extends {PolymerElement}
* @implements {SiteSettingsMixinInterface}
* @implements {RouteObserverMixinInterface}
* @implements {WebUIListenerBehaviorInterface}
*/
const SiteDetailsElementBase = mixinBehaviors(
[I18nBehavior, WebUIListenerBehavior],
SiteSettingsMixin(RouteObserverMixin(PolymerElement)));
/** @polymer */
class SiteDetailsElement extends SiteDetailsElementBase {
static get is() {
return 'site-details';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/**
* Whether unified autoplay blocking is enabled.
*/
blockAutoplayEnabled: Boolean,
/**
* Use the string representing the origin or extension name as the page
* title of the settings-subpage parent.
*/
pageTitle: {
type: String,
notify: true,
},
/**
* The origin that this widget is showing details for.
* @private
*/
origin_: String,
/**
* The amount of data stored for the origin.
* @private
*/
storedData_: {
type: String,
value: '',
},
/**
* The number of cookies stored for the origin.
* @private
*/
numCookies_: {
type: String,
value: '',
},
/** @private */
enableExperimentalWebPlatformFeatures_: {
type: Boolean,
value() {
return loadTimeData.getBoolean(
'enableExperimentalWebPlatformFeatures');
},
},
/** @private */
enableWebBluetoothNewPermissionsBackend_: {
type: Boolean,
value: () =>
loadTimeData.getBoolean('enableWebBluetoothNewPermissionsBackend'),
},
/** @private */
contentSettingsTypesEnum_: {
type: Object,
value: ContentSettingsTypes,
},
};
}
constructor() {
super();
/** @private {string} */
this.fetchingForHost_ = '';
/** @private {!WebsiteUsageBrowserProxy} */
this.websiteUsageProxy_ = WebsiteUsageBrowserProxyImpl.getInstance();
}
/** @override */
connectedCallback() {
super.connectedCallback();
this.addWebUIListener('usage-total-changed', (host, data, cookies) => {
this.onUsageTotalChanged_(host, data, cookies);
});
this.addWebUIListener(
'contentSettingSitePermissionChanged',
this.onPermissionChanged_.bind(this));
// Refresh block autoplay status from the backend.
this.browserProxy.fetchBlockAutoplayStatus();
}
/**
* RouteObserverMixin
* @override
*/
currentRouteChanged(route) {
if (route !== routes.SITE_SETTINGS_SITE_DETAILS) {
return;
}
const site = Router.getInstance().getQueryParameters().get('site');
if (!site) {
return;
}
this.origin_ = site;
this.browserProxy.isOriginValid(this.origin_).then((valid) => {
if (!valid) {
Router.getInstance().navigateToPreviousRoute();
} else {
this.fetchingForHost_ = this.toUrl(this.origin_).hostname;
this.storedData_ = '';
this.websiteUsageProxy_.fetchUsageTotal(this.fetchingForHost_);
this.browserProxy.getCategoryList(this.origin_).then((categoryList) => {
this.updatePermissions_(categoryList, /*hideOthers=*/ true);
});
}
});
}
/**
* Called when a site within a category has been changed.
* @param {!ContentSettingsTypes} category The category that
* changed.
* @param {string} origin The origin of the site that changed.
* @param {string} embeddingOrigin The embedding origin of the site that
* changed.
* @private
*/
onPermissionChanged_(category, origin, embeddingOrigin) {
if (this.origin_ === undefined || this.origin_ === '' ||
origin === undefined || origin === '') {
return;
}
this.browserProxy.getCategoryList(this.origin_).then((categoryList) => {
if (categoryList.includes(category)) {
this.updatePermissions_([category], /*hideOthers=*/ false);
}
});
}
/**
* Callback for when the usage total is known.
* @param {string} host The host that the usage was fetched for.
* @param {string} usage The string showing how much data the given host
* is using.
* @param {string} cookies The string showing how many cookies the given host
* is using.
* @private
*/
onUsageTotalChanged_(host, usage, cookies) {
if (this.fetchingForHost_ === host) {
this.storedData_ = usage;
this.numCookies_ = cookies;
}
}
/**
* Retrieves the permissions listed in |categoryList| from the backend for
* |this.origin_|.
* @param {!Array<!ContentSettingsTypes>} categoryList The list
* of categories to update permissions for.
* @param {boolean} hideOthers If true, permissions for categories not in
* |categoryList| will be hidden.
* @private
*/
updatePermissions_(categoryList, hideOthers) {
const permissionsMap =
/**
* @type {!Object<!ContentSettingsTypes,
* !SiteDetailsPermissionElement>}
*/
(Array.prototype.reduce.call(
this.root.querySelectorAll('site-details-permission'),
(map, element) => {
if (categoryList.includes(element.category)) {
map[element.category] = element;
} else if (hideOthers) {
// This will hide any permission not in the category list.
element.site = null;
}
return map;
},
{}));
this.browserProxy.getOriginPermissions(this.origin_, categoryList)
.then((exceptionList) => {
exceptionList.forEach((exception, i) => {
// |exceptionList| should be in the same order as
// |categoryList|.
if (permissionsMap[categoryList[i]]) {
permissionsMap[categoryList[i]].site = exception;
}
});
// The displayName won't change, so just use the first
// exception.
assert(exceptionList.length > 0);
this.pageTitle =
this.originRepresentation(exceptionList[0].displayName);
});
}
/** @private */
onCloseDialog_(e) {
e.target.closest('cr-dialog').close();
}
/**
* Confirms the resetting of all content settings for an origin.
* @param {!Event} e
* @private
*/
onConfirmClearSettings_(e) {
e.preventDefault();
this.$.confirmResetSettings.showModal();
}
/**
* Confirms the clearing of storage for an origin.
* @param {!Event} e
* @private
*/
onConfirmClearStorage_(e) {
e.preventDefault();
this.$.confirmClearStorageNew.showModal();
}
/**
* Resets all permissions for the current origin.
* @private
*/
onResetSettings_(e) {
this.browserProxy.setOriginPermissions(
this.origin_, null, ContentSetting.DEFAULT);
this.onCloseDialog_(e);
}
/**
* Clears all data stored, except cookies, for the current origin.
* @private
*/
onClearStorage_(e) {
MetricsBrowserProxyImpl.getInstance().recordSettingsPageHistogram(
PrivacyElementInteractions.SITE_DETAILS_CLEAR_DATA);
if (this.hasUsage_(this.storedData_, this.numCookies_)) {
this.websiteUsageProxy_.clearUsage(this.toUrl(this.origin_).href);
this.storedData_ = '';
this.numCookies_ = '';
}
this.onCloseDialog_(e);
}
/**
* Checks whether this site has any usage information to show.
* @return {boolean} Whether there is any usage information to show (e.g.
* disk or battery).
* @private
*/
hasUsage_(storage, cookies) {
return storage !== '' || cookies !== '';
}
/**
* Checks whether this site has both storage and cookies information to show.
* @return {boolean} Whether there are both storage and cookies information to
* show.
* @private
*/
hasDataAndCookies_(storage, cookies) {
return storage !== '' && cookies !== '';
}
/** @private */
onResetSettingsDialogClosed_() {
focusWithoutInk(
assert(this.shadowRoot.querySelector('#resetSettingsButton')));
}
/** @private */
onClearStorageDialogClosed_() {
focusWithoutInk(assert(this.shadowRoot.querySelector('#clearStorage')));
}
}
customElements.define(SiteDetailsElement.is, SiteDetailsElement);