Home | History | Annotate | Download | only in timeline
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.TargetAwareObject}
     34  * @param {!WebInspector.Target} target
     35  */
     36 WebInspector.TimelineModel = function(target)
     37 {
     38     WebInspector.TargetAwareObject.call(this, target);
     39     this._filters = [];
     40 }
     41 
     42 WebInspector.TimelineModel.RecordType = {
     43     Root: "Root",
     44     Program: "Program",
     45     EventDispatch: "EventDispatch",
     46 
     47     GPUTask: "GPUTask",
     48 
     49     RequestMainThreadFrame: "RequestMainThreadFrame",
     50     BeginFrame: "BeginFrame",
     51     ActivateLayerTree: "ActivateLayerTree",
     52     DrawFrame: "DrawFrame",
     53     ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
     54     RecalculateStyles: "RecalculateStyles",
     55     InvalidateLayout: "InvalidateLayout",
     56     Layout: "Layout",
     57     UpdateLayerTree: "UpdateLayerTree",
     58     PaintSetup: "PaintSetup",
     59     Paint: "Paint",
     60     Rasterize: "Rasterize",
     61     ScrollLayer: "ScrollLayer",
     62     DecodeImage: "DecodeImage",
     63     ResizeImage: "ResizeImage",
     64     CompositeLayers: "CompositeLayers",
     65 
     66     ParseHTML: "ParseHTML",
     67 
     68     TimerInstall: "TimerInstall",
     69     TimerRemove: "TimerRemove",
     70     TimerFire: "TimerFire",
     71 
     72     XHRReadyStateChange: "XHRReadyStateChange",
     73     XHRLoad: "XHRLoad",
     74     EvaluateScript: "EvaluateScript",
     75 
     76     MarkLoad: "MarkLoad",
     77     MarkDOMContent: "MarkDOMContent",
     78     MarkFirstPaint: "MarkFirstPaint",
     79 
     80     TimeStamp: "TimeStamp",
     81     ConsoleTime: "ConsoleTime",
     82 
     83     ResourceSendRequest: "ResourceSendRequest",
     84     ResourceReceiveResponse: "ResourceReceiveResponse",
     85     ResourceReceivedData: "ResourceReceivedData",
     86     ResourceFinish: "ResourceFinish",
     87 
     88     FunctionCall: "FunctionCall",
     89     GCEvent: "GCEvent",
     90     JSFrame: "JSFrame",
     91 
     92     UpdateCounters: "UpdateCounters",
     93 
     94     RequestAnimationFrame: "RequestAnimationFrame",
     95     CancelAnimationFrame: "CancelAnimationFrame",
     96     FireAnimationFrame: "FireAnimationFrame",
     97 
     98     WebSocketCreate : "WebSocketCreate",
     99     WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
    100     WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
    101     WebSocketDestroy : "WebSocketDestroy",
    102 
    103     EmbedderCallback : "EmbedderCallback",
    104 }
    105 
    106 WebInspector.TimelineModel.Events = {
    107     RecordAdded: "RecordAdded",
    108     RecordsCleared: "RecordsCleared",
    109     RecordingStarted: "RecordingStarted",
    110     RecordingStopped: "RecordingStopped",
    111     RecordingProgress: "RecordingProgress",
    112     RecordFilterChanged: "RecordFilterChanged"
    113 }
    114 
    115 /**
    116  * @param {!Array.<!WebInspector.TimelineModel.Record>} recordsArray
    117  * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
    118  * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
    119  * @return {boolean}
    120  */
    121 WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
    122 {
    123     /**
    124      * @param {!Array.<!WebInspector.TimelineModel.Record>} records
    125      * @param {number} depth
    126      * @return {boolean}
    127      */
    128     function processRecords(records, depth)
    129     {
    130         for (var i = 0; i < records.length; ++i) {
    131             var record = records[i];
    132             if (preOrderCallback && preOrderCallback(record, depth))
    133                 return true;
    134             if (processRecords(record.children(), depth + 1))
    135                 return true;
    136             if (postOrderCallback && postOrderCallback(record, depth))
    137                 return true;
    138         }
    139         return false;
    140     }
    141     return processRecords(recordsArray, 0);
    142 }
    143 
    144 WebInspector.TimelineModel.prototype = {
    145     /**
    146      * @param {boolean} captureStacks
    147      * @param {boolean} captureMemory
    148      * @param {boolean} capturePictures
    149      */
    150     startRecording: function(captureStacks, captureMemory, capturePictures)
    151     {
    152     },
    153 
    154     stopRecording: function()
    155     {
    156     },
    157 
    158     /**
    159      * @return {boolean}
    160      */
    161     loadedFromFile: function()
    162     {
    163         return false;
    164     },
    165 
    166     /**
    167      * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
    168      * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
    169      */
    170     forAllRecords: function(preOrderCallback, postOrderCallback)
    171     {
    172         WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
    173     },
    174 
    175     /**
    176      * @param {!WebInspector.TimelineModel.Filter} filter
    177      */
    178     addFilter: function(filter)
    179     {
    180         this._filters.push(filter);
    181         filter._model = this;
    182     },
    183 
    184     /**
    185      * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback
    186      */
    187     forAllFilteredRecords: function(callback)
    188     {
    189         /**
    190          * @param {!WebInspector.TimelineModel.Record} record
    191          * @param {number} depth
    192          * @this {WebInspector.TimelineModel}
    193          * @return {boolean}
    194          */
    195         function processRecord(record, depth)
    196         {
    197             var visible = this.isVisible(record);
    198             if (visible) {
    199                 if (callback(record, depth))
    200                     return true;
    201             }
    202 
    203             for (var i = 0; i < record.children().length; ++i) {
    204                 if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth))
    205                     return true;
    206             }
    207             return false;
    208         }
    209 
    210         for (var i = 0; i < this._records.length; ++i)
    211             processRecord.call(this, this._records[i], 0);
    212     },
    213 
    214     /**
    215      * @param {!WebInspector.TimelineModel.Record} record
    216      * @return {boolean}
    217      */
    218     isVisible: function(record)
    219     {
    220         for (var i = 0; i < this._filters.length; ++i) {
    221             if (!this._filters[i].accept(record))
    222                 return false;
    223         }
    224         return true;
    225     },
    226 
    227     _filterChanged: function()
    228     {
    229         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged);
    230     },
    231 
    232     /**
    233      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    234      */
    235     records: function()
    236     {
    237         return this._records;
    238     },
    239 
    240     /**
    241      * @param {!Blob} file
    242      * @param {!WebInspector.Progress} progress
    243      */
    244     loadFromFile: function(file, progress)
    245     {
    246         throw new Error("Not implemented");
    247     },
    248 
    249     /**
    250      * @param {string} url
    251      * @param {!WebInspector.Progress} progress
    252      */
    253     loadFromURL: function(url, progress)
    254     {
    255         throw new Error("Not implemented");
    256     },
    257 
    258     saveToFile: function()
    259     {
    260         throw new Error("Not implemented");
    261     },
    262 
    263     reset: function()
    264     {
    265         this._loadedFromFile = false;
    266         this._records = [];
    267         this._minimumRecordTime = 0;
    268         this._maximumRecordTime = 0;
    269         /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
    270         this._mainThreadTasks =  [];
    271         /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
    272         this._gpuThreadTasks = [];
    273         /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
    274         this._eventDividerRecords = [];
    275         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
    276     },
    277 
    278     /**
    279      * @return {number}
    280      */
    281     minimumRecordTime: function()
    282     {
    283         return this._minimumRecordTime;
    284     },
    285 
    286     /**
    287      * @return {number}
    288      */
    289     maximumRecordTime: function()
    290     {
    291         return this._maximumRecordTime;
    292     },
    293 
    294     /**
    295      * @param {!WebInspector.TimelineModel.Record} record
    296      */
    297     _updateBoundaries: function(record)
    298     {
    299         var startTime = record.startTime();
    300         var endTime = record.endTime();
    301 
    302         if (!this._minimumRecordTime || startTime < this._minimumRecordTime)
    303             this._minimumRecordTime = startTime;
    304         if (endTime > this._maximumRecordTime)
    305             this._maximumRecordTime = endTime;
    306     },
    307 
    308     /**
    309      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    310      */
    311     mainThreadTasks: function()
    312     {
    313         return this._mainThreadTasks;
    314     },
    315 
    316     /**
    317      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    318      */
    319     gpuThreadTasks: function()
    320     {
    321         return this._gpuThreadTasks;
    322     },
    323 
    324     /**
    325      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    326      */
    327     eventDividerRecords: function()
    328     {
    329         return this._eventDividerRecords;
    330     },
    331 
    332     __proto__: WebInspector.TargetAwareObject.prototype
    333 }
    334 
    335 /**
    336  * @interface
    337  */
    338 WebInspector.TimelineModel.Record = function()
    339 {
    340 }
    341 
    342 WebInspector.TimelineModel.Record.prototype = {
    343     /**
    344      * @return {?Array.<!ConsoleAgent.CallFrame>}
    345      */
    346     callSiteStackTrace: function() { },
    347 
    348     /**
    349      * @return {?WebInspector.TimelineModel.Record}
    350      */
    351     initiator: function() { },
    352 
    353     /**
    354      * @return {!WebInspector.Target}
    355      */
    356     target: function() { },
    357 
    358     /**
    359      * @return {number}
    360      */
    361     selfTime: function() { },
    362 
    363     /**
    364      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    365      */
    366     children: function() { },
    367 
    368     /**
    369      * @return {!WebInspector.TimelineCategory}
    370      */
    371     category: function() { },
    372 
    373     /**
    374      * @return {number}
    375      */
    376     startTime: function() { },
    377 
    378     /**
    379      * @return {string|undefined}
    380      */
    381     thread: function() { },
    382 
    383     /**
    384      * @return {number}
    385      */
    386     endTime: function() { },
    387 
    388     /**
    389      * @param {number} endTime
    390      */
    391     setEndTime: function(endTime) { },
    392 
    393     /**
    394      * @return {!Object}
    395      */
    396     data: function() { },
    397 
    398     /**
    399      * @return {string}
    400      */
    401     type: function() { },
    402 
    403     /**
    404      * @return {string}
    405      */
    406     frameId: function() { },
    407 
    408     /**
    409      * @return {?Array.<!ConsoleAgent.CallFrame>}
    410      */
    411     stackTrace: function() { },
    412 
    413     /**
    414      * @param {string} key
    415      * @return {?Object}
    416      */
    417     getUserObject: function(key) { },
    418 
    419     /**
    420      * @param {string} key
    421      * @param {?Object|undefined} value
    422      */
    423     setUserObject: function(key, value) { },
    424 
    425     /**
    426      * @return {!Object.<string, number>}
    427      */
    428     aggregatedStats: function() { },
    429 
    430     /**
    431      * @return {?Array.<string>}
    432      */
    433     warnings: function() { }
    434 }
    435 
    436 /**
    437  * @constructor
    438  */
    439 WebInspector.TimelineModel.Filter = function()
    440 {
    441     /** @type {!WebInspector.TimelineModel} */
    442     this._model;
    443 }
    444 
    445 WebInspector.TimelineModel.Filter.prototype = {
    446     /**
    447      * @param {!WebInspector.TimelineModel.Record} record
    448      * @return {boolean}
    449      */
    450     accept: function(record)
    451     {
    452         return true;
    453     },
    454 
    455     notifyFilterChanged: function()
    456     {
    457         this._model._filterChanged();
    458     }
    459 }
    460 
    461 /**
    462  * @constructor
    463  */
    464 WebInspector.TimelineMergingRecordBuffer = function()
    465 {
    466     this._backgroundRecordsBuffer = [];
    467 }
    468 
    469 /**
    470  * @constructor
    471  */
    472 WebInspector.TimelineMergingRecordBuffer.prototype = {
    473     /**
    474      * @param {string} thread
    475      * @param {!Array.<!WebInspector.TimelineModel.Record>} records
    476      * @return {!Array.<!WebInspector.TimelineModel.Record>}
    477      */
    478     process: function(thread, records)
    479     {
    480         if (thread) {
    481             this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records);
    482             return [];
    483         }
    484         /**
    485          * @param {!WebInspector.TimelineModel.Record} a
    486          * @param {!WebInspector.TimelineModel.Record} b
    487          */
    488         function recordTimestampComparator(a, b)
    489         {
    490             // Never return 0, as the merge function will squash identical entries.
    491             return a.startTime() < b.startTime() ? -1 : 1;
    492         }
    493         var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator);
    494         this._backgroundRecordsBuffer = [];
    495         return result;
    496     }
    497 }
    498