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. 4 5var SourceEntry = (function() { 6 'use strict'; 7 8 /** 9 * A SourceEntry gathers all log entries with the same source. 10 * 11 * @constructor 12 */ 13 function SourceEntry(logEntry, maxPreviousSourceId) { 14 this.maxPreviousSourceId_ = maxPreviousSourceId; 15 this.entries_ = []; 16 this.description_ = ''; 17 18 // Set to true on most net errors. 19 this.isError_ = false; 20 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; 24 25 if (logEntry.phase == EventPhase.PHASE_BEGIN) 26 this.isInactive_ = false; 27 28 this.update(logEntry); 29 } 30 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; 38 } 39 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. 45 if (netErrorCode) { 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.FAILED) { 49 this.isError_ = true; 50 } 51 } 52 } 53 54 var prevStartEntry = this.getStartEntry_(); 55 this.entries_.push(logEntry); 56 var curStartEntry = this.getStartEntry_(); 57 58 // If we just got the first entry for this source. 59 if (prevStartEntry != curStartEntry) 60 this.updateDescription_(); 61 }, 62 63 updateDescription_: function() { 64 var e = this.getStartEntry_(); 65 this.description_ = ''; 66 if (!e) 67 return; 68 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]; 73 return; 74 } 75 76 if (e.params == undefined) { 77 return; 78 } 79 80 switch (e.source.type) { 81 case EventSourceType.URL_REQUEST: 82 case EventSourceType.SOCKET_STREAM: 83 case EventSourceType.HTTP_STREAM_JOB: 84 this.description_ = e.params.url; 85 break; 86 case EventSourceType.CONNECT_JOB: 87 this.description_ = e.params.group_name; 88 break; 89 case EventSourceType.HOST_RESOLVER_IMPL_REQUEST: 90 case EventSourceType.HOST_RESOLVER_IMPL_JOB: 91 case EventSourceType.HOST_RESOLVER_IMPL_PROC_TASK: 92 this.description_ = e.params.host; 93 break; 94 case EventSourceType.DISK_CACHE_ENTRY: 95 case EventSourceType.MEMORY_CACHE_ENTRY: 96 this.description_ = e.params.key; 97 break; 98 case EventSourceType.QUIC_SESSION: 99 if (e.params.host != undefined) 100 this.description_ = e.params.host; 101 break; 102 case EventSourceType.SPDY_SESSION: 103 if (e.params.host) 104 this.description_ = e.params.host + ' (' + e.params.proxy + ')'; 105 break; 106 case EventSourceType.HTTP_PIPELINED_CONNECTION: 107 if (e.params.host_and_port) 108 this.description_ = e.params.host_and_port; 109 break; 110 case EventSourceType.SOCKET: 111 case EventSourceType.PROXY_CLIENT_SOCKET: 112 // Use description of parent source, if any. 113 if (e.params.source_dependency != undefined) { 114 var parentId = e.params.source_dependency.id; 115 this.description_ = 116 SourceTracker.getInstance().getDescription(parentId); 117 } 118 break; 119 case EventSourceType.UDP_SOCKET: 120 if (e.params.address != undefined) { 121 this.description_ = e.params.address; 122 // If the parent of |this| is a HOST_RESOLVER_IMPL_JOB, use 123 // '<DNS Server IP> [<host we're resolving>]'. 124 if (this.entries_[0].type == EventType.SOCKET_ALIVE && 125 this.entries_[0].params && 126 this.entries_[0].params.source_dependency != undefined) { 127 var parentId = this.entries_[0].params.source_dependency.id; 128 var parent = SourceTracker.getInstance().getSourceEntry(parentId); 129 if (parent && 130 parent.getSourceType() == 131 EventSourceType.HOST_RESOLVER_IMPL_JOB && 132 parent.getDescription().length > 0) { 133 this.description_ += ' [' + parent.getDescription() + ']'; 134 } 135 } 136 } 137 break; 138 case EventSourceType.ASYNC_HOST_RESOLVER_REQUEST: 139 case EventSourceType.DNS_TRANSACTION: 140 this.description_ = e.params.hostname; 141 break; 142 case EventSourceType.DOWNLOAD: 143 switch (e.type) { 144 case EventType.DOWNLOAD_FILE_RENAMED: 145 this.description_ = e.params.new_filename; 146 break; 147 case EventType.DOWNLOAD_FILE_OPENED: 148 this.description_ = e.params.file_name; 149 break; 150 case EventType.DOWNLOAD_ITEM_ACTIVE: 151 this.description_ = e.params.file_name; 152 break; 153 } 154 break; 155 case EventSourceType.FILESTREAM: 156 this.description_ = e.params.file_name; 157 break; 158 case EventSourceType.IPV6_PROBE_JOB: 159 if (e.type == EventType.IPV6_PROBE_RUNNING && 160 e.phase == EventPhase.PHASE_END) { 161 this.description_ = e.params.ipv6_supported ? 'IPv6 Supported' : 162 'IPv6 Not Supported'; 163 } 164 break; 165 } 166 167 if (this.description_ == undefined) 168 this.description_ = ''; 169 }, 170 171 /** 172 * Returns a description for this source log stream, which will be displayed 173 * in the list view. Most often this is a URL that identifies the request, 174 * or a hostname for a connect job, etc... 175 */ 176 getDescription: function() { 177 return this.description_; 178 }, 179 180 /** 181 * Returns the starting entry for this source. Conceptually this is the 182 * first entry that was logged to this source. However, we skip over the 183 * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB / 184 * TYPE_SOCKET_STREAM_CONNECT. 185 */ 186 getStartEntry_: function() { 187 if (this.entries_.length < 1) 188 return undefined; 189 if (this.entries_[0].source.type == EventSourceType.FILESTREAM) { 190 var e = this.findLogEntryByType_(EventType.FILE_STREAM_OPEN); 191 if (e != undefined) 192 return e; 193 } 194 if (this.entries_[0].source.type == EventSourceType.DOWNLOAD) { 195 // If any rename occurred, use the last name 196 e = this.findLastLogEntryStartByType_( 197 EventType.DOWNLOAD_FILE_RENAMED); 198 if (e != undefined) 199 return e; 200 // Otherwise, if the file was opened, use that name 201 e = this.findLogEntryByType_(EventType.DOWNLOAD_FILE_OPENED); 202 if (e != undefined) 203 return e; 204 // History items are never opened, so use the activation info 205 e = this.findLogEntryByType_(EventType.DOWNLOAD_ITEM_ACTIVE); 206 if (e != undefined) 207 return e; 208 } 209 if (this.entries_.length >= 2) { 210 // Needed for compatability with log dumps prior to M26. 211 // TODO(mmenke): Remove this. 212 if (this.entries_[0].type == EventType.SOCKET_POOL_CONNECT_JOB && 213 this.entries_[0].params == undefined) { 214 return this.entries_[1]; 215 } 216 if (this.entries_[1].type == EventType.UDP_CONNECT) 217 return this.entries_[1]; 218 if (this.entries_[0].type == EventType.REQUEST_ALIVE && 219 this.entries_[0].params == undefined) { 220 var startIndex = 1; 221 // Skip over delegate events for URL_REQUESTs. 222 for (; startIndex + 1 < this.entries_.length; ++startIndex) { 223 var type = this.entries_[startIndex].type; 224 if (type != EventType.URL_REQUEST_DELEGATE && 225 type != EventType.DELEGATE_INFO) { 226 break; 227 } 228 } 229 return this.entries_[startIndex]; 230 } 231 if (this.entries_[1].type == EventType.IPV6_PROBE_RUNNING) 232 return this.entries_[1]; 233 } 234 return this.entries_[0]; 235 }, 236 237 /** 238 * Returns the first entry with the specified type, or undefined if not 239 * found. 240 */ 241 findLogEntryByType_: function(type) { 242 for (var i = 0; i < this.entries_.length; ++i) { 243 if (this.entries_[i].type == type) { 244 return this.entries_[i]; 245 } 246 } 247 return undefined; 248 }, 249 250 /** 251 * Returns the beginning of the last entry with the specified type, or 252 * undefined if not found. 253 */ 254 findLastLogEntryStartByType_: function(type) { 255 for (var i = this.entries_.length - 1; i >= 0; --i) { 256 if (this.entries_[i].type == type) { 257 if (this.entries_[i].phase != EventPhase.PHASE_END) 258 return this.entries_[i]; 259 } 260 } 261 return undefined; 262 }, 263 264 getLogEntries: function() { 265 return this.entries_; 266 }, 267 268 getSourceTypeString: function() { 269 return EventSourceTypeNames[this.entries_[0].source.type]; 270 }, 271 272 getSourceType: function() { 273 return this.entries_[0].source.type; 274 }, 275 276 getSourceId: function() { 277 return this.entries_[0].source.id; 278 }, 279 280 /** 281 * Returns the largest source ID seen before this object was received. 282 * Used only for sorting SourceEntries without a source by source ID. 283 */ 284 getMaxPreviousEntrySourceId: function() { 285 return this.maxPreviousSourceId_; 286 }, 287 288 isInactive: function() { 289 return this.isInactive_; 290 }, 291 292 isError: function() { 293 return this.isError_; 294 }, 295 296 /** 297 * Returns time ticks of first event. 298 */ 299 getStartTicks: function() { 300 return this.entries_[0].time; 301 }, 302 303 /** 304 * Returns time of last event if inactive. Returns current time otherwise. 305 * Returned time is a "time ticks" value. 306 */ 307 getEndTicks: function() { 308 if (!this.isInactive_) 309 return timeutil.getCurrentTimeTicks(); 310 return this.entries_[this.entries_.length - 1].time; 311 }, 312 313 /** 314 * Returns the time between the first and last events with a matching 315 * source ID. If source is still active, uses the current time for the 316 * last event. 317 */ 318 getDuration: function() { 319 var startTime = this.getStartTicks(); 320 var endTime = this.getEndTicks(); 321 return endTime - startTime; 322 }, 323 324 /** 325 * Prints descriptive text about |entries_| to a new node added to the end 326 * of |parent|. 327 */ 328 printAsText: function(parent) { 329 var tablePrinter = this.createTablePrinter(); 330 331 // Format the table for fixed-width text. 332 tablePrinter.toText(0, parent); 333 }, 334 335 /** 336 * Creates a table printer for the SourceEntry. 337 */ 338 createTablePrinter: function() { 339 return createLogEntryTablePrinter( 340 this.entries_, 341 SourceTracker.getInstance().getPrivacyStripping(), 342 SourceTracker.getInstance().getUseRelativeTimes() ? 343 timeutil.getBaseTime() : 0, 344 Constants.clientInfo.numericDate); 345 }, 346 }; 347 348 return SourceEntry; 349})(); 350