Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / components / NetworkConditionsSelector.js
blob69058bf5dcf8203ab5719eec8ec6acdbe68cfcb3
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6 * @constructor
7 * @param {!HTMLSelectElement} selectElement
8 */
9 WebInspector.NetworkConditionsSelector = function(selectElement)
11 this._selectElement = selectElement;
12 this._selectElement.addEventListener("change", this._optionSelected.bind(this), false);
13 this._customSetting = WebInspector.moduleSetting("networkConditionsCustomProfiles");
14 this._customSetting.addChangeListener(this._populateOptions, this);
15 this._setting = WebInspector.moduleSetting("networkConditions");
16 this._setting.addChangeListener(this._settingChanged, this);
17 this._populateOptions();
20 /** @typedef {!{title: string, value: !WebInspector.NetworkManager.Conditions}} */
21 WebInspector.NetworkConditionsProfile;
23 /**
24 * @param {!WebInspector.NetworkManager.Conditions} conditions
25 * @return {string}
27 WebInspector.NetworkConditionsSelector.throughputText = function(conditions)
29 if (conditions.throughput < 0)
30 return "";
31 var throughputInKbps = conditions.throughput / (1024 / 8);
32 return (throughputInKbps < 1024) ? WebInspector.UIString("%d kb/s", throughputInKbps) : WebInspector.UIString("%d Mb/s", (throughputInKbps / 1024) | 0);
35 /**
36 * @param {string} value
37 * @return {string}
39 WebInspector.NetworkConditionsSelector.throughputValidator = function(value)
41 if (!value || (/^[\d]+(\.\d+)?|\.\d+$/.test(value) && value >= 0 && value <= 10000000))
42 return "";
43 return WebInspector.UIString("Value must be non-negative float");
46 /**
47 * @param {string} value
48 * @return {string}
50 WebInspector.NetworkConditionsSelector.latencyValidator = function(value)
52 if (!value || (/^[\d]+$/.test(value) && value >= 0 && value <= 1000000))
53 return "";
54 return WebInspector.UIString("Value must be non-negative integer");
57 /** @type {!Array.<!WebInspector.NetworkConditionsProfile>} */
58 WebInspector.NetworkConditionsSelector._networkConditionsPresets = [
59 {title: "Offline", value: {throughput: 0 * 1024 / 8, latency: 0}},
60 {title: "GPRS", value: {throughput: 50 * 1024 / 8, latency: 500}},
61 {title: "Regular 2G", value: {throughput: 250 * 1024 / 8, latency: 300}},
62 {title: "Good 2G", value: {throughput: 450 * 1024 / 8, latency: 150}},
63 {title: "Regular 3G", value: {throughput: 750 * 1024 / 8, latency: 100}},
64 {title: "Good 3G", value: {throughput: 1.5 * 1024 * 1024 / 8, latency: 40}},
65 {title: "Regular 4G", value: {throughput: 4 * 1024 * 1024 / 8, latency: 20}},
66 {title: "DSL", value: {throughput: 2 * 1024 * 1024 / 8, latency: 5}},
67 {title: "WiFi", value: {throughput: 30 * 1024 * 1024 / 8, latency: 2}}
70 /** @type {!WebInspector.NetworkConditionsProfile} */
71 WebInspector.NetworkConditionsSelector._disabledPreset = {title: "No throttling", value: {throughput: -1, latency: 0}};
73 WebInspector.NetworkConditionsSelector.prototype = {
74 _populateOptions: function()
76 this._selectElement.removeChildren();
78 var customGroup = this._addGroup(this._customSetting.get(), WebInspector.UIString("Custom"));
79 customGroup.insertBefore(new Option(WebInspector.UIString("Add\u2026"), WebInspector.UIString("Add\u2026")), customGroup.firstChild);
81 this._addGroup(WebInspector.NetworkConditionsSelector._networkConditionsPresets, WebInspector.UIString("Presets"));
82 this._addGroup([WebInspector.NetworkConditionsSelector._disabledPreset], WebInspector.UIString("Disabled"));
84 this._settingChanged();
87 /**
88 * @param {!Array.<!WebInspector.NetworkConditionsProfile>} presets
89 * @param {string} groupName
90 * @return {!Element}
92 _addGroup: function(presets, groupName)
94 var groupElement = this._selectElement.createChild("optgroup");
95 groupElement.label = groupName;
96 for (var i = 0; i < presets.length; ++i) {
97 var preset = presets[i];
98 var throughputInKbps = preset.value.throughput / (1024 / 8);
99 var isThrottling = (throughputInKbps > 0) || preset.value.latency;
100 var option;
101 var presetTitle = WebInspector.UIString(preset.title);
102 if (!isThrottling) {
103 option = new Option(presetTitle, presetTitle);
104 } else {
105 var throughputText = WebInspector.NetworkConditionsSelector.throughputText(preset.value);
106 var title = WebInspector.UIString("%s (%s %dms RTT)", presetTitle, throughputText, preset.value.latency);
107 option = new Option(title, presetTitle);
108 option.title = WebInspector.UIString("Maximum download throughput: %s.\r\nMinimum round-trip time: %dms.", throughputText, preset.value.latency);
110 option.settingValue = preset.value;
111 groupElement.appendChild(option);
113 return groupElement;
116 _optionSelected: function()
118 if (this._selectElement.selectedIndex === 0) {
119 WebInspector.Revealer.reveal(this._customSetting);
120 this._settingChanged();
121 return;
124 this._setting.removeChangeListener(this._settingChanged, this);
125 this._setting.set(this._selectElement.options[this._selectElement.selectedIndex].settingValue);
126 this._setting.addChangeListener(this._settingChanged, this);
129 _settingChanged: function()
131 var value = this._setting.get();
132 var options = this._selectElement.options;
133 for (var index = 1; index < options.length; ++index) {
134 var option = options[index];
135 if (option.settingValue.throughput === value.throughput && option.settingValue.latency === value.latency)
136 this._selectElement.selectedIndex = index;
143 * @constructor
144 * @extends {WebInspector.VBox}
146 WebInspector.NetworkConditionsSettingsTab = function()
148 WebInspector.VBox.call(this);
149 this.element.classList.add("settings-tab-container");
150 this.element.classList.add("network-conditions-settings-tab");
151 this.registerRequiredCSS("components/networkConditionsSettingsTab.css");
153 var header = this.element.createChild("header");
154 header.createChild("h3").createTextChild(WebInspector.UIString("Network Throttling Profiles"));
155 this.containerElement = this.element.createChild("div", "help-container-wrapper").createChild("div", "settings-tab help-content help-container");
157 var buttonsRow = this.containerElement.createChild("div", "button-row");
158 this._addCustomButton = createTextButton(WebInspector.UIString("Add custom profile..."), this._addCustomConditions.bind(this));
159 buttonsRow.appendChild(this._addCustomButton);
161 this._conditionsList = this.containerElement.createChild("div", "conditions-list");
162 this._customListSearator = createElementWithClass("div", "custom-separator");
164 this._editConditions = null;
165 this._editConditionsListItem = null;
166 this._customSetting = WebInspector.moduleSetting("networkConditionsCustomProfiles");
167 this._customSetting.addChangeListener(this._conditionsUpdated, this);
169 this._createEditConditionsElement();
172 WebInspector.NetworkConditionsSettingsTab.prototype = {
173 wasShown: function()
175 WebInspector.VBox.prototype.wasShown.call(this);
176 this._conditionsUpdated();
177 this._stopEditing();
180 _conditionsUpdated: function()
182 this._conditionsList.removeChildren();
184 var conditions = this._customSetting.get();
185 for (var i = 0; i < conditions.length; ++i)
186 this._conditionsList.appendChild(this._createConditionsListItem(conditions[i], true));
188 this._conditionsList.appendChild(this._customListSearator);
189 this._updateSeparatorVisibility();
191 conditions = WebInspector.NetworkConditionsSelector._networkConditionsPresets;
192 for (var i = 0; i < conditions.length; ++i)
193 this._conditionsList.appendChild(this._createConditionsListItem(conditions[i], false));
196 _updateSeparatorVisibility: function()
198 this._customListSearator.classList.toggle("hidden", this._conditionsList.firstChild === this._customListSearator);
202 * @param {!WebInspector.NetworkConditionsProfile} conditions
203 * @param {boolean} custom
204 * @return {!Element}
206 _createConditionsListItem: function(conditions, custom)
208 var item = createElementWithClass("div", "conditions-list-item");
209 var title = item.createChild("div", "conditions-list-text conditions-list-title");
210 var titleText = title.createChild("div", "conditions-list-title-text");
211 titleText.textContent = conditions.title;
212 titleText.title = conditions.title;
213 item.createChild("div", "conditions-list-separator");
214 item.createChild("div", "conditions-list-text").textContent = WebInspector.NetworkConditionsSelector.throughputText(conditions.value);
215 item.createChild("div", "conditions-list-separator");
216 item.createChild("div", "conditions-list-text").textContent = WebInspector.UIString("%dms", conditions.value.latency);
218 if (custom) {
219 var editButton = title.createChild("div", "conditions-list-edit");
220 editButton.title = WebInspector.UIString("Edit");
221 editButton.addEventListener("click", onEditClicked.bind(this), false);
223 var removeButton = title.createChild("div", "conditions-list-remove");
224 removeButton.title = WebInspector.UIString("Remove");
225 removeButton.addEventListener("click", onRemoveClicked.bind(this), false);
229 * @param {!Event} event
230 * @this {WebInspector.NetworkConditionsSettingsTab}
232 function onEditClicked(event)
234 event.consume();
235 this._startEditing(conditions, item);
239 * @param {!Event} event
240 * @this {WebInspector.NetworkConditionsSettingsTab}
242 function onRemoveClicked(event)
244 var list = this._customSetting.get();
245 list.remove(conditions);
246 this._customSetting.set(list);
247 event.consume();
250 return item;
253 _addCustomConditions: function()
255 var conditions = {title: "", value: {throughput: 0, latency: 0}};
256 this._startEditing(conditions, null);
259 _createEditConditionsElement: function()
261 this._editConditionsElement = createElementWithClass("div", "conditions-edit-container");
262 this._editConditionsElement.addEventListener("keydown", onKeyDown.bind(null, isEscKey, this._stopEditing.bind(this)), false);
263 this._editConditionsElement.addEventListener("keydown", onKeyDown.bind(null, isEnterKey, this._editConditionsCommitClicked.bind(this)), false);
265 var titles = this._editConditionsElement.createChild("div", "conditions-edit-row");
266 titles.createChild("div", "conditions-list-text conditions-list-title").textContent = WebInspector.UIString("Profile Name");
267 titles.createChild("div", "conditions-list-separator conditions-list-separator-invisible");
268 titles.createChild("div", "conditions-list-text").textContent = WebInspector.UIString("Throughput");
269 titles.createChild("div", "conditions-list-separator conditions-list-separator-invisible");
270 titles.createChild("div", "conditions-list-text").textContent = WebInspector.UIString("Latency");
272 var fields = this._editConditionsElement.createChild("div", "conditions-edit-row");
273 this._editConditionsTitle = this._createInput("");
274 fields.createChild("div", "conditions-list-text conditions-list-title").appendChild(this._editConditionsTitle);
275 fields.createChild("div", "conditions-list-separator conditions-list-separator-invisible");
277 this._editConditionsThroughput = this._createInput(WebInspector.UIString("kb/s"));
278 var cell = fields.createChild("div", "conditions-list-text");
279 cell.appendChild(this._editConditionsThroughput);
280 cell.createChild("div", "conditions-edit-optional").textContent = WebInspector.UIString("optional");
281 fields.createChild("div", "conditions-list-separator conditions-list-separator-invisible");
283 this._editConditionsLatency = this._createInput(WebInspector.UIString("ms"));
284 cell = fields.createChild("div", "conditions-list-text");
285 cell.appendChild(this._editConditionsLatency);
286 cell.createChild("div", "conditions-edit-optional").textContent = WebInspector.UIString("optional");
288 var buttons = this._editConditionsElement.createChild("div", "conditions-edit-row");
289 this._editConditionsCommitButton = createTextButton("", this._editConditionsCommitClicked.bind(this));
290 buttons.appendChild(this._editConditionsCommitButton);
291 this._editConditionsCancelButton = createTextButton(WebInspector.UIString("Cancel"), this._stopEditing.bind(this));
292 this._editConditionsCancelButton.addEventListener("keydown", onKeyDown.bind(null, isEnterKey, this._stopEditing.bind(this)), false);
293 buttons.appendChild(this._editConditionsCancelButton);
296 * @param {function(!Event):boolean} predicate
297 * @param {function()} callback
298 * @param {!Event} event
300 function onKeyDown(predicate, callback, event)
302 if (predicate(event)) {
303 event.consume(true);
304 callback();
310 * @param {string} placeholder
311 * @return {!Element}
313 _createInput: function(placeholder)
315 var input = createElement("input");
316 input.type = "text";
317 input.placeholder = placeholder;
318 input.addEventListener("input", this._validateInputs.bind(this, false), false);
319 input.addEventListener("blur", this._validateInputs.bind(this, false), false);
320 return input;
324 * @param {boolean} forceValid
326 _validateInputs: function(forceValid)
328 var trimmedTitle = this._editConditionsTitle.value.trim();
329 var titleValid = trimmedTitle.length > 0 && trimmedTitle.length < 50;
330 this._editConditionsTitle.classList.toggle("error-input", !titleValid && !forceValid);
332 var throughputValid = !WebInspector.NetworkConditionsSelector.throughputValidator(this._editConditionsThroughput.value);
333 this._editConditionsThroughput.classList.toggle("error-input", !throughputValid && !forceValid);
335 var latencyValid = !WebInspector.NetworkConditionsSelector.latencyValidator(this._editConditionsLatency.value);
336 this._editConditionsLatency.classList.toggle("error-input", !latencyValid && !forceValid);
338 var allValid = titleValid && throughputValid && latencyValid;
339 this._editConditionsCommitButton.disabled = !allValid;
343 * @param {!WebInspector.NetworkConditionsProfile} conditions
344 * @param {?Element} listItem
346 _startEditing: function(conditions, listItem)
348 this._stopEditing();
350 this._addCustomButton.disabled = true;
351 this._conditionsList.classList.add("conditions-list-editing");
352 this._editConditions = conditions;
353 this._editConditionsListItem = listItem;
354 if (listItem)
355 listItem.classList.add("hidden");
357 this._editConditionsCommitButton.textContent = listItem ? WebInspector.UIString("Save") : WebInspector.UIString("Add profile");
358 this._editConditionsTitle.value = conditions.title;
359 if (listItem) {
360 this._editConditionsThroughput.value = conditions.value.throughput < 0 ? "" : String(conditions.value.throughput / (1024 / 8));
361 this._editConditionsLatency.value = String(conditions.value.latency);
362 } else {
363 this._editConditionsThroughput.value = "";
364 this._editConditionsLatency.value = "";
366 this._validateInputs(true);
368 if (listItem && listItem.nextElementSibling)
369 this._conditionsList.insertBefore(this._editConditionsElement, listItem.nextElementSibling);
370 else
371 this._conditionsList.insertBefore(this._editConditionsElement, this._customListSearator);
372 this._editConditionsCommitButton.scrollIntoView();
373 this._editConditionsTitle.focus();
376 _editConditionsCommitClicked: function()
378 if (this._editConditionsCommitButton.disabled)
379 return;
381 this._editConditions.title = this._editConditionsTitle.value;
382 this._editConditions.value.throughput = this._editConditionsThroughput.value ? parseInt(this._editConditionsThroughput.value, 10) * (1024 / 8) : -1;
383 this._editConditions.value.latency = this._editConditionsLatency.value ? parseInt(this._editConditionsLatency.value, 10) : 0;
385 this._stopEditing();
387 var list = this._customSetting.get();
388 if (!this._editConditionsListItem)
389 list.push(this._editConditions);
390 this._customSetting.set(list);
392 this._editConditions = null;
393 this._editConditionsListItem = null;
396 _stopEditing: function()
398 this._conditionsList.classList.remove("conditions-list-editing");
399 if (this._editConditionsListItem)
400 this._editConditionsListItem.classList.remove("hidden");
401 if (this._editConditionsElement.parentElement)
402 this._conditionsList.removeChild(this._editConditionsElement);
403 this._addCustomButton.disabled = false;
404 this._addCustomButton.focus();
407 __proto__: WebInspector.VBox.prototype