| /* |
| * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. |
| * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org> |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @unrestricted |
| */ |
| Network.NetworkTimeBoundary = class { |
| /** |
| * @param {number} minimum |
| * @param {number} maximum |
| */ |
| constructor(minimum, maximum) { |
| this.minimum = minimum; |
| this.maximum = maximum; |
| } |
| |
| /** |
| * @param {!Network.NetworkTimeBoundary} other |
| * @return {boolean} |
| */ |
| equals(other) { |
| return (this.minimum === other.minimum) && (this.maximum === other.maximum); |
| } |
| }; |
| |
| /** |
| * @implements {PerfUI.TimelineGrid.Calculator} |
| * @unrestricted |
| */ |
| Network.NetworkTimeCalculator = class extends Common.Object { |
| constructor(startAtZero) { |
| super(); |
| this.startAtZero = startAtZero; |
| this._minimumBoundary = -1; |
| this._maximumBoundary = -1; |
| this._boundryChangedEventThrottler = new Common.Throttler(0); |
| /** @type {?Network.NetworkTimeBoundary} */ |
| this._window = null; |
| } |
| |
| /** |
| * @param {?Network.NetworkTimeBoundary} window |
| */ |
| setWindow(window) { |
| this._window = window; |
| this._boundaryChanged(); |
| } |
| |
| setInitialUserFriendlyBoundaries() { |
| this._minimumBoundary = 0; |
| this._maximumBoundary = 1; |
| } |
| |
| /** |
| * @override |
| * @param {number} time |
| * @return {number} |
| */ |
| computePosition(time) { |
| return (time - this.minimumBoundary()) / this.boundarySpan() * this._workingArea; |
| } |
| |
| /** |
| * @override |
| * @param {number} value |
| * @param {number=} precision |
| * @return {string} |
| */ |
| formatValue(value, precision) { |
| return Number.secondsToString(value, !!precision); |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| minimumBoundary() { |
| return this._window ? this._window.minimum : this._minimumBoundary; |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| zeroTime() { |
| return this._minimumBoundary; |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| maximumBoundary() { |
| return this._window ? this._window.maximum : this._maximumBoundary; |
| } |
| |
| /** |
| * @return {!Network.NetworkTimeBoundary} |
| */ |
| boundary() { |
| return new Network.NetworkTimeBoundary(this.minimumBoundary(), this.maximumBoundary()); |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| boundarySpan() { |
| return this.maximumBoundary() - this.minimumBoundary(); |
| } |
| |
| reset() { |
| this._minimumBoundary = -1; |
| this._maximumBoundary = -1; |
| this._boundaryChanged(); |
| } |
| |
| /** |
| * @return {number} |
| */ |
| _value(item) { |
| return 0; |
| } |
| |
| /** |
| * @param {number} clientWidth |
| */ |
| setDisplayWidth(clientWidth) { |
| this._workingArea = clientWidth; |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} request |
| * @return {!{start: number, middle: number, end: number}} |
| */ |
| computeBarGraphPercentages(request) { |
| let start; |
| let middle; |
| let end; |
| if (request.startTime !== -1) { |
| start = ((request.startTime - this.minimumBoundary()) / this.boundarySpan()) * 100; |
| } else { |
| start = 0; |
| } |
| |
| if (request.responseReceivedTime !== -1) { |
| middle = ((request.responseReceivedTime - this.minimumBoundary()) / this.boundarySpan()) * 100; |
| } else { |
| middle = (this.startAtZero ? start : 100); |
| } |
| |
| if (request.endTime !== -1) { |
| end = ((request.endTime - this.minimumBoundary()) / this.boundarySpan()) * 100; |
| } else { |
| end = (this.startAtZero ? middle : 100); |
| } |
| |
| if (this.startAtZero) { |
| end -= start; |
| middle -= start; |
| start = 0; |
| } |
| |
| return {start: start, middle: middle, end: end}; |
| } |
| |
| /** |
| * @param {number} eventTime |
| * @return {number} |
| */ |
| computePercentageFromEventTime(eventTime) { |
| // This function computes a percentage in terms of the total loading time |
| // of a specific event. If startAtZero is set, then this is useless, and we |
| // want to return 0. |
| if (eventTime !== -1 && !this.startAtZero) { |
| return ((eventTime - this.minimumBoundary()) / this.boundarySpan()) * 100; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @param {number} percentage |
| * @return {number} |
| */ |
| percentageToTime(percentage) { |
| return percentage * this.boundarySpan() / 100 + this.minimumBoundary(); |
| } |
| |
| _boundaryChanged() { |
| this._boundryChangedEventThrottler.schedule(dispatchEvent.bind(this)); |
| |
| /** |
| * @return {!Promise.<undefined>} |
| * @this {Network.NetworkTimeCalculator} |
| */ |
| function dispatchEvent() { |
| this.dispatchEventToListeners(Network.NetworkTimeCalculator.Events.BoundariesChanged); |
| return Promise.resolve(); |
| } |
| } |
| |
| /** |
| * @param {number} eventTime |
| */ |
| updateBoundariesForEventTime(eventTime) { |
| if (eventTime === -1 || this.startAtZero) { |
| return; |
| } |
| |
| if (this._maximumBoundary === undefined || eventTime > this._maximumBoundary) { |
| this._maximumBoundary = eventTime; |
| this._boundaryChanged(); |
| } |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} request |
| * @return {!{left: string, right: string, tooltip: (string|undefined)}} |
| */ |
| computeBarGraphLabels(request) { |
| let rightLabel = ''; |
| if (request.responseReceivedTime !== -1 && request.endTime !== -1) { |
| rightLabel = Number.secondsToString(request.endTime - request.responseReceivedTime); |
| } |
| |
| const hasLatency = request.latency > 0; |
| const leftLabel = hasLatency ? Number.secondsToString(request.latency) : rightLabel; |
| |
| if (request.timing) { |
| return {left: leftLabel, right: rightLabel}; |
| } |
| |
| let tooltip; |
| if (hasLatency && rightLabel) { |
| const total = Number.secondsToString(request.duration); |
| tooltip = Network.NetworkTimeCalculator._latencyDownloadTotalFormat.format(leftLabel, rightLabel, total); |
| } else if (hasLatency) { |
| tooltip = Network.NetworkTimeCalculator._latencyFormat.format(leftLabel); |
| } else if (rightLabel) { |
| tooltip = Network.NetworkTimeCalculator._downloadFormat.format(rightLabel); |
| } |
| |
| if (request.fetchedViaServiceWorker) { |
| tooltip = Network.NetworkTimeCalculator._fromServiceWorkerFormat.format(tooltip); |
| } else if (request.cached()) { |
| tooltip = Network.NetworkTimeCalculator._fromCacheFormat.format(tooltip); |
| } |
| return {left: leftLabel, right: rightLabel, tooltip: tooltip}; |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} request |
| */ |
| updateBoundaries(request) { |
| const lowerBound = this._lowerBound(request); |
| const upperBound = this._upperBound(request); |
| let changed = false; |
| if (lowerBound !== -1 || this.startAtZero) { |
| changed = this._extendBoundariesToIncludeTimestamp(this.startAtZero ? 0 : lowerBound); |
| } |
| if (upperBound !== -1) { |
| changed = this._extendBoundariesToIncludeTimestamp(upperBound) || changed; |
| } |
| if (changed) { |
| this._boundaryChanged(); |
| } |
| } |
| |
| /** |
| * @param {number} timestamp |
| * @return {boolean} |
| */ |
| _extendBoundariesToIncludeTimestamp(timestamp) { |
| const previousMinimumBoundary = this._minimumBoundary; |
| const previousMaximumBoundary = this._maximumBoundary; |
| const minOffset = Network.NetworkTimeCalculator._minimumSpread; |
| if (this._minimumBoundary === -1 || this._maximumBoundary === -1) { |
| this._minimumBoundary = timestamp; |
| this._maximumBoundary = timestamp + minOffset; |
| } else { |
| this._minimumBoundary = Math.min(timestamp, this._minimumBoundary); |
| this._maximumBoundary = Math.max(timestamp, this._minimumBoundary + minOffset, this._maximumBoundary); |
| } |
| return previousMinimumBoundary !== this._minimumBoundary || previousMaximumBoundary !== this._maximumBoundary; |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} request |
| * @return {number} |
| */ |
| _lowerBound(request) { |
| return 0; |
| } |
| |
| /** |
| * @param {!SDK.NetworkRequest} request |
| * @return {number} |
| */ |
| _upperBound(request) { |
| return 0; |
| } |
| }; |
| |
| Network.NetworkTimeCalculator._minimumSpread = 0.1; |
| |
| /** @enum {symbol} */ |
| Network.NetworkTimeCalculator.Events = { |
| BoundariesChanged: Symbol('BoundariesChanged') |
| }; |
| |
| /** @type {!Common.UIStringFormat} */ |
| Network.NetworkTimeCalculator._latencyDownloadTotalFormat = |
| new Common.UIStringFormat('%s latency, %s download (%s total)'); |
| |
| /** @type {!Common.UIStringFormat} */ |
| Network.NetworkTimeCalculator._latencyFormat = new Common.UIStringFormat('%s latency'); |
| |
| /** @type {!Common.UIStringFormat} */ |
| Network.NetworkTimeCalculator._downloadFormat = new Common.UIStringFormat('%s download'); |
| |
| /** @type {!Common.UIStringFormat} */ |
| Network.NetworkTimeCalculator._fromServiceWorkerFormat = new Common.UIStringFormat('%s (from ServiceWorker)'); |
| |
| /** @type {!Common.UIStringFormat} */ |
| Network.NetworkTimeCalculator._fromCacheFormat = new Common.UIStringFormat('%s (from cache)'); |
| |
| /** |
| * @unrestricted |
| */ |
| Network.NetworkTransferTimeCalculator = class extends Network.NetworkTimeCalculator { |
| constructor() { |
| super(false); |
| } |
| |
| /** |
| * @override |
| * @param {number} value |
| * @param {number=} precision |
| * @return {string} |
| */ |
| formatValue(value, precision) { |
| return Number.secondsToString(value - this.zeroTime(), !!precision); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.NetworkRequest} request |
| * @return {number} |
| */ |
| _lowerBound(request) { |
| return request.issueTime(); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.NetworkRequest} request |
| * @return {number} |
| */ |
| _upperBound(request) { |
| return request.endTime; |
| } |
| }; |
| |
| /** |
| * @unrestricted |
| */ |
| Network.NetworkTransferDurationCalculator = class extends Network.NetworkTimeCalculator { |
| constructor() { |
| super(true); |
| } |
| |
| /** |
| * @override |
| * @param {number} value |
| * @param {number=} precision |
| * @return {string} |
| */ |
| formatValue(value, precision) { |
| return Number.secondsToString(value, !!precision); |
| } |
| |
| /** |
| * @override |
| * @param {!SDK.NetworkRequest} request |
| * @return {number} |
| */ |
| _upperBound(request) { |
| return request.duration; |
| } |
| }; |