blob: bc322340bd451ad752b1ea937da88d2a133fa3d3 [file] [log] [blame]
// Copyright 2017 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 {UI.ListWidget.Delegate}
* @unrestricted
*/
export class ThrottlingSettingsTab extends UI.VBox {
constructor() {
super(true);
this.registerRequiredCSS('mobile_throttling/throttlingSettingsTab.css');
const header = this.contentElement.createChild('div', 'header');
header.textContent = ls`Network Throttling Profiles`;
UI.ARIAUtils.markAsHeading(header, 1);
const addButton = UI.createTextButton(
Common.UIString('Add custom profile...'), this._addButtonClicked.bind(this), 'add-conditions-button');
this.contentElement.appendChild(addButton);
this._list = new UI.ListWidget(this);
this._list.element.classList.add('conditions-list');
this._list.registerRequiredCSS('mobile_throttling/throttlingSettingsTab.css');
this._list.show(this.contentElement);
this._customSetting = Common.moduleSetting('customNetworkConditions');
this._customSetting.addChangeListener(this._conditionsUpdated, this);
this.setDefaultFocusedElement(addButton);
}
/**
* @override
*/
wasShown() {
super.wasShown();
this._conditionsUpdated();
}
_conditionsUpdated() {
this._list.clear();
const conditions = this._customSetting.get();
for (let i = 0; i < conditions.length; ++i) {
this._list.appendItem(conditions[i], true);
}
this._list.appendSeparator();
}
_addButtonClicked() {
this._list.addNewItem(this._customSetting.get().length, {title: '', download: -1, upload: -1, latency: 0});
}
/**
* @override
* @param {*} item
* @param {boolean} editable
* @return {!Element}
*/
renderItem(item, editable) {
const conditions = /** @type {!SDK.NetworkManager.Conditions} */ (item);
const element = createElementWithClass('div', 'conditions-list-item');
const title = element.createChild('div', 'conditions-list-text conditions-list-title');
const titleText = title.createChild('div', 'conditions-list-title-text');
titleText.textContent = conditions.title;
titleText.title = conditions.title;
element.createChild('div', 'conditions-list-separator');
element.createChild('div', 'conditions-list-text').textContent = throughputText(conditions.download);
element.createChild('div', 'conditions-list-separator');
element.createChild('div', 'conditions-list-text').textContent = throughputText(conditions.upload);
element.createChild('div', 'conditions-list-separator');
element.createChild('div', 'conditions-list-text').textContent = Common.UIString('%dms', conditions.latency);
return element;
}
/**
* @override
* @param {*} item
* @param {number} index
*/
removeItemRequested(item, index) {
const list = this._customSetting.get();
list.splice(index, 1);
this._customSetting.set(list);
}
/**
* @override
* @param {*} item
* @param {!UI.ListWidget.Editor} editor
* @param {boolean} isNew
*/
commitEdit(item, editor, isNew) {
const conditions = /** @type {?SDK.NetworkManager.Conditions} */ (item);
conditions.title = editor.control('title').value.trim();
const download = editor.control('download').value.trim();
conditions.download = download ? parseInt(download, 10) * (1024 / 8) : -1;
const upload = editor.control('upload').value.trim();
conditions.upload = upload ? parseInt(upload, 10) * (1024 / 8) : -1;
const latency = editor.control('latency').value.trim();
conditions.latency = latency ? parseInt(latency, 10) : 0;
const list = this._customSetting.get();
if (isNew) {
list.push(conditions);
}
this._customSetting.set(list);
}
/**
* @override
* @param {*} item
* @return {!UI.ListWidget.Editor}
*/
beginEdit(item) {
const conditions = /** @type {?SDK.NetworkManager.Conditions} */ (item);
const editor = this._createEditor();
editor.control('title').value = conditions.title;
editor.control('download').value = conditions.download <= 0 ? '' : String(conditions.download / (1024 / 8));
editor.control('upload').value = conditions.upload <= 0 ? '' : String(conditions.upload / (1024 / 8));
editor.control('latency').value = conditions.latency ? String(conditions.latency) : '';
return editor;
}
/**
* @return {!UI.ListWidget.Editor}
*/
_createEditor() {
if (this._editor) {
return this._editor;
}
const editor = new UI.ListWidget.Editor();
this._editor = editor;
const content = editor.contentElement();
const titles = content.createChild('div', 'conditions-edit-row');
const nameLabel = titles.createChild('div', 'conditions-list-text conditions-list-title');
const nameStr = ls`Profile Name`;
nameLabel.textContent = nameStr;
titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
const downloadLabel = titles.createChild('div', 'conditions-list-text');
const downloadStr = ls`Download`;
downloadLabel.textContent = downloadStr;
titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
const uploadLabel = titles.createChild('div', 'conditions-list-text');
const uploadStr = ls`Upload`;
uploadLabel.textContent = uploadStr;
titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
const latencyLabel = titles.createChild('div', 'conditions-list-text');
const latencyStr = ls`Latency`;
latencyLabel.textContent = latencyStr;
const fields = content.createChild('div', 'conditions-edit-row');
const nameInput = editor.createInput('title', 'text', '', titleValidator);
UI.ARIAUtils.setAccessibleName(nameInput, nameStr);
fields.createChild('div', 'conditions-list-text conditions-list-title').appendChild(nameInput);
fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
let cell = fields.createChild('div', 'conditions-list-text');
const downloadInput = editor.createInput('download', 'text', ls`kb/s`, throughputValidator);
cell.appendChild(downloadInput);
UI.ARIAUtils.setAccessibleName(downloadInput, downloadStr);
const downloadOptional = cell.createChild('div', 'conditions-edit-optional');
const optionalStr = ls`optional`;
downloadOptional.textContent = optionalStr;
UI.ARIAUtils.setDescription(downloadInput, optionalStr);
fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
cell = fields.createChild('div', 'conditions-list-text');
const uploadInput = editor.createInput('upload', 'text', ls`kb/s`, throughputValidator);
UI.ARIAUtils.setAccessibleName(uploadInput, uploadStr);
cell.appendChild(uploadInput);
const uploadOptional = cell.createChild('div', 'conditions-edit-optional');
uploadOptional.textContent = optionalStr;
UI.ARIAUtils.setDescription(uploadInput, optionalStr);
fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
cell = fields.createChild('div', 'conditions-list-text');
const latencyInput = editor.createInput('latency', 'text', ls`ms`, latencyValidator);
UI.ARIAUtils.setAccessibleName(latencyInput, latencyStr);
cell.appendChild(latencyInput);
const latencyOptional = cell.createChild('div', 'conditions-edit-optional');
latencyOptional.textContent = optionalStr;
UI.ARIAUtils.setDescription(latencyInput, optionalStr);
return editor;
/**
* @param {*} item
* @param {number} index
* @param {!HTMLInputElement|!HTMLSelectElement} input
* @return {!UI.ListWidget.ValidatorResult}
*/
function titleValidator(item, index, input) {
const maxLength = 49;
const value = input.value.trim();
const valid = value.length > 0 && value.length <= maxLength;
if (!valid) {
const errorMessage = ls`Profile Name characters length must be between 1 to ${maxLength} inclusive`;
return {valid, errorMessage};
}
return {valid};
}
/**
* @param {*} item
* @param {number} index
* @param {!HTMLInputElement|!HTMLSelectElement} input
* @return {!UI.ListWidget.ValidatorResult}
*/
function throughputValidator(item, index, input) {
const minThroughput = 0;
const maxThroughput = 10000000;
const value = input.value.trim();
const parsedValue = Number(value);
const throughput = input.getAttribute('aria-label');
const valid = !Number.isNaN(parsedValue) && parsedValue >= minThroughput && parsedValue <= maxThroughput;
if (!valid) {
const errorMessage =
ls`${throughput} must be a number between ${minThroughput}kb/s to ${maxThroughput}kb/s inclusive`;
return {valid, errorMessage};
}
return {valid};
}
/**
* @param {*} item
* @param {number} index
* @param {!HTMLInputElement|!HTMLSelectElement} input
* @return {!UI.ListWidget.ValidatorResult}
*/
function latencyValidator(item, index, input) {
const minLatency = 0;
const maxLatency = 1000000;
const value = input.value.trim();
const parsedValue = Number(value);
const valid = Number.isInteger(parsedValue) && parsedValue >= minLatency && parsedValue <= maxLatency;
if (!valid) {
const errorMessage = ls`Latency must be an integer between ${minLatency}ms to ${maxLatency}ms inclusive`;
return {valid, errorMessage};
}
return {valid};
}
}
}
/**
* @param {number} throughput
* @param {boolean=} plainText
* @return {string}
*/
export function throughputText(throughput, plainText) {
if (throughput < 0) {
return '';
}
const throughputInKbps = throughput / (1024 / 8);
const delimiter = plainText ? '' : ' ';
if (throughputInKbps < 1024) {
return Common.UIString('%d%skb/s', throughputInKbps, delimiter);
}
if (throughputInKbps < 1024 * 10) {
return Common.UIString('%.1f%sMb/s', throughputInKbps / 1024, delimiter);
}
return Common.UIString('%d%sMb/s', (throughputInKbps / 1024) | 0, delimiter);
}
/* Legacy exported object */
self.MobileThrottling = self.MobileThrottling || {};
/* Legacy exported object */
MobileThrottling = MobileThrottling || {};
/** @constructor */
MobileThrottling.ThrottlingSettingsTab = ThrottlingSettingsTab;
MobileThrottling.throughputText = throughputText;