The Great Blink mv for source files, part 2.
Move and rename files.
NOAUTOREVERT=true
NOPRESUBMIT=true
NOTREECHECKS=true
Bug: 768828
[email protected]
NOTRY=true
Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28
Reviewed-on: https://chromium-review.googlesource.com/1001153
Reviewed-by: Blink Reformat <[email protected]>
Commit-Queue: Blink Reformat <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#549061}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
diff --git a/front_end/mobile_throttling/MobileThrottlingSelector.js b/front_end/mobile_throttling/MobileThrottlingSelector.js
new file mode 100644
index 0000000..b7c246f
--- /dev/null
+++ b/front_end/mobile_throttling/MobileThrottlingSelector.js
@@ -0,0 +1,52 @@
+// 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.
+
+MobileThrottling.MobileThrottlingSelector = class {
+ /**
+ * @param {function(!Array<!MobileThrottling.MobileThrottlingConditionsGroup>):!MobileThrottling.ConditionsList} populateCallback
+ * @param {function(number)} selectCallback
+ */
+ constructor(populateCallback, selectCallback) {
+ this._populateCallback = populateCallback;
+ this._selectCallback = selectCallback;
+ MobileThrottling.throttlingManager().addEventListener(
+ MobileThrottling.ThrottlingManager.Events.RateChanged, this._conditionsChanged, this);
+ SDK.multitargetNetworkManager.addEventListener(
+ SDK.MultitargetNetworkManager.Events.ConditionsChanged, this._conditionsChanged, this);
+ /** @type {!MobileThrottling.ConditionsList} */
+ this._options = this._populateOptions();
+ this._conditionsChanged();
+ }
+
+ /**
+ * @param {!MobileThrottling.Conditions} conditions
+ */
+ optionSelected(conditions) {
+ SDK.multitargetNetworkManager.setNetworkConditions(conditions.network);
+ MobileThrottling.throttlingManager().setCPUThrottlingRate(conditions.cpuThrottlingRate);
+ }
+
+ /**
+ * @return {!MobileThrottling.ConditionsList}
+ */
+ _populateOptions() {
+ const disabledGroup = {title: Common.UIString('Disabled'), items: [MobileThrottling.NoThrottlingConditions]};
+ const presetsGroup = {title: Common.UIString('Presets'), items: MobileThrottling.mobilePresets};
+ const advancedGroup = {title: Common.UIString('Advanced'), items: MobileThrottling.advancedMobilePresets};
+ return this._populateCallback([disabledGroup, presetsGroup, advancedGroup]);
+ }
+
+ _conditionsChanged() {
+ const networkConditions = SDK.multitargetNetworkManager.networkConditions();
+ const cpuThrottlingRate = MobileThrottling.throttlingManager().cpuThrottlingRate();
+ for (let index = 0; index < this._options.length; ++index) {
+ const option = this._options[index];
+ if (option && option.network === networkConditions && option.cpuThrottlingRate === cpuThrottlingRate) {
+ this._selectCallback(index);
+ return;
+ }
+ }
+ this._selectCallback(this._options.indexOf(MobileThrottling.CustomConditions));
+ }
+};
diff --git a/front_end/mobile_throttling/NetworkPanelIndicator.js b/front_end/mobile_throttling/NetworkPanelIndicator.js
new file mode 100644
index 0000000..a3de20b
--- /dev/null
+++ b/front_end/mobile_throttling/NetworkPanelIndicator.js
@@ -0,0 +1,31 @@
+// 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.
+
+MobileThrottling.NetworkPanelIndicator = class {
+ constructor() {
+ // TODO: we should not access network from other modules.
+ if (!UI.inspectorView.hasPanel('network'))
+ return;
+ const manager = SDK.multitargetNetworkManager;
+ manager.addEventListener(SDK.MultitargetNetworkManager.Events.ConditionsChanged, updateVisibility);
+ manager.addEventListener(SDK.MultitargetNetworkManager.Events.BlockedPatternsChanged, updateVisibility);
+ manager.addEventListener(SDK.MultitargetNetworkManager.Events.InterceptorsChanged, updateVisibility);
+ updateVisibility();
+
+ function updateVisibility() {
+ let icon = null;
+ if (manager.isThrottling()) {
+ icon = UI.Icon.create('smallicon-warning');
+ icon.title = Common.UIString('Network throttling is enabled');
+ } else if (SDK.multitargetNetworkManager.isIntercepting()) {
+ icon = UI.Icon.create('smallicon-warning');
+ icon.title = Common.UIString('Requests may be rewritten');
+ } else if (manager.isBlocking()) {
+ icon = UI.Icon.create('smallicon-warning');
+ icon.title = Common.UIString('Requests may be blocked');
+ }
+ UI.inspectorView.setPanelIcon('network', icon);
+ }
+ }
+};
diff --git a/front_end/mobile_throttling/NetworkThrottlingSelector.js b/front_end/mobile_throttling/NetworkThrottlingSelector.js
new file mode 100644
index 0000000..cb1290f
--- /dev/null
+++ b/front_end/mobile_throttling/NetworkThrottlingSelector.js
@@ -0,0 +1,65 @@
+// 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.
+
+MobileThrottling.NetworkThrottlingSelector = class {
+ /**
+ * @param {function(!Array<!MobileThrottling.NetworkThrottlingConditionsGroup>):!Array<?SDK.NetworkManager.Conditions>} populateCallback
+ * @param {function(number)} selectCallback
+ * @param {!Common.Setting<!Array<!SDK.NetworkManager.Conditions>>} customNetworkConditionsSetting
+ */
+ constructor(populateCallback, selectCallback, customNetworkConditionsSetting) {
+ this._populateCallback = populateCallback;
+ this._selectCallback = selectCallback;
+ this._customNetworkConditionsSetting = customNetworkConditionsSetting;
+ this._customNetworkConditionsSetting.addChangeListener(this._populateOptions, this);
+ SDK.multitargetNetworkManager.addEventListener(
+ SDK.MultitargetNetworkManager.Events.ConditionsChanged, this._networkConditionsChanged, this);
+ /** @type {!Array<?SDK.NetworkManager.Conditions>} */
+ this._options;
+ this._populateOptions();
+ }
+
+ revealAndUpdate() {
+ Common.Revealer.reveal(this._customNetworkConditionsSetting);
+ this._networkConditionsChanged();
+ }
+
+ /**
+ * @param {!SDK.NetworkManager.Conditions} conditions
+ */
+ optionSelected(conditions) {
+ SDK.multitargetNetworkManager.setNetworkConditions(conditions);
+ }
+
+ _populateOptions() {
+ const disabledGroup = {title: Common.UIString('Disabled'), items: [SDK.NetworkManager.NoThrottlingConditions]};
+ const presetsGroup = {title: Common.UIString('Presets'), items: MobileThrottling.networkPresets};
+ const customGroup = {title: Common.UIString('Custom'), items: this._customNetworkConditionsSetting.get()};
+ this._options = this._populateCallback([disabledGroup, presetsGroup, customGroup]);
+ if (!this._networkConditionsChanged()) {
+ for (let i = this._options.length - 1; i >= 0; i--) {
+ if (this._options[i]) {
+ this.optionSelected(/** @type {!SDK.NetworkManager.Conditions} */ (this._options[i]));
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return {boolean} returns false if selected condition no longer exists
+ */
+ _networkConditionsChanged() {
+ const value = SDK.multitargetNetworkManager.networkConditions();
+ for (let index = 0; index < this._options.length; ++index) {
+ const option = this._options[index];
+ if (option && option.download === value.download && option.upload === value.upload &&
+ option.latency === value.latency && option.title === value.title) {
+ this._selectCallback(index);
+ return true;
+ }
+ }
+ return false;
+ }
+};
diff --git a/front_end/mobile_throttling/ThrottlingManager.js b/front_end/mobile_throttling/ThrottlingManager.js
new file mode 100644
index 0000000..ab8834b
--- /dev/null
+++ b/front_end/mobile_throttling/ThrottlingManager.js
@@ -0,0 +1,267 @@
+// 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 {SDK.SDKModelObserver<!SDK.EmulationModel>}
+ */
+MobileThrottling.ThrottlingManager = class extends Common.Object {
+ constructor() {
+ super();
+ /** @type {!MobileThrottling.CPUThrottlingRates} */
+ this._cpuThrottlingRate = MobileThrottling.CPUThrottlingRates.NoThrottling;
+ /** @type {!Set<!UI.ToolbarComboBox>} */
+ this._cpuThrottlingControls = new Set();
+ this._cpuThrottlingRates = MobileThrottling.cpuThrottlingPresets;
+ /** @type {!Common.Setting<!Array<!SDK.NetworkManager.Conditions>>} */
+ this._customNetworkConditionsSetting = Common.moduleSetting('customNetworkConditions');
+ /** @type {!SDK.NetworkManager.Conditions} */
+ this._currentNetworkThrottlingConditions = SDK.NetworkManager.NoThrottlingConditions;
+ /** @type {!SDK.NetworkManager.Conditions} */
+ this._lastNetworkThrottlingConditions;
+
+ SDK.multitargetNetworkManager.addEventListener(SDK.MultitargetNetworkManager.Events.ConditionsChanged, () => {
+ this._lastNetworkThrottlingConditions = this._currentNetworkThrottlingConditions;
+ this._currentNetworkThrottlingConditions = SDK.multitargetNetworkManager.networkConditions();
+ });
+
+ SDK.targetManager.observeModels(SDK.EmulationModel, this);
+ }
+
+
+ /**
+ * @param {!HTMLSelectElement} selectElement
+ * @return {!MobileThrottling.NetworkThrottlingSelector}
+ */
+ decorateSelectWithNetworkThrottling(selectElement) {
+ let options = [];
+ const selector =
+ new MobileThrottling.NetworkThrottlingSelector(populate, select, this._customNetworkConditionsSetting);
+ selectElement.addEventListener('change', optionSelected, false);
+ return selector;
+
+ /**
+ * @param {!Array.<!MobileThrottling.NetworkThrottlingConditionsGroup>} groups
+ * @return {!Array<?SDK.NetworkManager.Conditions>}
+ */
+ function populate(groups) {
+ selectElement.removeChildren();
+ options = [];
+ for (let i = 0; i < groups.length; ++i) {
+ const group = groups[i];
+ const groupElement = selectElement.createChild('optgroup');
+ groupElement.label = group.title;
+ for (const conditions of group.items) {
+ const title = conditions.title;
+ const option = new Option(title, title);
+ groupElement.appendChild(option);
+ options.push(conditions);
+ }
+ if (i === groups.length - 1) {
+ groupElement.appendChild(new Option(Common.UIString('Add\u2026'), Common.UIString('Add\u2026')));
+ options.push(null);
+ }
+ }
+ return options;
+ }
+
+ function optionSelected() {
+ if (selectElement.selectedIndex === selectElement.options.length - 1)
+ selector.revealAndUpdate();
+ else
+ selector.optionSelected(options[selectElement.selectedIndex]);
+ }
+
+ /**
+ * @param {number} index
+ */
+ function select(index) {
+ if (selectElement.selectedIndex !== index)
+ selectElement.selectedIndex = index;
+ }
+ }
+
+ /**
+ * @return {!UI.ToolbarCheckbox}
+ */
+ createOfflineToolbarCheckbox() {
+ const checkbox = new UI.ToolbarCheckbox(
+ Common.UIString('Offline'), Common.UIString('Force disconnected from network'), forceOffline.bind(this));
+ SDK.multitargetNetworkManager.addEventListener(
+ SDK.MultitargetNetworkManager.Events.ConditionsChanged, networkConditionsChanged);
+ checkbox.setChecked(SDK.multitargetNetworkManager.networkConditions() === SDK.NetworkManager.OfflineConditions);
+
+ /**
+ * @this {!MobileThrottling.ThrottlingManager}
+ */
+ function forceOffline() {
+ if (checkbox.checked())
+ SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.OfflineConditions);
+ else
+ SDK.multitargetNetworkManager.setNetworkConditions(this._lastNetworkThrottlingConditions);
+ }
+
+ function networkConditionsChanged() {
+ checkbox.setChecked(SDK.multitargetNetworkManager.networkConditions() === SDK.NetworkManager.OfflineConditions);
+ }
+
+ return checkbox;
+ }
+
+
+ /**
+ * @return {!UI.ToolbarMenuButton}
+ */
+ createMobileThrottlingButton() {
+ const button = new UI.ToolbarMenuButton(appendItems);
+ button.setTitle(Common.UIString('Throttling'));
+ button.setGlyph('');
+ button.turnIntoSelect();
+ button.setDarkText();
+
+ /** @type {!MobileThrottling.ConditionsList} */
+ let options = [];
+ let selectedIndex = -1;
+ const selector = new MobileThrottling.MobileThrottlingSelector(populate, select);
+ return button;
+
+ /**
+ * @param {!UI.ContextMenu} contextMenu
+ */
+ function appendItems(contextMenu) {
+ for (let index = 0; index < options.length; ++index) {
+ const conditions = options[index];
+ if (!conditions)
+ continue;
+ if (conditions.title === MobileThrottling.CustomConditions.title &&
+ conditions.description === MobileThrottling.CustomConditions.description)
+ continue;
+ contextMenu.defaultSection().appendCheckboxItem(
+ Common.UIString(conditions.title),
+ selector.optionSelected.bind(selector, /** @type {!MobileThrottling.Conditions} */ (conditions)),
+ selectedIndex === index);
+ }
+ }
+
+ /**
+ * @param {!Array.<!MobileThrottling.MobileThrottlingConditionsGroup>} groups
+ * @return {!MobileThrottling.ConditionsList}
+ */
+ function populate(groups) {
+ options = [];
+ for (const group of groups) {
+ for (const conditions of group.items)
+ options.push(conditions);
+ options.push(null);
+ }
+ return options;
+ }
+
+ /**
+ * @param {number} index
+ */
+ function select(index) {
+ selectedIndex = index;
+ button.setText(options[index].title);
+ button.setTitle(options[index].description);
+ }
+ }
+
+ /**
+ * @return {number}
+ */
+ cpuThrottlingRate() {
+ return this._cpuThrottlingRate;
+ }
+
+ /**
+ * @param {!MobileThrottling.CPUThrottlingRates} rate
+ */
+ setCPUThrottlingRate(rate) {
+ this._cpuThrottlingRate = rate;
+ for (const emulationModel of SDK.targetManager.models(SDK.EmulationModel))
+ emulationModel.setCPUThrottlingRate(this._cpuThrottlingRate);
+ let icon = null;
+ if (this._cpuThrottlingRate !== MobileThrottling.CPUThrottlingRates.NoThrottling) {
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.CpuThrottlingEnabled);
+ icon = UI.Icon.create('smallicon-warning');
+ icon.title = Common.UIString('CPU throttling is enabled');
+ }
+ const index = this._cpuThrottlingRates.indexOf(this._cpuThrottlingRate);
+ for (const control of this._cpuThrottlingControls)
+ control.setSelectedIndex(index);
+ UI.inspectorView.setPanelIcon('timeline', icon);
+ this.dispatchEventToListeners(MobileThrottling.ThrottlingManager.Events.RateChanged, this._cpuThrottlingRate);
+ }
+
+ /**
+ * @override
+ * @param {!SDK.EmulationModel} emulationModel
+ */
+ modelAdded(emulationModel) {
+ if (this._cpuThrottlingRate !== MobileThrottling.CPUThrottlingRates.NoThrottling)
+ emulationModel.setCPUThrottlingRate(this._cpuThrottlingRate);
+ }
+
+ /**
+ * @override
+ * @param {!SDK.EmulationModel} emulationModel
+ */
+ modelRemoved(emulationModel) {
+ }
+
+ /**
+ * @return {!UI.ToolbarComboBox}
+ */
+ createCPUThrottlingSelector() {
+ const control = new UI.ToolbarComboBox(
+ event => this.setCPUThrottlingRate(this._cpuThrottlingRates[event.target.selectedIndex]));
+ this._cpuThrottlingControls.add(control);
+ const currentRate = this._cpuThrottlingRate;
+
+ for (let i = 0; i < this._cpuThrottlingRates.length; ++i) {
+ const rate = this._cpuThrottlingRates[i];
+ const title = rate === 1 ? Common.UIString('No throttling') : Common.UIString('%d\xD7 slowdown', rate);
+ const option = control.createOption(title);
+ control.addOption(option);
+ if (currentRate === rate)
+ control.setSelectedIndex(i);
+ }
+ return control;
+ }
+};
+
+/** @enum {symbol} */
+MobileThrottling.ThrottlingManager.Events = {
+ RateChanged: Symbol('RateChanged')
+};
+
+/**
+ * @implements {UI.ActionDelegate}
+ */
+MobileThrottling.ThrottlingManager.ActionDelegate = class {
+ /**
+ * @override
+ * @param {!UI.Context} context
+ * @param {string} actionId
+ * @return {boolean}
+ */
+ handleAction(context, actionId) {
+ if (actionId === 'network-conditions.network-online') {
+ SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.NoThrottlingConditions);
+ return true;
+ }
+ if (actionId === 'network-conditions.network-offline') {
+ SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.OfflineConditions);
+ return true;
+ }
+ return false;
+ }
+};
+
+/**
+ * @return {!MobileThrottling.ThrottlingManager}
+ */
+MobileThrottling.throttlingManager = function() {
+ return self.singleton(MobileThrottling.ThrottlingManager);
+};
diff --git a/front_end/mobile_throttling/ThrottlingPresets.js b/front_end/mobile_throttling/ThrottlingPresets.js
new file mode 100644
index 0000000..463ddb0
--- /dev/null
+++ b/front_end/mobile_throttling/ThrottlingPresets.js
@@ -0,0 +1,99 @@
+// 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.
+
+/** @enum {number} */
+MobileThrottling.CPUThrottlingRates = {
+ NoThrottling: 1,
+ MidTierMobile: 4,
+ LowEndMobile: 6,
+};
+
+/**
+ * @typedef {{
+ * title: string,
+ * description: string,
+ * network: !SDK.NetworkManager.Conditions,
+ * cpuThrottlingRate: !MobileThrottling.CPUThrottlingRates
+ * }}
+ **/
+MobileThrottling.Conditions;
+
+/** @type {!MobileThrottling.Conditions} */
+MobileThrottling.NoThrottlingConditions = {
+ title: SDK.NetworkManager.NoThrottlingConditions.title,
+ description: Common.UIString('No throttling'),
+ network: SDK.NetworkManager.NoThrottlingConditions,
+ cpuThrottlingRate: MobileThrottling.CPUThrottlingRates.NoThrottling,
+};
+
+/** @type {!MobileThrottling.Conditions} */
+MobileThrottling.OfflineConditions = {
+ title: SDK.NetworkManager.OfflineConditions.title,
+ description: Common.UIString('No internet connectivity'),
+ network: SDK.NetworkManager.OfflineConditions,
+ cpuThrottlingRate: MobileThrottling.CPUThrottlingRates.NoThrottling,
+};
+
+/** @type {!MobileThrottling.Conditions} */
+MobileThrottling.LowEndMobileConditions = {
+ title: Common.UIString('Low-end mobile'),
+ description: Common.UIString('Slow 3G & 6x CPU slowdown'),
+ network: SDK.NetworkManager.Slow3GConditions,
+ cpuThrottlingRate: MobileThrottling.CPUThrottlingRates.LowEndMobile,
+};
+
+/** @type {!MobileThrottling.Conditions} */
+MobileThrottling.MidTierMobileConditions = {
+ title: Common.UIString('Mid-tier mobile'),
+ description: Common.UIString('Fast 3G & 4x CPU slowdown'),
+ network: SDK.NetworkManager.Fast3GConditions,
+ cpuThrottlingRate: MobileThrottling.CPUThrottlingRates.MidTierMobile,
+};
+
+/**
+ * @typedef {{
+ * title: string,
+ * description: string
+ * }}
+ **/
+MobileThrottling.PlaceholderConditions;
+
+/** @type {!MobileThrottling.PlaceholderConditions} */
+MobileThrottling.CustomConditions = {
+ title: Common.UIString('Custom'),
+ description: Common.UIString('Check Network and Performance panels'),
+};
+
+/** @typedef {!{title: string, items: !Array<!SDK.NetworkManager.Conditions>}} */
+MobileThrottling.NetworkThrottlingConditionsGroup;
+
+/** @typedef {!{title: string, items: !Array<!MobileThrottling.Conditions|!MobileThrottling.PlaceholderConditions>}} */
+MobileThrottling.MobileThrottlingConditionsGroup;
+
+/** @typedef {!Array<?MobileThrottling.Conditions|!MobileThrottling.PlaceholderConditions>} */
+MobileThrottling.ConditionsList;
+
+/** @type {!Array.<!MobileThrottling.Conditions>} */
+MobileThrottling.mobilePresets = [
+ MobileThrottling.MidTierMobileConditions, MobileThrottling.LowEndMobileConditions, MobileThrottling.CustomConditions
+];
+
+/** @type {!Array.<!MobileThrottling.Conditions>} */
+MobileThrottling.advancedMobilePresets = [
+ MobileThrottling.OfflineConditions,
+];
+
+/** @type {!Array<!SDK.NetworkManager.Conditions>} */
+MobileThrottling.networkPresets = [
+ SDK.NetworkManager.Fast3GConditions,
+ SDK.NetworkManager.Slow3GConditions,
+ SDK.NetworkManager.OfflineConditions,
+];
+
+/** @type {!Array<!MobileThrottling.CPUThrottlingRates>} */
+MobileThrottling.cpuThrottlingPresets = [
+ MobileThrottling.CPUThrottlingRates.NoThrottling,
+ MobileThrottling.CPUThrottlingRates.MidTierMobile,
+ MobileThrottling.CPUThrottlingRates.LowEndMobile,
+];
diff --git a/front_end/mobile_throttling/ThrottlingSettingsTab.js b/front_end/mobile_throttling/ThrottlingSettingsTab.js
new file mode 100644
index 0000000..e303163
--- /dev/null
+++ b/front_end/mobile_throttling/ThrottlingSettingsTab.js
@@ -0,0 +1,217 @@
+// 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
+ */
+MobileThrottling.ThrottlingSettingsTab = class extends UI.VBox {
+ constructor() {
+ super(true);
+ this.registerRequiredCSS('mobile_throttling/throttlingSettingsTab.css');
+
+ this.contentElement.createChild('div', 'header').textContent = Common.UIString('Network Throttling Profiles');
+
+ 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);
+ this.contentElement.tabIndex = 0;
+ }
+
+ /**
+ * @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 =
+ MobileThrottling.throughputText(conditions.download);
+ element.createChild('div', 'conditions-list-separator');
+ element.createChild('div', 'conditions-list-text').textContent = MobileThrottling.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');
+ titles.createChild('div', 'conditions-list-text conditions-list-title').textContent =
+ Common.UIString('Profile Name');
+ titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+ titles.createChild('div', 'conditions-list-text').textContent = Common.UIString('Download');
+ titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+ titles.createChild('div', 'conditions-list-text').textContent = Common.UIString('Upload');
+ titles.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+ titles.createChild('div', 'conditions-list-text').textContent = Common.UIString('Latency');
+
+ const fields = content.createChild('div', 'conditions-edit-row');
+ fields.createChild('div', 'conditions-list-text conditions-list-title')
+ .appendChild(editor.createInput('title', 'text', '', titleValidator));
+ fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+
+ let cell = fields.createChild('div', 'conditions-list-text');
+ cell.appendChild(editor.createInput('download', 'text', Common.UIString('kb/s'), throughputValidator));
+ cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIString('optional');
+ fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+
+ cell = fields.createChild('div', 'conditions-list-text');
+ cell.appendChild(editor.createInput('upload', 'text', Common.UIString('kb/s'), throughputValidator));
+ cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIString('optional');
+ fields.createChild('div', 'conditions-list-separator conditions-list-separator-invisible');
+
+ cell = fields.createChild('div', 'conditions-list-text');
+ cell.appendChild(editor.createInput('latency', 'text', Common.UIString('ms'), latencyValidator));
+ cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIString('optional');
+
+ return editor;
+
+ /**
+ * @param {*} item
+ * @param {number} index
+ * @param {!HTMLInputElement|!HTMLSelectElement} input
+ * @return {boolean}
+ */
+ function titleValidator(item, index, input) {
+ const value = input.value.trim();
+ return value.length > 0 && value.length < 50;
+ }
+
+ /**
+ * @param {*} item
+ * @param {number} index
+ * @param {!HTMLInputElement|!HTMLSelectElement} input
+ * @return {boolean}
+ */
+ function throughputValidator(item, index, input) {
+ const value = input.value.trim();
+ return !value || (/^[\d]+(\.\d+)?|\.\d+$/.test(value) && value >= 0 && value <= 10000000);
+ }
+
+ /**
+ * @param {*} item
+ * @param {number} index
+ * @param {!HTMLInputElement|!HTMLSelectElement} input
+ * @return {boolean}
+ */
+ function latencyValidator(item, index, input) {
+ const value = input.value.trim();
+ return !value || (/^[\d]+$/.test(value) && value >= 0 && value <= 1000000);
+ }
+ }
+};
+
+/**
+ * @param {number} throughput
+ * @param {boolean=} plainText
+ * @return {string}
+ */
+MobileThrottling.throughputText = function(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);
+};
diff --git a/front_end/mobile_throttling/module.json b/front_end/mobile_throttling/module.json
new file mode 100644
index 0000000..217bc85
--- /dev/null
+++ b/front_end/mobile_throttling/module.json
@@ -0,0 +1,54 @@
+{
+ "extensions": [
+ {
+ "type": "setting",
+ "settingName": "customNetworkConditions",
+ "settingType": "array",
+ "defaultValue": []
+ },
+ {
+ "type": "action",
+ "actionId": "network-conditions.network-offline",
+ "category": "Network",
+ "title": "Go offline",
+ "className": "MobileThrottling.ThrottlingManager.ActionDelegate",
+ "tags": "device"
+ },
+ {
+ "type": "action",
+ "actionId": "network-conditions.network-online",
+ "category": "Network",
+ "title": "Go online",
+ "className": "MobileThrottling.ThrottlingManager.ActionDelegate",
+ "tags": "device"
+ },
+ {
+ "type": "view",
+ "location": "settings-view",
+ "id": "throttling-conditions",
+ "title": "Throttling",
+ "order": 35,
+ "className": "MobileThrottling.ThrottlingSettingsTab",
+ "settings": [
+ "customNetworkConditions"
+ ]
+ }
+ ],
+ "dependencies": [
+ "common",
+ "sdk",
+ "ui",
+ "protocol"
+ ],
+ "scripts": [
+ "ThrottlingPresets.js",
+ "MobileThrottlingSelector.js",
+ "NetworkPanelIndicator.js",
+ "NetworkThrottlingSelector.js",
+ "ThrottlingSettingsTab.js",
+ "ThrottlingManager.js"
+ ],
+ "resources": [
+ "throttlingSettingsTab.css"
+ ]
+}
diff --git a/front_end/mobile_throttling/throttlingSettingsTab.css b/front_end/mobile_throttling/throttlingSettingsTab.css
new file mode 100644
index 0000000..d85c7bd
--- /dev/null
+++ b/front_end/mobile_throttling/throttlingSettingsTab.css
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+:host {
+ overflow:hidden;
+}
+
+.header {
+ padding: 0 0 6px;
+ border-bottom: 1px solid #EEEEEE;
+ font-size: 18px;
+ font-weight: normal;
+ flex: none;
+}
+
+.add-conditions-button {
+ flex: none;
+ margin: 10px 2px;
+ min-width: 140px;
+ align-self: flex-start;
+}
+
+.conditions-list {
+ max-width: 500px;
+ min-width: 340px;
+ flex: auto;
+}
+
+.conditions-list-item {
+ padding: 3px 5px 3px 5px;
+ height: 30px;
+ display: flex;
+ align-items: center;
+ position: relative;
+ flex: auto 1 1;
+}
+
+.conditions-list-text {
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ flex: 0 0 70px;
+ -webkit-user-select: none;
+ color: #222;
+ text-align: end;
+ position: relative;
+}
+
+.conditions-list-title {
+ text-align: start;
+ flex: auto;
+ display: flex;
+ align-items: flex-start;
+}
+
+.conditions-list-title-text {
+ overflow: hidden;
+ flex: auto;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.conditions-list-separator {
+ flex: 0 0 1px;
+ background-color: rgb(231, 231, 231);
+ height: 30px;
+ margin: 0 4px;
+}
+
+.conditions-list-separator-invisible {
+ visibility: hidden;
+ height: 100% !important;
+}
+
+.conditions-edit-row {
+ flex: none;
+ display: flex;
+ flex-direction: row;
+ margin: 6px 5px;
+}
+
+.conditions-edit-row input {
+ width: 100%;
+ text-align: inherit;
+}
+
+.conditions-edit-optional {
+ position: absolute;
+ bottom: -20px;
+ right: 0;
+ color: rgb(128, 128, 128);
+}