blob: 5966f5d426f61ff3c38d476e31b0fa99579b99ab [file] [log] [blame]
// Copyright (c) 2016 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.
/**
* @implements {SDK.TargetManager.Observer}
*/
Resources.ClearStorageView = class extends UI.ThrottledWidget {
constructor() {
super(true, 1000);
const types = Protocol.Storage.StorageType;
this._pieColors = new Map([
[types.Appcache, 'rgb(110, 161, 226)'], // blue
[types.Cache_storage, 'rgb(229, 113, 113)'], // red
[types.Cookies, 'rgb(239, 196, 87)'], // yellow
[types.Indexeddb, 'rgb(155, 127, 230)'], // purple
[types.Local_storage, 'rgb(116, 178, 102)'], // green
[types.Service_workers, 'rgb(255, 167, 36)'], // orange
[types.Websql, 'rgb(203, 220, 56)'], // lime
]);
this._reportView = new UI.ReportView(Common.UIString('Clear storage'));
this._reportView.registerRequiredCSS('resources/clearStorageView.css');
this._reportView.element.classList.add('clear-storage-header');
this._reportView.show(this.contentElement);
/** @type {?SDK.Target} */
this._target = null;
/** @type {?string} */
this._securityOrigin = null;
this._settings = new Map();
for (const type of Resources.ClearStorageView.AllStorageTypes) {
this._settings.set(type, Common.settings.createSetting('clear-storage-' + type, true));
}
const quota = this._reportView.appendSection(Common.UIString('Usage'));
this._quotaRow = quota.appendSelectableRow();
const learnMoreRow = quota.appendRow();
const learnMore = UI.XLink.create(
'https://developers.google.com/web/tools/chrome-devtools/progressive-web-apps#opaque-responses',
ls`Learn more`);
learnMoreRow.appendChild(learnMore);
this._quotaUsage = null;
this._pieChart = new PerfUI.PieChart(
{chartName: ls`Storage Usage`, size: 110, formatter: Number.bytesToString, showLegend: true});
const usageBreakdownRow = quota.appendRow();
usageBreakdownRow.classList.add('usage-breakdown-row');
usageBreakdownRow.appendChild(this._pieChart.element);
const clearButtonSection = this._reportView.appendSection('', 'clear-storage-button').appendRow();
this._clearButton = UI.createTextButton(ls`Clear site data`, this._clear.bind(this));
clearButtonSection.appendChild(this._clearButton);
const application = this._reportView.appendSection(Common.UIString('Application'));
this._appendItem(application, Common.UIString('Unregister service workers'), 'service_workers');
application.markFieldListAsGroup();
const storage = this._reportView.appendSection(Common.UIString('Storage'));
this._appendItem(storage, Common.UIString('Local and session storage'), 'local_storage');
this._appendItem(storage, Common.UIString('IndexedDB'), 'indexeddb');
this._appendItem(storage, Common.UIString('Web SQL'), 'websql');
this._appendItem(storage, Common.UIString('Cookies'), 'cookies');
storage.markFieldListAsGroup();
const caches = this._reportView.appendSection(Common.UIString('Cache'));
this._appendItem(caches, Common.UIString('Cache storage'), 'cache_storage');
this._appendItem(caches, Common.UIString('Application cache'), 'appcache');
caches.markFieldListAsGroup();
SDK.targetManager.observeTargets(this);
}
/**
* @param {!UI.ReportView.Section} section
* @param {string} title
* @param {string} settingName
*/
_appendItem(section, title, settingName) {
const row = section.appendRow();
row.appendChild(UI.SettingsUI.createSettingCheckbox(title, this._settings.get(settingName), true));
}
/**
* @override
* @param {!SDK.Target} target
*/
targetAdded(target) {
if (this._target) {
return;
}
this._target = target;
const securityOriginManager = target.model(SDK.SecurityOriginManager);
this._updateOrigin(
securityOriginManager.mainSecurityOrigin(), securityOriginManager.unreachableMainSecurityOrigin());
securityOriginManager.addEventListener(
SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, this._originChanged, this);
}
/**
* @override
* @param {!SDK.Target} target
*/
targetRemoved(target) {
if (this._target !== target) {
return;
}
const securityOriginManager = target.model(SDK.SecurityOriginManager);
securityOriginManager.removeEventListener(
SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, this._originChanged, this);
}
/**
* @param {!Common.Event} event
*/
_originChanged(event) {
const mainOrigin = /** *@type {string} */ (event.data.mainSecurityOrigin);
const unreachableMainOrigin = /** @type {string} */ (event.data.unreachableMainSecurityOrigin);
this._updateOrigin(mainOrigin, unreachableMainOrigin);
}
/**
* @param {string} mainOrigin
* @param {string} unreachableMainOrigin
*/
_updateOrigin(mainOrigin, unreachableMainOrigin) {
if (unreachableMainOrigin) {
this._securityOrigin = unreachableMainOrigin;
this._reportView.setSubtitle(ls`${unreachableMainOrigin} (failed to load)`);
} else {
this._securityOrigin = mainOrigin;
this._reportView.setSubtitle(mainOrigin);
}
this.doUpdate();
}
_clear() {
if (!this._securityOrigin) {
return;
}
const selectedStorageTypes = [];
for (const type of this._settings.keys()) {
if (this._settings.get(type).get()) {
selectedStorageTypes.push(type);
}
}
if (this._target) {
Resources.ClearStorageView.clear(this._target, this._securityOrigin, selectedStorageTypes);
}
this._clearButton.disabled = true;
const label = this._clearButton.textContent;
this._clearButton.textContent = Common.UIString('Clearing...');
setTimeout(() => {
this._clearButton.disabled = false;
this._clearButton.textContent = label;
}, 500);
}
/**
* @param {!SDK.Target} target
* @param {string} securityOrigin
* @param {!Array<string>} selectedStorageTypes
*/
static clear(target, securityOrigin, selectedStorageTypes) {
target.storageAgent().clearDataForOrigin(securityOrigin, selectedStorageTypes.join(','));
const set = new Set(selectedStorageTypes);
const hasAll = set.has(Protocol.Storage.StorageType.All);
if (set.has(Protocol.Storage.StorageType.Cookies) || hasAll) {
const cookieModel = target.model(SDK.CookieModel);
if (cookieModel) {
cookieModel.clear();
}
}
if (set.has(Protocol.Storage.StorageType.Indexeddb) || hasAll) {
for (const target of SDK.targetManager.targets()) {
const indexedDBModel = target.model(Resources.IndexedDBModel);
if (indexedDBModel) {
indexedDBModel.clearForOrigin(securityOrigin);
}
}
}
if (set.has(Protocol.Storage.StorageType.Local_storage) || hasAll) {
const storageModel = target.model(Resources.DOMStorageModel);
if (storageModel) {
storageModel.clearForOrigin(securityOrigin);
}
}
if (set.has(Protocol.Storage.StorageType.Websql) || hasAll) {
const databaseModel = target.model(Resources.DatabaseModel);
if (databaseModel) {
databaseModel.disable();
databaseModel.enable();
}
}
if (set.has(Protocol.Storage.StorageType.Cache_storage) || hasAll) {
const target = SDK.targetManager.mainTarget();
const model = target && target.model(SDK.ServiceWorkerCacheModel);
if (model) {
model.clearForOrigin(securityOrigin);
}
}
if (set.has(Protocol.Storage.StorageType.Appcache) || hasAll) {
const appcacheModel = target.model(Resources.ApplicationCacheModel);
if (appcacheModel) {
appcacheModel.reset();
}
}
}
/**
* @override
* @return {!Promise<?>}
*/
async doUpdate() {
if (!this._securityOrigin) {
return;
}
const securityOrigin = /** @type {string} */ (this._securityOrigin);
const response = await this._target.storageAgent().invoke_getUsageAndQuota({origin: securityOrigin});
if (response[Protocol.Error]) {
this._quotaRow.textContent = '';
this._resetPieChart(0);
return;
}
this._quotaRow.textContent = Common.UIString(
'%s used out of %s storage quota.\xA0', Number.bytesToString(response.usage),
Number.bytesToString(response.quota));
if (response.quota < 125829120) { // 120 MB
this._quotaRow.title = ls`Storage quota is limited in Incognito mode`;
this._quotaRow.appendChild(UI.Icon.create('smallicon-info'));
}
if (!this._quotaUsage || this._quotaUsage !== response.usage) {
this._quotaUsage = response.usage;
this._resetPieChart(response.usage);
for (const usageForType of response.usageBreakdown.sort((a, b) => b.usage - a.usage)) {
const value = usageForType.usage;
if (!value) {
continue;
}
const title = this._getStorageTypeName(usageForType.storageType);
const color = this._pieColors.get(usageForType.storageType) || '#ccc';
this._pieChart.addSlice(value, color, title);
}
}
this._usageUpdatedForTest(response.usage, response.quota, response.usageBreakdown);
this.update();
}
/**
* @param {number} total
*/
_resetPieChart(total) {
this._pieChart.setTotal(total);
}
/**
* @param {string} type
* @return {string}
*/
_getStorageTypeName(type) {
switch (type) {
case Protocol.Storage.StorageType.File_systems:
return Common.UIString('File System');
case Protocol.Storage.StorageType.Websql:
return Common.UIString('Web SQL');
case Protocol.Storage.StorageType.Appcache:
return Common.UIString('Application Cache');
case Protocol.Storage.StorageType.Indexeddb:
return Common.UIString('IndexedDB');
case Protocol.Storage.StorageType.Cache_storage:
return Common.UIString('Cache Storage');
case Protocol.Storage.StorageType.Service_workers:
return Common.UIString('Service Workers');
default:
return Common.UIString('Other');
}
}
/**
* @param {number} usage
* @param {number} quota
* @param {!Array<!Protocol.Storage.UsageForType>} usageBreakdown
*/
_usageUpdatedForTest(usage, quota, usageBreakdown) {
}
};
Resources.ClearStorageView.AllStorageTypes = [
Protocol.Storage.StorageType.Appcache, Protocol.Storage.StorageType.Cache_storage,
Protocol.Storage.StorageType.Cookies, Protocol.Storage.StorageType.Indexeddb,
Protocol.Storage.StorageType.Local_storage, Protocol.Storage.StorageType.Service_workers,
Protocol.Storage.StorageType.Websql
];
/**
* @implements {UI.ActionDelegate}
*/
Resources.ClearStorageView.ActionDelegate = class {
/**
* @override
* @param {!UI.Context} context
* @param {string} actionId
* @return {boolean}
*/
handleAction(context, actionId) {
switch (actionId) {
case 'resources.clear':
return this._handleClear();
}
return false;
}
/**
* @return {boolean}
*/
_handleClear() {
const target = SDK.targetManager.mainTarget();
if (!target) {
return false;
}
const resourceTreeModel = target.model(SDK.ResourceTreeModel);
if (!resourceTreeModel) {
return false;
}
const securityOrigin = resourceTreeModel.getMainSecurityOrigin();
if (!securityOrigin) {
return false;
}
Resources.ClearStorageView.clear(target, securityOrigin, Resources.ClearStorageView.AllStorageTypes);
return true;
}
};