// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. var SourceEntry = (function() { 'use strict'; /** * A SourceEntry gathers all log entries with the same source. * * @constructor */ function SourceEntry(logEntry, maxPreviousSourceId) { this.maxPreviousSourceId_ = maxPreviousSourceId; this.entries_ = []; this.description_ = ''; // Set to true on most net errors. this.isError_ = false; // If the first entry is a BEGIN_PHASE, set to false. // Set to true when an END_PHASE matching the first entry is encountered. this.isInactive_ = true; if (logEntry.phase == LogEventPhase.PHASE_BEGIN) this.isInactive_ = false; this.update(logEntry); } SourceEntry.prototype = { update: function(logEntry) { // Only the last event should have the same type first event, if (!this.isInactive_ && logEntry.phase == LogEventPhase.PHASE_END && logEntry.type == this.entries_[0].type) { this.isInactive_ = true; } // If we have a net error code, update |this.isError_| if apporpriate. if (logEntry.params) { var netErrorCode = logEntry.params.net_error; // Skip both cases where netErrorCode is undefined, and cases where it // is 0, indicating no actual error occurred. if (netErrorCode) { // Ignore error code caused by not finding an entry in the cache. if (logEntry.type != LogEventType.HTTP_CACHE_OPEN_ENTRY || netErrorCode != NetError.FAILED) { this.isError_ = true; } } } var prevStartEntry = this.getStartEntry_(); this.entries_.push(logEntry); var curStartEntry = this.getStartEntry_(); // If we just got the first entry for this source. if (prevStartEntry != curStartEntry) this.updateDescription_(); }, updateDescription_: function() { var e = this.getStartEntry_(); this.description_ = ''; if (!e) return; if (e.source.type == LogSourceType.NONE) { // NONE is what we use for global events that aren't actually grouped // by a "source ID", so we will just stringize the event's type. this.description_ = getKeyWithValue(LogEventType, e.type); return; } if (e.params == undefined) { return; } switch (e.source.type) { case LogSourceType.URL_REQUEST: case LogSourceType.SOCKET_STREAM: case LogSourceType.HTTP_STREAM_JOB: this.description_ = e.params.url; break; case LogSourceType.CONNECT_JOB: this.description_ = e.params.group_name; break; case LogSourceType.HOST_RESOLVER_IMPL_REQUEST: case LogSourceType.HOST_RESOLVER_IMPL_JOB: case LogSourceType.HOST_RESOLVER_IMPL_PROC_TASK: this.description_ = e.params.host; break; case LogSourceType.DISK_CACHE_ENTRY: case LogSourceType.MEMORY_CACHE_ENTRY: this.description_ = e.params.key; break; case LogSourceType.SPDY_SESSION: if (e.params.host) this.description_ = e.params.host + ' (' + e.params.proxy + ')'; break; case LogSourceType.HTTP_PIPELINED_CONNECTION: if (e.params.host_and_port) this.description_ = e.params.host_and_port; break; case LogSourceType.SOCKET: // Use description of parent source, if any. if (e.params.source_dependency != undefined) { var parentId = e.params.source_dependency.id; this.description_ = SourceTracker.getInstance().getDescription(parentId); } break; case LogSourceType.UDP_SOCKET: if (e.params.address != undefined) { this.description_ = e.params.address; // If the parent of |this| is a HOST_RESOLVER_IMPL_JOB, use // ' []'. if (this.entries_[0].type == LogEventType.SOCKET_ALIVE && this.entries_[0].params.source_dependency != undefined) { var parentId = this.entries_[0].params.source_dependency.id; var parent = SourceTracker.getInstance().getSourceEntry(parentId); if (parent && parent.getSourceType() == LogSourceType.HOST_RESOLVER_IMPL_JOB && parent.getDescription().length > 0) { this.description_ += ' [' + parent.getDescription() + ']'; } } } break; case LogSourceType.ASYNC_HOST_RESOLVER_REQUEST: case LogSourceType.DNS_TRANSACTION: this.description_ = e.params.hostname; break; case LogSourceType.DOWNLOAD: switch (e.type) { case LogEventType.DOWNLOAD_FILE_RENAMED: this.description_ = e.params.new_filename; break; case LogEventType.DOWNLOAD_FILE_OPENED: this.description_ = e.params.file_name; break; case LogEventType.DOWNLOAD_ITEM_ACTIVE: this.description_ = e.params.file_name; break; } break; case LogSourceType.FILESTREAM: this.description_ = e.params.file_name; break; } if (this.description_ == undefined) this.description_ = ''; }, /** * Returns a description for this source log stream, which will be displayed * in the list view. Most often this is a URL that identifies the request, * or a hostname for a connect job, etc... */ getDescription: function() { return this.description_; }, /** * Returns the starting entry for this source. Conceptually this is the * first entry that was logged to this source. However, we skip over the * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB / * TYPE_SOCKET_STREAM_CONNECT. */ getStartEntry_: function() { if (this.entries_.length < 1) return undefined; if (this.entries_[0].source.type == LogSourceType.FILESTREAM) { var e = this.findLogEntryByType_(LogEventType.FILE_STREAM_OPEN); if (e != undefined) return e; } if (this.entries_[0].source.type == LogSourceType.DOWNLOAD) { // If any rename occurred, use the last name e = this.findLastLogEntryStartByType_( LogEventType.DOWNLOAD_FILE_RENAMED); if (e != undefined) return e; // Otherwise, if the file was opened, use that name e = this.findLogEntryByType_(LogEventType.DOWNLOAD_FILE_OPENED); if (e != undefined) return e; // History items are never opened, so use the activation info e = this.findLogEntryByType_(LogEventType.DOWNLOAD_ITEM_ACTIVE); if (e != undefined) return e; } if (this.entries_.length >= 2) { if (this.entries_[0].type == LogEventType.REQUEST_ALIVE || this.entries_[0].type == LogEventType.SOCKET_POOL_CONNECT_JOB || this.entries_[1].type == LogEventType.UDP_CONNECT) { return this.entries_[1]; } } return this.entries_[0]; }, /** * Returns the first entry with the specified type, or undefined if not * found. */ findLogEntryByType_: function(type) { for (var i = 0; i < this.entries_.length; ++i) { if (this.entries_[i].type == type) { return this.entries_[i]; } } return undefined; }, /** * Returns the beginning of the last entry with the specified type, or * undefined if not found. */ findLastLogEntryStartByType_: function(type) { for (var i = this.entries_.length - 1; i >= 0; --i) { if (this.entries_[i].type == type) { if (this.entries_[i].phase != LogEventPhase.PHASE_END) return this.entries_[i]; } } return undefined; }, getLogEntries: function() { return this.entries_; }, getSourceTypeString: function() { return getKeyWithValue(LogSourceType, this.entries_[0].source.type); }, getSourceType: function() { return this.entries_[0].source.type; }, getSourceId: function() { return this.entries_[0].source.id; }, /** * Returns the largest source ID seen before this object was received. * Used only for sorting SourceEntries without a source by source ID. */ getMaxPreviousEntrySourceId: function() { return this.maxPreviousSourceId_; }, isInactive: function() { return this.isInactive_; }, isError: function() { return this.isError_; }, /** * Returns time of last event if inactive. Returns current time otherwise. */ getEndTime: function() { if (!this.isInactive_) { return timeutil.getCurrentTime(); } else { var endTicks = this.entries_[this.entries_.length - 1].time; return timeutil.convertTimeTicksToDate(endTicks).getTime(); } }, /** * Returns the time between the first and last events with a matching * source ID. If source is still active, uses the current time for the * last event. */ getDuration: function() { var startTicks = this.entries_[0].time; var startTime = timeutil.convertTimeTicksToDate(startTicks).getTime(); var endTime = this.getEndTime(); return endTime - startTime; }, /** * Prints descriptive text about |entries_| to a new node added to the end * of |parent|. */ printAsText: function(parent) { printLogEntriesAsText(this.entries_, parent); } }; return SourceEntry; })();