Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/components/NetworkConditionsSelector.js

Issue 2668413003: DevTools: extract NetworkConditionsSelector into its own module (Closed)
Patch Set: rebaseline Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4 /**
5 * @unrestricted
6 */
7 Components.NetworkConditionsSelector = class {
8 /**
9 * @param {function(!Array<!Components.NetworkConditionsGroup>):!Array<?SDK.Ne tworkManager.Conditions>} populateCallback
10 * @param {function(number)} selectCallback
11 */
12 constructor(populateCallback, selectCallback) {
13 this._populateCallback = populateCallback;
14 this._selectCallback = selectCallback;
15 this._customSetting = Common.moduleSetting('customNetworkConditions');
16 this._customSetting.addChangeListener(this._populateOptions, this);
17 this._manager = SDK.multitargetNetworkManager;
18 this._manager.addEventListener(
19 SDK.MultitargetNetworkManager.Events.ConditionsChanged, this._conditions Changed, this);
20 this._populateOptions();
21 }
22
23 /**
24 * @param {number} throughput
25 * @param {boolean=} plainText
26 * @return {string}
27 */
28 static _throughputText(throughput, plainText) {
29 if (throughput < 0)
30 return '';
31 var throughputInKbps = throughput / (1024 / 8);
32 var delimiter = plainText ? '' : ' ';
33 if (throughputInKbps < 1024)
34 return Common.UIString('%d%skb/s', throughputInKbps, delimiter);
35 if (throughputInKbps < 1024 * 10)
36 return Common.UIString('%.1f%sMb/s', throughputInKbps / 1024, delimiter);
37 return Common.UIString('%d%sMb/s', (throughputInKbps / 1024) | 0, delimiter) ;
38 }
39
40 /**
41 * @param {!SDK.NetworkManager.Conditions} conditions
42 * @param {boolean=} plainText
43 * @return {!{text: string, title: string}}
44 */
45 static _conditionsTitle(conditions, plainText) {
46 var downloadInKbps = conditions.download / (1024 / 8);
47 var uploadInKbps = conditions.upload / (1024 / 8);
48 var isThrottling = (downloadInKbps >= 0) || (uploadInKbps >= 0) || (conditio ns.latency > 0);
49 var conditionTitle = Common.UIString(conditions.title);
50 if (!isThrottling)
51 return {text: conditionTitle, title: conditionTitle};
52
53 var downloadText = Components.NetworkConditionsSelector._throughputText(cond itions.download, plainText);
54 var uploadText = Components.NetworkConditionsSelector._throughputText(condit ions.upload, plainText);
55 var pattern = plainText ? '%s (%dms, %s, %s)' : '%s (%dms RTT, %s\u2b07, %s\ u2b06)';
56 var title = Common.UIString(pattern, conditionTitle, conditions.latency, dow nloadText, uploadText);
57 return {
58 text: title,
59 title: Common.UIString(
60 'Maximum download throughput: %s.\r\nMaximum upload throughput: %s.\r\ nMinimum round-trip time: %dms.',
61 downloadText, uploadText, conditions.latency)
62 };
63 }
64
65 /**
66 * @param {!HTMLSelectElement} selectElement
67 */
68 static decorateSelect(selectElement) {
69 var options = [];
70 var selector = new Components.NetworkConditionsSelector(populate, select);
71 selectElement.addEventListener('change', optionSelected, false);
72
73 /**
74 * @param {!Array.<!Components.NetworkConditionsGroup>} groups
75 * @return {!Array<?SDK.NetworkManager.Conditions>}
76 */
77 function populate(groups) {
78 selectElement.removeChildren();
79 options = [];
80 for (var i = 0; i < groups.length; ++i) {
81 var group = groups[i];
82 var groupElement = selectElement.createChild('optgroup');
83 groupElement.label = group.title;
84 for (var conditions of group.items) {
85 var title = Components.NetworkConditionsSelector._conditionsTitle(cond itions, true);
86 var option = new Option(title.text, title.text);
87 option.title = title.title;
88 groupElement.appendChild(option);
89 options.push(conditions);
90 }
91 if (i === groups.length - 1) {
92 groupElement.appendChild(new Option(Common.UIString('Add\u2026'), Comm on.UIString('Add\u2026')));
93 options.push(null);
94 }
95 }
96 return options;
97 }
98
99 function optionSelected() {
100 if (selectElement.selectedIndex === selectElement.options.length - 1)
101 selector.revealAndUpdate();
102 else
103 selector.optionSelected(options[selectElement.selectedIndex]);
104 }
105
106 /**
107 * @param {number} index
108 */
109 function select(index) {
110 if (selectElement.selectedIndex !== index)
111 selectElement.selectedIndex = index;
112 }
113 }
114
115 /**
116 * @return {!UI.ToolbarMenuButton}
117 */
118 static createToolbarMenuButton() {
119 var button = new UI.ToolbarMenuButton(appendItems);
120 button.setGlyph('');
121 button.turnIntoSelect();
122
123 /** @type {!Array<?SDK.NetworkManager.Conditions>} */
124 var options = [];
125 var selectedIndex = -1;
126 var selector = new Components.NetworkConditionsSelector(populate, select);
127 return button;
128
129 /**
130 * @param {!UI.ContextMenu} contextMenu
131 */
132 function appendItems(contextMenu) {
133 for (var index = 0; index < options.length; ++index) {
134 var conditions = options[index];
135 if (!conditions) {
136 contextMenu.appendSeparator();
137 } else {
138 contextMenu.appendCheckboxItem(
139 Components.NetworkConditionsSelector._conditionsTitle(conditions, true).text,
140 selector.optionSelected.bind(selector, conditions), selectedIndex === index);
141 }
142 }
143 contextMenu.appendItem(Common.UIString('Edit\u2026'), selector.revealAndUp date.bind(selector));
144 }
145
146 /**
147 * @param {!Array.<!Components.NetworkConditionsGroup>} groups
148 * @return {!Array<?SDK.NetworkManager.Conditions>}
149 */
150 function populate(groups) {
151 options = [];
152 for (var group of groups) {
153 for (var conditions of group.items)
154 options.push(conditions);
155 options.push(null);
156 }
157 return options;
158 }
159
160 /**
161 * @param {number} index
162 */
163 function select(index) {
164 selectedIndex = index;
165 button.setText(options[index].title);
166 }
167 }
168
169 /**
170 * @return {!UI.ToolbarCheckbox}
171 */
172 static createOfflineToolbarCheckbox() {
173 var checkbox = new UI.ToolbarCheckbox(
174 Common.UIString('Offline'), Common.UIString('Force disconnected from net work'), undefined, forceOffline);
175 SDK.multitargetNetworkManager.addEventListener(
176 SDK.MultitargetNetworkManager.Events.ConditionsChanged, networkCondition sChanged);
177 checkbox.setChecked(SDK.multitargetNetworkManager.networkConditions() === SD K.NetworkManager.OfflineConditions);
178
179 var lastNetworkConditions;
180
181 function forceOffline() {
182 if (checkbox.checked()) {
183 lastNetworkConditions = SDK.multitargetNetworkManager.networkConditions( );
184 SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.Of flineConditions);
185 } else {
186 SDK.multitargetNetworkManager.setNetworkConditions(lastNetworkConditions );
187 }
188 }
189
190 function networkConditionsChanged() {
191 var conditions = SDK.multitargetNetworkManager.networkConditions();
192 if (conditions !== SDK.NetworkManager.OfflineConditions)
193 lastNetworkConditions = conditions;
194 checkbox.setChecked(conditions === SDK.NetworkManager.OfflineConditions);
195 }
196 return checkbox;
197 }
198
199 _populateOptions() {
200 var customGroup = {title: Common.UIString('Custom'), items: this._customSett ing.get()};
201 var presetsGroup = {title: Common.UIString('Presets'), items: Components.Net workConditionsSelector._presets};
202 var disabledGroup = {title: Common.UIString('Disabled'), items: [SDK.Network Manager.NoThrottlingConditions]};
203 this._options = this._populateCallback([disabledGroup, presetsGroup, customG roup]);
204 if (!this._conditionsChanged()) {
205 for (var i = this._options.length - 1; i >= 0; i--) {
206 if (this._options[i]) {
207 this.optionSelected(/** @type {!SDK.NetworkManager.Conditions} */ (thi s._options[i]));
208 break;
209 }
210 }
211 }
212 }
213
214 revealAndUpdate() {
215 Common.Revealer.reveal(this._customSetting);
216 this._conditionsChanged();
217 }
218
219 /**
220 * @param {!SDK.NetworkManager.Conditions} conditions
221 */
222 optionSelected(conditions) {
223 this._manager.setNetworkConditions(conditions);
224 }
225
226 /**
227 * @return {boolean}
228 */
229 _conditionsChanged() {
230 var value = this._manager.networkConditions();
231 for (var index = 0; index < this._options.length; ++index) {
232 var option = this._options[index];
233 if (option && option.download === value.download && option.upload === valu e.upload &&
234 option.latency === value.latency && option.title === value.title) {
235 this._selectCallback(index);
236 return true;
237 }
238 }
239 return false;
240 }
241 };
242
243 /** @typedef {!{title: string, items: !Array<!SDK.NetworkManager.Conditions>}} * /
244 Components.NetworkConditionsGroup;
245
246
247 /** @type {!Array.<!SDK.NetworkManager.Conditions>} */
248 Components.NetworkConditionsSelector._presets = [
249 SDK.NetworkManager.OfflineConditions, {title: 'GPRS', download: 50 * 1024 / 8, upload: 20 * 1024 / 8, latency: 500},
250 {title: 'Regular 2G', download: 250 * 1024 / 8, upload: 50 * 1024 / 8, latency : 300},
251 {title: 'Good 2G', download: 450 * 1024 / 8, upload: 150 * 1024 / 8, latency: 150},
252 {title: 'Regular 3G', download: 750 * 1024 / 8, upload: 250 * 1024 / 8, latenc y: 100},
253 {title: 'Good 3G', download: 1.5 * 1024 * 1024 / 8, upload: 750 * 1024 / 8, la tency: 40},
254 {title: 'Regular 4G', download: 4 * 1024 * 1024 / 8, upload: 3 * 1024 * 1024 / 8, latency: 20},
255 {title: 'DSL', download: 2 * 1024 * 1024 / 8, upload: 1 * 1024 * 1024 / 8, lat ency: 5},
256 {title: 'WiFi', download: 30 * 1024 * 1024 / 8, upload: 15 * 1024 * 1024 / 8, latency: 2}
257 ];
258
259
260 /**
261 * @implements {UI.ListWidget.Delegate}
262 * @unrestricted
263 */
264 Components.NetworkConditionsSettingsTab = class extends UI.VBox {
265 constructor() {
266 super(true);
267 this.registerRequiredCSS('components/networkConditionsSettingsTab.css');
268
269 this.contentElement.createChild('div', 'header').textContent = Common.UIStri ng('Network Throttling Profiles');
270
271 var addButton = UI.createTextButton(
272 Common.UIString('Add custom profile...'), this._addButtonClicked.bind(th is), 'add-conditions-button');
273 this.contentElement.appendChild(addButton);
274
275 this._list = new UI.ListWidget(this);
276 this._list.element.classList.add('conditions-list');
277 this._list.registerRequiredCSS('components/networkConditionsSettingsTab.css' );
278 this._list.show(this.contentElement);
279
280 this._customSetting = Common.moduleSetting('customNetworkConditions');
281 this._customSetting.addChangeListener(this._conditionsUpdated, this);
282
283 this.setDefaultFocusedElement(addButton);
284 this.contentElement.tabIndex = 0;
285 }
286
287 /**
288 * @override
289 */
290 wasShown() {
291 super.wasShown();
292 this._conditionsUpdated();
293 }
294
295 _conditionsUpdated() {
296 this._list.clear();
297
298 var conditions = this._customSetting.get();
299 for (var i = 0; i < conditions.length; ++i)
300 this._list.appendItem(conditions[i], true);
301
302 this._list.appendSeparator();
303
304 conditions = Components.NetworkConditionsSelector._presets;
305 for (var i = 0; i < conditions.length; ++i)
306 this._list.appendItem(conditions[i], false);
307 }
308
309 _addButtonClicked() {
310 this._list.addNewItem(this._customSetting.get().length, {title: '', download : -1, upload: -1, latency: 0});
311 }
312
313 /**
314 * @override
315 * @param {*} item
316 * @param {boolean} editable
317 * @return {!Element}
318 */
319 renderItem(item, editable) {
320 var conditions = /** @type {!SDK.NetworkManager.Conditions} */ (item);
321 var element = createElementWithClass('div', 'conditions-list-item');
322 var title = element.createChild('div', 'conditions-list-text conditions-list -title');
323 var titleText = title.createChild('div', 'conditions-list-title-text');
324 titleText.textContent = conditions.title;
325 titleText.title = conditions.title;
326 element.createChild('div', 'conditions-list-separator');
327 element.createChild('div', 'conditions-list-text').textContent =
328 Components.NetworkConditionsSelector._throughputText(conditions.download );
329 element.createChild('div', 'conditions-list-separator');
330 element.createChild('div', 'conditions-list-text').textContent =
331 Components.NetworkConditionsSelector._throughputText(conditions.upload);
332 element.createChild('div', 'conditions-list-separator');
333 element.createChild('div', 'conditions-list-text').textContent = Common.UISt ring('%dms', conditions.latency);
334 return element;
335 }
336
337 /**
338 * @override
339 * @param {*} item
340 * @param {number} index
341 */
342 removeItemRequested(item, index) {
343 var list = this._customSetting.get();
344 list.splice(index, 1);
345 this._customSetting.set(list);
346 }
347
348 /**
349 * @override
350 * @param {*} item
351 * @param {!UI.ListWidget.Editor} editor
352 * @param {boolean} isNew
353 */
354 commitEdit(item, editor, isNew) {
355 var conditions = /** @type {?SDK.NetworkManager.Conditions} */ (item);
356 conditions.title = editor.control('title').value.trim();
357 var download = editor.control('download').value.trim();
358 conditions.download = download ? parseInt(download, 10) * (1024 / 8) : -1;
359 var upload = editor.control('upload').value.trim();
360 conditions.upload = upload ? parseInt(upload, 10) * (1024 / 8) : -1;
361 var latency = editor.control('latency').value.trim();
362 conditions.latency = latency ? parseInt(latency, 10) : 0;
363
364 var list = this._customSetting.get();
365 if (isNew)
366 list.push(conditions);
367 this._customSetting.set(list);
368 }
369
370 /**
371 * @override
372 * @param {*} item
373 * @return {!UI.ListWidget.Editor}
374 */
375 beginEdit(item) {
376 var conditions = /** @type {?SDK.NetworkManager.Conditions} */ (item);
377 var editor = this._createEditor();
378 editor.control('title').value = conditions.title;
379 editor.control('download').value = conditions.download <= 0 ? '' : String(co nditions.download / (1024 / 8));
380 editor.control('upload').value = conditions.upload <= 0 ? '' : String(condit ions.upload / (1024 / 8));
381 editor.control('latency').value = conditions.latency ? String(conditions.lat ency) : '';
382 return editor;
383 }
384
385 /**
386 * @return {!UI.ListWidget.Editor}
387 */
388 _createEditor() {
389 if (this._editor)
390 return this._editor;
391
392 var editor = new UI.ListWidget.Editor();
393 this._editor = editor;
394 var content = editor.contentElement();
395
396 var titles = content.createChild('div', 'conditions-edit-row');
397 titles.createChild('div', 'conditions-list-text conditions-list-title').text Content =
398 Common.UIString('Profile Name');
399 titles.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
400 titles.createChild('div', 'conditions-list-text').textContent = Common.UIStr ing('Download');
401 titles.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
402 titles.createChild('div', 'conditions-list-text').textContent = Common.UIStr ing('Upload');
403 titles.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
404 titles.createChild('div', 'conditions-list-text').textContent = Common.UIStr ing('Latency');
405
406 var fields = content.createChild('div', 'conditions-edit-row');
407 fields.createChild('div', 'conditions-list-text conditions-list-title')
408 .appendChild(editor.createInput('title', 'text', '', titleValidator));
409 fields.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
410
411 var cell = fields.createChild('div', 'conditions-list-text');
412 cell.appendChild(editor.createInput('download', 'text', Common.UIString('kb/ s'), throughputValidator));
413 cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIS tring('optional');
414 fields.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
415
416 cell = fields.createChild('div', 'conditions-list-text');
417 cell.appendChild(editor.createInput('upload', 'text', Common.UIString('kb/s' ), throughputValidator));
418 cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIS tring('optional');
419 fields.createChild('div', 'conditions-list-separator conditions-list-separat or-invisible');
420
421 cell = fields.createChild('div', 'conditions-list-text');
422 cell.appendChild(editor.createInput('latency', 'text', Common.UIString('ms') , latencyValidator));
423 cell.createChild('div', 'conditions-edit-optional').textContent = Common.UIS tring('optional');
424
425 return editor;
426
427 /**
428 * @param {*} item
429 * @param {number} index
430 * @param {!HTMLInputElement|!HTMLSelectElement} input
431 * @return {boolean}
432 */
433 function titleValidator(item, index, input) {
434 var value = input.value.trim();
435 return value.length > 0 && value.length < 50;
436 }
437
438 /**
439 * @param {*} item
440 * @param {number} index
441 * @param {!HTMLInputElement|!HTMLSelectElement} input
442 * @return {boolean}
443 */
444 function throughputValidator(item, index, input) {
445 var value = input.value.trim();
446 return !value || (/^[\d]+(\.\d+)?|\.\d+$/.test(value) && value >= 0 && val ue <= 10000000);
447 }
448
449 /**
450 * @param {*} item
451 * @param {number} index
452 * @param {!HTMLInputElement|!HTMLSelectElement} input
453 * @return {boolean}
454 */
455 function latencyValidator(item, index, input) {
456 var value = input.value.trim();
457 return !value || (/^[\d]+$/.test(value) && value >= 0 && value <= 1000000) ;
458 }
459 }
460 };
461
462 /**
463 * @implements {UI.ActionDelegate}
464 * @unrestricted
465 */
466 Components.NetworkConditionsActionDelegate = class {
467 /**
468 * @override
469 * @param {!UI.Context} context
470 * @param {string} actionId
471 * @return {boolean}
472 */
473 handleAction(context, actionId) {
474 if (actionId === 'components.network-online') {
475 SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.NoTh rottlingConditions);
476 return true;
477 }
478 if (actionId === 'components.network-offline') {
479 SDK.multitargetNetworkManager.setNetworkConditions(SDK.NetworkManager.Offl ineConditions);
480 return true;
481 }
482 return false;
483 }
484 };
485
486 /**
487 * @param {!Protocol.Network.ResourcePriority} priority
488 * @return {string}
489 */
490 Components.uiLabelForPriority = function(priority) {
491 var map = Components.priorityUiLabelMap();
492 return map.get(priority) || '';
493 };
494
495 /**
496 * @param {string} priorityLabel
497 * @return {string}
498 */
499 Components.uiLabelToPriority = function(priorityLabel) {
500 /** @type {!Map<string, !Protocol.Network.ResourcePriority>} */
501 var labelToPriorityMap = Components.uiLabelToPriority._uiLabelToPriorityMap;
502
503 if (labelToPriorityMap)
504 return labelToPriorityMap.get(priorityLabel);
505
506 labelToPriorityMap = new Map();
507 Components.priorityUiLabelMap().forEach((value, key) => labelToPriorityMap.set (value, key));
508 Components.uiLabelToPriority._uiLabelToPriorityMap = labelToPriorityMap;
509 return labelToPriorityMap.get(priorityLabel) || '';
510 };
511
512 /**
513 * @return {!Map<!Protocol.Network.ResourcePriority, string>}
514 */
515 Components.priorityUiLabelMap = function() {
516 /** @type {!Map<!Protocol.Network.ResourcePriority, string>} */
517 var map = Components.priorityUiLabelMap._priorityUiLabelMap;
518
519 if (map)
520 return map;
521
522 map = new Map();
523 map.set(Protocol.Network.ResourcePriority.VeryLow, Common.UIString('Lowest'));
524 map.set(Protocol.Network.ResourcePriority.Low, Common.UIString('Low'));
525 map.set(Protocol.Network.ResourcePriority.Medium, Common.UIString('Medium'));
526 map.set(Protocol.Network.ResourcePriority.High, Common.UIString('High'));
527 map.set(Protocol.Network.ResourcePriority.VeryHigh, Common.UIString('Highest') );
528 Components.priorityUiLabelMap._priorityUiLabelMap = map;
529
530 return map;
531 };
532
533 /**
534 * @return {!Map<!Protocol.Network.ResourcePriority, number>}
535 */
536 Components.prioritySymbolToNumericMap = function() {
537 /** @type {!Map<!Protocol.Network.ResourcePriority, number>} */
538 var priorityMap = Components.prioritySymbolToNumericMap._symbolicToNumericPrio rityMap;
539
540 if (priorityMap)
541 return priorityMap;
542
543 priorityMap = new Map();
544 priorityMap.set(Protocol.Network.ResourcePriority.VeryLow, 1);
545 priorityMap.set(Protocol.Network.ResourcePriority.Low, 2);
546 priorityMap.set(Protocol.Network.ResourcePriority.Medium, 3);
547 priorityMap.set(Protocol.Network.ResourcePriority.High, 4);
548 priorityMap.set(Protocol.Network.ResourcePriority.VeryHigh, 5);
549 Components.prioritySymbolToNumericMap._symbolicToNumericPriorityMap = priority Map;
550
551 return priorityMap;
552 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698