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

Side by Side Diff: chrome/browser/resources/net_internals/eventsview.js

Issue 7531005: Rename the net_internals file names to include hyphens. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Add some missing files Created 9 years, 4 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 /**
6 * EventsView displays a filtered list of all events sharing a source, and
7 * a details pane for the selected sources.
8 *
9 * +----------------------++----------------+
10 * | filter box || |
11 * +----------------------+| |
12 * | || |
13 * | || |
14 * | || |
15 * | || |
16 * | source list || details |
17 * | || view |
18 * | || |
19 * | || |
20 * | || |
21 * | || |
22 * +----------------------++ |
23 * | action bar || |
24 * +----------------------++----------------+
25 *
26 * @constructor
27 */
28 function EventsView() {
29 const tableBodyId = 'eventsListTableBody';
30 const filterInputId = 'filterInput';
31 const filterCountId = 'filterCount';
32 const deleteSelectedId = 'deleteSelected';
33 const deleteAllId = 'deleteAll';
34 const selectAllId = 'selectAll';
35 const sortByIdId = 'sortById';
36 const sortBySourceTypeId = 'sortBySource';
37 const sortByDescriptionId = 'sortByDescription';
38 const tabHandlesContainerId = 'detailsTabHandles';
39 const logTabId = 'detailsLogTab';
40 const timelineTabId = 'detailsTimelineTab';
41 const detailsLogBoxId = 'detailsLogBox';
42 const detailsTimelineBoxId = 'detailsTimelineBox';
43 const topbarId = 'filterBox';
44 const middleboxId = 'eventsBox';
45 const bottombarId = 'actionBox';
46 const sizerId = 'splitterBoxForEventDetails';
47
48 View.call(this);
49
50 // Initialize the sub-views.
51 var leftPane = new TopMidBottomView(new DivView(topbarId),
52 new DivView(middleboxId),
53 new DivView(bottombarId));
54
55 this.detailsView_ = new DetailsView(tabHandlesContainerId,
56 logTabId,
57 timelineTabId,
58 detailsLogBoxId,
59 detailsTimelineBoxId);
60
61 this.splitterView_ = new ResizableVerticalSplitView(
62 leftPane, this.detailsView_, new DivView(sizerId));
63
64 g_browser.sourceTracker.addObserver(this);
65
66 this.tableBody_ = $(tableBodyId);
67
68 this.filterInput_ = $(filterInputId);
69 this.filterCount_ = $(filterCountId);
70
71 this.filterInput_.addEventListener('search',
72 this.onFilterTextChanged_.bind(this), true);
73
74 $(deleteSelectedId).onclick = this.deleteSelected_.bind(this);
75
76 $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind(
77 g_browser.sourceTracker);
78
79 $(selectAllId).addEventListener('click', this.selectAll_.bind(this), true);
80
81 $(sortByIdId).addEventListener('click', this.sortById_.bind(this), true);
82
83 $(sortBySourceTypeId).addEventListener(
84 'click', this.sortBySourceType_.bind(this), true);
85
86 $(sortByDescriptionId).addEventListener(
87 'click', this.sortByDescription_.bind(this), true);
88
89 // Sets sort order and filter.
90 this.setFilter_('');
91
92 this.initializeSourceList_();
93 }
94
95 inherits(EventsView, View);
96
97 /**
98 * Initializes the list of source entries. If source entries are already,
99 * being displayed, removes them all in the process.
100 */
101 EventsView.prototype.initializeSourceList_ = function() {
102 this.currentSelectedRows_ = [];
103 this.sourceIdToRowMap_ = {};
104 this.tableBody_.innerHTML = '';
105 this.numPrefilter_ = 0;
106 this.numPostfilter_ = 0;
107 this.invalidateFilterCounter_();
108 this.invalidateDetailsView_();
109 };
110
111 // How soon after updating the filter list the counter should be updated.
112 EventsView.REPAINT_FILTER_COUNTER_TIMEOUT_MS = 0;
113
114 EventsView.prototype.setGeometry = function(left, top, width, height) {
115 EventsView.superClass_.setGeometry.call(this, left, top, width, height);
116 this.splitterView_.setGeometry(left, top, width, height);
117 };
118
119 EventsView.prototype.show = function(isVisible) {
120 EventsView.superClass_.show.call(this, isVisible);
121 this.splitterView_.show(isVisible);
122 };
123
124 EventsView.prototype.getFilterText_ = function() {
125 return this.filterInput_.value;
126 };
127
128 EventsView.prototype.setFilterText_ = function(filterText) {
129 this.filterInput_.value = filterText;
130 this.onFilterTextChanged_();
131 };
132
133 EventsView.prototype.onFilterTextChanged_ = function() {
134 this.setFilter_(this.getFilterText_());
135 };
136
137 /**
138 * Updates text in the details view when security stripping is toggled.
139 */
140 EventsView.prototype.onSecurityStrippingChanged = function() {
141 this.invalidateDetailsView_();
142 }
143
144 /**
145 * Sorts active entries first. If both entries are inactive, puts the one
146 * that was active most recently first. If both are active, uses source ID,
147 * which puts longer lived events at the top, and behaves better than using
148 * duration or time of first event.
149 */
150 EventsView.compareActive_ = function(source1, source2) {
151 if (!source1.isInactive() && source2.isInactive())
152 return -1;
153 if (source1.isInactive() && !source2.isInactive())
154 return 1;
155 if (source1.isInactive()) {
156 var deltaEndTime = source1.getEndTime() - source2.getEndTime();
157 if (deltaEndTime != 0) {
158 // The one that ended most recently (Highest end time) should be sorted
159 // first.
160 return -deltaEndTime;
161 }
162 // If both ended at the same time, then odds are they were related events,
163 // started one after another, so sort in the opposite order of their
164 // source IDs to get a more intuitive ordering.
165 return -EventsView.compareSourceId_(source1, source2);
166 }
167 return EventsView.compareSourceId_(source1, source2);
168 };
169
170 EventsView.compareDescription_ = function(source1, source2) {
171 var source1Text = source1.getDescription().toLowerCase();
172 var source2Text = source2.getDescription().toLowerCase();
173 var compareResult = source1Text.localeCompare(source2Text);
174 if (compareResult != 0)
175 return compareResult;
176 return EventsView.compareSourceId_(source1, source2);
177 };
178
179 EventsView.compareDuration_ = function(source1, source2) {
180 var durationDifference = source2.getDuration() - source1.getDuration();
181 if (durationDifference)
182 return durationDifference;
183 return EventsView.compareSourceId_(source1, source2);
184 };
185
186 /**
187 * For the purposes of sorting by source IDs, entries without a source
188 * appear right after the SourceEntry with the highest source ID received
189 * before the sourceless entry. Any ambiguities are resolved by ordering
190 * the entries without a source by the order in which they were received.
191 */
192 EventsView.compareSourceId_ = function(source1, source2) {
193 var sourceId1 = source1.getSourceId();
194 if (sourceId1 < 0)
195 sourceId1 = source1.getMaxPreviousEntrySourceId();
196 var sourceId2 = source2.getSourceId();
197 if (sourceId2 < 0)
198 sourceId2 = source2.getMaxPreviousEntrySourceId();
199
200 if (sourceId1 != sourceId2)
201 return sourceId1 - sourceId2;
202
203 // One or both have a negative ID. In either case, the source with the
204 // highest ID should be sorted first.
205 return source2.getSourceId() - source1.getSourceId();
206 };
207
208 EventsView.compareSourceType_ = function(source1, source2) {
209 var source1Text = source1.getSourceTypeString();
210 var source2Text = source2.getSourceTypeString();
211 var compareResult = source1Text.localeCompare(source2Text);
212 if (compareResult != 0)
213 return compareResult;
214 return EventsView.compareSourceId_(source1, source2);
215 };
216
217 EventsView.prototype.comparisonFuncWithReversing_ = function(a, b) {
218 var result = this.comparisonFunction_(a, b);
219 if (this.doSortBackwards_)
220 result *= -1;
221 return result;
222 };
223
224 EventsView.comparisonFunctionTable_ = {
225 // sort: and sort:- are allowed
226 '': EventsView.compareSourceId_,
227 'active': EventsView.compareActive_,
228 'desc': EventsView.compareDescription_,
229 'description': EventsView.compareDescription_,
230 'duration': EventsView.compareDuration_,
231 'id': EventsView.compareSourceId_,
232 'source': EventsView.compareSourceType_,
233 'type': EventsView.compareSourceType_
234 };
235
236 EventsView.prototype.Sort_ = function() {
237 var sourceEntries = [];
238 for (var id in this.sourceIdToRowMap_) {
239 sourceEntries.push(this.sourceIdToRowMap_[id].getSourceEntry());
240 }
241 sourceEntries.sort(this.comparisonFuncWithReversing_.bind(this));
242
243 // Reposition source rows from back to front.
244 for (var i = sourceEntries.length - 2; i >= 0; --i) {
245 var sourceRow = this.sourceIdToRowMap_[sourceEntries[i].getSourceId()];
246 var nextSourceId = sourceEntries[i + 1].getSourceId();
247 if (sourceRow.getNextNodeSourceId() != nextSourceId) {
248 var nextSourceRow = this.sourceIdToRowMap_[nextSourceId];
249 sourceRow.moveBefore(nextSourceRow);
250 }
251 }
252 };
253
254 /**
255 * Looks for the first occurence of |directive|:parameter in |sourceText|.
256 * Parameter can be an empty string.
257 *
258 * On success, returns an object with two fields:
259 * |remainingText| - |sourceText| with |directive|:parameter removed,
260 and excess whitespace deleted.
261 * |parameter| - the parameter itself.
262 *
263 * On failure, returns null.
264 */
265 EventsView.prototype.parseDirective_ = function(sourceText, directive) {
266 // Adding a leading space allows a single regexp to be used, regardless of
267 // whether or not the directive is at the start of the string.
268 sourceText = ' ' + sourceText;
269 regExp = new RegExp('\\s+' + directive + ':(\\S*)\\s*', 'i');
270 matchInfo = regExp.exec(sourceText);
271 if (matchInfo == null)
272 return null;
273
274 return {'remainingText': sourceText.replace(regExp, ' ').trim(),
275 'parameter': matchInfo[1]};
276 };
277
278 /**
279 * Just like parseDirective_, except can optionally be a '-' before or
280 * the parameter, to negate it. Before is more natural, after
281 * allows more convenient toggling.
282 *
283 * Returned value has the additional field |isNegated|, and a leading
284 * '-' will be removed from |parameter|, if present.
285 */
286 EventsView.prototype.parseNegatableDirective_ = function(sourceText,
287 directive) {
288 var matchInfo = this.parseDirective_(sourceText, directive);
289 if (matchInfo == null)
290 return null;
291
292 // Remove any leading or trailing '-' from the directive.
293 var negationInfo = /^(-?)(\S*?)$/.exec(matchInfo.parameter);
294 matchInfo.parameter = negationInfo[2];
295 matchInfo.isNegated = (negationInfo[1] == '-');
296 return matchInfo;
297 };
298
299 /**
300 * Parse any "sort:" directives, and update |comparisonFunction_| and
301 * |doSortBackwards_|as needed. Note only the last valid sort directive
302 * is used.
303 *
304 * Returns |filterText| with all sort directives removed, including
305 * invalid ones.
306 */
307 EventsView.prototype.parseSortDirectives_ = function(filterText) {
308 this.comparisonFunction_ = EventsView.compareSourceId_;
309 this.doSortBackwards_ = false;
310
311 while (true) {
312 var sortInfo = this.parseNegatableDirective_(filterText, 'sort');
313 if (sortInfo == null)
314 break;
315 var comparisonName = sortInfo.parameter.toLowerCase();
316 if (EventsView.comparisonFunctionTable_[comparisonName] != null) {
317 this.comparisonFunction_ =
318 EventsView.comparisonFunctionTable_[comparisonName];
319 this.doSortBackwards_ = sortInfo.isNegated;
320 }
321 filterText = sortInfo.remainingText;
322 }
323
324 return filterText;
325 };
326
327 /**
328 * Parse any "is:" directives, and update |filter| accordingly.
329 *
330 * Returns |filterText| with all "is:" directives removed, including
331 * invalid ones.
332 */
333 EventsView.prototype.parseRestrictDirectives_ = function(filterText, filter) {
334 while (true) {
335 var filterInfo = this.parseNegatableDirective_(filterText, 'is');
336 if (filterInfo == null)
337 break;
338 if (filterInfo.parameter == 'active') {
339 if (!filterInfo.isNegated) {
340 filter.isActive = true;
341 } else {
342 filter.isInactive = true;
343 }
344 }
345 if (filterInfo.parameter == 'error') {
346 if (!filterInfo.isNegated) {
347 filter.isError = true;
348 } else {
349 filter.isNotError = true;
350 }
351 }
352 filterText = filterInfo.remainingText;
353 }
354 return filterText;
355 };
356
357 /**
358 * Parses all directives that take arbitrary strings as input,
359 * and updates |filter| accordingly. Directives of these types
360 * are stored as lists.
361 *
362 * Returns |filterText| with all recognized directives removed.
363 */
364 EventsView.prototype.parseStringDirectives_ = function(filterText, filter) {
365 var directives = ['type', 'id'];
366 for (var i = 0; i < directives.length; ++i) {
367 while (true) {
368 var directive = directives[i];
369 var filterInfo = this.parseDirective_(filterText, directive);
370 if (filterInfo == null)
371 break;
372 if (!filter[directive])
373 filter[directive] = [];
374 filter[directive].push(filterInfo.parameter);
375 filterText = filterInfo.remainingText;
376 }
377 }
378 return filterText;
379 };
380
381 /*
382 * Converts |filterText| into an object representing the filter.
383 */
384 EventsView.prototype.createFilter_ = function(filterText) {
385 var filter = {};
386 filterText = filterText.toLowerCase();
387 filterText = this.parseRestrictDirectives_(filterText, filter);
388 filterText = this.parseStringDirectives_(filterText, filter);
389 filter.text = filterText.trim();
390 return filter;
391 };
392
393 EventsView.prototype.setFilter_ = function(filterText) {
394 var lastComparisonFunction = this.comparisonFunction_;
395 var lastDoSortBackwards = this.doSortBackwards_;
396
397 filterText = this.parseSortDirectives_(filterText);
398
399 if (lastComparisonFunction != this.comparisonFunction_ ||
400 lastDoSortBackwards != this.doSortBackwards_) {
401 this.Sort_();
402 }
403
404 this.currentFilter_ = this.createFilter_(filterText);
405
406 // Iterate through all of the rows and see if they match the filter.
407 for (var id in this.sourceIdToRowMap_) {
408 var entry = this.sourceIdToRowMap_[id];
409 entry.setIsMatchedByFilter(entry.matchesFilter(this.currentFilter_));
410 }
411 };
412
413 /**
414 * Repositions |sourceRow|'s in the table using an insertion sort.
415 * Significantly faster than sorting the entire table again, when only
416 * one entry has changed.
417 */
418 EventsView.prototype.InsertionSort_ = function(sourceRow) {
419 // SourceRow that should be after |sourceRow|, if it needs
420 // to be moved earlier in the list.
421 var sourceRowAfter = sourceRow;
422 while (true) {
423 var prevSourceId = sourceRowAfter.getPreviousNodeSourceId();
424 if (prevSourceId == null)
425 break;
426 var prevSourceRow = this.sourceIdToRowMap_[prevSourceId];
427 if (this.comparisonFuncWithReversing_(
428 sourceRow.getSourceEntry(),
429 prevSourceRow.getSourceEntry()) >= 0) {
430 break;
431 }
432 sourceRowAfter = prevSourceRow;
433 }
434 if (sourceRowAfter != sourceRow) {
435 sourceRow.moveBefore(sourceRowAfter);
436 return;
437 }
438
439 var sourceRowBefore = sourceRow;
440 while (true) {
441 var nextSourceId = sourceRowBefore.getNextNodeSourceId();
442 if (nextSourceId == null)
443 break;
444 var nextSourceRow = this.sourceIdToRowMap_[nextSourceId];
445 if (this.comparisonFuncWithReversing_(
446 sourceRow.getSourceEntry(),
447 nextSourceRow.getSourceEntry()) <= 0) {
448 break;
449 }
450 sourceRowBefore = nextSourceRow;
451 }
452 if (sourceRowBefore != sourceRow)
453 sourceRow.moveAfter(sourceRowBefore);
454 };
455
456 EventsView.prototype.onSourceEntryUpdated = function(sourceEntry) {
457 // Lookup the row.
458 var sourceRow = this.sourceIdToRowMap_[sourceEntry.getSourceId()];
459
460 if (!sourceRow) {
461 sourceRow = new SourceRow(this, sourceEntry);
462 this.sourceIdToRowMap_[sourceEntry.getSourceId()] = sourceRow;
463 }
464
465 sourceRow.onSourceUpdated();
466
467 if (sourceRow.isSelected())
468 this.invalidateDetailsView_();
469
470 // TODO(mmenke): Fix sorting when sorting by duration.
471 // Duration continuously increases for all entries that are
472 // still active. This can result in incorrect sorting, until
473 // Sort_ is called.
474 this.InsertionSort_(sourceRow);
475 };
476
477 /**
478 * Returns the SourceRow with the specified ID, if there is one.
479 * Otherwise, returns undefined.
480 */
481 EventsView.prototype.getSourceRow = function(id) {
482 return this.sourceIdToRowMap_[id];
483 };
484
485 /**
486 * Called whenever some log events are deleted. |sourceIds| lists
487 * the source IDs of all deleted log entries.
488 */
489 EventsView.prototype.onSourceEntriesDeleted = function(sourceIds) {
490 for (var i = 0; i < sourceIds.length; ++i) {
491 var id = sourceIds[i];
492 var sourceRow = this.sourceIdToRowMap_[id];
493 if (sourceRow) {
494 sourceRow.remove();
495 delete this.sourceIdToRowMap_[id];
496 this.incrementPrefilterCount(-1);
497 }
498 }
499 };
500
501 /**
502 * Called whenever all log events are deleted.
503 */
504 EventsView.prototype.onAllSourceEntriesDeleted = function() {
505 this.initializeSourceList_();
506 };
507
508 /**
509 * Called when either a log file is loaded, after clearing the old entries,
510 * but before getting any new ones.
511 */
512 EventsView.prototype.onLoadLogStart = function() {
513 // Needed to sort new sourceless entries correctly.
514 this.maxReceivedSourceId_ = 0;
515 };
516
517 EventsView.prototype.onLoadLogFinish = function(data) {
518 return true;
519 };
520
521 EventsView.prototype.incrementPrefilterCount = function(offset) {
522 this.numPrefilter_ += offset;
523 this.invalidateFilterCounter_();
524 };
525
526 EventsView.prototype.incrementPostfilterCount = function(offset) {
527 this.numPostfilter_ += offset;
528 this.invalidateFilterCounter_();
529 };
530
531 EventsView.prototype.onSelectionChanged = function() {
532 this.invalidateDetailsView_();
533 };
534
535 EventsView.prototype.clearSelection = function() {
536 var prevSelection = this.currentSelectedRows_;
537 this.currentSelectedRows_ = [];
538
539 // Unselect everything that is currently selected.
540 for (var i = 0; i < prevSelection.length; ++i) {
541 prevSelection[i].setSelected(false);
542 }
543
544 this.onSelectionChanged();
545 };
546
547 EventsView.prototype.deleteSelected_ = function() {
548 var sourceIds = [];
549 for (var i = 0; i < this.currentSelectedRows_.length; ++i) {
550 var sourceRow = this.currentSelectedRows_[i];
551 sourceIds.push(sourceRow.getSourceEntry().getSourceId());
552 }
553 g_browser.sourceTracker.deleteSourceEntries(sourceIds);
554 };
555
556 EventsView.prototype.selectAll_ = function(event) {
557 for (var id in this.sourceIdToRowMap_) {
558 var sourceRow = this.sourceIdToRowMap_[id];
559 if (sourceRow.isMatchedByFilter()) {
560 sourceRow.setSelected(true);
561 }
562 }
563 event.preventDefault();
564 };
565
566 EventsView.prototype.unselectAll_ = function() {
567 var entries = this.currentSelectedRows_.slice(0);
568 for (var i = 0; i < entries.length; ++i) {
569 entries[i].setSelected(false);
570 }
571 };
572
573 /**
574 * If |params| includes a query, replaces the current filter and unselects.
575 * all items.
576 */
577 EventsView.prototype.setParameters = function(params) {
578 if (params.q) {
579 this.unselectAll_();
580 this.setFilterText_(params.q);
581 }
582 };
583
584 /**
585 * If already using the specified sort method, flips direction. Otherwise,
586 * removes pre-existing sort parameter before adding the new one.
587 */
588 EventsView.prototype.toggleSortMethod_ = function(sortMethod) {
589 // Remove old sort directives, if any.
590 var filterText = this.parseSortDirectives_(this.getFilterText_());
591
592 // If already using specified sortMethod, sort backwards.
593 if (!this.doSortBackwards_ &&
594 EventsView.comparisonFunctionTable_[sortMethod] ==
595 this.comparisonFunction_)
596 sortMethod = '-' + sortMethod;
597
598 filterText = 'sort:' + sortMethod + ' ' + filterText;
599 this.setFilterText_(filterText.trim());
600 };
601
602 EventsView.prototype.sortById_ = function(event) {
603 this.toggleSortMethod_('id');
604 };
605
606 EventsView.prototype.sortBySourceType_ = function(event) {
607 this.toggleSortMethod_('source');
608 };
609
610 EventsView.prototype.sortByDescription_ = function(event) {
611 this.toggleSortMethod_('desc');
612 };
613
614 EventsView.prototype.modifySelectionArray = function(
615 sourceRow, addToSelection) {
616 // Find the index for |sourceEntry| in the current selection list.
617 var index = -1;
618 for (var i = 0; i < this.currentSelectedRows_.length; ++i) {
619 if (this.currentSelectedRows_[i] == sourceRow) {
620 index = i;
621 break;
622 }
623 }
624
625 if (index != -1 && !addToSelection) {
626 // Remove from the selection.
627 this.currentSelectedRows_.splice(index, 1);
628 }
629
630 if (index == -1 && addToSelection) {
631 this.currentSelectedRows_.push(sourceRow);
632 }
633 };
634
635 EventsView.prototype.getSelectedSourceEntries_ = function() {
636 var sourceEntries = [];
637 for (var id in this.currentSelectedRows_) {
638 sourceEntries.push(this.currentSelectedRows_[id].getSourceEntry());
639 }
640 return sourceEntries;
641 };
642
643 EventsView.prototype.invalidateDetailsView_ = function() {
644 this.detailsView_.setData(this.getSelectedSourceEntries_());
645 };
646
647 EventsView.prototype.invalidateFilterCounter_ = function() {
648 if (!this.outstandingRepaintFilterCounter_) {
649 this.outstandingRepaintFilterCounter_ = true;
650 window.setTimeout(this.repaintFilterCounter_.bind(this),
651 EventsView.REPAINT_FILTER_COUNTER_TIMEOUT_MS);
652 }
653 };
654
655 EventsView.prototype.repaintFilterCounter_ = function() {
656 this.outstandingRepaintFilterCounter_ = false;
657 this.filterCount_.innerHTML = '';
658 addTextNode(this.filterCount_,
659 this.numPostfilter_ + ' of ' + this.numPrefilter_);
660 };
OLDNEW
« no previous file with comments | « chrome/browser/resources/net_internals/events_view.js ('k') | chrome/browser/resources/net_internals/hsts-view.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698