text: /*
   * 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.
   */
  
  /**
   * @constructor
   * @extends {WebInspector.SortableDataGridNode}
   * @param {!WebInspector.NetworkLogView} parentView
   * @param {!WebInspector.NetworkRequest} request
   */
  WebInspector.NetworkDataGridNode = function(parentView, request)
  {
      WebInspector.SortableDataGridNode.call(this, {});
      this._parentView = parentView;
      this._request = request;
      this._staleGraph = true;
      this._isNavigationRequest = false;
      this.selectable = true;
  }
  
  WebInspector.NetworkDataGridNode._hoveredRowSymbol = Symbol("hoveredRow");
  
  WebInspector.NetworkDataGridNode.prototype = {
      /**
       * @return {!WebInspector.NetworkRequest}
       */
      request: function()
      {
          return this._request;
      },
  
      markAsNavigationRequest: function()
      {
          this._isNavigationRequest = true;
          this.refresh();
      },
  
      /**
       * @override
       * @return {number}
       */
      nodeSelfHeight: function()
      {
          return this._parentView.rowHeight();
      },
  
      /**
       * @override
       */
      createCells: function()
      {
          this._showTiming = !WebInspector.moduleSetting("networkColorCodeResourceTypes").get() && !this._parentView.calculator().startAtZero;
          this._nameCell = null;
          this._timelineCell = null;
          this._initiatorCell = null;
  
          this._element.classList.toggle("network-error-row", this._isFailed());
          this._element.classList.toggle("network-navigation-row", this._isNavigationRequest);
          WebInspector.SortableDataGridNode.prototype.createCells.call(this);
  
          this._updateGraph();
      },
  
      /**
       * @override
       * @param {string} columnIdentifier
       * @return {!Element}
       */
      createCell: function(columnIdentifier)
      {
          var cell = this.createTD(columnIdentifier);
          switch (columnIdentifier) {
          case "name": this._renderNameCell(cell); break;
          case "timeline": this._createTimelineBar(cell); break;
          case "method": cell.setTextAndTitle(this._request.requestMethod); break;
          case "status": this._renderStatusCell(cell); break;
          case "protocol": cell.setTextAndTitle(this._request.protocol); break;
          case "scheme": cell.setTextAndTitle(this._request.scheme); break;
          case "domain": cell.setTextAndTitle(this._request.domain); break;
          case "remoteAddress": cell.setTextAndTitle(this._request.remoteAddress()); break;
          case "cookies": cell.setTextAndTitle(this._arrayLength(this._request.requestCookies)); break;
          case "setCookies": cell.setTextAndTitle(this._arrayLength(this._request.responseCookies)); break;
          case "connectionId": cell.setTextAndTitle(this._request.connectionId); break;
          case "type": this._renderTypeCell(cell); break;
          case "initiator": this._renderInitiatorCell(cell); break;
          case "size": this._renderSizeCell(cell); break;
          case "time": this._renderTimeCell(cell); break;
          default: cell.setTextAndTitle(this._request.responseHeaderValue(columnIdentifier) || ""); break;
          }
  
          return cell;
      },
  
      /**
       * @param {?Array} array
       * @return {string}
       */
      _arrayLength: function(array)
      {
          return array ? "" + array.length : "";
      },
  
      /**
       * @override
       * @protected
       */
      willAttach: function()
      {
          if (this._staleGraph)
              this._updateGraph();
          if (this._initiatorCell && this._request.initiatorInfo().type === WebInspector.NetworkRequest.InitiatorType.Script)
              this._initiatorCell.insertBefore(this._linkifiedInitiatorAnchor, this._initiatorCell.firstChild);
      },
  
      wasDetached: function()
      {
          if (this._linkifiedInitiatorAnchor)
              this._linkifiedInitiatorAnchor.remove();
      },
  
      dispose: function()
      {
          if (this._linkifiedInitiatorAnchor)
              this._parentView.linkifier.disposeAnchor(this._request.target(), this._linkifiedInitiatorAnchor);
      },
  
      select: function()
      {
          WebInspector.SortableDataGridNode.prototype.select.apply(this, arguments);
          this._parentView.dispatchEventToListeners(WebInspector.NetworkLogView.EventTypes.RequestSelected, this._request);
      },
  
      /**
       * @param {!RegExp=} regexp
       * @return {!Array.<!Object>}
       */
      highlightMatchedSubstring: function(regexp)
      {
          // Ensure element is created.
          this.element();
          var domChanges = [];
          var matchInfo = this._nameCell.textContent.match(regexp);
          if (matchInfo)
              WebInspector.highlightSearchResult(this._nameCell, matchInfo.index, matchInfo[0].length, domChanges);
          return domChanges;
      },
  
      _openInNewTab: function()
      {
          InspectorFrontendHost.openInNewTab(this._request.url);
      },
  
      /**
       * @param {!Element} cell
       */
      _createTimelineBar: function(cell)
      {
          cell = cell.createChild("div");
          this._timelineCell = cell;
  
          cell.className = "network-graph-side";
  
          this._barAreaElement = cell.createChild("div", "network-graph-bar-area");
          this._barAreaElement.request = this._request;
  
          if (this._showTiming)
              return;
  
          var type = this._request.resourceType().name();
          var cached = this._request.cached();
  
          this._barLeftElement = this._barAreaElement.createChild("div", "network-graph-bar");
          this._barLeftElement.classList.add(type, "waiting");
          this._barLeftElement.classList.toggle("cached", cached);
  
          this._barRightElement = this._barAreaElement.createChild("div", "network-graph-bar");
          this._barRightElement.classList.add(type);
          this._barRightElement.classList.toggle("cached", cached);
  
          this._labelLeftElement = this._barAreaElement.createChild("div", "network-graph-label");
          this._labelLeftElement.classList.add("waiting");
  
          this._labelRightElement = this._barAreaElement.createChild("div", "network-graph-label");
  
          cell.addEventListener("mouseover", this._onMouseOver.bind(this), false);
      },
  
      /**
       * @param {!Event} event
       */
      _onMouseOver: function(event)
      {
          this._refreshLabelPositions();
          this._parentView[WebInspector.NetworkDataGridNode._hoveredRowSymbol] = this;
      },
  
      /**
       * @return {boolean}
       */
      _isFailed: function()
      {
          return (this._request.failed && !this._request.statusCode) || (this._request.statusCode >= 400);
      },
  
      /**
       * @param {!Element} cell
       */
      _renderNameCell: function(cell)
      {
          this._nameCell = cell;
          cell.addEventListener("dblclick", this._openInNewTab.bind(this), false);
          var iconElement;
          if (this._request.resourceType() === WebInspector.resourceTypes.Image) {
              var previewImage = createElementWithClass("img", "image-network-icon-preview");
              this._request.populateImageSource(previewImage);
  
              iconElement = createElementWithClass("div", "icon");
              iconElement.appendChild(previewImage);
          } else {
              iconElement = createElementWithClass("img", "icon");
          }
          iconElement.classList.add(this._request.resourceType().name());
  
          cell.appendChild(iconElement);
          cell.createTextChild(this._request.target().decorateLabel(this._request.name()));
          this._appendSubtitle(cell, this._request.path());
          cell.title = this._request.url;
      },
  
      /**
       * @param {!Element} cell
       */
      _renderStatusCell: function(cell)
      {
          cell.classList.toggle("network-dim-cell", !this._isFailed() && (this._request.cached() || !this._request.statusCode));
  
          if (this._request.failed && !this._request.canceled) {
              var failText = WebInspector.UIString("(failed)");
              if (this._request.localizedFailDescription) {
                  cell.createTextChild(failText);
                  this._appendSubtitle(cell, this._request.localizedFailDescription);
                  cell.title = failText + " " + this._request.localizedFailDescription;
              } else
                  cell.setTextAndTitle(failText);
          } else if (this._request.statusCode) {
              cell.createTextChild("" + this._request.statusCode);
              this._appendSubtitle(cell, this._request.statusText);
              cell.title = this._request.statusCode + " " + this._request.statusText;
          } else if (this._request.parsedURL.isDataURL()) {
              cell.setTextAndTitle(WebInspector.UIString("(data)"));
          } else if (this._request.canceled) {
              cell.setTextAndTitle(WebInspector.UIString("(canceled)"));
          } else if (this._request.finished) {
              cell.setTextAndTitle(WebInspector.UIString("Finished"));
          } else {
              cell.setTextAndTitle(WebInspector.UIString("(pending)"));
          }
      },
  
      /**
       * @param {!Element} cell
       */
      _renderTypeCell: function(cell)
      {
          var mimeType = this._request.mimeType || this._request.requestContentType() || "";
          var resourceType = this._request.resourceType();
          var simpleType = resourceType.name();
  
          if (resourceType == WebInspector.resourceTypes.Other
              || resourceType == WebInspector.resourceTypes.Image)
              simpleType = mimeType.replace(/^(application|image)\//, "");
  
          cell.setTextAndTitle(simpleType);
      },
  
      /**
       * @param {!Element} cell
       */
      _renderInitiatorCell: function(cell)
      {
          this._initiatorCell = cell;
          var request = this._request;
          var initiator = request.initiatorInfo();
  
          switch (initiator.type) {
          case WebInspector.NetworkRequest.InitiatorType.Parser:
              cell.title = initiator.url + ":" + initiator.lineNumber;
              var uiSourceCode = WebInspector.networkMapping.uiSourceCodeForURLForAnyTarget(initiator.url);
              cell.appendChild(WebInspector.linkifyResourceAsNode(initiator.url, initiator.lineNumber - 1, undefined, undefined, uiSourceCode ? uiSourceCode.displayName() : undefined));
              this._appendSubtitle(cell, WebInspector.UIString("Parser"));
              break;
  
          case WebInspector.NetworkRequest.InitiatorType.Redirect:
              cell.title = initiator.url;
              console.assert(request.redirectSource);
              var redirectSource = /** @type {!WebInspector.NetworkRequest} */ (request.redirectSource);
              cell.appendChild(WebInspector.linkifyRequestAsNode(redirectSource));
              this._appendSubtitle(cell, WebInspector.UIString("Redirect"));
              break;
  
          case WebInspector.NetworkRequest.InitiatorType.Script:
              if (!this._linkifiedInitiatorAnchor) {
                  this._linkifiedInitiatorAnchor = this._parentView.linkifier.linkifyScriptLocation(request.target(), null, initiator.url, initiator.lineNumber - 1, initiator.columnNumber - 1);
                  this._linkifiedInitiatorAnchor.title = "";
              }
              cell.appendChild(this._linkifiedInitiatorAnchor);
              this._appendSubtitle(cell, WebInspector.UIString("Script"));
              cell.classList.add("network-script-initiated");
              cell.request = request;
              break;
  
          default:
              cell.title = "";
              cell.classList.add("network-dim-cell");
              cell.setTextAndTitle(WebInspector.UIString("Other"));
          }
      },
  
      /**
       * @param {!Element} cell
       */
      _renderSizeCell: function(cell)
      {
          if (this._request.fetchedViaServiceWorker) {
              cell.setTextAndTitle(WebInspector.UIString("(from ServiceWorker)"));
              cell.classList.add("network-dim-cell");
          } else if (this._request.cached()) {
              cell.setTextAndTitle(WebInspector.UIString("(from cache)"));
              cell.classList.add("network-dim-cell");
          } else {
              var resourceSize = Number.bytesToString(this._request.resourceSize);
              var transferSize = Number.bytesToString(this._request.transferSize);
              cell.setTextAndTitle(transferSize);
              this._appendSubtitle(cell, resourceSize);
          }
      },
  
      /**
       * @param {!Element} cell
       */
      _renderTimeCell: function(cell)
      {
          if (this._request.duration > 0) {
              cell.setTextAndTitle(Number.secondsToString(this._request.duration));
              this._appendSubtitle(cell, Number.secondsToString(this._request.latency));
          } else {
              cell.classList.add("network-dim-cell");
              cell.setTextAndTitle(WebInspector.UIString("Pending"));
          }
      },
  
      /**
       * @param {!Element} cellElement
       * @param {string} subtitleText
       */
      _appendSubtitle: function(cellElement, subtitleText)
      {
          var subtitleElement = createElement("div");
          subtitleElement.className = "network-cell-subtitle";
          subtitleElement.textContent = subtitleText;
          cellElement.appendChild(subtitleElement);
      },
  
      refreshGraph: function()
      {
          if (!this._timelineCell)
              return;
          this._staleGraph = true;
          if (this.attached())
              this.dataGrid.scheduleUpdate();
      },
  
      _updateTimingGraph: function()
      {
          var calculator = this._parentView.calculator();
          var timeRanges = WebInspector.RequestTimingView.calculateRequestTimeRanges(this._request);
          var right = timeRanges[0].end;
  
          var container = this._barAreaElement;
          var nextBar = container.firstChild;
          for (var i = 0; i < timeRanges.length; ++i) {
              var range = timeRanges[i];
              var start = calculator.computePercentageFromEventTime(range.start);
              var end = (range.end !== Number.MAX_VALUE) ? calculator.computePercentageFromEventTime(range.end) : 100;
              if (!nextBar)
                  nextBar = container.createChild("div");
              nextBar.className = "network-graph-bar request-timing";
              nextBar.classList.add(range.name);
              nextBar.style.setProperty("left", start + "%");
              nextBar.style.setProperty("right", (100 - end) + "%");
              nextBar = nextBar.nextSibling;
          }
          while (nextBar) {
              var nextSibling = nextBar.nextSibling;
              nextBar.remove();
              nextBar = nextSibling;
          }
      },
  
      _updateGraph: function()
      {
          this._staleGraph = false;
          if (!this._timelineCell)
              return;
          if (this._showTiming) {
              this._updateTimingGraph();
              return;
          }
  
          var calculator = this._parentView.calculator();
          var percentages = calculator.computeBarGraphPercentages(this._request);
          this._percentages = percentages;
  
          this._barAreaElement.classList.remove("hidden");
  
          this._barLeftElement.style.setProperty("left", percentages.start + "%");
          this._barLeftElement.style.setProperty("right", (100 - percentages.middle) + "%");
  
          this._barRightElement.style.setProperty("left", percentages.middle + "%");
          this._barRightElement.style.setProperty("right", (100 - percentages.end) + "%");
  
          var labels = calculator.computeBarGraphLabels(this._request);
          this._labelLeftElement.textContent = labels.left;
          this._labelRightElement.textContent = labels.right;
  
          var tooltip = (labels.tooltip || "");
          this._barLeftElement.title = tooltip;
          this._labelLeftElement.title = tooltip;
          this._labelRightElement.title = tooltip;
          this._barRightElement.title = tooltip;
  
          if (this._parentView[WebInspector.NetworkDataGridNode._hoveredRowSymbol] === this)
              this._refreshLabelPositions();
      },
  
      _refreshLabelPositions: function()
      {
          if (!this._percentages)
              return;
          this._labelLeftElement.style.removeProperty("left");
          this._labelLeftElement.style.removeProperty("right");
          this._labelLeftElement.classList.remove("before");
          this._labelLeftElement.classList.remove("hidden");
  
          this._labelRightElement.style.removeProperty("left");
          this._labelRightElement.style.removeProperty("right");
          this._labelRightElement.classList.remove("after");
          this._labelRightElement.classList.remove("hidden");
  
          const labelPadding = 10;
          const barRightElementOffsetWidth = this._barRightElement.offsetWidth;
          const barLeftElementOffsetWidth = this._barLeftElement.offsetWidth;
  
          if (this._barLeftElement) {
              var leftBarWidth = barLeftElementOffsetWidth - labelPadding;
              var rightBarWidth = (barRightElementOffsetWidth - barLeftElementOffsetWidth) - labelPadding;
          } else {
              var leftBarWidth = (barLeftElementOffsetWidth - barRightElementOffsetWidth) - labelPadding;
              var rightBarWidth = barRightElementOffsetWidth - labelPadding;
          }
  
          const labelLeftElementOffsetWidth = this._labelLeftElement.offsetWidth;
          const labelRightElementOffsetWidth = this._labelRightElement.offsetWidth;
  
          const labelBefore = (labelLeftElementOffsetWidth > leftBarWidth);
          const labelAfter = (labelRightElementOffsetWidth > rightBarWidth);
          const graphElementOffsetWidth = this._timelineCell.offsetWidth;
  
          if (labelBefore && (graphElementOffsetWidth * (this._percentages.start / 100)) < (labelLeftElementOffsetWidth + 10))
              var leftHidden = true;
  
          if (labelAfter && (graphElementOffsetWidth * ((100 - this._percentages.end) / 100)) < (labelRightElementOffsetWidth + 10))
              var rightHidden = true;
  
          if (barLeftElementOffsetWidth == barRightElementOffsetWidth) {
              // The left/right label data are the same, so a before/after label can be replaced by an on-bar label.
              if (labelBefore && !labelAfter)
                  leftHidden = true;
              else if (labelAfter && !labelBefore)
                  rightHidden = true;
          }
  
          if (labelBefore) {
              if (leftHidden)
                  this._labelLeftElement.classList.add("hidden");
              this._labelLeftElement.style.setProperty("right", (100 - this._percentages.start) + "%");
              this._labelLeftElement.classList.add("before");
          } else {
              this._labelLeftElement.style.setProperty("left", this._percentages.start + "%");
              this._labelLeftElement.style.setProperty("right", (100 - this._percentages.middle) + "%");
          }
  
          if (labelAfter) {
              if (rightHidden)
                  this._labelRightElement.classList.add("hidden");
              this._labelRightElement.style.setProperty("left", this._percentages.end + "%");
              this._labelRightElement.classList.add("after");
          } else {
              this._labelRightElement.style.setProperty("left", this._percentages.middle + "%");
              this._labelRightElement.style.setProperty("right", (100 - this._percentages.end) + "%");
          }
      },
  
      __proto__: WebInspector.SortableDataGridNode.prototype
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.NameComparator = function(a, b)
  {
      var aFileName = a._request.name();
      var bFileName = b._request.name();
      if (aFileName > bFileName)
          return 1;
      if (bFileName > aFileName)
          return -1;
      return a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.RemoteAddressComparator = function(a, b)
  {
      var aRemoteAddress = a._request.remoteAddress();
      var bRemoteAddress = b._request.remoteAddress();
      if (aRemoteAddress > bRemoteAddress)
          return 1;
      if (bRemoteAddress > aRemoteAddress)
          return -1;
      return a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.SizeComparator = function(a, b)
  {
      if (b._request.cached() && !a._request.cached())
          return 1;
      if (a._request.cached() && !b._request.cached())
          return -1;
      return (a._request.transferSize - b._request.transferSize) || a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.InitiatorComparator = function(a, b)
  {
      var aInitiator = a._request.initiatorInfo();
      var bInitiator = b._request.initiatorInfo();
  
      if (aInitiator.type < bInitiator.type)
          return -1;
      if (aInitiator.type > bInitiator.type)
          return 1;
  
      if (typeof aInitiator.__source === "undefined")
          aInitiator.__source = WebInspector.displayNameForURL(aInitiator.url);
      if (typeof bInitiator.__source === "undefined")
          bInitiator.__source = WebInspector.displayNameForURL(bInitiator.url);
  
      if (aInitiator.__source < bInitiator.__source)
          return -1;
      if (aInitiator.__source > bInitiator.__source)
          return 1;
  
      if (aInitiator.lineNumber < bInitiator.lineNumber)
          return -1;
      if (aInitiator.lineNumber > bInitiator.lineNumber)
          return 1;
  
      if (aInitiator.columnNumber < bInitiator.columnNumber)
          return -1;
      if (aInitiator.columnNumber > bInitiator.columnNumber)
          return 1;
  
      return a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.RequestCookiesCountComparator = function(a, b)
  {
      var aScore = a._request.requestCookies ? a._request.requestCookies.length : 0;
      var bScore = b._request.requestCookies ? b._request.requestCookies.length : 0;
      return (aScore - bScore) || a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.ResponseCookiesCountComparator = function(a, b)
  {
      var aScore = a._request.responseCookies ? a._request.responseCookies.length : 0;
      var bScore = b._request.responseCookies ? b._request.responseCookies.length : 0;
      return (aScore - bScore) || a._request.indentityCompare(b._request);
  }
  
  /**
   * @param {string} propertyName
   * @param {boolean} revert
   * @param {!WebInspector.NetworkDataGridNode} a
   * @param {!WebInspector.NetworkDataGridNode} b
   * @return {number}
   */
  WebInspector.NetworkDataGridNode.RequestPropertyComparator = function(propertyName, revert, a, b)
  {
      var aValue = a._request[propertyName];
      var bValue = b._request[propertyName];
      if (aValue > bValue)
          return revert ? -1 : 1;
      if (bValue > aValue)
          return revert ? 1 : -1;
      return a._request.indentityCompare(b._request);
  }
  