1 // Copyright (c) 2012 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 var SourceEntry = (function() {
9 * A SourceEntry gathers all log entries with the same source.
13 function SourceEntry(logEntry, maxPreviousSourceId) {
14 this.maxPreviousSourceId_ = maxPreviousSourceId;
16 this.description_ = '';
18 // Set to true on most net errors.
19 this.isError_ = false;
21 // If the first entry is a BEGIN_PHASE, set to false.
22 // Set to true when an END_PHASE matching the first entry is encountered.
23 this.isInactive_ = true;
25 if (logEntry.phase == EventPhase.PHASE_BEGIN)
26 this.isInactive_ = false;
28 this.update(logEntry);
31 SourceEntry.prototype = {
32 update: function(logEntry) {
33 // Only the last event should have the same type first event,
34 if (!this.isInactive_ &&
35 logEntry.phase == EventPhase.PHASE_END &&
36 logEntry.type == this.entries_[0].type) {
37 this.isInactive_ = true;
40 // If we have a net error code, update |this.isError_| if appropriate.
41 if (logEntry.params) {
42 var netErrorCode = logEntry.params.net_error;
43 // Skip both cases where netErrorCode is undefined, and cases where it
44 // is 0, indicating no actual error occurred.
46 // Ignore error code caused by not finding an entry in the cache.
47 if (logEntry.type != EventType.HTTP_CACHE_OPEN_ENTRY ||
48 netErrorCode != NetError.ERR_FAILED) {
54 var prevStartEntry = this.getStartEntry_();
55 this.entries_.push(logEntry);
56 var curStartEntry = this.getStartEntry_();
58 // If we just got the first entry for this source.
59 if (prevStartEntry != curStartEntry)
60 this.updateDescription_();
63 updateDescription_: function() {
64 var e = this.getStartEntry_();
65 this.description_ = '';
69 if (e.source.type == EventSourceType.NONE) {
70 // NONE is what we use for global events that aren't actually grouped
71 // by a "source ID", so we will just stringize the event's type.
72 this.description_ = EventTypeNames[e.type];
76 if (e.params == undefined) {
80 switch (e.source.type) {
81 case EventSourceType.URL_REQUEST:
82 // TODO(ricea): Remove SOCKET_STREAM after M41 is released.
83 case EventSourceType.SOCKET_STREAM:
84 case EventSourceType.HTTP_STREAM_JOB:
85 case EventSourceType.ASYNC_REVALIDATION:
86 this.description_ = e.params.url;
88 case EventSourceType.CONNECT_JOB:
89 this.description_ = e.params.group_name;
91 case EventSourceType.HOST_RESOLVER_IMPL_JOB:
92 case EventSourceType.HOST_RESOLVER_IMPL_PROC_TASK:
93 this.description_ = e.params.host;
95 case EventSourceType.DISK_CACHE_ENTRY:
96 case EventSourceType.MEMORY_CACHE_ENTRY:
97 this.description_ = e.params.key;
99 case EventSourceType.QUIC_SESSION:
100 if (e.params.host != undefined)
101 this.description_ = e.params.host;
103 case EventSourceType.HTTP2_SESSION:
105 this.description_ = e.params.host + ' (' + e.params.proxy + ')';
107 case EventSourceType.HTTP_PIPELINED_CONNECTION:
108 if (e.params.host_and_port)
109 this.description_ = e.params.host_and_port;
111 case EventSourceType.SOCKET:
112 case EventSourceType.PROXY_CLIENT_SOCKET:
113 // Use description of parent source, if any.
114 if (e.params.source_dependency != undefined) {
115 var parentId = e.params.source_dependency.id;
117 SourceTracker.getInstance().getDescription(parentId);
120 case EventSourceType.UDP_SOCKET:
121 if (e.params.address != undefined) {
122 this.description_ = e.params.address;
123 // If the parent of |this| is a HOST_RESOLVER_IMPL_JOB, use
124 // '<DNS Server IP> [<host we're resolving>]'.
125 if (this.entries_[0].type == EventType.SOCKET_ALIVE &&
126 this.entries_[0].params &&
127 this.entries_[0].params.source_dependency != undefined) {
128 var parentId = this.entries_[0].params.source_dependency.id;
129 var parent = SourceTracker.getInstance().getSourceEntry(parentId);
131 parent.getSourceType() ==
132 EventSourceType.HOST_RESOLVER_IMPL_JOB &&
133 parent.getDescription().length > 0) {
134 this.description_ += ' [' + parent.getDescription() + ']';
139 case EventSourceType.ASYNC_HOST_RESOLVER_REQUEST:
140 case EventSourceType.DNS_TRANSACTION:
141 this.description_ = e.params.hostname;
143 case EventSourceType.DOWNLOAD:
145 case EventType.DOWNLOAD_FILE_RENAMED:
146 this.description_ = e.params.new_filename;
148 case EventType.DOWNLOAD_FILE_OPENED:
149 this.description_ = e.params.file_name;
151 case EventType.DOWNLOAD_ITEM_ACTIVE:
152 this.description_ = e.params.file_name;
156 case EventSourceType.FILESTREAM:
157 this.description_ = e.params.file_name;
159 case EventSourceType.IPV6_PROBE_JOB:
160 if (e.type == EventType.IPV6_PROBE_RUNNING &&
161 e.phase == EventPhase.PHASE_END) {
162 this.description_ = e.params.ipv6_supported ? 'IPv6 Supported' :
163 'IPv6 Not Supported';
168 if (this.description_ == undefined)
169 this.description_ = '';
173 * Returns a description for this source log stream, which will be displayed
174 * in the list view. Most often this is a URL that identifies the request,
175 * or a hostname for a connect job, etc...
177 getDescription: function() {
178 return this.description_;
182 * Returns the starting entry for this source. Conceptually this is the
183 * first entry that was logged to this source. However, we skip over the
184 * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB
187 getStartEntry_: function() {
188 if (this.entries_.length < 1)
190 if (this.entries_[0].source.type == EventSourceType.FILESTREAM) {
191 var e = this.findLogEntryByType_(EventType.FILE_STREAM_OPEN);
195 if (this.entries_[0].source.type == EventSourceType.DOWNLOAD) {
196 // If any rename occurred, use the last name
197 e = this.findLastLogEntryStartByType_(
198 EventType.DOWNLOAD_FILE_RENAMED);
201 // Otherwise, if the file was opened, use that name
202 e = this.findLogEntryByType_(EventType.DOWNLOAD_FILE_OPENED);
205 // History items are never opened, so use the activation info
206 e = this.findLogEntryByType_(EventType.DOWNLOAD_ITEM_ACTIVE);
210 if (this.entries_.length >= 2) {
211 // Needed for compatability with log dumps prior to M26.
212 // TODO(mmenke): Remove this.
213 if (this.entries_[0].type == EventType.SOCKET_POOL_CONNECT_JOB &&
214 this.entries_[0].params == undefined) {
215 return this.entries_[1];
217 if (this.entries_[1].type == EventType.UDP_CONNECT)
218 return this.entries_[1];
219 if (this.entries_[0].type == EventType.REQUEST_ALIVE &&
220 this.entries_[0].params == undefined) {
222 // Skip over delegate events for URL_REQUESTs.
223 for (; startIndex + 1 < this.entries_.length; ++startIndex) {
224 var type = this.entries_[startIndex].type;
225 if (type != EventType.URL_REQUEST_DELEGATE &&
226 type != EventType.DELEGATE_INFO) {
230 return this.entries_[startIndex];
232 if (this.entries_[1].type == EventType.IPV6_PROBE_RUNNING)
233 return this.entries_[1];
235 return this.entries_[0];
239 * Returns the first entry with the specified type, or undefined if not
242 findLogEntryByType_: function(type) {
243 for (var i = 0; i < this.entries_.length; ++i) {
244 if (this.entries_[i].type == type) {
245 return this.entries_[i];
252 * Returns the beginning of the last entry with the specified type, or
253 * undefined if not found.
255 findLastLogEntryStartByType_: function(type) {
256 for (var i = this.entries_.length - 1; i >= 0; --i) {
257 if (this.entries_[i].type == type) {
258 if (this.entries_[i].phase != EventPhase.PHASE_END)
259 return this.entries_[i];
265 getLogEntries: function() {
266 return this.entries_;
269 getSourceTypeString: function() {
270 return EventSourceTypeNames[this.entries_[0].source.type];
273 getSourceType: function() {
274 return this.entries_[0].source.type;
277 getSourceId: function() {
278 return this.entries_[0].source.id;
282 * Returns the largest source ID seen before this object was received.
283 * Used only for sorting SourceEntries without a source by source ID.
285 getMaxPreviousEntrySourceId: function() {
286 return this.maxPreviousSourceId_;
289 isInactive: function() {
290 return this.isInactive_;
293 isError: function() {
294 return this.isError_;
298 * Returns time ticks of first event.
300 getStartTicks: function() {
301 return this.entries_[0].time;
305 * Returns time of last event if inactive. Returns current time otherwise.
306 * Returned time is a "time ticks" value.
308 getEndTicks: function() {
309 if (!this.isInactive_)
310 return timeutil.getCurrentTimeTicks();
311 return this.entries_[this.entries_.length - 1].time;
315 * Returns the time between the first and last events with a matching
316 * source ID. If source is still active, uses the current time for the
319 getDuration: function() {
320 var startTime = this.getStartTicks();
321 var endTime = this.getEndTicks();
322 return endTime - startTime;
326 * Prints descriptive text about |entries_| to a new node added to the end
329 printAsText: function(parent) {
330 var tablePrinter = this.createTablePrinter();
332 // Format the table for fixed-width text.
333 tablePrinter.toText(0, parent);
337 * Creates a table printer for the SourceEntry.
339 createTablePrinter: function() {
340 return createLogEntryTablePrinter(
342 SourceTracker.getInstance().getPrivacyStripping(),
343 SourceTracker.getInstance().getUseRelativeTimes() ?
344 timeutil.getBaseTime() : 0,
345 Constants.clientInfo.numericDate);