Commit a8da91f0 authored by timothy@apple.com's avatar timothy@apple.com

Support collapsing call site records into the resource timeline.

Also fix some filtering and graph issues.

https://bugs.webkit.org/show_bug.cgi?id=127440

Reviewed by Joseph Pecoraro.

* UserInterface/NavigationSidebarPanel.js:
(WebInspector.NavigationSidebarPanel.prototype.updateFilter):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement.matchTextFilter):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement.makeVisible):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement):
(WebInspector.NavigationSidebarPanel.prototype._updateFilter):
Tweak how filtering happens so custom filters never expand to reveal and auto expanded
tree elements will auto-collapse again later even with custom filters.

* UserInterface/OverviewTimelineView.css:
(.timeline-view.overview > .data-grid tr.parent:not(.expanded) td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
(.timeline-view.overview > .data-grid tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
(.timeline-view.overview > .data-grid:focus tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
Add a shadow to provide some negative space between juxtaposed records. Only needed when not expanded and not netwrok records.

* UserInterface/OverviewTimelineView.js:
(WebInspector.OverviewTimelineView.prototype.updateLayout):
(WebInspector.OverviewTimelineView.prototype._addResourceToTreeIfNeeded):
Update the filter when current time changes and only auto expand the main resource.

* UserInterface/ResourceTimelineDataGridNode.js:
(WebInspector.ResourceTimelineDataGridNode):
(WebInspector.ResourceTimelineDataGridNode.prototype._timelineRecordUpdated):
Don't schedule a refresh of the graph if the record isn't visible.

* UserInterface/SourceCodeTimelineTimelineDataGridNode.js:
(WebInspector.SourceCodeTimelineTimelineDataGridNode):
(WebInspector.SourceCodeTimelineTimelineDataGridNode.prototype._timelineRecordAdded):
Don't schedule a refresh of the graph if the record isn't visible.

* UserInterface/TimelineContentView.js:
(WebInspector.TimelineContentView.prototype._timeRangeSelectionChanged):
Remove the boolean for updateFilter.

* UserInterface/TimelineDataGrid.js:
(WebInspector.TimelineDataGrid.prototype._refreshDirtyDataGridNodes):
(WebInspector.TimelineDataGrid.prototype._sort):
Keep the hidden state in-sync between node and element.

* UserInterface/TimelineDataGridNode.js:
(WebInspector.TimelineDataGridNode.prototype.collapse):
(WebInspector.TimelineDataGridNode.prototype.expand):
(WebInspector.TimelineDataGridNode.prototype.appendChild):
(WebInspector.TimelineDataGridNode.prototype.insertChild):
(WebInspector.TimelineDataGridNode.prototype.removeChild):
(WebInspector.TimelineDataGridNode.prototype.removeChildren):
(WebInspector.TimelineDataGridNode.prototype.removeChildrenRecursive):
(WebInspector.TimelineDataGridNode.prototype.refreshGraph.createBarsForRecords):
(WebInspector.TimelineDataGridNode.prototype.refreshGraph.else.collectRecordsByType.get if):
(WebInspector.TimelineDataGridNode.prototype.needsGraphRefresh):
(WebInspector.TimelineDataGridNode.prototype.isRecordVisible):
Support drawing the children records on the parent graph.

* UserInterface/TimelineRecordBar.css:
(.timeline-record-bar.unfinished > .segment):
(:focus .selected .timeline-record-bar > .segment):
(:focus .selected .timeline-record-bar > .segment.inactive):
(:focus .selected .timeline-record-bar.has-inactive-segment > .segment:not(.inactive)):
Tweaked styles to look bettwen when selected.

* UserInterface/TimelineRecordBar.js:
(WebInspector.TimelineRecordBar.recordsCannotBeCombined):
(WebInspector.TimelineRecordBar.prototype.set records):
(WebInspector.TimelineRecordBar.prototype.refresh):
Drive-by fixes for some bug with bars being reused.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@162560 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 588b6e43
2014-01-22 Timothy Hatcher <timothy@apple.com>
Support collapsing call site records into the resource timeline.
Also fix some filtering and graph issues.
https://bugs.webkit.org/show_bug.cgi?id=127440
Reviewed by Joseph Pecoraro.
* UserInterface/NavigationSidebarPanel.js:
(WebInspector.NavigationSidebarPanel.prototype.updateFilter):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement.matchTextFilter):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement.makeVisible):
(WebInspector.NavigationSidebarPanel.prototype.applyFiltersToTreeElement):
(WebInspector.NavigationSidebarPanel.prototype._updateFilter):
Tweak how filtering happens so custom filters never expand to reveal and auto expanded
tree elements will auto-collapse again later even with custom filters.
* UserInterface/OverviewTimelineView.css:
(.timeline-view.overview > .data-grid tr.parent:not(.expanded) td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
(.timeline-view.overview > .data-grid tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
(.timeline-view.overview > .data-grid:focus tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.timeline-record-type-network) > .segment):
Add a shadow to provide some negative space between juxtaposed records. Only needed when not expanded.
* UserInterface/OverviewTimelineView.js:
(WebInspector.OverviewTimelineView.prototype.updateLayout):
(WebInspector.OverviewTimelineView.prototype._addResourceToTreeIfNeeded):
Update the filter when current time changes and only auto expand the main resource.
* UserInterface/ResourceTimelineDataGridNode.js:
(WebInspector.ResourceTimelineDataGridNode):
(WebInspector.ResourceTimelineDataGridNode.prototype._timelineRecordUpdated):
Don't schedule a refresh of the graph if the record isn't visible.
* UserInterface/SourceCodeTimelineTimelineDataGridNode.js:
(WebInspector.SourceCodeTimelineTimelineDataGridNode):
(WebInspector.SourceCodeTimelineTimelineDataGridNode.prototype._timelineRecordAdded):
Don't schedule a refresh of the graph if the record isn't visible.
* UserInterface/TimelineContentView.js:
(WebInspector.TimelineContentView.prototype._timeRangeSelectionChanged):
Remove the boolean for updateFilter.
* UserInterface/TimelineDataGrid.js:
(WebInspector.TimelineDataGrid.prototype._refreshDirtyDataGridNodes):
(WebInspector.TimelineDataGrid.prototype._sort):
Keep the hidden state in-sync between node and element.
* UserInterface/TimelineDataGridNode.js:
(WebInspector.TimelineDataGridNode.prototype.collapse):
(WebInspector.TimelineDataGridNode.prototype.expand):
(WebInspector.TimelineDataGridNode.prototype.appendChild):
(WebInspector.TimelineDataGridNode.prototype.insertChild):
(WebInspector.TimelineDataGridNode.prototype.removeChild):
(WebInspector.TimelineDataGridNode.prototype.removeChildren):
(WebInspector.TimelineDataGridNode.prototype.removeChildrenRecursive):
(WebInspector.TimelineDataGridNode.prototype.refreshGraph.createBarsForRecords):
(WebInspector.TimelineDataGridNode.prototype.refreshGraph.else.collectRecordsByType.get if):
(WebInspector.TimelineDataGridNode.prototype.needsGraphRefresh):
(WebInspector.TimelineDataGridNode.prototype.isRecordVisible):
Support drawing the children records on the parent graph.
* UserInterface/TimelineRecordBar.css:
(.timeline-record-bar.unfinished > .segment):
(:focus .selected .timeline-record-bar > .segment):
(:focus .selected .timeline-record-bar > .segment.inactive):
(:focus .selected .timeline-record-bar.has-inactive-segment > .segment:not(.inactive)):
Tweaked styles to look better when selected.
* UserInterface/TimelineRecordBar.js:
(WebInspector.TimelineRecordBar.recordsCannotBeCombined):
(WebInspector.TimelineRecordBar.prototype.set records):
(WebInspector.TimelineRecordBar.prototype.refresh):
Drive-by fixes for some bug with bars being reused.
2014-01-21 Timothy Hatcher <timothy@apple.com>
Remember the Timeline Overview zoom and selection between sessions.
......
......@@ -292,9 +292,9 @@ WebInspector.NavigationSidebarPanel.prototype = {
// Implemented by subclasses if needed.
},
updateFilter: function(dontExpandOnMatch)
updateFilter: function()
{
this._updateFilter(dontExpandOnMatch);
this._updateFilter();
},
hasCustomFilters: function()
......@@ -309,7 +309,7 @@ WebInspector.NavigationSidebarPanel.prototype = {
return true;
},
applyFiltersToTreeElement: function(treeElement, dontExpandOnMatch)
applyFiltersToTreeElement: function(treeElement)
{
if (!this._filterBar.hasActiveFilters() && !this.hasCustomFilters()) {
// No filters, so make everything visible.
......@@ -326,6 +326,8 @@ WebInspector.NavigationSidebarPanel.prototype = {
var filterableData = treeElement.filterableData || {};
var matchedBuiltInFilters = false;
var self = this;
function matchTextFilter(inputs)
{
......@@ -340,8 +342,10 @@ WebInspector.NavigationSidebarPanel.prototype = {
for (var input of inputs) {
if (!input)
continue;
if (self._textFilterRegex.test(input))
if (self._textFilterRegex.test(input)) {
matchedBuiltInFilters = true;
return true;
}
}
// No inputs matched.
......@@ -358,7 +362,8 @@ WebInspector.NavigationSidebarPanel.prototype = {
while (currentAncestor && !currentAncestor.root) {
currentAncestor.hidden = false;
if (!currentAncestor.expanded && !dontExpandOnMatch) {
// Only expand if the built-in filters matched, not custom filters.
if (matchedBuiltInFilters && !currentAncestor.expanded) {
currentAncestor.__wasExpandedDuringFiltering = true;
currentAncestor.expand();
}
......@@ -370,6 +375,13 @@ WebInspector.NavigationSidebarPanel.prototype = {
if (matchTextFilter(filterableData.text) && this.matchTreeElementAgainstCustomFilters(treeElement)) {
// Make this element visible since it matches.
makeVisible();
// If this tree element didn't match a built-in filter and was expanded earlier during filtering, collapse it again.
if (!matchedBuiltInFilters && treeElement.expanded && treeElement.__wasExpandedDuringFiltering) {
delete treeElement.__wasExpandedDuringFiltering;
treeElement.collapse();
}
return;
}
......@@ -468,7 +480,7 @@ WebInspector.NavigationSidebarPanel.prototype = {
this._updateFilter();
},
_updateFilter: function(dontExpandOnMatch)
_updateFilter: function()
{
var filters = this._filterBar.filters;
this._textFilterRegex = simpleGlobStringToRegExp(filters.text, "i");
......@@ -477,7 +489,7 @@ WebInspector.NavigationSidebarPanel.prototype = {
// Update the whole tree.
var currentTreeElement = this._contentTreeOutline.children[0];
while (currentTreeElement && !currentTreeElement.root) {
this.applyFiltersToTreeElement(currentTreeElement, dontExpandOnMatch);
this.applyFiltersToTreeElement(currentTreeElement);
currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, false);
}
......
......@@ -69,3 +69,27 @@
.timeline-view.overview > .data-grid td.graph-column .timeline-record-bar {
top: 2px;
}
.timeline-view.overview > .data-grid tr.parent:not(.expanded) td.graph-column .timeline-record-bar:not(.has-inactive-segment) > .segment {
box-shadow: white 0 0 0 1px;
}
.timeline-view.overview > .data-grid tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.has-inactive-segment) > .segment {
box-shadow: rgb(212, 212, 212) 0 0 0 1px;
}
.timeline-view.overview > .data-grid:focus tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar:not(.has-inactive-segment) > .segment {
box-shadow: rgb(56, 121, 217) 0 0 0 1px;
}
.timeline-view.overview > .data-grid tr.parent:not(.expanded) td.graph-column .timeline-record-bar.has-inactive-segment > .segment:not(.inactive) {
box-shadow: white 1px 0 0;
}
.timeline-view.overview > .data-grid tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar.has-inactive-segment > .segment:not(.inactive) {
box-shadow: rgb(212, 212, 212) 1px 0 0;
}
.timeline-view.overview > .data-grid:focus tr.parent:not(.expanded).selected td.graph-column .timeline-record-bar.has-inactive-segment > .segment:not(.inactive) {
box-shadow: rgb(56, 121, 217) 1px 0 0;
}
......@@ -93,13 +93,18 @@ WebInspector.OverviewTimelineView.prototype = {
// The TimelineDataGridNode graphs are positioned with percentages, so they auto resize with the view.
// We only need to refresh the graphs when the any of the times change.
if (this.zeroTime !== oldZeroTime || this.startTime !== oldStartTime || this.endTime !== oldEndTime || this.currentTime !== oldCurrentTime) {
var item = this._dataGrid.children[0];
while (item) {
item.refreshGraph();
item = item.traverseNextNode(false, null, true);
var dataGridNode = this._dataGrid.children[0];
while (dataGridNode) {
dataGridNode.refreshGraph();
dataGridNode = dataGridNode.traverseNextNode(true, null, true);
}
}
if (!this.currentTime !== oldCurrentTime) {
// Check the filters again since the current time change might have revealed this node. Start and end time changes are handled by TimelineContentView.
WebInspector.timelineSidebarPanel.updateFilter();
}
this._timelineRuler.updateLayout();
this._processPendingRepresentedObjects();
......@@ -216,11 +221,15 @@ WebInspector.OverviewTimelineView.prototype = {
if (!parentFrame)
return;
if (parentFrame.mainResource === resource || parentFrame.provisionalMainResource === resource)
var expandedByDefault = false;
if (parentFrame.mainResource === resource || parentFrame.provisionalMainResource === resource) {
parentFrame = parentFrame.parentFrame;
expandedByDefault = !parentFrame; // Main frame expands by default.
}
var resourceTreeElement = new WebInspector.ResourceTreeElement(resource);
resourceTreeElement.expand();
if (expandedByDefault)
resourceTreeElement.expand();
var resourceTimelineRecord = this._networkTimeline ? this._networkTimeline.recordForResource(resource) : null;
if (!resourceTimelineRecord)
......
......@@ -30,7 +30,7 @@ WebInspector.ResourceTimelineDataGridNode = function(resourceTimelineRecord, gra
this._resource = resourceTimelineRecord.resource;
this._record = resourceTimelineRecord;
this._record.addEventListener(WebInspector.TimelineRecord.Event.Updated, graphOnly ? this.needsGraphRefresh : this._needsRefresh, this);
this._record.addEventListener(WebInspector.TimelineRecord.Event.Updated, graphOnly ? this._timelineRecordUpdated : this._needsRefresh, this);
if (!graphOnly) {
this._resource.addEventListener(WebInspector.Resource.Event.URLDidChange, this._needsRefresh, this);
......@@ -182,5 +182,11 @@ WebInspector.ResourceTimelineDataGridNode.prototype = {
_goToResource: function(event)
{
WebInspector.resourceSidebarPanel.showSourceCode(this._resource);
},
_timelineRecordUpdated: function(event)
{
if (this.isRecordVisible(this._record))
this.needsGraphRefresh();
}
};
......@@ -28,7 +28,7 @@ WebInspector.SourceCodeTimelineTimelineDataGridNode = function(sourceCodeTimelin
WebInspector.TimelineDataGridNode.call(this, true, graphDataSource);
this._sourceCodeTimeline = sourceCodeTimeline;
this._sourceCodeTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this.needsGraphRefresh, this);
this._sourceCodeTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._timelineRecordAdded, this);
};
WebInspector.Object.addConstructorFunctions(WebInspector.SourceCodeTimelineTimelineDataGridNode);
......@@ -52,5 +52,13 @@ WebInspector.SourceCodeTimelineTimelineDataGridNode.prototype = {
get data()
{
return {graph: this._sourceCodeTimeline.startTime};
},
// Private
_timelineRecordAdded: function(event)
{
if (this.isRecordVisible(event.data.record))
this.needsGraphRefresh();
}
};
......@@ -404,6 +404,6 @@ WebInspector.TimelineContentView.prototype = {
this._currentTimelineView.endTime = this._timelineOverview.selectionStartTime + this._timelineOverview.selectionDuration;
// Delay until the next frame to stay in sync with the current timeline view's time-based layout changes.
requestAnimationFrame(function() { WebInspector.timelineSidebarPanel.updateFilter(true); });
requestAnimationFrame(function() { WebInspector.timelineSidebarPanel.updateFilter(); });
}
};
......@@ -238,6 +238,10 @@ WebInspector.TimelineDataGrid.prototype = {
treeOutline.insertChild(treeElement, insertionIndex);
this.insertChild(dataGridNode, insertionIndex);
// Adding the tree element back to the tree outline subjects it to filters.
// Make sure we keep the hidden state in-sync while the synchronizer is disabled.
dataGridNode.element.classList.toggle("hidden", treeElement.hidden);
if (dataGridNode === selectedNode) {
selectedNode.revealAndSelect();
delete this._ignoreSelectionEvent;
......@@ -274,6 +278,10 @@ WebInspector.TimelineDataGrid.prototype = {
treeOutline.appendChild(treeElement);
this.appendChild(dataGridNode);
// Adding the tree element back to the tree outline subjects it to filters.
// Make sure we keep the hidden state in-sync while the synchronizer is disabled.
dataGridNode.element.classList.toggle("hidden", treeElement.hidden);
}
this._treeOutlineDataGridSynchronizer.enabled = true;
......
......@@ -64,6 +64,33 @@ WebInspector.TimelineDataGridNode.prototype = {
return {graph: records.length ? records[0].startTime : 0};
},
collapse: function()
{
WebInspector.DataGridNode.prototype.collapse.call(this);
// Refresh to show child bars in our graph now that we collapsed.
this.refreshGraph();
},
expand: function()
{
WebInspector.DataGridNode.prototype.expand.call(this);
if (!this.revealed)
return;
// Refresh to remove child bars from our graph now that we expanded.
this.refreshGraph();
// Refresh child graphs since they haven't been updating while we were collapsed.
var childNode = this.children[0];
while (childNode) {
if (childNode instanceof WebInspector.TimelineDataGridNode)
childNode.refreshGraph();
childNode = childNode.traverseNextNode(true, this);
}
},
createCellContent: function(columnIdentifier, cell)
{
if (columnIdentifier === "graph" && this._graphDataSource) {
......@@ -198,42 +225,29 @@ WebInspector.TimelineDataGridNode.prototype = {
delete this._scheduledGraphRefreshIdentifier;
}
var records = this.records;
if (!records || !records.length)
return;
// Fast path for single records.
if (records.length === 1) {
var record = records[0];
var timelineRecordBar = this._timelineRecordBars[0];
if (!this.revealed) {
// We are not visible, but an ancestor will be drawing our graph.
// Notify the next visible ancestor to refresh their graph.
var ancestor = this;
while (ancestor && !ancestor.root) {
if (ancestor.revealed && ancestor instanceof WebInspector.TimelineDataGridNode) {
ancestor.refreshGraph();
return;
}
if (timelineRecordBar && timelineRecordBar.record !== record) {
timelineRecordBar.element.remove();
timelineRecordBar = null;
ancestor = ancestor.parent;
}
if (!timelineRecordBar)
timelineRecordBar = this._timelineRecordBars[0] = new WebInspector.TimelineRecordBar(record);
if (timelineRecordBar.refresh(this._graphDataSource)) {
if (!timelineRecordBar.element.parentNode)
this._graphContainerElement.appendChild(timelineRecordBar.element);
} else
timelineRecordBar.element.remove();
return;
}
// Multiple records attempt to share a bar if their time is close to prevent overlapping bars.
var startTime = this._graphDataSource.startTime;
var currentTime = this._graphDataSource.currentTime;
var endTime = this._graphDataSource.endTime;
var duration = endTime - startTime;
var visibleWidth = this._graphContainerElement.offsetWidth;
var secondsPerPixel = duration / visibleWidth;
var recordBarIndex = 0;
var barRecords = [];
function createBar(barRecords)
{
......@@ -247,35 +261,71 @@ WebInspector.TimelineDataGridNode.prototype = {
++recordBarIndex;
}
for (var record of records) {
// Combining multiple record bars is not supported with records that have inactive time.
// ResourceTimelineRecord is the only one right, and it is always a single record handled above.
console.assert(!record.usesActiveStartTime);
function createBarsForRecords(records)
{
var barRecords = [];
if (isNaN(record.startTime))
continue;
for (var record of records) {
if (isNaN(record.startTime))
continue;
// If this bar is completely before the bounds of the graph, skip this record.
if (record.endTime < startTime)
continue;
// If this bar is completely before the bounds of the graph, skip this record.
if (record.endTime < startTime)
continue;
// If this record is completely after the current time or end time, break out now.
// Records are sorted, so all records after this will be beyond the current or end time too.
if (record.startTime > currentTime || record.startTime > endTime)
break;
// If this record is completely after the current time or end time, break out now.
// Records are sorted, so all records after this will be beyond the current or end time too.
if (record.startTime > currentTime || record.startTime > endTime)
break;
// Check if the previous record can be combined with the current record, if not make a new bar.
if (barRecords.length && WebInspector.TimelineRecordBar.recordsCannotBeCombined(barRecords, record, secondsPerPixel)) {
createBar.call(this, barRecords);
barRecords = [];
// Check if the previous record can be combined with the current record, if not make a new bar.
if (barRecords.length && WebInspector.TimelineRecordBar.recordsCannotBeCombined(barRecords, record, secondsPerPixel)) {
createBar.call(this, barRecords);
barRecords = [];
}
barRecords.push(record);
}
barRecords.push(record);
// Create the bar for the last record if needed.
if (barRecords.length)
createBar.call(this, barRecords);
}
// Create the bar for the last record if needed.
if (barRecords.length)
createBar.call(this, barRecords);
if (this.expanded) {
// When expanded just use the records for this node.
createBarsForRecords.call(this, this.records);
} else {
// When collapsed use the records for this node and its descendants.
// To share bars better, group records by type.
var recordTypeMap = new Map;
function collectRecordsByType(records)
{
for (var record of records) {
var typedRecords = recordTypeMap.get(record.type);
if (!typedRecords) {
typedRecords = [];
recordTypeMap.set(record.type, typedRecords);
}
typedRecords.push(record);
}
}
collectRecordsByType(this.records);
var childNode = this.children[0];
while (childNode) {
if (childNode instanceof WebInspector.TimelineDataGridNode)
collectRecordsByType(childNode.records);
childNode = childNode.traverseNextNode(false, this);
}
for (var records of recordTypeMap.values())
createBarsForRecords.call(this, records);
}
// Remove the remaining unused TimelineRecordBars.
for (; recordBarIndex < this._timelineRecordBars.length; ++recordBarIndex) {
......@@ -290,5 +340,26 @@ WebInspector.TimelineDataGridNode.prototype = {
return;
this._scheduledGraphRefreshIdentifier = requestAnimationFrame(this.refreshGraph.bind(this));
},
// Protected
isRecordVisible: function(record)
{
if (!this._graphDataSource)
return false;
if (isNaN(record.startTime))
return false;
// If this bar is completely before the bounds of the graph, not visible.
if (record.endTime < this.graphDataSource.startTime)
return false;
// If this record is completely after the current time or end time, not visible.
if (record.startTime > this.graphDataSource.currentTime || record.startTime > this.graphDataSource.endTime)
return false;
return true;
}
};
......@@ -35,6 +35,7 @@
border: 1px solid rgb(200, 200, 200);
border-radius: 3px;
min-width: 4px;
z-index: 1;
}
.timeline-record-bar:not(.has-inactive-segment) > .segment {
......@@ -42,11 +43,16 @@
width: 100%;
}
.timeline-record-bar > .segment.inactive {
z-index: 0;
}
.timeline-record-bar > .segment.inactive,
.timeline-record-bar.unfinished > .segment {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
border-right: none;
margin-right: -1px;
}
.timeline-record-bar > .segment.inactive + .segment {
......@@ -56,7 +62,15 @@
:focus .selected .timeline-record-bar > .segment {
background-color: white !important;
border-color: white !important;
border: none !important;
}
:focus .selected .timeline-record-bar > .segment.inactive {
opacity: 0.7;
}
:focus .selected .timeline-record-bar.has-inactive-segment > .segment:not(.inactive) {
border-left: 1px solid rgba(56, 121, 217, 0.7) !important;
}
.timeline-record-bar.timeline-record-type-network > .segment {
......
......@@ -58,7 +58,13 @@ WebInspector.TimelineRecordBar.recordsCannotBeCombined = function(records, candi
if (!records.length)
return true;
if (candidateRecord.usesActiveStartTime)
return true;
var lastRecord = records.lastValue;
if (lastRecord.usesActiveStartTime)
return true;
if (lastRecord.type !== candidateRecord.type)
return true;
......@@ -112,9 +118,10 @@ WebInspector.TimelineRecordBar.prototype = {
this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.BarSegmentStyleClassName);
this._inactiveBarElement.classList.add(WebInspector.TimelineRecordBar.InactiveStyleClassName);
this._element.classList.add(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
this._element.insertBefore(this._inactiveBarElement, this._activeBarElement);
this._element.insertBefore(this._inactiveBarElement, this._element.firstChild);
}
} else if (this._inactiveBarElement) {
this._element.classList.remove(WebInspector.TimelineRecordBar.HasInactiveSegmentStyleClassName);
this._inactiveBarElement.remove();
delete this._inactiveBarElement;
}
......@@ -165,8 +172,15 @@ WebInspector.TimelineRecordBar.prototype = {
var newBarWidth = ((barEndTime - graphStartTime) / graphDuration) - newBarLeftPosition;
this._updateElementPosition(this._element, newBarWidth, "width");
if (!this._inactiveBarElement)
if (!this._inactiveBarElement) {
// If this TimelineRecordBar is reused and had an inactive bar previously,
// we might need to remove some styles and add the active element back.
this._activeBarElement.style.removeProperty("left");
this._activeBarElement.style.removeProperty("width");
if (!this._activeBarElement.parentNode)
this._element.appendChild(this._activeBarElement);
return true;
}
console.assert(firstRecord === lastRecord);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment