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.
7 * @param {!HTMLSelectElement} selectElement
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
;
24 * @param {!WebInspector.NetworkManager.Conditions} conditions
27 WebInspector
.NetworkConditionsSelector
.throughputText = function(conditions
)
29 if (conditions
.throughput
< 0)
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);
36 * @param {string} value
39 WebInspector
.NetworkConditionsSelector
.throughputValidator = function(value
)
41 if (!value
|| (/^[\d]+(\.\d+)?|\.\d+$/.test(value
) && value
>= 0 && value
<= 10000000))
43 return WebInspector
.UIString("Value must be non-negative float");
47 * @param {string} value
50 WebInspector
.NetworkConditionsSelector
.latencyValidator = function(value
)
52 if (!value
|| (/^[\d]+$/.test(value
) && value
>= 0 && value
<= 1000000))
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();
88 * @param {!Array.<!WebInspector.NetworkConditionsProfile>} presets
89 * @param {string} groupName
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
;
101 var presetTitle
= WebInspector
.UIString(preset
.title
);
103 option
= new Option(presetTitle
, presetTitle
);
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
);
116 _optionSelected: function()
118 if (this._selectElement
.selectedIndex
=== 0) {
119 WebInspector
.Revealer
.reveal(this._customSetting
);
120 this._settingChanged();
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
;
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 = {
175 WebInspector
.VBox
.prototype.wasShown
.call(this);
176 this._conditionsUpdated();
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
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
);
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
)
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
);
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
)) {
310 * @param {string} placeholder
313 _createInput: function(placeholder
)
315 var input
= createElement("input");
317 input
.placeholder
= placeholder
;
318 input
.addEventListener("input", this._validateInputs
.bind(this, false), false);
319 input
.addEventListener("blur", this._validateInputs
.bind(this, false), false);
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
)
350 this._addCustomButton
.disabled
= true;
351 this._conditionsList
.classList
.add("conditions-list-editing");
352 this._editConditions
= conditions
;
353 this._editConditionsListItem
= listItem
;
355 listItem
.classList
.add("hidden");
357 this._editConditionsCommitButton
.textContent
= listItem
? WebInspector
.UIString("Save") : WebInspector
.UIString("Add profile");
358 this._editConditionsTitle
.value
= conditions
.title
;
360 this._editConditionsThroughput
.value
= conditions
.value
.throughput
< 0 ? "" : String(conditions
.value
.throughput
/ (1024 / 8));
361 this._editConditionsLatency
.value
= String(conditions
.value
.latency
);
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
);
371 this._conditionsList
.insertBefore(this._editConditionsElement
, this._customListSearator
);
372 this._editConditionsCommitButton
.scrollIntoView();
373 this._editConditionsTitle
.focus();
376 _editConditionsCommitClicked: function()
378 if (this._editConditionsCommitButton
.disabled
)
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;
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