Changeset 181626 in webkit


Ignore:
Timestamp:
Mar 17, 2015 1:43:30 AM (9 years ago)
Author:
commit-queue@webkit.org
Message:

Web Inspector: Show rendering frames (and FPS) in Layout and Rendering timeline
https://bugs.webkit.org/show_bug.cgi?id=142029

Patch by Matt Baker <Matt Baker> on 2015-03-17
Reviewed by Timothy Hatcher.

Add UI for showing runloop records and their child records as a frame histogram,
with the recording time on the x-axis and the frame duration on the y-axis. Each frame
is comprised of colored regions representing the time spent in various activities (script,
layout, etc).

Eventually the Frames timeline will replace the Layout & Rendering timeline. Until the views
for the new timeline are finalized the Layout & Rendering timeline will remain in place.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Main.html:

New string and files.

  • UserInterface/Controllers/TimelineManager.js:

(WebInspector.TimelineManager.prototype.eventRecorded):
(WebInspector.TimelineManager.prototype.pageDidLoad):
(WebInspector.TimelineManager.prototype._processNestedRecords):
(WebInspector.TimelineManager.prototype._processRecord):
(WebInspector.TimelineManager.prototype._processEvent):
(WebInspector.TimelineManager.prototype._loadNewRecording):
(WebInspector.TimelineManager.prototype.eventRecorded.processRecord): Deleted.
Added support for new runloop record type and nested record handling.

  • UserInterface/Images/Frames.png: Added.
  • UserInterface/Images/Frames@2x.png: Added.
  • UserInterface/Images/FramesLarge.png: Added.
  • UserInterface/Images/FramesLarge@2x.png: Added.

New images for runloop timeline overview graph and runloop tree records.

  • UserInterface/Models/RunLoopTimelineRecord.js: Added.

(WebInspector.RunLoopTimelineRecord):
(WebInspector.RunLoopTimelineRecord.prototype.get children):
(WebInspector.RunLoopTimelineRecord.prototype.get durationRemainder):
(WebInspector.RunLoopTimelineRecord.prototype.durationForRecords.get var):
Extends TimelineRecord to add child records and subframe duration details.

  • UserInterface/Models/Timeline.js:

(WebInspector.Timeline.prototype.get displayName):
(WebInspector.Timeline.prototype.get iconClassName):
New UI strings and icons.

  • UserInterface/Models/TimelineRecord.js:
  • UserInterface/Views/ContentView.js:

(WebInspector.ContentView):

  • UserInterface/Views/LayoutTimelineView.js:

(WebInspector.LayoutTimelineView.prototype._layoutTimelineRecordAdded):

  • UserInterface/Views/TimelineRecordTreeElement.js:

(WebInspector.TimelineRecordTreeElement):
Added support for new runloop record type.

  • UserInterface/Views/RunLoopTimelineOverviewGraph.css: Added.

(.timeline-overview-graph.runloop > .divider):
(.timeline-overview-graph.runloop > .divider > span):
New styles for runloop timeline graph.

  • UserInterface/Views/RunLoopTimelineOverviewGraph.js: Added.

(WebInspector.RunLoopTimelineOverviewGraph):
(WebInspector.RunLoopTimelineOverviewGraph.prototype.updateLayout.createFrame):
(WebInspector.RunLoopTimelineOverviewGraph.prototype.get graphHeightSeconds.this):
(WebInspector.RunLoopTimelineOverviewGraph.prototype.get graphHeightSeconds):
(WebInspector.RunLoopTimelineOverviewGraph.prototype._updateDividers.createDividerAtPosition.get if):
New overview graph for displaying TimelineRecordFrames and horizontal frame budget dividers.

  • UserInterface/Views/TimelineIcons.css:

(.runloop-icon .icon):
(.runloop-icon.large .icon):
(.runloop-record .icon):

  • UserInterface/Views/TimelineSidebarPanel.js:

New runloop icon styles.

  • UserInterface/Views/TimelineOverviewGraph.js:

(WebInspector.TimelineOverviewGraph):
Updated factory to support creation of the new overview graph.

  • UserInterface/Views/TimelineRecordFrame.css: Added.

(.timeline-record-frame):
(.timeline-record-frame > .frame):
(.timeline-record-frame > .dropped):
(.timeline-record-frame > .frame > .duration):
(.timeline-record-frame > .frame > .duration:first-child):
(.timeline-record-frame > .frame > .duration:last-child):
(.timeline-record-frame > .frame > .duration.timeline-record-type-network):
(.timeline-record-frame > .frame > .duration.timeline-record-type-layout):
(.timeline-record-frame > .frame > .duration.timeline-record-type-script):
New styles for frame bars in the runloop timeline graph.

  • UserInterface/Views/TimelineRecordFrame.js: Added.

(WebInspector.TimelineRecordFrame):
(WebInspector.TimelineRecordFrame.createCombinedFrames):
(WebInspector.TimelineRecordFrame.prototype.get element):
(WebInspector.TimelineRecordFrame.prototype.get duration):
(WebInspector.TimelineRecordFrame.prototype.get records):
(WebInspector.TimelineRecordFrame.prototype.set records):
(WebInspector.TimelineRecordFrame.prototype._updateChildElements.createDurationElement):
New view representing a single frame within the runloop overview graph.

  • WebInspectorUI.vcxproj/WebInspectorUI.vcxproj:
  • WebInspectorUI.vcxproj/WebInspectorUI.vcxproj.filters:

New files.

Location:
trunk/Source/WebInspectorUI
Files:
9 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r181614 r181626  
     12015-03-17  Matt Baker  <mattbaker@apple.com>
     2
     3        Web Inspector: Show rendering frames (and FPS) in Layout and Rendering timeline
     4        https://bugs.webkit.org/show_bug.cgi?id=142029
     5
     6        Reviewed by Timothy Hatcher.
     7
     8        Add UI for showing runloop records and their child records as a frame histogram,
     9        with the recording time on the x-axis and the frame duration on the y-axis. Each frame
     10        is comprised of colored regions representing the time spent in various activities (script,
     11        layout, etc).
     12
     13        Eventually the Frames timeline will replace the Layout & Rendering timeline. Until the views
     14        for the new timeline are finalized the Layout & Rendering timeline will remain in place.
     15
     16        * Localizations/en.lproj/localizedStrings.js:
     17        * UserInterface/Main.html:
     18        New string and files.
     19
     20        * UserInterface/Controllers/TimelineManager.js:
     21        (WebInspector.TimelineManager.prototype.eventRecorded):
     22        (WebInspector.TimelineManager.prototype.pageDidLoad):
     23        (WebInspector.TimelineManager.prototype._processNestedRecords):
     24        (WebInspector.TimelineManager.prototype._processRecord):
     25        (WebInspector.TimelineManager.prototype._processEvent):
     26        (WebInspector.TimelineManager.prototype._loadNewRecording):
     27        (WebInspector.TimelineManager.prototype.eventRecorded.processRecord): Deleted.
     28        Added support for new runloop record type and nested record handling.
     29
     30        * UserInterface/Images/Frames.png: Added.
     31        * UserInterface/Images/Frames@2x.png: Added.
     32        * UserInterface/Images/FramesLarge.png: Added.
     33        * UserInterface/Images/FramesLarge@2x.png: Added.
     34        New images for runloop timeline overview graph and runloop tree records.
     35
     36        * UserInterface/Models/RunLoopTimelineRecord.js: Added.
     37        (WebInspector.RunLoopTimelineRecord):
     38        (WebInspector.RunLoopTimelineRecord.prototype.get children):
     39        (WebInspector.RunLoopTimelineRecord.prototype.get durationRemainder):
     40        (WebInspector.RunLoopTimelineRecord.prototype.durationForRecords.get var):
     41        Extends TimelineRecord to add child records and subframe duration details.
     42
     43        * UserInterface/Models/Timeline.js:
     44        (WebInspector.Timeline.prototype.get displayName):
     45        (WebInspector.Timeline.prototype.get iconClassName):
     46        New UI strings and icons.
     47
     48        * UserInterface/Models/TimelineRecord.js:
     49        * UserInterface/Views/ContentView.js:
     50        (WebInspector.ContentView):
     51        * UserInterface/Views/LayoutTimelineView.js:
     52        (WebInspector.LayoutTimelineView.prototype._layoutTimelineRecordAdded):
     53        * UserInterface/Views/TimelineRecordTreeElement.js:
     54        (WebInspector.TimelineRecordTreeElement):
     55        Added support for new runloop record type.
     56
     57        * UserInterface/Views/RunLoopTimelineOverviewGraph.css: Added.
     58        (.timeline-overview-graph.runloop > .divider):
     59        (.timeline-overview-graph.runloop > .divider > span):
     60        New styles for runloop timeline graph.
     61
     62        * UserInterface/Views/RunLoopTimelineOverviewGraph.js: Added.
     63        (WebInspector.RunLoopTimelineOverviewGraph):
     64        (WebInspector.RunLoopTimelineOverviewGraph.prototype.updateLayout.createFrame):
     65        (WebInspector.RunLoopTimelineOverviewGraph.prototype.get graphHeightSeconds.this):
     66        (WebInspector.RunLoopTimelineOverviewGraph.prototype.get graphHeightSeconds):
     67        (WebInspector.RunLoopTimelineOverviewGraph.prototype._updateDividers.createDividerAtPosition.get if):
     68        New overview graph for displaying TimelineRecordFrames and horizontal frame budget dividers.
     69
     70        * UserInterface/Views/TimelineIcons.css:
     71        (.runloop-icon .icon):
     72        (.runloop-icon.large .icon):
     73        (.runloop-record .icon):
     74        * UserInterface/Views/TimelineSidebarPanel.js:
     75        New runloop icon styles.
     76
     77        * UserInterface/Views/TimelineOverviewGraph.js:
     78        (WebInspector.TimelineOverviewGraph):
     79        Updated factory to support creation of the new overview graph.
     80
     81        * UserInterface/Views/TimelineRecordFrame.css: Added.
     82        (.timeline-record-frame):
     83        (.timeline-record-frame > .frame):
     84        (.timeline-record-frame > .dropped):
     85        (.timeline-record-frame > .frame > .duration):
     86        (.timeline-record-frame > .frame > .duration:first-child):
     87        (.timeline-record-frame > .frame > .duration:last-child):
     88        (.timeline-record-frame > .frame > .duration.timeline-record-type-network):
     89        (.timeline-record-frame > .frame > .duration.timeline-record-type-layout):
     90        (.timeline-record-frame > .frame > .duration.timeline-record-type-script):
     91        New styles for frame bars in the runloop timeline graph.
     92
     93        * UserInterface/Views/TimelineRecordFrame.js: Added.
     94        (WebInspector.TimelineRecordFrame):
     95        (WebInspector.TimelineRecordFrame.createCombinedFrames):
     96        (WebInspector.TimelineRecordFrame.prototype.get element):
     97        (WebInspector.TimelineRecordFrame.prototype.get duration):
     98        (WebInspector.TimelineRecordFrame.prototype.get records):
     99        (WebInspector.TimelineRecordFrame.prototype.set records):
     100        (WebInspector.TimelineRecordFrame.prototype._updateChildElements.createDurationElement):
     101        New view representing a single frame within the runloop overview graph.
     102
     103        * WebInspectorUI.vcxproj/WebInspectorUI.vcxproj:
     104        * WebInspectorUI.vcxproj/WebInspectorUI.vcxproj.filters:
     105        New files.
     106
    11072015-03-16  Joseph Pecoraro  <pecoraro@apple.com>
    2108
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r181452 r181626  
    398398localizedStrings["Role"] = "Role";
    399399localizedStrings["Rules"] = "Rules";
     400localizedStrings["Runloop Executed"] = "Runloop Executed";
    400401localizedStrings["Scheme"] = "Scheme";
    401402localizedStrings["Scope Chain"] = "Scope Chain";
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/TimelineManager.js

    r181185 r181626  
    163163    },
    164164
    165     eventRecorded: function(originalRecordPayload)
     165    eventRecorded: function(recordPayload)
    166166    {
    167167        // Called from WebInspector.TimelineObserver.
     
    170170            return;
    171171
    172         function processRecord(recordPayload, parentRecordPayload)
    173         {
    174             var startTime = this.activeRecording.computeElapsedTime(recordPayload.startTime);
    175             var endTime = this.activeRecording.computeElapsedTime(recordPayload.endTime);
    176             var callFrames = this._callFramesFromPayload(recordPayload.stackTrace);
    177 
    178             var significantCallFrame = null;
    179             if (callFrames) {
    180                 for (var i = 0; i < callFrames.length; ++i) {
    181                     if (callFrames[i].nativeCode)
    182                         continue;
    183                     significantCallFrame = callFrames[i];
    184                     break;
    185                 }
    186             }
    187 
    188             var sourceCodeLocation = significantCallFrame && significantCallFrame.sourceCodeLocation;
    189 
    190             switch (recordPayload.type) {
    191             case TimelineAgent.EventType.MarkLoad:
    192                 console.assert(isNaN(endTime));
    193 
    194                 var frame = WebInspector.frameResourceManager.frameForIdentifier(recordPayload.frameId);
    195                 console.assert(frame);
    196                 if (!frame)
    197                     break;
    198 
    199                 frame.markLoadEvent(startTime);
    200 
    201                 if (!frame.isMainFrame())
    202                     break;
    203 
    204                 var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.LoadEvent);
    205                 this._activeRecording.addEventMarker(eventMarker);
    206 
    207                 this._stopAutoRecordingSoon();
    208                 break;
    209 
    210             case TimelineAgent.EventType.MarkDOMContent:
    211                 console.assert(isNaN(endTime));
    212 
    213                 var frame = WebInspector.frameResourceManager.frameForIdentifier(recordPayload.frameId);
    214                 console.assert(frame);
    215                 if (!frame)
    216                     break;
    217 
    218                 frame.markDOMContentReadyEvent(startTime);
    219 
    220                 if (!frame.isMainFrame())
    221                     break;
    222 
    223                 var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.DOMContentEvent);
    224                 this._activeRecording.addEventMarker(eventMarker);
    225                 break;
    226 
    227             case TimelineAgent.EventType.ScheduleStyleRecalculation:
    228                 console.assert(isNaN(endTime));
    229 
    230                 // Pass the startTime as the endTime since this record type has no duration.
    231                 this._addRecord(new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.InvalidateStyles, startTime, startTime, callFrames, sourceCodeLocation));
    232                 break;
    233 
    234             case TimelineAgent.EventType.RecalculateStyles:
    235                 this._addRecord(new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.RecalculateStyles, startTime, endTime, callFrames, sourceCodeLocation));
    236                 break;
    237 
    238             case TimelineAgent.EventType.InvalidateLayout:
    239                 console.assert(isNaN(endTime));
    240 
    241                 // Pass the startTime as the endTime since this record type has no duration.
    242                 this._addRecord(new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.InvalidateLayout, startTime, startTime, callFrames, sourceCodeLocation));
    243                 break;
    244 
    245             case TimelineAgent.EventType.Layout:
    246                 var layoutRecordType = sourceCodeLocation ? WebInspector.LayoutTimelineRecord.EventType.ForcedLayout : WebInspector.LayoutTimelineRecord.EventType.Layout;
    247 
    248                 // COMPATIBILITY (iOS 6): Layout records did not contain area properties. This is not exposed via a quad "root".
    249                 var quad = recordPayload.data.root ? new WebInspector.Quad(recordPayload.data.root) : null;
    250                 if (quad)
    251                     this._addRecord(new WebInspector.LayoutTimelineRecord(layoutRecordType, startTime, endTime, callFrames, sourceCodeLocation, quad.points[0].x, quad.points[0].y, quad.width, quad.height, quad));
    252                 else
    253                     this._addRecord(new WebInspector.LayoutTimelineRecord(layoutRecordType, startTime, endTime, callFrames, sourceCodeLocation));
    254                 break;
    255 
    256             case TimelineAgent.EventType.Paint:
    257                 // COMPATIBILITY (iOS 6): Paint records data contained x, y, width, height properties. This became a quad "clip".
    258                 var quad = recordPayload.data.clip ? new WebInspector.Quad(recordPayload.data.clip) : null;
    259                 if (quad)
    260                     this._addRecord(new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.Paint, startTime, endTime, callFrames, sourceCodeLocation, null, null, quad.width, quad.height, quad));
    261                 else
    262                     this._addRecord(new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.Paint, startTime, endTime, callFrames, sourceCodeLocation, recordPayload.data.x, recordPayload.data.y, recordPayload.data.width, recordPayload.data.height));
    263                 break;
    264 
    265             case TimelineAgent.EventType.EvaluateScript:
    266                 if (!sourceCodeLocation) {
    267                     var mainFrame = WebInspector.frameResourceManager.mainFrame;
    268                     var scriptResource = mainFrame.url === recordPayload.data.url ? mainFrame.mainResource : mainFrame.resourceForURL(recordPayload.data.url, true);
    269                     if (scriptResource) {
    270                         // The lineNumber is 1-based, but we expect 0-based.
    271                         var lineNumber = recordPayload.data.lineNumber - 1;
    272 
    273                         // FIXME: No column number is provided.
    274                         sourceCodeLocation = scriptResource.createSourceCodeLocation(lineNumber, 0);
    275                     }
    276                 }
    277 
    278                 var profileData = recordPayload.data.profile;
    279 
    280                 switch (parentRecordPayload && parentRecordPayload.type) {
    281                 case TimelineAgent.EventType.TimerFire:
    282                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.timerId, profileData));
    283                     break;
    284                 default:
    285                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated, startTime, endTime, callFrames, sourceCodeLocation, null, profileData));
    286                     break;
    287                 }
    288 
    289                 break;
    290 
    291             case TimelineAgent.EventType.TimeStamp:
    292                 var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.TimeStamp);
    293                 this._activeRecording.addEventMarker(eventMarker);
    294                 break;
    295 
    296             case TimelineAgent.EventType.ConsoleProfile:
    297                 var profileData = recordPayload.data.profile;
    298                 console.assert(profileData);
    299                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ConsoleProfileRecorded, startTime, endTime, callFrames, sourceCodeLocation, recordPayload.data.title, profileData));
    300                 break;
    301 
    302             case TimelineAgent.EventType.FunctionCall:
    303                 // FunctionCall always happens as a child of another record, and since the FunctionCall record
    304                 // has useful info we just make the timeline record here (combining the data from both records).
    305                 if (!parentRecordPayload)
    306                     break;
    307 
    308                 if (!sourceCodeLocation) {
    309                     var mainFrame = WebInspector.frameResourceManager.mainFrame;
    310                     var scriptResource = mainFrame.url === recordPayload.data.scriptName ? mainFrame.mainResource : mainFrame.resourceForURL(recordPayload.data.scriptName, true);
    311                     if (scriptResource) {
    312                         // The lineNumber is 1-based, but we expect 0-based.
    313                         var lineNumber = recordPayload.data.scriptLine - 1;
    314 
    315                         // FIXME: No column number is provided.
    316                         sourceCodeLocation = scriptResource.createSourceCodeLocation(lineNumber, 0);
    317                     }
    318                 }
    319 
    320                 var profileData = recordPayload.data.profile;
    321 
    322                 switch (parentRecordPayload.type) {
    323                 case TimelineAgent.EventType.TimerFire:
    324                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.timerId, profileData));
    325                     break;
    326                 case TimelineAgent.EventType.EventDispatch:
    327                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.type, profileData));
    328                     break;
    329                 case TimelineAgent.EventType.XHRLoad:
    330                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, "load", profileData));
    331                     break;
    332                 case TimelineAgent.EventType.XHRReadyStateChange:
    333                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, "readystatechange", profileData));
    334                     break;
    335                 case TimelineAgent.EventType.FireAnimationFrame:
    336                     this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.id, profileData));
    337                     break;
    338                 }
    339 
    340                 break;
    341 
    342             case TimelineAgent.EventType.ProbeSample:
    343                 // Pass the startTime as the endTime since this record type has no duration.
    344                 sourceCodeLocation = WebInspector.probeManager.probeForIdentifier(recordPayload.data.probeId).breakpoint.sourceCodeLocation;
    345                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ProbeSampleRecorded, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.probeId));
    346                 break;
    347 
    348             case TimelineAgent.EventType.TimerInstall:
    349                 console.assert(isNaN(endTime));
    350 
    351                 // Pass the startTime as the endTime since this record type has no duration.
    352                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerInstalled, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId));
    353                 break;
    354 
    355             case TimelineAgent.EventType.TimerRemove:
    356                 console.assert(isNaN(endTime));
    357 
    358                 // Pass the startTime as the endTime since this record type has no duration.
    359                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerRemoved, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId));
    360                 break;
    361 
    362             case TimelineAgent.EventType.RequestAnimationFrame:
    363                 console.assert(isNaN(endTime));
    364 
    365                 // Pass the startTime as the endTime since this record type has no duration.
    366                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameRequested, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId));
    367                 break;
    368 
    369             case TimelineAgent.EventType.CancelAnimationFrame:
    370                 console.assert(isNaN(endTime));
    371 
    372                 // Pass the startTime as the endTime since this record type has no duration.
    373                 this._addRecord(new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameCanceled, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId));
    374                 break;
    375             }
    376         }
     172        var records = this._processNestedRecords(recordPayload);
     173        records.forEach(function(currentValue) {
     174            this._addRecord(currentValue);
     175        }.bind(this));
     176    },
     177
     178    pageDidLoad: function(timestamp)
     179    {
     180        if (isNaN(WebInspector.frameResourceManager.mainFrame.loadEventTimestamp))
     181            WebInspector.frameResourceManager.mainFrame.markLoadEvent(this.activeRecording.computeElapsedTime(timestamp));
     182    },
     183
     184    // Private
     185
     186    _processNestedRecords: function(childRecordPayloads, parentRecordPayload)
     187    {
     188        // Convert to a single item array if needed.
     189        if (!(childRecordPayloads instanceof Array))
     190            childRecordPayloads = [childRecordPayloads];
     191
     192        var records = [];
    377193
    378194        // Iterate over the records tree using a stack. Doing this recursively has
    379195        // been known to cause a call stack overflow. https://webkit.org/b/79106
    380         var stack = [{array: [originalRecordPayload], parent: null, index: 0}];
     196        var stack = [{array: childRecordPayloads, parent: parentRecordPayload || null, index: 0}];
    381197        while (stack.length) {
    382198            var entry = stack.lastValue;
    383199            var recordPayloads = entry.array;
    384             var parentRecordPayload = entry.parent;
    385200
    386201            if (entry.index < recordPayloads.length) {
    387202                var recordPayload = recordPayloads[entry.index];
    388 
    389                 processRecord.call(this, recordPayload, parentRecordPayload);
     203                var record = this._processEvent(recordPayload, entry.parent);
     204                if (record)
     205                    records.push(record);
    390206
    391207                if (recordPayload.children)
     
    395211                stack.pop();
    396212        }
    397     },
    398 
    399     pageDidLoad: function(timestamp)
    400     {
    401         if (isNaN(WebInspector.frameResourceManager.mainFrame.loadEventTimestamp))
    402             WebInspector.frameResourceManager.mainFrame.markLoadEvent(this.activeRecording.computeElapsedTime(timestamp));
    403     },
    404 
    405     // Private
     213
     214        return records;
     215    },
     216
     217    _processRecord: function(recordPayload, parentRecordPayload)
     218    {
     219        var startTime = this.activeRecording.computeElapsedTime(recordPayload.startTime);
     220        var endTime = this.activeRecording.computeElapsedTime(recordPayload.endTime);
     221        var callFrames = this._callFramesFromPayload(recordPayload.stackTrace);
     222
     223        var significantCallFrame = null;
     224        if (callFrames) {
     225            for (var i = 0; i < callFrames.length; ++i) {
     226                if (callFrames[i].nativeCode)
     227                    continue;
     228                significantCallFrame = callFrames[i];
     229                break;
     230            }
     231        }
     232
     233        var sourceCodeLocation = significantCallFrame && significantCallFrame.sourceCodeLocation;
     234
     235        switch (recordPayload.type) {
     236        case TimelineAgent.EventType.ScheduleStyleRecalculation:
     237            console.assert(isNaN(endTime));
     238
     239            // Pass the startTime as the endTime since this record type has no duration.
     240            return new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.InvalidateStyles, startTime, startTime, callFrames, sourceCodeLocation);
     241
     242        case TimelineAgent.EventType.RecalculateStyles:
     243            return new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.RecalculateStyles, startTime, endTime, callFrames, sourceCodeLocation);
     244
     245        case TimelineAgent.EventType.InvalidateLayout:
     246            console.assert(isNaN(endTime));
     247
     248            // Pass the startTime as the endTime since this record type has no duration.
     249            return new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.InvalidateLayout, startTime, startTime, callFrames, sourceCodeLocation);
     250
     251        case TimelineAgent.EventType.Layout:
     252            var layoutRecordType = sourceCodeLocation ? WebInspector.LayoutTimelineRecord.EventType.ForcedLayout : WebInspector.LayoutTimelineRecord.EventType.Layout;
     253
     254            // COMPATIBILITY (iOS 6): Layout records did not contain area properties. This is not exposed via a quad "root".
     255            var quad = recordPayload.data.root ? new WebInspector.Quad(recordPayload.data.root) : null;
     256            if (quad)
     257                return new WebInspector.LayoutTimelineRecord(layoutRecordType, startTime, endTime, callFrames, sourceCodeLocation, quad.points[0].x, quad.points[0].y, quad.width, quad.height, quad);
     258            else
     259                return new WebInspector.LayoutTimelineRecord(layoutRecordType, startTime, endTime, callFrames, sourceCodeLocation);
     260
     261        case TimelineAgent.EventType.Paint:
     262            // COMPATIBILITY (iOS 6): Paint records data contained x, y, width, height properties. This became a quad "clip".
     263            var quad = recordPayload.data.clip ? new WebInspector.Quad(recordPayload.data.clip) : null;
     264            if (quad)
     265                return new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.Paint, startTime, endTime, callFrames, sourceCodeLocation, null, null, quad.width, quad.height, quad);
     266            else
     267                return new WebInspector.LayoutTimelineRecord(WebInspector.LayoutTimelineRecord.EventType.Paint, startTime, endTime, callFrames, sourceCodeLocation, recordPayload.data.x, recordPayload.data.y, recordPayload.data.width, recordPayload.data.height);
     268
     269        case TimelineAgent.EventType.RunLoop:
     270            if (!recordPayload.children)
     271                return null;
     272
     273            var children = this._processNestedRecords(recordPayload.children, recordPayload);
     274            return new WebInspector.RunLoopTimelineRecord(startTime, endTime, children);
     275
     276        case TimelineAgent.EventType.EvaluateScript:
     277            if (!sourceCodeLocation) {
     278                var mainFrame = WebInspector.frameResourceManager.mainFrame;
     279                var scriptResource = mainFrame.url === recordPayload.data.url ? mainFrame.mainResource : mainFrame.resourceForURL(recordPayload.data.url, true);
     280                if (scriptResource) {
     281                    // The lineNumber is 1-based, but we expect 0-based.
     282                    var lineNumber = recordPayload.data.lineNumber - 1;
     283
     284                    // FIXME: No column number is provided.
     285                    sourceCodeLocation = scriptResource.createSourceCodeLocation(lineNumber, 0);
     286                }
     287            }
     288
     289            var profileData = recordPayload.data.profile;
     290
     291            switch (parentRecordPayload && parentRecordPayload.type) {
     292            case TimelineAgent.EventType.TimerFire:
     293                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.timerId, profileData);
     294            default:
     295                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ScriptEvaluated, startTime, endTime, callFrames, sourceCodeLocation, null, profileData);
     296            }
     297
     298            break;
     299
     300        case TimelineAgent.EventType.ConsoleProfile:
     301            var profileData = recordPayload.data.profile;
     302            console.assert(profileData);
     303            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ConsoleProfileRecorded, startTime, endTime, callFrames, sourceCodeLocation, recordPayload.data.title, profileData);
     304
     305        case TimelineAgent.EventType.FunctionCall:
     306            // FunctionCall always happens as a child of another record, and since the FunctionCall record
     307            // has useful info we just make the timeline record here (combining the data from both records).
     308            if (!parentRecordPayload)
     309                break;
     310
     311            if (!sourceCodeLocation) {
     312                var mainFrame = WebInspector.frameResourceManager.mainFrame;
     313                var scriptResource = mainFrame.url === recordPayload.data.scriptName ? mainFrame.mainResource : mainFrame.resourceForURL(recordPayload.data.scriptName, true);
     314                if (scriptResource) {
     315                    // The lineNumber is 1-based, but we expect 0-based.
     316                    var lineNumber = recordPayload.data.scriptLine - 1;
     317
     318                    // FIXME: No column number is provided.
     319                    sourceCodeLocation = scriptResource.createSourceCodeLocation(lineNumber, 0);
     320                }
     321            }
     322
     323            var profileData = recordPayload.data.profile;
     324
     325            switch (parentRecordPayload.type) {
     326            case TimelineAgent.EventType.TimerFire:
     327                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.timerId, profileData);
     328            case TimelineAgent.EventType.EventDispatch:
     329                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.type, profileData);
     330            case TimelineAgent.EventType.XHRLoad:
     331                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, "load", profileData);
     332            case TimelineAgent.EventType.XHRReadyStateChange:
     333                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.EventDispatched, startTime, endTime, callFrames, sourceCodeLocation, "readystatechange", profileData);
     334            case TimelineAgent.EventType.FireAnimationFrame:
     335                return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameFired, startTime, endTime, callFrames, sourceCodeLocation, parentRecordPayload.data.id, profileData);
     336            }
     337
     338            break;
     339
     340        case TimelineAgent.EventType.ProbeSample:
     341            // Pass the startTime as the endTime since this record type has no duration.
     342            sourceCodeLocation = WebInspector.probeManager.probeForIdentifier(recordPayload.data.probeId).breakpoint.sourceCodeLocation;
     343            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.ProbeSampleRecorded, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.probeId);
     344
     345        case TimelineAgent.EventType.TimerInstall:
     346            console.assert(isNaN(endTime));
     347
     348            // Pass the startTime as the endTime since this record type has no duration.
     349            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerInstalled, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId);
     350
     351        case TimelineAgent.EventType.TimerRemove:
     352            console.assert(isNaN(endTime));
     353
     354            // Pass the startTime as the endTime since this record type has no duration.
     355            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.TimerRemoved, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId);
     356
     357        case TimelineAgent.EventType.RequestAnimationFrame:
     358            console.assert(isNaN(endTime));
     359
     360            // Pass the startTime as the endTime since this record type has no duration.
     361            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameRequested, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId);
     362
     363        case TimelineAgent.EventType.CancelAnimationFrame:
     364            console.assert(isNaN(endTime));
     365
     366            // Pass the startTime as the endTime since this record type has no duration.
     367            return new WebInspector.ScriptTimelineRecord(WebInspector.ScriptTimelineRecord.EventType.AnimationFrameCanceled, startTime, startTime, callFrames, sourceCodeLocation, recordPayload.data.timerId);
     368        }
     369
     370        return null;
     371    },
     372
     373    _processEvent: function(recordPayload, parentRecordPayload)
     374    {
     375        var startTime = this.activeRecording.computeElapsedTime(recordPayload.startTime);
     376        var endTime = this.activeRecording.computeElapsedTime(recordPayload.endTime);
     377
     378        switch (recordPayload.type) {
     379        case TimelineAgent.EventType.MarkLoad:
     380            console.assert(isNaN(endTime));
     381
     382            var frame = WebInspector.frameResourceManager.frameForIdentifier(recordPayload.frameId);
     383            console.assert(frame);
     384            if (!frame)
     385                break;
     386
     387            frame.markLoadEvent(startTime);
     388
     389            if (!frame.isMainFrame())
     390                break;
     391
     392            var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.LoadEvent);
     393            this._activeRecording.addEventMarker(eventMarker);
     394
     395            this._stopAutoRecordingSoon();
     396            break;
     397
     398        case TimelineAgent.EventType.MarkDOMContent:
     399            console.assert(isNaN(endTime));
     400
     401            var frame = WebInspector.frameResourceManager.frameForIdentifier(recordPayload.frameId);
     402            console.assert(frame);
     403            if (!frame)
     404                break;
     405
     406            frame.markDOMContentReadyEvent(startTime);
     407
     408            if (!frame.isMainFrame())
     409                break;
     410
     411            var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.DOMContentEvent);
     412            this._activeRecording.addEventMarker(eventMarker);
     413            break;
     414
     415        case TimelineAgent.EventType.TimeStamp:
     416            var eventMarker = new WebInspector.TimelineMarker(startTime, WebInspector.TimelineMarker.Type.TimeStamp);
     417            this._activeRecording.addEventMarker(eventMarker);
     418            break;
     419
     420        default:
     421            return this._processRecord(recordPayload, parentRecordPayload);
     422        }
     423
     424        return null;
     425    },
    406426
    407427    _loadNewRecording: function()
     
    412432        var identifier = this._nextRecordingIdentifier++;
    413433        var newRecording = new WebInspector.TimelineRecording(identifier, WebInspector.UIString("Timeline Recording %d").format(identifier));
    414         newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Network));
    415         newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Layout));
    416         newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Script));
     434        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Network, newRecording));
     435        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.RunLoop, newRecording));
     436        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Layout, newRecording));
     437        newRecording.addTimeline(new WebInspector.Timeline(WebInspector.TimelineRecord.Type.Script, newRecording));
    417438
    418439        this._recordings.push(newRecording);
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r181610 r181626  
    115115    <link rel="stylesheet" href="Views/ResourceTreeElement.css">
    116116    <link rel="stylesheet" href="Views/RulesStyleDetailsPanel.css">
     117    <link rel="stylesheet" href="Views/RunLoopTimelineOverviewGraph.css">
    117118    <link rel="stylesheet" href="Views/ScopeBar.css">
    118119    <link rel="stylesheet" href="Views/ScriptContentView.css">
     
    135136    <link rel="stylesheet" href="Views/TimelineOverview.css">
    136137    <link rel="stylesheet" href="Views/TimelineRecordBar.css">
     138    <link rel="stylesheet" href="Views/TimelineRecordFrame.css">
    137139    <link rel="stylesheet" href="Views/TimelineRecordingContentView.css">
    138140    <link rel="stylesheet" href="Views/TimelineRuler.css">
     
    273275    <script src="Models/ResourceTimelineRecord.js"></script>
    274276    <script src="Models/Revision.js"></script>
     277    <script src="Models/RunLoopTimelineRecord.js"></script>
    275278    <script src="Models/ScopeChainNode.js"></script>
    276279    <script src="Models/Script.js"></script>
     
    449452    <script src="Views/ResourceTimelineDataGridNodePathComponent.js"></script>
    450453    <script src="Views/RulesStyleDetailsPanel.js"></script>
     454    <script src="Views/RunLoopTimelineOverviewGraph.js"></script>
    451455    <script src="Views/ScopeBar.js"></script>
    452456    <script src="Views/ScopeBarItem.js"></script>
     
    472476    <script src="Views/TimelineOverview.js"></script>
    473477    <script src="Views/TimelineRecordBar.js"></script>
     478    <script src="Views/TimelineRecordFrame.js"></script>
    474479    <script src="Views/TimelineRecordingContentView.js"></script>
    475480    <script src="Views/TimelineRuler.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/Timeline.js

    r181185 r181626  
    8181        if (this._type === WebInspector.TimelineRecord.Type.Script)
    8282            return WebInspector.UIString("JavaScript & Events");
     83        if (this._type === WebInspector.TimelineRecord.Type.RunLoop)
     84            return WebInspector.UIString("Frames");
    8385
    8486        console.error("Timeline has unknown type:", this._type, this);
     
    9193        if (this._type === WebInspector.TimelineRecord.Type.Layout)
    9294            return WebInspector.TimelineSidebarPanel.ColorsIconStyleClass;
     95        if (this._type === WebInspector.TimelineRecord.Type.RunLoop)
     96            return WebInspector.TimelineSidebarPanel.RunLoopIconStyleClass;
    9397        if (this._type === WebInspector.TimelineRecord.Type.Script)
    9498            return WebInspector.TimelineSidebarPanel.ScriptIconStyleClass;
  • trunk/Source/WebInspectorUI/UserInterface/Models/TimelineRecord.js

    r164543 r181626  
    4747    Network: "timeline-record-type-network",
    4848    Layout: "timeline-record-type-layout",
    49     Script: "timeline-record-type-script"
     49    Script: "timeline-record-type-script",
     50    RunLoop: "timeline-record-type-runloop"
    5051};
    5152
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r181011 r181626  
    4848                return new WebInspector.NetworkTimelineView(representedObject);
    4949
    50             if (timelineType === WebInspector.TimelineRecord.Type.Layout)
     50            if (timelineType === WebInspector.TimelineRecord.Type.Layout || timelineType === WebInspector.TimelineRecord.Type.RunLoop)
    5151                return new WebInspector.LayoutTimelineView(representedObject);
    5252
  • trunk/Source/WebInspectorUI/UserInterface/Views/LayoutTimelineView.js

    r181011 r181626  
    2828    WebInspector.TimelineView.call(this, timeline);
    2929
    30     console.assert(timeline.type === WebInspector.TimelineRecord.Type.Layout);
     30    console.assert(timeline.type === WebInspector.TimelineRecord.Type.Layout || WebInspector.TimelineRecord.Type.RunLoop);
    3131
    3232    this.navigationSidebarTreeOutline.onselect = this._treeElementSelected.bind(this);
     
    186186    {
    187187        var layoutTimelineRecord = event.data.record;
    188         console.assert(layoutTimelineRecord instanceof WebInspector.LayoutTimelineRecord);
     188        console.assert(layoutTimelineRecord instanceof WebInspector.LayoutTimelineRecord || layoutTimelineRecord instanceof WebInspector.RunLoopTimelineRecord);
    189189
    190190        this._pendingRecords.push(layoutTimelineRecord);
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineIcons.css

    r172241 r181626  
    6464}
    6565
     66.runloop-icon .icon {
     67    content: -webkit-image-set(url(../Images/Frames.png) 1x, url(../Images/Frames@2x.png) 2x);
     68}
     69
     70.runloop-icon.large .icon {
     71    content: -webkit-image-set(url(../Images/FramesLarge.png) 1x, url(../Images/FramesLarge@2x.png) 2x);
     72}
     73
    6674.stopwatch-icon .icon {
    6775    content: -webkit-image-set(url(../Images/Stopwatch.png) 1x, url(../Images/Stopwatch@2x.png) 2x);
     
    8290.paint-record .icon {
    8391    content: url(../Images/TimelineRecordPaint.svg);
     92}
     93
     94.runloop-record .icon {
     95    content: -webkit-image-set(url(../Images/Frames.png) 1x, url(../Images/Frames@2x.png) 2x);
    8496}
    8597
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverviewGraph.js

    r179701 r181626  
    4040        if (timelineType === WebInspector.TimelineRecord.Type.Script)
    4141            return new WebInspector.ScriptTimelineOverviewGraph(timeline);
     42
     43        if (timelineType === WebInspector.TimelineRecord.Type.RunLoop)
     44            return new WebInspector.RunLoopTimelineOverviewGraph(timeline);
    4245
    4346        throw Error("Can't make a graph for an unknown timeline.");
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordTreeElement.js

    r175784 r181626  
    100100        break;
    101101
     102    case WebInspector.TimelineRecord.Type.RunLoop:
     103        title = WebInspector.UIString("Runloop Executed");
     104        iconStyleClass = WebInspector.TimelineRecordTreeElement.RunLoopRecordIconStyleClass;
     105        break;
     106
    102107    default:
    103108        console.error("Unknown TimelineRecord type: " + timelineRecord.type, timelineRecord);
     
    115120WebInspector.TimelineRecordTreeElement.LayoutRecordIconStyleClass = "layout-record";
    116121WebInspector.TimelineRecordTreeElement.PaintRecordIconStyleClass = "paint-record";
     122WebInspector.TimelineRecordTreeElement.RunLoopRecordIconStyleClass = "runloop-record";
    117123WebInspector.TimelineRecordTreeElement.EvaluatedRecordIconStyleClass = "evaluated-record";
    118124WebInspector.TimelineRecordTreeElement.EventRecordIconStyleClass = "event-record";
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js

    r180001 r181626  
    140140WebInspector.TimelineSidebarPanel.ColorsIconStyleClass = "colors-icon";
    141141WebInspector.TimelineSidebarPanel.ScriptIconStyleClass = "script-icon";
     142WebInspector.TimelineSidebarPanel.RunLoopIconStyleClass = "runloop-icon";
    142143WebInspector.TimelineSidebarPanel.TimelineRecordingContentViewShowingStyleClass = "timeline-recording-content-view-showing";
    143144
  • trunk/Source/WebInspectorUI/WebInspectorUI.vcxproj/WebInspectorUI.vcxproj

    r181610 r181626  
    526526    <None Include="..\UserInterface\Images\ForwardArrow.svg" />
    527527    <None Include="..\UserInterface\Images\ForwardArrowLegacy.svg" />
     528    <None Include="..\UserInterface\Images\Frames.png" />
     529    <None Include="..\UserInterface\Images\Frames%402x.png" />
     530    <None Include="..\UserInterface\Images\FramesLarge.png" />
     531    <None Include="..\UserInterface\Images\FramesLarge%402x.png" />
    528532    <None Include="..\UserInterface\Images\Function.svg" />
    529533    <None Include="..\UserInterface\Images\GoToArrow.svg" />
     
    748752    <None Include="..\UserInterface\RulesStyleDetailsPanel.css" />
    749753    <None Include="..\UserInterface\RulesStyleDetailsPanel.js" />
     754    <None Include="..\UserInterface\RunLoopTimelineOverviewGraph.css" />
     755    <None Include="..\UserInterface\RunLoopTimelineOverviewGraph.js" />
     756    <None Include="..\UserInterface\RunLoopTimelineRecord.js" />
    750757    <None Include="..\UserInterface\RuntimeObserver.js" />
    751758    <None Include="..\UserInterface\ScopeBar.css" />
     
    809816    <None Include="..\UserInterface\TimelineOverview.js" />
    810817    <None Include="..\UserInterface\TimelineRecord.js" />
     818    <None Include="..\UserInterface\TimelineRecordFrame.css" />
     819    <None Include="..\UserInterface\TimelineRecordFrame.js" />
    811820    <None Include="..\UserInterface\TimelinesContentView.css" />
    812821    <None Include="..\UserInterface\TimelinesContentView.js" />
  • trunk/Source/WebInspectorUI/WebInspectorUI.vcxproj/WebInspectorUI.vcxproj.filters

    r181610 r181626  
    826826      <Filter>UserInterface</Filter>
    827827    </None>
     828    <None Include="..\UserInterface\RunLoopTimelineOverviewGraph.css">
     829      <Filter>UserInterface</Filter>
     830    </None>
     831    <None Include="..\UserInterface\RunLoopTimelineOverviewGraph.js">
     832      <Filter>UserInterface</Filter>
     833    </None>
     834    <None Include="..\UserInterface\RunLoopTimelineRecord.js">
     835      <Filter>UserInterface</Filter>
     836    </None>
    828837    <None Include="..\UserInterface\RuntimeObserver.js">
    829838      <Filter>UserInterface</Filter>
     
    10071016    </None>
    10081017    <None Include="..\UserInterface\TimelineRecord.js">
     1018      <Filter>UserInterface</Filter>
     1019    </None>
     1020    <None Include="..\UserInterface\TimelineRecordFrame.css">
     1021      <Filter>UserInterface</Filter>
     1022    </None>
     1023    <None Include="..\UserInterface\TimelineRecordFrame.js">
    10091024      <Filter>UserInterface</Filter>
    10101025    </None>
Note: See TracChangeset for help on using the changeset viewer.