home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / chrome.dll / 0 / BINDATA / 601 < prev    next >
Encoding:
Text File  |  2013-04-03  |  348.1 KB  |  11,149 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. /**
  6. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file.
  9.  
  10. * WARNING: This file is generated by generate_about_tracing_contents.py
  11. *
  12. *        Do not edit directly.
  13. */
  14.  
  15. window.FLATTENED = {};
  16. window.FLATTENED['base'] = true;
  17. window.FLATTENED['event_target'] = true;
  18. window.FLATTENED['measuring_stick'] = true;
  19. window.FLATTENED['timeline_filter'] = true;
  20. window.FLATTENED['timeline_selection'] = true;
  21. window.FLATTENED['timeline_viewport'] = true;
  22. window.FLATTENED['ui'] = true;
  23. window.FLATTENED['tracks.timeline_track'] = true;
  24. window.FLATTENED['tracks.timeline_container_track'] = true;
  25. window.FLATTENED['fast_rect_renderer'] = true;
  26. window.FLATTENED['timeline_color_scheme'] = true;
  27. window.FLATTENED['tracks.timeline_canvas_based_track'] = true;
  28. window.FLATTENED['sorted_array_utils'] = true;
  29. window.FLATTENED['tracks.timeline_slice_track'] = true;
  30. window.FLATTENED['timeline_slice'] = true;
  31. window.FLATTENED['timeline_slice_group'] = true;
  32. window.FLATTENED['timeline_async_slice_group'] = true;
  33. window.FLATTENED['timeline_thread'] = true;
  34. window.FLATTENED['timeline_counter'] = true;
  35. window.FLATTENED['timeline_process'] = true;
  36. window.FLATTENED['timeline_cpu'] = true;
  37. window.FLATTENED['timeline_model'] = true;
  38. window.FLATTENED['tracks.timeline_cpu_track'] = true;
  39. window.FLATTENED['tracks.timeline_counter_track'] = true;
  40. window.FLATTENED['tracks.timeline_slice_group_track'] = true;
  41. window.FLATTENED['tracks.timeline_async_slice_group_track'] = true;
  42. window.FLATTENED['tracks.timeline_thread_track'] = true;
  43. window.FLATTENED['tracks.timeline_process_track'] = true;
  44. window.FLATTENED['tracks.timeline_model_track'] = true;
  45. window.FLATTENED['tracks.timeline_viewport_track'] = true;
  46. window.FLATTENED['timeline'] = true;
  47. window.FLATTENED['timeline_analysis'] = true;
  48. window.FLATTENED['overlay'] = true;
  49. window.FLATTENED['timeline_category_filter_dialog'] = true;
  50. window.FLATTENED['timeline_find_control'] = true;
  51. window.FLATTENED['trace_event_importer'] = true;
  52. window.FLATTENED['linux_perf_parser'] = true;
  53. window.FLATTENED['linux_perf_bus_parser'] = true;
  54. window.FLATTENED['linux_perf_clock_parser'] = true;
  55. window.FLATTENED['linux_perf_cpufreq_parser'] = true;
  56. window.FLATTENED['linux_perf_drm_parser'] = true;
  57. window.FLATTENED['linux_perf_exynos_parser'] = true;
  58. window.FLATTENED['linux_perf_gesture_parser'] = true;
  59. window.FLATTENED['linux_perf_i915_parser'] = true;
  60. window.FLATTENED['linux_perf_mali_parser'] = true;
  61. window.FLATTENED['linux_perf_power_parser'] = true;
  62. window.FLATTENED['linux_perf_sched_parser'] = true;
  63. window.FLATTENED['linux_perf_workqueue_parser'] = true;
  64. window.FLATTENED['linux_perf_android_parser'] = true;
  65. window.FLATTENED['linux_perf_importer'] = true;
  66. window.FLATTENED['settings'] = true;
  67. window.FLATTENED['timeline_view'] = true;
  68. window.FLATTENED['tracing_controller'] = true;
  69. window.FLATTENED['profiling_view'] = true;
  70. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  71. // Use of this source code is governed by a BSD-style license that can be
  72. // found in the LICENSE file.
  73.  
  74. 'use strict';
  75.  
  76.  
  77. /**
  78.  * The global object.
  79.  * @type {!Object}
  80.  * @const
  81.  */
  82. var global = this;
  83.  
  84.  
  85. /** Platform, package, object property, and Event support. */
  86. this.base = (function() {
  87.  
  88.   /**
  89.    * Base path for modules. Used to form URLs for module 'require' requests.
  90.    */
  91.   var moduleBasePath = '.';
  92.   function setModuleBasePath(path) {
  93.     if (path[path.length - 1] == '/')
  94.       path = path.substring(0, path.length - 1);
  95.     moduleBasePath = path;
  96.   }
  97.  
  98.  
  99.   function mLog(text, opt_indentLevel) {
  100.     if (true)
  101.       return;
  102.  
  103.     var spacing = '';
  104.     var indentLevel = opt_indentLevel || 0;
  105.     for (var i = 0; i < indentLevel; i++)
  106.       spacing += ' ';
  107.     console.log(spacing + text);
  108.   }
  109.  
  110.   /**
  111.    * Builds an object structure for the provided namespace path,
  112.    * ensuring that names that already exist are not overwritten. For
  113.    * example:
  114.    * 'a.b.c' -> a = {};a.b={};a.b.c={};
  115.    * @param {string} name Name of the object that this file defines.
  116.    * @param {*=} opt_object The object to expose at the end of the path.
  117.    * @param {Object=} opt_objectToExportTo The object to add the path to;
  118.    *     default is {@code global}.
  119.    * @private
  120.    */
  121.   function exportPath(name, opt_object, opt_objectToExportTo) {
  122.     var parts = name.split('.');
  123.     var cur = opt_objectToExportTo || global;
  124.  
  125.     for (var part; parts.length && (part = parts.shift());) {
  126.       if (!parts.length && opt_object !== undefined) {
  127.         // last part and we have an object; use it
  128.         cur[part] = opt_object;
  129.       } else if (part in cur) {
  130.         cur = cur[part];
  131.       } else {
  132.         cur = cur[part] = {};
  133.       }
  134.     }
  135.     return cur;
  136.   };
  137.  
  138.   var didLoadModules = false;
  139.   var moduleDependencies = {};
  140.   var moduleStylesheets = {};
  141.  
  142.   function addModuleDependency(moduleName, dependentModuleName) {
  143.     if (!moduleDependencies[moduleName])
  144.       moduleDependencies[moduleName] = [];
  145.  
  146.     var dependentModules = moduleDependencies[moduleName];
  147.     var found = false;
  148.     for (var i = 0; i < dependentModules.length; i++)
  149.       if (dependentModules[i] == dependentModuleName)
  150.         found = true;
  151.       if (!found)
  152.         dependentModules.push(dependentModuleName);
  153.   }
  154.  
  155.   function addModuleStylesheet(moduleName, stylesheetName) {
  156.     if (!moduleStylesheets[moduleName])
  157.       moduleStylesheets[moduleName] = [];
  158.  
  159.     var stylesheets = moduleStylesheets[moduleName];
  160.     var found = false;
  161.     for (var i = 0; i < stylesheets.length; i++)
  162.       if (stylesheets[i] == stylesheetName)
  163.         found = true;
  164.       if (!found)
  165.         stylesheets.push(stylesheetName);
  166.   }
  167.  
  168.   function ensureDepsLoaded() {
  169.     if (didLoadModules)
  170.       return;
  171.     didLoadModules = true;
  172.  
  173.     var req = new XMLHttpRequest();
  174.     var src = moduleBasePath + '/' + 'deps.js';
  175.     req.open('GET', src, false);
  176.     req.send(null);
  177.     if (req.status != 200)
  178.       throw new Error('Could not find ' + src +
  179.                       '. Run calcdeps.py and try again.');
  180.  
  181.     base.addModuleStylesheet = addModuleStylesheet;
  182.     base.addModuleDependency = addModuleDependency;
  183.     try {
  184.       // By construction, the deps file should call addModuleDependency.
  185.       eval(req.responseText);
  186.     } catch (e) {
  187.       throw new Error('When loading deps, got ' + e.stack ? e.stack : e);
  188.     }
  189.     delete base.addModuleDependency;
  190.     delete base.addModuleStylesheet;
  191.   }
  192.  
  193.   var moduleLoadStatus = {};
  194.   function require(dependentModuleName, opt_indentLevel) {
  195.     var indentLevel = opt_indentLevel || 0;
  196.  
  197.     if (window.FLATTENED) {
  198.       if (!window.FLATTENED[dependentModuleName])
  199.         throw new Error('Somehow, module ' + dependentModuleName +
  200.                         ' didn\'t get stored in the flattened js file! ' +
  201.                         'You may need to rerun build/flatten.py');
  202.       return;
  203.     }
  204.     ensureDepsLoaded();
  205.  
  206.     mLog('require(' + dependentModuleName + ')', indentLevel);
  207.  
  208.     if (moduleLoadStatus[dependentModuleName] == 'APPENDED')
  209.       return;
  210.     if (moduleLoadStatus[dependentModuleName] == 'RESOLVING')
  211.       throw new Error('Circular dependency betwen modules. Cannot continue!');
  212.     moduleLoadStatus[dependentModuleName] = 'RESOLVING';
  213.  
  214.     // Load the module stylesheet first.
  215.     var stylesheets = moduleStylesheets[dependentModuleName] || [];
  216.     for (var i = 0; i < stylesheets.length; i++)
  217.       requireStylesheet(stylesheets[i]);
  218.  
  219.     // Load the module's dependent scripts after.
  220.     var dependentModules =
  221.         moduleDependencies[dependentModuleName] || [];
  222.     for (var i = 0; i < dependentModules.length; i++)
  223.       require(dependentModules[i], indentLevel + 1);
  224.  
  225.     mLog('load(' + dependentModuleName + ')', indentLevel);
  226.     // Load the module itself.
  227.     var localPath = dependentModuleName.replace(/\./g, '/') + '.js';
  228.     var src = moduleBasePath + '/' + localPath;
  229.     var text = '<script type="text/javascript" src="' + src +
  230.         '"></' + 'script>';
  231.     base.doc.write(text);
  232.     moduleLoadStatus[dependentModuleName] = 'APPENDED';
  233.   }
  234.  
  235.   var stylesheetLoadStatus = {};
  236.   function requireStylesheet(dependentStylesheetName) {
  237.     if (window.FLATTENED)
  238.       return;
  239.  
  240.     if (stylesheetLoadStatus[dependentStylesheetName])
  241.       return;
  242.     stylesheetLoadStatus[dependentStylesheetName] = true;
  243.     var localPath = dependentStylesheetName.replace(/\./g, '/') + '.css';
  244.     var stylesheetPath = moduleBasePath + '/' + localPath;
  245.  
  246.     var linkEl = document.createElement('link');
  247.     linkEl.setAttribute('rel', 'stylesheet');
  248.     linkEl.setAttribute('href', stylesheetPath);
  249.     base.doc.head.appendChild(linkEl);
  250.   }
  251.  
  252.   function exportTo(namespace, fn) {
  253.     var obj = exportPath(namespace);
  254.     try {
  255.       var exports = fn();
  256.     } catch (e) {
  257.       console.log('While running exports for ', name, ':');
  258.       console.log(e.stack || e);
  259.       return;
  260.     }
  261.  
  262.     for (var propertyName in exports) {
  263.       // Maybe we should check the prototype chain here? The current usage
  264.       // pattern is always using an object literal so we only care about own
  265.       // properties.
  266.       var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
  267.                                                                propertyName);
  268.       if (propertyDescriptor) {
  269.         Object.defineProperty(obj, propertyName, propertyDescriptor);
  270.         mLog('  +' + propertyName);
  271.       }
  272.     }
  273.   };
  274.  
  275.   /**
  276.    * Fires a property change event on the target.
  277.    * @param {EventTarget} target The target to dispatch the event on.
  278.    * @param {string} propertyName The name of the property that changed.
  279.    * @param {*} newValue The new value for the property.
  280.    * @param {*} oldValue The old value for the property.
  281.    */
  282.   function dispatchPropertyChange(target, propertyName, newValue, oldValue) {
  283.     var e = new base.Event(propertyName + 'Change');
  284.     e.propertyName = propertyName;
  285.     e.newValue = newValue;
  286.     e.oldValue = oldValue;
  287.     target.dispatchEvent(e);
  288.   }
  289.  
  290.   /**
  291.    * Converts a camelCase javascript property name to a hyphenated-lower-case
  292.    * attribute name.
  293.    * @param {string} jsName The javascript camelCase property name.
  294.    * @return {string} The equivalent hyphenated-lower-case attribute name.
  295.    */
  296.   function getAttributeName(jsName) {
  297.     return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
  298.   }
  299.  
  300.   /**
  301.    * The kind of property to define in {@code defineProperty}.
  302.    * @enum {number}
  303.    * @const
  304.    */
  305.   var PropertyKind = {
  306.     /**
  307.      * Plain old JS property where the backing data is stored as a 'private'
  308.      * field on the object.
  309.      */
  310.     JS: 'js',
  311.  
  312.     /**
  313.      * The property backing data is stored as an attribute on an element.
  314.      */
  315.     ATTR: 'attr',
  316.  
  317.     /**
  318.      * The property backing data is stored as an attribute on an element. If the
  319.      * element has the attribute then the value is true.
  320.      */
  321.     BOOL_ATTR: 'boolAttr'
  322.   };
  323.  
  324.   /**
  325.    * Helper function for defineProperty that returns the getter to use for the
  326.    * property.
  327.    * @param {string} name The name of the property.
  328.    * @param {base.PropertyKind} kind The kind of the property.
  329.    * @return {function():*} The getter for the property.
  330.    */
  331.   function getGetter(name, kind) {
  332.     switch (kind) {
  333.       case PropertyKind.JS:
  334.         var privateName = name + '_';
  335.         return function() {
  336.           return this[privateName];
  337.         };
  338.       case PropertyKind.ATTR:
  339.         var attributeName = getAttributeName(name);
  340.         return function() {
  341.           return this.getAttribute(attributeName);
  342.         };
  343.       case PropertyKind.BOOL_ATTR:
  344.         var attributeName = getAttributeName(name);
  345.         return function() {
  346.           return this.hasAttribute(attributeName);
  347.         };
  348.     }
  349.   }
  350.  
  351.   /**
  352.    * Helper function for defineProperty that returns the setter of the right
  353.    * kind.
  354.    * @param {string} name The name of the property we are defining the setter
  355.    *     for.
  356.    * @param {base.PropertyKind} kind The kind of property we are getting the
  357.    *     setter for.
  358.    * @param {function(*):void} opt_setHook A function to run after the property
  359.    *     is set, but before the propertyChange event is fired.
  360.    * @return {function(*):void} The function to use as a setter.
  361.    */
  362.   function getSetter(name, kind, opt_setHook) {
  363.     switch (kind) {
  364.       case PropertyKind.JS:
  365.         var privateName = name + '_';
  366.         return function(value) {
  367.           var oldValue = this[privateName];
  368.           if (value !== oldValue) {
  369.             this[privateName] = value;
  370.             if (opt_setHook)
  371.               opt_setHook.call(this, value, oldValue);
  372.             dispatchPropertyChange(this, name, value, oldValue);
  373.           }
  374.         };
  375.  
  376.       case PropertyKind.ATTR:
  377.         var attributeName = getAttributeName(name);
  378.         return function(value) {
  379.           var oldValue = this[attributeName];
  380.           if (value !== oldValue) {
  381.             if (value == undefined)
  382.               this.removeAttribute(attributeName);
  383.             else
  384.               this.setAttribute(attributeName, value);
  385.             if (opt_setHook)
  386.               opt_setHook.call(this, value, oldValue);
  387.             dispatchPropertyChange(this, name, value, oldValue);
  388.           }
  389.         };
  390.  
  391.       case PropertyKind.BOOL_ATTR:
  392.         var attributeName = getAttributeName(name);
  393.         return function(value) {
  394.           var oldValue = this[attributeName];
  395.           if (value !== oldValue) {
  396.             if (value)
  397.               this.setAttribute(attributeName, name);
  398.             else
  399.               this.removeAttribute(attributeName);
  400.             if (opt_setHook)
  401.               opt_setHook.call(this, value, oldValue);
  402.             dispatchPropertyChange(this, name, value, oldValue);
  403.           }
  404.         };
  405.     }
  406.   }
  407.  
  408.   /**
  409.    * Defines a property on an object. When the setter changes the value a
  410.    * property change event with the type {@code name + 'Change'} is fired.
  411.    * @param {!Object} obj The object to define the property for.
  412.    * @param {string} name The name of the property.
  413.    * @param {base.PropertyKind=} opt_kind What kind of underlying storage to
  414.    * use.
  415.    * @param {function(*):void} opt_setHook A function to run after the
  416.    *     property is set, but before the propertyChange event is fired.
  417.    */
  418.   function defineProperty(obj, name, opt_kind, opt_setHook) {
  419.     if (typeof obj == 'function')
  420.       obj = obj.prototype;
  421.  
  422.     var kind = opt_kind || PropertyKind.JS;
  423.  
  424.     if (!obj.__lookupGetter__(name))
  425.       obj.__defineGetter__(name, getGetter(name, kind));
  426.  
  427.     if (!obj.__lookupSetter__(name))
  428.       obj.__defineSetter__(name, getSetter(name, kind, opt_setHook));
  429.   }
  430.  
  431.   /**
  432.    * Counter for use with createUid
  433.    */
  434.   var uidCounter = 1;
  435.  
  436.   /**
  437.    * @return {number} A new unique ID.
  438.    */
  439.   function createUid() {
  440.     return uidCounter++;
  441.   }
  442.  
  443.   /**
  444.    * Returns a unique ID for the item. This mutates the item so it needs to be
  445.    * an object
  446.    * @param {!Object} item The item to get the unique ID for.
  447.    * @return {number} The unique ID for the item.
  448.    */
  449.   function getUid(item) {
  450.     if (item.hasOwnProperty('uid'))
  451.       return item.uid;
  452.     return item.uid = createUid();
  453.   }
  454.  
  455.   /**
  456.    * Dispatches a simple event on an event target.
  457.    * @param {!EventTarget} target The event target to dispatch the event on.
  458.    * @param {string} type The type of the event.
  459.    * @param {boolean=} opt_bubbles Whether the event bubbles or not.
  460.    * @param {boolean=} opt_cancelable Whether the default action of the event
  461.    *     can be prevented.
  462.    * @return {boolean} If any of the listeners called {@code preventDefault}
  463.    *     during the dispatch this will return false.
  464.    */
  465.   function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
  466.     var e = new base.Event(type, opt_bubbles, opt_cancelable);
  467.     return target.dispatchEvent(e);
  468.   }
  469.  
  470.   /**
  471.    * Adds a {@code getInstance} static method that always return the same
  472.    * instance object.
  473.    * @param {!Function} ctor The constructor for the class to add the static
  474.    *     method to.
  475.    */
  476.   function addSingletonGetter(ctor) {
  477.     ctor.getInstance = function() {
  478.       return ctor.instance_ || (ctor.instance_ = new ctor());
  479.     };
  480.   }
  481.  
  482.   /**
  483.    * Creates a new event to be used with base.EventTarget or DOM EventTarget
  484.    * objects.
  485.    * @param {string} type The name of the event.
  486.    * @param {boolean=} opt_bubbles Whether the event bubbles.
  487.    *     Default is false.
  488.    * @param {boolean=} opt_preventable Whether the default action of the event
  489.    *     can be prevented.
  490.    * @constructor
  491.    * @extends {Event}
  492.    */
  493.   function Event(type, opt_bubbles, opt_preventable) {
  494.     var e = base.doc.createEvent('Event');
  495.     e.initEvent(type, !!opt_bubbles, !!opt_preventable);
  496.     e.__proto__ = global.Event.prototype;
  497.     return e;
  498.   };
  499.  
  500.   /**
  501.    * Initialization which must be deferred until run-time.
  502.    */
  503.   function initialize() {
  504.     // If 'document' isn't defined, then we must be being pre-compiled,
  505.     // so set a trap so that we're initialized on first access at run-time.
  506.     if (!global.document) {
  507.       var originalCr = cr;
  508.  
  509.       Object.defineProperty(global, 'cr', {
  510.         get: function() {
  511.           Object.defineProperty(global, 'cr', {value: originalCr});
  512.           originalBase.initialize();
  513.           return originalCr;
  514.         },
  515.         configurable: true
  516.       });
  517.  
  518.       return;
  519.     }
  520.  
  521.     Event.prototype = {__proto__: global.Event.prototype};
  522.  
  523.     base.doc = document;
  524.  
  525.     base.isMac = /Mac/.test(navigator.platform);
  526.     base.isWindows = /Win/.test(navigator.platform);
  527.     base.isChromeOS = /CrOS/.test(navigator.userAgent);
  528.     base.isLinux = /Linux/.test(navigator.userAgent);
  529.     base.isGTK = /GTK/.test(chrome.toolkit);
  530.     base.isViews = /views/.test(chrome.toolkit);
  531.  
  532.     setModuleBasePath('/src');
  533.   }
  534.  
  535.   return {
  536.     set moduleBasePath(path) {
  537.       setModuleBasePath(path);
  538.     },
  539.  
  540.     get moduleBasePath() {
  541.       return moduleBasePath;
  542.     },
  543.  
  544.     require: require,
  545.     requireStylesheet: requireStylesheet,
  546.     exportTo: exportTo,
  547.  
  548.     addSingletonGetter: addSingletonGetter,
  549.     createUid: createUid,
  550.     defineProperty: defineProperty,
  551.     dispatchPropertyChange: dispatchPropertyChange,
  552.     dispatchSimpleEvent: dispatchSimpleEvent,
  553.     Event: Event,
  554.     getUid: getUid,
  555.     initialize: initialize,
  556.     PropertyKind: PropertyKind
  557.   };
  558. })();
  559.  
  560.  
  561. /**
  562.  * TODO(kgr): Move this to another file which is to be loaded last.
  563.  * This will be done as part of future work to make this code pre-compilable.
  564.  */
  565. base.initialize();
  566.  
  567. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  568. // Use of this source code is governed by a BSD-style license that can be
  569. // found in the LICENSE file.
  570.  
  571. /**
  572.  * @fileoverview This contains an implementation of the EventTarget interface
  573.  * as defined by DOM Level 2 Events.
  574.  */
  575. base.exportTo('base', function() {
  576.  
  577.   /**
  578.    * Creates a new EventTarget. This class implements the DOM level 2
  579.    * EventTarget interface and can be used wherever those are used.
  580.    * @constructor
  581.    */
  582.   function EventTarget() {
  583.   }
  584.  
  585.   EventTarget.prototype = {
  586.  
  587.     /**
  588.      * Adds an event listener to the target.
  589.      * @param {string} type The name of the event.
  590.      * @param {!Function|{handleEvent:Function}} handler The handler for the
  591.      *     event. This is called when the event is dispatched.
  592.      */
  593.     addEventListener: function(type, handler) {
  594.       if (!this.listeners_)
  595.         this.listeners_ = Object.create(null);
  596.       if (!(type in this.listeners_)) {
  597.         this.listeners_[type] = [handler];
  598.       } else {
  599.         var handlers = this.listeners_[type];
  600.         if (handlers.indexOf(handler) < 0)
  601.           handlers.push(handler);
  602.       }
  603.     },
  604.  
  605.     /**
  606.      * Removes an event listener from the target.
  607.      * @param {string} type The name of the event.
  608.      * @param {!Function|{handleEvent:Function}} handler The handler for the
  609.      *     event.
  610.      */
  611.     removeEventListener: function(type, handler) {
  612.       if (!this.listeners_)
  613.         return;
  614.       if (type in this.listeners_) {
  615.         var handlers = this.listeners_[type];
  616.         var index = handlers.indexOf(handler);
  617.         if (index >= 0) {
  618.           // Clean up if this was the last listener.
  619.           if (handlers.length == 1)
  620.             delete this.listeners_[type];
  621.           else
  622.             handlers.splice(index, 1);
  623.         }
  624.       }
  625.     },
  626.  
  627.     /**
  628.      * Dispatches an event and calls all the listeners that are listening to
  629.      * the type of the event.
  630.      * @param {!cr.event.Event} event The event to dispatch.
  631.      * @return {boolean} Whether the default action was prevented. If someone
  632.      *     calls preventDefault on the event object then this returns false.
  633.      */
  634.     dispatchEvent: function(event) {
  635.       if (!this.listeners_)
  636.         return true;
  637.  
  638.       // Since we are using DOM Event objects we need to override some of the
  639.       // properties and methods so that we can emulate this correctly.
  640.       var self = this;
  641.       event.__defineGetter__('target', function() {
  642.         return self;
  643.       });
  644.       event.preventDefault = function() {
  645.         this.returnValue = false;
  646.       };
  647.  
  648.       var type = event.type;
  649.       var prevented = 0;
  650.       if (type in this.listeners_) {
  651.         // Clone to prevent removal during dispatch
  652.         var handlers = this.listeners_[type].concat();
  653.         for (var i = 0, handler; handler = handlers[i]; i++) {
  654.           if (handler.handleEvent)
  655.             prevented |= handler.handleEvent.call(handler, event) === false;
  656.           else
  657.             prevented |= handler.call(this, event) === false;
  658.         }
  659.       }
  660.  
  661.       return !prevented && event.returnValue;
  662.     }
  663.   };
  664.  
  665.   // Export
  666.   return {
  667.     EventTarget: EventTarget
  668.   };
  669. });
  670.  
  671. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  672. // Use of this source code is governed by a BSD-style license that can be
  673. // found in the LICENSE file.
  674.  
  675. base.exportTo('tracing', function() {
  676.  
  677.   /**
  678.    * Uses an embedded iframe to measure provided elements without forcing layout
  679.    * on the main document. You must call attach() on the stick before using it,
  680.    * and call detach() on it when you are done using it.
  681.    * @constructor
  682.    * @extends {Object}
  683.    */
  684.   function MeasuringStick() {
  685.     this.iframe_ = undefined;
  686.   }
  687.  
  688.   MeasuringStick.prototype = {
  689.     __proto__: Object.prototype,
  690.  
  691.     /**
  692.      * Measures the provided element without forcing layout on the main
  693.      * document.
  694.      */
  695.     measure: function(element) {
  696.       this.iframe_.contentDocument.body.appendChild(element);
  697.       var style = this.iframe_.contentWindow.getComputedStyle(element);
  698.       var width = parseInt(style.width, 10);
  699.       var height = parseInt(style.height, 10);
  700.       this.iframe_.contentDocument.body.removeChild(element);
  701.       return { width: width, height: height };
  702.     },
  703.  
  704.     attach: function() {
  705.       var iframe = document.createElement('iframe');
  706.       iframe.style.cssText =
  707.           'width:100%;height:0;border:0;visibility:hidden';
  708.       document.body.appendChild(iframe);
  709.       this.iframe_ = iframe;
  710.       this.iframe_.contentDocument.body.style.cssText =
  711.           'padding:0;margin:0;overflow:hidden';
  712.  
  713.       var stylesheets = document.querySelectorAll('link[rel=stylesheet]');
  714.       for (var i = 0; i < stylesheets.length; i++) {
  715.         var stylesheet = stylesheets[i];
  716.         var link = this.iframe_.contentDocument.createElement('link');
  717.         link.rel = 'stylesheet';
  718.         link.href = stylesheet.href;
  719.         this.iframe_.contentDocument.head.appendChild(link);
  720.       }
  721.     },
  722.  
  723.     detach: function() {
  724.       document.body.removeChild(this.iframe_);
  725.       this.iframe_ = undefined;
  726.     }
  727.   };
  728.  
  729.   return {
  730.     MeasuringStick: MeasuringStick
  731.   };
  732. });
  733.  
  734. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  735. // Use of this source code is governed by a BSD-style license that can be
  736. // found in the LICENSE file.
  737.  
  738. 'use strict';
  739.  
  740. /**
  741.  * @fileoverview TimelineModel is a parsed representation of the
  742.  * TraceEvents obtained from base/trace_event in which the begin-end
  743.  * tokens are converted into a hierarchy of processes, threads,
  744.  * subrows, and slices.
  745.  *
  746.  * The building block of the model is a slice. A slice is roughly
  747.  * equivalent to function call executing on a specific thread. As a
  748.  * result, slices may have one or more subslices.
  749.  *
  750.  * A thread contains one or more subrows of slices. Row 0 corresponds to
  751.  * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
  752.  * are nested 1 deep in the stack, and so on. We use these subrows to draw
  753.  * nesting tasks.
  754.  *
  755.  */
  756. base.exportTo('tracing', function() {
  757.  
  758.   function filterSliceArray(filter, slices) {
  759.     if (filter === undefined)
  760.       return slices;
  761.  
  762.     var matched = [];
  763.     for (var i = 0; i < slices.length; ++i) {
  764.       if (filter.matchSlice(slices[i]))
  765.         matched.push(slices[i]);
  766.     }
  767.     return matched;
  768.   }
  769.  
  770.   function filterCounterArray(filter, counters) {
  771.     if (filter === undefined)
  772.       return counters;
  773.  
  774.     var matched = [];
  775.     for (var i = 0; i < counters.length; ++i) {
  776.       if (filter.matchCounter(counters[i]))
  777.         matched.push(counters[i]);
  778.     }
  779.     return matched;
  780.   }
  781.  
  782.   /**
  783.    * @constructor The generic base class for filtering a TimelineModel based on
  784.    * various rules. The base class returns true for everything.
  785.    */
  786.   function TimelineFilter() {
  787.   }
  788.  
  789.   TimelineFilter.prototype = {
  790.     __proto__: Object.prototype,
  791.  
  792.     matchCounter: function(counter) {
  793.       return true;
  794.     },
  795.  
  796.     matchCpu: function(cpu) {
  797.       return true;
  798.     },
  799.  
  800.     matchProcess: function(process) {
  801.       return true;
  802.     },
  803.  
  804.     matchSlice: function(slice) {
  805.       return true;
  806.     },
  807.  
  808.     matchThread: function(thread) {
  809.       return true;
  810.     }
  811.   };
  812.  
  813.   /**
  814.    * @constructor A filter that matches objects by their name.
  815.    * Timeline.findAllObjectsMatchingFilter
  816.    */
  817.   function TimelineTitleFilter(text) {
  818.     TimelineFilter.call(this);
  819.     this.text_ = text;
  820.   }
  821.   TimelineTitleFilter.prototype = {
  822.     __proto__: TimelineFilter.prototype,
  823.  
  824.     matchCounter: function(counter) {
  825.       if (this.text_.length == 0)
  826.         return false;
  827.       if (counter.name === undefined)
  828.         return false;
  829.       return counter.name.indexOf(this.text_) != -1;
  830.     },
  831.  
  832.     matchSlice: function(slice) {
  833.       if (this.text_.length == 0)
  834.         return false;
  835.       if (slice.title === undefined)
  836.         return false;
  837.       return slice.title.indexOf(this.text_) != -1;
  838.     }
  839.   };
  840.  
  841.   /**
  842.    * @constructor A filter that filters objects by their category.
  843.    * Objects match if they are NOT in the list of categories
  844.    * @param {Array<string>} opt_categories Categories to blacklist.
  845.    */
  846.   function TimelineCategoryFilter(opt_categories) {
  847.     TimelineFilter.call(this);
  848.     this.categories_ = {};
  849.     var cats = opt_categories || [];
  850.     for (var i = 0; i < cats.length; i++)
  851.       this.addCategory(cats[i]);
  852.   }
  853.   TimelineCategoryFilter.prototype = {
  854.     __proto__: TimelineFilter.prototype,
  855.  
  856.     addCategory: function(cat) {
  857.       this.categories_[cat] = true;
  858.     },
  859.  
  860.     matchCounter: function(counter) {
  861.       if (!counter.category)
  862.         return true;
  863.       return !this.categories_[counter.category];
  864.     },
  865.  
  866.     matchSlice: function(slice) {
  867.       if (!slice.category)
  868.         return true;
  869.       return !this.categories_[slice.category];
  870.     }
  871.   };
  872.  
  873.   return {
  874.     filterCounterArray: filterCounterArray,
  875.     filterSliceArray: filterSliceArray,
  876.     TimelineFilter: TimelineFilter,
  877.     TimelineTitleFilter: TimelineTitleFilter,
  878.     TimelineCategoryFilter: TimelineCategoryFilter
  879.   };
  880. });
  881.  
  882. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  883. // Use of this source code is governed by a BSD-style license that can be
  884. // found in the LICENSE file.
  885.  
  886. 'use strict';
  887.  
  888. /**
  889.  * @fileoverview Code for the timeline viewport.
  890.  */
  891. base.require('event_target');
  892. base.exportTo('tracing', function() {
  893.  
  894.   function TimelineSelectionSliceHit(track, slice) {
  895.     this.track = track;
  896.     this.slice = slice;
  897.   }
  898.   TimelineSelectionSliceHit.prototype = {
  899.     get selected() {
  900.       return this.slice.selected;
  901.     },
  902.     set selected(v) {
  903.       this.slice.selected = v;
  904.     }
  905.   };
  906.  
  907.   function TimelineSelectionCounterSampleHit(track, counter, sampleIndex) {
  908.     this.track = track;
  909.     this.counter = counter;
  910.     this.sampleIndex = sampleIndex;
  911.   }
  912.   TimelineSelectionCounterSampleHit.prototype = {
  913.     get selected() {
  914.       return this.track.selectedSamples[this.sampleIndex] == true;
  915.     },
  916.     set selected(v) {
  917.       if (v)
  918.         this.track.selectedSamples[this.sampleIndex] = true;
  919.       else
  920.         this.track.selectedSamples[this.sampleIndex] = false;
  921.       this.track.invalidate();
  922.     }
  923.   };
  924.  
  925.  
  926.   /**
  927.    * Represents a selection within a Timeline and its associated set of tracks.
  928.    * @constructor
  929.    */
  930.   function TimelineSelection() {
  931.     this.range_dirty_ = true;
  932.     this.range_ = {};
  933.     this.length_ = 0;
  934.   }
  935.   TimelineSelection.prototype = {
  936.     __proto__: Object.prototype,
  937.  
  938.     get range() {
  939.       if (this.range_dirty_) {
  940.         var wmin = Infinity;
  941.         var wmax = -wmin;
  942.         for (var i = 0; i < this.length_; i++) {
  943.           var hit = this[i];
  944.           if (hit.slice) {
  945.             wmin = Math.min(wmin, hit.slice.start);
  946.             wmax = Math.max(wmax, hit.slice.end);
  947.           }
  948.         }
  949.         this.range_ = {
  950.           min: wmin,
  951.           max: wmax
  952.         };
  953.         this.range_dirty_ = false;
  954.       }
  955.       return this.range_;
  956.     },
  957.  
  958.     get duration() {
  959.       return this.range.max - this.range.min;
  960.     },
  961.  
  962.     get length() {
  963.       return this.length_;
  964.     },
  965.  
  966.     clear: function() {
  967.       for (var i = 0; i < this.length_; ++i)
  968.         delete this[i];
  969.       this.length_ = 0;
  970.       this.range_dirty_ = true;
  971.     },
  972.  
  973.     pushHit: function(hit) {
  974.       this.push_(hit);
  975.     },
  976.  
  977.     push_: function(hit) {
  978.       this[this.length_++] = hit;
  979.       this.range_dirty_ = true;
  980.       return hit;
  981.     },
  982.  
  983.     addSlice: function(track, slice) {
  984.       return this.push_(new TimelineSelectionSliceHit(track, slice));
  985.     },
  986.  
  987.     addCounterSample: function(track, counter, sampleIndex) {
  988.       return this.push_(
  989.           new TimelineSelectionCounterSampleHit(
  990.           track, counter, sampleIndex));
  991.     },
  992.  
  993.     subSelection: function(index, count) {
  994.       count = count || 1;
  995.  
  996.       var selection = new TimelineSelection();
  997.       selection.range_dirty_ = true;
  998.       if (index < 0 || index + count > this.length_)
  999.         throw new Error('Index out of bounds');
  1000.  
  1001.       for (var i = index; i < index + count; i++)
  1002.         selection.push_(this[i]);
  1003.  
  1004.       return selection;
  1005.     },
  1006.  
  1007.     getCounterSampleHitsAsSelection: function() {
  1008.       var selection = new TimelineSelection();
  1009.       for (var i = 0; i < this.length_; i++)
  1010.         if (this[i] instanceof TimelineSelectionCounterSampleHit)
  1011.           selection.push_(this[i]);
  1012.       return selection;
  1013.     },
  1014.  
  1015.     getSliceHitsAsSelection: function() {
  1016.       var selection = new TimelineSelection();
  1017.       for (var i = 0; i < this.length_; i++)
  1018.         if (this[i] instanceof TimelineSelectionSliceHit)
  1019.           selection.push_(this[i]);
  1020.       return selection;
  1021.     },
  1022.  
  1023.     getNumSliceHits: function() {
  1024.       var numHits = 0;
  1025.       for (var i = 0; i < this.length_; i++)
  1026.         if (this[i] instanceof TimelineSelectionSliceHit)
  1027.           numHits++;
  1028.       return numHits;
  1029.     },
  1030.  
  1031.     getNumCounterHits: function() {
  1032.       var numHits = 0;
  1033.       for (var i = 0; i < this.length_; i++)
  1034.         if (this[i] instanceof TimelineSelectionCounterSampleHit)
  1035.           numHits++;
  1036.       return numHits;
  1037.     },
  1038.  
  1039.     map: function(fn) {
  1040.       for (var i = 0; i < this.length_; i++)
  1041.         fn(this[i]);
  1042.     },
  1043.  
  1044.     /**
  1045.      * Helper for selection previous or next.
  1046.      * @param {boolean} forwardp If true, select one forward (next).
  1047.      *   Else, select previous.
  1048.      * @return {boolean} true if current selection changed.
  1049.      */
  1050.     getShiftedSelection: function(offset) {
  1051.       var newSelection = new TimelineSelection();
  1052.       for (var i = 0; i < this.length_; i++) {
  1053.         var hit = this[i];
  1054.         hit.track.addItemNearToProvidedHitToSelection(
  1055.             hit, offset, newSelection);
  1056.       }
  1057.  
  1058.       if (newSelection.length == 0)
  1059.         return undefined;
  1060.       return newSelection;
  1061.     }
  1062.   };
  1063.  
  1064.   return {
  1065.     TimelineSelectionSliceHit: TimelineSelectionSliceHit,
  1066.     TimelineSelectionCounterSampleHit: TimelineSelectionCounterSampleHit,
  1067.     TimelineSelection: TimelineSelection
  1068.   };
  1069. });
  1070.  
  1071. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1072. // Use of this source code is governed by a BSD-style license that can be
  1073. // found in the LICENSE file.
  1074.  
  1075. 'use strict';
  1076.  
  1077. /**
  1078.  * @fileoverview Code for the timeline viewport.
  1079.  */
  1080. base.require('event_target');
  1081.  
  1082. base.exportTo('tracing', function() {
  1083.  
  1084.   /**
  1085.    * The TimelineViewport manages the transform used for navigating
  1086.    * within the timeline. It is a simple transform:
  1087.    *   x' = (x+pan) * scale
  1088.    *
  1089.    * The timeline code tries to avoid directly accessing this transform,
  1090.    * instead using this class to do conversion between world and viewspace,
  1091.    * as well as the math for centering the viewport in various interesting
  1092.    * ways.
  1093.    *
  1094.    * @constructor
  1095.    * @extends {base.EventTarget}
  1096.    */
  1097.   function TimelineViewport(parentEl) {
  1098.     this.parentEl_ = parentEl;
  1099.     this.scaleX_ = 1;
  1100.     this.panX_ = 0;
  1101.     this.gridTimebase_ = 0;
  1102.     this.gridStep_ = 1000 / 60;
  1103.     this.gridEnabled_ = false;
  1104.     this.hasCalledSetupFunction_ = false;
  1105.  
  1106.     this.onResizeBoundToThis_ = this.onResize_.bind(this);
  1107.  
  1108.     // The following code uses an interval to detect when the parent element
  1109.     // is attached to the document. That is a trigger to run the setup function
  1110.     // and install a resize listener.
  1111.     this.checkForAttachInterval_ = setInterval(
  1112.         this.checkForAttach_.bind(this), 250);
  1113.  
  1114.     this.markers = [];
  1115.   }
  1116.  
  1117.   TimelineViewport.prototype = {
  1118.     __proto__: base.EventTarget.prototype,
  1119.  
  1120.     drawUnderContent: function(ctx, viewLWorld, viewRWorld, canvasH) {
  1121.     },
  1122.  
  1123.     drawOverContent: function(ctx, viewLWorld, viewRWorld, canvasH) {
  1124.       if (this.gridEnabled) {
  1125.         var x = this.gridTimebase;
  1126.  
  1127.         ctx.beginPath();
  1128.         while (x < viewRWorld) {
  1129.           if (x >= viewLWorld) {
  1130.             // Do conversion to viewspace here rather than on
  1131.             // x to avoid precision issues.
  1132.             var vx = this.xWorldToView(x);
  1133.             ctx.moveTo(vx, 0);
  1134.             ctx.lineTo(vx, canvasH);
  1135.           }
  1136.           x += this.gridStep;
  1137.         }
  1138.         ctx.strokeStyle = 'rgba(255,0,0,0.25)';
  1139.         ctx.stroke();
  1140.       }
  1141.  
  1142.       for (var i = 0; i < this.markers.length; ++i) {
  1143.         this.markers[i].drawLine(ctx, viewLWorld, viewRWorld, canvasH, this);
  1144.       }
  1145.     },
  1146.  
  1147.     /**
  1148.      * Allows initialization of the viewport when the viewport's parent element
  1149.      * has been attached to the document and given a size.
  1150.      * @param {Function} fn Function to call when the viewport can be safely
  1151.      * initialized.
  1152.      */
  1153.     setWhenPossible: function(fn) {
  1154.       this.pendingSetFunction_ = fn;
  1155.     },
  1156.  
  1157.     /**
  1158.      * @return {boolean} Whether the current timeline is attached to the
  1159.      * document.
  1160.      */
  1161.     get isAttachedToDocument_() {
  1162.       var cur = this.parentEl_;
  1163.       while (cur.parentNode)
  1164.         cur = cur.parentNode;
  1165.       return cur == this.parentEl_.ownerDocument;
  1166.     },
  1167.  
  1168.     onResize_: function() {
  1169.       this.dispatchChangeEvent();
  1170.     },
  1171.  
  1172.     /**
  1173.      * Checks whether the parentNode is attached to the document.
  1174.      * When it is, it installs the iframe-based resize detection hook
  1175.      * and then runs the pendingSetFunction_, if present.
  1176.      */
  1177.     checkForAttach_: function() {
  1178.       if (!this.isAttachedToDocument_ || this.clientWidth == 0)
  1179.         return;
  1180.  
  1181.       if (!this.iframe_) {
  1182.         this.iframe_ = document.createElement('iframe');
  1183.         this.iframe_.style.cssText =
  1184.             'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
  1185.         this.parentEl_.appendChild(this.iframe_);
  1186.  
  1187.         this.iframe_.contentWindow.addEventListener('resize',
  1188.                                                     this.onResizeBoundToThis_);
  1189.       }
  1190.  
  1191.       var curSize = this.clientWidth + 'x' + this.clientHeight;
  1192.       if (this.pendingSetFunction_) {
  1193.         this.lastSize_ = curSize;
  1194.         try {
  1195.           this.pendingSetFunction_();
  1196.         } catch (ex) {
  1197.           console.log('While running setWhenPossible:', ex);
  1198.         }
  1199.         this.pendingSetFunction_ = undefined;
  1200.       }
  1201.  
  1202.       window.clearInterval(this.checkForAttachInterval_);
  1203.       this.checkForAttachInterval_ = undefined;
  1204.     },
  1205.  
  1206.     /**
  1207.      * Fires the change event on this viewport. Used to notify listeners
  1208.      * to redraw when the underlying model has been mutated.
  1209.      */
  1210.     dispatchChangeEvent: function() {
  1211.       base.dispatchSimpleEvent(this, 'change');
  1212.     },
  1213.  
  1214.     dispatchMarkersChangeEvent_: function() {
  1215.       base.dispatchSimpleEvent(this, 'markersChange');
  1216.     },
  1217.  
  1218.     detach: function() {
  1219.       if (this.checkForAttachInterval_) {
  1220.         window.clearInterval(this.checkForAttachInterval_);
  1221.         this.checkForAttachInterval_ = undefined;
  1222.       }
  1223.       if (this.iframe_) {
  1224.         this.iframe_.removeEventListener('resize', this.onResizeBoundToThis_);
  1225.         this.parentEl_.removeChild(this.iframe_);
  1226.       }
  1227.     },
  1228.  
  1229.     get scaleX() {
  1230.       return this.scaleX_;
  1231.     },
  1232.     set scaleX(s) {
  1233.       var changed = this.scaleX_ != s;
  1234.       if (changed) {
  1235.         this.scaleX_ = s;
  1236.         this.dispatchChangeEvent();
  1237.       }
  1238.     },
  1239.  
  1240.     get panX() {
  1241.       return this.panX_;
  1242.     },
  1243.     set panX(p) {
  1244.       var changed = this.panX_ != p;
  1245.       if (changed) {
  1246.         this.panX_ = p;
  1247.         this.dispatchChangeEvent();
  1248.       }
  1249.     },
  1250.  
  1251.     setPanAndScale: function(p, s) {
  1252.       var changed = this.scaleX_ != s || this.panX_ != p;
  1253.       if (changed) {
  1254.         this.scaleX_ = s;
  1255.         this.panX_ = p;
  1256.         this.dispatchChangeEvent();
  1257.       }
  1258.     },
  1259.  
  1260.     xWorldToView: function(x) {
  1261.       return (x + this.panX_) * this.scaleX_;
  1262.     },
  1263.  
  1264.     xWorldVectorToView: function(x) {
  1265.       return x * this.scaleX_;
  1266.     },
  1267.  
  1268.     xViewToWorld: function(x) {
  1269.       return (x / this.scaleX_) - this.panX_;
  1270.     },
  1271.  
  1272.     xViewVectorToWorld: function(x) {
  1273.       return x / this.scaleX_;
  1274.     },
  1275.  
  1276.     xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
  1277.       if (typeof viewX == 'string') {
  1278.         if (viewX == 'left') {
  1279.           viewX = 0;
  1280.         } else if (viewX == 'center') {
  1281.           viewX = viewWidth / 2;
  1282.         } else if (viewX == 'right') {
  1283.           viewX = viewWidth - 1;
  1284.         } else {
  1285.           throw new Error('unrecognized string for viewPos. left|center|right');
  1286.         }
  1287.       }
  1288.       this.panX = (viewX / this.scaleX_) - worldX;
  1289.     },
  1290.  
  1291.     xPanWorldRangeIntoView: function(worldMin, worldMax, viewWidth) {
  1292.       if (this.xWorldToView(worldMin) < 0)
  1293.         this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
  1294.       else if (this.xWorldToView(worldMax) > viewWidth)
  1295.         this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
  1296.     },
  1297.  
  1298.     xSetWorldRange: function(worldMin, worldMax, viewWidth) {
  1299.       var worldRange = worldMax - worldMin;
  1300.       var scaleX = viewWidth / worldRange;
  1301.       var panX = -worldMin;
  1302.       this.setPanAndScale(panX, scaleX);
  1303.     },
  1304.  
  1305.     get gridEnabled() {
  1306.       return this.gridEnabled_;
  1307.     },
  1308.  
  1309.     set gridEnabled(enabled) {
  1310.       if (this.gridEnabled_ == enabled)
  1311.         return;
  1312.       this.gridEnabled_ = enabled && true;
  1313.       this.dispatchChangeEvent();
  1314.     },
  1315.  
  1316.     get gridTimebase() {
  1317.       return this.gridTimebase_;
  1318.     },
  1319.  
  1320.     set gridTimebase(timebase) {
  1321.       if (this.gridTimebase_ == timebase)
  1322.         return;
  1323.       this.gridTimebase_ = timebase;
  1324.       base.dispatchSimpleEvent(this, 'change');
  1325.     },
  1326.  
  1327.     get gridStep() {
  1328.       return this.gridStep_;
  1329.     },
  1330.  
  1331.     applyTransformToCanvas: function(ctx) {
  1332.       ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
  1333.     },
  1334.  
  1335.     addMarker: function(positionWorld) {
  1336.       var marker = new TimelineViewportMarker(this, positionWorld);
  1337.       this.markers.push(marker);
  1338.       this.dispatchChangeEvent();
  1339.       this.dispatchMarkersChangeEvent_();
  1340.       return marker;
  1341.     },
  1342.  
  1343.     removeMarker: function(marker) {
  1344.       for (var i = 0; i < this.markers.length; ++i) {
  1345.         if (this.markers[i] === marker) {
  1346.           this.markers.splice(i, 1);
  1347.           this.dispatchChangeEvent();
  1348.           this.dispatchMarkersChangeEvent_();
  1349.           return true;
  1350.         }
  1351.       }
  1352.     },
  1353.  
  1354.     findMarkerNear: function(positionWorld, nearnessInViewPixels) {
  1355.       // Converts pixels into distance in world.
  1356.       var nearnessThresholdWorld = this.xViewVectorToWorld(
  1357.           nearnessInViewPixels);
  1358.       for (var i = 0; i < this.markers.length; ++i) {
  1359.         if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
  1360.             nearnessThresholdWorld) {
  1361.           var marker = this.markers[i];
  1362.           return marker;
  1363.         }
  1364.       }
  1365.       return undefined;
  1366.     }
  1367.   };
  1368.  
  1369.   /**
  1370.    * Represents a marked position in the world, at a viewport level.
  1371.    * @constructor
  1372.    */
  1373.   function TimelineViewportMarker(vp, positionWorld) {
  1374.     this.viewport_ = vp;
  1375.     this.positionWorld_ = positionWorld;
  1376.     this.selected_ = false;
  1377.   }
  1378.  
  1379.   TimelineViewportMarker.prototype = {
  1380.     get positionWorld() {
  1381.       return this.positionWorld_;
  1382.     },
  1383.  
  1384.     set positionWorld(positionWorld) {
  1385.       this.positionWorld_ = positionWorld;
  1386.       this.viewport_.dispatchChangeEvent();
  1387.     },
  1388.  
  1389.     set selected(selected) {
  1390.       this.selected_ = selected;
  1391.       this.viewport_.dispatchChangeEvent();
  1392.     },
  1393.  
  1394.     get selected() {
  1395.       return this.selected_;
  1396.     },
  1397.  
  1398.     get color() {
  1399.       if (this.selected)
  1400.         return 'rgb(255,0,0)';
  1401.       return 'rgb(0,0,0)';
  1402.     },
  1403.  
  1404.     drawTriangle_: function(ctx, viewLWorld, viewRWorld,
  1405.                             canvasH, rulerHeight, vp) {
  1406.       ctx.beginPath();
  1407.       var ts = this.positionWorld_;
  1408.       if (ts >= viewLWorld && ts < viewRWorld) {
  1409.         var viewX = vp.xWorldToView(ts);
  1410.         ctx.moveTo(viewX, rulerHeight);
  1411.         ctx.lineTo(viewX - 3, rulerHeight / 2);
  1412.         ctx.lineTo(viewX + 3, rulerHeight / 2);
  1413.         ctx.lineTo(viewX, rulerHeight);
  1414.         ctx.closePath();
  1415.         ctx.fillStyle = this.color;
  1416.         ctx.fill();
  1417.         if (rulerHeight != canvasH) {
  1418.           ctx.beginPath();
  1419.           ctx.moveTo(viewX, rulerHeight);
  1420.           ctx.lineTo(viewX, canvasH);
  1421.           ctx.closePath();
  1422.           ctx.strokeStyle = this.color;
  1423.           ctx.stroke();
  1424.         }
  1425.       }
  1426.     },
  1427.  
  1428.     drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
  1429.       ctx.beginPath();
  1430.       var ts = this.positionWorld_;
  1431.       if (ts >= viewLWorld && ts < viewRWorld) {
  1432.         var viewX = vp.xWorldToView(ts);
  1433.         ctx.moveTo(viewX, 0);
  1434.         ctx.lineTo(viewX, canvasH);
  1435.       }
  1436.       ctx.strokeStyle = this.color;
  1437.       ctx.stroke();
  1438.     }
  1439.   };
  1440.  
  1441.   return {
  1442.     TimelineViewport: TimelineViewport,
  1443.     TimelineViewportMarker: TimelineViewportMarker
  1444.   };
  1445. });
  1446.  
  1447. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1448. // Use of this source code is governed by a BSD-style license that can be
  1449. // found in the LICENSE file.
  1450.  
  1451. base.exportTo('base.ui', function() {
  1452.  
  1453.   /**
  1454.    * Decorates elements as an instance of a class.
  1455.    * @param {string|!Element} source The way to find the element(s) to decorate.
  1456.    *     If this is a string then {@code querySeletorAll} is used to find the
  1457.    *     elements to decorate.
  1458.    * @param {!Function} constr The constructor to decorate with. The constr
  1459.    *     needs to have a {@code decorate} function.
  1460.    */
  1461.   function decorate(source, constr) {
  1462.     var elements;
  1463.     if (typeof source == 'string')
  1464.       elements = base.doc.querySelectorAll(source);
  1465.     else
  1466.       elements = [source];
  1467.  
  1468.     for (var i = 0, el; el = elements[i]; i++) {
  1469.       if (!(el instanceof constr))
  1470.         constr.decorate(el);
  1471.     }
  1472.   }
  1473.  
  1474.   /**
  1475.    * Helper function for creating new element for define.
  1476.    */
  1477.   function createElementHelper(tagName, opt_bag) {
  1478.     // Allow passing in ownerDocument to create in a different document.
  1479.     var doc;
  1480.     if (opt_bag && opt_bag.ownerDocument)
  1481.       doc = opt_bag.ownerDocument;
  1482.     else
  1483.       doc = base.doc;
  1484.     return doc.createElement(tagName);
  1485.   }
  1486.  
  1487.   /**
  1488.    * Creates the constructor for a UI element class.
  1489.    *
  1490.    * Usage:
  1491.    * <pre>
  1492.    * var List = base.ui.define('list');
  1493.    * List.prototype = {
  1494.    *   __proto__: HTMLUListElement.prototype,
  1495.    *   decorate: function() {
  1496.    *     ...
  1497.    *   },
  1498.    *   ...
  1499.    * };
  1500.    * </pre>
  1501.    *
  1502.    * @param {string|Function} tagNameOrFunction The tagName or
  1503.    *     function to use for newly created elements. If this is a function it
  1504.    *     needs to return a new element when called.
  1505.    * @return {function(Object=):Element} The constructor function which takes
  1506.    *     an optional property bag. The function also has a static
  1507.    *     {@code decorate} method added to it.
  1508.    */
  1509.   function define(tagNameOrFunction) {
  1510.     var createFunction, tagName;
  1511.     if (typeof tagNameOrFunction == 'function') {
  1512.       createFunction = tagNameOrFunction;
  1513.       tagName = '';
  1514.     } else {
  1515.       createFunction = createElementHelper;
  1516.       tagName = tagNameOrFunction;
  1517.     }
  1518.  
  1519.     /**
  1520.      * Creates a new UI element constructor.
  1521.      * @param {Object=} opt_propertyBag Optional bag of properties to set on the
  1522.      *     object after created. The property {@code ownerDocument} is special
  1523.      *     cased and it allows you to create the element in a different
  1524.      *     document than the default.
  1525.      * @constructor
  1526.      */
  1527.     function f(opt_propertyBag) {
  1528.       var el = createFunction(tagName, opt_propertyBag);
  1529.       f.decorate(el);
  1530.       for (var propertyName in opt_propertyBag) {
  1531.         el[propertyName] = opt_propertyBag[propertyName];
  1532.       }
  1533.       return el;
  1534.     }
  1535.  
  1536.     /**
  1537.      * Decorates an element as a UI element class.
  1538.      * @param {!Element} el The element to decorate.
  1539.      */
  1540.     f.decorate = function(el) {
  1541.       el.__proto__ = f.prototype;
  1542.       el.decorate();
  1543.     };
  1544.  
  1545.     return f;
  1546.   }
  1547.  
  1548.   /**
  1549.    * Input elements do not grow and shrink with their content. This is a simple
  1550.    * (and not very efficient) way of handling shrinking to content with support
  1551.    * for min width and limited by the width of the parent element.
  1552.    * @param {HTMLElement} el The element to limit the width for.
  1553.    * @param {number} parentEl The parent element that should limit the size.
  1554.    * @param {number} min The minimum width.
  1555.    */
  1556.   function limitInputWidth(el, parentEl, min) {
  1557.     // Needs a size larger than borders
  1558.     el.style.width = '10px';
  1559.     var doc = el.ownerDocument;
  1560.     var win = doc.defaultView;
  1561.     var computedStyle = win.getComputedStyle(el);
  1562.     var parentComputedStyle = win.getComputedStyle(parentEl);
  1563.     var rtl = computedStyle.direction == 'rtl';
  1564.  
  1565.     // To get the max width we get the width of the treeItem minus the position
  1566.     // of the input.
  1567.     var inputRect = el.getBoundingClientRect();  // box-sizing
  1568.     var parentRect = parentEl.getBoundingClientRect();
  1569.     var startPos = rtl ? parentRect.right - inputRect.right :
  1570.         inputRect.left - parentRect.left;
  1571.  
  1572.     // Add up border and padding of the input.
  1573.     var inner = parseInt(computedStyle.borderLeftWidth, 10) +
  1574.         parseInt(computedStyle.paddingLeft, 10) +
  1575.         parseInt(computedStyle.paddingRight, 10) +
  1576.         parseInt(computedStyle.borderRightWidth, 10);
  1577.  
  1578.     // We also need to subtract the padding of parent to prevent it to overflow.
  1579.     var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) :
  1580.         parseInt(parentComputedStyle.paddingRight, 10);
  1581.  
  1582.     var max = parentEl.clientWidth - startPos - inner - parentPadding;
  1583.  
  1584.     function limit() {
  1585.       if (el.scrollWidth > max) {
  1586.         el.style.width = max + 'px';
  1587.       } else {
  1588.         el.style.width = 0;
  1589.         var sw = el.scrollWidth;
  1590.         if (sw < min) {
  1591.           el.style.width = min + 'px';
  1592.         } else {
  1593.           el.style.width = sw + 'px';
  1594.         }
  1595.       }
  1596.     }
  1597.  
  1598.     el.addEventListener('input', limit);
  1599.     limit();
  1600.   }
  1601.  
  1602.   /**
  1603.    * Takes a number and spits out a value CSS will be happy with. To avoid
  1604.    * subpixel layout issues, the value is rounded to the nearest integral value.
  1605.    * @param {number} pixels The number of pixels.
  1606.    * @return {string} e.g. '16px'.
  1607.    */
  1608.   function toCssPx(pixels) {
  1609.     if (!window.isFinite(pixels))
  1610.       console.error('Pixel value is not a number: ' + pixels);
  1611.     return Math.round(pixels) + 'px';
  1612.   }
  1613.  
  1614.   return {
  1615.     decorate: decorate,
  1616.     define: define,
  1617.     limitInputWidth: limitInputWidth,
  1618.     toCssPx: toCssPx
  1619.   };
  1620. });
  1621.  
  1622. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1623. // Use of this source code is governed by a BSD-style license that can be
  1624. // found in the LICENSE file.
  1625.  
  1626.  
  1627. /**
  1628.  * @fileoverview Renders an array of slices into the provided div,
  1629.  * using a child canvas element. Uses a FastRectRenderer to draw only
  1630.  * the visible slices.
  1631.  */
  1632. base.requireStylesheet('tracks.timeline_track');
  1633. base.require('ui');
  1634. base.exportTo('tracks', function() {
  1635.  
  1636.   /**
  1637.    * The base class for all tracks.
  1638.    * @constructor
  1639.    */
  1640.   var TimelineTrack = base.ui.define('div');
  1641.   TimelineTrack.prototype = {
  1642.     __proto__: HTMLDivElement.prototype,
  1643.  
  1644.     decorate: function() {
  1645.     },
  1646.  
  1647.     get visible() {
  1648.       return this.style.display !== 'none';
  1649.     },
  1650.  
  1651.     set visible(v) {
  1652.       this.style.display = (v ? '' : 'none');
  1653.     },
  1654.  
  1655.     get numVisibleTracks() {
  1656.       return (this.visible ? 1 : 0);
  1657.     },
  1658.  
  1659.     addControlButtonElements_: function(canCollapse) {
  1660.       var closeEl = document.createElement('div');
  1661.       closeEl.classList.add('timeline-track-button');
  1662.       closeEl.classList.add('timeline-track-close-button');
  1663.       closeEl.textContent = String.fromCharCode(215); // ×
  1664.       var that = this;
  1665.       closeEl.addEventListener('click', function() {
  1666.         that.style.display = 'None';
  1667.       });
  1668.       this.appendChild(closeEl);
  1669.  
  1670.       var collapseEl = document.createElement('div');
  1671.       collapseEl.classList.add('timeline-track-button');
  1672.       collapseEl.classList.add('timeline-track-collapse-button');
  1673.       var minus = '\u2212'; // minus sign;
  1674.       var plus = '\u002b'; // plus sign;
  1675.       collapseEl.textContent = minus;
  1676.       var collapsed = false;
  1677.       collapseEl.addEventListener('click', function() {
  1678.         collapsed = !collapsed;
  1679.         this.collapsedDidChange(collapsed);
  1680.         collapseEl.textContent = collapsed ? plus : minus;
  1681.       });
  1682.       this.appendChild(collapseEl);
  1683.       if (!canCollapse)
  1684.         collapseEl.style.display = 'None';
  1685.     }
  1686.   };
  1687.  
  1688.   return {
  1689.     TimelineTrack: TimelineTrack
  1690.   };
  1691. });
  1692.  
  1693. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1694. // Use of this source code is governed by a BSD-style license that can be
  1695. // found in the LICENSE file.
  1696.  
  1697. 'use strict';
  1698.  
  1699. base.require('tracks.timeline_track');
  1700. base.require('timeline_filter');
  1701. base.require('ui');
  1702. base.exportTo('tracks', function() {
  1703.  
  1704.   /**
  1705.    * A generic track that contains other tracks as its children.
  1706.    * @constructor
  1707.    */
  1708.   var TimelineContainerTrack = base.ui.define(tracks.TimelineTrack);
  1709.   TimelineContainerTrack.prototype = {
  1710.     __proto__: tracks.TimelineTrack.prototype,
  1711.  
  1712.     decorate: function() {
  1713.       this.categoryFilter_ = new tracing.TimelineFilter();
  1714.       this.headingWidth_ = undefined;
  1715.       this.tracks_ = [];
  1716.     },
  1717.  
  1718.     detach: function() {
  1719.       this.detachAllChildren();
  1720.     },
  1721.  
  1722.     detachAllChildren: function() {
  1723.       for (var i = 0; i < this.tracks_.length; i++)
  1724.         this.tracks_[i].detach();
  1725.       this.tracks_ = [];
  1726.       this.textContent = '';
  1727.     },
  1728.  
  1729.     get viewport() {
  1730.       return this.viewport_;
  1731.     },
  1732.  
  1733.     set viewport(v) {
  1734.       this.viewport_ = v;
  1735.       for (var i = 0; i < this.tracks_.length; i++)
  1736.         this.tracks_[i].viewport = v;
  1737.     },
  1738.  
  1739.     get firstCanvas() {
  1740.       for (var i = 0; i < this.tracks_.length; i++)
  1741.         if (this.tracks_[i].visible)
  1742.           return this.tracks_[i].firstCanvas;
  1743.       return undefined;
  1744.     },
  1745.  
  1746.     // The number of tracks actually displayed.
  1747.     get numVisibleTracks() {
  1748.       if (!this.visible)
  1749.         return 0;
  1750.       return this.numVisibleChildTracks;
  1751.     },
  1752.  
  1753.     // The number of tracks that would be displayed if this track were visible.
  1754.     get numVisibleChildTracks() {
  1755.       var sum = 0;
  1756.       for (var i = 0; i < this.tracks_.length; ++i) {
  1757.         sum += this.tracks_[i].numVisibleTracks;
  1758.       }
  1759.       return sum;
  1760.     },
  1761.  
  1762.     get headingWidth() {
  1763.       return this.headingWidth_;
  1764.     },
  1765.  
  1766.     set headingWidth(w) {
  1767.       this.headingWidth_ = w;
  1768.       for (var i = 0; i < this.tracks_.length; ++i) {
  1769.         this.tracks_[i].headingWidth = w;
  1770.       }
  1771.     },
  1772.  
  1773.     get categoryFilter() {
  1774.       return this.categoryFilter_;
  1775.     },
  1776.  
  1777.     set categoryFilter(v) {
  1778.       this.categoryFilter_ = v;
  1779.       for (var i = 0; i < this.tracks_.length; ++i) {
  1780.         this.tracks_[i].categoryFilter = v;
  1781.       }
  1782.       this.applyCategoryFilter_();
  1783.       this.updateFirstVisibleChildCSS();
  1784.     },
  1785.  
  1786.     applyCategoryFilter_: function() {
  1787.     },
  1788.  
  1789.     addTrack_: function(track) {
  1790.       track.headingWidth = this.headingWidth_;
  1791.       track.viewport = this.viewport_;
  1792.       track.categoryFilter = this.categoryFilter;
  1793.  
  1794.       this.tracks_.push(track);
  1795.       this.appendChild(track);
  1796.       return track;
  1797.     },
  1798.  
  1799.     updateFirstVisibleChildCSS: function() {
  1800.       var isFirst = true;
  1801.       for (var i = 0; i < this.tracks_.length; ++i) {
  1802.         var track = this.tracks_[i];
  1803.         if (isFirst && track.visible) {
  1804.           track.classList.add('first-visible-child');
  1805.           isFirst = false;
  1806.         } else {
  1807.           track.classList.remove('first-visible-child');
  1808.         }
  1809.       }
  1810.     },
  1811.  
  1812.     /**
  1813.      * Adds items intersecting a point to a selection.
  1814.      * @param {number} vX X location to search at, in viewspace.
  1815.      * @param {number} vY Y location to search at, in viewspace.
  1816.      * @param {TimelineSelection} selection Selection to which to add hits.
  1817.      * @return {boolean} true if a slice was found, otherwise false.
  1818.      */
  1819.     addIntersectingItemsToSelection: function(vX, vY, selection) {
  1820.       for (var i = 0; i < this.tracks_.length; i++) {
  1821.         var trackClientRect = this.tracks_[i].getBoundingClientRect();
  1822.         if (vY >= trackClientRect.top && vY < trackClientRect.bottom)
  1823.           this.tracks_[i].addIntersectingItemsToSelection(vX, vY, selection);
  1824.       }
  1825.       return false;
  1826.     },
  1827.  
  1828.     /**
  1829.      * Adds items intersecting the given range to a selection.
  1830.      * @param {number} loVX Lower X bound of the interval to search, in
  1831.      *     viewspace.
  1832.      * @param {number} hiVX Upper X bound of the interval to search, in
  1833.      *     viewspace.
  1834.      * @param {number} loY Lower Y bound of the interval to search, in
  1835.      *     viewspace space.
  1836.      * @param {number} hiY Upper Y bound of the interval to search, in
  1837.      *     viewspace space.
  1838.      * @param {TimelineSelection} selection Selection to which to add hits.
  1839.      */
  1840.     addIntersectingItemsInRangeToSelection: function(
  1841.         loVX, hiVX, loY, hiY, selection) {
  1842.       for (var i = 0; i < this.tracks_.length; i++) {
  1843.         var trackClientRect = this.tracks_[i].getBoundingClientRect();
  1844.         var a = Math.max(loY, trackClientRect.top);
  1845.         var b = Math.min(hiY, trackClientRect.bottom);
  1846.         if (a <= b)
  1847.           this.tracks_[i].addIntersectingItemsInRangeToSelection(
  1848.               loVX, hiVX, loY, hiY, selection);
  1849.       }
  1850.     },
  1851.  
  1852.     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
  1853.       for (var i = 0; i < this.tracks_.length; i++)
  1854.         this.tracks_[i].addAllObjectsMatchingFilterToSelection(
  1855.             filter, selection);
  1856.     }
  1857.   };
  1858.  
  1859.   return {
  1860.     TimelineContainerTrack: TimelineContainerTrack
  1861.   };
  1862. });
  1863.  
  1864. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1865. // Use of this source code is governed by a BSD-style license that can be
  1866. // found in the LICENSE file.
  1867.  
  1868.  
  1869. /**
  1870.  * @fileoverview Provides a mechanism for drawing massive numbers of
  1871.  * colored rectangles into a canvas in an efficient manner, provided
  1872.  * they are drawn left to right with fixed y and height throughout.
  1873.  *
  1874.  * The basic idea used here is to fuse subpixel rectangles together so that
  1875.  * we never issue a canvas fillRect for them. It turns out Javascript can
  1876.  * do this quite efficiently, compared to asking Canvas2D to do the same.
  1877.  *
  1878.  * A few extra things are done by this class in the name of speed:
  1879.  * - Viewport culling: off-viewport rectangles are discarded.
  1880.  *
  1881.  * - The actual discarding operation is done in world space,
  1882.  *   e.g. pre-transform.
  1883.  *
  1884.  * - Rather than expending compute cycles trying to figure out an average
  1885.  *   color for fused rectangles from css strings, you instead draw using
  1886.  *   palletized colors. The fused rect is the max pallete index encountered.
  1887.  *
  1888.  * Make sure to flush the trackRenderer before finishing drawing in order
  1889.  * to commit any queued drawing operations.
  1890.  */
  1891. base.exportTo('tracing', function() {
  1892.  
  1893.   /**
  1894.    * Creates a fast rect renderer with a specific set of culling rules
  1895.    * and color pallette.
  1896.    * @param {GraphicsContext2D} ctx Canvas2D drawing context.
  1897.    * @param {number} minRectSize Only rectangles with width < minRectSize are
  1898.    *    considered for merging.
  1899.    * @param {number} maxMergeDist Controls how many successive small rectangles
  1900.    *    can be merged together before issuing a rectangle.
  1901.    * @param {Array} pallette The color pallete for drawing. Pallette slots
  1902.    *    should map to valid Canvas fillStyle strings.
  1903.    *
  1904.    * @constructor
  1905.    */
  1906.   function FastRectRenderer(ctx, minRectSize, maxMergeDist, pallette) {
  1907.     this.ctx_ = ctx;
  1908.     this.minRectSize_ = minRectSize;
  1909.     this.maxMergeDist_ = maxMergeDist;
  1910.     this.pallette_ = pallette;
  1911.   }
  1912.  
  1913.   FastRectRenderer.prototype = {
  1914.     y_: 0,
  1915.     h_: 0,
  1916.     merging_: false,
  1917.     mergeStartX_: 0,
  1918.     mergeCurRight_: 0,
  1919.  
  1920.     /**
  1921.      * Changes the y position and height for subsequent fillRect
  1922.      * calls. x and width are specifieid on the fillRect calls.
  1923.      */
  1924.     setYandH: function(y, h) {
  1925.       this.flush();
  1926.       this.y_ = y;
  1927.       this.h_ = h;
  1928.     },
  1929.  
  1930.     /**
  1931.      * Fills rectangle at the specified location, if visible. If the
  1932.      * rectangle is subpixel, it will be merged with adjacent rectangles.
  1933.      * The drawing operation may not take effect until flush is called.
  1934.      * @param {number} colorId The color of this rectangle, as an index
  1935.      *     in the renderer's color pallete.
  1936.      */
  1937.     fillRect: function(x, w, colorId) {
  1938.       var r = x + w;
  1939.       if (w < this.minRectSize_) {
  1940.         if (r - this.mergeStartX_ > this.maxMergeDist_)
  1941.           this.flush();
  1942.         if (!this.merging_) {
  1943.           this.merging_ = true;
  1944.           this.mergeStartX_ = x;
  1945.           this.mergeCurRight_ = r;
  1946.           this.mergedColorId = colorId;
  1947.         } else {
  1948.           this.mergeCurRight_ = r;
  1949.           this.mergedColorId = Math.max(this.mergedColorId, colorId);
  1950.         }
  1951.       } else {
  1952.         if (this.merging_)
  1953.           this.flush();
  1954.         this.ctx_.fillStyle = this.pallette_[colorId];
  1955.         this.ctx_.fillRect(x, this.y_, w, this.h_);
  1956.       }
  1957.     },
  1958.  
  1959.     /**
  1960.      * Commits any pending fillRect operations to the underlying graphics
  1961.      * context.
  1962.      */
  1963.     flush: function() {
  1964.       if (this.merging_) {
  1965.         this.ctx_.fillStyle = this.pallette_[this.mergedColorId];
  1966.         this.ctx_.fillRect(this.mergeStartX_, this.y_,
  1967.                            this.mergeCurRight_ - this.mergeStartX_, this.h_);
  1968.         this.merging_ = false;
  1969.       }
  1970.     }
  1971.   };
  1972.  
  1973.   return {
  1974.     FastRectRenderer: FastRectRenderer
  1975.   };
  1976.  
  1977. });
  1978.  
  1979. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  1980. // Use of this source code is governed by a BSD-style license that can be
  1981. // found in the LICENSE file.
  1982.  
  1983. 'use strict';
  1984.  
  1985. /**
  1986.  * @fileoverview Provides color scheme related functions.
  1987.  */
  1988. base.exportTo('tracing', function() {
  1989.  
  1990.   // The color palette is split in half, with the upper
  1991.   // half of the palette being the "highlighted" verison
  1992.   // of the base color. So, color 7's highlighted form is
  1993.   // 7 + (palette.length / 2).
  1994.   //
  1995.   // These bright versions of colors are automatically generated
  1996.   // from the base colors.
  1997.   //
  1998.   // Within the color palette, there are "regular" colors,
  1999.   // which can be used for random color selection, and
  2000.   // reserved colors, which are used when specific colors
  2001.   // need to be used, e.g. where red is desired.
  2002.   var paletteBase = [
  2003.     {r: 138, g: 113, b: 152},
  2004.     {r: 175, g: 112, b: 133},
  2005.     {r: 127, g: 135, b: 225},
  2006.     {r: 93, g: 81, b: 137},
  2007.     {r: 116, g: 143, b: 119},
  2008.     {r: 178, g: 214, b: 122},
  2009.     {r: 87, g: 109, b: 147},
  2010.     {r: 119, g: 155, b: 95},
  2011.     {r: 114, g: 180, b: 160},
  2012.     {r: 132, g: 85, b: 103},
  2013.     {r: 157, g: 210, b: 150},
  2014.     {r: 148, g: 94, b: 86},
  2015.     {r: 164, g: 108, b: 138},
  2016.     {r: 139, g: 191, b: 150},
  2017.     {r: 110, g: 99, b: 145},
  2018.     {r: 80, g: 129, b: 109},
  2019.     {r: 125, g: 140, b: 149},
  2020.     {r: 93, g: 124, b: 132},
  2021.     {r: 140, g: 85, b: 140},
  2022.     {r: 104, g: 163, b: 162},
  2023.     {r: 132, g: 141, b: 178},
  2024.     {r: 131, g: 105, b: 147},
  2025.     {r: 135, g: 183, b: 98},
  2026.     {r: 152, g: 134, b: 177},
  2027.     {r: 141, g: 188, b: 141},
  2028.     {r: 133, g: 160, b: 210},
  2029.     {r: 126, g: 186, b: 148},
  2030.     {r: 112, g: 198, b: 205},
  2031.     {r: 180, g: 122, b: 195},
  2032.     {r: 203, g: 144, b: 152},
  2033.     // Reserved Entires
  2034.     {r: 182, g: 125, b: 143},
  2035.     {r: 126, g: 200, b: 148},
  2036.     {r: 133, g: 160, b: 210},
  2037.     {r: 240, g: 240, b: 240}];
  2038.  
  2039.   // Make sure this number tracks the number of reserved entries in the
  2040.   // palette.
  2041.   var numReservedColorIds = 4;
  2042.  
  2043.   function brighten(c) {
  2044.     var k;
  2045.     if (c.r >= 240 && c.g >= 240 && c.b >= 240)
  2046.       k = -0.20;
  2047.     else
  2048.       k = 0.45;
  2049.  
  2050.     return {r: Math.min(255, c.r + Math.floor(c.r * k)),
  2051.       g: Math.min(255, c.g + Math.floor(c.g * k)),
  2052.       b: Math.min(255, c.b + Math.floor(c.b * k))};
  2053.   }
  2054.   function colorToString(c) {
  2055.     return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
  2056.   }
  2057.  
  2058.   /**
  2059.    * The number of color IDs that getStringColorId can choose from.
  2060.    */
  2061.   var numRegularColorIds = paletteBase.length - numReservedColorIds;
  2062.   var highlightIdBoost = paletteBase.length;
  2063.  
  2064.   var palette = paletteBase.concat(paletteBase.map(brighten)).
  2065.       map(colorToString);
  2066.   /**
  2067.    * Computes a simplistic hashcode of the provide name. Used to chose colors
  2068.    * for slices.
  2069.    * @param {string} name The string to hash.
  2070.    */
  2071.   function getStringHash(name) {
  2072.     var hash = 0;
  2073.     for (var i = 0; i < name.length; ++i)
  2074.       hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF;
  2075.     return hash;
  2076.   }
  2077.  
  2078.   /**
  2079.    * Gets the color palette.
  2080.    */
  2081.   function getColorPalette() {
  2082.     return palette;
  2083.   }
  2084.  
  2085.   /**
  2086.    * @return {Number} The value to add to a color ID to get its highlighted
  2087.    * colro ID. E.g. 7 + getPaletteHighlightIdBoost() yields a brightened from
  2088.    * of 7's base color.
  2089.    */
  2090.   function getColorPaletteHighlightIdBoost() {
  2091.     return highlightIdBoost;
  2092.   }
  2093.  
  2094.   /**
  2095.    * @param {String} name The color name.
  2096.    * @return {Number} The color ID for the given color name.
  2097.    */
  2098.   function getColorIdByName(name) {
  2099.     if (name == 'iowait')
  2100.       return numRegularColorIds;
  2101.     if (name == 'running')
  2102.       return numRegularColorIds + 1;
  2103.     if (name == 'runnable')
  2104.       return numRegularColorIds + 2;
  2105.     if (name == 'sleeping')
  2106.       return numRegularColorIds + 3;
  2107.     throw new Error('Unrecognized color ') + name;
  2108.   }
  2109.  
  2110.   // Previously computed string color IDs. They are based on a stable hash, so
  2111.   // it is safe to save them throughout the program time.
  2112.   var stringColorIdCache = {};
  2113.  
  2114.   /**
  2115.    * @return {Number} A color ID that is stably associated to the provided via
  2116.    * the getStringHash method. The color ID will be chosen from the regular
  2117.    * ID space only, e.g. no reserved ID will be used.
  2118.    */
  2119.   function getStringColorId(string) {
  2120.     if (stringColorIdCache[string] === undefined) {
  2121.       var hash = getStringHash(string);
  2122.       stringColorIdCache[string] = hash % numRegularColorIds;
  2123.     }
  2124.     return stringColorIdCache[string];
  2125.   }
  2126.  
  2127.   return {
  2128.     getColorPalette: getColorPalette,
  2129.     getColorPaletteHighlightIdBoost: getColorPaletteHighlightIdBoost,
  2130.     getColorIdByName: getColorIdByName,
  2131.     getStringHash: getStringHash,
  2132.     getStringColorId: getStringColorId
  2133.   };
  2134. });
  2135.  
  2136. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2137. // Use of this source code is governed by a BSD-style license that can be
  2138. // found in the LICENSE file.
  2139.  
  2140. 'use strict';
  2141.  
  2142. base.requireStylesheet('tracks.timeline_canvas_based_track');
  2143. base.require('tracks.timeline_track');
  2144. base.require('fast_rect_renderer');
  2145. base.require('timeline_color_scheme');
  2146. base.require('ui');
  2147.  
  2148. base.exportTo('tracks', function() {
  2149.  
  2150.   /**
  2151.    * A canvas-based track constructed. Provides the basic heading and
  2152.    * invalidation-managment infrastructure. Subclasses must implement drawing
  2153.    * and picking code.
  2154.    * @constructor
  2155.    * @extends {HTMLDivElement}
  2156.    */
  2157.   var TimelineCanvasBasedTrack = base.ui.define(tracks.TimelineTrack);
  2158.  
  2159.   TimelineCanvasBasedTrack.prototype = {
  2160.     __proto__: tracks.TimelineTrack.prototype,
  2161.  
  2162.     decorate: function() {
  2163.       this.className = 'timeline-canvas-based-track';
  2164.       this.slices_ = null;
  2165.  
  2166.       this.headingDiv_ = document.createElement('div');
  2167.       this.headingDiv_.className = 'timeline-canvas-based-track-title';
  2168.       this.appendChild(this.headingDiv_);
  2169.  
  2170.       this.canvasContainer_ = document.createElement('div');
  2171.       this.canvasContainer_.className =
  2172.           'timeline-canvas-based-track-canvas-container';
  2173.       this.appendChild(this.canvasContainer_);
  2174.       this.canvas_ = document.createElement('canvas');
  2175.       this.canvas_.className = 'timeline-canvas-based-track-canvas';
  2176.       this.canvasContainer_.appendChild(this.canvas_);
  2177.  
  2178.       this.ctx_ = this.canvas_.getContext('2d');
  2179.     },
  2180.  
  2181.     detach: function() {
  2182.       if (this.viewport_) {
  2183.         this.viewport_.removeEventListener('change',
  2184.                                            this.viewportChangeBoundToThis_);
  2185.         this.viewport_.removeEventListener('markersChange',
  2186.             this.viewportMarkersChangeBoundToThis_);
  2187.       }
  2188.     },
  2189.  
  2190.     set headingWidth(width) {
  2191.       this.headingDiv_.style.width = width;
  2192.     },
  2193.  
  2194.     get heading() {
  2195.       return this.headingDiv_.textContent;
  2196.     },
  2197.  
  2198.     set heading(text) {
  2199.       this.headingDiv_.textContent = text;
  2200.     },
  2201.  
  2202.     set tooltip(text) {
  2203.       this.headingDiv_.title = text;
  2204.     },
  2205.  
  2206.     get viewport() {
  2207.       return this.viewport_;
  2208.     },
  2209.  
  2210.     set viewport(v) {
  2211.       this.viewport_ = v;
  2212.       if (this.viewport_) {
  2213.         this.viewport_.removeEventListener('change',
  2214.                                            this.viewportChangeBoundToThis_);
  2215.         this.viewport_.removeEventListener('markersChange',
  2216.             this.viewportMarkersChangeBoundToThis_);
  2217.       }
  2218.       this.viewport_ = v;
  2219.       if (this.viewport_) {
  2220.         this.viewportChangeBoundToThis_ = this.viewportChange_.bind(this);
  2221.         this.viewport_.addEventListener('change',
  2222.                                         this.viewportChangeBoundToThis_);
  2223.         this.viewportMarkersChangeBoundToThis_ =
  2224.             this.viewportMarkersChange_.bind(this);
  2225.         this.viewport_.addEventListener('markersChange',
  2226.                                         this.viewportMarkersChangeBoundToThis_);
  2227.         if (this.isAttachedToDocument_)
  2228.           this.updateCanvasSizeIfNeeded_();
  2229.       }
  2230.       this.invalidate();
  2231.     },
  2232.  
  2233.     viewportChange_: function() {
  2234.       this.invalidate();
  2235.     },
  2236.  
  2237.     viewportMarkersChange_: function() {
  2238.       if (this.viewport_.markers.length < 2)
  2239.         this.classList.remove('timeline-viewport-track-with' +
  2240.             '-distance-measurements');
  2241.       else
  2242.         this.classList.add('timeline-viewport-track-with' +
  2243.             '-distance-measurements');
  2244.     },
  2245.  
  2246.     invalidate: function() {
  2247.       if (this.rafPending_)
  2248.         return;
  2249.       webkitRequestAnimationFrame(function() {
  2250.         this.rafPending_ = false;
  2251.         if (!this.viewport_)
  2252.           return;
  2253.         this.updateCanvasSizeIfNeeded_();
  2254.         this.redraw();
  2255.       }.bind(this), this);
  2256.       this.rafPending_ = true;
  2257.     },
  2258.  
  2259.     /**
  2260.      * @return {boolean} Whether the current timeline is attached to the
  2261.      * document.
  2262.      */
  2263.     get isAttachedToDocument_() {
  2264.       var cur = this.parentNode;
  2265.       if (!cur)
  2266.         return;
  2267.       while (cur.parentNode)
  2268.         cur = cur.parentNode;
  2269.       return cur == this.ownerDocument;
  2270.     },
  2271.  
  2272.  
  2273.     updateCanvasSizeIfNeeded_: function() {
  2274.       var style = window.getComputedStyle(this.canvasContainer_);
  2275.       var innerWidth = parseInt(style.width) -
  2276.           parseInt(style.paddingLeft) - parseInt(style.paddingRight) -
  2277.           parseInt(style.borderLeftWidth) - parseInt(style.borderRightWidth);
  2278.       var innerHeight = parseInt(style.height) -
  2279.           parseInt(style.paddingTop) - parseInt(style.paddingBottom) -
  2280.           parseInt(style.borderTopWidth) - parseInt(style.borderBottomWidth);
  2281.       var pixelRatio = window.devicePixelRatio || 1;
  2282.       if (this.canvas_.width != innerWidth) {
  2283.         this.canvas_.width = innerWidth * pixelRatio;
  2284.         this.canvas_.style.width = innerWidth + 'px';
  2285.       }
  2286.       if (this.canvas_.height != innerHeight) {
  2287.         this.canvas_.height = innerHeight * pixelRatio;
  2288.         this.canvas_.style.height = innerHeight + 'px';
  2289.       }
  2290.     },
  2291.     get firstCanvas() {
  2292.       return this.canvas_;
  2293.     }
  2294.   };
  2295.  
  2296.   return {
  2297.     TimelineCanvasBasedTrack: TimelineCanvasBasedTrack
  2298.   };
  2299. });
  2300.  
  2301. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2302. // Use of this source code is governed by a BSD-style license that can be
  2303. // found in the LICENSE file.
  2304.  
  2305.  
  2306. /**
  2307.  * @fileoverview Helper functions for doing intersections and iteration
  2308.  * over sorted arrays and intervals.
  2309.  *
  2310.  */
  2311. base.exportTo('tracing', function() {
  2312.   /**
  2313.    * Finds the first index in the array whose value is >= loVal.
  2314.    *
  2315.    * The key for the search is defined by the mapFn. This array must
  2316.    * be prearranged such that ary.map(mapFn) would also be sorted in
  2317.    * ascending order.
  2318.    *
  2319.    * @param {Array} ary An array of arbitrary objects.
  2320.    * @param {function():*} mapFn Callback that produces a key value
  2321.    *     from an element in ary.
  2322.    * @param {number} loVal Value for which to search.
  2323.    * @return {Number} Offset o into ary where all ary[i] for i <= o
  2324.    *     are < loVal, or ary.length if loVal is greater than all elements in
  2325.    *     the array.
  2326.    */
  2327.   function findLowIndexInSortedArray(ary, mapFn, loVal) {
  2328.     if (ary.length == 0)
  2329.       return 1;
  2330.  
  2331.     var low = 0;
  2332.     var high = ary.length - 1;
  2333.     var i, comparison;
  2334.     var hitPos = -1;
  2335.     while (low <= high) {
  2336.       i = Math.floor((low + high) / 2);
  2337.       comparison = mapFn(ary[i]) - loVal;
  2338.       if (comparison < 0) {
  2339.         low = i + 1; continue;
  2340.       } else if (comparison > 0) {
  2341.         high = i - 1; continue;
  2342.       } else {
  2343.         hitPos = i;
  2344.         high = i - 1;
  2345.       }
  2346.     }
  2347.     // return where we hit, or failing that the low pos
  2348.     return hitPos != -1 ? hitPos : low;
  2349.   }
  2350.  
  2351.   /**
  2352.    * Finds an index in an array of intervals that either
  2353.    * intersects the provided loVal, or if no intersection is found,
  2354.    * the index of the first interval whose start is > loVal.
  2355.    *
  2356.    * The array of intervals is defined implicitly via two mapping functions
  2357.    * over the provided ary. mapLoFn determines the lower value of the interval,
  2358.    * mapWidthFn the width. Intersection is lower-inclusive, e.g. [lo,lo+w).
  2359.    *
  2360.    * The array of intervals formed by this mapping must be non-overlapping and
  2361.    * sorted in ascending order by loVal.
  2362.    *
  2363.    * @param {Array} ary An array of objects that can be converted into sorted
  2364.    *     nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
  2365.    * @param {function():*} mapLoFn Callback that produces the low value for the
  2366.    *     interval represented by an  element in the array.
  2367.    * @param {function():*} mapLoFn Callback that produces the width for the
  2368.    *     interval represented by an  element in the array.
  2369.    * @param {number} loVal The low value for the search.
  2370.    * @return {Number} An index in the array that intersects or is first-above
  2371.    *     loVal, -1 if none found and loVal is below than all the intervals,
  2372.    *     ary.length if loVal is greater than all the intervals.
  2373.    */
  2374.   function findLowIndexInSortedIntervals(ary, mapLoFn, mapWidthFn, loVal) {
  2375.     var first = findLowIndexInSortedArray(ary, mapLoFn, loVal);
  2376.     if (first == 0) {
  2377.       if (loVal >= mapLoFn(ary[0]) &&
  2378.           loVal < mapLoFn(ary[0] + mapWidthFn(ary[0]))) {
  2379.         return 0;
  2380.       } else {
  2381.         return -1;
  2382.       }
  2383.     } else if (first <= ary.length &&
  2384.                loVal >= mapLoFn(ary[first - 1]) &&
  2385.                loVal < mapLoFn(ary[first - 1]) + mapWidthFn(ary[first - 1])) {
  2386.       return first - 1;
  2387.     } else {
  2388.       return ary.length;
  2389.     }
  2390.   }
  2391.  
  2392.   /**
  2393.    * Calls cb for all intervals in the implicit array of intervals
  2394.    * defnied by ary, mapLoFn and mapHiFn that intersect the range
  2395.    * [loVal,hiVal)
  2396.    *
  2397.    * This function uses the same scheme as findLowIndexInSortedArray
  2398.    * to define the intervals. The same restrictions on sortedness and
  2399.    * non-overlappingness apply.
  2400.    *
  2401.    * @param {Array} ary An array of objects that can be converted into sorted
  2402.    * nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
  2403.    * @param {function():*} mapLoFn Callback that produces the low value for the
  2404.    * interval represented by an element in the array.
  2405.    * @param {function():*} mapLoFn Callback that produces the width for the
  2406.    * interval represented by an element in the array.
  2407.    * @param {number} The low value for the search, inclusive.
  2408.    * @param {number} loVal The high value for the search, non inclusive.
  2409.    * @param {function():*} cb The function to run for intersecting intervals.
  2410.    */
  2411.   function iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal,
  2412.                                             hiVal, cb) {
  2413.     if (ary.length == 0)
  2414.       return;
  2415.  
  2416.     if (loVal > hiVal) return;
  2417.  
  2418.     var i = findLowIndexInSortedArray(ary, mapLoFn, loVal);
  2419.     if (i == -1) {
  2420.       return;
  2421.     }
  2422.     if (i > 0) {
  2423.       var hi = mapLoFn(ary[i - 1]) + mapWidthFn(ary[i - 1]);
  2424.       if (hi >= loVal) {
  2425.         cb(ary[i - 1]);
  2426.       }
  2427.     }
  2428.     if (i == ary.length) {
  2429.       return;
  2430.     }
  2431.  
  2432.     for (var n = ary.length; i < n; i++) {
  2433.       var lo = mapLoFn(ary[i]);
  2434.       if (lo >= hiVal)
  2435.         break;
  2436.       cb(ary[i]);
  2437.     }
  2438.   }
  2439.  
  2440.   /**
  2441.    * Non iterative version of iterateOverIntersectingIntervals.
  2442.    *
  2443.    * @return {Array} Array of elements in ary that intersect loVal, hiVal.
  2444.    */
  2445.   function getIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal) {
  2446.     var tmp = [];
  2447.     iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal,
  2448.                                      function(d) {
  2449.                                        tmp.push(d);
  2450.                                      });
  2451.     return tmp;
  2452.   }
  2453.  
  2454.   return {
  2455.     findLowIndexInSortedArray: findLowIndexInSortedArray,
  2456.     findLowIndexInSortedIntervals: findLowIndexInSortedIntervals,
  2457.     iterateOverIntersectingIntervals: iterateOverIntersectingIntervals,
  2458.     getIntersectingIntervals: getIntersectingIntervals
  2459.   };
  2460. });
  2461.  
  2462. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2463. // Use of this source code is governed by a BSD-style license that can be
  2464. // found in the LICENSE file.
  2465.  
  2466. 'use strict';
  2467.  
  2468. base.requireStylesheet('tracks.timeline_slice_track');
  2469.  
  2470. base.require('tracks.timeline_canvas_based_track');
  2471. base.require('sorted_array_utils');
  2472. base.require('fast_rect_renderer');
  2473. base.require('timeline_color_scheme');
  2474. base.require('ui');
  2475.  
  2476. base.exportTo('tracks', function() {
  2477.  
  2478.   var palette = tracing.getColorPalette();
  2479.  
  2480.   /**
  2481.    * A track that displays an array of TimelineSlice objects.
  2482.    * @constructor
  2483.    * @extends {CanvasBasedTrack}
  2484.    */
  2485.  
  2486.   var TimelineSliceTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
  2487.  
  2488.   TimelineSliceTrack.prototype = {
  2489.  
  2490.     __proto__: tracks.TimelineCanvasBasedTrack.prototype,
  2491.  
  2492.     /**
  2493.      * Should we elide text on trace labels?
  2494.      * Without eliding, text that is too wide isn't drawn at all.
  2495.      * Disable if you feel this causes a performance problem.
  2496.      * This is a default value that can be overridden in tracks for testing.
  2497.      * @const
  2498.      */
  2499.     SHOULD_ELIDE_TEXT: true,
  2500.  
  2501.     decorate: function() {
  2502.       this.classList.add('timeline-slice-track');
  2503.       this.elidedTitleCache = new ElidedTitleCache();
  2504.       this.asyncStyle_ = false;
  2505.     },
  2506.  
  2507.     /**
  2508.      * Called by all the addToSelection functions on the created selection
  2509.      * hit objects. Override this function on parent classes to add
  2510.      * context-specific information to the hit.
  2511.      */
  2512.     decorateHit: function(hit) {
  2513.     },
  2514.  
  2515.     get asyncStyle() {
  2516.       return this.asyncStyle_;
  2517.     },
  2518.  
  2519.     set asyncStyle(v) {
  2520.       this.asyncStyle_ = !!v;
  2521.       this.invalidate();
  2522.     },
  2523.  
  2524.     get slices() {
  2525.       return this.slices_;
  2526.     },
  2527.  
  2528.     set slices(slices) {
  2529.       this.slices_ = slices || [];
  2530.       if (!slices)
  2531.         this.visible = false;
  2532.       this.invalidate();
  2533.     },
  2534.  
  2535.     get height() {
  2536.       return window.getComputedStyle(this).height;
  2537.     },
  2538.  
  2539.     set height(height) {
  2540.       this.style.height = height;
  2541.       this.invalidate();
  2542.     },
  2543.  
  2544.     labelWidth: function(title) {
  2545.       return quickMeasureText(this.ctx_, title) + 2;
  2546.     },
  2547.  
  2548.     labelWidthWorld: function(title, pixWidth) {
  2549.       return this.labelWidth(title) * pixWidth;
  2550.     },
  2551.  
  2552.     redraw: function() {
  2553.       var ctx = this.ctx_;
  2554.       var canvasW = this.canvas_.width;
  2555.       var canvasH = this.canvas_.height;
  2556.  
  2557.       ctx.clearRect(0, 0, canvasW, canvasH);
  2558.  
  2559.       // Culling parameters.
  2560.       var vp = this.viewport_;
  2561.       var pixWidth = vp.xViewVectorToWorld(1);
  2562.       var viewLWorld = vp.xViewToWorld(0);
  2563.       var viewRWorld = vp.xViewToWorld(canvasW);
  2564.  
  2565.       // Give the viewport a chance to draw onto this canvas.
  2566.       vp.drawUnderContent(ctx, viewLWorld, viewRWorld, canvasH);
  2567.  
  2568.       // Begin rendering in world space.
  2569.       ctx.save();
  2570.       vp.applyTransformToCanvas(ctx);
  2571.  
  2572.       // Slices.
  2573.       if (this.asyncStyle_)
  2574.         ctx.globalAlpha = 0.25;
  2575.       var tr = new tracing.FastRectRenderer(ctx, 2 * pixWidth, 2 * pixWidth,
  2576.                                             palette);
  2577.       tr.setYandH(0, canvasH);
  2578.       var slices = this.slices_;
  2579.       var lowSlice = tracing.findLowIndexInSortedArray(slices,
  2580.                                                        function(slice) {
  2581.                                                          return slice.start +
  2582.                                                                 slice.duration;
  2583.                                                        },
  2584.                                                        viewLWorld);
  2585.       for (var i = lowSlice; i < slices.length; ++i) {
  2586.         var slice = slices[i];
  2587.         var x = slice.start;
  2588.         if (x > viewRWorld) {
  2589.           break;
  2590.         }
  2591.         // Less than 0.001 causes short events to disappear when zoomed in.
  2592.         var w = Math.max(slice.duration, 0.001);
  2593.         var colorId = slice.selected ?
  2594.             slice.colorId + highlightIdBoost :
  2595.             slice.colorId;
  2596.  
  2597.         if (w < pixWidth)
  2598.           w = pixWidth;
  2599.         if (slice.duration > 0) {
  2600.           tr.fillRect(x, w, colorId);
  2601.         } else {
  2602.           // Instant: draw a triangle.  If zoomed too far, collapse
  2603.           // into the FastRectRenderer.
  2604.           if (pixWidth > 0.001) {
  2605.             tr.fillRect(x, pixWidth, colorId);
  2606.           } else {
  2607.             ctx.fillStyle = palette[colorId];
  2608.             ctx.beginPath();
  2609.             ctx.moveTo(x - (4 * pixWidth), canvasH);
  2610.             ctx.lineTo(x, 0);
  2611.             ctx.lineTo(x + (4 * pixWidth), canvasH);
  2612.             ctx.closePath();
  2613.             ctx.fill();
  2614.           }
  2615.         }
  2616.       }
  2617.       tr.flush();
  2618.       ctx.restore();
  2619.  
  2620.       // Labels.
  2621.       var pixelRatio = window.devicePixelRatio || 1;
  2622.       if (canvasH > 8) {
  2623.         ctx.textAlign = 'center';
  2624.         ctx.textBaseline = 'top';
  2625.         ctx.font = (10 * pixelRatio) + 'px sans-serif';
  2626.         ctx.strokeStyle = 'rgb(0,0,0)';
  2627.         ctx.fillStyle = 'rgb(0,0,0)';
  2628.         // Don't render text until until it is 20px wide
  2629.         var quickDiscardThresshold = pixWidth * 20;
  2630.         var shouldElide = this.SHOULD_ELIDE_TEXT;
  2631.         for (var i = lowSlice; i < slices.length; ++i) {
  2632.           var slice = slices[i];
  2633.           if (slice.start > viewRWorld) {
  2634.             break;
  2635.           }
  2636.           if (slice.duration > quickDiscardThresshold) {
  2637.             var title = slice.title;
  2638.             if (slice.didNotFinish) {
  2639.               title += ' (Did Not Finish)';
  2640.             }
  2641.             var drawnTitle = title;
  2642.             var drawnWidth = this.labelWidth(drawnTitle);
  2643.             if (shouldElide &&
  2644.                 this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
  2645.               var elidedValues = this.elidedTitleCache.get(
  2646.                   this, pixWidth,
  2647.                   drawnTitle, drawnWidth,
  2648.                   slice.duration);
  2649.               drawnTitle = elidedValues.string;
  2650.               drawnWidth = elidedValues.width;
  2651.             }
  2652.             if (drawnWidth * pixWidth < slice.duration) {
  2653.               var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
  2654.               ctx.fillText(drawnTitle, cX, 2.5 * pixelRatio, drawnWidth);
  2655.             }
  2656.           }
  2657.         }
  2658.       }
  2659.  
  2660.       // Give the viewport a chance to draw over this canvas.
  2661.       vp.drawOverContent(ctx, viewLWorld, viewRWorld, canvasH);
  2662.     },
  2663.  
  2664.     /**
  2665.      * Finds slices intersecting the given interval.
  2666.      * @param {number} vX X location to search at, in viewspace.
  2667.      * @param {number} vY Y location to search at, in viewspace.
  2668.      * @param {TimelineSelection} selection Selection to which to add hits.
  2669.      * @return {boolean} true if a slice was found, otherwise false.
  2670.      */
  2671.     addIntersectingItemsToSelection: function(vX, vY, selection) {
  2672.       var clientRect = this.getBoundingClientRect();
  2673.       if (vY < clientRect.top || vY >= clientRect.bottom)
  2674.         return false;
  2675.       var pixelRatio = window.devicePixelRatio || 1;
  2676.       var wX = this.viewport_.xViewVectorToWorld(vX * devicePixelRatio);
  2677.       var x = tracing.findLowIndexInSortedIntervals(this.slices_,
  2678.           function(x) { return x.start; },
  2679.           function(x) { return x.duration; },
  2680.           wX);
  2681.       if (x >= 0 && x < this.slices_.length) {
  2682.         var hit = selection.addSlice(this, this.slices_[x]);
  2683.         this.decorateHit(hit);
  2684.         return true;
  2685.       }
  2686.       return false;
  2687.     },
  2688.  
  2689.     /**
  2690.      * Adds items intersecting the given range to a selection.
  2691.      * @param {number} loVX Lower X bound of the interval to search, in
  2692.      *     viewspace.
  2693.      * @param {number} hiVX Upper X bound of the interval to search, in
  2694.      *     viewspace.
  2695.      * @param {number} loVY Lower Y bound of the interval to search, in
  2696.      *     viewspace.
  2697.      * @param {number} hiVY Upper Y bound of the interval to search, in
  2698.      *     viewspace.
  2699.      * @param {TimelineSelection} selection Selection to which to add hits.
  2700.      */
  2701.     addIntersectingItemsInRangeToSelection: function(
  2702.         loVX, hiVX, loVY, hiVY, selection) {
  2703.  
  2704.       var pixelRatio = window.devicePixelRatio || 1;
  2705.       var loWX = this.viewport_.xViewToWorld(loVX * pixelRatio);
  2706.       var hiWX = this.viewport_.xViewToWorld(hiVX * pixelRatio);
  2707.  
  2708.       var clientRect = this.getBoundingClientRect();
  2709.       var a = Math.max(loVY, clientRect.top);
  2710.       var b = Math.min(hiVY, clientRect.bottom);
  2711.       if (a > b)
  2712.         return;
  2713.  
  2714.       var that = this;
  2715.       function onPickHit(slice) {
  2716.         var hit = selection.addSlice(that, slice);
  2717.         that.decorateHit(hit);
  2718.       }
  2719.       tracing.iterateOverIntersectingIntervals(this.slices_,
  2720.           function(x) { return x.start; },
  2721.           function(x) { return x.duration; },
  2722.           loWX, hiWX,
  2723.           onPickHit);
  2724.     },
  2725.  
  2726.     /**
  2727.      * Find the index for the given slice.
  2728.      * @return {index} Index of the given slice, or undefined.
  2729.      * @private
  2730.      */
  2731.     indexOfSlice_: function(slice) {
  2732.       var index = tracing.findLowIndexInSortedArray(this.slices_,
  2733.           function(x) { return x.start; },
  2734.           slice.start);
  2735.       while (index < this.slices_.length &&
  2736.           slice.start == this.slices_[index].start &&
  2737.           slice.colorId != this.slices_[index].colorId) {
  2738.         index++;
  2739.       }
  2740.       return index < this.slices_.length ? index : undefined;
  2741.     },
  2742.  
  2743.     /**
  2744.      * Add the item to the left or right of the provided hit, if any, to the
  2745.      * selection.
  2746.      * @param {slice} The current slice.
  2747.      * @param {Number} offset Number of slices away from the hit to look.
  2748.      * @param {TimelineSelection} selection The selection to add a hit to,
  2749.      * if found.
  2750.      * @return {boolean} Whether a hit was found.
  2751.      * @private
  2752.      */
  2753.     addItemNearToProvidedHitToSelection: function(hit, offset, selection) {
  2754.       if (!hit.slice)
  2755.         return false;
  2756.  
  2757.       var index = this.indexOfSlice_(hit.slice);
  2758.       if (index === undefined)
  2759.         return false;
  2760.  
  2761.       var newIndex = index + offset;
  2762.       if (newIndex < 0 || newIndex >= this.slices_.length)
  2763.         return false;
  2764.  
  2765.       var hit = selection.addSlice(this, this.slices_[newIndex]);
  2766.       this.decorateHit(hit);
  2767.       return true;
  2768.     },
  2769.  
  2770.     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
  2771.       for (var i = 0; i < this.slices_.length; ++i) {
  2772.         if (filter.matchSlice(this.slices_[i])) {
  2773.           var hit = selection.addSlice(this, this.slices_[i]);
  2774.           this.decorateHit(hit);
  2775.         }
  2776.       }
  2777.     }
  2778.   };
  2779.  
  2780.   var highlightIdBoost = tracing.getColorPaletteHighlightIdBoost();
  2781.  
  2782.   // TODO(jrg): possibly obsoleted with the elided string cache.
  2783.   // Consider removing.
  2784.   var textWidthMap = { };
  2785.   function quickMeasureText(ctx, text) {
  2786.     var w = textWidthMap[text];
  2787.     if (!w) {
  2788.       w = ctx.measureText(text).width;
  2789.       textWidthMap[text] = w;
  2790.     }
  2791.     return w;
  2792.   }
  2793.  
  2794.   /**
  2795.    * Cache for elided strings.
  2796.    * Moved from the ElidedTitleCache protoype to a "global" for speed
  2797.    * (variable reference is 100x faster).
  2798.    *   key: String we wish to elide.
  2799.    *   value: Another dict whose key is width
  2800.    *     and value is an ElidedStringWidthPair.
  2801.    */
  2802.   var elidedTitleCacheDict = {};
  2803.  
  2804.   /**
  2805.    * A cache for elided strings.
  2806.    * @constructor
  2807.    */
  2808.   function ElidedTitleCache() {
  2809.   }
  2810.  
  2811.   ElidedTitleCache.prototype = {
  2812.     /**
  2813.      * Return elided text.
  2814.      * @param {track} A timeline slice track or other object that defines
  2815.      *                functions labelWidth() and labelWidthWorld().
  2816.      * @param {pixWidth} Pixel width.
  2817.      * @param {title} Original title text.
  2818.      * @param {width} Drawn width in world coords.
  2819.      * @param {sliceDuration} Where the title must fit (in world coords).
  2820.      * @return {ElidedStringWidthPair} Elided string and width.
  2821.      */
  2822.     get: function(track, pixWidth, title, width, sliceDuration) {
  2823.       var elidedDict = elidedTitleCacheDict[title];
  2824.       if (!elidedDict) {
  2825.         elidedDict = {};
  2826.         elidedTitleCacheDict[title] = elidedDict;
  2827.       }
  2828.       var elidedDictForPixWidth = elidedDict[pixWidth];
  2829.       if (!elidedDictForPixWidth) {
  2830.         elidedDict[pixWidth] = {};
  2831.         elidedDictForPixWidth = elidedDict[pixWidth];
  2832.       }
  2833.       var stringWidthPair = elidedDictForPixWidth[sliceDuration];
  2834.       if (stringWidthPair === undefined) {
  2835.         var newtitle = title;
  2836.         var elided = false;
  2837.         while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
  2838.           newtitle = newtitle.substring(0, newtitle.length * 0.75);
  2839.           elided = true;
  2840.         }
  2841.         if (elided && newtitle.length > 3)
  2842.           newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
  2843.         stringWidthPair = new ElidedStringWidthPair(
  2844.             newtitle,
  2845.             track.labelWidth(newtitle));
  2846.         elidedDictForPixWidth[sliceDuration] = stringWidthPair;
  2847.       }
  2848.       return stringWidthPair;
  2849.     }
  2850.   };
  2851.  
  2852.   /**
  2853.    * A pair representing an elided string and world-coordinate width
  2854.    * to draw it.
  2855.    * @constructor
  2856.    */
  2857.   function ElidedStringWidthPair(string, width) {
  2858.     this.string = string;
  2859.     this.width = width;
  2860.   }
  2861.  
  2862.   return {
  2863.     TimelineSliceTrack: TimelineSliceTrack
  2864.   };
  2865. });
  2866.  
  2867. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2868. // Use of this source code is governed by a BSD-style license that can be
  2869. // found in the LICENSE file.
  2870.  
  2871. 'use strict';
  2872.  
  2873. /**
  2874.  * @fileoverview Provides the TimelineSlice class.
  2875.  */
  2876. base.exportTo('tracing', function() {
  2877.  
  2878.   /**
  2879.    * A TimelineSlice represents an interval of time plus parameters associated
  2880.    * with that interval.
  2881.    *
  2882.    * All time units are stored in milliseconds.
  2883.    * @constructor
  2884.    */
  2885.   function TimelineSlice(category, title, colorId, start, args, opt_duration) {
  2886.     this.category = category || '';
  2887.     this.title = title;
  2888.     this.start = start;
  2889.     this.colorId = colorId;
  2890.     this.args = args;
  2891.     this.didNotFinish = false;
  2892.     if (opt_duration !== undefined)
  2893.       this.duration = opt_duration;
  2894.   }
  2895.  
  2896.   TimelineSlice.prototype = {
  2897.     selected: false,
  2898.  
  2899.     duration: undefined,
  2900.  
  2901.     get end() {
  2902.       return this.start + this.duration;
  2903.     }
  2904.   };
  2905.  
  2906.   return {
  2907.     TimelineSlice: TimelineSlice
  2908.   };
  2909. });
  2910.  
  2911. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2912. // Use of this source code is governed by a BSD-style license that can be
  2913. // found in the LICENSE file.
  2914.  
  2915. 'use strict';
  2916.  
  2917. /**
  2918.  * @fileoverview Provides the TimelineSliceGroup class.
  2919.  */
  2920. base.require('timeline_slice');
  2921. base.require('timeline_color_scheme');
  2922. base.require('timeline_filter');
  2923.  
  2924. base.exportTo('tracing', function() {
  2925.   var TimelineSlice = tracing.TimelineSlice;
  2926.  
  2927.   /**
  2928.    * A group of TimelineSlices, plus code to create them from B/E events, as
  2929.    * well as arrange them into subRows.
  2930.    *
  2931.    * Do not mutate the slices array directly. Modify it only by
  2932.    * TimelineSliceGroup mutation methods.
  2933.    *
  2934.    * @constructor
  2935.    * @param {function(new:TimelineSlice, category, title, colorId, start, args)}
  2936.    *     opt_sliceConstructor The constructor to use when creating slices.
  2937.    */
  2938.   function TimelineSliceGroup(opt_sliceConstructor) {
  2939.     var sliceConstructor = opt_sliceConstructor || TimelineSlice;
  2940.     this.sliceConstructor = sliceConstructor;
  2941.  
  2942.     this.openPartialSlices_ = [];
  2943.  
  2944.     this.slices = [];
  2945.   }
  2946.  
  2947.   TimelineSliceGroup.prototype = {
  2948.     __proto__: Object.prototype,
  2949.  
  2950.     /**
  2951.      * Helper function that pushes the provided slice onto the slices array.
  2952.      * @param {TimelineSlice} slice The slice to be added to the slices array.
  2953.      */
  2954.     pushSlice: function(slice) {
  2955.       this.slices.push(slice);
  2956.       return slice;
  2957.     },
  2958.  
  2959.     /**
  2960.      * Helper function that pushes the provided slice onto the slices array.
  2961.      * @param {Array.<TimelineSlice>} slices An array of slices to be added.
  2962.      */
  2963.     pushSlices: function(slices) {
  2964.       this.slices.push.apply(this.slices, slices);
  2965.     },
  2966.  
  2967.     /**
  2968.      * Opens a new slice in the group's slices.
  2969.      *
  2970.      * Calls to beginSlice and
  2971.      * endSlice must be made with non-monotonically-decreasing timestamps.
  2972.      *
  2973.      * @param {String} title Title of the slice to add.
  2974.      * @param {Number} ts The timetsamp of the slice, in milliseconds.
  2975.      * @param {Object.<string, Object>} opt_args Arguments associated with
  2976.      * the slice.
  2977.      */
  2978.     beginSlice: function(category, title, ts, opt_args) {
  2979.       if (this.openPartialSlices_.length) {
  2980.         var prevSlice = this.openPartialSlices_[
  2981.             this.openPartialSlices_.length - 1];
  2982.         if (ts < prevSlice.start)
  2983.           throw new Error('Slices must be added in increasing timestamp order');
  2984.       }
  2985.  
  2986.       var colorId = tracing.getStringColorId(title);
  2987.       var slice = new this.sliceConstructor(category, title, colorId, ts,
  2988.                                             opt_args ? opt_args : {});
  2989.       this.openPartialSlices_.push(slice);
  2990.       return slice;
  2991.     },
  2992.  
  2993.     isTimestampValidForBeginOrEnd: function(ts) {
  2994.       if (!this.openPartialSlices_.length)
  2995.         return true;
  2996.       var top = this.openPartialSlices_[this.openPartialSlices_.length - 1];
  2997.       return ts >= top.start;
  2998.     },
  2999.  
  3000.     /**
  3001.      * @return {Number} The number of beginSlices for which an endSlice has not
  3002.      * been issued.
  3003.      */
  3004.     get openSliceCount() {
  3005.       return this.openPartialSlices_.length;
  3006.     },
  3007.  
  3008.     /**
  3009.      * Ends the last begun slice in this group and pushes it onto the slice
  3010.      * array.
  3011.      *
  3012.      * @param {Number} ts Timestamp when the slice ended.
  3013.      * @return {TimelineSlice} slice.
  3014.      */
  3015.     endSlice: function(ts) {
  3016.       if (!this.openSliceCount)
  3017.         throw new Error('endSlice called without an open slice');
  3018.       var slice = this.openPartialSlices_[this.openSliceCount - 1];
  3019.       this.openPartialSlices_.splice(this.openSliceCount - 1, 1);
  3020.       if (ts < slice.start)
  3021.         throw new Error('Slice ' + slice.name +
  3022.                         ' end time is before its start.');
  3023.  
  3024.       slice.duration = ts - slice.start;
  3025.       this.pushSlice(slice);
  3026.  
  3027.       return slice;
  3028.     },
  3029.  
  3030.     /**
  3031.      * Closes any open slices.
  3032.      * @param {Number} opt_maxTimestamp The end time to use for the closed
  3033.      * slices. If not provided,
  3034.      * the max timestamp for this slice is provided.
  3035.      */
  3036.     autoCloseOpenSlices: function(opt_maxTimestamp) {
  3037.       if (!opt_maxTimestamp) {
  3038.         this.updateBounds();
  3039.         opt_maxTimestamp = this.maxTimestamp;
  3040.       }
  3041.       while (this.openSliceCount > 0) {
  3042.         var slice = this.endSlice(opt_maxTimestamp);
  3043.         slice.didNotFinish = true;
  3044.       }
  3045.     },
  3046.  
  3047.     /**
  3048.      * Shifts all the timestamps inside this group forward by the amount
  3049.      * specified.
  3050.      */
  3051.     shiftTimestampsForward: function(amount) {
  3052.       for (var sI = 0; sI < this.slices.length; sI++) {
  3053.         var slice = this.slices[sI];
  3054.         slice.start = (slice.start + amount);
  3055.       }
  3056.       for (var sI = 0; sI < this.openPartialSlices_.length; sI++) {
  3057.         var slice = this.openPartialSlices_[i];
  3058.         slice.start = (slice.start + amount);
  3059.       }
  3060.     },
  3061.  
  3062.     /**
  3063.      * Updates the bounds for this group based on the slices it contains.
  3064.      */
  3065.     updateBounds: function() {
  3066.       var vals = [];
  3067.       if (this.slices.length) {
  3068.         var minTimestamp = Number.MAX_VALUE;
  3069.         var maxTimestamp = -Number.MAX_VALUE;
  3070.         for (var i = 0; i < this.slices.length; i++) {
  3071.           if (this.slices[i].start < minTimestamp)
  3072.             minTimestamp = this.slices[i].start;
  3073.           if (this.slices[i].end > maxTimestamp)
  3074.             maxTimestamp = this.slices[i].end;
  3075.         }
  3076.         vals.push(minTimestamp);
  3077.         vals.push(maxTimestamp);
  3078.       }
  3079.  
  3080.       if (this.openPartialSlices_.length) {
  3081.         vals.push(this.openPartialSlices_[0].start);
  3082.         vals.push(
  3083.             this.openPartialSlices_[this.openPartialSlices_.length - 1].start);
  3084.       }
  3085.  
  3086.       if (vals.length) {
  3087.         this.minTimestamp = Math.min.apply(Math, vals);
  3088.         this.maxTimestamp = Math.max.apply(Math, vals);
  3089.       } else {
  3090.         this.minTimestamp = undefined;
  3091.         this.maxTimestamp = undefined;
  3092.       }
  3093.     }
  3094.   };
  3095.  
  3096.   return {
  3097.     TimelineSliceGroup: TimelineSliceGroup
  3098.   };
  3099. });
  3100.  
  3101. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3102. // Use of this source code is governed by a BSD-style license that can be
  3103. // found in the LICENSE file.
  3104.  
  3105. 'use strict';
  3106.  
  3107. /**
  3108.  * @fileoverview Provides the TimelineAsyncSliceGroup class.
  3109.  */
  3110. base.require('timeline_slice');
  3111. base.exportTo('tracing', function() {
  3112.  
  3113.   var TimelineSlice = tracing.TimelineSlice;
  3114.  
  3115.   /**
  3116.    * A TimelineAsyncSlice represents an interval of time during which an
  3117.    * asynchronous operation is in progress. An AsyncSlice consumes no CPU time
  3118.    * itself and so is only associated with Threads at its start and end point.
  3119.    *
  3120.    * @constructor
  3121.    */
  3122.   function TimelineAsyncSlice(category, title, colorId, start, args) {
  3123.     TimelineSlice.call(this, category, title, colorId, start, args);
  3124.   };
  3125.  
  3126.   TimelineAsyncSlice.prototype = {
  3127.     __proto__: TimelineSlice.prototype,
  3128.  
  3129.     toJSON: function() {
  3130.       var obj = new Object();
  3131.       var keys = Object.keys(this);
  3132.       for (var i = 0; i < keys.length; i++) {
  3133.         var key = keys[i];
  3134.         if (typeof this[key] == 'function')
  3135.           continue;
  3136.         if (key == 'startThread' || key == 'endThread') {
  3137.           obj[key] = this[key].ptid;
  3138.           continue;
  3139.         }
  3140.         obj[key] = this[key];
  3141.       }
  3142.       return obj;
  3143.     },
  3144.  
  3145.     id: undefined,
  3146.  
  3147.     startThread: undefined,
  3148.  
  3149.     endThread: undefined,
  3150.  
  3151.     subSlices: undefined
  3152.   };
  3153.  
  3154.   /**
  3155.    * A group of AsyncSlices.
  3156.    * @constructor
  3157.    */
  3158.   function TimelineAsyncSliceGroup(name) {
  3159.     this.name = name;
  3160.     this.slices = [];
  3161.   }
  3162.  
  3163.   TimelineAsyncSliceGroup.prototype = {
  3164.     __proto__: Object.prototype,
  3165.  
  3166.     /**
  3167.      * Helper function that pushes the provided slice onto the slices array.
  3168.      */
  3169.     push: function(slice) {
  3170.       this.slices.push(slice);
  3171.     },
  3172.  
  3173.     /**
  3174.      * @return {Number} The number of slices in this group.
  3175.      */
  3176.     get length() {
  3177.       return this.slices.length;
  3178.     },
  3179.  
  3180.     /**
  3181.      * Shifts all the timestamps inside this group forward by the amount
  3182.      * specified.
  3183.      */
  3184.     shiftTimestampsForward: function(amount) {
  3185.       for (var sI = 0; sI < this.slices.length; sI++) {
  3186.         var slice = this.slices[sI];
  3187.         slice.start = (slice.start + amount);
  3188.         for (var sJ = 0; sJ < slice.subSlices.length; sJ++)
  3189.           slice.subSlices[sJ].start += amount;
  3190.       }
  3191.     },
  3192.  
  3193.     /**
  3194.      * Updates the bounds for this group based on the slices it contains.
  3195.      */
  3196.     updateBounds: function() {
  3197.       if (this.slices.length) {
  3198.         var minTimestamp = Number.MAX_VALUE;
  3199.         var maxTimestamp = -Number.MAX_VALUE;
  3200.         for (var i = 0; i < this.slices.length; i++) {
  3201.           if (this.slices[i].start < minTimestamp)
  3202.             minTimestamp = this.slices[i].start;
  3203.           if (this.slices[i].end > maxTimestamp)
  3204.             maxTimestamp = this.slices[i].end;
  3205.         }
  3206.         this.minTimestamp = minTimestamp;
  3207.         this.maxTimestamp = maxTimestamp;
  3208.       } else {
  3209.         this.minTimestamp = undefined;
  3210.         this.maxTimestamp = undefined;
  3211.       }
  3212.     },
  3213.  
  3214.     /**
  3215.      * Breaks up this group into slices based on start thread.
  3216.      *
  3217.      * @return {Array} An array of TimelineAsyncSliceGroups where each group has
  3218.      * slices that started on the same thread.
  3219.      */
  3220.     computeSubGroups: function() {
  3221.       var subGroupsByPTID = {};
  3222.       for (var i = 0; i < this.slices.length; ++i) {
  3223.         var slice = this.slices[i];
  3224.         var slicePTID = slice.startThread.ptid;
  3225.         if (!subGroupsByPTID[slicePTID])
  3226.           subGroupsByPTID[slicePTID] = new TimelineAsyncSliceGroup(this.name);
  3227.         subGroupsByPTID[slicePTID].slices.push(slice);
  3228.       }
  3229.       var groups = [];
  3230.       for (var ptid in subGroupsByPTID) {
  3231.         var group = subGroupsByPTID[ptid];
  3232.         group.updateBounds();
  3233.         groups.push(group);
  3234.       }
  3235.       return groups;
  3236.     }
  3237.   };
  3238.  
  3239.   return {
  3240.     TimelineAsyncSlice: TimelineAsyncSlice,
  3241.     TimelineAsyncSliceGroup: TimelineAsyncSliceGroup
  3242.   };
  3243. });
  3244.  
  3245. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3246. // Use of this source code is governed by a BSD-style license that can be
  3247. // found in the LICENSE file.
  3248.  
  3249. 'use strict';
  3250.  
  3251. /**
  3252.  * @fileoverview Provides the TimelineThread class.
  3253.  */
  3254. base.require('timeline_slice');
  3255. base.require('timeline_slice_group');
  3256. base.require('timeline_async_slice_group');
  3257. base.exportTo('tracing', function() {
  3258.  
  3259.   var TimelineSlice = tracing.TimelineSlice;
  3260.   var TimelineSliceGroup = tracing.TimelineSliceGroup;
  3261.   var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
  3262.   var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
  3263.  
  3264.   /**
  3265.    * A TimelineThreadSlice represents an interval of time on a thread resource
  3266.    * with associated nestinged slice information.
  3267.    *
  3268.    * ThreadSlices are typically associated with a specific trace event pair on a
  3269.    * specific thread.
  3270.    * For example,
  3271.    *   TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
  3272.    *   TRACE_EVENT_END0()                 at time=0.3ms
  3273.    * This results in a single timeline slice from 0.1 with duration 0.2 on a
  3274.    * specific thread.
  3275.    *
  3276.    * @constructor
  3277.    */
  3278.   function TimelineThreadSlice(cat, title, colorId, start, args, opt_duration) {
  3279.     TimelineSlice.call(this, cat, title, colorId, start, args, opt_duration);
  3280.     // Do not modify this directly.
  3281.     // subSlices is configured by TimelineSliceGroup.rebuildSubRows_.
  3282.     this.subSlices = [];
  3283.   }
  3284.  
  3285.   TimelineThreadSlice.prototype = {
  3286.     __proto__: TimelineSlice.prototype
  3287.   };
  3288.  
  3289.   /**
  3290.    * A TimelineThread stores all the trace events collected for a particular
  3291.    * thread. We organize the synchronous slices on a thread by "subrows," where
  3292.    * subrow 0 has all the root slices, subrow 1 those nested 1 deep, and so on.
  3293.    * The asynchronous slices are stored in an TimelineAsyncSliceGroup object.
  3294.    *
  3295.    * The slices stored on a TimelineThread should be instances of
  3296.    * TimelineThreadSlice.
  3297.    *
  3298.    * @constructor
  3299.    */
  3300.   function TimelineThread(parent, tid) {
  3301.     TimelineSliceGroup.call(this, TimelineThreadSlice);
  3302.     if (!parent)
  3303.       throw new Error('Parent must be provided.');
  3304.     this.pid = parent.pid;
  3305.     this.tid = tid;
  3306.     this.cpuSlices = undefined;
  3307.     this.asyncSlices = new TimelineAsyncSliceGroup(this.ptid);
  3308.   }
  3309.  
  3310.   var ptidMap = {};
  3311.  
  3312.   /**
  3313.    * @return {String} A string that can be used as a unique key for a specific
  3314.    * thread within a process.
  3315.    */
  3316.   TimelineThread.getPTIDFromPidAndTid = function(pid, tid) {
  3317.     if (!ptidMap[pid])
  3318.       ptidMap[pid] = {};
  3319.     if (!ptidMap[pid][tid])
  3320.       ptidMap[pid][tid] = pid + ':' + tid;
  3321.     return ptidMap[pid][tid];
  3322.   }
  3323.  
  3324.   TimelineThread.prototype = {
  3325.  
  3326.     __proto__: TimelineSliceGroup.prototype,
  3327.  
  3328.     /**
  3329.      * Name of the thread, if present.
  3330.      */
  3331.     name: undefined,
  3332.  
  3333.     /**
  3334.      * @return {string} A concatenation of the pid and the thread's
  3335.      * tid. Can be used to uniquely identify a thread.
  3336.      */
  3337.     get ptid() {
  3338.       return TimelineThread.getPTIDFromPidAndTid(this.tid, this.pid);
  3339.     },
  3340.  
  3341.     /**
  3342.      * Shifts all the timestamps inside this thread forward by the amount
  3343.      * specified.
  3344.      */
  3345.     shiftTimestampsForward: function(amount) {
  3346.       TimelineSliceGroup.prototype.shiftTimestampsForward.call(this, amount);
  3347.  
  3348.       if (this.cpuSlices) {
  3349.         for (var i = 0; i < this.cpuSlices.length; i++) {
  3350.           var slice = this.cpuSlices[i];
  3351.           slice.start += amount;
  3352.         }
  3353.       }
  3354.  
  3355.       this.asyncSlices.shiftTimestampsForward(amount);
  3356.     },
  3357.  
  3358.     /**
  3359.      * Determins whether this thread is empty. If true, it usually implies
  3360.      * that it should be pruned from the model.
  3361.      */
  3362.     get isEmpty() {
  3363.       if (this.slices.length)
  3364.         return false;
  3365.       if (this.openSliceCount)
  3366.         return false;
  3367.       if (this.cpuSlices && this.cpuSlices.length)
  3368.         return false;
  3369.       if (this.asyncSlices.length)
  3370.         return false;
  3371.       return true;
  3372.     },
  3373.  
  3374.     /**
  3375.      * Updates the minTimestamp and maxTimestamp fields based on the
  3376.      * current objects associated with the thread.
  3377.      */
  3378.     updateBounds: function() {
  3379.       TimelineSliceGroup.prototype.updateBounds.call(this);
  3380.       var values = [];
  3381.       if (this.minTimestamp !== undefined)
  3382.         values.push(this.minTimestamp, this.maxTimestamp);
  3383.  
  3384.       if (this.asyncSlices.slices.length) {
  3385.         this.asyncSlices.updateBounds();
  3386.         values.push(this.asyncSlices.minTimestamp);
  3387.         values.push(this.asyncSlices.maxTimestamp);
  3388.       }
  3389.  
  3390.       if (this.cpuSlices && this.cpuSlices.length) {
  3391.         values.push(this.cpuSlices[0].start);
  3392.         values.push(this.cpuSlices[this.cpuSlices.length - 1].end);
  3393.       }
  3394.  
  3395.       if (values.length) {
  3396.         this.minTimestamp = Math.min.apply(Math, values);
  3397.         this.maxTimestamp = Math.max.apply(Math, values);
  3398.       } else {
  3399.         this.minTimestamp = undefined;
  3400.         this.maxTimestamp = undefined;
  3401.       }
  3402.     },
  3403.  
  3404.     /**
  3405.      * @return {String} A user-friendly name for this thread.
  3406.      */
  3407.     get userFriendlyName() {
  3408.       var tname = this.name || this.tid;
  3409.       return this.pid + ': ' + tname;
  3410.     },
  3411.  
  3412.     /**
  3413.      * @return {String} User friendly details about this thread.
  3414.      */
  3415.     get userFriendlyDetails() {
  3416.       return 'pid: ' + this.pid +
  3417.           ', tid: ' + this.tid +
  3418.           (this.name ? ', name: ' + this.name : '');
  3419.     }
  3420.   };
  3421.  
  3422.   /**
  3423.    * Comparison between threads that orders first by pid,
  3424.    * then by names, then by tid.
  3425.    */
  3426.   TimelineThread.compare = function(x, y) {
  3427.     if (x.pid != y.pid)
  3428.       return x.pid - y.pid;
  3429.  
  3430.     if (x.name && y.name) {
  3431.       var tmp = x.name.localeCompare(y.name);
  3432.       if (tmp == 0)
  3433.         return x.tid - y.tid;
  3434.       return tmp;
  3435.     } else if (x.name) {
  3436.       return -1;
  3437.     } else if (y.name) {
  3438.       return 1;
  3439.     } else {
  3440.       return x.tid - y.tid;
  3441.     }
  3442.   };
  3443.  
  3444.   return {
  3445.     TimelineThreadSlice: TimelineThreadSlice,
  3446.     TimelineThread: TimelineThread
  3447.   };
  3448. });
  3449.  
  3450. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3451. // Use of this source code is governed by a BSD-style license that can be
  3452. // found in the LICENSE file.
  3453.  
  3454. 'use strict';
  3455.  
  3456. /**
  3457.  * @fileoverview Provides the TimelineCounter class.
  3458.  */
  3459. base.exportTo('tracing', function() {
  3460.  
  3461.   var nextCounterGUID = 1;
  3462.  
  3463.   /**
  3464.    * Stores all the samples for a given counter.
  3465.    * @constructor
  3466.    */
  3467.   function TimelineCounter(parent, id, category, name) {
  3468.     if (parent == null) {
  3469.       this.parent_id = null;
  3470.     } else if (parent.pid != undefined) {
  3471.       this.parent_id = parent.pid;
  3472.     } else if (parent.cpuNumber != undefined) {
  3473.       this.parent_id = parent.cpuNumber;
  3474.     }
  3475.     this.id = id;
  3476.     this.category = category || '';
  3477.     this.name = name;
  3478.     this.seriesNames = [];
  3479.     this.seriesColors = [];
  3480.     this.timestamps = [];
  3481.     this.samples = [];
  3482.     this.guid_ = nextCounterGUID++;
  3483.   }
  3484.  
  3485.   TimelineCounter.prototype = {
  3486.     __proto__: Object.prototype,
  3487.     /*
  3488.      * @return {Number} A globally unique identifier for this counter.
  3489.      */
  3490.     get guid() {
  3491.       return this.guid_;
  3492.     },
  3493.  
  3494.     get numSeries() {
  3495.       return this.seriesNames.length;
  3496.     },
  3497.  
  3498.     get numSamples() {
  3499.       return this.timestamps.length;
  3500.     },
  3501.  
  3502.     getSampleValue: function(index, seriesIndex) {
  3503.       return this.samples[index * this.numSeries + seriesIndex];
  3504.     },
  3505.  
  3506.     /**
  3507.      * Obtains min, max, avg, values, start, and end for different series for
  3508.      * a given counter
  3509.      *     getSampleStatistics([0,1])
  3510.      * The statistics objects that this returns are an array of objects, one
  3511.      * object for each series for the counter in the form:
  3512.      * {min: minVal, max: maxVal, avg: avgVal, start: startVal, end: endVal}
  3513.      *
  3514.      * @param {Array.<Number>} Indices to summarize.
  3515.      * @return {Object} An array of statistics. Each element in the array
  3516.      * has data for one of the series in the selected counter.
  3517.      */
  3518.     getSampleStatistics: function(sampleIndices) {
  3519.       sampleIndices.sort();
  3520.       var sampleIndex = this.sampleIndex;
  3521.       var numSeries = this.numSeries;
  3522.       var numSamples = this.numSamples;
  3523.  
  3524.       var ret = [];
  3525.  
  3526.       for (var i = 0; i < numSeries; ++i) {
  3527.         var sum = 0;
  3528.         var min = Number.MAX_VALUE;
  3529.         var max = -Number.MAX_VALUE;
  3530.         for (var j = 0; j < sampleIndices.length; j++) {
  3531.           var x = sampleIndices[j];
  3532.           sum += this.getSampleValue(x, i);
  3533.           min = Math.min(this.getSampleValue(x, i), min);
  3534.           max = Math.max(this.getSampleValue(x, i), max);
  3535.         }
  3536.         var avg = sum / sampleIndices.length;
  3537.         var start = this.getSampleValue(sampleIndices[0], i);
  3538.         var end = this.getSampleValue(
  3539.             sampleIndices[sampleIndices.length - 1], i);
  3540.  
  3541.         ret.push({min: min,
  3542.           max: max,
  3543.           avg: avg,
  3544.           start: start,
  3545.           end: end});
  3546.       }
  3547.       return ret;
  3548.     },
  3549.  
  3550.     /**
  3551.      * Shifts all the timestamps inside this counter forward by the amount
  3552.      * specified.
  3553.      */
  3554.     shiftTimestampsForward: function(amount) {
  3555.       for (var sI = 0; sI < this.timestamps.length; sI++)
  3556.         this.timestamps[sI] = (this.timestamps[sI] + amount);
  3557.     },
  3558.  
  3559.     /**
  3560.      * Updates the bounds for this counter based on the samples it contains.
  3561.      */
  3562.     updateBounds: function() {
  3563.       if (this.seriesNames.length != this.seriesColors.length)
  3564.         throw new Error('seriesNames.length must match seriesColors.length');
  3565.       if (this.numSeries * this.numSamples != this.samples.length)
  3566.         throw new Error('samples.length must be a multiple of numSamples.');
  3567.  
  3568.       this.totals = [];
  3569.       if (this.samples.length == 0) {
  3570.         this.minTimestamp = undefined;
  3571.         this.maxTimestamp = undefined;
  3572.         this.maxTotal = 0;
  3573.         return;
  3574.       }
  3575.       this.minTimestamp = this.timestamps[0];
  3576.       this.maxTimestamp = this.timestamps[this.timestamps.length - 1];
  3577.  
  3578.       var numSeries = this.numSeries;
  3579.       var maxTotal = -Infinity;
  3580.       for (var i = 0; i < this.timestamps.length; i++) {
  3581.         var total = 0;
  3582.         for (var j = 0; j < numSeries; j++) {
  3583.           total += this.samples[i * numSeries + j];
  3584.           this.totals.push(total);
  3585.         }
  3586.         if (total > maxTotal)
  3587.           maxTotal = total;
  3588.       }
  3589.       this.maxTotal = maxTotal;
  3590.     }
  3591.  
  3592.   };
  3593.  
  3594.   /**
  3595.    * Comparison between counters that orders by parent_id, then name.
  3596.    */
  3597.   TimelineCounter.compare = function(x, y) {
  3598.     if (x.parent_id != y.parent_id) {
  3599.       return x.parent_id - y.parent_id;
  3600.     }
  3601.     var tmp = x.name.localeCompare(y.name);
  3602.     if (tmp == 0)
  3603.       return x.tid - y.tid;
  3604.     return tmp;
  3605.   };
  3606.  
  3607.   return {
  3608.     TimelineCounter: TimelineCounter
  3609.   };
  3610. });
  3611.  
  3612. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3613. // Use of this source code is governed by a BSD-style license that can be
  3614. // found in the LICENSE file.
  3615.  
  3616. 'use strict';
  3617.  
  3618. /**
  3619.  * @fileoverview Provides the TimelineProcess class.
  3620.  */
  3621. base.require('timeline_thread');
  3622. base.require('timeline_counter');
  3623. base.exportTo('tracing', function() {
  3624.  
  3625.   var TimelineThread = tracing.TimelineThread;
  3626.   var TimelineCounter = tracing.TimelineCounter;
  3627.  
  3628.   /**
  3629.    * The TimelineProcess represents a single process in the
  3630.    * trace. Right now, we keep this around purely for bookkeeping
  3631.    * reasons.
  3632.    * @constructor
  3633.    */
  3634.   function TimelineProcess(pid) {
  3635.     this.pid = pid;
  3636.     this.threads = {};
  3637.     this.counters = {};
  3638.   };
  3639.  
  3640.   TimelineProcess.prototype = {
  3641.     get numThreads() {
  3642.       var n = 0;
  3643.       for (var p in this.threads) {
  3644.         n++;
  3645.       }
  3646.       return n;
  3647.     },
  3648.  
  3649.     /**
  3650.      * Shifts all the timestamps inside this process forward by the amount
  3651.      * specified.
  3652.      */
  3653.     shiftTimestampsForward: function(amount) {
  3654.       for (var tid in this.threads)
  3655.         this.threads[tid].shiftTimestampsForward(amount);
  3656.       for (var id in this.counters)
  3657.         this.counters[id].shiftTimestampsForward(amount);
  3658.     },
  3659.  
  3660.     /**
  3661.      * @return {TimlineThread} The thread identified by tid on this process,
  3662.      * creating it if it doesn't exist.
  3663.      */
  3664.     getOrCreateThread: function(tid) {
  3665.       if (!this.threads[tid])
  3666.         this.threads[tid] = new TimelineThread(this, tid);
  3667.       return this.threads[tid];
  3668.     },
  3669.  
  3670.     /**
  3671.      * @return {TimlineCounter} The counter on this process named 'name',
  3672.      * creating it if it doesn't exist.
  3673.      */
  3674.     getOrCreateCounter: function(cat, name) {
  3675.       var id = cat + '.' + name;
  3676.       if (!this.counters[id])
  3677.         this.counters[id] = new TimelineCounter(this, id, cat, name);
  3678.       return this.counters[id];
  3679.     }
  3680.   };
  3681.  
  3682.   /**
  3683.    * Comparison between processes that orders by pid.
  3684.    */
  3685.   TimelineProcess.compare = function(x, y) {
  3686.     return x.pid - y.pid;
  3687.   };
  3688.  
  3689.   return {
  3690.     TimelineProcess: TimelineProcess
  3691.   };
  3692. });
  3693.  
  3694. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3695. // Use of this source code is governed by a BSD-style license that can be
  3696. // found in the LICENSE file.
  3697.  
  3698. 'use strict';
  3699.  
  3700. /**
  3701.  * @fileoverview Provides the TimelineCpu class.
  3702.  */
  3703. base.require('timeline_slice');
  3704. base.require('timeline_counter');
  3705. base.exportTo('tracing', function() {
  3706.  
  3707.   var TimelineCounter = tracing.TimelineCounter;
  3708.  
  3709.   /**
  3710.    * The TimelineCpu represents a Cpu from the kernel's point of view.
  3711.    * @constructor
  3712.    */
  3713.   function TimelineCpu(number) {
  3714.     this.cpuNumber = number;
  3715.     this.slices = [];
  3716.     this.counters = {};
  3717.   };
  3718.  
  3719.   TimelineCpu.prototype = {
  3720.     /**
  3721.      * @return {TimlineCounter} The counter on this process named 'name',
  3722.      * creating it if it doesn't exist.
  3723.      */
  3724.     getOrCreateCounter: function(cat, name) {
  3725.       var id;
  3726.       if (cat.length)
  3727.         id = cat + '.' + name;
  3728.       else
  3729.         id = name;
  3730.       if (!this.counters[id])
  3731.         this.counters[id] = new TimelineCounter(this, id, cat, name);
  3732.       return this.counters[id];
  3733.     },
  3734.  
  3735.     /**
  3736.      * Shifts all the timestamps inside this CPU forward by the amount
  3737.      * specified.
  3738.      */
  3739.     shiftTimestampsForward: function(amount) {
  3740.       for (var sI = 0; sI < this.slices.length; sI++)
  3741.         this.slices[sI].start = (this.slices[sI].start + amount);
  3742.       for (var id in this.counters)
  3743.         this.counters[id].shiftTimestampsForward(amount);
  3744.     },
  3745.  
  3746.     /**
  3747.      * Updates the minTimestamp and maxTimestamp fields based on the
  3748.      * current slices attached to the cpu.
  3749.      */
  3750.     updateBounds: function() {
  3751.       var values = [];
  3752.       if (this.slices.length) {
  3753.         this.minTimestamp = this.slices[0].start;
  3754.         this.maxTimestamp = this.slices[this.slices.length - 1].end;
  3755.       } else {
  3756.         this.minTimestamp = undefined;
  3757.         this.maxTimestamp = undefined;
  3758.       }
  3759.     }
  3760.   };
  3761.  
  3762.   /**
  3763.    * Comparison between processes that orders by cpuNumber.
  3764.    */
  3765.   TimelineCpu.compare = function(x, y) {
  3766.     return x.cpuNumber - y.cpuNumber;
  3767.   };
  3768.  
  3769.  
  3770.   return {
  3771.     TimelineCpu: TimelineCpu
  3772.   };
  3773. });
  3774.  
  3775. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3776. // Use of this source code is governed by a BSD-style license that can be
  3777. // found in the LICENSE file.
  3778.  
  3779. 'use strict';
  3780.  
  3781. /**
  3782.  * @fileoverview TimelineModel is a parsed representation of the
  3783.  * TraceEvents obtained from base/trace_event in which the begin-end
  3784.  * tokens are converted into a hierarchy of processes, threads,
  3785.  * subrows, and slices.
  3786.  *
  3787.  * The building block of the model is a slice. A slice is roughly
  3788.  * equivalent to function call executing on a specific thread. As a
  3789.  * result, slices may have one or more subslices.
  3790.  *
  3791.  * A thread contains one or more subrows of slices. Row 0 corresponds to
  3792.  * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
  3793.  * are nested 1 deep in the stack, and so on. We use these subrows to draw
  3794.  * nesting tasks.
  3795.  *
  3796.  */
  3797. base.require('event_target');
  3798. base.require('timeline_process');
  3799. base.require('timeline_cpu');
  3800. base.require('timeline_filter');
  3801. base.exportTo('tracing', function() {
  3802.  
  3803.   var TimelineProcess = tracing.TimelineProcess;
  3804.   var TimelineCpu = tracing.TimelineCpu;
  3805.  
  3806.   /**
  3807.    * Builds a model from an array of TraceEvent objects.
  3808.    * @param {Object=} opt_eventData Data from a single trace to be imported into
  3809.    *     the new model. See TimelineModel.importTraces for details on how to
  3810.    *     import multiple traces at once.
  3811.    * @param {bool=} opt_shiftWorldToZero Whether to shift the world to zero.
  3812.    * Defaults to true.
  3813.    * @constructor
  3814.    */
  3815.   function TimelineModel(opt_eventData, opt_shiftWorldToZero) {
  3816.     this.cpus = {};
  3817.     this.processes = {};
  3818.     this.importErrors = [];
  3819.     this.metadata = [];
  3820.     this.categories = [];
  3821.  
  3822.     if (opt_eventData)
  3823.       this.importTraces([opt_eventData], opt_shiftWorldToZero);
  3824.   }
  3825.  
  3826.   var importerConstructors = [];
  3827.  
  3828.   /**
  3829.    * Registers an importer. All registered importers are considered
  3830.    * when processing an import request.
  3831.    *
  3832.    * @param {Function} importerConstructor The importer's constructor function.
  3833.    */
  3834.   TimelineModel.registerImporter = function(importerConstructor) {
  3835.     importerConstructors.push(importerConstructor);
  3836.   };
  3837.  
  3838.   function TimelineModelEmptyImporter(events) {
  3839.     this.importPriority = 0;
  3840.   };
  3841.  
  3842.   TimelineModelEmptyImporter.canImport = function(eventData) {
  3843.     if (eventData instanceof Array && eventData.length == 0)
  3844.       return true;
  3845.     if (typeof(eventData) === 'string' || eventData instanceof String) {
  3846.       return eventData.length == 0;
  3847.     }
  3848.     return false;
  3849.   };
  3850.  
  3851.   TimelineModelEmptyImporter.prototype = {
  3852.     __proto__: Object.prototype,
  3853.  
  3854.     importEvents: function() {
  3855.     },
  3856.     finalizeImport: function() {
  3857.     }
  3858.   };
  3859.  
  3860.   TimelineModel.registerImporter(TimelineModelEmptyImporter);
  3861.  
  3862.   TimelineModel.prototype = {
  3863.     __proto__: base.EventTarget.prototype,
  3864.  
  3865.     get numProcesses() {
  3866.       var n = 0;
  3867.       for (var p in this.processes)
  3868.         n++;
  3869.       return n;
  3870.     },
  3871.  
  3872.     /**
  3873.      * @return {TimelineProcess} Gets a specific TimelineCpu or creates one if
  3874.      * it does not exist.
  3875.      */
  3876.     getOrCreateCpu: function(cpuNumber) {
  3877.       if (!this.cpus[cpuNumber])
  3878.         this.cpus[cpuNumber] = new TimelineCpu(cpuNumber);
  3879.       return this.cpus[cpuNumber];
  3880.     },
  3881.  
  3882.     /**
  3883.      * @return {TimelineProcess} Gets a TimlineProcess for a specified pid or
  3884.      * creates one if it does not exist.
  3885.      */
  3886.     getOrCreateProcess: function(pid) {
  3887.       if (!this.processes[pid])
  3888.         this.processes[pid] = new TimelineProcess(pid);
  3889.       return this.processes[pid];
  3890.     },
  3891.  
  3892.     /**
  3893.      * Closes any slices that need closing
  3894.      */
  3895.     autoCloseOpenSlices_: function() {
  3896.       this.updateBounds();
  3897.       var maxTimestamp = this.maxTimestamp;
  3898.       for (var pid in this.processes) {
  3899.         var process = this.processes[pid];
  3900.         for (var tid in process.threads) {
  3901.           var thread = process.threads[tid];
  3902.           thread.autoCloseOpenSlices(maxTimestamp);
  3903.         }
  3904.       }
  3905.     },
  3906.  
  3907.     /**
  3908.      * Generates the set of categories from the slices and counters.
  3909.      */
  3910.     updateCategories_: function() {
  3911.       var threads = this.getAllThreads();
  3912.       for (var tI = 0; tI < threads.length; tI++) {
  3913.         var slices = threads[tI].slices;
  3914.         for (var i = 0; i < slices.length; i++) {
  3915.           var category = slices[i].category;
  3916.           if (category && this.categories.indexOf(category) == -1) {
  3917.             this.categories.push(category);
  3918.           }
  3919.         }
  3920.       }
  3921.       var counters = this.getAllCounters();
  3922.       for (var tI = 0; tI < counters.length; tI++) {
  3923.         var category = counters[tI].category;
  3924.         if (category && this.categories.indexOf(category) == -1) {
  3925.           this.categories.push(category);
  3926.         }
  3927.       }
  3928.       for (var cpu in this.cpus) {
  3929.         var slices = this.cpus[cpu].slices;
  3930.         for (var i = 0; i < slices.length; i++) {
  3931.           var category = slices[i].category;
  3932.           if (category && this.categories.indexOf(category) == -1) {
  3933.             this.categories.push(category);
  3934.           }
  3935.         }
  3936.       }
  3937.     },
  3938.  
  3939.     /**
  3940.      * Removes threads from the model that are fully empty.
  3941.      */
  3942.     pruneEmptyThreads_: function() {
  3943.       for (var pid in this.processes) {
  3944.         var process = this.processes[pid];
  3945.         var threadsToKeep = {};
  3946.         for (var tid in process.threads) {
  3947.           var thread = process.threads[tid];
  3948.           if (!thread.isEmpty)
  3949.             threadsToKeep[tid] = thread;
  3950.         }
  3951.         process.threads = threadsToKeep;
  3952.       }
  3953.     },
  3954.  
  3955.     updateBounds: function() {
  3956.       var wmin = Infinity;
  3957.       var wmax = -wmin;
  3958.       var hasData = false;
  3959.  
  3960.       var threads = this.getAllThreads();
  3961.       for (var tI = 0; tI < threads.length; tI++) {
  3962.         var thread = threads[tI];
  3963.         thread.updateBounds();
  3964.         if (thread.minTimestamp != undefined &&
  3965.             thread.maxTimestamp != undefined) {
  3966.           wmin = Math.min(wmin, thread.minTimestamp);
  3967.           wmax = Math.max(wmax, thread.maxTimestamp);
  3968.           hasData = true;
  3969.         }
  3970.       }
  3971.       var counters = this.getAllCounters();
  3972.       for (var tI = 0; tI < counters.length; tI++) {
  3973.         var counter = counters[tI];
  3974.         counter.updateBounds();
  3975.         if (counter.minTimestamp != undefined &&
  3976.             counter.maxTimestamp != undefined) {
  3977.           hasData = true;
  3978.           wmin = Math.min(wmin, counter.minTimestamp);
  3979.           wmax = Math.max(wmax, counter.maxTimestamp);
  3980.         }
  3981.       }
  3982.  
  3983.       for (var cpuNumber in this.cpus) {
  3984.         var cpu = this.cpus[cpuNumber];
  3985.         cpu.updateBounds();
  3986.         if (cpu.minTimestamp != undefined &&
  3987.             cpu.maxTimestamp != undefined) {
  3988.           hasData = true;
  3989.           wmin = Math.min(wmin, cpu.minTimestamp);
  3990.           wmax = Math.max(wmax, cpu.maxTimestamp);
  3991.         }
  3992.       }
  3993.  
  3994.       if (hasData) {
  3995.         this.minTimestamp = wmin;
  3996.         this.maxTimestamp = wmax;
  3997.       } else {
  3998.         this.maxTimestamp = undefined;
  3999.         this.minTimestamp = undefined;
  4000.       }
  4001.     },
  4002.  
  4003.     shiftWorldToZero: function() {
  4004.       if (this.minTimestamp === undefined)
  4005.         return;
  4006.       var timeBase = this.minTimestamp;
  4007.       for (var pid in this.processes)
  4008.         this.processes[pid].shiftTimestampsForward(-timeBase);
  4009.       for (var cpuNumber in this.cpus)
  4010.         this.cpus[cpuNumber].shiftTimestampsForward(-timeBase);
  4011.       this.updateBounds();
  4012.     },
  4013.  
  4014.     getAllThreads: function() {
  4015.       var threads = [];
  4016.       for (var pid in this.processes) {
  4017.         var process = this.processes[pid];
  4018.         for (var tid in process.threads) {
  4019.           threads.push(process.threads[tid]);
  4020.         }
  4021.       }
  4022.       return threads;
  4023.     },
  4024.  
  4025.     /**
  4026.      * @return {Array} An array of all cpus in the model.
  4027.      */
  4028.     getAllCpus: function() {
  4029.       var cpus = [];
  4030.       for (var cpu in this.cpus)
  4031.         cpus.push(this.cpus[cpu]);
  4032.       return cpus;
  4033.     },
  4034.  
  4035.     /**
  4036.      * @return {Array} An array of all processes in the model.
  4037.      */
  4038.     getAllProcesses: function() {
  4039.       var processes = [];
  4040.       for (var pid in this.processes)
  4041.         processes.push(this.processes[pid]);
  4042.       return processes;
  4043.     },
  4044.  
  4045.     /**
  4046.      * @return {Array} An array of all the counters in the model.
  4047.      */
  4048.     getAllCounters: function() {
  4049.       var counters = [];
  4050.       for (var pid in this.processes) {
  4051.         var process = this.processes[pid];
  4052.         for (var tid in process.counters) {
  4053.           counters.push(process.counters[tid]);
  4054.         }
  4055.       }
  4056.       for (var cpuNumber in this.cpus) {
  4057.         var cpu = this.cpus[cpuNumber];
  4058.         for (var counterName in cpu.counters)
  4059.           counters.push(cpu.counters[counterName]);
  4060.       }
  4061.       return counters;
  4062.     },
  4063.  
  4064.     /**
  4065.      * @param {String} The name of the thread to find.
  4066.      * @return {Array} An array of all the matched threads.
  4067.      */
  4068.     findAllThreadsNamed: function(name) {
  4069.       var namedThreads = [];
  4070.       var threads = this.getAllThreads();
  4071.       for (var i = 0; i < threads.length; i++) {
  4072.         var thread = threads[i];
  4073.         if (thread.name == name)
  4074.           namedThreads.push(thread);
  4075.       }
  4076.       return namedThreads;
  4077.     },
  4078.  
  4079.     createImporter_: function(eventData) {
  4080.       var importerConstructor;
  4081.       for (var i = 0; i < importerConstructors.length; ++i) {
  4082.         if (importerConstructors[i].canImport(eventData)) {
  4083.           importerConstructor = importerConstructors[i];
  4084.           break;
  4085.         }
  4086.       }
  4087.       if (!importerConstructor)
  4088.         throw new Error(
  4089.             'Could not find an importer for the provided eventData.');
  4090.  
  4091.       var importer = new importerConstructor(
  4092.           this, eventData);
  4093.       return importer;
  4094.     },
  4095.  
  4096.     /**
  4097.      * Imports the provided traces into the model. The eventData type
  4098.      * is undefined and will be passed to all the timeline importers registered
  4099.      * via TimelineModel.registerImporter. The first importer that returns true
  4100.      * for canImport(events) will be used to import the events.
  4101.      *
  4102.      * The primary trace is provided via the eventData variable. If multiple
  4103.      * traces are to be imported, specify the first one as events, and the
  4104.      * remainder in the opt_additionalEventData array.
  4105.      *
  4106.      * @param {Array} traces An array of eventData to be imported. Each
  4107.      * eventData should correspond to a single trace file and will be handled by
  4108.      * a separate importer.
  4109.      * @param {bool=} opt_shiftWorldToZero Whether to shift the world to zero.
  4110.      * Defaults to true.
  4111.      */
  4112.     importTraces: function(traces,
  4113.                            opt_shiftWorldToZero) {
  4114.       if (opt_shiftWorldToZero === undefined)
  4115.         opt_shiftWorldToZero = true;
  4116.  
  4117.       // Figure out which importers to use.
  4118.       var importers = [];
  4119.       for (var i = 0; i < traces.length; ++i)
  4120.         importers.push(this.createImporter_(traces[i]));
  4121.  
  4122.       // Sort them on priority. This ensures importing happens in a predictable
  4123.       // order, e.g. linux_perf_importer before trace_event_importer.
  4124.       importers.sort(function(x, y) {
  4125.         return x.importPriority - y.importPriority;
  4126.       });
  4127.  
  4128.       // Run the import.
  4129.       for (var i = 0; i < importers.length; i++)
  4130.         importers[i].importEvents(i > 0);
  4131.  
  4132.       this.autoCloseOpenSlices_();
  4133.  
  4134.       for (var i = 0; i < importers.length; i++)
  4135.         importers[i].finalizeImport();
  4136.  
  4137.       this.pruneEmptyThreads_();
  4138.       this.updateBounds();
  4139.  
  4140.       this.updateCategories_();
  4141.  
  4142.       if (opt_shiftWorldToZero)
  4143.         this.shiftWorldToZero();
  4144.     }
  4145.   };
  4146.  
  4147.   return {
  4148.     TimelineModel: TimelineModel
  4149.   };
  4150.  
  4151. });
  4152.  
  4153. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4154. // Use of this source code is governed by a BSD-style license that can be
  4155. // found in the LICENSE file.
  4156.  
  4157. 'use strict';
  4158.  
  4159. base.require('tracks.timeline_container_track');
  4160. base.require('tracks.timeline_slice_track');
  4161. base.require('timeline_filter');
  4162. base.require('timeline_model');
  4163. base.require('ui');
  4164. base.exportTo('tracks', function() {
  4165.  
  4166.   /**
  4167.    * Visualizes a TimelineCpu using a series of of TimelineSliceTracks.
  4168.    * @constructor
  4169.    */
  4170.   var TimelineCpuTrack = base.ui.define(tracks.TimelineContainerTrack);
  4171.   TimelineCpuTrack.prototype = {
  4172.     __proto__: tracks.TimelineContainerTrack.prototype,
  4173.  
  4174.     decorate: function() {
  4175.       this.classList.add('timeline-cpu-track');
  4176.     },
  4177.  
  4178.     get cpu() {
  4179.       return this.cpu_;
  4180.     },
  4181.  
  4182.     set cpu(cpu) {
  4183.       this.cpu_ = cpu;
  4184.       this.updateChildTracks_();
  4185.     },
  4186.  
  4187.     get tooltip() {
  4188.       return this.tooltip_;
  4189.     },
  4190.  
  4191.     set tooltip(value) {
  4192.       this.tooltip_ = value;
  4193.       this.updateChildTracks_();
  4194.     },
  4195.  
  4196.     get heading() {
  4197.       return this.heading_;
  4198.     },
  4199.  
  4200.     set heading(h) {
  4201.       this.heading_ = h;
  4202.       this.updateChildTracks_();
  4203.     },
  4204.  
  4205.     applyCategoryFilter_: function() {
  4206.       if (this.categoryFilter.matchCpu(this.cpu_))
  4207.         this.updateChildTracks_();
  4208.       else
  4209.         this.visible = false;
  4210.     },
  4211.  
  4212.     updateChildTracks_: function() {
  4213.       this.detach();
  4214.       if (this.cpu_) {
  4215.         var slices = tracing.filterSliceArray(this.categoryFilter_,
  4216.                                               this.cpu_.slices);
  4217.         if (slices.length) {
  4218.           var track = new tracks.TimelineSliceTrack();
  4219.           track.slices = slices;
  4220.           track.heading = this.heading_;
  4221.           track.tooltip = this.tooltip_;
  4222.           this.addTrack_(track);
  4223.         }
  4224.  
  4225.         for (var counterName in this.cpu_.counters) {
  4226.           var counter = this.cpu_.counters[counterName];
  4227.           track = new tracks.TimelineCounterTrack();
  4228.           track.heading = 'CPU ' + this.cpu_.cpuNumber + ' ' +
  4229.               counter.name + ':';
  4230.           track.counter = counter;
  4231.           this.addTrack_(track);
  4232.         }
  4233.       }
  4234.       this.addControlButtonElements_(false);
  4235.     }
  4236.   };
  4237.  
  4238.   return {
  4239.     TimelineCpuTrack: TimelineCpuTrack
  4240.   };
  4241. });
  4242.  
  4243. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4244. // Use of this source code is governed by a BSD-style license that can be
  4245. // found in the LICENSE file.
  4246.  
  4247. 'use strict';
  4248.  
  4249. base.requireStylesheet('tracks.timeline_counter_track');
  4250.  
  4251. base.require('tracks.timeline_canvas_based_track');
  4252. base.require('timeline_color_scheme');
  4253. base.require('ui');
  4254.  
  4255. base.exportTo('tracks', function() {
  4256.  
  4257.   var palette = tracing.getColorPalette();
  4258.  
  4259.   /**
  4260.    * A track that displays a TimelineCounter object.
  4261.    * @constructor
  4262.    * @extends {CanvasBasedTrack}
  4263.    */
  4264.  
  4265.   var TimelineCounterTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
  4266.  
  4267.   TimelineCounterTrack.prototype = {
  4268.  
  4269.     __proto__: tracks.TimelineCanvasBasedTrack.prototype,
  4270.  
  4271.     decorate: function() {
  4272.       this.classList.add('timeline-counter-track');
  4273.       this.addControlButtonElements_(false);
  4274.       this.selectedSamples_ = {};
  4275.       this.categoryFilter_ = new tracing.TimelineFilter();
  4276.     },
  4277.  
  4278.     /**
  4279.      * Called by all the addToSelection functions on the created selection
  4280.      * hit objects. Override this function on parent classes to add
  4281.      * context-specific information to the hit.
  4282.      */
  4283.     decorateHit: function(hit) {
  4284.     },
  4285.  
  4286.     get counter() {
  4287.       return this.counter_;
  4288.     },
  4289.  
  4290.     set counter(counter) {
  4291.       this.counter_ = counter;
  4292.       this.invalidate();
  4293.       this.updateVisibility_();
  4294.     },
  4295.  
  4296.     set categoryFilter(v) {
  4297.       this.categoryFilter_ = v;
  4298.       this.updateVisibility_();
  4299.     },
  4300.  
  4301.     /**
  4302.      * @return {Object} A sparse, mutable map from sample index to bool. Samples
  4303.      * indices the map that are true are drawn as selected. Callers that mutate
  4304.      * the map must manually call invalidate on the track to trigger a redraw.
  4305.      */
  4306.     get selectedSamples() {
  4307.       return this.selectedSamples_;
  4308.     },
  4309.  
  4310.     updateVisibility_: function() {
  4311.       this.visible = (this.counter_ &&
  4312.                       this.categoryFilter_.matchCounter(this.counter_));
  4313.     },
  4314.  
  4315.     redraw: function() {
  4316.       var ctr = this.counter_;
  4317.       var ctx = this.ctx_;
  4318.       var canvasW = this.canvas_.width;
  4319.       var canvasH = this.canvas_.height;
  4320.  
  4321.       ctx.clearRect(0, 0, canvasW, canvasH);
  4322.  
  4323.       // Culling parametrs.
  4324.       var vp = this.viewport_;
  4325.       var pixWidth = vp.xViewVectorToWorld(1);
  4326.       var viewLWorld = vp.xViewToWorld(0);
  4327.       var viewRWorld = vp.xViewToWorld(canvasW);
  4328.  
  4329.       // Give the viewport a chance to draw onto this canvas.
  4330.       vp.drawUnderContent(ctx, viewLWorld, viewRWorld, canvasH);
  4331.  
  4332.       // Drop sampels that are less than skipDistancePix apart.
  4333.       var skipDistancePix = 1;
  4334.       var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix);
  4335.  
  4336.       // Begin rendering in world space.
  4337.       ctx.save();
  4338.       vp.applyTransformToCanvas(ctx);
  4339.  
  4340.       // Figure out where drawing should begin.
  4341.       var numSeries = ctr.numSeries;
  4342.       var numSamples = ctr.numSamples;
  4343.       var startIndex = tracing.findLowIndexInSortedArray(ctr.timestamps,
  4344.                                                          function(x) {
  4345.                                                            return x;
  4346.                                                          },
  4347.                                                          viewLWorld);
  4348.       startIndex = startIndex - 1 > 0 ? startIndex - 1 : 0;
  4349.  
  4350.       // Draw indices one by one until we fall off the viewRWorld.
  4351.       var yScale = canvasH / ctr.maxTotal;
  4352.       for (var seriesIndex = ctr.numSeries - 1;
  4353.            seriesIndex >= 0; seriesIndex--) {
  4354.         var colorId = ctr.seriesColors[seriesIndex];
  4355.         ctx.fillStyle = palette[colorId];
  4356.         ctx.beginPath();
  4357.  
  4358.         // Set iLast and xLast such that the first sample we draw is the
  4359.         // startIndex sample.
  4360.         var iLast = startIndex - 1;
  4361.         var xLast = iLast >= 0 ? ctr.timestamps[iLast] - skipDistanceWorld : -1;
  4362.         var yLastView = canvasH;
  4363.  
  4364.         // Iterate over samples from iLast onward until we either fall off the
  4365.         // viewRWorld or we run out of samples. To avoid drawing too much, after
  4366.         // drawing a sample at xLast, skip subsequent samples that are less than
  4367.         // skipDistanceWorld from xLast.
  4368.         var hasMoved = false;
  4369.         while (true) {
  4370.           var i = iLast + 1;
  4371.           if (i >= numSamples) {
  4372.             ctx.lineTo(xLast, yLastView);
  4373.             ctx.lineTo(xLast + 8 * pixWidth, yLastView);
  4374.             ctx.lineTo(xLast + 8 * pixWidth, canvasH);
  4375.             break;
  4376.           }
  4377.  
  4378.           var x = ctr.timestamps[i];
  4379.  
  4380.           var y = ctr.totals[i * numSeries + seriesIndex];
  4381.           var yView = canvasH - (yScale * y);
  4382.  
  4383.           if (x > viewRWorld) {
  4384.             ctx.lineTo(x, yLastView);
  4385.             ctx.lineTo(x, canvasH);
  4386.             break;
  4387.           }
  4388.  
  4389.           if (x - xLast < skipDistanceWorld) {
  4390.             iLast = i;
  4391.             continue;
  4392.           }
  4393.  
  4394.           if (!hasMoved) {
  4395.             ctx.moveTo(viewLWorld, canvasH);
  4396.             hasMoved = true;
  4397.           }
  4398.           ctx.lineTo(x, yLastView);
  4399.           ctx.lineTo(x, yView);
  4400.           iLast = i;
  4401.           xLast = x;
  4402.           yLastView = yView;
  4403.         }
  4404.         ctx.closePath();
  4405.         ctx.fill();
  4406.       }
  4407.       ctx.fillStyle = 'rgba(255, 0, 0, 1)';
  4408.       for (var i in this.selectedSamples_) {
  4409.         if (!this.selectedSamples_[i])
  4410.           continue;
  4411.  
  4412.         var x = ctr.timestamps[i];
  4413.         for (var seriesIndex = ctr.numSeries - 1;
  4414.              seriesIndex >= 0; seriesIndex--) {
  4415.           var y = ctr.totals[i * numSeries + seriesIndex];
  4416.           var yView = canvasH - (yScale * y);
  4417.           ctx.fillRect(x - pixWidth, yView - 1, 3 * pixWidth, 3);
  4418.         }
  4419.       }
  4420.       ctx.restore();
  4421.  
  4422.       // Give the viewport a chance to draw over this canvas.
  4423.       vp.drawOverContent(ctx, viewLWorld, viewRWorld, canvasH);
  4424.     },
  4425.  
  4426.     /**
  4427.      * Adds items intersecting a point to a selection.
  4428.      * @param {number} vX X location to search at, in viewspace.
  4429.      * @param {number} vY Y location to search at, in viewspace.
  4430.      * @param {TimelineSelection} selection Selection to which to add hits.
  4431.      * @return {boolean} true if a slice was found, otherwise false.
  4432.      */
  4433.     addIntersectingItemsToSelection: function(vX, vY, selection) {
  4434.       var clientRect = this.getBoundingClientRect();
  4435.       if (vY < clientRect.top || vY >= clientRect.bottom)
  4436.         return false;
  4437.  
  4438.       var pixelRatio = window.devicePixelRatio || 1;
  4439.       var wX = this.viewport_.xViewVectorToWorld(vX * devicePixelRatio);
  4440.  
  4441.       var ctr = this.counter_;
  4442.       if (vX < this.counter_.timestamps[0])
  4443.         return false;
  4444.       var i = tracing.findLowIndexInSortedArray(ctr.timestamps,
  4445.                                                 function(x) { return x; },
  4446.                                                 wX);
  4447.       if (i < 0 || i >= ctr.timestamps.length)
  4448.         return false;
  4449.  
  4450.       // Sample i is going to either be exactly at wX or slightly above it,
  4451.       // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed.
  4452.       if (i > 0 && wX > this.counter_.timestamps[i - 1])
  4453.         i--;
  4454.  
  4455.       // Some preliminaries.
  4456.       var canvasH = this.getBoundingClientRect().height;
  4457.       var yScale = canvasH / ctr.maxTotal;
  4458.  
  4459.       /*
  4460.       // Figure out which sample we hit
  4461.       var seriesIndexHit;
  4462.       for (var seriesIndex = 0; seriesIndex < ctr.numSeries; seriesIndex++) {
  4463.         var y = ctr.totals[i * ctr.numSeries + seriesIndex];
  4464.         var yView = canvasH - (yScale * y) + clientRect.top;
  4465.         if (wY >= yView) {
  4466.           seriesIndexHit = seriesIndex;
  4467.           break;
  4468.         }
  4469.       }
  4470.       if (seriesIndexHit === undefined)
  4471.         return false;
  4472.       */
  4473.       var hit = selection.addCounterSample(this, this.counter, i);
  4474.       this.decorateHit(hit);
  4475.       return true;
  4476.     },
  4477.  
  4478.     /**
  4479.      * Adds items intersecting the given range to a selection.
  4480.      * @param {number} loVX Lower X bound of the interval to search, in
  4481.      *     viewspace.
  4482.      * @param {number} hiVX Upper X bound of the interval to search, in
  4483.      *     viewspace.
  4484.      * @param {number} loVY Lower Y bound of the interval to search, in
  4485.      *     viewspace.
  4486.      * @param {number} hiVY Upper Y bound of the interval to search, in
  4487.      *     viewspace.
  4488.      * @param {TimelineSelection} selection Selection to which to add hits.
  4489.      */
  4490.     addIntersectingItemsInRangeToSelection: function(
  4491.         loVX, hiVX, loVY, hiVY, selection) {
  4492.  
  4493.       var clientRect = this.getBoundingClientRect();
  4494.       var a = Math.max(loVY, clientRect.top);
  4495.       var b = Math.min(hiVY, clientRect.bottom);
  4496.       if (a > b)
  4497.         return;
  4498.  
  4499.       var ctr = this.counter_;
  4500.  
  4501.       var pixelRatio = window.devicePixelRatio || 1;
  4502.       var loWX = this.viewport_.xViewToWorld(loVX * pixelRatio);
  4503.       var hiWX = this.viewport_.xViewToWorld(hiVX * pixelRatio);
  4504.  
  4505.       var iLo = tracing.findLowIndexInSortedArray(ctr.timestamps,
  4506.                                                   function(x) { return x; },
  4507.                                                   loWX);
  4508.       var iHi = tracing.findLowIndexInSortedArray(ctr.timestamps,
  4509.                                                   function(x) { return x; },
  4510.                                                   hiWX);
  4511.  
  4512.       // Sample i is going to either be exactly at wX or slightly above it,
  4513.       // E.g. asking for 7.5 in [7,8] gives i=1. So bump i back by 1 if needed.
  4514.       if (iLo > 0 && loWX > ctr.timestamps[iLo - 1])
  4515.         iLo--;
  4516.       if (iHi > 0 && hiWX > ctr.timestamps[iHi - 1])
  4517.         iHi--;
  4518.  
  4519.       // Iterate over every sample intersecting..
  4520.       for (var i = iLo; i <= iHi; i++) {
  4521.         if (i >= ctr.timestamps.length)
  4522.           continue;
  4523.  
  4524.         // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values.
  4525.         var hit = selection.addCounterSample(this, this.counter, i);
  4526.         this.decorateHit(hit);
  4527.       }
  4528.     },
  4529.  
  4530.     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
  4531.     }
  4532.   };
  4533.  
  4534.   return {
  4535.     TimelineCounterTrack: TimelineCounterTrack
  4536.   };
  4537. });
  4538.  
  4539. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4540. // Use of this source code is governed by a BSD-style license that can be
  4541. // found in the LICENSE file.
  4542.  
  4543. 'use strict';
  4544.  
  4545. base.require('tracks.timeline_container_track');
  4546. base.require('sorted_array_utils');
  4547. base.require('ui');
  4548.  
  4549. base.exportTo('tracks', function() {
  4550.  
  4551.   /**
  4552.    * A track that displays a TimelineSliceGroup.
  4553.    * @constructor
  4554.    * @extends {TimelineContainerTrack}
  4555.    */
  4556.  
  4557.   var TimelineSliceGroupTrack = base.ui.define(tracks.TimelineContainerTrack);
  4558.  
  4559.   TimelineSliceGroupTrack.prototype = {
  4560.  
  4561.     __proto__: tracks.TimelineContainerTrack.prototype,
  4562.  
  4563.     decorate: function() {
  4564.       this.classList.add('timeline-slice-group-track');
  4565.     },
  4566.  
  4567.     get group() {
  4568.       return this.group_;
  4569.     },
  4570.  
  4571.     set group(g) {
  4572.       this.group_ = g;
  4573.       this.updateChildTracks_();
  4574.     },
  4575.  
  4576.     set heading(h) {
  4577.       if (this.tracks_.length)
  4578.         this.tracks_[0].heading = h;
  4579.     },
  4580.  
  4581.     set tooltip(t) {
  4582.       if (this.tracks_.length)
  4583.         this.tracks_[0].tooltip = t;
  4584.     },
  4585.  
  4586.     set decorateHit(f) {
  4587.       this.decorateHit_ = f;
  4588.       this.updateChildTracks_();
  4589.     },
  4590.  
  4591.     applyCategoryFilter_: function() {
  4592.       this.updateChildTracks_();
  4593.     },
  4594.  
  4595.     addSliceTrack_: function(slices) {
  4596.       var track = new tracks.TimelineSliceTrack();
  4597.       track.slices = slices;
  4598.       track.decorateHit = this.decorateHit_;
  4599.       this.addTrack_(track);
  4600.       return track;
  4601.     },
  4602.  
  4603.     updateChildTracks_: function() {
  4604.       if (!this.group_) {
  4605.         this.visible = false;
  4606.         return;
  4607.       }
  4608.  
  4609.       var slices = tracing.filterSliceArray(this.categoryFilter,
  4610.                                             this.group_.slices);
  4611.       if (!slices.length) {
  4612.         this.visible = false;
  4613.         return;
  4614.       }
  4615.       this.visible = true;
  4616.  
  4617.       if (this.areArrayContentsSame_(this.filteredSlices_, slices))
  4618.         return;
  4619.  
  4620.       this.filteredSlices_ = slices;
  4621.       this.detach();
  4622.       this.subRows_ = this.buildSubRows_(slices);
  4623.       for (var srI = 0; srI < this.subRows_.length; srI++) {
  4624.         if (this.subRows_[srI].length) {
  4625.           this.addSliceTrack_(this.subRows_[srI]);
  4626.         }
  4627.       }
  4628.     },
  4629.  
  4630.     /**
  4631.      * Breaks up the list of slices into N rows, each of which is a list of
  4632.      * slices that are non overlapping.
  4633.      */
  4634.     buildSubRows_: function(slices) {
  4635.       // This function works by walking through slices by start time.
  4636.       //
  4637.       // The basic idea here is to insert each slice as deep into the subrow
  4638.       // list as it can go such that every subSlice is fully contained by its
  4639.       // parent slice.
  4640.       //
  4641.       // Visually, if we start with this:
  4642.       //  0:  [    a       ]
  4643.       //  1:    [  b  ]
  4644.       //  2:    [c][d]
  4645.       //
  4646.       // To place this slice:
  4647.       //               [e]
  4648.       // We first check row 2's last item, [d]. [e] wont fit into [d] (they dont
  4649.       // even intersect). So we go to row 1. That gives us [b], and [d] wont fit
  4650.       // into that either. So, we go to row 0 and its last slice, [a]. That can
  4651.       // completely contain [e], so that means we should add [e] as a subchild
  4652.       // of [a]. That puts it on row 1, yielding:
  4653.       //  0:  [    a       ]
  4654.       //  1:    [  b  ][e]
  4655.       //  2:    [c][d]
  4656.       //
  4657.       // If we then get this slice:
  4658.       //                      [f]
  4659.       // We do the same deepest-to-shallowest walk of the subrows trying to fit
  4660.       // it. This time, it doesn't fit in any open slice. So, we simply append
  4661.       // it to row 0:
  4662.       //  0:  [    a       ]  [f]
  4663.       //  1:    [  b  ][e]
  4664.       //  2:    [c][d]
  4665.       if (!slices.length)
  4666.         return [];
  4667.  
  4668.       var ops = [];
  4669.       for (var i = 0; i < slices.length; i++) {
  4670.         if (slices[i].subSlices)
  4671.           slices[i].subSlices.splice(0,
  4672.                                      slices[i].subSlices.length);
  4673.         ops.push(i);
  4674.       }
  4675.  
  4676.       ops.sort(function(ix, iy) {
  4677.         var x = slices[ix];
  4678.         var y = slices[iy];
  4679.         if (x.start != y.start)
  4680.           return x.start - y.start;
  4681.  
  4682.         // Elements get inserted into the slices array in order of when the
  4683.         // slices end.  Because slices must be properly nested, we break
  4684.         // start-time ties by assuming that the elements appearing earlier in
  4685.         // the slices array (and thus ending earlier) start later.
  4686.         return iy - ix;
  4687.       });
  4688.  
  4689.       var subRows = [[]];
  4690.       this.badSlices_ = [];  // TODO(simonjam): Connect this again.
  4691.  
  4692.       for (var i = 0; i < ops.length; i++) {
  4693.         var op = ops[i];
  4694.         var slice = slices[op];
  4695.  
  4696.         // Try to fit the slice into the existing subrows.
  4697.         var inserted = false;
  4698.         for (var j = subRows.length - 1; j >= 0; j--) {
  4699.           if (subRows[j].length == 0)
  4700.             continue;
  4701.  
  4702.           var insertedSlice = subRows[j][subRows[j].length - 1];
  4703.           if (slice.start < insertedSlice.start) {
  4704.             this.badSlices_.push(slice);
  4705.             inserted = true;
  4706.           }
  4707.           if (slice.start >= insertedSlice.start &&
  4708.               slice.end <= insertedSlice.end) {
  4709.             // Insert it into subRow j + 1.
  4710.             while (subRows.length <= j + 1)
  4711.               subRows.push([]);
  4712.             subRows[j + 1].push(slice);
  4713.             if (insertedSlice.subSlices)
  4714.               insertedSlice.subSlices.push(slice);
  4715.             inserted = true;
  4716.             break;
  4717.           }
  4718.         }
  4719.         if (inserted)
  4720.           continue;
  4721.  
  4722.         // Append it to subRow[0] as a root.
  4723.         subRows[0].push(slice);
  4724.       }
  4725.  
  4726.       return subRows;
  4727.     },
  4728.  
  4729.     areArrayContentsSame_: function(a, b) {
  4730.       if (!a || !b)
  4731.         return false;
  4732.       if (!a.length || !b.length)
  4733.         return false;
  4734.       if (a.length != b.length)
  4735.         return false;
  4736.       for (var i = 0; i < a.length; ++i) {
  4737.         if (a[i] != b[i])
  4738.           return false;
  4739.       }
  4740.       return true;
  4741.     }
  4742.   };
  4743.  
  4744.   return {
  4745.     TimelineSliceGroupTrack: TimelineSliceGroupTrack
  4746.   };
  4747. });
  4748.  
  4749. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4750. // Use of this source code is governed by a BSD-style license that can be
  4751. // found in the LICENSE file.
  4752.  
  4753. 'use strict';
  4754.  
  4755. base.require('tracks.timeline_slice_group_track');
  4756. base.require('ui');
  4757. base.exportTo('tracks', function() {
  4758.  
  4759.   /**
  4760.    * A track that displays a TimelineAsyncSliceGroup.
  4761.    * @constructor
  4762.    * @extends {TimelineSliceGroup}
  4763.    */
  4764.  
  4765.   var TimelineAsyncSliceGroupTrack = base.ui.define(
  4766.       tracks.TimelineSliceGroupTrack);
  4767.  
  4768.   TimelineAsyncSliceGroupTrack.prototype = {
  4769.  
  4770.     __proto__: tracks.TimelineSliceGroupTrack.prototype,
  4771.  
  4772.     decorate: function() {
  4773.       this.classList.add('timeline-async-slice-group-track');
  4774.     },
  4775.  
  4776.     addSliceTrack_: function(slices) {
  4777.       var track = tracks.TimelineSliceGroupTrack.prototype.addSliceTrack_.call(
  4778.           this, slices);
  4779.       track.asyncStyle = true;
  4780.       return track;
  4781.     },
  4782.  
  4783.     /**
  4784.      * Breaks up the list of slices into N rows, each of which is a list of
  4785.      * slices that are non overlapping.
  4786.      *
  4787.      * It uses a very simple approach: walk through the slices in sorted order
  4788.      * by start time. For each slice, try to fit it in an existing subRow. If it
  4789.      * doesn't fit in any subrow, make another subRow.
  4790.      */
  4791.     buildSubRows_: function() {
  4792.       var slices = tracing.filterSliceArray(this.categoryFilter,
  4793.                                             this.group_.slices);
  4794.       slices.sort(function(x, y) {
  4795.         return x.start - y.start;
  4796.       });
  4797.  
  4798.       var subRows = [];
  4799.       for (var i = 0; i < slices.length; i++) {
  4800.         var slice = slices[i];
  4801.  
  4802.         var found = false;
  4803.         for (var j = 0; j < subRows.length; j++) {
  4804.           var subRow = subRows[j];
  4805.           var lastSliceInSubRow = subRow[subRow.length - 1];
  4806.           if (slice.start >= lastSliceInSubRow.end) {
  4807.             found = true;
  4808.             // Instead of plotting one big slice for the entire
  4809.             // TimelineAsyncEvent, we plot each of the subSlices.
  4810.             if (slice.subSlices === undefined || slice.subSlices.length < 1)
  4811.               throw new Error('TimelineAsyncEvent missing subSlices: ') +
  4812.                   slice.name;
  4813.             for (var k = 0; k < slice.subSlices.length; k++)
  4814.               subRow.push(slice.subSlices[k]);
  4815.             break;
  4816.           }
  4817.         }
  4818.         if (!found) {
  4819.           var subRow = [];
  4820.           if (slice.subSlices !== undefined) {
  4821.             for (var k = 0; k < slice.subSlices.length; k++)
  4822.               subRow.push(slice.subSlices[k]);
  4823.             subRows.push(subRow);
  4824.           }
  4825.         }
  4826.       }
  4827.       return subRows;
  4828.     }
  4829.   };
  4830.  
  4831.   return {
  4832.     TimelineAsyncSliceGroupTrack: TimelineAsyncSliceGroupTrack
  4833.   };
  4834. });
  4835.  
  4836. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4837. // Use of this source code is governed by a BSD-style license that can be
  4838. // found in the LICENSE file.
  4839.  
  4840. 'use strict';
  4841.  
  4842. base.requireStylesheet('tracks.timeline_thread_track');
  4843.  
  4844. base.require('tracks.timeline_container_track');
  4845. base.require('tracks.timeline_slice_track');
  4846. base.require('tracks.timeline_slice_group_track');
  4847. base.require('tracks.timeline_async_slice_group_track');
  4848. base.require('timeline_filter');
  4849. base.require('ui');
  4850.  
  4851. base.exportTo('tracks', function() {
  4852.  
  4853.   /**
  4854.    * Visualizes a TimelineThread using a series of of TimelineSliceTracks.
  4855.    * @constructor
  4856.    */
  4857.   var TimelineThreadTrack = base.ui.define(tracks.TimelineContainerTrack);
  4858.   TimelineThreadTrack.prototype = {
  4859.     __proto__: tracks.TimelineContainerTrack.prototype,
  4860.  
  4861.     decorate: function() {
  4862.       this.classList.add('timeline-thread-track');
  4863.       this.categoryFilter_ = new tracing.TimelineFilter();
  4864.     },
  4865.  
  4866.     get thread() {
  4867.       return this.thread_;
  4868.     },
  4869.  
  4870.     set thread(thread) {
  4871.       this.thread_ = thread;
  4872.       this.updateChildTracks_();
  4873.     },
  4874.  
  4875.     get tooltip() {
  4876.       return this.tooltip_;
  4877.     },
  4878.  
  4879.     set tooltip(value) {
  4880.       this.tooltip_ = value;
  4881.       this.updateChildTracks_();
  4882.     },
  4883.  
  4884.     get heading() {
  4885.       return this.heading_;
  4886.     },
  4887.  
  4888.     set heading(h) {
  4889.       this.heading_ = h;
  4890.       this.updateChildTracks_();
  4891.     },
  4892.  
  4893.     applyCategoryFilter_: function() {
  4894.       this.updateVisibility_();
  4895.     },
  4896.  
  4897.     updateChildTracks_: function() {
  4898.       this.detach();
  4899.       if (this.thread_) {
  4900.         var cpuTrack = new tracks.TimelineSliceTrack();
  4901.         cpuTrack.heading = '';
  4902.         cpuTrack.slices = this.thread_.cpuSlices;
  4903.         cpuTrack.height = '4px';
  4904.         cpuTrack.decorateHit = function(hit) {
  4905.           hit.thread = this.thread_;
  4906.         }
  4907.         this.addTrack_(cpuTrack);
  4908.  
  4909.         var asyncTrack = new tracks.TimelineAsyncSliceGroupTrack();
  4910.         asyncTrack.categoryFilter = this.categoryFilter;
  4911.         asyncTrack.decorateHit = function(hit) {
  4912.           // TODO(simonjam): figure out how to associate subSlice hits back
  4913.           // to their parent slice.
  4914.         }
  4915.         asyncTrack.group = this.thread_.asyncSlices;
  4916.         this.addTrack_(asyncTrack);
  4917.  
  4918.         var track = new tracks.TimelineSliceGroupTrack();
  4919.         track.decorateHit = function(hit) {
  4920.           hit.thread = this.thread_;
  4921.         }
  4922.         track.group = this.thread_;
  4923.         this.addTrack_(track);
  4924.  
  4925.         this.updateVisibility_();
  4926.       }
  4927.       this.addControlButtonElements_(this.tracks_.length >= 4);
  4928.     },
  4929.  
  4930.     updateVisibility_: function() {
  4931.       if (!this.categoryFilter.matchThread(this.thread)) {
  4932.         this.visible = false;
  4933.         return;
  4934.       }
  4935.       var shouldBeVisible = false;
  4936.       for (var i = 0; i < this.tracks_.length; ++i) {
  4937.         var track = this.tracks_[i];
  4938.         if (track.visible) {
  4939.           shouldBeVisible = true;
  4940.           if (i >= 1) {
  4941.             track.heading = this.heading_;
  4942.             track.tooltip = this.tooltip_;
  4943.             break;
  4944.           }
  4945.         }
  4946.       }
  4947.       this.visible = shouldBeVisible;
  4948.     },
  4949.  
  4950.     collapsedDidChange: function(collapsed) {
  4951.       if (collapsed) {
  4952.         var h = parseInt(this.tracks_[0].height);
  4953.         for (var i = 0; i < this.tracks_.length; ++i) {
  4954.           if (h > 2) {
  4955.             this.tracks_[i].height = Math.floor(h) + 'px';
  4956.           } else {
  4957.             this.tracks_[i].style.display = 'none';
  4958.           }
  4959.           h = h * 0.5;
  4960.         }
  4961.       } else {
  4962.         for (var i = 0; i < this.tracks_.length; ++i) {
  4963.           this.tracks_[i].height = this.tracks_[0].height;
  4964.           this.tracks_[i].style.display = '';
  4965.         }
  4966.       }
  4967.     }
  4968.   };
  4969.  
  4970.   return {
  4971.     TimelineThreadTrack: TimelineThreadTrack
  4972.   };
  4973. });
  4974.  
  4975. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4976. // Use of this source code is governed by a BSD-style license that can be
  4977. // found in the LICENSE file.
  4978.  
  4979. 'use strict';
  4980.  
  4981. base.require('tracks.timeline_container_track');
  4982. base.require('tracks.timeline_counter_track');
  4983. base.require('tracks.timeline_thread_track');
  4984. base.require('timeline_filter');
  4985. base.require('ui');
  4986.  
  4987. base.exportTo('tracks', function() {
  4988.  
  4989.   /**
  4990.    * Visualizes a TimelineProcess by building TimelineThreadTracks and
  4991.    * TimelineCounterTracks.
  4992.    * @constructor
  4993.    */
  4994.   var TimelineProcessTrack = base.ui.define(tracks.TimelineContainerTrack);
  4995.  
  4996.   TimelineProcessTrack.prototype = {
  4997.  
  4998.     __proto__: tracks.TimelineContainerTrack.prototype,
  4999.  
  5000.     decorate: function() {
  5001.       this.classList.add('timeline-process-track');
  5002.       this.categoryFilter_ = new tracing.TimelineFilter();
  5003.     },
  5004.  
  5005.     get process() {
  5006.       return this.process_;
  5007.     },
  5008.  
  5009.     set process(process) {
  5010.       this.process_ = process;
  5011.       this.updateChildTracks_();
  5012.     },
  5013.  
  5014.     applyCategoryFilter_: function() {
  5015.       this.visible = (this.categoryFilter.matchProcess(this.process) &&
  5016.                       !!this.numVisibleChildTracks);
  5017.     },
  5018.  
  5019.     updateChildTracks_: function() {
  5020.       this.detach();
  5021.       if (this.process_) {
  5022.         // Add counter tracks for this process.
  5023.         var counters = [];
  5024.         for (var tid in this.process.counters) {
  5025.           counters.push(this.process.counters[tid]);
  5026.         }
  5027.         counters.sort(tracing.TimelineCounter.compare);
  5028.  
  5029.         // Create the counters for this process.
  5030.         counters.forEach(function(counter) {
  5031.           var track = new tracks.TimelineCounterTrack();
  5032.           track.heading = counter.name + ':';
  5033.           track.counter = counter;
  5034.           this.addTrack_(track);
  5035.         }.bind(this));
  5036.  
  5037.         // Get a sorted list of threads.
  5038.         var threads = [];
  5039.         for (var tid in this.process.threads)
  5040.           threads.push(this.process.threads[tid]);
  5041.         threads.sort(tracing.TimelineThread.compare);
  5042.  
  5043.         // Create the threads.
  5044.         threads.forEach(function(thread) {
  5045.           var track = new tracks.TimelineThreadTrack();
  5046.           track.heading = thread.userFriendlyName + ':';
  5047.           track.tooltip = thread.userFriendlyDetails;
  5048.           track.thread = thread;
  5049.           this.addTrack_(track);
  5050.         }.bind(this));
  5051.       }
  5052.     }
  5053.   };
  5054.  
  5055.   return {
  5056.     TimelineProcessTrack: TimelineProcessTrack
  5057.   };
  5058. });
  5059.  
  5060. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5061. // Use of this source code is governed by a BSD-style license that can be
  5062. // found in the LICENSE file.
  5063.  
  5064. 'use strict';
  5065.  
  5066. base.requireStylesheet('tracks.timeline_model_track');
  5067. base.require('tracks.timeline_container_track');
  5068. base.require('tracks.timeline_cpu_track');
  5069. base.require('tracks.timeline_process_track');
  5070. base.require('ui');
  5071.  
  5072. base.exportTo('tracks', function() {
  5073.  
  5074.   /**
  5075.    * Visualizes a TimelineModel by building TimelineProcessTracks and
  5076.    * TimelineCpuTracks.
  5077.    * @constructor
  5078.    */
  5079.   var TimelineModelTrack = base.ui.define(tracks.TimelineContainerTrack);
  5080.  
  5081.   TimelineModelTrack.prototype = {
  5082.  
  5083.     __proto__: tracks.TimelineContainerTrack.prototype,
  5084.  
  5085.     decorate: function() {
  5086.       this.classList.add('timeline-model-track');
  5087.       this.measuringStick_ = new tracing.MeasuringStick();
  5088.       this.measuringStick_.attach();
  5089.     },
  5090.  
  5091.     detach: function() {
  5092.       tracks.TimelineContainerTrack.prototype.detach.call(this);
  5093.       this.measuringStick_.detach();
  5094.     },
  5095.  
  5096.     get model() {
  5097.       return this.model_;
  5098.     },
  5099.  
  5100.     set model(model) {
  5101.       this.model_ = model;
  5102.       this.updateHeadingWidth_();
  5103.       this.updateChildTracks_();
  5104.     },
  5105.  
  5106.     updateHeadingWidth_: function() {
  5107.       // Figure out all the headings.
  5108.       var allHeadings = [];
  5109.       this.model.getAllThreads().forEach(function(t) {
  5110.         allHeadings.push(t.userFriendlyName);
  5111.       });
  5112.       this.model.getAllCounters().forEach(function(c) {
  5113.         allHeadings.push(c.name);
  5114.       });
  5115.       this.model.getAllCpus().forEach(function(c) {
  5116.         allHeadings.push('CPU ' + c.cpuNumber);
  5117.       });
  5118.  
  5119.       // Figure out the maximum heading size.
  5120.       var maxHeadingWidth = 0;
  5121.       var headingEl = document.createElement('div');
  5122.       headingEl.style.position = 'fixed';
  5123.       headingEl.className = 'timeline-canvas-based-track-title';
  5124.       for (var i = 0; i < allHeadings.length; i++) {
  5125.         var text = allHeadings[i];
  5126.         headingEl.textContent = text + ':__';
  5127.         var w = this.measuringStick_.measure(headingEl).width;
  5128.         // Limit heading width to 300px.
  5129.         if (w > 300)
  5130.           w = 300;
  5131.         if (w > maxHeadingWidth)
  5132.           maxHeadingWidth = w;
  5133.       }
  5134.       this.headingWidth = maxHeadingWidth + 'px';
  5135.     },
  5136.  
  5137.     updateChildTracks_: function() {
  5138.       this.detachAllChildren();
  5139.       if (this.model_) {
  5140.         var cpus = this.model_.getAllCpus();
  5141.         cpus.sort(tracing.TimelineCpu.compare);
  5142.  
  5143.         for (var i = 0; i < cpus.length; ++i) {
  5144.           var cpu = cpus[i];
  5145.           var track = new tracks.TimelineCpuTrack();
  5146.           track.heading = 'CPU ' + cpu.cpuNumber + ':';
  5147.           track.cpu = cpu;
  5148.           this.addTrack_(track);
  5149.         }
  5150.  
  5151.         // Get a sorted list of processes.
  5152.         var processes = this.model_.getAllProcesses();
  5153.         processes.sort(tracing.TimelineProcess.compare);
  5154.  
  5155.         for (var i = 0; i < processes.length; ++i) {
  5156.           var process = processes[i];
  5157.           var track = new tracks.TimelineProcessTrack();
  5158.           track.process = process;
  5159.           this.addTrack_(track);
  5160.         }
  5161.       }
  5162.     }
  5163.   };
  5164.  
  5165.   return {
  5166.     TimelineModelTrack: TimelineModelTrack
  5167.   };
  5168. });
  5169.  
  5170. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5171. // Use of this source code is governed by a BSD-style license that can be
  5172. // found in the LICENSE file.
  5173.  
  5174. 'use strict';
  5175.  
  5176. base.requireStylesheet('tracks.timeline_viewport_track');
  5177.  
  5178. base.require('tracks.timeline_track');
  5179. base.require('tracks.timeline_canvas_based_track');
  5180. base.require('ui');
  5181.  
  5182. base.exportTo('tracks', function() {
  5183.  
  5184.   /**
  5185.    * A track that displays the viewport size and scale.
  5186.    * @constructor
  5187.    * @extends {CanvasBasedTrack}
  5188.    */
  5189.  
  5190.   var TimelineViewportTrack = base.ui.define(tracks.TimelineCanvasBasedTrack);
  5191.  
  5192.   var logOf10 = Math.log(10);
  5193.   function log10(x) {
  5194.     return Math.log(x) / logOf10;
  5195.   }
  5196.  
  5197.   TimelineViewportTrack.prototype = {
  5198.  
  5199.     __proto__: tracks.TimelineCanvasBasedTrack.prototype,
  5200.  
  5201.     decorate: function() {
  5202.       this.classList.add('timeline-viewport-track');
  5203.       this.strings_secs_ = [];
  5204.       this.strings_msecs_ = [];
  5205.       this.addEventListener('mousedown', this.onMouseDown);
  5206.     },
  5207.  
  5208.     onMouseDown: function(e) {
  5209.       if (e.button != 0)
  5210.         return;
  5211.       this.placeAndBeginDraggingMarker(e.clientX);
  5212.     },
  5213.  
  5214.  
  5215.     placeAndBeginDraggingMarker: function(clientX) {
  5216.       var viewX = clientX - this.canvasContainer_.offsetLeft;
  5217.       var worldX = this.viewport_.xViewToWorld(viewX);
  5218.       var marker = this.viewport_.findMarkerNear(worldX, 6);
  5219.       var createdMarker = false;
  5220.       var movedMarker = false;
  5221.       if (!marker) {
  5222.         marker = this.viewport_.addMarker(worldX);
  5223.         createdMarker = true;
  5224.       }
  5225.       marker.selected = true;
  5226.  
  5227.       var that = this;
  5228.       var onMouseMove = function(e) {
  5229.         var viewX = e.clientX - that.canvasContainer_.offsetLeft;
  5230.         var worldX = that.viewport_.xViewToWorld(viewX);
  5231.         marker.positionWorld = worldX;
  5232.         movedMarker = true;
  5233.       };
  5234.  
  5235.       var onMouseUp = function(e) {
  5236.         marker.selected = false;
  5237.         if (!movedMarker && !createdMarker)
  5238.           that.viewport_.removeMarker(marker);
  5239.         document.removeEventListener('mouseup', onMouseUp);
  5240.         document.removeEventListener('mousemove', onMouseMove);
  5241.       };
  5242.  
  5243.       document.addEventListener('mouseup', onMouseUp);
  5244.       document.addEventListener('mousemove', onMouseMove);
  5245.     },
  5246.  
  5247.     drawLine_: function(ctx, x1, y1, x2, y2, color) {
  5248.       ctx.beginPath();
  5249.       ctx.moveTo(x1, y1);
  5250.       ctx.lineTo(x2, y2);
  5251.       ctx.closePath();
  5252.       ctx.strokeStyle = color;
  5253.       ctx.stroke();
  5254.     },
  5255.  
  5256.     drawArrow_: function(ctx, x1, y1, x2, y2, arrowWidth, color) {
  5257.  
  5258.       this.drawLine_(ctx, x1, y1, x2, y2, color);
  5259.  
  5260.       var dx = x2 - x1;
  5261.       var dy = y2 - y1;
  5262.       var len = Math.sqrt(dx * dx + dy * dy);
  5263.       var perc = (len - 10) / len;
  5264.       var bx = x1 + perc * dx;
  5265.       var by = y1 + perc * dy;
  5266.       var ux = dx / len;
  5267.       var uy = dy / len;
  5268.       var ax = uy * arrowWidth;
  5269.       var ay = -ux * arrowWidth;
  5270.  
  5271.       ctx.beginPath();
  5272.       ctx.fillStyle = color;
  5273.       ctx.moveTo(bx + ax, by + ay);
  5274.       ctx.lineTo(x2, y2);
  5275.       ctx.lineTo(bx - ax, by - ay);
  5276.       ctx.lineTo(bx + ax, by + ay);
  5277.       ctx.closePath();
  5278.       ctx.fill();
  5279.     },
  5280.  
  5281.     redraw: function() {
  5282.       var ctx = this.ctx_;
  5283.       var canvasW = this.canvas_.width;
  5284.       var canvasH = this.canvas_.height;
  5285.  
  5286.       ctx.clearRect(0, 0, canvasW, canvasH);
  5287.  
  5288.       // Culling parametrs.
  5289.       var vp = this.viewport_;
  5290.       var pixWidth = vp.xViewVectorToWorld(1);
  5291.       var viewLWorld = vp.xViewToWorld(0);
  5292.       var viewRWorld = vp.xViewToWorld(canvasW);
  5293.  
  5294.       var measurements = this.classList.contains('timeline-viewport' +
  5295.           '-track-with-distance-measurements');
  5296.  
  5297.       var rulerHeight = measurements ? canvasH / 2 : canvasH;
  5298.  
  5299.       for (var i = 0; i < vp.markers.length; ++i) {
  5300.         vp.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
  5301.                                     canvasH, rulerHeight, vp);
  5302.       }
  5303.  
  5304.       var idealMajorMarkDistancePix = 150;
  5305.       var idealMajorMarkDistanceWorld =
  5306.           vp.xViewVectorToWorld(idealMajorMarkDistancePix);
  5307.  
  5308.       var majorMarkDistanceWorld;
  5309.       var unit;
  5310.       var unitDivisor;
  5311.       var tickLabels;
  5312.  
  5313.       // The conservative guess is the nearest enclosing 0.1, 1, 10, 100, etc.
  5314.       var conservativeGuess =
  5315.           Math.pow(10, Math.ceil(log10(idealMajorMarkDistanceWorld)));
  5316.  
  5317.       // Once we have a conservative guess, consider things that evenly add up
  5318.       // to the conservative guess, e.g. 0.5, 0.2, 0.1 Pick the one that still
  5319.       // exceeds the ideal mark distance.
  5320.       var divisors = [10, 5, 2, 1];
  5321.       for (var i = 0; i < divisors.length; ++i) {
  5322.         var tightenedGuess = conservativeGuess / divisors[i];
  5323.         if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
  5324.           continue;
  5325.         majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
  5326.         break;
  5327.       }
  5328.       var tickLabels = undefined;
  5329.       if (majorMarkDistanceWorld < 100) {
  5330.         unit = 'ms';
  5331.         unitDivisor = 1;
  5332.         tickLabels = this.strings_msecs_;
  5333.       } else {
  5334.         unit = 's';
  5335.         unitDivisor = 1000;
  5336.         tickLabels = this.strings_secs_;
  5337.       }
  5338.  
  5339.       var numTicksPerMajor = 5;
  5340.       var minorMarkDistanceWorld = majorMarkDistanceWorld / numTicksPerMajor;
  5341.       var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
  5342.  
  5343.       var firstMajorMark =
  5344.           Math.floor(viewLWorld / majorMarkDistanceWorld) *
  5345.               majorMarkDistanceWorld;
  5346.  
  5347.       var minorTickH = Math.floor(canvasH * 0.25);
  5348.  
  5349.       ctx.fillStyle = 'rgb(0, 0, 0)';
  5350.       ctx.strokeStyle = 'rgb(0, 0, 0)';
  5351.       ctx.textAlign = 'left';
  5352.       ctx.textBaseline = 'top';
  5353.  
  5354.       var pixelRatio = window.devicePixelRatio || 1;
  5355.       ctx.font = (9 * pixelRatio) + 'px sans-serif';
  5356.  
  5357.       // Each iteration of this loop draws one major mark
  5358.       // and numTicksPerMajor minor ticks.
  5359.       //
  5360.       // Rendering can't be done in world space because canvas transforms
  5361.       // affect line width. So, do the conversions manually.
  5362.       for (var curX = firstMajorMark;
  5363.            curX < viewRWorld;
  5364.            curX += majorMarkDistanceWorld) {
  5365.  
  5366.         var curXView = Math.floor(vp.xWorldToView(curX));
  5367.  
  5368.         var unitValue = curX / unitDivisor;
  5369.         var roundedUnitValue = Math.floor(unitValue * 100000) / 100000;
  5370.  
  5371.         if (!tickLabels[roundedUnitValue])
  5372.           tickLabels[roundedUnitValue] = roundedUnitValue + ' ' + unit;
  5373.         ctx.fillText(tickLabels[roundedUnitValue],
  5374.                      curXView + 2 * pixelRatio, 0);
  5375.         ctx.beginPath();
  5376.  
  5377.         // Major mark
  5378.         ctx.moveTo(curXView, 0);
  5379.         ctx.lineTo(curXView, rulerHeight);
  5380.  
  5381.         // Minor marks
  5382.         for (var i = 1; i < numTicksPerMajor; ++i) {
  5383.           var xView = Math.floor(curXView + minorMarkDistancePx * i);
  5384.           ctx.moveTo(xView, rulerHeight - minorTickH);
  5385.           ctx.lineTo(xView, rulerHeight);
  5386.         }
  5387.  
  5388.         ctx.stroke();
  5389.       }
  5390.  
  5391.       // Give distance between directly adjacent markers.
  5392.       if (measurements) {
  5393.  
  5394.         // Divide canvas horizontally between ruler and measurements.
  5395.         ctx.moveTo(0, rulerHeight);
  5396.         ctx.lineTo(canvasW, rulerHeight);
  5397.         ctx.stroke();
  5398.  
  5399.         // Obtain a sorted array of markers
  5400.         var sortedMarkers = vp.markers.slice();
  5401.         sortedMarkers.sort(function(a, b) {
  5402.           return a.positionWorld_ - b.positionWorld_;
  5403.         });
  5404.  
  5405.         // Distance Variables.
  5406.         var displayDistance;
  5407.         var unitDivisor;
  5408.         var displayTextColor = 'rgb(0,0,0)';
  5409.         var measurementsPosY = rulerHeight + 2;
  5410.  
  5411.         // Arrow Variables.
  5412.         var arrowSpacing = 10;
  5413.         var arrowColor = 'rgb(128,121,121)';
  5414.         var arrowPosY = measurementsPosY + 4;
  5415.         var arrowWidthView = 3;
  5416.         var spaceForArrowsView = 2 * (arrowWidthView + arrowSpacing);
  5417.  
  5418.         for (i = 0; i < sortedMarkers.length - 1; i++) {
  5419.           var rightMarker = sortedMarkers[i + 1];
  5420.           var leftMarker = sortedMarkers[i];
  5421.           var distanceBetweenMarkers =
  5422.               rightMarker.positionWorld - leftMarker.positionWorld;
  5423.           var distanceBetweenMarkersView =
  5424.               vp.xWorldVectorToView(distanceBetweenMarkers);
  5425.  
  5426.           var positionInMiddleOfMarkers = leftMarker.positionWorld +
  5427.                                               distanceBetweenMarkers / 2;
  5428.           var positionInMiddleOfMarkersView =
  5429.               vp.xWorldToView(positionInMiddleOfMarkers);
  5430.  
  5431.           // Determine units.
  5432.           if (distanceBetweenMarkers < 100) {
  5433.             unit = 'ms';
  5434.             unitDivisor = 1;
  5435.           } else {
  5436.             unit = 's';
  5437.             unitDivisor = 1000;
  5438.           }
  5439.           // Calculate display value to print.
  5440.           displayDistance = distanceBetweenMarkers / unitDivisor;
  5441.           var roundedDisplayDistance =
  5442.               Math.abs((Math.floor(displayDistance * 1000) / 1000));
  5443.           var textToDraw = roundedDisplayDistance + ' ' + unit;
  5444.           var textWidthView = ctx.measureText(textToDraw).width;
  5445.           var textWidthWorld = vp.xViewVectorToWorld(textWidthView);
  5446.           var spaceForArrowsAndTextView = textWidthView +
  5447.                                           spaceForArrowsView + arrowSpacing;
  5448.  
  5449.           // Set text positions.
  5450.           var textLeft = leftMarker.positionWorld +
  5451.               (distanceBetweenMarkers / 2) - (textWidthWorld / 2);
  5452.           var textRight = textLeft + textWidthWorld;
  5453.           var textPosY = measurementsPosY;
  5454.           var textLeftView = vp.xWorldToView(textLeft);
  5455.           var textRightView = vp.xWorldToView(textRight);
  5456.           var leftMarkerView = vp.xWorldToView(leftMarker.positionWorld);
  5457.           var rightMarkerView = vp.xWorldToView(rightMarker.positionWorld);
  5458.           var textDrawn = false;
  5459.  
  5460.           if (spaceForArrowsAndTextView <= distanceBetweenMarkersView) {
  5461.             // Print the display distance text.
  5462.             ctx.fillStyle = displayTextColor;
  5463.             ctx.fillText(textToDraw, textLeftView, textPosY);
  5464.             textDrawn = true;
  5465.           }
  5466.  
  5467.           if (spaceForArrowsView <= distanceBetweenMarkersView) {
  5468.             var leftArrowStart;
  5469.             var rightArrowStart;
  5470.             if (textDrawn) {
  5471.               leftArrowStart = textLeftView - arrowSpacing;
  5472.               rightArrowStart = textRightView + arrowSpacing;
  5473.             } else {
  5474.               leftArrowStart = positionInMiddleOfMarkersView;
  5475.               rightArrowStart = positionInMiddleOfMarkersView;
  5476.             }
  5477.             // Draw left arrow.
  5478.             this.drawArrow_(ctx, leftArrowStart, arrowPosY,
  5479.                 leftMarkerView, arrowPosY, arrowWidthView, arrowColor);
  5480.             // Draw right arrow.
  5481.             this.drawArrow_(ctx, rightArrowStart, arrowPosY,
  5482.                 rightMarkerView, arrowPosY, arrowWidthView, arrowColor);
  5483.           }
  5484.         }
  5485.       }
  5486.     },
  5487.  
  5488.     /**
  5489.      * Adds items intersecting a point to a selection.
  5490.      * @param {number} vX X location to search at, in viewspace.
  5491.      * @param {number} vY Y location to search at, in viewspace.
  5492.      * @param {TimelineSelection} selection Selection to which to add hits.
  5493.      * @return {boolean} true if a slice was found, otherwise false.
  5494.      */
  5495.     addIntersectingItemsToSelection: function(vX, vY, selection) {
  5496.       // Does nothing. There's nothing interesting to pick on the viewport
  5497.       // track.
  5498.     },
  5499.  
  5500.     /**
  5501.      * Adds items intersecting the given range to a selection.
  5502.      * @param {number} loVX Lower X bound of the interval to search, in
  5503.      *     viewspace.
  5504.      * @param {number} hiVX Upper X bound of the interval to search, in
  5505.      *     viewspace.
  5506.      * @param {number} loVY Lower Y bound of the interval to search, in
  5507.      *     viewspace.
  5508.      * @param {number} hiVY Upper Y bound of the interval to search, in
  5509.      *     viewspace.
  5510.      * @param {TimelineSelection} selection Selection to which to add hits.
  5511.      */
  5512.     addIntersectingItemsInRangeToSelection: function(
  5513.         loVX, hiVX, loY, hiY, selection) {
  5514.       // Does nothing. There's nothing interesting to pick on the viewport
  5515.       // track.
  5516.     },
  5517.  
  5518.     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
  5519.     }
  5520.   };
  5521.  
  5522.   return {
  5523.     TimelineViewportTrack: TimelineViewportTrack
  5524.   };
  5525. });
  5526.  
  5527. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  5528. // Use of this source code is governed by a BSD-style license that can be
  5529. // found in the LICENSE file.
  5530.  
  5531. 'use strict';
  5532.  
  5533. /**
  5534.  * @fileoverview Interactive visualizaiton of TimelineModel objects
  5535.  * based loosely on gantt charts. Each thread in the TimelineModel is given a
  5536.  * set of TimelineTracks, one per subrow in the thread. The Timeline class
  5537.  * acts as a controller, creating the individual tracks, while TimelineTracks
  5538.  * do actual drawing.
  5539.  *
  5540.  * Visually, the Timeline produces (prettier) visualizations like the following:
  5541.  *    Thread1:  AAAAAAAAAA         AAAAA
  5542.  *                  BBBB              BB
  5543.  *    Thread2:     CCCCCC                 CCCCC
  5544.  *
  5545.  */
  5546. base.requireStylesheet('timeline');
  5547. base.require('event_target');
  5548. base.require('measuring_stick');
  5549. base.require('timeline_filter');
  5550. base.require('timeline_selection');
  5551. base.require('timeline_viewport');
  5552. base.require('tracks.timeline_model_track');
  5553. base.require('tracks.timeline_viewport_track');
  5554. base.require('ui');
  5555.  
  5556. base.exportTo('tracing', function() {
  5557.  
  5558.   var TimelineSelection = tracing.TimelineSelection;
  5559.   var TimelineViewport = tracing.TimelineViewport;
  5560.  
  5561.   function intersectRect_(r1, r2) {
  5562.     var results = new Object;
  5563.     if (r2.left > r1.right || r2.right < r1.left ||
  5564.          r2.top > r1.bottom || r2.bottom < r1.top) {
  5565.       return false;
  5566.     }
  5567.     results.left = Math.max(r1.left, r2.left);
  5568.     results.top = Math.max(r1.top, r2.top);
  5569.     results.right = Math.min(r1.right, r2.right);
  5570.     results.bottom = Math.min(r1.bottom, r2.bottom);
  5571.     results.width = (results.right - results.left);
  5572.     results.height = (results.bottom - results.top);
  5573.     return results;
  5574.   }
  5575.  
  5576.   /**
  5577.    * Renders a TimelineModel into a div element, making one
  5578.    * TimelineTrack for each subrow in each thread of the model, managing
  5579.    * overall track layout, and handling user interaction with the
  5580.    * viewport.
  5581.    *
  5582.    * @constructor
  5583.    * @extends {HTMLDivElement}
  5584.    */
  5585.   var Timeline = base.ui.define('div');
  5586.  
  5587.   Timeline.prototype = {
  5588.     __proto__: HTMLDivElement.prototype,
  5589.  
  5590.     model_: null,
  5591.  
  5592.     decorate: function() {
  5593.       this.classList.add('timeline');
  5594.  
  5595.       this.categoryFilter_ = new tracing.TimelineCategoryFilter();
  5596.  
  5597.       this.viewport_ = new TimelineViewport(this);
  5598.  
  5599.       // Add the viewport track.
  5600.       this.viewportTrack_ = new tracks.TimelineViewportTrack();
  5601.       this.viewportTrack_.viewport = this.viewport_;
  5602.       this.appendChild(this.viewportTrack_);
  5603.  
  5604.       this.modelTrackContainer_ = document.createElement('div');
  5605.       this.modelTrackContainer_.className = 'timeline-model-track-container';
  5606.       this.appendChild(this.modelTrackContainer_);
  5607.  
  5608.       this.modelTrack_ = new tracks.TimelineModelTrack();
  5609.       this.modelTrackContainer_.appendChild(this.modelTrack_);
  5610.  
  5611.       this.dragBox_ = this.ownerDocument.createElement('div');
  5612.       this.dragBox_.className = 'timeline-drag-box';
  5613.       this.appendChild(this.dragBox_);
  5614.       this.hideDragBox_();
  5615.  
  5616.       this.bindEventListener_(document, 'keypress', this.onKeypress_, this);
  5617.       this.bindEventListener_(document, 'keydown', this.onKeydown_, this);
  5618.       this.bindEventListener_(document, 'keyup', this.onKeyup_, this);
  5619.       this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this);
  5620.       this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this);
  5621.  
  5622.       this.addEventListener('mousewheel', this.onMouseWheel_);
  5623.       this.addEventListener('mousedown', this.onMouseDown_);
  5624.       this.addEventListener('dblclick', this.onDblClick_);
  5625.  
  5626.       this.lastMouseViewPos_ = {x: 0, y: 0};
  5627.       this.maxHeadingWidth_ = 0;
  5628.  
  5629.       this.selection_ = new TimelineSelection();
  5630.     },
  5631.  
  5632.     /**
  5633.      * Wraps the standard addEventListener but automatically binds the provided
  5634.      * func to the provided target, tracking the resulting closure. When detach
  5635.      * is called, these listeners will be automatically removed.
  5636.      */
  5637.     bindEventListener_: function(object, event, func, target) {
  5638.       if (!this.boundListeners_)
  5639.         this.boundListeners_ = [];
  5640.       var boundFunc = func.bind(target);
  5641.       this.boundListeners_.push({object: object,
  5642.         event: event,
  5643.         boundFunc: boundFunc});
  5644.       object.addEventListener(event, boundFunc);
  5645.     },
  5646.  
  5647.     detach: function() {
  5648.       this.modelTrack_.detach();
  5649.  
  5650.       for (var i = 0; i < this.boundListeners_.length; i++) {
  5651.         var binding = this.boundListeners_[i];
  5652.         binding.object.removeEventListener(binding.event, binding.boundFunc);
  5653.       }
  5654.       this.boundListeners_ = undefined;
  5655.       this.viewport_.detach();
  5656.     },
  5657.  
  5658.     get viewport() {
  5659.       return this.viewport_;
  5660.     },
  5661.  
  5662.     get categoryFilter() {
  5663.       return this.categoryFilter_;
  5664.     },
  5665.  
  5666.     set categoryFilter(filter) {
  5667.       this.categoryFilter_ = filter;
  5668.       this.modelTrack_.categoryFilter = filter;
  5669.     },
  5670.  
  5671.     get model() {
  5672.       return this.model_;
  5673.     },
  5674.  
  5675.     set model(model) {
  5676.       if (!model)
  5677.         throw new Error('Model cannot be null');
  5678.  
  5679.       var modelInstanceChanged = this.model_ != model;
  5680.       this.model_ = model;
  5681.       this.modelTrack_.model = model;
  5682.       this.modelTrack_.viewport = this.viewport_;
  5683.       this.modelTrack_.categoryFilter = this.categoryFilter;
  5684.       this.viewportTrack_.headingWidth = this.modelTrack_.headingWidth;
  5685.  
  5686.       // Set up a reasonable viewport.
  5687.       if (modelInstanceChanged)
  5688.         this.viewport_.setWhenPossible(this.setInitialViewport_.bind(this));
  5689.     },
  5690.  
  5691.     get numVisibleTracks() {
  5692.       return this.modelTrack_.numVisibleTracks;
  5693.     },
  5694.  
  5695.     setInitialViewport_: function() {
  5696.       var w = this.firstCanvas.width;
  5697.       var boost =
  5698.           (this.model_.maxTimestamp - this.model_.minTimestamp) * 0.15;
  5699.       this.viewport_.xSetWorldRange(this.model_.minTimestamp - boost,
  5700.                                     this.model_.maxTimestamp + boost,
  5701.                                     w);
  5702.     },
  5703.  
  5704.     /**
  5705.      * @param {TimelineFilter} filter The filter to use for finding matches.
  5706.      * @param {TimelineSelection} selection The selection to add matches to.
  5707.      * @return {Array} An array of objects that match the provided
  5708.      * TimelineTitleFilter.
  5709.      */
  5710.     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
  5711.       this.modelTrack_.addAllObjectsMatchingFilterToSelection(filter,
  5712.                                                               selection);
  5713.     },
  5714.  
  5715.     /**
  5716.      * @return {Element} The element whose focused state determines
  5717.      * whether to respond to keyboard inputs.
  5718.      * Defaults to the parent element.
  5719.      */
  5720.     get focusElement() {
  5721.       if (this.focusElement_)
  5722.         return this.focusElement_;
  5723.       return this.parentElement;
  5724.     },
  5725.  
  5726.     /**
  5727.      * Sets the element whose focus state will determine whether
  5728.      * to respond to keybaord input.
  5729.      */
  5730.     set focusElement(value) {
  5731.       this.focusElement_ = value;
  5732.     },
  5733.  
  5734.     get listenToKeys_() {
  5735.       if (!this.viewport_.isAttachedToDocument_)
  5736.         return false;
  5737.       if (this.activeElement instanceof tracing.TimelineFindControl)
  5738.         return false;
  5739.       if (!this.focusElement_)
  5740.         return true;
  5741.       if (this.focusElement.tabIndex >= 0)
  5742.         return document.activeElement == this.focusElement;
  5743.       return true;
  5744.     },
  5745.  
  5746.     onKeypress_: function(e) {
  5747.       var vp = this.viewport_;
  5748.       if (!this.firstCanvas)
  5749.         return;
  5750.       if (!this.listenToKeys_)
  5751.         return;
  5752.       if (document.activeElement.nodeName == 'INPUT')
  5753.         return;
  5754.       var viewWidth = this.firstCanvas.clientWidth;
  5755.       var curMouseV, curCenterW;
  5756.       switch (e.keyCode) {
  5757.         case 119:  // w
  5758.         case 44:   // ,
  5759.           this.zoomBy_(1.5);
  5760.           break;
  5761.         case 115:  // s
  5762.         case 111:  // o
  5763.           this.zoomBy_(1 / 1.5);
  5764.           break;
  5765.         case 103:  // g
  5766.           this.onGridToggle_(true);
  5767.           break;
  5768.         case 71:  // G
  5769.           this.onGridToggle_(false);
  5770.           break;
  5771.         case 87:  // W
  5772.         case 60:  // <
  5773.           this.zoomBy_(10);
  5774.           break;
  5775.         case 83:  // S
  5776.         case 79:  // O
  5777.           this.zoomBy_(1 / 10);
  5778.           break;
  5779.         case 97:  // a
  5780.           vp.panX += vp.xViewVectorToWorld(viewWidth * 0.1);
  5781.           break;
  5782.         case 100:  // d
  5783.         case 101:  // e
  5784.           vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.1);
  5785.           break;
  5786.         case 65:  // A
  5787.           vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5);
  5788.           break;
  5789.         case 68:  // D
  5790.           vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5);
  5791.           break;
  5792.         case 48:  // 0
  5793.         case 122: // z
  5794.           this.setInitialViewport_();
  5795.           break;
  5796.         case 102:  // f
  5797.           this.zoomToSelection_();
  5798.           break;
  5799.       }
  5800.     },
  5801.  
  5802.     onMouseWheel_: function(e) {
  5803.       if (e.altKey) {
  5804.         var delta = e.wheelDeltaY / 120;
  5805.         var zoomScale = Math.pow(1.5, delta);
  5806.         this.zoomBy_(zoomScale);
  5807.         e.preventDefault();
  5808.       }
  5809.     },
  5810.  
  5811.     // Not all keys send a keypress.
  5812.     onKeydown_: function(e) {
  5813.       if (!this.listenToKeys_)
  5814.         return;
  5815.       var sel;
  5816.       var vp = this.viewport_;
  5817.       var viewWidth = this.firstCanvas.clientWidth;
  5818.       switch (e.keyCode) {
  5819.         case 37:   // left arrow
  5820.           sel = this.selection.getShiftedSelection(-1);
  5821.           if (sel) {
  5822.             this.setSelectionAndMakeVisible(sel);
  5823.             e.preventDefault();
  5824.           } else {
  5825.             if (!this.firstCanvas)
  5826.               return;
  5827.             vp.panX += vp.xViewVectorToWorld(viewWidth * 0.1);
  5828.           }
  5829.           break;
  5830.         case 39:   // right arrow
  5831.           sel = this.selection.getShiftedSelection(1);
  5832.           if (sel) {
  5833.             this.setSelectionAndMakeVisible(sel);
  5834.             e.preventDefault();
  5835.           } else {
  5836.             if (!this.firstCanvas)
  5837.               return;
  5838.             vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.1);
  5839.           }
  5840.           break;
  5841.         case 9:    // TAB
  5842.           if (this.focusElement.tabIndex == -1) {
  5843.             if (e.shiftKey)
  5844.               this.selectPrevious_(e);
  5845.             else
  5846.               this.selectNext_(e);
  5847.             e.preventDefault();
  5848.           }
  5849.           break;
  5850.       }
  5851.       if (e.shiftKey && this.dragBeginEvent_) {
  5852.           var vertical = e.shiftKey;
  5853.           if (this.dragBeginEvent_) {
  5854.             this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
  5855.                                this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
  5856.           }
  5857.       }
  5858.     },
  5859.  
  5860.     onKeyup_: function(e) {
  5861.       if (!this.listenToKeys_)
  5862.         return;
  5863.       if (!e.shiftKey) {
  5864.         if (this.dragBeginEvent_) {
  5865.           var vertical = e.shiftKey;
  5866.           this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
  5867.                                 this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
  5868.           }
  5869.       }
  5870.     },
  5871.  
  5872.     /**
  5873.      * Zoom in or out on the timeline by the given scale factor.
  5874.      * @param {integer} scale The scale factor to apply.  If <1, zooms out.
  5875.      */
  5876.     zoomBy_: function(scale) {
  5877.       if (!this.firstCanvas)
  5878.         return;
  5879.       var vp = this.viewport_;
  5880.       var viewWidth = this.firstCanvas.clientWidth;
  5881.       var pixelRatio = window.devicePixelRatio || 1;
  5882.       var curMouseV = this.lastMouseViewPos_.x * pixelRatio;
  5883.       var curCenterW = vp.xViewToWorld(curMouseV);
  5884.       vp.scaleX = vp.scaleX * scale;
  5885.       vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth);
  5886.     },
  5887.  
  5888.     /**
  5889.      * Zoom into the current selection.
  5890.      */
  5891.     zoomToSelection_: function() {
  5892.       if (!this.selection)
  5893.         return;
  5894.       var range = this.selection.range;
  5895.       var worldCenter = range.min + (range.max - range.min) * 0.5;
  5896.       var worldRange = (range.max - range.min) * 0.5;
  5897.       var boost = worldRange * 0.15;
  5898.       this.viewport_.xSetWorldRange(worldCenter - worldRange - boost,
  5899.                                     worldCenter + worldRange + boost,
  5900.                                     this.firstCanvas.width);
  5901.     },
  5902.  
  5903.     get keyHelp() {
  5904.       var mod = navigator.platform.indexOf('Mac') == 0 ? 'cmd' : 'ctrl';
  5905.       var help = 'Qwerty Controls\n' +
  5906.           ' w/s           : Zoom in/out    (with shift: go faster)\n' +
  5907.           ' a/d           : Pan left/right\n\n' +
  5908.           'Dvorak Controls\n' +
  5909.           ' ,/o           : Zoom in/out     (with shift: go faster)\n' +
  5910.           ' a/e           : Pan left/right\n\n' +
  5911.           'Mouse Controls\n' +
  5912.           ' drag          : Select slices   (with ' + mod +
  5913.                                                         ': zoom to slices)\n' +
  5914.           ' drag + shift  : Select all slices vertically\n\n';
  5915.  
  5916.       if (this.focusElement.tabIndex) {
  5917.         help +=
  5918.           ' <-            : Select previous event on current timeline\n' +
  5919.           ' ->            : Select next event on current timeline\n';
  5920.       } else {
  5921.         help += 'General Navigation\n' +
  5922.           ' g/General     : Shows grid at the start/end of the selected' +
  5923.                                                                   ' task\n' +
  5924.           ' <-,^TAB       : Select previous event on current timeline\n' +
  5925.           ' ->, TAB       : Select next event on current timeline\n';
  5926.       }
  5927.       help +=
  5928.           '\n' +
  5929.           'Alt + Scroll to zoom in/out\n' +
  5930.           'Dbl-click to zoom in; Shift dbl-click to zoom out\n' +
  5931.           'f to zoom into selection\n' +
  5932.           'z to reset zoom and pan to initial view\n';
  5933.       return help;
  5934.     },
  5935.  
  5936.     get selection() {
  5937.       return this.selection_;
  5938.     },
  5939.  
  5940.     set selection(selection) {
  5941.       if (!(selection instanceof TimelineSelection))
  5942.         throw new Error('Expected TimelineSelection');
  5943.  
  5944.       // Clear old selection.
  5945.       var i;
  5946.       for (i = 0; i < this.selection_.length; i++)
  5947.         this.selection_[i].selected = false;
  5948.  
  5949.       this.selection_ = selection;
  5950.  
  5951.       base.dispatchSimpleEvent(this, 'selectionChange');
  5952.       for (i = 0; i < this.selection_.length; i++)
  5953.         this.selection_[i].selected = true;
  5954.       this.viewport_.dispatchChangeEvent(); // Triggers a redraw.
  5955.     },
  5956.  
  5957.     setSelectionAndMakeVisible: function(selection, zoomAllowed) {
  5958.       if (!(selection instanceof TimelineSelection))
  5959.         throw new Error('Expected TimelineSelection');
  5960.       this.selection = selection;
  5961.       var range = this.selection.range;
  5962.       var size = this.viewport_.xWorldVectorToView(range.max - range.min);
  5963.       if (zoomAllowed && size < 50) {
  5964.         var worldCenter = range.min + (range.max - range.min) * 0.5;
  5965.         var worldRange = (range.max - range.min) * 5;
  5966.         this.viewport_.xSetWorldRange(worldCenter - worldRange * 0.5,
  5967.                                       worldCenter + worldRange * 0.5,
  5968.                                       this.firstCanvas.width);
  5969.         return;
  5970.       }
  5971.  
  5972.       this.viewport_.xPanWorldRangeIntoView(range.min, range.max,
  5973.                                             this.firstCanvas.width);
  5974.     },
  5975.  
  5976.     get firstCanvas() {
  5977.       if (this.viewportTrack_)
  5978.         return this.viewportTrack_.firstCanvas;
  5979.       if (this.modelTrack_)
  5980.         return this.modelTrack_.firstCanvas;
  5981.       return undefined;
  5982.     },
  5983.  
  5984.     hideDragBox_: function() {
  5985.       this.dragBox_.style.left = '-1000px';
  5986.       this.dragBox_.style.top = '-1000px';
  5987.       this.dragBox_.style.width = 0;
  5988.       this.dragBox_.style.height = 0;
  5989.     },
  5990.  
  5991.     setDragBoxPosition_: function(xStart, yStart, xEnd, yEnd, vertical) {
  5992.       var loY;
  5993.       var hiY;
  5994.       var loX = Math.min(xStart, xEnd);
  5995.       var hiX = Math.max(xStart, xEnd);
  5996.       var modelTrackRect = this.modelTrack_.getBoundingClientRect();
  5997.  
  5998.       if (vertical) {
  5999.         loY = modelTrackRect.top;
  6000.         hiY = modelTrackRect.bottom;
  6001.       } else {
  6002.         loY = Math.min(yStart, yEnd);
  6003.         hiY = Math.max(yStart, yEnd);
  6004.       }
  6005.  
  6006.       var dragRect = {left: loX, top: loY, width: hiX - loX, height: hiY - loY};
  6007.       dragRect.right = dragRect.left + dragRect.width;
  6008.       dragRect.bottom = dragRect.top + dragRect.height;
  6009.       var modelTrackContainerRect =
  6010.                               this.modelTrackContainer_.getBoundingClientRect();
  6011.       var clipRect = {
  6012.         left: modelTrackContainerRect.left,
  6013.         top: modelTrackContainerRect.top,
  6014.         right: modelTrackContainerRect.right,
  6015.         bottom: modelTrackContainerRect.bottom,
  6016.       };
  6017.       var trackTitleWidth = parseInt(this.modelTrack_.headingWidth);
  6018.       clipRect.left = clipRect.left + trackTitleWidth;
  6019.  
  6020.       var finalDragBox = intersectRect_(clipRect, dragRect);
  6021.  
  6022.       this.dragBox_.style.left = finalDragBox.left + 'px';
  6023.       this.dragBox_.style.width = finalDragBox.width + 'px';
  6024.       this.dragBox_.style.top = finalDragBox.top + 'px';
  6025.       this.dragBox_.style.height = finalDragBox.height + 'px';
  6026.  
  6027.       var pixelRatio = window.devicePixelRatio || 1;
  6028.       var canv = this.firstCanvas;
  6029.       var loWX = this.viewport_.xViewToWorld(
  6030.           (loX - canv.offsetLeft) * pixelRatio);
  6031.       var hiWX = this.viewport_.xViewToWorld(
  6032.           (hiX - canv.offsetLeft) * pixelRatio);
  6033.  
  6034.       var roundedDuration = Math.round((hiWX - loWX) * 100) / 100;
  6035.       this.dragBox_.textContent = roundedDuration + 'ms';
  6036.  
  6037.       var e = new base.Event('selectionChanging');
  6038.       e.loWX = loWX;
  6039.       e.hiWX = hiWX;
  6040.       this.dispatchEvent(e);
  6041.     },
  6042.  
  6043.     onGridToggle_: function(left) {
  6044.       var tb;
  6045.       if (left)
  6046.         tb = this.selection_.range.min;
  6047.       else
  6048.         tb = this.selection_.range.max;
  6049.  
  6050.       // Shift the timebase left until its just left of minTimestamp.
  6051.       var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) /
  6052.           this.viewport_.gridStep_);
  6053.       this.viewport_.gridTimebase = tb -
  6054.           (numInterfvalsSinceStart + 1) * this.viewport_.gridStep_;
  6055.       this.viewport_.gridEnabled = true;
  6056.     },
  6057.  
  6058.     isChildOfThis_: function(el) {
  6059.       if (el == this)
  6060.         return;
  6061.  
  6062.       var isChildOfThis = false;
  6063.       var cur = el;
  6064.       while (cur.parentNode) {
  6065.         if (cur == this)
  6066.           return true;
  6067.         cur = cur.parentNode;
  6068.       }
  6069.       return false;
  6070.     },
  6071.  
  6072.     onMouseDown_: function(e) {
  6073.       if (e.button !== 0)
  6074.         return;
  6075.  
  6076.       if (e.shiftKey) {
  6077.         this.viewportTrack_.placeAndBeginDraggingMarker(e.clientX);
  6078.         return;
  6079.       }
  6080.  
  6081.       var canv = this.firstCanvas;
  6082.       var rect = this.modelTrack_.getBoundingClientRect();
  6083.       var canvRect = this.firstCanvas.getBoundingClientRect();
  6084.  
  6085.       var inside = rect &&
  6086.           e.clientX >= rect.left &&
  6087.           e.clientX < rect.right &&
  6088.           e.clientY >= rect.top &&
  6089.           e.clientY < rect.bottom &&
  6090.           e.clientX >= canvRect.left &&
  6091.           e.clientX < canvRect.right;
  6092.  
  6093.       if (!inside)
  6094.         return;
  6095.  
  6096.       var pos = {
  6097.         x: e.clientX - canv.offsetLeft,
  6098.         y: e.clientY - canv.offsetTop
  6099.       };
  6100.  
  6101.       var wX = this.viewport_.xViewToWorld(pos.x);
  6102.  
  6103.       this.dragBeginEvent_ = e;
  6104.       e.preventDefault();
  6105.       if (document.activeElement)
  6106.         document.activeElement.blur();
  6107.       if (this.focusElement.tabIndex >= 0)
  6108.         this.focusElement.focus();
  6109.     },
  6110.  
  6111.     onMouseMove_: function(e) {
  6112.       if (!this.firstCanvas)
  6113.         return;
  6114.       var canv = this.firstCanvas;
  6115.       var pos = {
  6116.         x: e.clientX - canv.offsetLeft,
  6117.         y: e.clientY - canv.offsetTop
  6118.       };
  6119.  
  6120.       // Remember position. Used during keyboard zooming.
  6121.       this.lastMouseViewPos_ = pos;
  6122.  
  6123.       // Update the drag box
  6124.       if (this.dragBeginEvent_) {
  6125.         this.dragBoxXStart_ = this.dragBeginEvent_.clientX;
  6126.         this.dragBoxXEnd_ = e.clientX;
  6127.         this.dragBoxYStart_ = this.dragBeginEvent_.clientY;
  6128.         this.dragBoxYEnd_ = e.clientY;
  6129.         var vertical = e.shiftKey;
  6130.         this.setDragBoxPosition_(this.dragBoxXStart_, this.dragBoxYStart_,
  6131.                                 this.dragBoxXEnd_, this.dragBoxYEnd_, vertical);
  6132.       }
  6133.     },
  6134.  
  6135.     onMouseUp_: function(e) {
  6136.       var i;
  6137.       if (this.dragBeginEvent_) {
  6138.         // Stop the dragging.
  6139.         this.hideDragBox_();
  6140.         var eDown = this.dragBeginEvent_;
  6141.         this.dragBeginEvent_ = null;
  6142.  
  6143.         // Figure out extents of the drag.
  6144.         var loY;
  6145.         var hiY;
  6146.         var loX = Math.min(eDown.clientX, e.clientX);
  6147.         var hiX = Math.max(eDown.clientX, e.clientX);
  6148.         var tracksContainer = this.modelTrackContainer_.getBoundingClientRect();
  6149.         var topBoundary = tracksContainer.height;
  6150.         var vertical = e.shiftKey;
  6151.         if (vertical) {
  6152.           var modelTrackRect = this.modelTrack_.getBoundingClientRect();
  6153.           loY = modelTrackRect.top;
  6154.           hiY = modelTrackRect.bottom;
  6155.         } else {
  6156.           loY = Math.min(eDown.clientY, e.clientY);
  6157.           hiY = Math.max(eDown.clientY, e.clientY);
  6158.         }
  6159.  
  6160.         // Convert to worldspace.
  6161.         var canv = this.firstCanvas;
  6162.         var loVX = loX - canv.offsetLeft;
  6163.         var hiVX = hiX - canv.offsetLeft;
  6164.  
  6165.         // Figure out what has been hit.
  6166.         var selection = new TimelineSelection();
  6167.         this.modelTrack_.addIntersectingItemsInRangeToSelection(
  6168.             loVX, hiVX, loY, hiY, selection);
  6169.  
  6170.         // Activate the new selection, and zoom if ctrl key held down.
  6171.         this.selection = selection;
  6172.         var isMac = navigator.platform.indexOf('Mac') == 0;
  6173.         if ((isMac && e.metaKey) || (!isMac && e.ctrlKey)) {
  6174.           this.zoomToSelection_();
  6175.         }
  6176.       }
  6177.     },
  6178.  
  6179.     onDblClick_: function(e) {
  6180.       var modelTrackContainerRect =
  6181.                               this.modelTrackContainer_.getBoundingClientRect();
  6182.       var clipBounds = {
  6183.         left: modelTrackContainerRect.left,
  6184.         right: modelTrackContainerRect.right,
  6185.       };
  6186.       var trackTitleWidth = parseInt(this.modelTrack_.headingWidth);
  6187.       clipBounds.left = clipBounds.left + trackTitleWidth;
  6188.  
  6189.       if (e.clientX < clipBounds.left || e.clientX > clipBounds.right)
  6190.         return;
  6191.  
  6192.       var canv = this.firstCanvas;
  6193.  
  6194.       var scale = 4;
  6195.       if (e.shiftKey)
  6196.         scale = 1 / scale;
  6197.       this.zoomBy_(scale);
  6198.       e.preventDefault();
  6199.     }
  6200.   };
  6201.  
  6202.   /**
  6203.    * The TimelineModel being viewed by the timeline
  6204.    * @type {TimelineModel}
  6205.    */
  6206.   base.defineProperty(Timeline, 'model', base.PropertyKind.JS);
  6207.  
  6208.   return {
  6209.     Timeline: Timeline
  6210.   };
  6211. });
  6212.  
  6213. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6214. // Use of this source code is governed by a BSD-style license that can be
  6215. // found in the LICENSE file.
  6216.  
  6217. 'use strict';
  6218.  
  6219. /**
  6220.  * @fileoverview TimelineAnalysis summarizes info about the selected slices
  6221.  * to the analysis panel.
  6222.  */
  6223. base.require('ui');
  6224. base.requireStylesheet('timeline_analysis');
  6225. base.exportTo('tracing', function() {
  6226.  
  6227.   var AnalysisResults = base.ui.define('div');
  6228.  
  6229.   AnalysisResults.prototype = {
  6230.     __proto__: HTMLDivElement.prototype,
  6231.  
  6232.     decorate: function() {
  6233.     },
  6234.  
  6235.     tsRound_: function(ts) {
  6236.       return Math.round(ts * 1000.0) / 1000.0;
  6237.     },
  6238.  
  6239.     appendElement_: function(parent, tagName, opt_text) {
  6240.       var n = parent.ownerDocument.createElement(tagName);
  6241.       parent.appendChild(n);
  6242.       if (opt_text != undefined)
  6243.         n.textContent = opt_text;
  6244.       return n;
  6245.     },
  6246.  
  6247.     appendText_: function(parent, text) {
  6248.       var textElement = parent.ownerDocument.createTextNode(text);
  6249.       parent.appendChild(textNode);
  6250.       return textNode;
  6251.     },
  6252.  
  6253.     appendTableCell_: function(table, row, cellnum, text) {
  6254.       var td = this.appendElement_(row, 'td', text);
  6255.       td.className = table.className + '-col-' + cellnum;
  6256.       return td;
  6257.     },
  6258.  
  6259.     appendTableCellWithTooltip_: function(table, row, cellnum, text, tooltip) {
  6260.       if (tooltip) {
  6261.         var td = this.appendElement_(row, 'td');
  6262.         td.className = table.className + '-col-' + cellnum;
  6263.         var span = this.appendElement_(td, 'span', text);
  6264.         span.className = 'tooltip';
  6265.         span.title = tooltip;
  6266.         return td;
  6267.       } else {
  6268.         this.appendTableCell_(table, row, cellnum, text);
  6269.       }
  6270.     },
  6271.  
  6272.     /**
  6273.      * Adds a table with the given className.
  6274.      * @return {HTMLTableElement} The newly created table.
  6275.      */
  6276.     appendTable: function(className, numColumns) {
  6277.       var table = this.appendElement_(this, 'table');
  6278.       table.className = className + ' timeline-analysis-table';
  6279.       table.numColumns = numColumns;
  6280.       return table;
  6281.     },
  6282.  
  6283.     /**
  6284.      * Creates and appends a row to |table| with a left-aligned |label]
  6285.      * header that spans all columns.
  6286.      */
  6287.     appendTableHeader: function(table, label) {
  6288.       var row = this.appendElement_(table, 'tr');
  6289.  
  6290.       var th = this.appendElement_(row, 'th', label);
  6291.       th.className = 'timeline-analysis-table-header';
  6292.     },
  6293.  
  6294.     /**
  6295.      * Creates and appends a row to |table| with a left-aligned |label]
  6296.      * in the first column and an optional |opt_text| value in the second
  6297.      * column.
  6298.      */
  6299.     appendSummaryRow: function(table, label, opt_text) {
  6300.       var row = this.appendElement_(table, 'tr');
  6301.       row.className = 'timeline-analysis-table-row';
  6302.  
  6303.       this.appendTableCell_(table, row, 0, label);
  6304.       if (opt_text !== undefined) {
  6305.         this.appendTableCell_(table, row, 1, opt_text);
  6306.         for (var i = 2; i < table.numColumns; i++)
  6307.           this.appendTableCell_(table, row, i, '');
  6308.       } else {
  6309.         for (var i = 1; i < table.numColumns; i++)
  6310.           this.appendTableCell_(table, row, 1, '');
  6311.       }
  6312.     },
  6313.  
  6314.     /**
  6315.      * Adds a spacing row to spread out results.
  6316.      */
  6317.     appendSpacingRow: function(table) {
  6318.       var row = this.appendElement_(table, 'tr');
  6319.       row.className = 'timeline-analysis-table-row';
  6320.       for (var i = 0; i < table.numColumns; i++)
  6321.         this.appendTableCell_(table, row, i, ' ');
  6322.     },
  6323.  
  6324.     /**
  6325.      * Creates and appends a row to |table| with a left-aligned |label]
  6326.      * in the first column and a millisecvond |time| value in the second
  6327.      * column.
  6328.      */
  6329.     appendSummaryRowTime: function(table, label, time) {
  6330.       this.appendSummaryRow(table, label, this.tsRound_(time) + ' ms');
  6331.     },
  6332.  
  6333.     /**
  6334.      * Creates and appends a row to |table| that summarizes one or more slices,
  6335.      * or one or more counters.
  6336.      * The row has a left-aligned |label| in the first column, the |duration|
  6337.      * of the data in the second, the number of |occurrences| in the third.
  6338.      * @param {object} opt_statistics May be undefined, or an object which
  6339.      * contains calculated staistics containing min/max/avg for slices, or
  6340.      * min/max/avg/start/end for counters.
  6341.      */
  6342.     appendDataRow: function(
  6343.         table, label, opt_duration, opt_occurences, opt_statistics) {
  6344.  
  6345.       var tooltip = undefined;
  6346.       if (opt_statistics) {
  6347.         tooltip = 'Min Duration:\u0009' + this.tsRound_(opt_statistics.min) +
  6348.                   ' ms \u000DMax Duration:\u0009' +
  6349.                   this.tsRound_(opt_statistics.max) +
  6350.                   ' ms \u000DAvg Duration:\u0009' +
  6351.                   this.tsRound_(opt_statistics.avg) + ' ms (\u03C3 = ' +
  6352.                   this.tsRound_(opt_statistics.avg_stddev) + ')';
  6353.  
  6354.         if (opt_statistics.start) {
  6355.           tooltip += '\u000DStart Time:\u0009' +
  6356.               this.tsRound_(opt_statistics.start) + ' ms';
  6357.         }
  6358.         if (opt_statistics.end) {
  6359.           tooltip += '\u000DEnd Time:\u0009' +
  6360.               this.tsRound_(opt_statistics.end) + ' ms';
  6361.         }
  6362.         if (opt_statistics.frequency && opt_statistics.frequency_stddev) {
  6363.           tooltip += '\u000DFrequency:\u0009' +
  6364.               this.tsRound_(opt_statistics.frequency) +
  6365.               ' occurrences/s (\u03C3 = ' +
  6366.               this.tsRound_(opt_statistics.frequency_stddev) + ')';
  6367.         }
  6368.       }
  6369.  
  6370.       var row = this.appendElement_(table, 'tr');
  6371.       row.className = 'timeline-analysis-table-row';
  6372.  
  6373.       this.appendTableCellWithTooltip_(table, row, 0, label, tooltip);
  6374.  
  6375.       if (opt_duration !== undefined) {
  6376.         this.appendTableCellWithTooltip_(table, row, 1,
  6377.             this.tsRound_(opt_duration) + ' ms', tooltip);
  6378.       } else {
  6379.         this.appendTableCell_(table, row, 1, '');
  6380.       }
  6381.  
  6382.       if (opt_occurences !== undefined) {
  6383.         this.appendTableCellWithTooltip_(table, row, 2,
  6384.             String(opt_occurences) + ' occurrences', tooltip);
  6385.  
  6386.       } else {
  6387.         this.appendTableCell_(table, row, 2, '');
  6388.       }
  6389.     }
  6390.   };
  6391.  
  6392.   /**
  6393.    * Analyzes the selection, outputting the analysis results into the provided
  6394.    * results object.
  6395.    *
  6396.    * @param {AnalysisResults} results Where the analysis is placed.
  6397.    * @param {TimelineSelection} selection What to analyze.
  6398.    */
  6399.   function analyzeSelection(results, selection) {
  6400.  
  6401.     var sliceHits = selection.getSliceHitsAsSelection();
  6402.     var counterSampleHits = selection.getCounterSampleHitsAsSelection();
  6403.  
  6404.     if (sliceHits.length == 1) {
  6405.       var slice = sliceHits[0].slice;
  6406.       var table = results.appendTable('timeline-analysis-slice-table', 2);
  6407.  
  6408.       results.appendTableHeader(table, 'Selected slice:');
  6409.       results.appendSummaryRow(table, 'Title', slice.title);
  6410.  
  6411.       if (slice.category)
  6412.         results.appendSummaryRow(table, 'Category', slice.category);
  6413.  
  6414.       results.appendSummaryRowTime(table, 'Start', slice.start);
  6415.       results.appendSummaryRowTime(table, 'Duration', slice.duration);
  6416.  
  6417.       if (slice.durationInUserTime) {
  6418.         results.appendSummaryRowTime(
  6419.             table, 'Duration (U)', slice.durationInUserTime);
  6420.       }
  6421.  
  6422.       var n = 0;
  6423.       for (var argName in slice.args) {
  6424.         n += 1;
  6425.       }
  6426.       if (n > 0) {
  6427.         results.appendSummaryRow(table, 'Args');
  6428.         for (var argName in slice.args) {
  6429.           var argVal = slice.args[argName];
  6430.           // TODO(sleffler) use span instead?
  6431.           results.appendSummaryRow(table, ' ' + argName, argVal);
  6432.         }
  6433.       }
  6434.     } else if (sliceHits.length > 1) {
  6435.       var tsLo = sliceHits.range.min;
  6436.       var tsHi = sliceHits.range.max;
  6437.  
  6438.       // compute total sliceHits duration
  6439.       var titles = sliceHits.map(function(i) { return i.slice.title; });
  6440.  
  6441.       var numTitles = 0;
  6442.       var slicesByTitle = {};
  6443.       for (var i = 0; i < sliceHits.length; i++) {
  6444.         var slice = sliceHits[i].slice;
  6445.         if (!slicesByTitle[slice.title]) {
  6446.           slicesByTitle[slice.title] = {
  6447.             slices: []
  6448.           };
  6449.           numTitles++;
  6450.         }
  6451.         slicesByTitle[slice.title].slices.push(slice);
  6452.       }
  6453.  
  6454.       var table;
  6455.       table = results.appendTable('timeline-analysis-slices-table', 3);
  6456.       results.appendTableHeader(table, 'Slices:');
  6457.  
  6458.       var totalDuration = 0;
  6459.       for (var sliceGroupTitle in slicesByTitle) {
  6460.         var sliceGroup = slicesByTitle[sliceGroupTitle];
  6461.         var duration = 0;
  6462.         var avg = 0;
  6463.         var startOfFirstOccurrence = Number.MAX_VALUE;
  6464.         var startOfLastOccurrence = -Number.MAX_VALUE;
  6465.         var frequencyDetails = undefined;
  6466.         var min = Number.MAX_VALUE;
  6467.         var max = -Number.MAX_VALUE;
  6468.         for (var i = 0; i < sliceGroup.slices.length; i++) {
  6469.           duration += sliceGroup.slices[i].duration;
  6470.           startOfFirstOccurrence = Math.min(sliceGroup.slices[i].start,
  6471.                                             startOfFirstOccurrence);
  6472.           startOfLastOccurrence = Math.max(sliceGroup.slices[i].start,
  6473.               startOfLastOccurrence);
  6474.           min = Math.min(sliceGroup.slices[i].duration, min);
  6475.           max = Math.max(sliceGroup.slices[i].duration, max);
  6476.         }
  6477.  
  6478.         totalDuration += duration;
  6479.  
  6480.         if (sliceGroup.slices.length == 0)
  6481.           avg = 0;
  6482.         avg = duration / sliceGroup.slices.length;
  6483.  
  6484.         var details = {min: min,
  6485.           max: max,
  6486.           avg: avg,
  6487.           avg_stddev: undefined,
  6488.           frequency: undefined,
  6489.           frequency_stddev: undefined};
  6490.  
  6491.         // Compute the stddev of the slice durations.
  6492.         var sumOfSquaredDistancesToMean = 0;
  6493.         for (var i = 0; i < sliceGroup.slices.length; i++) {
  6494.           var signedDistance = details.avg - sliceGroup.slices[i].duration;
  6495.           sumOfSquaredDistancesToMean += signedDistance * signedDistance;
  6496.         }
  6497.  
  6498.         details.avg_stddev = Math.sqrt(
  6499.             sumOfSquaredDistancesToMean / (sliceGroup.slices.length - 1));
  6500.  
  6501.         // We require at least 3 samples to compute the stddev.
  6502.         var elapsed = startOfLastOccurrence - startOfFirstOccurrence;
  6503.         if (sliceGroup.slices.length > 2 && elapsed > 0) {
  6504.           var numDistances = sliceGroup.slices.length - 1;
  6505.           details.frequency = (1000 * numDistances) / elapsed;
  6506.  
  6507.           // Compute the stddev.
  6508.           sumOfSquaredDistancesToMean = 0;
  6509.           for (var i = 1; i < sliceGroup.slices.length; i++) {
  6510.             var currentFrequency = 1000 /
  6511.                 (sliceGroup.slices[i].start - sliceGroup.slices[i - 1].start);
  6512.             var signedDistance = details.frequency - currentFrequency;
  6513.             sumOfSquaredDistancesToMean += signedDistance * signedDistance;
  6514.           }
  6515.  
  6516.           details.frequency_stddev = Math.sqrt(
  6517.               sumOfSquaredDistancesToMean / (numDistances - 1));
  6518.         }
  6519.         results.appendDataRow(
  6520.             table, sliceGroupTitle, duration, sliceGroup.slices.length,
  6521.             details);
  6522.       }
  6523.       results.appendDataRow(table, '*Totals', totalDuration, sliceHits.length);
  6524.       results.appendSpacingRow(table);
  6525.       results.appendSummaryRowTime(table, 'Selection start', tsLo);
  6526.       results.appendSummaryRowTime(table, 'Selection extent', tsHi - tsLo);
  6527.     }
  6528.  
  6529.     if (counterSampleHits.length == 1) {
  6530.       var hit = counterSampleHits[0];
  6531.       var ctr = hit.counter;
  6532.       var sampleIndex = hit.sampleIndex;
  6533.       var values = [];
  6534.       for (var i = 0; i < ctr.numSeries; ++i)
  6535.         values.push(ctr.samples[ctr.numSeries * sampleIndex + i]);
  6536.  
  6537.       var table = results.appendTable('timeline-analysis-counter-table', 2);
  6538.       results.appendTableHeader(table, 'Selected counter:');
  6539.       results.appendSummaryRow(table, 'Title', ctr.name);
  6540.       results.appendSummaryRowTime(
  6541.           table, 'Timestamp', ctr.timestamps[sampleIndex]);
  6542.  
  6543.       for (var i = 0; i < ctr.numSeries; i++)
  6544.         results.appendSummaryRow(table, ctr.seriesNames[i], values[i]);
  6545.     } else if (counterSampleHits.length > 1) {
  6546.       var hitsByCounter = {};
  6547.       for (var i = 0; i < counterSampleHits.length; i++) {
  6548.         var ctr = counterSampleHits[i].counter;
  6549.         if (!hitsByCounter[ctr.guid])
  6550.           hitsByCounter[ctr.guid] = [];
  6551.         hitsByCounter[ctr.guid].push(counterSampleHits[i]);
  6552.       }
  6553.  
  6554.       var table = results.appendTable('timeline-analysis-counter-table', 7);
  6555.       results.appendTableHeader(table, 'Counters:');
  6556.       for (var id in hitsByCounter) {
  6557.         var hits = hitsByCounter[id];
  6558.         var ctr = hits[0].counter;
  6559.         var sampleIndices = [];
  6560.         for (var i = 0; i < hits.length; i++)
  6561.           sampleIndices.push(hits[i].sampleIndex);
  6562.  
  6563.         var stats = ctr.getSampleStatistics(sampleIndices);
  6564.         for (var i = 0; i < stats.length; i++) {
  6565.           results.appendDataRow(
  6566.               table, ctr.name + ': ' + ctr.seriesNames[i], undefined,
  6567.               undefined, stats[i]);
  6568.         }
  6569.       }
  6570.     }
  6571.   }
  6572.  
  6573.   var TimelineAnalysisView = base.ui.define('div');
  6574.  
  6575.   TimelineAnalysisView.prototype = {
  6576.     __proto__: HTMLDivElement.prototype,
  6577.  
  6578.     decorate: function() {
  6579.       this.className = 'timeline-analysis';
  6580.     },
  6581.  
  6582.     set selection(selection) {
  6583.       this.textContent = '';
  6584.       var results = new AnalysisResults();
  6585.       analyzeSelection(results, selection);
  6586.       this.appendChild(results);
  6587.     }
  6588.   };
  6589.  
  6590.   return {
  6591.     TimelineAnalysisView: TimelineAnalysisView,
  6592.     analyzeSelection_: analyzeSelection
  6593.   };
  6594. });
  6595.  
  6596. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6597. // Use of this source code is governed by a BSD-style license that can be
  6598. // found in the LICENSE file.
  6599.  
  6600.  
  6601. /**
  6602.  * @fileoverview Implements an element that is hidden by default, but
  6603.  * when shown, dims and (attempts to) disable the main document.
  6604.  *
  6605.  * You can turn any div into an overlay. Note that while an
  6606.  * overlay element is shown, its parent is changed. Hiding the overlay
  6607.  * restores its original parentage.
  6608.  *
  6609.  */
  6610. base.requireStylesheet('overlay');
  6611. base.require('ui');
  6612. base.require('event_target');
  6613. base.exportTo('tracing', function() {
  6614.   /**
  6615.    * Manages a full-window div that darkens the window, disables
  6616.    * input, and hosts the currently-visible overlays. You shouldn't
  6617.    * have to instantiate this directly --- it gets set automatically.
  6618.    * @param {Object=} opt_propertyBag Optional properties.
  6619.    * @constructor
  6620.    * @extends {HTMLDivElement}
  6621.    */
  6622.   var OverlayRoot = base.ui.define('div');
  6623.   OverlayRoot.prototype = {
  6624.     __proto__: HTMLDivElement.prototype,
  6625.     decorate: function() {
  6626.       this.classList.add('overlay-root');
  6627.       this.visible = false;
  6628.  
  6629.       this.contentHost = this.ownerDocument.createElement('div');
  6630.       this.contentHost.classList.add('content-host');
  6631.  
  6632.       this.tabCatcher = this.ownerDocument.createElement('span');
  6633.       this.tabCatcher.tabIndex = 0;
  6634.  
  6635.       this.appendChild(this.contentHost);
  6636.  
  6637.       this.onKeydownBoundToThis_ = this.onKeydown_.bind(this);
  6638.       this.onFocusInBoundToThis_ = this.onFocusIn_.bind(this);
  6639.       this.addEventListener('mousedown', this.onMousedown_.bind(this));
  6640.     },
  6641.  
  6642.     /**
  6643.      * Adds an overlay, attaching it to the contentHost so that it is visible.
  6644.      */
  6645.     showOverlay: function(overlay) {
  6646.       // Reparent this to the overlay content host.
  6647.       overlay.oldParent_ = overlay.parentNode;
  6648.       this.contentHost.appendChild(overlay);
  6649.       this.contentHost.appendChild(this.tabCatcher);
  6650.  
  6651.       // Show the overlay root.
  6652.       this.ownerDocument.body.classList.add('disabled-by-overlay');
  6653.       this.visible = true;
  6654.  
  6655.       // Bring overlay into focus.
  6656.       overlay.tabIndex = 0;
  6657.       var focusElement =
  6658.           overlay.querySelector('button, input, list, select, a');
  6659.       if (!focusElement) {
  6660.         focusElement = overlay;
  6661.       }
  6662.       focusElement.focus();
  6663.  
  6664.       // Listen to key and focus events to prevent focus from
  6665.       // leaving the overlay.
  6666.       this.ownerDocument.addEventListener('focusin',
  6667.           this.onFocusInBoundToThis_, true);
  6668.       overlay.addEventListener('keydown', this.onKeydownBoundToThis_);
  6669.     },
  6670.  
  6671.     /**
  6672.      * Clicking outside of the overlay will de-focus the overlay. The
  6673.      * next tab will look at the entire document to determine the focus.
  6674.      * For certain documents, this can cause focus to "leak" outside of
  6675.      * the overlay.
  6676.      */
  6677.     onMousedown_: function(e) {
  6678.       if (e.target == this) {
  6679.         e.preventDefault();
  6680.       }
  6681.     },
  6682.  
  6683.     /**
  6684.      * Prevents forward-tabbing out of the overlay
  6685.      */
  6686.     onFocusIn_: function(e) {
  6687.       if (e.target == this.tabCatcher) {
  6688.         window.setTimeout(this.focusOverlay_.bind(this), 0);
  6689.       }
  6690.     },
  6691.  
  6692.     focusOverlay_: function() {
  6693.       this.contentHost.firstChild.focus();
  6694.     },
  6695.  
  6696.     /**
  6697.      * Prevent the user from shift-tabbing backwards out of the overlay.
  6698.      */
  6699.     onKeydown_: function(e) {
  6700.       if (e.keyCode == 9 &&
  6701.           e.shiftKey &&
  6702.           e.target == this.contentHost.firstChild) {
  6703.         e.preventDefault();
  6704.       }
  6705.     },
  6706.  
  6707.     /**
  6708.      * Hides an overlay, attaching it to its original parent if needed.
  6709.      */
  6710.     hideOverlay: function(overlay) {
  6711.       // hide the overlay root
  6712.       this.visible = false;
  6713.       this.ownerDocument.body.classList.remove('disabled-by-overlay');
  6714.       this.lastFocusOut_ = undefined;
  6715.  
  6716.       // put the overlay back on its previous parent
  6717.       overlay.parentNode.removeChild(this.tabCatcher);
  6718.       if (overlay.oldParent_) {
  6719.         overlay.oldParent_.appendChild(overlay);
  6720.         delete overlay.oldParent_;
  6721.       } else {
  6722.         this.contentHost.removeChild(overlay);
  6723.       }
  6724.  
  6725.       // remove listeners
  6726.       overlay.removeEventListener('keydown', this.onKeydownBoundToThis_);
  6727.       this.ownerDocument.removeEventListener('focusin',
  6728.           this.onFocusInBoundToThis_);
  6729.     }
  6730.   };
  6731.  
  6732.   base.defineProperty(OverlayRoot, 'visible', base.PropertyKind.BOOL_ATTR);
  6733.  
  6734.   /**
  6735.    * Creates a new overlay element. It will not be visible until shown.
  6736.    * @param {Object=} opt_propertyBag Optional properties.
  6737.    * @constructor
  6738.    * @extends {HTMLDivElement}
  6739.    */
  6740.   var Overlay = base.ui.define('div');
  6741.  
  6742.   Overlay.prototype = {
  6743.     __proto__: HTMLDivElement.prototype,
  6744.  
  6745.     /**
  6746.      * Initializes the overlay element.
  6747.      */
  6748.     decorate: function() {
  6749.       // create the overlay root on this document if its not present
  6750.       if (!this.ownerDocument.querySelector('.overlay-root')) {
  6751.         var overlayRoot = this.ownerDocument.createElement('div');
  6752.         base.ui.decorate(overlayRoot, OverlayRoot);
  6753.         this.ownerDocument.body.appendChild(overlayRoot);
  6754.       }
  6755.  
  6756.       this.classList.add('overlay');
  6757.       this.visible = false;
  6758.       this.defaultClickShouldClose = true;
  6759.       this.autoClose = false;
  6760.       this.additionalCloseKeyCodes = [];
  6761.       this.onKeyDown = this.onKeyDown.bind(this);
  6762.       this.onKeyPress = this.onKeyPress.bind(this);
  6763.       this.onDocumentClick = this.onDocumentClick.bind(this);
  6764.     },
  6765.  
  6766.     onVisibleChanged_: function() {
  6767.       var overlayRoot = this.ownerDocument.querySelector('.overlay-root');
  6768.       base.dispatchSimpleEvent(this, 'visibleChange');
  6769.       if (this.visible) {
  6770.         overlayRoot.showOverlay(this);
  6771.         document.addEventListener('keydown', this.onKeyDown, true);
  6772.         document.addEventListener('keypress', this.onKeyPress, true);
  6773.         document.addEventListener('click', this.onDocumentClick, true);
  6774.       } else {
  6775.         document.removeEventListener('keydown', this.onKeyDown, true);
  6776.         document.removeEventListener('keypress', this.onKeyPress, true);
  6777.         document.removeEventListener('click', this.onDocumentClick, true);
  6778.         overlayRoot.hideOverlay(this);
  6779.       }
  6780.     },
  6781.  
  6782.     onKeyDown: function(e) {
  6783.       if (!this.autoClose)
  6784.         return;
  6785.  
  6786.       if (e.keyCode == 27) {
  6787.         this.visible = false;
  6788.         e.preventDefault();
  6789.         return;
  6790.       }
  6791.     },
  6792.  
  6793.     onKeyPress: function(e) {
  6794.       if (!this.autoClose)
  6795.         return;
  6796.  
  6797.       for (var i = 0; i < this.additionalCloseKeyCodes.length; i++) {
  6798.         if (e.keyCode == this.additionalCloseKeyCodes[i]) {
  6799.           this.visible = false;
  6800.           e.preventDefault();
  6801.           return;
  6802.         }
  6803.       }
  6804.     },
  6805.  
  6806.     onDocumentClick: function(e) {
  6807.       if (!this.defaultClickShouldClose)
  6808.         return;
  6809.       var target = e.target;
  6810.       while (target !== null) {
  6811.         if (target === this)
  6812.           return;
  6813.         target = target.parentNode;
  6814.       }
  6815.       this.visible = false;
  6816.       e.preventDefault();
  6817.       return;
  6818.     }
  6819.  
  6820.   };
  6821.  
  6822.   /**
  6823.    * Shows and hides the overlay. Note that while visible == true, the overlay
  6824.    * element will be tempoarily reparented to another place in the DOM.
  6825.    */
  6826.   base.defineProperty(Overlay, 'visible', base.PropertyKind.BOOL_ATTR,
  6827.       Overlay.prototype.onVisibleChanged_);
  6828.   base.defineProperty(Overlay, 'defaultClickShouldClose',
  6829.       base.PropertyKind.BOOL_ATTR);
  6830.  
  6831.   return {
  6832.     Overlay: Overlay
  6833.   };
  6834. });
  6835.  
  6836. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6837. // Use of this source code is governed by a BSD-style license that can be
  6838. // found in the LICENSE file.
  6839.  
  6840. 'use strict';
  6841.  
  6842. /**
  6843.  * @fileoverview TimelineCategoryFilterButton extracts categories from the model
  6844.  * and shows/hides them based on settings.
  6845.  */
  6846. base.require('timeline_filter');
  6847. base.require('overlay');
  6848. base.exportTo('tracing', function() {
  6849.  
  6850.   var TimelineCategoryFilterDialog = base.ui.define('div');
  6851.  
  6852.   TimelineCategoryFilterDialog.prototype = {
  6853.     __proto__: tracing.Overlay.prototype,
  6854.  
  6855.     decorate: function() {
  6856.       tracing.Overlay.prototype.decorate.call(this);
  6857.  
  6858.       this.className = 'timeline-view-category-filter-overlay';
  6859.       this.autoClose = true;
  6860.  
  6861.       var containerEl = document.createElement('div');
  6862.       containerEl.className = 'category-filter-dialog';
  6863.       containerEl.textContent = 'Select active categories:';
  6864.       this.formEl_ = document.createElement('form');
  6865.       this.formEl_.className = 'category-filter-dialog-form';
  6866.       containerEl.appendChild(this.formEl_);
  6867.       this.appendChild(containerEl);
  6868.  
  6869.       this.addEventListener('visibleChange', this.onVisibleChange_.bind(this));
  6870.     },
  6871.  
  6872.     get model() {
  6873.       return this.model_;
  6874.     },
  6875.  
  6876.     set model(m) {
  6877.       this.model_ = m;
  6878.     },
  6879.  
  6880.     get settings() {
  6881.       return this.settings_;
  6882.     },
  6883.  
  6884.     set settings(s) {
  6885.       this.settings_ = s;
  6886.     },
  6887.  
  6888.     set settingUpdatedCallback(c) {
  6889.       this.settingUpdatedCallback_ = c;
  6890.     },
  6891.  
  6892.     onVisibleChange_: function() {
  6893.       if (this.visible) {
  6894.         this.updateForm_();
  6895.       }
  6896.     },
  6897.  
  6898.     updateForm_: function() {
  6899.       // Clear and update the form every time the dialog is shown, in case
  6900.       // the model or settings have changed with new categories.
  6901.       this.formEl_.innerHTML = ''; // Clear old categories
  6902.       var categories = this.model_.categories;
  6903.       categories.concat(this.settings_.keys('categories'));
  6904.       for (var i = 0; i < categories.length; i++) {
  6905.         var category = categories[i];
  6906.         var inputEl = document.createElement('input');
  6907.         inputEl.type = 'checkbox';
  6908.         inputEl.id = inputEl.value = category;
  6909.         inputEl.checked =
  6910.             this.settings_.get(category, 'true', 'categories') == 'true';
  6911.         inputEl.onchange = this.updateSetting_.bind(this);
  6912.         var labelEl = document.createElement('label');
  6913.         labelEl.textContent = category;
  6914.         labelEl.setAttribute('for', category);
  6915.         this.formEl_.appendChild(inputEl);
  6916.         this.formEl_.appendChild(labelEl);
  6917.         this.formEl_.appendChild(document.createElement('br'));
  6918.       }
  6919.     },
  6920.  
  6921.     updateSetting_: function(e) {
  6922.       var checkbox = e.target;
  6923.       this.settings_.set(checkbox.value, checkbox.checked, 'categories');
  6924.       this.settingUpdatedCallback_();
  6925.     }
  6926.   };
  6927.  
  6928.  
  6929.   return {
  6930.     TimelineCategoryFilterDialog: TimelineCategoryFilterDialog
  6931.   };
  6932. });
  6933.  
  6934. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  6935. // Use of this source code is governed by a BSD-style license that can be
  6936. // found in the LICENSE file.
  6937.  
  6938. 'use strict';
  6939.  
  6940. /**
  6941.  * @fileoverview TimelineFindControl and TimelineFindController.
  6942.  */
  6943. base.require('timeline');
  6944. base.require('timeline_filter');
  6945. base.require('overlay');
  6946. base.exportTo('tracing', function() {
  6947.  
  6948.   /**
  6949.    * TimelineFindControl
  6950.    * @constructor
  6951.    * @extends {tracing.Overlay}
  6952.    */
  6953.   var TimelineFindControl = base.ui.define('div');
  6954.  
  6955.   TimelineFindControl.prototype = {
  6956.     __proto__: tracing.Overlay.prototype,
  6957.  
  6958.     decorate: function() {
  6959.       tracing.Overlay.prototype.decorate.call(this);
  6960.  
  6961.       this.className = 'timeline-find-control';
  6962.  
  6963.       this.hitCountEl_ = document.createElement('div');
  6964.       this.hitCountEl_.className = 'hit-count-label';
  6965.       this.hitCountEl_.textContent = '1 of 7';
  6966.  
  6967.       var findPreviousBn = document.createElement('div');
  6968.       findPreviousBn.className = 'timeline-button find-previous';
  6969.       findPreviousBn.textContent = '\u2190';
  6970.       findPreviousBn.addEventListener('click', function() {
  6971.         this.controller.findPrevious();
  6972.         this.updateHitCountEl_();
  6973.       }.bind(this));
  6974.  
  6975.       var findNextBn = document.createElement('div');
  6976.       findNextBn.className = 'timeline-button find-next';
  6977.       findNextBn.textContent = '\u2192';
  6978.       findNextBn.addEventListener('click', function() {
  6979.         this.controller.findNext();
  6980.         this.updateHitCountEl_();
  6981.       }.bind(this));
  6982.  
  6983.       // Filter input element.
  6984.       this.filterEl_ = document.createElement('input');
  6985.       this.filterEl_.type = 'input';
  6986.  
  6987.       this.filterEl_.addEventListener('input', function(e) {
  6988.         this.controller.filterText = this.filterEl_.value;
  6989.         this.updateHitCountEl_();
  6990.       }.bind(this));
  6991.  
  6992.       this.filterEl_.addEventListener('keydown', function(e) {
  6993.         if (e.keyCode == 13) {
  6994.           findNextBn.click();
  6995.         } else if (e.keyCode == 27) {
  6996.           this.filterEl_.blur();
  6997.           this.updateHitCountEl_();
  6998.         }
  6999.       }.bind(this));
  7000.  
  7001.       this.filterEl_.addEventListener('blur', function(e) {
  7002.         this.updateHitCountEl_();
  7003.       }.bind(this));
  7004.  
  7005.       this.filterEl_.addEventListener('focus', function(e) {
  7006.         this.updateHitCountEl_();
  7007.       }.bind(this));
  7008.  
  7009.       // Attach everything.
  7010.       this.appendChild(this.filterEl_);
  7011.  
  7012.       this.appendChild(findPreviousBn);
  7013.       this.appendChild(findNextBn);
  7014.       this.appendChild(this.hitCountEl_);
  7015.  
  7016.       this.updateHitCountEl_();
  7017.     },
  7018.  
  7019.     get controller() {
  7020.       return this.controller_;
  7021.     },
  7022.  
  7023.     set controller(c) {
  7024.       this.controller_ = c;
  7025.       this.updateHitCountEl_();
  7026.     },
  7027.  
  7028.     focus: function() {
  7029.       this.filterEl_.selectionStart = 0;
  7030.       this.filterEl_.selectionEnd = this.filterEl_.value.length;
  7031.       this.filterEl_.focus();
  7032.     },
  7033.  
  7034.     updateHitCountEl_: function() {
  7035.       if (!this.controller || document.activeElement != this.filterEl_) {
  7036.         this.hitCountEl_.textContent = '';
  7037.         return;
  7038.       }
  7039.       var i = this.controller.currentHitIndex;
  7040.       var n = this.controller.filterHits.length;
  7041.       if (n == 0)
  7042.         this.hitCountEl_.textContent = '0 of 0';
  7043.       else
  7044.         this.hitCountEl_.textContent = (i + 1) + ' of ' + n;
  7045.     }
  7046.   };
  7047.  
  7048.   function TimelineFindController() {
  7049.     this.timeline_ = undefined;
  7050.     this.model_ = undefined;
  7051.     this.filterText_ = '';
  7052.     this.filterHits_ = new tracing.TimelineSelection();
  7053.     this.filterHitsDirty_ = true;
  7054.     this.currentHitIndex_ = 0;
  7055.   };
  7056.  
  7057.   TimelineFindController.prototype = {
  7058.     __proto__: Object.prototype,
  7059.  
  7060.     get timeline() {
  7061.       return this.timeline_;
  7062.     },
  7063.  
  7064.     set timeline(t) {
  7065.       this.timeline_ = t;
  7066.       this.filterHitsDirty_ = true;
  7067.     },
  7068.  
  7069.     get filterText() {
  7070.       return this.filterText_;
  7071.     },
  7072.  
  7073.     set filterText(f) {
  7074.       if (f == this.filterText_)
  7075.         return;
  7076.       this.filterText_ = f;
  7077.       this.filterHitsDirty_ = true;
  7078.       this.findNext();
  7079.     },
  7080.  
  7081.     get filterHits() {
  7082.       if (this.filterHitsDirty_) {
  7083.         this.filterHitsDirty_ = false;
  7084.         if (this.timeline_) {
  7085.           var filter = new tracing.TimelineTitleFilter(this.filterText);
  7086.           this.filterHits_.clear();
  7087.           this.timeline.addAllObjectsMatchingFilterToSelection(
  7088.               filter, this.filterHits_);
  7089.           this.currentHitIndex_ = this.filterHits_.length - 1;
  7090.         } else {
  7091.           this.filterHits_.clear();
  7092.           this.currentHitIndex_ = 0;
  7093.         }
  7094.       }
  7095.       return this.filterHits_;
  7096.     },
  7097.  
  7098.     get currentHitIndex() {
  7099.       return this.currentHitIndex_;
  7100.     },
  7101.  
  7102.     find_: function(dir) {
  7103.       if (!this.timeline)
  7104.         return;
  7105.  
  7106.       var N = this.filterHits.length;
  7107.       this.currentHitIndex_ = this.currentHitIndex_ + dir;
  7108.  
  7109.       if (this.currentHitIndex_ < 0) this.currentHitIndex_ = N - 1;
  7110.       if (this.currentHitIndex_ >= N) this.currentHitIndex_ = 0;
  7111.  
  7112.       if (this.currentHitIndex_ < 0 || this.currentHitIndex_ >= N) {
  7113.         this.timeline.selection = new tracing.TimelineSelection();
  7114.         return;
  7115.       }
  7116.  
  7117.       // We allow the zoom level to change on the first hit level. But, when
  7118.       // then cycling through subsequent changes, restrict it to panning.
  7119.       var zoomAllowed = this.currentHitIndex_ == 0;
  7120.       var subSelection = this.filterHits.subSelection(this.currentHitIndex_);
  7121.       this.timeline.setSelectionAndMakeVisible(subSelection, zoomAllowed);
  7122.     },
  7123.  
  7124.     findNext: function() {
  7125.       this.find_(1);
  7126.     },
  7127.  
  7128.     findPrevious: function() {
  7129.       this.find_(-1);
  7130.     }
  7131.   };
  7132.  
  7133.   return {
  7134.     TimelineFindControl: TimelineFindControl,
  7135.     TimelineFindController: TimelineFindController
  7136.   };
  7137. });
  7138.  
  7139. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7140. // Use of this source code is governed by a BSD-style license that can be
  7141. // found in the LICENSE file.
  7142.  
  7143. /**
  7144.  * @fileoverview TraceEventImporter imports TraceEvent-formatted data
  7145.  * into the provided timeline model.
  7146.  */
  7147. base.require('timeline_model');
  7148. base.require('timeline_color_scheme');
  7149. base.exportTo('tracing', function() {
  7150.  
  7151.   function TraceEventImporter(model, eventData) {
  7152.     this.importPriority = 1;
  7153.     this.model_ = model;
  7154.  
  7155.     if (typeof(eventData) === 'string' || eventData instanceof String) {
  7156.       // If the event data begins with a [, then we know it should end with a ].
  7157.       // The reason we check for this is because some tracing implementations
  7158.       // cannot guarantee that a ']' gets written to the trace file. So, we are
  7159.       // forgiving and if this is obviously the case, we fix it up before
  7160.       // throwing the string at JSON.parse.
  7161.       if (eventData[0] == '[') {
  7162.         n = eventData.length;
  7163.         if (eventData[n - 1] == '\n') {
  7164.           eventData = eventData.substring(0, n - 1);
  7165.           n--;
  7166.  
  7167.           if (eventData[n - 1] == '\r') {
  7168.             eventData = eventData.substring(0, n - 1);
  7169.             n--;
  7170.           }
  7171.         }
  7172.  
  7173.         if (eventData[n - 1] == ',')
  7174.           eventData = eventData.substring(0, n - 1);
  7175.         if (eventData[n - 1] != ']')
  7176.           eventData = eventData + ']';
  7177.       }
  7178.  
  7179.       this.events_ = JSON.parse(eventData);
  7180.  
  7181.     } else {
  7182.       this.events_ = eventData;
  7183.     }
  7184.  
  7185.     // Some trace_event implementations put the actual trace events
  7186.     // inside a container. E.g { ... , traceEvents: [ ] }
  7187.     // If we see that, just pull out the trace events.
  7188.     if (this.events_.traceEvents) {
  7189.       this.events_ = this.events_.traceEvents;
  7190.       for (fieldName in this.events_) {
  7191.         if (fieldName == 'traceEvents')
  7192.           continue;
  7193.         this.model_.metadata.push({name: fieldName,
  7194.           value: this.events_[fieldName]});
  7195.       }
  7196.     }
  7197.  
  7198.     // Async events need to be processed durign finalizeEvents
  7199.     this.allAsyncEvents_ = [];
  7200.   }
  7201.  
  7202.   /**
  7203.    * @return {boolean} Whether obj is a TraceEvent array.
  7204.    */
  7205.   TraceEventImporter.canImport = function(eventData) {
  7206.     // May be encoded JSON. But we dont want to parse it fully yet.
  7207.     // Use a simple heuristic:
  7208.     //   - eventData that starts with [ are probably trace_event
  7209.     //   - eventData that starts with { are probably trace_event
  7210.     // May be encoded JSON. Treat files that start with { as importable by us.
  7211.     if (typeof(eventData) === 'string' || eventData instanceof String) {
  7212.       return eventData[0] == '{' || eventData[0] == '[';
  7213.     }
  7214.  
  7215.     // Might just be an array of events
  7216.     if (eventData instanceof Array && eventData.length && eventData[0].ph)
  7217.       return true;
  7218.  
  7219.     // Might be an object with a traceEvents field in it.
  7220.     if (eventData.traceEvents)
  7221.       return eventData.traceEvents instanceof Array &&
  7222.           eventData.traceEvents[0].ph;
  7223.  
  7224.     return false;
  7225.   };
  7226.  
  7227.   TraceEventImporter.prototype = {
  7228.  
  7229.     __proto__: Object.prototype,
  7230.  
  7231.     /**
  7232.      * Helper to process an 'async finish' event, which will close an open slice
  7233.      * on a TimelineAsyncSliceGroup object.
  7234.      */
  7235.     processAsyncEvent: function(index, event) {
  7236.       var thread = this.model_.getOrCreateProcess(event.pid).
  7237.           getOrCreateThread(event.tid);
  7238.       this.allAsyncEvents_.push({
  7239.         event: event,
  7240.         thread: thread});
  7241.     },
  7242.  
  7243.     /**
  7244.      * Helper that creates and adds samples to a TimelineCounter object based on
  7245.      * 'C' phase events.
  7246.      */
  7247.     processCounterEvent: function(event) {
  7248.       var ctr_name;
  7249.       if (event.id !== undefined)
  7250.         ctr_name = event.name + '[' + event.id + ']';
  7251.       else
  7252.         ctr_name = event.name;
  7253.  
  7254.       var ctr = this.model_.getOrCreateProcess(event.pid)
  7255.           .getOrCreateCounter(event.cat, ctr_name);
  7256.       // Initialize the counter's series fields if needed.
  7257.       if (ctr.numSeries == 0) {
  7258.         for (var seriesName in event.args) {
  7259.           ctr.seriesNames.push(seriesName);
  7260.           ctr.seriesColors.push(
  7261.               tracing.getStringColorId(ctr.name + '.' + seriesName));
  7262.         }
  7263.         if (ctr.numSeries == 0) {
  7264.           this.model_.importErrors.push('Expected counter ' + event.name +
  7265.               ' to have at least one argument to use as a value.');
  7266.           // Drop the counter.
  7267.           delete ctr.parent.counters[ctr.name];
  7268.           return;
  7269.         }
  7270.       }
  7271.  
  7272.       // Add the sample values.
  7273.       ctr.timestamps.push(event.ts / 1000);
  7274.       for (var i = 0; i < ctr.numSeries; i++) {
  7275.         var seriesName = ctr.seriesNames[i];
  7276.         if (event.args[seriesName] === undefined) {
  7277.           ctr.samples.push(0);
  7278.           continue;
  7279.         }
  7280.         ctr.samples.push(event.args[seriesName]);
  7281.       }
  7282.     },
  7283.  
  7284.     /**
  7285.      * Walks through the events_ list and outputs the structures discovered to
  7286.      * model_.
  7287.      */
  7288.     importEvents: function() {
  7289.       // Walk through events
  7290.       var events = this.events_;
  7291.       // Some events cannot be handled until we have done a first pass over the
  7292.       // data set.  So, accumulate them into a temporary data structure.
  7293.       var second_pass_events = [];
  7294.       for (var eI = 0; eI < events.length; eI++) {
  7295.         var event = events[eI];
  7296.         if (event.ph == 'B') {
  7297.           var thread = this.model_.getOrCreateProcess(event.pid)
  7298.             .getOrCreateThread(event.tid);
  7299.           if (!thread.isTimestampValidForBeginOrEnd(event.ts / 1000)) {
  7300.             this.model_.importErrors.push(
  7301.                 'Timestamps are moving backward.');
  7302.             continue;
  7303.           }
  7304.           thread.beginSlice(event.cat, event.name, event.ts / 1000, event.args);
  7305.         } else if (event.ph == 'E') {
  7306.           var thread = this.model_.getOrCreateProcess(event.pid)
  7307.             .getOrCreateThread(event.tid);
  7308.           if (!thread.isTimestampValidForBeginOrEnd(event.ts / 1000)) {
  7309.             this.model_.importErrors.push(
  7310.                 'Timestamps are moving backward.');
  7311.             continue;
  7312.           }
  7313.           if (!thread.openSliceCount) {
  7314.             this.model_.importErrors.push(
  7315.                 'E phase event without a matching B phase event.');
  7316.             continue;
  7317.           }
  7318.  
  7319.           var slice = thread.endSlice(event.ts / 1000);
  7320.           for (var arg in event.args) {
  7321.             if (slice.args[arg] !== undefined) {
  7322.               this.model_.importErrors.push(
  7323.                   'Both the B and E phases of ' + slice.name +
  7324.                   'provided values for argument ' + arg + '. ' +
  7325.                   'The value of the E phase event will be used.');
  7326.             }
  7327.             slice.args[arg] = event.args[arg];
  7328.           }
  7329.  
  7330.         } else if (event.ph == 'S') {
  7331.           this.processAsyncEvent(eI, event);
  7332.         } else if (event.ph == 'F') {
  7333.           this.processAsyncEvent(eI, event);
  7334.         } else if (event.ph == 'T') {
  7335.           this.processAsyncEvent(eI, event);
  7336.         } else if (event.ph == 'I') {
  7337.           // Treat an Instant event as a duration 0 slice.
  7338.           // TimelineSliceTrack's redraw() knows how to handle this.
  7339.           var thread = this.model_.getOrCreateProcess(event.pid)
  7340.             .getOrCreateThread(event.tid);
  7341.           thread.beginSlice(event.cat, event.name, event.ts / 1000, event.args);
  7342.           thread.endSlice(event.ts / 1000);
  7343.         } else if (event.ph == 'C') {
  7344.           this.processCounterEvent(event);
  7345.         } else if (event.ph == 'M') {
  7346.           if (event.name == 'thread_name') {
  7347.             var thread = this.model_.getOrCreateProcess(event.pid)
  7348.                              .getOrCreateThread(event.tid);
  7349.             thread.name = event.args.name;
  7350.           } else {
  7351.             this.model_.importErrors.push(
  7352.                 'Unrecognized metadata name: ' + event.name);
  7353.           }
  7354.         } else if (event.ph == 's') {
  7355.           // NB: toss until there's proper support
  7356.         } else if (event.ph == 't') {
  7357.           // NB: toss until there's proper support
  7358.         } else if (event.ph == 'f') {
  7359.           // NB: toss until there's proper support
  7360.         } else {
  7361.           this.model_.importErrors.push(
  7362.               'Unrecognized event phase: ' + event.ph +
  7363.               '(' + event.name + ')');
  7364.         }
  7365.       }
  7366.     },
  7367.  
  7368.     /**
  7369.      * Called by the TimelineModel after all other importers have imported their
  7370.      * events.
  7371.      */
  7372.     finalizeImport: function() {
  7373.       this.createAsyncSlices_();
  7374.     },
  7375.  
  7376.     createAsyncSlices_: function() {
  7377.       if (this.allAsyncEvents_.length == 0)
  7378.         return;
  7379.  
  7380.       this.allAsyncEvents_.sort(function(x, y) {
  7381.         return x.event.ts - y.event.ts;
  7382.       });
  7383.  
  7384.       var asyncEventStatesByNameThenID = {};
  7385.  
  7386.       var allAsyncEvents = this.allAsyncEvents_;
  7387.       for (var i = 0; i < allAsyncEvents.length; i++) {
  7388.         var asyncEventState = allAsyncEvents[i];
  7389.  
  7390.         var event = asyncEventState.event;
  7391.         var name = event.name;
  7392.         if (name === undefined) {
  7393.           this.model_.importErrors.push(
  7394.               'Async events (ph: S, T or F) require an name parameter.');
  7395.           continue;
  7396.         }
  7397.  
  7398.         var id = event.id;
  7399.         if (id === undefined) {
  7400.           this.model_.importErrors.push(
  7401.               'Async events (ph: S, T or F) require an id parameter.');
  7402.           continue;
  7403.         }
  7404.  
  7405.         // TODO(simonjam): Add a synchronous tick on the appropriate thread.
  7406.  
  7407.         if (event.ph == 'S') {
  7408.           if (asyncEventStatesByNameThenID[name] === undefined)
  7409.             asyncEventStatesByNameThenID[name] = {};
  7410.           if (asyncEventStatesByNameThenID[name][id]) {
  7411.             this.model_.importErrors.push(
  7412.                 'At ' + event.ts + ', a slice of the same id ' + id +
  7413.                 ' was alrady open.');
  7414.             continue;
  7415.           }
  7416.           asyncEventStatesByNameThenID[name][id] = [];
  7417.           asyncEventStatesByNameThenID[name][id].push(asyncEventState);
  7418.         } else {
  7419.           if (asyncEventStatesByNameThenID[name] === undefined) {
  7420.             this.model_.importErrors.push(
  7421.                 'At ' + event.ts + ', no slice named ' + name +
  7422.                 ' was open.');
  7423.             continue;
  7424.           }
  7425.           if (asyncEventStatesByNameThenID[name][id] === undefined) {
  7426.             this.model_.importErrors.push(
  7427.                 'At ' + event.ts + ', no slice named ' + name +
  7428.                 ' with id=' + id + ' was open.');
  7429.             continue;
  7430.           }
  7431.           var events = asyncEventStatesByNameThenID[name][id];
  7432.           events.push(asyncEventState);
  7433.  
  7434.           if (event.ph == 'F') {
  7435.             // Create a slice from start to end.
  7436.             var slice = new tracing.TimelineAsyncSlice(
  7437.                 events[0].event.cat,
  7438.                 name,
  7439.                 tracing.getStringColorId(name),
  7440.                 events[0].event.ts / 1000);
  7441.  
  7442.             slice.duration = (event.ts / 1000) - (events[0].event.ts / 1000);
  7443.  
  7444.             slice.startThread = events[0].thread;
  7445.             slice.endThread = asyncEventState.thread;
  7446.             slice.id = id;
  7447.             slice.args = events[0].event.args;
  7448.             slice.subSlices = [];
  7449.  
  7450.             // Create subSlices for each step.
  7451.             for (var j = 1; j < events.length; ++j) {
  7452.               var subName = name;
  7453.               if (events[j - 1].event.ph == 'T')
  7454.                 subName = name + ':' + events[j - 1].event.args.step;
  7455.               var subSlice = new tracing.TimelineAsyncSlice(
  7456.                   events[0].event.cat,
  7457.                   subName,
  7458.                   tracing.getStringColorId(name + j),
  7459.                   events[j - 1].event.ts / 1000);
  7460.  
  7461.               subSlice.duration =
  7462.                   (events[j].event.ts / 1000) - (events[j - 1].event.ts / 1000);
  7463.  
  7464.               subSlice.startThread = events[j - 1].thread;
  7465.               subSlice.endThread = events[j].thread;
  7466.               subSlice.id = id;
  7467.               subSlice.args = events[j - 1].event.args;
  7468.  
  7469.               slice.subSlices.push(subSlice);
  7470.             }
  7471.  
  7472.             // The args for the finish event go in the last subSlice.
  7473.             var lastSlice = slice.subSlices[slice.subSlices.length - 1];
  7474.             for (var arg in event.args)
  7475.               lastSlice.args[arg] = event.args[arg];
  7476.  
  7477.             // Add |slice| to the start-thread's asyncSlices.
  7478.             slice.startThread.asyncSlices.push(slice);
  7479.             delete asyncEventStatesByNameThenID[name][id];
  7480.           }
  7481.         }
  7482.       }
  7483.     }
  7484.   };
  7485.  
  7486.   tracing.TimelineModel.registerImporter(TraceEventImporter);
  7487.  
  7488.   return {
  7489.     TraceEventImporter: TraceEventImporter
  7490.   };
  7491. });
  7492.  
  7493. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7494. // Use of this source code is governed by a BSD-style license that can be
  7495. // found in the LICENSE file.
  7496.  
  7497. /**
  7498.  * @fileoverview Base class for linux perf event parsers.
  7499.  *
  7500.  * The linux perf trace event importer depends on subclasses of
  7501.  * LinuxPerfParser to parse event data.  Each subclass corresponds
  7502.  * to a group of trace events; e.g. LinuxPerfSchedParser implements
  7503.  * parsing of sched:* kernel trace events.  Parser subclasses must
  7504.  * call LinuxPerfParser.registerSubtype to arrange to be instantiated
  7505.  * and their constructor must register their event handlers with the
  7506.  * importer.  For example,
  7507.  *
  7508.  * var LinuxPerfParser = tracing.LinuxPerfParser;
  7509.  *
  7510.  * function LinuxPerfWorkqueueParser(importer) {
  7511.  *   LinuxPerfParser.call(this, importer);
  7512.  *
  7513.  *   importer.registerEventHandler('workqueue_execute_start',
  7514.  *       LinuxPerfWorkqueueParser.prototype.executeStartEvent.bind(this));
  7515.  *   importer.registerEventHandler('workqueue_execute_end',
  7516.  *       LinuxPerfWorkqueueParser.prototype.executeEndEvent.bind(this));
  7517.  * }
  7518.  *
  7519.  * LinuxPerfParser.registerSubtype(LinuxPerfWorkqueueParser);
  7520.  *
  7521.  * When a registered event name is found in the data stream the associated
  7522.  * event handler is invoked:
  7523.  *
  7524.  *   executeStartEvent: function(eventName, cpuNumber, ts, eventBase)
  7525.  *
  7526.  * If the routine returns false the caller will generate an import error
  7527.  * saying there was a problem parsing it.  Handlers can also emit import
  7528.  * messages using this.importer.importError.  If this is done in lieu of
  7529.  * the generic import error it may be desirable for the handler to return
  7530.  * true.
  7531.  *
  7532.  * Trace events generated by writing to the trace_marker file are expected
  7533.  * to have a leading text marker followed by a ':'; e.g. the trace clock
  7534.  * synchronization event is:
  7535.  *
  7536.  *  tracing_mark_write: trace_event_clock_sync: parent_ts=0
  7537.  *
  7538.  * To register an event handler for these events, prepend the marker with
  7539.  * 'tracing_mark_write:'; e.g.
  7540.  *
  7541.  *    this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
  7542.  *
  7543.  * All subclasses should depend on linux_perf_parser, e.g.
  7544.  *
  7545.  * base.defineModule('linux_perf_workqueue_parser')
  7546.  *   .dependsOn('linux_perf_parser')
  7547.  *   .exportsTo('tracing', function()
  7548.  *
  7549.  * and be listed in the dependsOn of LinuxPerfImporter.  Beware that after
  7550.  * adding a new subclass you must run build/generate_about_tracing_contents.py
  7551.  * to regenerate about_tracing.*.
  7552.  */
  7553. base.exportTo('tracing', function() {
  7554.  
  7555.   var subtypeConstructors = [];
  7556.  
  7557.   /**
  7558.    * Registers a subclass that will help parse linux perf events.
  7559.    * The importer will call createParsers (below) before importing
  7560.    * data so each subclass can register its handlers.
  7561.    *
  7562.    * @param {Function} subtypeConstructor The subtype's constructor function.
  7563.    */
  7564.   LinuxPerfParser.registerSubtype = function(subtypeConstructor) {
  7565.     subtypeConstructors.push(subtypeConstructor);
  7566.   };
  7567.  
  7568.   LinuxPerfParser.getSubtypeConstructors = function() {
  7569.     return subtypeConstructors;
  7570.   };
  7571.  
  7572.   /**
  7573.    * Parses linux perf events.
  7574.    * @constructor
  7575.    */
  7576.   function LinuxPerfParser(importer) {
  7577.     this.importer = importer;
  7578.     this.model = importer.model;
  7579.   }
  7580.  
  7581.   LinuxPerfParser.prototype = {
  7582.     __proto__: Object.prototype
  7583.   };
  7584.  
  7585.   return {
  7586.     LinuxPerfParser: LinuxPerfParser
  7587.   };
  7588.  
  7589. });
  7590.  
  7591. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7592. // Use of this source code is governed by a BSD-style license that can be
  7593. // found in the LICENSE file.
  7594.  
  7595. /**
  7596.  * @fileoverview Parses trace_marker events that were inserted in the trace by
  7597.  * userland.
  7598.  */
  7599. base.require('linux_perf_parser');
  7600. base.exportTo('tracing', function() {
  7601.  
  7602.   var LinuxPerfParser = tracing.LinuxPerfParser;
  7603.  
  7604.   /**
  7605.    * Parses linux trace mark events that were inserted in the trace by userland.
  7606.    * @constructor
  7607.    */
  7608.   function LinuxPerfBusParser(importer) {
  7609.     LinuxPerfParser.call(this, importer);
  7610.  
  7611.     importer.registerEventHandler('memory_bus_usage',
  7612.         LinuxPerfBusParser.prototype.traceMarkWriteBusEvent.bind(this));
  7613.  
  7614.     this.model_ = importer.model_;
  7615.     this.ppids_ = {};
  7616.   }
  7617.  
  7618.   LinuxPerfBusParser.prototype = {
  7619.     __proto__: LinuxPerfParser.prototype,
  7620.  
  7621.     traceMarkWriteBusEvent: function(eventName, cpuNumber, pid, ts,
  7622.                                   eventBase, threadName) {
  7623.         var re = new RegExp('bus=(\\S+) rw_bytes=(\\d+) r_bytes=(\\d+) ' +
  7624.                             'w_bytes=(\\d+) cycles=(\\d+) ns=(\\d+)');
  7625.         var event = re.exec(eventBase[5]);
  7626.  
  7627.         var name = event[1];
  7628.         var rw_bytes = parseInt(event[2]);
  7629.         var r_bytes = parseInt(event[3]);
  7630.         var w_bytes = parseInt(event[4]);
  7631.         var cycles = parseInt(event[5]);
  7632.         var ns = parseInt(event[6]);
  7633.  
  7634.         // BW in MB/s
  7635.         var r_bw = r_bytes * 1000000000 / ns;
  7636.         r_bw /= 1024 * 1024;
  7637.         var w_bw = w_bytes * 1000000000 / ns;
  7638.         w_bw /= 1024 * 1024;
  7639.  
  7640.         var ctr = this.model_.getOrCreateProcess(0)
  7641.               .getOrCreateCounter(null, 'bus ' + name + ' read');
  7642.         // Initialize the counter's series fields if needed.
  7643.         if (ctr.numSeries == 0) {
  7644.             ctr.seriesNames.push('value');
  7645.             ctr.seriesColors.push(
  7646.                 tracing.getStringColorId(ctr.name + '.' + 'value'));
  7647.         }
  7648.  
  7649.         // Add the sample value.
  7650.         ctr.timestamps.push(ts);
  7651.         ctr.samples.push(r_bw);
  7652.  
  7653.         ctr = this.model_.getOrCreateProcess(0)
  7654.               .getOrCreateCounter(null, 'bus ' + name + ' write');
  7655.         // Initialize the counter's series fields if needed.
  7656.         if (ctr.numSeries == 0) {
  7657.             ctr.seriesNames.push('value');
  7658.             ctr.seriesColors.push(
  7659.                 tracing.getStringColorId(ctr.name + '.' + 'value'));
  7660.         }
  7661.  
  7662.         // Add the sample value.
  7663.         ctr.timestamps.push(ts);
  7664.         ctr.samples.push(w_bw);
  7665.  
  7666.         return true;
  7667.     },
  7668.   };
  7669.  
  7670.   LinuxPerfParser.registerSubtype(LinuxPerfBusParser);
  7671.  
  7672.   return {
  7673.     LinuxPerfBusParser: LinuxPerfBusParser
  7674.   };
  7675. });
  7676.  
  7677. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7678. // Use of this source code is governed by a BSD-style license that can be
  7679. // found in the LICENSE file.
  7680.  
  7681. /**
  7682.  * @fileoverview Parses trace_marker events that were inserted in the trace by
  7683.  * userland.
  7684.  */
  7685. base.require('linux_perf_parser');
  7686. base.exportTo('tracing', function() {
  7687.  
  7688.   var LinuxPerfParser = tracing.LinuxPerfParser;
  7689.  
  7690.   /**
  7691.    * Parses linux trace mark events that were inserted in the trace by userland.
  7692.    * @constructor
  7693.    */
  7694.   function LinuxPerfClockParser(importer) {
  7695.     LinuxPerfParser.call(this, importer);
  7696.  
  7697.     importer.registerEventHandler('clock_set_rate',
  7698.         LinuxPerfClockParser.prototype.traceMarkWriteClockEvent.bind(this));
  7699.  
  7700.     this.model_ = importer.model_;
  7701.     this.ppids_ = {};
  7702.   }
  7703.  
  7704.   LinuxPerfClockParser.prototype = {
  7705.     __proto__: LinuxPerfParser.prototype,
  7706.  
  7707.     traceMarkWriteClockEvent: function(eventName, cpuNumber, pid, ts,
  7708.                                   eventBase, threadName) {
  7709.         var event = /(\S+) state=(\d+) cpu_id=(\d+)/.exec(eventBase[5]);
  7710.  
  7711.  
  7712.         var name = event[1];
  7713.         var rate = parseInt(event[2]);
  7714.  
  7715.         var ctr = this.model_.getOrCreateProcess(0)
  7716.               .getOrCreateCounter(null, name);
  7717.         // Initialize the counter's series fields if needed.
  7718.         if (ctr.numSeries == 0) {
  7719.             ctr.seriesNames.push('value');
  7720.             ctr.seriesColors.push(
  7721.                 tracing.getStringColorId(ctr.name + '.' + 'value'));
  7722.         }
  7723.  
  7724.         // Add the sample value.
  7725.         ctr.timestamps.push(ts);
  7726.         ctr.samples.push(rate);
  7727.  
  7728.         return true;
  7729.     },
  7730.   };
  7731.  
  7732.   LinuxPerfParser.registerSubtype(LinuxPerfClockParser);
  7733.  
  7734.   return {
  7735.     LinuxPerfClockParser: LinuxPerfClockParser
  7736.   };
  7737. });
  7738.  
  7739. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7740. // Use of this source code is governed by a BSD-style license that can be
  7741. // found in the LICENSE file.
  7742.  
  7743. /**
  7744.  * @fileoverview Parses cpufreq events in the Linux event trace format.
  7745.  */
  7746. base.require('linux_perf_parser');
  7747. base.exportTo('tracing', function() {
  7748.  
  7749.   var LinuxPerfParser = tracing.LinuxPerfParser;
  7750.  
  7751.   /**
  7752.    * Parses linux cpufreq trace events.
  7753.    * @constructor
  7754.    */
  7755.   function LinuxPerfCpufreqParser(importer) {
  7756.     LinuxPerfParser.call(this, importer);
  7757.  
  7758.     importer.registerEventHandler('cpufreq_interactive_up',
  7759.         LinuxPerfCpufreqParser.prototype.cpufreqUpDownEvent.bind(this));
  7760.     importer.registerEventHandler('cpufreq_interactive_down',
  7761.         LinuxPerfCpufreqParser.prototype.cpufreqUpDownEvent.bind(this));
  7762.     importer.registerEventHandler('cpufreq_interactive_already',
  7763.         LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
  7764.     importer.registerEventHandler('cpufreq_interactive_notyet',
  7765.         LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
  7766.     importer.registerEventHandler('cpufreq_interactive_target',
  7767.         LinuxPerfCpufreqParser.prototype.cpufreqTargetEvent.bind(this));
  7768.     importer.registerEventHandler('cpufreq_interactive_boost',
  7769.         LinuxPerfCpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));
  7770.     importer.registerEventHandler('cpufreq_interactive_unboost',
  7771.         LinuxPerfCpufreqParser.prototype.cpufreqBoostUnboostEvent.bind(this));
  7772.   }
  7773.  
  7774.   LinuxPerfCpufreqParser.prototype = {
  7775.     __proto__: LinuxPerfParser.prototype,
  7776.  
  7777.     cpufreqSlice: function(ts, eventName, cpu, args) {
  7778.       // TODO(sleffler) should be per-cpu
  7779.       var kthread = this.importer.getOrCreatePseudoThread('cpufreq');
  7780.       kthread.openSlice = eventName;
  7781.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  7782.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  7783.  
  7784.       kthread.thread.pushSlice(slice);
  7785.     },
  7786.  
  7787.     cpufreqBoostSlice: function(ts, eventName, args) {
  7788.       var kthread = this.importer.getOrCreatePseudoThread('cpufreq_boost');
  7789.       kthread.openSlice = eventName;
  7790.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  7791.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  7792.  
  7793.       kthread.thread.pushSlice(slice);
  7794.     },
  7795.  
  7796.     /**
  7797.      * Parses cpufreq events and sets up state in the importer.
  7798.      */
  7799.     cpufreqUpDownEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  7800.       var event = /cpu=(\d+) targ=(\d+) actual=(\d+)/.exec(eventBase[5]);
  7801.       if (!event)
  7802.         return false;
  7803.  
  7804.       // TODO(sleffler) split by cpu
  7805.       var cpu = parseInt(event[1]);
  7806.       var targ = parseInt(event[2]);
  7807.       var actual = parseInt(event[3]);
  7808.       this.cpufreqSlice(ts, eventName, cpu,
  7809.           {
  7810.             cpu: cpu,
  7811.             targ: targ,
  7812.             actual: actual
  7813.           });
  7814.       return true;
  7815.     },
  7816.  
  7817.     cpufreqTargetEvent: function(eventName, cpuNumber, pid, ts,
  7818.                                  eventBase) {
  7819.       var event = /cpu=(\d+) load=(\d+) cur=(\d+) targ=(\d+)/
  7820.           .exec(eventBase[5]);
  7821.       if (!event)
  7822.         return false;
  7823.  
  7824.       // TODO(sleffler) split by cpu
  7825.       var cpu = parseInt(event[1]);
  7826.       var load = parseInt(event[2]);
  7827.       var cur = parseInt(event[3]);
  7828.       var targ = parseInt(event[4]);
  7829.       this.cpufreqSlice(ts, eventName, cpu,
  7830.           {
  7831.             cpu: cpu,
  7832.             load: load,
  7833.             cur: cur,
  7834.             targ: targ
  7835.           });
  7836.       return true;
  7837.     },
  7838.  
  7839.     cpufreqBoostUnboostEvent: function(eventName, cpuNumber, pid, ts,
  7840.                                        eventBase) {
  7841.       this.cpufreqBoostSlice(ts, eventName,
  7842.           {
  7843.             type: eventBase[5]
  7844.           });
  7845.       return true;
  7846.     }
  7847.   };
  7848.  
  7849.   LinuxPerfParser.registerSubtype(LinuxPerfCpufreqParser);
  7850.  
  7851.   return {
  7852.     LinuxPerfCpufreqParser: LinuxPerfCpufreqParser
  7853.   };
  7854. });
  7855.  
  7856. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7857. // Use of this source code is governed by a BSD-style license that can be
  7858. // found in the LICENSE file.
  7859.  
  7860. /**
  7861.  * @fileoverview Parses drm driver events in the Linux event trace format.
  7862.  */
  7863. base.require('linux_perf_parser');
  7864. base.exportTo('tracing', function() {
  7865.  
  7866.   var LinuxPerfParser = tracing.LinuxPerfParser;
  7867.  
  7868.   /**
  7869.    * Parses linux drm trace events.
  7870.    * @constructor
  7871.    */
  7872.   function LinuxPerfDrmParser(importer) {
  7873.     LinuxPerfParser.call(this, importer);
  7874.  
  7875.     importer.registerEventHandler('drm_vblank_event',
  7876.         LinuxPerfDrmParser.prototype.vblankEvent.bind(this));
  7877.   }
  7878.  
  7879.   LinuxPerfDrmParser.prototype = {
  7880.     __proto__: LinuxPerfParser.prototype,
  7881.  
  7882.     drmVblankSlice: function(ts, eventName, args) {
  7883.       var kthread = this.importer.getOrCreatePseudoThread('drm_vblank');
  7884.       kthread.openSlice = eventName;
  7885.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  7886.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  7887.  
  7888.       kthread.thread.pushSlice(slice);
  7889.     },
  7890.  
  7891.     /**
  7892.      * Parses drm driver events and sets up state in the importer.
  7893.      */
  7894.     vblankEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  7895.       var event = /crtc=(\d+), seq=(\d+)/.exec(eventBase[5]);
  7896.       if (!event)
  7897.         return false;
  7898.  
  7899.       var crtc = parseInt(event[1]);
  7900.       var seq = parseInt(event[2]);
  7901.       this.drmVblankSlice(ts, 'vblank:' + crtc,
  7902.           {
  7903.             crtc: crtc,
  7904.             seq: seq
  7905.           });
  7906.       return true;
  7907.     }
  7908.   };
  7909.  
  7910.   LinuxPerfParser.registerSubtype(LinuxPerfDrmParser);
  7911.  
  7912.   return {
  7913.     LinuxPerfDrmParser: LinuxPerfDrmParser
  7914.   };
  7915. });
  7916.  
  7917. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7918. // Use of this source code is governed by a BSD-style license that can be
  7919. // found in the LICENSE file.
  7920.  
  7921. /**
  7922.  * @fileoverview Parses exynos events in the Linux event trace format.
  7923.  */
  7924. base.require('linux_perf_parser');
  7925. base.exportTo('tracing', function() {
  7926.  
  7927.   var LinuxPerfParser = tracing.LinuxPerfParser;
  7928.  
  7929.   /**
  7930.    * Parses linux exynos trace events.
  7931.    * @constructor
  7932.    */
  7933.   function LinuxPerfExynosParser(importer) {
  7934.     LinuxPerfParser.call(this, importer);
  7935.  
  7936.     importer.registerEventHandler('exynos_flip_request',
  7937.         LinuxPerfExynosParser.prototype.flipEvent.bind(this));
  7938.     importer.registerEventHandler('exynos_flip_complete',
  7939.         LinuxPerfExynosParser.prototype.flipEvent.bind(this));
  7940.  
  7941.     importer.registerEventHandler('exynos_busfreq_target_int',
  7942.         LinuxPerfExynosParser.prototype.busfreqTargetIntEvent.bind(this));
  7943.     importer.registerEventHandler('exynos_busfreq_target_mif',
  7944.         LinuxPerfExynosParser.prototype.busfreqTargetMifEvent.bind(this));
  7945.   }
  7946.  
  7947.   LinuxPerfExynosParser.prototype = {
  7948.     __proto__: LinuxPerfParser.prototype,
  7949.  
  7950.     exynosFlipOpenSlice: function(ts, pipe) {
  7951.       // use pipe?
  7952.       var kthread = this.importer.getOrCreatePseudoThread('exynos_flip');
  7953.       kthread.openSliceTS = ts;
  7954.       kthread.openSlice = 'flip:' + pipe;
  7955.     },
  7956.  
  7957.     exynosFlipCloseSlice: function(ts, args) {
  7958.       var kthread = this.importer.getOrCreatePseudoThread('exynos_flip');
  7959.       if (kthread.openSlice) {
  7960.         var slice = new tracing.TimelineSlice('', kthread.openSlice,
  7961.             tracing.getStringColorId(kthread.openSlice),
  7962.             kthread.openSliceTS,
  7963.             args,
  7964.             ts - kthread.openSliceTS);
  7965.  
  7966.         kthread.thread.pushSlice(slice);
  7967.       }
  7968.       kthread.openSlice = undefined;
  7969.     },
  7970.  
  7971.     /**
  7972.      * Parses exynos events and sets up state in the importer.
  7973.      */
  7974.     flipEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  7975.       var event = /pipe=(\d+)/.exec(eventBase[5]);
  7976.       if (!event)
  7977.         return false;
  7978.  
  7979.       var pipe = parseInt(event[1]);
  7980.       if (eventName == 'exynos_flip_request')
  7981.         this.exynosFlipOpenSlice(ts, pipe);
  7982.       else
  7983.         this.exynosFlipCloseSlice(ts,
  7984.             {
  7985.               pipe: pipe
  7986.             });
  7987.       return true;
  7988.     },
  7989.  
  7990.     exynosBusfreqSample: function(name, ts, frequency) {
  7991.       var targetCpu = this.importer.getOrCreateCpuState(0);
  7992.       var counter = targetCpu.cpu.getOrCreateCounter('', name);
  7993.       if (counter.numSeries == 0) {
  7994.         counter.seriesNames.push('frequency');
  7995.         counter.seriesColors.push(
  7996.             tracing.getStringColorId(counter.name + '.' + 'frequency'));
  7997.       }
  7998.       counter.timestamps.push(ts);
  7999.       counter.samples.push(frequency);
  8000.     },
  8001.  
  8002.     /**
  8003.      * Parses exynos_busfreq_target_int events and sets up state.
  8004.      */
  8005.     busfreqTargetIntEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8006.       var event = /frequency=(\d+)/.exec(eventBase[5]);
  8007.       if (!event)
  8008.         return false;
  8009.  
  8010.       this.exynosBusfreqSample('INT Frequency', ts, parseInt(event[1]));
  8011.       return true;
  8012.     },
  8013.  
  8014.     /**
  8015.      * Parses exynos_busfreq_target_mif events and sets up state.
  8016.      */
  8017.     busfreqTargetMifEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8018.       var event = /frequency=(\d+)/.exec(eventBase[5]);
  8019.       if (!event)
  8020.         return false;
  8021.  
  8022.       this.exynosBusfreqSample('MIF Frequency', ts, parseInt(event[1]));
  8023.       return true;
  8024.     },
  8025.   };
  8026.  
  8027.   LinuxPerfParser.registerSubtype(LinuxPerfExynosParser);
  8028.  
  8029.   return {
  8030.     LinuxPerfExynosParser: LinuxPerfExynosParser
  8031.   };
  8032. });
  8033.  
  8034. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8035. // Use of this source code is governed by a BSD-style license that can be
  8036. // found in the LICENSE file.
  8037.  
  8038. /**
  8039.  * @fileoverview Parses gesture events in the Linux event trace format.
  8040.  */
  8041. base.require('linux_perf_parser');
  8042. base.exportTo('tracing', function() {
  8043.  
  8044.   var LinuxPerfParser = tracing.LinuxPerfParser;
  8045.  
  8046.   /**
  8047.    * Parses trace events generated by gesture library for touchpad.
  8048.    * @constructor
  8049.    */
  8050.   function LinuxPerfGestureParser(importer) {
  8051.     LinuxPerfParser.call(this, importer);
  8052.     importer.registerEventHandler('tracing_mark_write:log',
  8053.         LinuxPerfGestureParser.prototype.logEvent.bind(this));
  8054.     importer.registerEventHandler('tracing_mark_write:SyncInterpret',
  8055.         LinuxPerfGestureParser.prototype.syncEvent.bind(this));
  8056.     importer.registerEventHandler('tracing_mark_write:HandleTimer',
  8057.         LinuxPerfGestureParser.prototype.timerEvent.bind(this));
  8058.   }
  8059.  
  8060.   LinuxPerfGestureParser.prototype = {
  8061.     __proto__: LinuxPerfParser.prototype,
  8062.  
  8063.     /**
  8064.      * Parse events generate by gesture library.
  8065.      * gestureOpenSlice and gestureCloseSlice are two common
  8066.      * functions to store the begin time and end time for all
  8067.      * events in gesture library
  8068.      */
  8069.     gestureOpenSlice: function(title, ts, opt_args) {
  8070.       this.importer.getOrCreatePseudoThread('gesture').thread.beginSlice(
  8071.               'touchpad_gesture', title, ts, opt_args);
  8072.     },
  8073.  
  8074.     gestureCloseSlice: function(title, ts) {
  8075.       var thread = this.importer.getOrCreatePseudoThread('gesture').thread;
  8076.       if (thread.openSliceCount) {
  8077.         var slice = thread.openPartialSlices_[thread.openSliceCount - 1];
  8078.         if (slice.title != title) {
  8079.            this.importer.importError('Titles do not match. Title is ' +
  8080.                                      slice.title + ' in openSlice, and is ' +
  8081.                                      title + ' in endSlice');
  8082.         } else {
  8083.           thread.endSlice(ts);
  8084.         }
  8085.       }
  8086.     },
  8087.  
  8088.     /**
  8089.      * For log events, events will come in pairs with a tag log:
  8090.      * like this:
  8091.      * tracing_mark_write: log: start: TimerLogOutputs
  8092.      * tracing_mark_write: log: end: TimerLogOutputs
  8093.      * which represent the start and the end time of certain log behavior
  8094.      * Take these logs above for example, they are the start and end time
  8095.      * of logging Output for HandleTimer function
  8096.      */
  8097.     logEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8098.       var innerEvent =
  8099.           /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
  8100.       switch (innerEvent[1]) {
  8101.         case 'start':
  8102.           this.gestureOpenSlice('GestureLog', ts, {name: innerEvent[2]});
  8103.           break;
  8104.         case 'end':
  8105.           this.gestureCloseSlice('GestureLog', ts);
  8106.       }
  8107.       return true;
  8108.     },
  8109.  
  8110.     /**
  8111.      * For SyncInterpret events, events will come in pairs with
  8112.      * a tag SyncInterpret:
  8113.      * like this:
  8114.      * tracing_mark_write: SyncInterpret: start: ClickWiggleFilterInterpreter
  8115.      * tracing_mark_write: SyncInterpret: end: ClickWiggleFilterInterpreter
  8116.      * which represent the start and the end time of SyncInterpret function
  8117.      * inside the certain interpreter in the gesture library.
  8118.      * Take the logs above for example, they are the start and end time
  8119.      * of the SyncInterpret function inside ClickWiggleFilterInterpreter
  8120.      */
  8121.     syncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8122.       var innerEvent = /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
  8123.       switch (innerEvent[1]) {
  8124.         case 'start':
  8125.           this.gestureOpenSlice('SyncInterpret', ts,
  8126.                                 {interpreter: innerEvent[2]});
  8127.           break;
  8128.         case 'end':
  8129.           this.gestureCloseSlice('SyncInterpret', ts);
  8130.       }
  8131.       return true;
  8132.     },
  8133.  
  8134.     /**
  8135.      * For HandleTimer events, events will come in pairs with
  8136.      * a tag HandleTimer:
  8137.      * like this:
  8138.      * tracing_mark_write: HandleTimer: start: LookaheadFilterInterpreter
  8139.      * tracing_mark_write: HandleTimer: end: LookaheadFilterInterpreter
  8140.      * which represent the start and the end time of HandleTimer function
  8141.      * inside the certain interpreter in the gesture library.
  8142.      * Take the logs above for example, they are the start and end time
  8143.      * of the HandleTimer function inside LookaheadFilterInterpreter
  8144.      */
  8145.     timerEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8146.       var innerEvent = /^\s*(\w+):\s*(\w+)$/.exec(eventBase[2]);
  8147.       switch (innerEvent[1]) {
  8148.         case 'start':
  8149.           this.gestureOpenSlice('HandleTimer', ts,
  8150.                                 {interpreter: innerEvent[2]});
  8151.           break;
  8152.         case 'end':
  8153.           this.gestureCloseSlice('HandleTimer', ts);
  8154.       }
  8155.       return true;
  8156.     }
  8157.   };
  8158.  
  8159.   LinuxPerfParser.registerSubtype(LinuxPerfGestureParser);
  8160.  
  8161.   return {
  8162.     LinuxPerfGestureParser: LinuxPerfGestureParser
  8163.   };
  8164. });
  8165.  
  8166. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8167. // Use of this source code is governed by a BSD-style license that can be
  8168. // found in the LICENSE file.
  8169.  
  8170. /**
  8171.  * @fileoverview Parses i915 driver events in the Linux event trace format.
  8172.  */
  8173. base.require('linux_perf_parser');
  8174. base.exportTo('tracing', function() {
  8175.  
  8176.   var LinuxPerfParser = tracing.LinuxPerfParser;
  8177.  
  8178.   /**
  8179.    * Parses linux i915 trace events.
  8180.    * @constructor
  8181.    */
  8182.   function LinuxPerfI915Parser(importer) {
  8183.     LinuxPerfParser.call(this, importer);
  8184.  
  8185.     importer.registerEventHandler('i915_gem_object_create',
  8186.         LinuxPerfI915Parser.prototype.gemObjectCreateEvent.bind(this));
  8187.     importer.registerEventHandler('i915_gem_object_bind',
  8188.         LinuxPerfI915Parser.prototype.gemObjectBindEvent.bind(this));
  8189.     importer.registerEventHandler('i915_gem_object_unbind',
  8190.         LinuxPerfI915Parser.prototype.gemObjectBindEvent.bind(this));
  8191.     importer.registerEventHandler('i915_gem_object_change_domain',
  8192.         LinuxPerfI915Parser.prototype.gemObjectChangeDomainEvent.bind(this));
  8193.     importer.registerEventHandler('i915_gem_object_pread',
  8194.         LinuxPerfI915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
  8195.     importer.registerEventHandler('i915_gem_object_pwrite',
  8196.         LinuxPerfI915Parser.prototype.gemObjectPreadWriteEvent.bind(this));
  8197.     importer.registerEventHandler('i915_gem_object_fault',
  8198.         LinuxPerfI915Parser.prototype.gemObjectFaultEvent.bind(this));
  8199.     importer.registerEventHandler('i915_gem_object_clflush',
  8200.         // NB: reuse destroy handler
  8201.         LinuxPerfI915Parser.prototype.gemObjectDestroyEvent.bind(this));
  8202.     importer.registerEventHandler('i915_gem_object_destroy',
  8203.         LinuxPerfI915Parser.prototype.gemObjectDestroyEvent.bind(this));
  8204.     importer.registerEventHandler('i915_gem_ring_dispatch',
  8205.         LinuxPerfI915Parser.prototype.gemRingDispatchEvent.bind(this));
  8206.     importer.registerEventHandler('i915_gem_ring_flush',
  8207.         LinuxPerfI915Parser.prototype.gemRingFlushEvent.bind(this));
  8208.     importer.registerEventHandler('i915_gem_request',
  8209.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8210.     importer.registerEventHandler('i915_gem_request_add',
  8211.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8212.     importer.registerEventHandler('i915_gem_request_complete',
  8213.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8214.     importer.registerEventHandler('i915_gem_request_retire',
  8215.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8216.     importer.registerEventHandler('i915_gem_request_wait_begin',
  8217.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8218.     importer.registerEventHandler('i915_gem_request_wait_end',
  8219.         LinuxPerfI915Parser.prototype.gemRequestEvent.bind(this));
  8220.     importer.registerEventHandler('i915_gem_ring_wait_begin',
  8221.         LinuxPerfI915Parser.prototype.gemRingWaitEvent.bind(this));
  8222.     importer.registerEventHandler('i915_gem_ring_wait_end',
  8223.         LinuxPerfI915Parser.prototype.gemRingWaitEvent.bind(this));
  8224.     importer.registerEventHandler('i915_reg_rw',
  8225.         LinuxPerfI915Parser.prototype.regRWEvent.bind(this));
  8226.     importer.registerEventHandler('i915_flip_request',
  8227.         LinuxPerfI915Parser.prototype.flipEvent.bind(this));
  8228.     importer.registerEventHandler('i915_flip_complete',
  8229.         LinuxPerfI915Parser.prototype.flipEvent.bind(this));
  8230.   }
  8231.  
  8232.   LinuxPerfI915Parser.prototype = {
  8233.     __proto__: LinuxPerfParser.prototype,
  8234.  
  8235.     i915FlipOpenSlice: function(ts, obj, plane) {
  8236.       // use i915_flip_obj_plane?
  8237.       var kthread = this.importer.getOrCreatePseudoThread('i915_flip');
  8238.       kthread.openSliceTS = ts;
  8239.       kthread.openSlice = 'flip:' + obj + '/' + plane;
  8240.     },
  8241.  
  8242.     i915FlipCloseSlice: function(ts, args) {
  8243.       var kthread = this.importer.getOrCreatePseudoThread('i915_flip');
  8244.       if (kthread.openSlice) {
  8245.         var slice = new tracing.TimelineSlice('', kthread.openSlice,
  8246.             tracing.getStringColorId(kthread.openSlice),
  8247.             kthread.openSliceTS,
  8248.             args,
  8249.             ts - kthread.openSliceTS);
  8250.  
  8251.         kthread.thread.pushSlice(slice);
  8252.       }
  8253.       kthread.openSlice = undefined;
  8254.     },
  8255.  
  8256.     i915GemObjectSlice: function(ts, eventName, obj, args) {
  8257.       var kthread = this.importer.getOrCreatePseudoThread('i915_gem');
  8258.       kthread.openSlice = eventName + ':' + obj;
  8259.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  8260.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  8261.  
  8262.       kthread.thread.pushSlice(slice);
  8263.     },
  8264.  
  8265.     i915GemRingSlice: function(ts, eventName, dev, ring, args) {
  8266.       var kthread = this.importer.getOrCreatePseudoThread('i915_gem_ring');
  8267.       kthread.openSlice = eventName + ':' + dev + '.' + ring;
  8268.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  8269.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  8270.  
  8271.       kthread.thread.pushSlice(slice);
  8272.     },
  8273.  
  8274.     i915RegSlice: function(ts, eventName, reg, args) {
  8275.       var kthread = this.importer.getOrCreatePseudoThread('i915_reg');
  8276.       kthread.openSlice = eventName + ':' + reg;
  8277.       var slice = new tracing.TimelineSlice('', kthread.openSlice,
  8278.           tracing.getStringColorId(kthread.openSlice), ts, args, 0);
  8279.  
  8280.       kthread.thread.pushSlice(slice);
  8281.     },
  8282.  
  8283.     /**
  8284.      * Parses i915 driver events and sets up state in the importer.
  8285.      */
  8286.     gemObjectCreateEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8287.       var event = /obj=(\w+), size=(\d+)/.exec(eventBase[5]);
  8288.       if (!event)
  8289.         return false;
  8290.  
  8291.       var obj = event[1];
  8292.       var size = parseInt(event[2]);
  8293.       this.i915GemObjectSlice(ts, eventName, obj,
  8294.           {
  8295.             obj: obj,
  8296.             size: size
  8297.           });
  8298.       return true;
  8299.     },
  8300.  
  8301.     gemObjectBindEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8302.       // TODO(sleffler) mappable
  8303.       var event = /obj=(\w+), offset=(\w+), size=(\d+)/.exec(eventBase[5]);
  8304.       if (!event)
  8305.         return false;
  8306.  
  8307.       var obj = event[1];
  8308.       var offset = event[2];
  8309.       var size = parseInt(event[3]);
  8310.       this.i915ObjectGemSlice(ts, eventName + ':' + obj,
  8311.           {
  8312.             obj: obj,
  8313.             offset: offset,
  8314.             size: size
  8315.           });
  8316.       return true;
  8317.     },
  8318.  
  8319.     gemObjectChangeDomainEvent: function(eventName, cpuNumber, pid, ts,
  8320.                                          eventBase) {
  8321.       var event = /obj=(\w+), read=(\w+=>\w+), write=(\w+=>\w+)/
  8322.           .exec(eventBase[5]);
  8323.       if (!event)
  8324.         return false;
  8325.  
  8326.       var obj = event[1];
  8327.       var read = event[2];
  8328.       var write = event[3];
  8329.       this.i915GemObjectSlice(ts, eventName, obj,
  8330.           {
  8331.             obj: obj,
  8332.             read: read,
  8333.             write: write
  8334.           });
  8335.       return true;
  8336.     },
  8337.  
  8338.     gemObjectPreadWriteEvent: function(eventName, cpuNumber, pid, ts,
  8339.                                        eventBase) {
  8340.       var event = /obj=(\w+), offset=(\d+), len=(\d+)/.exec(eventBase[5]);
  8341.       if (!event)
  8342.         return false;
  8343.  
  8344.       var obj = event[1];
  8345.       var offset = parseInt(event[2]);
  8346.       var len = parseInt(event[3]);
  8347.       this.i915GemObjectSlice(ts, eventName, obj,
  8348.           {
  8349.             obj: obj,
  8350.             offset: offset,
  8351.             len: len
  8352.           });
  8353.       return true;
  8354.     },
  8355.  
  8356.     gemObjectFaultEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8357.       // TODO(sleffler) writable
  8358.       var event = /obj=(\w+), (\w+) index=(\d+)/.exec(eventBase[5]);
  8359.       if (!event)
  8360.         return false;
  8361.  
  8362.       var obj = event[1];
  8363.       var type = event[2];
  8364.       var index = parseInt(event[3]);
  8365.       this.i915GemObjectSlice(ts, eventName, obj,
  8366.           {
  8367.             obj: obj,
  8368.             type: type,
  8369.             index: index
  8370.           });
  8371.       return true;
  8372.     },
  8373.  
  8374.     gemObjectDestroyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8375.       var event = /obj=(\w+)/.exec(eventBase[5]);
  8376.       if (!event)
  8377.         return false;
  8378.  
  8379.       var obj = event[1];
  8380.       this.i915GemObjectSlice(ts, eventName, obj,
  8381.           {
  8382.             obj: obj
  8383.           });
  8384.       return true;
  8385.     },
  8386.  
  8387.     gemRingDispatchEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8388.       var event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase[5]);
  8389.       if (!event)
  8390.         return false;
  8391.  
  8392.       var dev = parseInt(event[1]);
  8393.       var ring = parseInt(event[2]);
  8394.       var seqno = parseInt(event[3]);
  8395.       this.i915GemRingSlice(ts, eventName, dev, ring,
  8396.           {
  8397.             dev: dev,
  8398.             ring: ring,
  8399.             seqno: seqno
  8400.           });
  8401.       return true;
  8402.     },
  8403.  
  8404.     gemRingFlushEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8405.       var event = /dev=(\d+), ring=(\w+), invalidate=(\w+), flush=(\w+)/
  8406.           .exec(eventBase[5]);
  8407.       if (!event)
  8408.         return false;
  8409.  
  8410.       var dev = parseInt(event[1]);
  8411.       var ring = parseInt(event[2]);
  8412.       var invalidate = event[3];
  8413.       var flush = event[4];
  8414.       this.i915GemRingSlice(ts, eventName, dev, ring,
  8415.           {
  8416.             dev: dev,
  8417.             ring: ring,
  8418.             invalidate: invalidate,
  8419.             flush: flush
  8420.           });
  8421.       return true;
  8422.     },
  8423.  
  8424.     gemRequestEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8425.       var event = /dev=(\d+), ring=(\d+), seqno=(\d+)/.exec(eventBase[5]);
  8426.       if (!event)
  8427.         return false;
  8428.  
  8429.       var dev = parseInt(event[1]);
  8430.       var ring = parseInt(event[2]);
  8431.       var seqno = parseInt(event[3]);
  8432.       this.i915GemRingSlice(ts, eventName, dev, ring,
  8433.           {
  8434.             dev: dev,
  8435.             ring: ring,
  8436.             seqno: seqno
  8437.           });
  8438.       return true;
  8439.     },
  8440.  
  8441.     gemRingWaitEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8442.       var event = /dev=(\d+), ring=(\d+)/.exec(eventBase[5]);
  8443.       if (!event)
  8444.         return false;
  8445.  
  8446.       var dev = parseInt(event[1]);
  8447.       var ring = parseInt(event[2]);
  8448.       this.i915GemRingSlice(ts, eventName, dev, ring,
  8449.           {
  8450.             dev: dev,
  8451.             ring: ring
  8452.           });
  8453.       return true;
  8454.     },
  8455.  
  8456.     regRWEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8457.       var event = /(\w+) reg=(\w+), len=(\d+), val=(\(\w+, \w+\))/
  8458.           .exec(eventBase[5]);
  8459.       if (!event)
  8460.         return false;
  8461.  
  8462.       var rw = event[1];
  8463.       var reg = event[2];
  8464.       var len = event[3];
  8465.       var data = event[3];
  8466.       this.i915RegSlice(ts, rw, reg,
  8467.           {
  8468.             rw: rw,
  8469.             reg: reg,
  8470.             len: len,
  8471.             data: data
  8472.           });
  8473.       return true;
  8474.     },
  8475.  
  8476.     flipEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8477.       var event = /plane=(\d+), obj=(\w+)/.exec(eventBase[5]);
  8478.       if (!event)
  8479.         return false;
  8480.  
  8481.       var plane = parseInt(event[1]);
  8482.       var obj = event[2];
  8483.       if (eventName == 'i915_flip_request')
  8484.         this.i915FlipOpenSlice(ts, obj, plane);
  8485.       else
  8486.         this.i915FlipCloseSlice(ts,
  8487.             {
  8488.               obj: obj,
  8489.               plane: plane
  8490.             });
  8491.       return true;
  8492.     }
  8493.   };
  8494.  
  8495.   LinuxPerfParser.registerSubtype(LinuxPerfI915Parser);
  8496.  
  8497.   return {
  8498.     LinuxPerfI915Parser: LinuxPerfI915Parser
  8499.   };
  8500. });
  8501.  
  8502. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  8503. // Use of this source code is governed by a BSD-style license that can be
  8504. // found in the LICENSE file.
  8505.  
  8506. /**
  8507.  * @fileoverview Parses Mali DDK/kernel events in the Linux event trace format.
  8508.  */
  8509. base.require('linux_perf_parser');
  8510. base.exportTo('tracing', function() {
  8511.  
  8512.   var LinuxPerfParser = tracing.LinuxPerfParser;
  8513.  
  8514.   /**
  8515.    * Parses Mali DDK/kernel trace events.
  8516.    * @constructor
  8517.    */
  8518.   function LinuxPerfMaliParser(importer) {
  8519.     LinuxPerfParser.call(this, importer);
  8520.  
  8521.     // kernel DVFS events
  8522.     importer.registerEventHandler('mali_dvfs_event',
  8523.         LinuxPerfMaliParser.prototype.dvfsEventEvent.bind(this));
  8524.     importer.registerEventHandler('mali_dvfs_set_clock',
  8525.         LinuxPerfMaliParser.prototype.dvfsSetClockEvent.bind(this));
  8526.     importer.registerEventHandler('mali_dvfs_set_voltage',
  8527.         LinuxPerfMaliParser.prototype.dvfsSetVoltageEvent.bind(this));
  8528.  
  8529.     // kernel Mali hw counter events
  8530.     this.addJMCounter('mali_hwc_MESSAGES_SENT', 'Messages Sent');
  8531.     this.addJMCounter('mali_hwc_MESSAGES_RECEIVED', 'Messages Received');
  8532.     this.addJMCycles('mali_hwc_GPU_ACTIVE', 'GPU Active');
  8533.     this.addJMCycles('mali_hwc_IRQ_ACTIVE', 'IRQ Active');
  8534.  
  8535.     for (var i = 0; i < 7; i++) {
  8536.       var jobStr = 'JS' + i;
  8537.       var jobHWCStr = 'mali_hwc_' + jobStr;
  8538.       this.addJMCounter(jobHWCStr + '_JOBS', jobStr + ' Jobs');
  8539.       this.addJMCounter(jobHWCStr + '_TASKS', jobStr + ' Tasks');
  8540.       this.addJMCycles(jobHWCStr + '_ACTIVE', jobStr + ' Active');
  8541.       this.addJMCycles(jobHWCStr + '_WAIT_READ', jobStr + ' Wait Read');
  8542.       this.addJMCycles(jobHWCStr + '_WAIT_ISSUE', jobStr + ' Wait Issue');
  8543.       this.addJMCycles(jobHWCStr + '_WAIT_DEPEND', jobStr + ' Wait Depend');
  8544.       this.addJMCycles(jobHWCStr + '_WAIT_FINISH', jobStr + ' Wait Finish');
  8545.     }
  8546.  
  8547.     this.addTilerCounter('mali_hwc_TRIANGLES', 'Triangles');
  8548.     this.addTilerCounter('mali_hwc_QUADS', 'Quads');
  8549.     this.addTilerCounter('mali_hwc_POLYGONS', 'Polygons');
  8550.     this.addTilerCounter('mali_hwc_POINTS', 'Points');
  8551.     this.addTilerCounter('mali_hwc_LINES', 'Lines');
  8552.     this.addTilerCounter('mali_hwc_VCACHE_HIT', 'VCache Hit');
  8553.     this.addTilerCounter('mali_hwc_VCACHE_MISS', 'VCache Miss');
  8554.     this.addTilerCounter('mali_hwc_FRONT_FACING', 'Front Facing');
  8555.     this.addTilerCounter('mali_hwc_BACK_FACING', 'Back Facing');
  8556.     this.addTilerCounter('mali_hwc_PRIM_VISIBLE', 'Prim Visible');
  8557.     this.addTilerCounter('mali_hwc_PRIM_CULLED', 'Prim Culled');
  8558.     this.addTilerCounter('mali_hwc_PRIM_CLIPPED', 'Prim Clipped');
  8559.  
  8560.     this.addTilerCounter('mali_hwc_WRBUF_HIT', 'Wrbuf Hit');
  8561.     this.addTilerCounter('mali_hwc_WRBUF_MISS', 'Wrbuf Miss');
  8562.     this.addTilerCounter('mali_hwc_WRBUF_LINE', 'Wrbuf Line');
  8563.     this.addTilerCounter('mali_hwc_WRBUF_PARTIAL', 'Wrbuf Partial');
  8564.     this.addTilerCounter('mali_hwc_WRBUF_STALL', 'Wrbuf Stall');
  8565.  
  8566.     this.addTilerCycles('mali_hwc_ACTIVE', 'Tiler Active');
  8567.     this.addTilerCycles('mali_hwc_INDEX_WAIT', 'Index Wait');
  8568.     this.addTilerCycles('mali_hwc_INDEX_RANGE_WAIT', 'Index Range Wait');
  8569.     this.addTilerCycles('mali_hwc_VERTEX_WAIT', 'Vertex Wait');
  8570.     this.addTilerCycles('mali_hwc_PCACHE_WAIT', 'Pcache Wait');
  8571.     this.addTilerCycles('mali_hwc_WRBUF_WAIT', 'Wrbuf Wait');
  8572.     this.addTilerCycles('mali_hwc_BUS_READ', 'Bus Read');
  8573.     this.addTilerCycles('mali_hwc_BUS_WRITE', 'Bus Write');
  8574.  
  8575.     this.addTilerCycles('mali_hwc_TILER_UTLB_STALL', 'Tiler UTLB Stall');
  8576.     this.addTilerCycles('mali_hwc_TILER_UTLB_HIT', 'Tiler UTLB Hit');
  8577.  
  8578.     this.addFragCycles('mali_hwc_FRAG_ACTIVE', 'Active');
  8579.     /* NB: don't propagate spelling mistakes to labels */
  8580.     this.addFragCounter('mali_hwc_FRAG_PRIMATIVES', 'Primitives');
  8581.     this.addFragCounter('mali_hwc_FRAG_PRIMATIVES_DROPPED',
  8582.         'Primitives Dropped');
  8583.     this.addFragCycles('mali_hwc_FRAG_CYCLE_DESC', 'Descriptor Processing');
  8584.     this.addFragCycles('mali_hwc_FRAG_CYCLES_PLR', 'PLR Processing??');
  8585.     this.addFragCycles('mali_hwc_FRAG_CYCLES_VERT', 'Vertex Processing');
  8586.     this.addFragCycles('mali_hwc_FRAG_CYCLES_TRISETUP', 'Triangle Setup');
  8587.     this.addFragCycles('mali_hwc_FRAG_CYCLES_RAST', 'Rasterization???');
  8588.     this.addFragCounter('mali_hwc_FRAG_THREADS', 'Threads');
  8589.     this.addFragCounter('mali_hwc_FRAG_DUMMY_THREADS', 'Dummy Threads');
  8590.     this.addFragCounter('mali_hwc_FRAG_QUADS_RAST', 'Quads Rast');
  8591.     this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_TEST', 'Quads EZS Test');
  8592.     this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_KILLED', 'Quads EZS Killed');
  8593.     this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_TEST', 'Quads LZS Test');
  8594.     this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_KILLED', 'Quads LZS Killed');
  8595.     this.addFragCycles('mali_hwc_FRAG_CYCLE_NO_TILE', 'No Tiles');
  8596.     this.addFragCounter('mali_hwc_FRAG_NUM_TILES', 'Tiles');
  8597.     this.addFragCounter('mali_hwc_FRAG_TRANS_ELIM', 'Transactions Eliminated');
  8598.  
  8599.     this.addComputeCycles('mali_hwc_COMPUTE_ACTIVE', 'Active');
  8600.     this.addComputeCounter('mali_hwc_COMPUTE_TASKS', 'Tasks');
  8601.     this.addComputeCounter('mali_hwc_COMPUTE_THREADS', 'Threads Started');
  8602.     this.addComputeCycles('mali_hwc_COMPUTE_CYCLES_DESC',
  8603.         'Waiting for Descriptors');
  8604.  
  8605.     this.addTripipeCycles('mali_hwc_TRIPIPE_ACTIVE', 'Active');
  8606.  
  8607.     this.addArithCounter('mali_hwc_ARITH_WORDS', 'Instructions (/Pipes)');
  8608.     this.addArithCycles('mali_hwc_ARITH_CYCLES_REG',
  8609.         'Reg scheduling stalls (/Pipes)');
  8610.     this.addArithCycles('mali_hwc_ARITH_CYCLES_L0',
  8611.         'L0 cache miss stalls (/Pipes)');
  8612.     this.addArithCounter('mali_hwc_ARITH_FRAG_DEPEND',
  8613.         'Frag dep check failures (/Pipes)');
  8614.  
  8615.     this.addLSCounter('mali_hwc_LS_WORDS', 'Instruction Words Completed');
  8616.     this.addLSCounter('mali_hwc_LS_ISSUES', 'Full Pipeline Issues');
  8617.     this.addLSCounter('mali_hwc_LS_RESTARTS', 'Restarts (unpairable insts)');
  8618.     this.addLSCounter('mali_hwc_LS_REISSUES_MISS',
  8619.         'Pipeline reissue (cache miss/uTLB)');
  8620.     this.addLSCounter('mali_hwc_LS_REISSUES_VD',
  8621.         'Pipeline reissue (varying data)');
  8622.     /* TODO(sleffler) fix kernel event typo */
  8623.     this.addLSCounter('mali_hwc_LS_REISSUE_ATTRIB_MISS',
  8624.         'Pipeline reissue (attribute cache miss)');
  8625.     this.addLSCounter('mali_hwc_LS_REISSUE_NO_WB', 'Writeback not used');
  8626.  
  8627.     this.addTexCounter('mali_hwc_TEX_WORDS', 'Words');
  8628.     this.addTexCounter('mali_hwc_TEX_BUBBLES', 'Bubbles');
  8629.     this.addTexCounter('mali_hwc_TEX_WORDS_L0', 'Words L0');
  8630.     this.addTexCounter('mali_hwc_TEX_WORDS_DESC', 'Words Desc');
  8631.     this.addTexCounter('mali_hwc_TEX_THREADS', 'Threads');
  8632.     this.addTexCounter('mali_hwc_TEX_RECIRC_FMISS', 'Recirc due to Full Miss');
  8633.     this.addTexCounter('mali_hwc_TEX_RECIRC_DESC', 'Recirc due to Desc Miss');
  8634.     this.addTexCounter('mali_hwc_TEX_RECIRC_MULTI', 'Recirc due to Multipass');
  8635.     this.addTexCounter('mali_hwc_TEX_RECIRC_PMISS',
  8636.         'Recirc due to Partial Cache Miss');
  8637.     this.addTexCounter('mali_hwc_TEX_RECIRC_CONF',
  8638.         'Recirc due to Cache Conflict');
  8639.  
  8640.     this.addLSCCounter('mali_hwc_LSC_READ_HITS', 'Read Hits');
  8641.     this.addLSCCounter('mali_hwc_LSC_READ_MISSES', 'Read Misses');
  8642.     this.addLSCCounter('mali_hwc_LSC_WRITE_HITS', 'Write Hits');
  8643.     this.addLSCCounter('mali_hwc_LSC_WRITE_MISSES', 'Write Misses');
  8644.     this.addLSCCounter('mali_hwc_LSC_ATOMIC_HITS', 'Atomic Hits');
  8645.     this.addLSCCounter('mali_hwc_LSC_ATOMIC_MISSES', 'Atomic Misses');
  8646.     this.addLSCCounter('mali_hwc_LSC_LINE_FETCHES', 'Line Fetches');
  8647.     this.addLSCCounter('mali_hwc_LSC_DIRTY_LINE', 'Dirty Lines');
  8648.     this.addLSCCounter('mali_hwc_LSC_SNOOPS', 'Snoops');
  8649.  
  8650.     this.addAXICounter('mali_hwc_AXI_TLB_STALL', 'Address channel stall');
  8651.     this.addAXICounter('mali_hwc_AXI_TLB_MISS', 'Cache Miss');
  8652.     this.addAXICounter('mali_hwc_AXI_TLB_TRANSACTION', 'Transactions');
  8653.     this.addAXICounter('mali_hwc_LS_TLB_MISS', 'LS Cache Miss');
  8654.     this.addAXICounter('mali_hwc_LS_TLB_HIT', 'LS Cache Hit');
  8655.     this.addAXICounter('mali_hwc_AXI_BEATS_READ', 'Read Beats');
  8656.     this.addAXICounter('mali_hwc_AXI_BEATS_WRITE', 'Write Beats');
  8657.  
  8658.     this.addMMUCounter('mali_hwc_MMU_TABLE_WALK', 'Page Table Walks');
  8659.     this.addMMUCounter('mali_hwc_MMU_REPLAY_MISS',
  8660.         'Cache Miss from Replay Buffer');
  8661.     this.addMMUCounter('mali_hwc_MMU_REPLAY_FULL', 'Replay Buffer Full');
  8662.     this.addMMUCounter('mali_hwc_MMU_NEW_MISS', 'Cache Miss on New Request');
  8663.     this.addMMUCounter('mali_hwc_MMU_HIT', 'Cache Hit');
  8664.  
  8665.     this.addMMUCycles('mali_hwc_UTLB_STALL', 'UTLB Stalled');
  8666.     this.addMMUCycles('mali_hwc_UTLB_REPLAY_MISS', 'UTLB Replay Miss');
  8667.     this.addMMUCycles('mali_hwc_UTLB_REPLAY_FULL', 'UTLB Replay Full');
  8668.     this.addMMUCycles('mali_hwc_UTLB_NEW_MISS', 'UTLB New Miss');
  8669.     this.addMMUCycles('mali_hwc_UTLB_HIT', 'UTLB Hit');
  8670.  
  8671.     this.addL2Counter('mali_hwc_L2_READ_BEATS', 'Read Beats');
  8672.     this.addL2Counter('mali_hwc_L2_WRITE_BEATS', 'Write Beats');
  8673.     this.addL2Counter('mali_hwc_L2_ANY_LOOKUP', 'Any Lookup');
  8674.     this.addL2Counter('mali_hwc_L2_READ_LOOKUP', 'Read Lookup');
  8675.     this.addL2Counter('mali_hwc_L2_SREAD_LOOKUP', 'Shareable Read Lookup');
  8676.     this.addL2Counter('mali_hwc_L2_READ_REPLAY', 'Read Replayed');
  8677.     this.addL2Counter('mali_hwc_L2_READ_SNOOP', 'Read Snoop');
  8678.     this.addL2Counter('mali_hwc_L2_READ_HIT', 'Read Cache Hit');
  8679.     this.addL2Counter('mali_hwc_L2_CLEAN_MISS', 'CleanUnique Miss');
  8680.     this.addL2Counter('mali_hwc_L2_WRITE_LOOKUP', 'Write Lookup');
  8681.     this.addL2Counter('mali_hwc_L2_SWRITE_LOOKUP', 'Shareable Write Lookup');
  8682.     this.addL2Counter('mali_hwc_L2_WRITE_REPLAY', 'Write Replayed');
  8683.     this.addL2Counter('mali_hwc_L2_WRITE_SNOOP', 'Write Snoop');
  8684.     this.addL2Counter('mali_hwc_L2_WRITE_HIT', 'Write Cache Hit');
  8685.     this.addL2Counter('mali_hwc_L2_EXT_READ_FULL', 'ExtRD with BIU Full');
  8686.     this.addL2Counter('mali_hwc_L2_EXT_READ_HALF', 'ExtRD with BIU >1/2 Full');
  8687.     this.addL2Counter('mali_hwc_L2_EXT_WRITE_FULL', 'ExtWR with BIU Full');
  8688.     this.addL2Counter('mali_hwc_L2_EXT_WRITE_HALF', 'ExtWR with BIU >1/2 Full');
  8689.  
  8690.     this.addL2Counter('mali_hwc_L2_EXT_READ', 'External Read (ExtRD)');
  8691.     this.addL2Counter('mali_hwc_L2_EXT_READ_LINE', 'ExtRD (linefill)');
  8692.     this.addL2Counter('mali_hwc_L2_EXT_WRITE', 'External Write (ExtWR)');
  8693.     this.addL2Counter('mali_hwc_L2_EXT_WRITE_LINE', 'ExtWR (linefill)');
  8694.     this.addL2Counter('mali_hwc_L2_EXT_WRITE_SMALL', 'ExtWR (burst size <64B)');
  8695.     this.addL2Counter('mali_hwc_L2_EXT_BARRIER', 'External Barrier');
  8696.     this.addL2Counter('mali_hwc_L2_EXT_AR_STALL', 'Address Read stalls');
  8697.     this.addL2Counter('mali_hwc_L2_EXT_R_BUF_FULL',
  8698.         'Response Buffer full stalls');
  8699.     this.addL2Counter('mali_hwc_L2_EXT_RD_BUF_FULL',
  8700.         'Read Data Buffer full stalls');
  8701.     this.addL2Counter('mali_hwc_L2_EXT_R_RAW', 'RAW hazard stalls');
  8702.     this.addL2Counter('mali_hwc_L2_EXT_W_STALL', 'Write Data stalls');
  8703.     this.addL2Counter('mali_hwc_L2_EXT_W_BUF_FULL', 'Write Data Buffer full');
  8704.     this.addL2Counter('mali_hwc_L2_EXT_R_W_HAZARD', 'WAW or WAR hazard stalls');
  8705.     this.addL2Counter('mali_hwc_L2_TAG_HAZARD', 'Tag hazard replays');
  8706.     this.addL2Cycles('mali_hwc_L2_SNOOP_FULL', 'Snoop buffer full');
  8707.     this.addL2Cycles('mali_hwc_L2_REPLAY_FULL', 'Replay buffer full');
  8708.  
  8709.     // DDK events (from X server)
  8710.     importer.registerEventHandler('tracing_mark_write:mali_driver',
  8711.         LinuxPerfMaliParser.prototype.maliDDKEvent.bind(this));
  8712.  
  8713.     this.model_ = importer.model_;
  8714.   }
  8715.  
  8716.   LinuxPerfMaliParser.prototype = {
  8717.     __proto__: LinuxPerfParser.prototype,
  8718.  
  8719.     maliDDKOpenSlice: function(pid, tid, ts, func, blockinfo) {
  8720.       var thread = this.importer.model_.getOrCreateProcess(pid)
  8721.         .getOrCreateThread(tid);
  8722.       var funcArgs = /^([\w\d_]*)(?:\(\))?:?\s*(.*)$/.exec(func);
  8723.       thread.beginSlice('gpu-driver', funcArgs[1], ts,
  8724.           { 'args': funcArgs[2],
  8725.             'blockinfo': blockinfo });
  8726.     },
  8727.  
  8728.     maliDDKCloseSlice: function(pid, tid, ts, args, blockinfo) {
  8729.       var thread = this.importer.model_.getOrCreateProcess(pid)
  8730.         .getOrCreateThread(tid);
  8731.       if (!thread.openSliceCount) {
  8732.         // Discard unmatched ends.
  8733.         return;
  8734.       }
  8735.       thread.endSlice(ts);
  8736.     },
  8737.  
  8738.     /**
  8739.      * Deduce the format of Mali perf events.
  8740.      *
  8741.      * @return {RegExp} the regular expression for parsing data when the format
  8742.      * is recognized; otherwise null.
  8743.      */
  8744.     autoDetectLineRE: function(line) {
  8745.       // Matches Mali perf events with thread info
  8746.       var lineREWithThread =
  8747.         /^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;
  8748.       if (lineREWithThread.test(line))
  8749.         return lineREWithThread;
  8750.  
  8751.       // Matches old-style Mali perf events
  8752.       var lineRENoThread = /^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;
  8753.       if (lineRENoThread.test(line))
  8754.         return lineRENoThread;
  8755.       return null;
  8756.     },
  8757.  
  8758.     lineRE: null,
  8759.  
  8760.     /**
  8761.      * Parses maliDDK events and sets up state in the importer.
  8762.      * events will come in pairs with a cros_trace_print_enter
  8763.      * like this (line broken here for formatting):
  8764.      *
  8765.      * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_enter: \
  8766.      *   gles/src/texture/mali_gles_texture_slave.c@1505: gles2_texturep_upload
  8767.      *
  8768.      * and a cros_trace_print_exit like this:
  8769.      *
  8770.      * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_exit: \
  8771.      *   gles/src/texture/mali_gles_texture_slave.c@1505:
  8772.      */
  8773.     maliDDKEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8774.       if (this.lineRE == null) {
  8775.         this.lineRE = this.autoDetectLineRE(eventBase[2]);
  8776.         if (this.lineRE == null)
  8777.           return false;
  8778.       }
  8779.       var maliEvent = this.lineRE.exec(eventBase[2]);
  8780.       // Old-style Mali perf events have no thread id, so make one.
  8781.       var tid = (maliEvent[1] === '' ? 'mali' : maliEvent[1]);
  8782.       switch (maliEvent[2]) {
  8783.         case 'cros_trace_print_enter':
  8784.           this.maliDDKOpenSlice(pid, tid, ts, maliEvent[4],
  8785.               maliEvent[3]);
  8786.           break;
  8787.         case 'cros_trace_print_exit':
  8788.           this.maliDDKCloseSlice(pid, tid, ts, [], maliEvent[3]);
  8789.       }
  8790.       return true;
  8791.     },
  8792.  
  8793.     /*
  8794.      * Kernel event support.
  8795.      */
  8796.  
  8797.     dvfsSample: function(counterName, seriesName, ts, s) {
  8798.       var value = parseInt(s);
  8799.       var counter = this.model_.getOrCreateProcess(0).
  8800.           getOrCreateCounter('DVFS', counterName);
  8801.       if (counter.numSeries == 0) {
  8802.         counter.seriesNames.push(seriesName);
  8803.         counter.seriesColors.push(tracing.getStringColorId(counter.name));
  8804.       }
  8805.       counter.timestamps.push(ts);
  8806.       counter.samples.push(value);
  8807.     },
  8808.  
  8809.     dvfsEventEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8810.       var event = /utilization=(\d+)/.exec(eventBase[5]);
  8811.       if (!event)
  8812.         return false;
  8813.  
  8814.       this.dvfsSample('DVFS Utilization', 'utilization', ts, event[1]);
  8815.       return true;
  8816.     },
  8817.  
  8818.     dvfsSetClockEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8819.       var event = /frequency=(\d+)/.exec(eventBase[5]);
  8820.       if (!event)
  8821.         return false;
  8822.  
  8823.       this.dvfsSample('DVFS Frequency', 'frequency', ts, event[1]);
  8824.       return true;
  8825.     },
  8826.  
  8827.     dvfsSetVoltageEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  8828.       var event = /voltage=(\d+)/.exec(eventBase[5]);
  8829.       if (!event)
  8830.         return false;
  8831.  
  8832.       this.dvfsSample('DVFS Voltage', 'voltage', ts, event[1]);
  8833.       return true;
  8834.     },
  8835.  
  8836.     hwcSample: function(cat, counterName, seriesName, ts, eventBase) {
  8837.       var event = /val=(\d+)/.exec(eventBase[5]);
  8838.       if (!event)
  8839.         return false;
  8840.       var value = parseInt(event[1]);
  8841.  
  8842.       var counter = this.model_.getOrCreateProcess(0).
  8843.           getOrCreateCounter(cat, counterName);
  8844.       if (counter.numSeries == 0) {
  8845.         counter.seriesNames.push(seriesName);
  8846.         counter.seriesColors.push(tracing.getStringColorId(counter.name));
  8847.       }
  8848.       counter.timestamps.push(ts);
  8849.       counter.samples.push(value);
  8850.       return true;
  8851.     },
  8852.  
  8853.     /*
  8854.      * Job Manager block counters.
  8855.      */
  8856.     jmSample: function(ctrName, seriesName, ts, eventBase) {
  8857.       return this.hwcSample('mali:jm', 'JM: ' + ctrName, seriesName, ts,
  8858.           eventBase);
  8859.     },
  8860.     addJMCounter: function(hwcEventName, hwcTitle) {
  8861.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8862.         return this.jmSample(hwcTitle, 'count', ts, eventBase);
  8863.       }
  8864.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8865.     },
  8866.     addJMCycles: function(hwcEventName, hwcTitle) {
  8867.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8868.         return this.jmSample(hwcTitle, 'cycles', ts, eventBase);
  8869.       }
  8870.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8871.     },
  8872.  
  8873.     /*
  8874.      * Tiler block counters.
  8875.      */
  8876.     tilerSample: function(ctrName, seriesName, ts, eventBase) {
  8877.       return this.hwcSample('mali:tiler', 'Tiler: ' + ctrName, seriesName,
  8878.           ts, eventBase);
  8879.     },
  8880.     addTilerCounter: function(hwcEventName, hwcTitle) {
  8881.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8882.         return this.tilerSample(hwcTitle, 'count', ts, eventBase);
  8883.       }
  8884.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8885.     },
  8886.     addTilerCycles: function(hwcEventName, hwcTitle) {
  8887.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8888.         return this.tilerSample(hwcTitle, 'cycles', ts, eventBase);
  8889.       }
  8890.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8891.     },
  8892.  
  8893.     /*
  8894.      * Fragment counters.
  8895.      */
  8896.     fragSample: function(ctrName, seriesName, ts, eventBase) {
  8897.       return this.hwcSample('mali:fragment', 'Fragment: ' + ctrName,
  8898.           seriesName, ts, eventBase);
  8899.     },
  8900.     addFragCounter: function(hwcEventName, hwcTitle) {
  8901.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8902.         return this.fragSample(hwcTitle, 'count', ts, eventBase);
  8903.       }
  8904.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8905.     },
  8906.     addFragCycles: function(hwcEventName, hwcTitle) {
  8907.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8908.         return this.fragSample(hwcTitle, 'cycles', ts, eventBase);
  8909.       }
  8910.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8911.     },
  8912.  
  8913.     /*
  8914.      * Compute counters.
  8915.      */
  8916.     computeSample: function(ctrName, seriesName, ts, eventBase) {
  8917.       return this.hwcSample('mali:compute', 'Compute: ' + ctrName,
  8918.           seriesName, ts, eventBase);
  8919.     },
  8920.     addComputeCounter: function(hwcEventName, hwcTitle) {
  8921.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8922.         return this.computeSample(hwcTitle, 'count', ts, eventBase);
  8923.       }
  8924.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8925.     },
  8926.     addComputeCycles: function(hwcEventName, hwcTitle) {
  8927.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8928.         return this.computeSample(hwcTitle, 'cycles', ts, eventBase);
  8929.       }
  8930.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8931.     },
  8932.  
  8933.     /*
  8934.      * Tripipe counters.
  8935.      */
  8936.     addTripipeCycles: function(hwcEventName, hwcTitle) {
  8937.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8938.         return this.hwcSample('mali:shader', 'Tripipe: ' + hwcTitle, 'cycles',
  8939.             ts, eventBase);
  8940.       }
  8941.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8942.     },
  8943.  
  8944.     /*
  8945.      * Arith counters.
  8946.      */
  8947.     arithSample: function(ctrName, seriesName, ts, eventBase) {
  8948.       return this.hwcSample('mali:arith', 'Arith: ' + ctrName, seriesName, ts,
  8949.           eventBase);
  8950.     },
  8951.     addArithCounter: function(hwcEventName, hwcTitle) {
  8952.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8953.         return this.arithSample(hwcTitle, 'count', ts, eventBase);
  8954.       }
  8955.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8956.     },
  8957.     addArithCycles: function(hwcEventName, hwcTitle) {
  8958.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8959.         return this.arithSample(hwcTitle, 'cycles', ts, eventBase);
  8960.       }
  8961.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8962.     },
  8963.  
  8964.     /*
  8965.      * Load/Store counters.
  8966.      */
  8967.     addLSCounter: function(hwcEventName, hwcTitle) {
  8968.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8969.         return this.hwcSample('mali:ls', 'LS: ' + hwcTitle, 'count', ts,
  8970.             eventBase);
  8971.       }
  8972.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8973.     },
  8974.  
  8975.     /*
  8976.      * Texture counters.
  8977.      */
  8978.     textureSample: function(ctrName, seriesName, ts, eventBase) {
  8979.       return this.hwcSample('mali:texture', 'Texture: ' + ctrName,
  8980.           seriesName, ts, eventBase);
  8981.     },
  8982.     addTexCounter: function(hwcEventName, hwcTitle) {
  8983.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8984.         return this.textureSample(hwcTitle, 'count', ts, eventBase);
  8985.       }
  8986.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8987.     },
  8988.  
  8989.     /*
  8990.      * LSC counters.
  8991.      */
  8992.     addLSCCounter: function(hwcEventName, hwcTitle) {
  8993.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  8994.         return this.hwcSample('mali:lsc', 'LSC: ' + hwcTitle, 'count', ts,
  8995.             eventBase);
  8996.       }
  8997.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  8998.     },
  8999.  
  9000.     /*
  9001.      * TLB counters.
  9002.      */
  9003.     addAXICounter: function(hwcEventName, hwcTitle) {
  9004.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  9005.         return this.hwcSample('mali:axi', 'AXI: ' + hwcTitle, 'count', ts,
  9006.             eventBase);
  9007.       }
  9008.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  9009.     },
  9010.  
  9011.     /*
  9012.      * MMU counters.
  9013.      */
  9014.     mmuSample: function(ctrName, seriesName, ts, eventBase) {
  9015.       return this.hwcSample('mali:mmu', 'MMU: ' + ctrName, seriesName, ts,
  9016.           eventBase);
  9017.     },
  9018.     addMMUCounter: function(hwcEventName, hwcTitle) {
  9019.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  9020.         return this.mmuSample(hwcTitle, 'count', ts, eventBase);
  9021.       }
  9022.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  9023.     },
  9024.     addMMUCycles: function(hwcEventName, hwcTitle) {
  9025.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  9026.         return this.mmuSample(hwcTitle, 'cycles', ts, eventBase);
  9027.       }
  9028.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  9029.     },
  9030.  
  9031.     /*
  9032.      * L2 counters.
  9033.      */
  9034.     l2Sample: function(ctrName, seriesName, ts, eventBase) {
  9035.       return this.hwcSample('mali:l2', 'L2: ' + ctrName, seriesName, ts,
  9036.           eventBase);
  9037.     },
  9038.     addL2Counter: function(hwcEventName, hwcTitle) {
  9039.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  9040.         return this.l2Sample(hwcTitle, 'count', ts, eventBase);
  9041.       }
  9042.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  9043.     },
  9044.     addL2Cycles: function(hwcEventName, hwcTitle) {
  9045.       function handler(eventName, cpuNumber, pid, ts, eventBase) {
  9046.         return this.l2Sample(hwcTitle, 'cycles', ts, eventBase);
  9047.       }
  9048.       this.importer.registerEventHandler(hwcEventName, handler.bind(this));
  9049.     }
  9050.   };
  9051.  
  9052.   LinuxPerfParser.registerSubtype(LinuxPerfMaliParser);
  9053.  
  9054.   return {
  9055.     LinuxPerfMaliParser: LinuxPerfMaliParser
  9056.   };
  9057. });
  9058.  
  9059. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9060. // Use of this source code is governed by a BSD-style license that can be
  9061. // found in the LICENSE file.
  9062.  
  9063. /**
  9064.  * @fileoverview Parses power events in the Linux event trace format.
  9065.  */
  9066. base.require('linux_perf_parser');
  9067. base.exportTo('tracing', function() {
  9068.  
  9069.   var LinuxPerfParser = tracing.LinuxPerfParser;
  9070.  
  9071.   /**
  9072.    * Parses linux power trace events.
  9073.    * @constructor
  9074.    */
  9075.   function LinuxPerfPowerParser(importer) {
  9076.     LinuxPerfParser.call(this, importer);
  9077.  
  9078.     // NB: old-style power events, deprecated
  9079.     importer.registerEventHandler('power_start',
  9080.         LinuxPerfPowerParser.prototype.powerStartEvent.bind(this));
  9081.     importer.registerEventHandler('power_frequency',
  9082.         LinuxPerfPowerParser.prototype.powerFrequencyEvent.bind(this));
  9083.  
  9084.     importer.registerEventHandler('cpu_frequency',
  9085.         LinuxPerfPowerParser.prototype.cpuFrequencyEvent.bind(this));
  9086.     importer.registerEventHandler('cpu_idle',
  9087.         LinuxPerfPowerParser.prototype.cpuIdleEvent.bind(this));
  9088.   }
  9089.  
  9090.   LinuxPerfPowerParser.prototype = {
  9091.     __proto__: LinuxPerfParser.prototype,
  9092.  
  9093.     cpuStateSlice: function(ts, targetCpuNumber, eventType, cpuState) {
  9094.       var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
  9095.       var powerCounter;
  9096.       if (eventType != '1') {
  9097.         this.importer.importError('Don\'t understand power_start events of ' +
  9098.             'type ' + eventType);
  9099.         return;
  9100.       }
  9101.       powerCounter = targetCpu.cpu.getOrCreateCounter('', 'C-State');
  9102.       if (powerCounter.numSeries == 0) {
  9103.         powerCounter.seriesNames.push('state');
  9104.         powerCounter.seriesColors.push(
  9105.             tracing.getStringColorId(powerCounter.name + '.' + 'state'));
  9106.       }
  9107.       powerCounter.timestamps.push(ts);
  9108.       powerCounter.samples.push(cpuState);
  9109.     },
  9110.  
  9111.     cpuIdleSlice: function(ts, targetCpuNumber, cpuState) {
  9112.       var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
  9113.       var powerCounter = targetCpu.cpu.getOrCreateCounter('', 'C-State');
  9114.       if (powerCounter.numSeries == 0) {
  9115.         powerCounter.seriesNames.push('state');
  9116.         powerCounter.seriesColors.push(
  9117.             tracing.getStringColorId(powerCounter.name));
  9118.       }
  9119.       // NB: 4294967295/-1 means an exit from the current state
  9120.       if (cpuState != 4294967295)
  9121.         powerCounter.samples.push(cpuState);
  9122.       else
  9123.         powerCounter.samples.push(0);
  9124.       powerCounter.timestamps.push(ts);
  9125.     },
  9126.  
  9127.     cpuFrequencySlice: function(ts, targetCpuNumber, powerState) {
  9128.       var targetCpu = this.importer.getOrCreateCpuState(targetCpuNumber);
  9129.       var powerCounter =
  9130.           targetCpu.cpu.getOrCreateCounter('', 'Clock Frequency');
  9131.       if (powerCounter.numSeries == 0) {
  9132.         powerCounter.seriesNames.push('state');
  9133.         powerCounter.seriesColors.push(
  9134.             tracing.getStringColorId(powerCounter.name + '.' + 'state'));
  9135.       }
  9136.       powerCounter.timestamps.push(ts);
  9137.       powerCounter.samples.push(powerState);
  9138.     },
  9139.  
  9140.     /**
  9141.      * Parses power events and sets up state in the importer.
  9142.      */
  9143.     powerStartEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9144.       var event = /type=(\d+) state=(\d) cpu_id=(\d)+/.exec(eventBase[5]);
  9145.       if (!event)
  9146.         return false;
  9147.  
  9148.       var targetCpuNumber = parseInt(event[3]);
  9149.       var cpuState = parseInt(event[2]);
  9150.       this.cpuStateSlice(ts, targetCpuNumber, event[1], cpuState);
  9151.       return true;
  9152.     },
  9153.  
  9154.     powerFrequencyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9155.       var event = /type=(\d+) state=(\d+) cpu_id=(\d)+/
  9156.           .exec(eventBase[5]);
  9157.       if (!event)
  9158.         return false;
  9159.  
  9160.       var targetCpuNumber = parseInt(event[3]);
  9161.       var powerState = parseInt(event[2]);
  9162.       this.cpuFrequencySlice(ts, targetCpuNumber, powerState);
  9163.       return true;
  9164.     },
  9165.  
  9166.     cpuFrequencyEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9167.       var event = /state=(\d+) cpu_id=(\d)+/.exec(eventBase[5]);
  9168.       if (!event)
  9169.         return false;
  9170.  
  9171.       var targetCpuNumber = parseInt(event[2]);
  9172.       var powerState = parseInt(event[1]);
  9173.       this.cpuFrequencySlice(ts, targetCpuNumber, powerState);
  9174.       return true;
  9175.     },
  9176.  
  9177.     cpuIdleEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9178.       var event = /state=(\d+) cpu_id=(\d)+/.exec(eventBase[5]);
  9179.       if (!event)
  9180.         return false;
  9181.  
  9182.       var targetCpuNumber = parseInt(event[2]);
  9183.       var cpuState = parseInt(event[1]);
  9184.       this.cpuIdleSlice(ts, targetCpuNumber, cpuState);
  9185.       return true;
  9186.     }
  9187.   };
  9188.  
  9189.   LinuxPerfParser.registerSubtype(LinuxPerfPowerParser);
  9190.  
  9191.   return {
  9192.     LinuxPerfPowerParser: LinuxPerfPowerParser
  9193.   };
  9194. });
  9195.  
  9196. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9197. // Use of this source code is governed by a BSD-style license that can be
  9198. // found in the LICENSE file.
  9199.  
  9200. /**
  9201.  * @fileoverview Parses scheduler events in the Linux event trace format.
  9202.  */
  9203. base.require('linux_perf_parser');
  9204. base.exportTo('tracing', function() {
  9205.  
  9206.   var LinuxPerfParser = tracing.LinuxPerfParser;
  9207.  
  9208.   /**
  9209.    * Parses linux sched trace events.
  9210.    * @constructor
  9211.    */
  9212.   function LinuxPerfSchedParser(importer) {
  9213.     LinuxPerfParser.call(this, importer);
  9214.  
  9215.     importer.registerEventHandler('sched_switch',
  9216.         LinuxPerfSchedParser.prototype.schedSwitchEvent.bind(this));
  9217.     importer.registerEventHandler('sched_wakeup',
  9218.         LinuxPerfSchedParser.prototype.schedWakeupEvent.bind(this));
  9219.   }
  9220.  
  9221.   TestExports = {};
  9222.  
  9223.   // Matches the sched_switch record
  9224.   var schedSwitchRE = new RegExp(
  9225.       'prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) ' +
  9226.       'prev_state=(\\S\\+?|\\S\\|\\S) ==> ' +
  9227.       'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');
  9228.   TestExports.schedSwitchRE = schedSwitchRE;
  9229.  
  9230.   // Matches the sched_wakeup record
  9231.   var schedWakeupRE =
  9232.       /comm=(.+) pid=(\d+) prio=(\d+) success=(\d+) target_cpu=(\d+)/;
  9233.   TestExports.schedWakeupRE = schedWakeupRE;
  9234.  
  9235.   LinuxPerfSchedParser.prototype = {
  9236.     __proto__: LinuxPerfParser.prototype,
  9237.  
  9238.     /**
  9239.      * Parses scheduler events and sets up state in the importer.
  9240.      */
  9241.     schedSwitchEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9242.       var event = schedSwitchRE.exec(eventBase[5]);
  9243.       if (!event)
  9244.         return false;
  9245.  
  9246.       var prevState = event[4];
  9247.       var nextComm = event[5];
  9248.       var nextPid = parseInt(event[6]);
  9249.       var nextPrio = parseInt(event[7]);
  9250.  
  9251.       var cpuState = this.importer.getOrCreateCpuState(cpuNumber);
  9252.       cpuState.switchRunningLinuxPid(this.importer,
  9253.           prevState, ts, nextPid, nextComm, nextPrio);
  9254.       return true;
  9255.     },
  9256.  
  9257.     schedWakeupEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9258.       var event = schedWakeupRE.exec(eventBase[5]);
  9259.       if (!event)
  9260.         return false;
  9261.  
  9262.       var comm = event[1];
  9263.       var pid = parseInt(event[2]);
  9264.       var prio = parseInt(event[3]);
  9265.       this.importer.markPidRunnable(ts, pid, comm, prio);
  9266.       return true;
  9267.     }
  9268.   };
  9269.  
  9270.   LinuxPerfParser.registerSubtype(LinuxPerfSchedParser);
  9271.  
  9272.   return {
  9273.     LinuxPerfSchedParser: LinuxPerfSchedParser,
  9274.     _LinuxPerfSchedParserTestExports: TestExports
  9275.   };
  9276. });
  9277.  
  9278. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9279. // Use of this source code is governed by a BSD-style license that can be
  9280. // found in the LICENSE file.
  9281.  
  9282. /**
  9283.  * @fileoverview Parses workqueue events in the Linux event trace format.
  9284.  */
  9285. base.require('linux_perf_parser');
  9286. base.exportTo('tracing', function() {
  9287.  
  9288.   var LinuxPerfParser = tracing.LinuxPerfParser;
  9289.  
  9290.   /**
  9291.    * Parses linux workqueue trace events.
  9292.    * @constructor
  9293.    */
  9294.   function LinuxPerfWorkqueueParser(importer) {
  9295.     LinuxPerfParser.call(this, importer);
  9296.  
  9297.     importer.registerEventHandler('workqueue_execute_start',
  9298.         LinuxPerfWorkqueueParser.prototype.executeStartEvent.bind(this));
  9299.     importer.registerEventHandler('workqueue_execute_end',
  9300.         LinuxPerfWorkqueueParser.prototype.executeEndEvent.bind(this));
  9301.   }
  9302.  
  9303.   // Matches the workqueue_execute_start record
  9304.   //  workqueue_execute_start: work struct c7a8a89c: function MISRWrapper
  9305.   var workqueueExecuteStartRE = /work struct (.+): function (\S+)/;
  9306.  
  9307.   // Matches the workqueue_execute_start record
  9308.   //  workqueue_execute_end: work struct c7a8a89c
  9309.   var workqueueExecuteEndRE = /work struct (.+)/;
  9310.  
  9311.   LinuxPerfWorkqueueParser.prototype = {
  9312.     __proto__: LinuxPerfParser.prototype,
  9313.  
  9314.     /**
  9315.      * Parses workqueue events and sets up state in the importer.
  9316.      */
  9317.     executeStartEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9318.       var event = workqueueExecuteStartRE.exec(eventBase[5]);
  9319.       if (!event)
  9320.         return false;
  9321.  
  9322.       var kthread = this.importer.getOrCreateKernelThread(eventBase[1]);
  9323.       kthread.openSliceTS = ts;
  9324.       kthread.openSlice = event[2];
  9325.       return true;
  9326.     },
  9327.  
  9328.     executeEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9329.       var event = workqueueExecuteEndRE.exec(eventBase[5]);
  9330.       if (!event)
  9331.         return false;
  9332.  
  9333.       var kthread = this.importer.getOrCreateKernelThread(eventBase[1]);
  9334.       if (kthread.openSlice) {
  9335.         var slice = new tracing.TimelineSlice('', kthread.openSlice,
  9336.             tracing.getStringColorId(kthread.openSlice),
  9337.             kthread.openSliceTS,
  9338.             {},
  9339.             ts - kthread.openSliceTS);
  9340.  
  9341.         kthread.thread.pushSlice(slice);
  9342.       }
  9343.       kthread.openSlice = undefined;
  9344.       return true;
  9345.     }
  9346.   };
  9347.  
  9348.   LinuxPerfParser.registerSubtype(LinuxPerfWorkqueueParser);
  9349.  
  9350.   return {
  9351.     LinuxPerfWorkqueueParser: LinuxPerfWorkqueueParser
  9352.   };
  9353. });
  9354.  
  9355. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9356. // Use of this source code is governed by a BSD-style license that can be
  9357. // found in the LICENSE file.
  9358.  
  9359. /**
  9360.  * @fileoverview Parses trace_marker events that were inserted in the trace by
  9361.  * userland.
  9362.  */
  9363. base.require('linux_perf_parser');
  9364. base.exportTo('tracing', function() {
  9365.  
  9366.   var LinuxPerfParser = tracing.LinuxPerfParser;
  9367.  
  9368.   /**
  9369.    * Parses linux trace mark events that were inserted in the trace by userland.
  9370.    * @constructor
  9371.    */
  9372.   function LinuxPerfAndroidParser(importer) {
  9373.     LinuxPerfParser.call(this, importer);
  9374.  
  9375.     importer.registerEventHandler('tracing_mark_write:android',
  9376.         LinuxPerfAndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
  9377.     importer.registerEventHandler('0:android',
  9378.         LinuxPerfAndroidParser.prototype.traceMarkWriteAndroidEvent.bind(this));
  9379.  
  9380.     this.model_ = importer.model_;
  9381.     this.ppids_ = {};
  9382.   }
  9383.  
  9384.   function parseArgs(argsString) {
  9385.     var args = {};
  9386.     if (argsString) {
  9387.       var argsArray = argsString.split(';');
  9388.       for (var i = 0; i < argsArray.length; ++i) {
  9389.         var parts = argsArray[i].split('=');
  9390.         if (parts[0])
  9391.           args[parts[0]] = parts[1];
  9392.       }
  9393.     }
  9394.     return args;
  9395.   }
  9396.  
  9397.   LinuxPerfAndroidParser.prototype = {
  9398.     __proto__: LinuxPerfParser.prototype,
  9399.  
  9400.     traceMarkWriteAndroidEvent: function(eventName, cpuNumber, pid, ts,
  9401.                                   eventBase, threadName) {
  9402.       var eventData = eventBase[2].split('|');
  9403.       switch (eventData[0]) {
  9404.         case 'B':
  9405.           var ppid = parseInt(eventData[1]);
  9406.           var name = eventData[2];
  9407.           var thread = this.model_.getOrCreateProcess(ppid)
  9408.             .getOrCreateThread(pid);
  9409.           thread.name = threadName;
  9410.           if (!thread.isTimestampValidForBeginOrEnd(ts)) {
  9411.             this.model_.importErrors.push(
  9412.                 'Timestamps are moving backward.');
  9413.             return false;
  9414.           }
  9415.  
  9416.           this.ppids_[pid] = ppid;
  9417.           thread.beginSlice(null, name, ts, parseArgs(eventData[3]));
  9418.  
  9419.           break;
  9420.         case 'E':
  9421.           var ppid = this.ppids_[pid];
  9422.           if (ppid === undefined) {
  9423.             // Silently ignore unmatched E events.
  9424.             break;
  9425.           }
  9426.  
  9427.           var thread = this.model_.getOrCreateProcess(ppid)
  9428.             .getOrCreateThread(pid);
  9429.           if (!thread.openSliceCount) {
  9430.             // Silently ignore unmatched E events.
  9431.             break;
  9432.           }
  9433.  
  9434.           var slice = thread.endSlice(ts);
  9435.  
  9436.           var args = parseArgs(eventData[3]);
  9437.           for (var arg in args) {
  9438.             if (slice.args[arg] !== undefined) {
  9439.               this.model_.importErrors.push(
  9440.                   'Both the B and E events of ' + slice.name +
  9441.                   'provided values for argument ' + arg + '. ' +
  9442.                   'The value of the E event will be used.');
  9443.             }
  9444.             slice.args[arg] = args[arg];
  9445.           }
  9446.  
  9447.           break;
  9448.         case 'C':
  9449.           var ppid = parseInt(eventData[1]);
  9450.           var name = eventData[2];
  9451.           var value = parseInt(eventData[3]);
  9452.  
  9453.           var ctr = this.model_.getOrCreateProcess(ppid)
  9454.               .getOrCreateCounter(null, name);
  9455.           // Initialize the counter's series fields if needed.
  9456.           if (ctr.numSeries == 0) {
  9457.             ctr.seriesNames.push('value');
  9458.             ctr.seriesColors.push(
  9459.                 tracing.getStringColorId(ctr.name + '.' + 'value'));
  9460.           }
  9461.  
  9462.           // Add the sample value.
  9463.           ctr.timestamps.push(ts);
  9464.           ctr.samples.push(value);
  9465.  
  9466.           break;
  9467.         default:
  9468.           return false;
  9469.       }
  9470.  
  9471.       return true;
  9472.     },
  9473.   };
  9474.  
  9475.   LinuxPerfParser.registerSubtype(LinuxPerfAndroidParser);
  9476.  
  9477.   return {
  9478.     LinuxPerfAndroidParser: LinuxPerfAndroidParser
  9479.   };
  9480. });
  9481.  
  9482. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  9483. // Use of this source code is governed by a BSD-style license that can be
  9484. // found in the LICENSE file.
  9485.  
  9486. /**
  9487.  * @fileoverview Imports text files in the Linux event trace format into the
  9488.  * timeline model. This format is output both by sched_trace and by Linux's perf
  9489.  * tool.
  9490.  *
  9491.  * This importer assumes the events arrive as a string. The unit tests provide
  9492.  * examples of the trace format.
  9493.  *
  9494.  * Linux scheduler traces use a definition for 'pid' that is different than
  9495.  * tracing uses. Whereas tracing uses pid to identify a specific process, a pid
  9496.  * in a linux trace refers to a specific thread within a process. Within this
  9497.  * file, we the definition used in Linux traces, as it improves the importing
  9498.  * code's readability.
  9499.  */
  9500. base.require('timeline_model');
  9501. base.require('timeline_color_scheme');
  9502. base.require('linux_perf_bus_parser');
  9503. base.require('linux_perf_clock_parser');
  9504. base.require('linux_perf_cpufreq_parser');
  9505. base.require('linux_perf_drm_parser');
  9506. base.require('linux_perf_exynos_parser');
  9507. base.require('linux_perf_gesture_parser');
  9508. base.require('linux_perf_i915_parser');
  9509. base.require('linux_perf_mali_parser');
  9510. base.require('linux_perf_power_parser');
  9511. base.require('linux_perf_sched_parser');
  9512. base.require('linux_perf_workqueue_parser');
  9513. base.require('linux_perf_android_parser');
  9514.  
  9515. base.exportTo('tracing', function() {
  9516.   /**
  9517.    * Represents the scheduling state for a single thread.
  9518.    * @constructor
  9519.    */
  9520.   function CpuState(cpu) {
  9521.     this.cpu = cpu;
  9522.   }
  9523.  
  9524.   CpuState.prototype = {
  9525.     __proto__: Object.prototype,
  9526.  
  9527.     /**
  9528.      * Switches the active pid on this Cpu. If necessary, add a TimelineSlice
  9529.      * to the cpu representing the time spent on that Cpu since the last call to
  9530.      * switchRunningLinuxPid.
  9531.      */
  9532.     switchRunningLinuxPid: function(importer, prevState, ts, pid, comm, prio) {
  9533.       // Generate a slice if the last active pid was not the idle task
  9534.       if (this.lastActivePid !== undefined && this.lastActivePid != 0) {
  9535.         var duration = ts - this.lastActiveTs;
  9536.         var thread = importer.threadsByLinuxPid[this.lastActivePid];
  9537.         if (thread)
  9538.           name = thread.userFriendlyName;
  9539.         else
  9540.           name = this.lastActiveComm;
  9541.  
  9542.         var slice = new tracing.TimelineSlice('', name,
  9543.                                               tracing.getStringColorId(name),
  9544.                                               this.lastActiveTs,
  9545.                                               {
  9546.                                                 comm: this.lastActiveComm,
  9547.                                                 tid: this.lastActivePid,
  9548.                                                 prio: this.lastActivePrio,
  9549.                                                 stateWhenDescheduled: prevState
  9550.                                               },
  9551.                                               duration);
  9552.         this.cpu.slices.push(slice);
  9553.       }
  9554.  
  9555.       this.lastActiveTs = ts;
  9556.       this.lastActivePid = pid;
  9557.       this.lastActiveComm = comm;
  9558.       this.lastActivePrio = prio;
  9559.     }
  9560.   };
  9561.  
  9562.   /**
  9563.    * Imports linux perf events into a specified model.
  9564.    * @constructor
  9565.    */
  9566.   function LinuxPerfImporter(model, events) {
  9567.     this.importPriority = 2;
  9568.     this.model_ = model;
  9569.     this.events_ = events;
  9570.     this.clockSyncRecords_ = [];
  9571.     this.cpuStates_ = {};
  9572.     this.kernelThreadStates_ = {};
  9573.     this.buildMapFromLinuxPidsToTimelineThreads();
  9574.     this.lineNumber = -1;
  9575.     this.pseudoThreadCounter = 1;
  9576.     this.parsers_ = [];
  9577.     this.eventHandlers_ = {};
  9578.   }
  9579.  
  9580.   TestExports = {};
  9581.  
  9582.   // Matches the default trace record in 3.2 and later (includes irq-info):
  9583.   //          <idle>-0     [001] d...  1.23: sched_switch
  9584.   var lineREWithIRQInfo = new RegExp(
  9585.       '^\\s*(.+?)\\s+\\[(\\d+)\\]' + '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
  9586.       '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
  9587.   TestExports.lineREWithIRQInfo = lineREWithIRQInfo;
  9588.  
  9589.   // Matches the default trace record pre-3.2:
  9590.   //          <idle>-0     [001]  1.23: sched_switch
  9591.   var lineRE = /^\s*(.+?)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
  9592.   TestExports.lineRE = lineRE;
  9593.  
  9594.   // Matches the trace_event_clock_sync record
  9595.   //  0: trace_event_clock_sync: parent_ts=19581477508
  9596.   var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
  9597.   TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
  9598.  
  9599.   // Some kernel trace events are manually classified in slices and
  9600.   // hand-assigned a pseudo PID.
  9601.   var pseudoKernelPID = 0;
  9602.  
  9603.   /**
  9604.    * Deduce the format of trace data. Linix kernels prior to 3.3 used
  9605.    * one format (by default); 3.4 and later used another.
  9606.    *
  9607.    * @return {string} the regular expression for parsing data when
  9608.    * the format is recognized; otherwise null.
  9609.    */
  9610.   function autoDetectLineRE(line) {
  9611.     if (lineREWithIRQInfo.test(line))
  9612.       return lineREWithIRQInfo;
  9613.     if (lineRE.test(line))
  9614.       return lineRE;
  9615.     return null;
  9616.   };
  9617.   TestExports.autoDetectLineRE = autoDetectLineRE;
  9618.  
  9619.   /**
  9620.    * Guesses whether the provided events is a Linux perf string.
  9621.    * Looks for the magic string "# tracer" at the start of the file,
  9622.    * or the typical task-pid-cpu-timestamp-function sequence of a typical
  9623.    * trace's body.
  9624.    *
  9625.    * @return {boolean} True when events is a linux perf array.
  9626.    */
  9627.   LinuxPerfImporter.canImport = function(events) {
  9628.     if (!(typeof(events) === 'string' || events instanceof String))
  9629.       return false;
  9630.  
  9631.     if (/^# tracer:/.test(events))
  9632.       return true;
  9633.  
  9634.     var m = /^(.+)\n/.exec(events);
  9635.     if (m)
  9636.       events = m[1];
  9637.     if (autoDetectLineRE(events))
  9638.       return true;
  9639.  
  9640.     return false;
  9641.   };
  9642.  
  9643.   LinuxPerfImporter.prototype = {
  9644.     __proto__: Object.prototype,
  9645.  
  9646.     get model() {
  9647.       return this.model_;
  9648.     },
  9649.  
  9650.     /**
  9651.      * Precomputes a lookup table from linux pids back to existing
  9652.      * TimelineThreads. This is used during importing to add information to each
  9653.      * timeline thread about whether it was running, descheduled, sleeping, et
  9654.      * cetera.
  9655.      */
  9656.     buildMapFromLinuxPidsToTimelineThreads: function() {
  9657.       this.threadsByLinuxPid = {};
  9658.       this.model_.getAllThreads().forEach(
  9659.           function(thread) {
  9660.             this.threadsByLinuxPid[thread.tid] = thread;
  9661.           }.bind(this));
  9662.     },
  9663.  
  9664.     /**
  9665.      * @return {CpuState} A CpuState corresponding to the given cpuNumber.
  9666.      */
  9667.     getOrCreateCpuState: function(cpuNumber) {
  9668.       if (!this.cpuStates_[cpuNumber]) {
  9669.         var cpu = this.model_.getOrCreateCpu(cpuNumber);
  9670.         this.cpuStates_[cpuNumber] = new CpuState(cpu);
  9671.       }
  9672.       return this.cpuStates_[cpuNumber];
  9673.     },
  9674.  
  9675.     /**
  9676.      * @return {TimelinThread} A thread corresponding to the kernelThreadName.
  9677.      */
  9678.     getOrCreateKernelThread: function(kernelThreadName, opt_pid, opt_tid) {
  9679.       if (!this.kernelThreadStates_[kernelThreadName]) {
  9680.         var pid = opt_pid;
  9681.         if (pid == undefined) {
  9682.           pid = /.+-(\d+)/.exec(kernelThreadName)[1];
  9683.           pid = parseInt(pid, 10);
  9684.         }
  9685.         var tid = opt_tid;
  9686.         if (tid == undefined)
  9687.           tid = pid;
  9688.  
  9689.         var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
  9690.         thread.name = kernelThreadName;
  9691.         this.kernelThreadStates_[kernelThreadName] = {
  9692.           pid: pid,
  9693.           thread: thread,
  9694.           openSlice: undefined,
  9695.           openSliceTS: undefined
  9696.         };
  9697.         this.threadsByLinuxPid[pid] = thread;
  9698.       }
  9699.       return this.kernelThreadStates_[kernelThreadName];
  9700.     },
  9701.  
  9702.     /**
  9703.      * @return {TimelinThread} A pseudo thread corresponding to the
  9704.      * threadName.  Pseudo threads are for events that we want to break
  9705.      * out to a separate timeline but would not otherwise happen.
  9706.      * These threads are assigned to pseudoKernelPID and given a
  9707.      * unique (incrementing) TID.
  9708.      */
  9709.     getOrCreatePseudoThread: function(threadName) {
  9710.       var thread = this.kernelThreadStates_[threadName];
  9711.       if (!thread) {
  9712.         thread = this.getOrCreateKernelThread(threadName, pseudoKernelPID,
  9713.             this.pseudoThreadCounter);
  9714.         this.pseudoThreadCounter++;
  9715.       }
  9716.       return thread;
  9717.     },
  9718.  
  9719.     /**
  9720.      * Imports the data in this.events_ into model_.
  9721.      */
  9722.     importEvents: function(isSecondaryImport) {
  9723.       this.createParsers();
  9724.       this.importCpuData();
  9725.       if (!this.alignClocks(isSecondaryImport))
  9726.         return;
  9727.       this.buildMapFromLinuxPidsToTimelineThreads();
  9728.       this.buildPerThreadCpuSlicesFromCpuState();
  9729.     },
  9730.  
  9731.     /**
  9732.      * Called by the TimelineModel after all other importers have imported their
  9733.      * events.
  9734.      */
  9735.     finalizeImport: function() {
  9736.     },
  9737.  
  9738.     /**
  9739.      * Builds the cpuSlices array on each thread based on our knowledge of what
  9740.      * each Cpu is doing.  This is done only for TimelineThreads that are
  9741.      * already in the model, on the assumption that not having any traced data
  9742.      * on a thread means that it is not of interest to the user.
  9743.      */
  9744.     buildPerThreadCpuSlicesFromCpuState: function() {
  9745.       // Push the cpu slices to the threads that they run on.
  9746.       for (var cpuNumber in this.cpuStates_) {
  9747.         var cpuState = this.cpuStates_[cpuNumber];
  9748.         var cpu = cpuState.cpu;
  9749.  
  9750.         for (var i = 0; i < cpu.slices.length; i++) {
  9751.           var slice = cpu.slices[i];
  9752.  
  9753.           var thread = this.threadsByLinuxPid[slice.args.tid];
  9754.           if (!thread)
  9755.             continue;
  9756.           if (!thread.tempCpuSlices)
  9757.             thread.tempCpuSlices = [];
  9758.           thread.tempCpuSlices.push(slice);
  9759.         }
  9760.       }
  9761.  
  9762.       // Create slices for when the thread is not running.
  9763.       var runningId = tracing.getColorIdByName('running');
  9764.       var runnableId = tracing.getColorIdByName('runnable');
  9765.       var sleepingId = tracing.getColorIdByName('sleeping');
  9766.       var ioWaitId = tracing.getColorIdByName('iowait');
  9767.       this.model_.getAllThreads().forEach(function(thread) {
  9768.         if (!thread.tempCpuSlices)
  9769.           return;
  9770.         var origSlices = thread.tempCpuSlices;
  9771.         delete thread.tempCpuSlices;
  9772.  
  9773.         origSlices.sort(function(x, y) {
  9774.           return x.start - y.start;
  9775.         });
  9776.  
  9777.         // Walk the slice list and put slices between each original slice
  9778.         // to show when the thread isn't running
  9779.         var slices = [];
  9780.         if (origSlices.length) {
  9781.           var slice = origSlices[0];
  9782.           slices.push(new tracing.TimelineSlice('', 'Running', runningId,
  9783.               slice.start, {}, slice.duration));
  9784.         }
  9785.         for (var i = 1; i < origSlices.length; i++) {
  9786.           var prevSlice = origSlices[i - 1];
  9787.           var nextSlice = origSlices[i];
  9788.           var midDuration = nextSlice.start - prevSlice.end;
  9789.           if (prevSlice.args.stateWhenDescheduled == 'S') {
  9790.             slices.push(new tracing.TimelineSlice('', 'Sleeping', sleepingId,
  9791.                 prevSlice.end, {}, midDuration));
  9792.           } else if (prevSlice.args.stateWhenDescheduled == 'R' ||
  9793.                      prevSlice.args.stateWhenDescheduled == 'R+') {
  9794.             slices.push(new tracing.TimelineSlice('', 'Runnable', runnableId,
  9795.                 prevSlice.end, {}, midDuration));
  9796.           } else if (prevSlice.args.stateWhenDescheduled == 'D') {
  9797.             slices.push(new tracing.TimelineSlice(
  9798.                 '', 'Uninterruptible Sleep', ioWaitId,
  9799.                 prevSlice.end, {}, midDuration));
  9800.           } else if (prevSlice.args.stateWhenDescheduled == 'T') {
  9801.             slices.push(new tracing.TimelineSlice('', '__TASK_STOPPED',
  9802.                 ioWaitId, prevSlice.end, {}, midDuration));
  9803.           } else if (prevSlice.args.stateWhenDescheduled == 't') {
  9804.             slices.push(new tracing.TimelineSlice('', 'debug', ioWaitId,
  9805.                 prevSlice.end, {}, midDuration));
  9806.           } else if (prevSlice.args.stateWhenDescheduled == 'Z') {
  9807.             slices.push(new tracing.TimelineSlice('', 'Zombie', ioWaitId,
  9808.                 prevSlice.end, {}, midDuration));
  9809.           } else if (prevSlice.args.stateWhenDescheduled == 'X') {
  9810.             slices.push(new tracing.TimelineSlice('', 'Exit Dead', ioWaitId,
  9811.                 prevSlice.end, {}, midDuration));
  9812.           } else if (prevSlice.args.stateWhenDescheduled == 'x') {
  9813.             slices.push(new tracing.TimelineSlice('', 'Task Dead', ioWaitId,
  9814.                 prevSlice.end, {}, midDuration));
  9815.           } else if (prevSlice.args.stateWhenDescheduled == 'W') {
  9816.             slices.push(new tracing.TimelineSlice('', 'WakeKill', ioWaitId,
  9817.                 prevSlice.end, {}, midDuration));
  9818.           } else if (prevSlice.args.stateWhenDescheduled == 'D|W') {
  9819.             slices.push(new tracing.TimelineSlice(
  9820.                 '', 'Uninterruptable Sleep | WakeKill', ioWaitId,
  9821.                 prevSlice.end, {}, midDuration));
  9822.           } else {
  9823.             throw new Error('Unrecognized state: ') +
  9824.                 prevSlice.args.stateWhenDescheduled;
  9825.           }
  9826.  
  9827.           slices.push(new tracing.TimelineSlice('', 'Running', runningId,
  9828.               nextSlice.start, {}, nextSlice.duration));
  9829.         }
  9830.         thread.cpuSlices = slices;
  9831.       });
  9832.     },
  9833.  
  9834.     /**
  9835.      * Walks the slices stored on this.cpuStates_ and adjusts their timestamps
  9836.      * based on any alignment metadata we discovered.
  9837.      */
  9838.     alignClocks: function(isSecondaryImport) {
  9839.       if (this.clockSyncRecords_.length == 0) {
  9840.         // If this is a secondary import, and no clock syncing records were
  9841.         // found, then abort the import. Otherwise, just skip clock alignment.
  9842.         if (!isSecondaryImport)
  9843.           return true;
  9844.  
  9845.         // Remove the newly imported CPU slices from the model.
  9846.         this.abortImport();
  9847.         return false;
  9848.       }
  9849.  
  9850.       // Shift all the slice times based on the sync record.
  9851.       var sync = this.clockSyncRecords_[0];
  9852.       // NB: parentTS of zero denotes no times-shift; this is
  9853.       // used when user and kernel event clocks are identical.
  9854.       if (sync.parentTS == 0 || sync.parentTS == sync.perfTS)
  9855.         return true;
  9856.       var timeShift = sync.parentTS - sync.perfTS;
  9857.       for (var cpuNumber in this.cpuStates_) {
  9858.         var cpuState = this.cpuStates_[cpuNumber];
  9859.         var cpu = cpuState.cpu;
  9860.  
  9861.         for (var i = 0; i < cpu.slices.length; i++) {
  9862.           var slice = cpu.slices[i];
  9863.           slice.start = slice.start + timeShift;
  9864.           slice.duration = slice.duration;
  9865.         }
  9866.  
  9867.         for (var counterName in cpu.counters) {
  9868.           var counter = cpu.counters[counterName];
  9869.           for (var sI = 0; sI < counter.timestamps.length; sI++)
  9870.             counter.timestamps[sI] = (counter.timestamps[sI] + timeShift);
  9871.         }
  9872.       }
  9873.       for (var kernelThreadName in this.kernelThreadStates_) {
  9874.         var kthread = this.kernelThreadStates_[kernelThreadName];
  9875.         var thread = kthread.thread;
  9876.         thread.shiftTimestampsForward(timeShift);
  9877.       }
  9878.       return true;
  9879.     },
  9880.  
  9881.     /**
  9882.      * Removes any data that has been added to the model because of an error
  9883.      * detected during the import.
  9884.      */
  9885.     abortImport: function() {
  9886.       if (this.pushedEventsToThreads)
  9887.         throw new Error('Cannot abort, have alrady pushedCpuDataToThreads.');
  9888.  
  9889.       for (var cpuNumber in this.cpuStates_)
  9890.         delete this.model_.cpus[cpuNumber];
  9891.       for (var kernelThreadName in this.kernelThreadStates_) {
  9892.         var kthread = this.kernelThreadStates_[kernelThreadName];
  9893.         var thread = kthread.thread;
  9894.         var process = thread.parent;
  9895.         delete process.threads[thread.tid];
  9896.         delete this.model_.processes[process.pid];
  9897.       }
  9898.       this.model_.importErrors.push(
  9899.           'Cannot import kernel trace without a clock sync.');
  9900.     },
  9901.  
  9902.     /**
  9903.      * Creates an instance of each registered linux perf event parser.
  9904.      * This allows the parsers to register handlers for the events they
  9905.      * understand.  We also register our own special handlers (for the
  9906.      * timestamp synchronization markers).
  9907.      */
  9908.     createParsers: function() {
  9909.       // Instantiate the parsers; this will register handlers for known events
  9910.       var parserConstructors = tracing.LinuxPerfParser.getSubtypeConstructors();
  9911.       for (var i = 0; i < parserConstructors.length; ++i) {
  9912.         var parserConstructor = parserConstructors[i];
  9913.         this.parsers_.push(new parserConstructor(this));
  9914.       }
  9915.  
  9916.       this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
  9917.           LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
  9918.       this.registerEventHandler('tracing_mark_write',
  9919.           LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
  9920.       // NB: old-style trace markers; deprecated
  9921.       this.registerEventHandler('0:trace_event_clock_sync',
  9922.           LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
  9923.       this.registerEventHandler('0',
  9924.           LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
  9925.     },
  9926.  
  9927.     /**
  9928.      * Registers a linux perf event parser used by importCpuData.
  9929.      */
  9930.     registerEventHandler: function(eventName, handler) {
  9931.       // TODO(sleffler) how to handle conflicts?
  9932.       this.eventHandlers_[eventName] = handler;
  9933.     },
  9934.  
  9935.     /**
  9936.      * Records the fact that a pid has become runnable. This data will
  9937.      * eventually get used to derive each thread's cpuSlices array.
  9938.      */
  9939.     markPidRunnable: function(ts, pid, comm, prio) {
  9940.       // TODO(nduca): implement this functionality.
  9941.     },
  9942.  
  9943.     importError: function(message) {
  9944.       this.model_.importErrors.push('Line ' + (this.lineNumber + 1) +
  9945.           ': ' + message);
  9946.     },
  9947.  
  9948.     /**
  9949.      * Processes a trace_event_clock_sync event.
  9950.      */
  9951.     traceClockSyncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
  9952.       var event = /parent_ts=(\d+\.?\d*)/.exec(eventBase[2]);
  9953.       if (!event)
  9954.         return false;
  9955.  
  9956.       this.clockSyncRecords_.push({
  9957.         perfTS: ts,
  9958.         parentTS: event[1] * 1000
  9959.       });
  9960.       return true;
  9961.     },
  9962.  
  9963.     /**
  9964.      * Processes a trace_marking_write event.
  9965.      */
  9966.     traceMarkingWriteEvent: function(eventName, cpuNumber, pid, ts, eventBase,
  9967.                                      threadName) {
  9968.       var event = /^\s*(\w+):\s*(.*)$/.exec(eventBase[5]);
  9969.       if (!event) {
  9970.         // Check if the event matches events traced by the Android framework
  9971.         var tag = eventBase[5].substring(0, 2);
  9972.         if (tag == 'B|' || tag == 'E' || tag == 'E|' || tag == 'C|')
  9973.           event = [eventBase[5], 'android', eventBase[5]];
  9974.         else
  9975.           return false;
  9976.       }
  9977.  
  9978.       var writeEventName = eventName + ':' + event[1];
  9979.       var threadName = (/(.+)-\d+/.exec(eventBase[1]))[1];
  9980.       var handler = this.eventHandlers_[writeEventName];
  9981.       if (!handler) {
  9982.         this.importError('Unknown trace_marking_write event ' + writeEventName);
  9983.         return true;
  9984.       }
  9985.       return handler(writeEventName, cpuNumber, pid, ts, event, threadName);
  9986.     },
  9987.  
  9988.     /**
  9989.      * Walks the this.events_ structure and creates TimelineCpu objects.
  9990.      */
  9991.     importCpuData: function() {
  9992.       this.lines_ = this.events_.split('\n');
  9993.  
  9994.       var lineRE = null;
  9995.       for (this.lineNumber = 0; this.lineNumber < this.lines_.length;
  9996.           ++this.lineNumber) {
  9997.         var line = this.lines_[this.lineNumber];
  9998.         if (line.length == 0 || /^#/.test(line))
  9999.           continue;
  10000.         if (lineRE == null) {
  10001.           lineRE = autoDetectLineRE(line);
  10002.           if (lineRE == null) {
  10003.             this.importError('Cannot parse line: ' + line);
  10004.             continue;
  10005.           }
  10006.         }
  10007.         var eventBase = lineRE.exec(line);
  10008.         if (!eventBase) {
  10009.           this.importError('Unrecognized line: ' + line);
  10010.           continue;
  10011.         }
  10012.  
  10013.         var pid = parseInt((/.+-(\d+)/.exec(eventBase[1]))[1]);
  10014.         var cpuNumber = parseInt(eventBase[2]);
  10015.         var ts = parseFloat(eventBase[3]) * 1000;
  10016.         var eventName = eventBase[4];
  10017.  
  10018.         var handler = this.eventHandlers_[eventName];
  10019.         if (!handler) {
  10020.           this.importError('Unknown event ' + eventName + ' (' + line + ')');
  10021.           continue;
  10022.         }
  10023.         if (!handler(eventName, cpuNumber, pid, ts, eventBase))
  10024.           this.importError('Malformed ' + eventName + ' event (' + line + ')');
  10025.       }
  10026.     }
  10027.   };
  10028.  
  10029.   tracing.TimelineModel.registerImporter(LinuxPerfImporter);
  10030.  
  10031.   return {
  10032.     LinuxPerfImporter: LinuxPerfImporter,
  10033.     _LinuxPerfImporterTestExports: TestExports
  10034.   };
  10035.  
  10036. });
  10037.  
  10038. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10039. // Use of this source code is governed by a BSD-style license that can be
  10040. // found in the LICENSE file.
  10041.  
  10042. 'use strict';
  10043.  
  10044. /**
  10045.  * @fileoverview Provides the Settings class.
  10046.  */
  10047. base.exportTo('base', function() {
  10048.  
  10049.   /**
  10050.    * Settings is a simple wrapper around local storage, to make it easier
  10051.    * to test classes that have settings.
  10052.    *
  10053.    * @constructor
  10054.    */
  10055.   function Settings() {
  10056.     if ('G_testRunner' in global) {
  10057.       /**
  10058.        * In unit tests, use a mock object for storage so we don't change
  10059.        * localStorage in tests.
  10060.        */
  10061.       this.storage_ = FakeLocalStorage();
  10062.     } else {
  10063.       this.storage_ = localStorage;
  10064.     }
  10065.   }
  10066.  
  10067.   Settings.prototype = {
  10068.  
  10069.     /**
  10070.      * Get the setting with the given name.
  10071.      *
  10072.      * @param {string} key The name of the setting.
  10073.      * @param {string} opt_default The default value to return if not set.
  10074.      * @param {string} opt_namespace If set, the setting name will be prefixed
  10075.      * with this namespace, e.g. "categories.settingName". This is useful for
  10076.      * a set of related settings.
  10077.      */
  10078.     get: function(key, opt_default, opt_namespace) {
  10079.       key = this.namespace_(key, opt_namespace);
  10080.       if (!(key in this.storage_))
  10081.         return opt_default;
  10082.       return String(this.storage_[key]);
  10083.     },
  10084.  
  10085.     /**
  10086.      * Set the setting with the given name to the given value.
  10087.      *
  10088.      * @param {string} key The name of the setting.
  10089.      * @param {string} value The value of the setting.
  10090.      * @param {string} opt_namespace If set, the setting name will be prefixed
  10091.      * with this namespace, e.g. "categories.settingName". This is useful for
  10092.      * a set of related settings.
  10093.      */
  10094.     set: function(key, value, opt_namespace) {
  10095.       this.storage_[this.namespace_(key, opt_namespace)] = String(value);
  10096.     },
  10097.  
  10098.     /**
  10099.      * Return a list of all the keys, or all the keys in the given namespace
  10100.      * if one is provided.
  10101.      *
  10102.      * @param {string} opt_namespace If set, only return settings which
  10103.      * begin with this prefix.
  10104.      */
  10105.     keys: function(opt_namespace) {
  10106.       var result = [];
  10107.       opt_namespace = opt_namespace || '';
  10108.       for (var i = 0; i < this.storage_.length; i++) {
  10109.         var key = this.storage_.key(i);
  10110.         if (this.isnamespaced_(key, opt_namespace))
  10111.           result.push(this.unnamespace_(key, opt_namespace));
  10112.       }
  10113.       return result;
  10114.     },
  10115.  
  10116.     isnamespaced_: function(key, opt_namespace) {
  10117.       return key.indexOf(this.normalize_(opt_namespace)) == 0;
  10118.     },
  10119.  
  10120.     namespace_: function(key, opt_namespace) {
  10121.       return this.normalize_(opt_namespace) + key;
  10122.     },
  10123.  
  10124.     unnamespace_: function(key, opt_namespace) {
  10125.       return key.replace(this.normalize_(opt_namespace), '');
  10126.     },
  10127.  
  10128.     /**
  10129.      * All settings are prefixed with a global namespace to avoid collisions.
  10130.      * Settings may also be namespaced with an additional prefix passed into
  10131.      * the get, set, and keys methods in order to group related settings.
  10132.      * This method makes sure the two namespaces are always set properly.
  10133.      */
  10134.     normalize_: function(opt_namespace) {
  10135.       return Settings.NAMESPACE + (opt_namespace ? opt_namespace + '.' : '');
  10136.     }
  10137.   };
  10138.  
  10139.   Settings.NAMESPACE = 'trace-viewer';
  10140.  
  10141.   return {
  10142.     Settings: Settings
  10143.   };
  10144. });
  10145.  
  10146.  
  10147.  
  10148. /**
  10149.  * Create a Fake localStorage object which just stores to a dictionary
  10150.  * instead of actually saving into localStorage. Only used in unit tests.
  10151.  * @constructor
  10152.  */
  10153. function FakeLocalStorage() {
  10154.   return Object.create({}, {
  10155.     key: { value: function(i) {
  10156.       return Object.keys(this).sort()[i];
  10157.     }},
  10158.     length: { get: function() {
  10159.       return Object.keys(this).length;
  10160.     }}
  10161.   });
  10162. }
  10163.  
  10164. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10165. // Use of this source code is governed by a BSD-style license that can be
  10166. // found in the LICENSE file.
  10167.  
  10168. 'use strict';
  10169.  
  10170. /**
  10171.  * @fileoverview TimelineView visualizes TRACE_EVENT events using the
  10172.  * tracing.Timeline component and adds in selection summary and control buttons.
  10173.  */
  10174. base.requireStylesheet('timeline_view');
  10175.  
  10176. base.require('timeline');
  10177. base.require('timeline_analysis');
  10178. base.require('timeline_category_filter_dialog');
  10179. base.require('timeline_filter');
  10180. base.require('timeline_find_control');
  10181. base.require('overlay');
  10182. base.require('trace_event_importer');
  10183. base.require('linux_perf_importer');
  10184. base.require('settings');
  10185.  
  10186. base.exportTo('tracing', function() {
  10187.  
  10188.   /**
  10189.    * TimelineView
  10190.    * @constructor
  10191.    * @extends {HTMLDivElement}
  10192.    */
  10193.   var TimelineView = base.ui.define('div');
  10194.  
  10195.   TimelineView.prototype = {
  10196.     __proto__: HTMLDivElement.prototype,
  10197.  
  10198.     decorate: function() {
  10199.       this.classList.add('timeline-view');
  10200.  
  10201.       // Create individual elements.
  10202.       this.titleEl_ = document.createElement('div');
  10203.       this.titleEl_.textContent = 'Tracing: ';
  10204.       this.titleEl_.className = 'title';
  10205.  
  10206.       this.controlDiv_ = document.createElement('div');
  10207.       this.controlDiv_.className = 'control';
  10208.  
  10209.       this.leftControlsEl_ = document.createElement('div');
  10210.       this.leftControlsEl_.className = 'controls';
  10211.       this.rightControlsEl_ = document.createElement('div');
  10212.       this.rightControlsEl_.className = 'controls';
  10213.  
  10214.       var spacingEl = document.createElement('div');
  10215.       spacingEl.className = 'spacer';
  10216.  
  10217.       this.timelineContainer_ = document.createElement('div');
  10218.       this.timelineContainer_.className = 'timeline-container';
  10219.  
  10220.       var analysisContainer_ = document.createElement('div');
  10221.       analysisContainer_.className = 'analysis-container';
  10222.  
  10223.       this.analysisEl_ = new tracing.TimelineAnalysisView();
  10224.  
  10225.       this.dragEl_ = new TimelineDragHandle();
  10226.       this.dragEl_.target = analysisContainer_;
  10227.  
  10228.       this.findCtl_ = new tracing.TimelineFindControl();
  10229.       this.findCtl_.controller = new tracing.TimelineFindController();
  10230.  
  10231.       this.importErrorsButton_ = this.createImportErrorsButton_();
  10232.       this.categoryFilterButton_ = this.createCategoryFilterButton_();
  10233.       this.categoryFilterButton_.callback =
  10234.           this.updateCategoryFilterFromSettings_.bind(this);
  10235.       this.metadataButton_ = this.createMetadataButton_();
  10236.  
  10237.       // Connect everything up.
  10238.       this.rightControls.appendChild(this.importErrorsButton_);
  10239.       this.rightControls.appendChild(this.categoryFilterButton_);
  10240.       this.rightControls.appendChild(this.metadataButton_);
  10241.       this.rightControls.appendChild(this.findCtl_);
  10242.       this.controlDiv_.appendChild(this.titleEl_);
  10243.       this.controlDiv_.appendChild(this.leftControlsEl_);
  10244.       this.controlDiv_.appendChild(spacingEl);
  10245.       this.controlDiv_.appendChild(this.rightControlsEl_);
  10246.       this.appendChild(this.controlDiv_);
  10247.  
  10248.       this.appendChild(this.timelineContainer_);
  10249.       this.appendChild(this.dragEl_);
  10250.  
  10251.       analysisContainer_.appendChild(this.analysisEl_);
  10252.       this.appendChild(analysisContainer_);
  10253.  
  10254.       this.rightControls.appendChild(this.createHelpButton_());
  10255.  
  10256.       // Bookkeeping.
  10257.       this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this);
  10258.       document.addEventListener('keypress', this.onKeypress_.bind(this), true);
  10259.     },
  10260.  
  10261.     createImportErrorsButton_: function() {
  10262.       var dlg = new tracing.Overlay();
  10263.       dlg.classList.add('timeline-view-import-errors-overlay');
  10264.       dlg.autoClose = true;
  10265.  
  10266.       var showEl = document.createElement('div');
  10267.       showEl.className = 'timeline-button timeline-view-import-errors-button' +
  10268.           ' timeline-view-info-button';
  10269.       showEl.textContent = 'Import errors!';
  10270.  
  10271.       var textEl = document.createElement('div');
  10272.       textEl.className = 'info-button-text import-errors-dialog-text';
  10273.  
  10274.       var containerEl = document.createElement('div');
  10275.       containerEl.className = 'info-button-container' +
  10276.           'import-errors-dialog';
  10277.  
  10278.       containerEl.textContent = 'Errors occurred during import:';
  10279.       containerEl.appendChild(textEl);
  10280.       dlg.appendChild(containerEl);
  10281.  
  10282.       var that = this;
  10283.       function onClick() {
  10284.         dlg.visible = true;
  10285.         textEl.textContent = that.model.importErrors.join('\n');
  10286.       }
  10287.       showEl.addEventListener('click', onClick.bind(this));
  10288.  
  10289.       function updateVisibility() {
  10290.         if (that.model &&
  10291.             that.model.importErrors.length)
  10292.           showEl.style.display = '';
  10293.         else
  10294.           showEl.style.display = 'none';
  10295.       }
  10296.       updateVisibility();
  10297.       that.addEventListener('modelChange', updateVisibility);
  10298.  
  10299.       return showEl;
  10300.     },
  10301.  
  10302.     createCategoryFilterButton_: function() {
  10303.       // Set by the embedder of the help button that we create in this function.
  10304.       var callback;
  10305.  
  10306.       var showEl = document.createElement('div');
  10307.       showEl.className = 'timeline-button timeline-view-info-button';
  10308.       showEl.textContent = 'Categories';
  10309.       showEl.__defineSetter__('callback', function(value) {
  10310.         callback = value;
  10311.       });
  10312.  
  10313.  
  10314.       var that = this;
  10315.       function onClick() {
  10316.         var dlg = new tracing.TimelineCategoryFilterDialog();
  10317.         dlg.model = that.model;
  10318.         dlg.settings = that.settings;
  10319.         dlg.settingUpdatedCallback = callback;
  10320.         dlg.visible = true;
  10321.       }
  10322.  
  10323.       function updateVisibility() {
  10324.         if (that.model)
  10325.           showEl.style.display = '';
  10326.         else
  10327.           showEl.style.display = 'none';
  10328.       }
  10329.       updateVisibility();
  10330.       that.addEventListener('modelChange', updateVisibility);
  10331.  
  10332.       showEl.addEventListener('click', onClick.bind(this));
  10333.       return showEl;
  10334.     },
  10335.  
  10336.     createHelpButton_: function() {
  10337.       var dlg = new tracing.Overlay();
  10338.       dlg.classList.add('timeline-view-help-overlay');
  10339.       dlg.autoClose = true;
  10340.       dlg.additionalCloseKeyCodes.push('?'.charCodeAt(0));
  10341.  
  10342.       var showEl = document.createElement('div');
  10343.       showEl.className = 'timeline-button timeline-view-help-button';
  10344.       showEl.textContent = '?';
  10345.  
  10346.       var helpTextEl = document.createElement('div');
  10347.       helpTextEl.style.whiteSpace = 'pre';
  10348.       helpTextEl.style.fontFamily = 'monospace';
  10349.       dlg.appendChild(helpTextEl);
  10350.  
  10351.       function onClick(e) {
  10352.         dlg.visible = true;
  10353.         if (this.timeline_)
  10354.           helpTextEl.textContent = this.timeline_.keyHelp;
  10355.         else
  10356.           helpTextEl.textContent = 'No content loaded. For interesting help,' +
  10357.               ' load something.';
  10358.  
  10359.         // Stop event so it doesn't trigger new click listener on document.
  10360.         e.stopPropagation();
  10361.         return false;
  10362.       }
  10363.  
  10364.       showEl.addEventListener('click', onClick.bind(this));
  10365.  
  10366.       return showEl;
  10367.     },
  10368.  
  10369.     createMetadataButton_: function() {
  10370.       var dlg = new tracing.Overlay();
  10371.       dlg.classList.add('timeline-view-metadata-overlay');
  10372.       dlg.autoClose = true;
  10373.  
  10374.       var showEl = document.createElement('div');
  10375.       showEl.className = 'timeline-button timeline-view-metadata-button' +
  10376.           ' timeline-view-info-button';
  10377.       showEl.textContent = 'Metadata';
  10378.  
  10379.       var textEl = document.createElement('div');
  10380.       textEl.className = 'info-button-text metadata-dialog-text';
  10381.  
  10382.       var containerEl = document.createElement('div');
  10383.       containerEl.className = 'info-button-container metadata-dialog';
  10384.  
  10385.       containerEl.textContent = 'Metadata Info:';
  10386.       containerEl.appendChild(textEl);
  10387.       dlg.appendChild(containerEl);
  10388.  
  10389.       var that = this;
  10390.       function onClick() {
  10391.         dlg.visible = true;
  10392.  
  10393.         var metadataStrings = [];
  10394.  
  10395.         var model = that.model;
  10396.         for (var data in model.metadata) {
  10397.           metadataStrings.push(JSON.stringify(model.metadata[data].name) +
  10398.               ': ' + JSON.stringify(model.metadata[data].value));
  10399.         }
  10400.         textEl.textContent = metadataStrings.join('\n');
  10401.       }
  10402.       showEl.addEventListener('click', onClick.bind(this));
  10403.  
  10404.       function updateVisibility() {
  10405.         if (that.model &&
  10406.             that.model.metadata.length)
  10407.           showEl.style.display = '';
  10408.         else
  10409.           showEl.style.display = 'none';
  10410.       }
  10411.       updateVisibility();
  10412.       that.addEventListener('modelChange', updateVisibility);
  10413.  
  10414.       return showEl;
  10415.     },
  10416.  
  10417.     get leftControls() {
  10418.       return this.leftControlsEl_;
  10419.     },
  10420.  
  10421.     get rightControls() {
  10422.       return this.rightControlsEl_;
  10423.     },
  10424.  
  10425.     get title() {
  10426.       return this.titleEl_.textContent.substring(
  10427.           this.titleEl_.textContent.length - 2);
  10428.     },
  10429.  
  10430.     set title(text) {
  10431.       this.titleEl_.textContent = text + ':';
  10432.     },
  10433.  
  10434.     set traceData(traceData) {
  10435.       this.model = new tracing.TimelineModel(traceData);
  10436.     },
  10437.  
  10438.     get model() {
  10439.       if (this.timeline_)
  10440.         return this.timeline_.model;
  10441.       return undefined;
  10442.     },
  10443.  
  10444.     set model(model) {
  10445.       var modelInstanceChanged = model != this.model;
  10446.       var modelValid = model && model.minTimestamp !== undefined;
  10447.  
  10448.       // Remove old timeline if the model has completely changed.
  10449.       if (modelInstanceChanged) {
  10450.         this.timelineContainer_.textContent = '';
  10451.         if (this.timeline_) {
  10452.           this.timeline_.removeEventListener(
  10453.               'selectionChange', this.onSelectionChangedBoundToThis_);
  10454.           this.timeline_.detach();
  10455.           this.timeline_ = undefined;
  10456.           this.findCtl_.controller.timeline = undefined;
  10457.         }
  10458.       }
  10459.  
  10460.       // Create new timeline if needed.
  10461.       if (modelValid && !this.timeline_) {
  10462.         this.timeline_ = new tracing.Timeline();
  10463.         this.timeline_.focusElement =
  10464.             this.focusElement_ ? this.focusElement_ : this.parentElement;
  10465.         this.timelineContainer_.appendChild(this.timeline_);
  10466.         this.findCtl_.controller.timeline = this.timeline_;
  10467.         this.timeline_.addEventListener(
  10468.             'selectionChange', this.onSelectionChangedBoundToThis_);
  10469.         this.updateCategoryFilterFromSettings_();
  10470.       }
  10471.  
  10472.       // Set the model.
  10473.       if (modelValid)
  10474.         this.timeline_.model = model;
  10475.       base.dispatchSimpleEvent(this, 'modelChange');
  10476.  
  10477.       // Do things that are selection specific
  10478.       if (modelInstanceChanged)
  10479.         this.onSelectionChanged_();
  10480.     },
  10481.  
  10482.     get timeline() {
  10483.       return this.timeline_;
  10484.     },
  10485.  
  10486.     get settings() {
  10487.       if (!this.settings_)
  10488.         this.settings_ = new base.Settings();
  10489.       return this.settings_;
  10490.     },
  10491.  
  10492.     /**
  10493.      * Sets the element whose focus state will determine whether
  10494.      * to respond to keybaord input.
  10495.      */
  10496.     set focusElement(value) {
  10497.       this.focusElement_ = value;
  10498.       if (this.timeline_)
  10499.         this.timeline_.focusElement = value;
  10500.     },
  10501.  
  10502.     /**
  10503.      * @return {Element} The element whose focused state determines
  10504.      * whether to respond to keyboard inputs.
  10505.      * Defaults to the parent element.
  10506.      */
  10507.     get focusElement() {
  10508.       if (this.focusElement_)
  10509.         return this.focusElement_;
  10510.       return this.parentElement;
  10511.     },
  10512.  
  10513.     /**
  10514.      * @return {boolean} Whether the current timeline is attached to the
  10515.      * document.
  10516.      */
  10517.     get isAttachedToDocument_() {
  10518.       var cur = this;
  10519.       while (cur.parentNode)
  10520.         cur = cur.parentNode;
  10521.       return cur == this.ownerDocument;
  10522.     },
  10523.  
  10524.     get listenToKeys_() {
  10525.       if (!this.isAttachedToDocument_)
  10526.         return;
  10527.       if (!this.focusElement_)
  10528.         return true;
  10529.       if (this.focusElement.tabIndex >= 0)
  10530.         return document.activeElement == this.focusElement;
  10531.       return true;
  10532.     },
  10533.  
  10534.     onKeypress_: function(e) {
  10535.       if (!this.listenToKeys_)
  10536.         return;
  10537.  
  10538.       if (event.keyCode == '/'.charCodeAt(0)) { // / key
  10539.         this.findCtl_.focus();
  10540.         event.preventDefault();
  10541.         return;
  10542.       } else if (e.keyCode == '?'.charCodeAt(0)) {
  10543.         this.querySelector('.timeline-view-help-button').click();
  10544.         e.preventDefault();
  10545.       }
  10546.     },
  10547.  
  10548.     beginFind: function() {
  10549.       if (this.findInProgress_)
  10550.         return;
  10551.       this.findInProgress_ = true;
  10552.       var dlg = tracing.TimelineFindControl();
  10553.       dlg.controller = new tracing.TimelineFindController();
  10554.       dlg.controller.timeline = this.timeline;
  10555.       dlg.visible = true;
  10556.       dlg.addEventListener('close', function() {
  10557.         this.findInProgress_ = false;
  10558.       }.bind(this));
  10559.       dlg.addEventListener('findNext', function() {
  10560.       });
  10561.       dlg.addEventListener('findPrevious', function() {
  10562.       });
  10563.     },
  10564.  
  10565.     onSelectionChanged_: function(e) {
  10566.       var oldScrollTop = this.timelineContainer_.scrollTop;
  10567.  
  10568.       var selection = this.timeline_ ?
  10569.           this.timeline_.selection :
  10570.           new tracing.TimelineSelection();
  10571.       this.analysisEl_.selection = selection;
  10572.       this.timelineContainer_.scrollTop = oldScrollTop;
  10573.     },
  10574.  
  10575.     updateCategoryFilterFromSettings_: function() {
  10576.       if (!this.timeline_)
  10577.         return;
  10578.  
  10579.       // Get the disabled categories from settings.
  10580.       var categories = this.settings.keys('categories');
  10581.       var disabledCategories = [];
  10582.       for (var i = 0; i < categories.length; i++) {
  10583.         if (this.settings.get(categories[i], 'true', 'categories') == 'false')
  10584.           disabledCategories.push(categories[i]);
  10585.       }
  10586.  
  10587.       this.timeline_.categoryFilter =
  10588.           new tracing.TimelineCategoryFilter(disabledCategories);
  10589.     }
  10590.   };
  10591.  
  10592.   /**
  10593.    * Timeline Drag Handle
  10594.    * Detects when user clicks handle determines new height of container based
  10595.    * on user's vertical mouse move and resizes the target.
  10596.    * @constructor
  10597.    * @extends {HTMLDivElement}
  10598.    * You will need to set target to be the draggable element
  10599.    */
  10600.   var TimelineDragHandle = base.ui.define('div');
  10601.  
  10602.   TimelineDragHandle.prototype = {
  10603.     __proto__: HTMLDivElement.prototype,
  10604.  
  10605.     decorate: function() {
  10606.       this.className = 'timeline-drag-handle';
  10607.       this.lastMousePosY = 0;
  10608.       this.dragAnalysis = this.dragAnalysis.bind(this);
  10609.       this.onMouseUp = this.onMouseUp.bind(this);
  10610.       this.addEventListener('mousedown', this.onMouseDown);
  10611.     },
  10612.  
  10613.     dragAnalysis: function(e) {
  10614.       // Compute the difference in height position.
  10615.       var dy = this.lastMousePosY - e.clientY;
  10616.       // If style is not set, start off with computed height.
  10617.       if (!this.target.style.height)
  10618.         this.target.style.height = window.getComputedStyle(this.target).height;
  10619.       // Calculate new height of the container.
  10620.       this.target.style.height = parseInt(this.target.style.height) + dy + 'px';
  10621.       this.lastMousePosY = e.clientY;
  10622.     },
  10623.  
  10624.     onMouseDown: function(e) {
  10625.       this.lastMousePosY = e.clientY;
  10626.       document.addEventListener('mousemove', this.dragAnalysis);
  10627.       document.addEventListener('mouseup', this.onMouseUp);
  10628.       e.stopPropagation();
  10629.       return false;
  10630.     },
  10631.  
  10632.     onMouseUp: function(e) {
  10633.       document.removeEventListener('mousemove', this.dragAnalysis);
  10634.       document.removeEventListener('mouseup', this.onMouseUp);
  10635.     }
  10636.   };
  10637.  
  10638.   return {
  10639.     TimelineView: TimelineView
  10640.   };
  10641. });
  10642.  
  10643. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10644. // Use of this source code is governed by a BSD-style license that can be
  10645. // found in the LICENSE file.
  10646.  
  10647.  
  10648. /**
  10649.  * @fileoverview State and UI for trace data collection.
  10650.  */
  10651. base.requireStylesheet('tracing_controller');
  10652. base.require('event_target');
  10653. base.exportTo('tracing', function() {
  10654.  
  10655.   /**
  10656.    * The tracing controller is responsible for talking to tracing_ui.cc in
  10657.    * chrome
  10658.    * @constructor
  10659.    * @param {function(String, opt_Array.<String>} Function to be used to send
  10660.    * data to chrome.
  10661.    */
  10662.   function TracingController(sendFn) {
  10663.     this.sendFn_ = sendFn;
  10664.     this.overlay_ = document.createElement('div');
  10665.     this.overlay_.className = 'tracing-overlay';
  10666.  
  10667.     base.ui.decorate(this.overlay_, tracing.Overlay);
  10668.  
  10669.     this.statusDiv_ = document.createElement('div');
  10670.     this.overlay_.appendChild(this.statusDiv_);
  10671.  
  10672.     this.bufferPercentDiv_ = document.createElement('div');
  10673.     this.overlay_.appendChild(this.bufferPercentDiv_);
  10674.  
  10675.     this.stopButton_ = document.createElement('button');
  10676.     this.stopButton_.onclick = this.endTracing.bind(this);
  10677.     this.stopButton_.textContent = 'Stop tracing';
  10678.     this.overlay_.appendChild(this.stopButton_);
  10679.  
  10680.     this.traceEvents_ = [];
  10681.     this.systemTraceEvents_ = [];
  10682.  
  10683.     this.onKeydownBoundToThis_ = this.onKeydown_.bind(this);
  10684.     this.onKeypressBoundToThis_ = this.onKeypress_.bind(this);
  10685.  
  10686.     this.supportsSystemTracing_ = base.isChromeOS;
  10687.  
  10688.     if (this.sendFn_)
  10689.       this.sendFn_('tracingControllerInitialized');
  10690.   }
  10691.  
  10692.   TracingController.prototype = {
  10693.     __proto__: base.EventTarget.prototype,
  10694.  
  10695.     gpuInfo_: undefined,
  10696.     clientInfo_: undefined,
  10697.     tracingEnabled_: false,
  10698.     tracingEnding_: false,
  10699.     systemTraceDataFilename_: undefined,
  10700.  
  10701.     get supportsSystemTracing() {
  10702.       return this.supportsSystemTracing_;
  10703.     },
  10704.  
  10705.     onRequestBufferPercentFullComplete: function(percent_full) {
  10706.       if (!this.overlay_.visible)
  10707.         return;
  10708.  
  10709.       window.setTimeout(this.beginRequestBufferPercentFull_.bind(this), 250);
  10710.  
  10711.       this.bufferPercentDiv_.textContent = 'Buffer usage: ' +
  10712.           Math.round(100 * percent_full) + '%';
  10713.     },
  10714.  
  10715.     /**
  10716.      * Begin requesting the buffer fullness
  10717.      */
  10718.     beginRequestBufferPercentFull_: function() {
  10719.       this.sendFn_('beginRequestBufferPercentFull');
  10720.     },
  10721.  
  10722.     /**
  10723.      * Called by info_view to empty the trace buffer
  10724.      *
  10725.      * |opt_trace_categories| is a comma-delimited list of category wildcards.
  10726.      * A category can have an optional '-' prefix to make it an excluded
  10727.      * category.  All the same rules apply above, so for example, having both
  10728.      * included and excluded categories in the same list would not be
  10729.      * supported.
  10730.      *
  10731.      * Example: beginTracing("test_MyTest*");
  10732.      * Example: beginTracing("test_MyTest*,test_OtherStuff");
  10733.      * Example: beginTracing("-excluded_category1,-excluded_category2");
  10734.      */
  10735.     beginTracing: function(opt_systemTracingEnabled, opt_trace_categories) {
  10736.       if (this.tracingEnabled_)
  10737.         throw new Error('Tracing already begun.');
  10738.  
  10739.       this.stopButton_.hidden = false;
  10740.       this.statusDiv_.textContent = 'Tracing active.';
  10741.       this.overlay_.visible = true;
  10742.       this.overlay_.defaultClickShouldClose = false;
  10743.  
  10744.       this.tracingEnabled_ = true;
  10745.  
  10746.       console.log('Beginning to trace...');
  10747.       this.statusDiv_.textContent = 'Tracing active.';
  10748.  
  10749.       this.traceEvents_ = [];
  10750.       this.systemTraceEvents_ = [];
  10751.       this.sendFn_(
  10752.           'beginTracing',
  10753.           [
  10754.            opt_systemTracingEnabled || false,
  10755.            opt_trace_categories || '-test_*'
  10756.           ]
  10757.       );
  10758.       this.beginRequestBufferPercentFull_();
  10759.  
  10760.       var e = new base.Event('traceBegun');
  10761.       e.events = this.traceEvents_;
  10762.       this.dispatchEvent(e);
  10763.  
  10764.       e = new base.Event('traceEventsChanged');
  10765.       e.numEvents = this.traceEvents_.length;
  10766.       this.dispatchEvent(e);
  10767.  
  10768.       window.addEventListener('keypress', this.onKeypressBoundToThis_);
  10769.       window.addEventListener('keydown', this.onKeydownBoundToThis_);
  10770.     },
  10771.  
  10772.     onKeydown_: function(e) {
  10773.       if (e.keyCode == 27) {
  10774.         this.endTracing();
  10775.       }
  10776.     },
  10777.  
  10778.     onKeypress_: function(e) {
  10779.       if (e.keyIdentifier == 'Enter') {
  10780.         this.endTracing();
  10781.       }
  10782.     },
  10783.  
  10784.     /**
  10785.      * Called from gpu c++ code when ClientInfo is updated.
  10786.      */
  10787.     onClientInfoUpdate: function(clientInfo) {
  10788.       this.clientInfo_ = clientInfo;
  10789.     },
  10790.  
  10791.     /**
  10792.      * Called from gpu c++ code when GPU Info is updated.
  10793.      */
  10794.     onGpuInfoUpdate: function(gpuInfo) {
  10795.       this.gpuInfo_ = gpuInfo;
  10796.     },
  10797.  
  10798.     /**
  10799.      * Checks whether tracing is enabled
  10800.      */
  10801.     get isTracingEnabled() {
  10802.       return this.tracingEnabled_;
  10803.     },
  10804.  
  10805.     /**
  10806.      * Gets the currently traced events. If tracing is active, then
  10807.      * this can change on the fly.
  10808.      */
  10809.     get traceEvents() {
  10810.       return this.traceEvents_;
  10811.     },
  10812.  
  10813.     /**
  10814.      * Called by tracing c++ code when new trace data arrives.
  10815.      */
  10816.     onTraceDataCollected: function(events) {
  10817.       this.statusDiv_.textContent = 'Processing trace...';
  10818.       this.traceEvents_.push.apply(this.traceEvents_, events);
  10819.     },
  10820.  
  10821.     /**
  10822.      * Called to finish tracing and update all views.
  10823.      */
  10824.     endTracing: function() {
  10825.       if (!this.tracingEnabled_) throw new Error('Tracing not begun.');
  10826.       if (this.tracingEnding_) return;
  10827.       this.tracingEnding_ = true;
  10828.  
  10829.       this.statusDiv_.textContent = 'Ending trace...';
  10830.       console.log('Finishing trace');
  10831.       this.statusDiv_.textContent = 'Downloading trace data...';
  10832.       this.stopButton_.hidden = true;
  10833.       // delay sending endTracingAsync until we get a chance to
  10834.       // update the screen...
  10835.       var that = this;
  10836.       window.setTimeout(function() {
  10837.         that.sendFn_('endTracingAsync');
  10838.       }, 100);
  10839.     },
  10840.  
  10841.     /**
  10842.      * Called by the browser when all processes complete tracing.
  10843.      */
  10844.     onEndTracingComplete: function() {
  10845.       window.removeEventListener('keydown', this.onKeydownBoundToThis_);
  10846.       window.removeEventListener('keypress', this.onKeypressBoundToThis_);
  10847.       this.overlay_.visible = false;
  10848.       this.tracingEnabled_ = false;
  10849.       this.tracingEnding_ = false;
  10850.       console.log('onEndTracingComplete p1 with ' +
  10851.                   this.traceEvents_.length + ' events.');
  10852.       var e = new base.Event('traceEnded');
  10853.       e.events = this.traceEvents_;
  10854.       this.dispatchEvent(e);
  10855.     },
  10856.  
  10857.     /**
  10858.      * Called by tracing c++ code when new system trace data arrives.
  10859.      */
  10860.     onSystemTraceDataCollected: function(events) {
  10861.       console.log('onSystemTraceDataCollected with ' +
  10862.                   events.length + ' chars of data.');
  10863.       this.systemTraceEvents_ = events;
  10864.     },
  10865.  
  10866.     /**
  10867.      * Gets the currentl system trace events. If tracing is active, then
  10868.      * this can change on the fly.
  10869.      */
  10870.     get systemTraceEvents() {
  10871.       return this.systemTraceEvents_;
  10872.     },
  10873.  
  10874.     /**
  10875.      * Tells browser to put up a load dialog and load the trace file
  10876.      */
  10877.     beginLoadTraceFile: function() {
  10878.       this.sendFn_('loadTraceFile');
  10879.     },
  10880.  
  10881.     /**
  10882.      * Called by the browser when a trace file is loaded.
  10883.      */
  10884.     onLoadTraceFileComplete: function(data) {
  10885.       if (data.traceEvents) {
  10886.         this.traceEvents_ = data.traceEvents;
  10887.       } else { // path for loading traces saved without metadata
  10888.         if (!data.length)
  10889.           console.log('Expected an array when loading the trace file');
  10890.         else
  10891.           this.traceEvents_ = data;
  10892.       }
  10893.  
  10894.       if (data.systemTraceEvents)
  10895.         this.systemTraceEvents_ = data.systemTraceEvents;
  10896.       else
  10897.         this.systemTraceEvents_ = [];
  10898.  
  10899.       var e = new base.Event('loadTraceFileComplete');
  10900.       e.events = this.traceEvents_;
  10901.       this.dispatchEvent(e);
  10902.     },
  10903.  
  10904.     /**
  10905.      * Called by the browser when loading a trace file was canceled.
  10906.      */
  10907.     onLoadTraceFileCanceled: function() {
  10908.       base.dispatchSimpleEvent(this, 'loadTraceFileCanceled');
  10909.     },
  10910.  
  10911.     /**
  10912.      * Tells browser to put up a save dialog and save the trace file
  10913.      */
  10914.     beginSaveTraceFile: function(traceEvents, systemTraceEvents) {
  10915.       var data = {
  10916.         traceEvents: this.traceEvents_,
  10917.         systemTraceEvents: this.systemTraceEvents_,
  10918.         clientInfo: this.clientInfo_,
  10919.         gpuInfo: this.gpuInfo_
  10920.       };
  10921.       this.sendFn_('saveTraceFile', [JSON.stringify(data)]);
  10922.     },
  10923.  
  10924.     /**
  10925.      * Called by the browser when a trace file is saveed.
  10926.      */
  10927.     onSaveTraceFileComplete: function() {
  10928.       base.dispatchSimpleEvent(this, 'saveTraceFileComplete');
  10929.     },
  10930.  
  10931.     /**
  10932.      * Called by the browser when saving a trace file was canceled.
  10933.      */
  10934.     onSaveTraceFileCanceled: function() {
  10935.       base.dispatchSimpleEvent(this, 'saveTraceFileCanceled');
  10936.     }
  10937.   };
  10938.   return {
  10939.     TracingController: TracingController
  10940.   };
  10941. });
  10942.  
  10943. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  10944. // Use of this source code is governed by a BSD-style license that can be
  10945. // found in the LICENSE file.
  10946.  
  10947. 'use strict';
  10948.  
  10949. /**
  10950.  * @fileoverview ProfilingView glues the TimelineView control to
  10951.  * TracingController.
  10952.  */
  10953. base.requireStylesheet('profiling_view');
  10954. base.require('timeline_view');
  10955. base.require('tracing_controller');
  10956. base.exportTo('tracing', function() {
  10957.   /**
  10958.    * ProfilingView
  10959.    * @constructor
  10960.    * @extends {HTMLDivElement}
  10961.    */
  10962.   var ProfilingView = base.ui.define('div');
  10963.  
  10964.   ProfilingView.prototype = {
  10965.     __proto__: HTMLDivElement.prototype,
  10966.  
  10967.     traceEvents_: [],
  10968.     systemTraceEvents_: [],
  10969.  
  10970.     decorate: function() {
  10971.       this.classList.add('profiling-view');
  10972.  
  10973.       // make the <list>/add/save/record element
  10974.       this.recordBn_ = document.createElement('button');
  10975.       this.recordBn_.className = 'record';
  10976.       this.recordBn_.textContent = 'Record';
  10977.       this.recordBn_.addEventListener('click', this.onRecord_.bind(this));
  10978.  
  10979.       this.saveBn_ = document.createElement('button');
  10980.       this.saveBn_.textContent = 'Save';
  10981.       this.saveBn_.addEventListener('click', this.onSave_.bind(this));
  10982.  
  10983.       this.loadBn_ = document.createElement('button');
  10984.       this.loadBn_.textContent = 'Load';
  10985.       this.loadBn_.addEventListener('click', this.onLoad_.bind(this));
  10986.  
  10987.       this.systemTracingBn_ = document.createElement('input');
  10988.       this.systemTracingBn_.type = 'checkbox';
  10989.       this.systemTracingBn_.checked = false;
  10990.  
  10991.       this.systemTracingLabelEl_ = document.createElement('label');
  10992.       this.systemTracingLabelEl_.textContent = 'System events';
  10993.       this.systemTracingLabelEl_.appendChild(this.systemTracingBn_);
  10994.       this.systemTracingLabelEl_.style.display = 'none';
  10995.       this.systemTracingLabelEl_.style.marginLeft = '16px';
  10996.  
  10997.       this.timelineView_ = new tracing.TimelineView();
  10998.       this.timelineView_.leftControls.appendChild(this.recordBn_);
  10999.       this.timelineView_.leftControls.appendChild(this.saveBn_);
  11000.       this.timelineView_.leftControls.appendChild(this.loadBn_);
  11001.       this.timelineView_.leftControls.appendChild(this.systemTracingLabelEl_);
  11002.  
  11003.       this.appendChild(this.timelineView_);
  11004.  
  11005.       document.addEventListener('keypress', this.onKeypress_.bind(this));
  11006.  
  11007.       this.refresh_();
  11008.     },
  11009.  
  11010.     didSetTracingController_: function(value, oldValue) {
  11011.       if (oldValue)
  11012.         throw new Error('Can only set tracing controller once.');
  11013.  
  11014.       if (this.tracingController_.supportsSystemTracing) {
  11015.         this.systemTracingLabelEl_.style.display = 'block';
  11016.         this.systemTracingBn_.checked = true;
  11017.       } else {
  11018.         this.systemTracingLabelEl_.style.display = 'none';
  11019.       }
  11020.  
  11021.       this.refresh_();
  11022.     },
  11023.  
  11024.     refresh_: function() {
  11025.       if (!this.tracingController_)
  11026.         return;
  11027.  
  11028.       var traceEvents = this.tracingController_.traceEvents;
  11029.       var hasEvents = traceEvents && traceEvents.length;
  11030.  
  11031.       this.saveBn_.disabled = !hasEvents;
  11032.  
  11033.       if (!hasEvents) return;
  11034.  
  11035.       var traces = [traceEvents];
  11036.       if (this.tracingController_.systemTraceEvents.length)
  11037.         traces.push(this.tracingController_.systemTraceEvents);
  11038.  
  11039.       var m = new tracing.TimelineModel();
  11040.       m.importTraces(traces, true);
  11041.       this.timelineView_.model = m;
  11042.     },
  11043.  
  11044.     onKeypress_: function(event) {
  11045.       if (event.keyCode == 114 && !this.tracingController_.isTracingEnabled &&
  11046.           document.activeElement.nodeName != 'INPUT') {
  11047.         this.onRecord_();
  11048.       }
  11049.     },
  11050.  
  11051.     get timelineView() {
  11052.       return this.timelineView_;
  11053.     },
  11054.  
  11055.     ///////////////////////////////////////////////////////////////////////////
  11056.  
  11057.     onRecord_: function() {
  11058.       var that = this;
  11059.       var tc = this.tracingController_;
  11060.       tc.beginTracing(this.systemTracingBn_.checked);
  11061.       function response() {
  11062.         that.refresh_();
  11063.         setTimeout(function() {
  11064.           tc.removeEventListener('traceEnded', response);
  11065.         }, 0);
  11066.       }
  11067.       tc.addEventListener('traceEnded', response);
  11068.     },
  11069.  
  11070.     ///////////////////////////////////////////////////////////////////////////
  11071.  
  11072.     onSave_: function() {
  11073.       this.overlayEl_ = new tracing.Overlay();
  11074.       this.overlayEl_.className = 'profiling-overlay';
  11075.  
  11076.       var labelEl = document.createElement('div');
  11077.       labelEl.className = 'label';
  11078.       labelEl.textContent = 'Saving...';
  11079.       this.overlayEl_.appendChild(labelEl);
  11080.       this.overlayEl_.visible = true;
  11081.  
  11082.       var that = this;
  11083.       var tc = this.tracingController_;
  11084.       function response() {
  11085.         that.overlayEl_.visible = false;
  11086.         that.overlayEl_ = undefined;
  11087.         setTimeout(function() {
  11088.           tc.removeEventListener('saveTraceFileComplete', response);
  11089.           tc.removeEventListener('saveTraceFileCanceled', response);
  11090.         }, 0);
  11091.       }
  11092.       tc.addEventListener('saveTraceFileComplete', response);
  11093.       tc.addEventListener('saveTraceFileCanceled', response);
  11094.       tc.beginSaveTraceFile();
  11095.     },
  11096.  
  11097.     ///////////////////////////////////////////////////////////////////////////
  11098.  
  11099.     onLoad_: function() {
  11100.       this.overlayEl_ = new tracing.Overlay();
  11101.       this.overlayEl_.className = 'profiling-overlay';
  11102.  
  11103.       var labelEl = document.createElement('div');
  11104.       labelEl.className = 'label';
  11105.       labelEl.textContent = 'Loading...';
  11106.       this.overlayEl_.appendChild(labelEl);
  11107.       this.overlayEl_.visible = true;
  11108.  
  11109.       var that = this;
  11110.       var tc = this.tracingController_;
  11111.       this.tracingController_.beginLoadTraceFile();
  11112.       function response(e) {
  11113.         that.overlayEl_.visible = false;
  11114.         that.overlayEl_ = undefined;
  11115.         if (e.type == 'loadTraceFileComplete')
  11116.           that.refresh_();
  11117.         setTimeout(function() {
  11118.           tc.removeEventListener('loadTraceFileComplete', response);
  11119.           tc.removeEventListener('loadTraceFileCanceled', response);
  11120.         }, 0);
  11121.       }
  11122.  
  11123.       tc.addEventListener('loadTraceFileComplete', response);
  11124.       tc.addEventListener('loadTraceFileCanceled', response);
  11125.     }
  11126.   };
  11127.  
  11128.   base.defineProperty(ProfilingView, 'tracingController', base.PropertyKind.JS,
  11129.       ProfilingView.prototype.didSetTracingController_);
  11130.  
  11131.   return {
  11132.     ProfilingView: ProfilingView
  11133.   };
  11134. });
  11135.  
  11136.  
  11137.  
  11138. var tracingController;
  11139. var profilingView;  // Made global for debugging purposes only.
  11140.  
  11141. document.addEventListener('DOMContentLoaded', function() {
  11142.   tracingController = new tracing.TracingController(
  11143.       chrome.send.bind(chrome));
  11144.  
  11145.   profilingView = document.body.querySelector('#profiling-view');
  11146.   base.ui.decorate(profilingView, tracing.ProfilingView);
  11147.   profilingView.tracingController = tracingController;
  11148. });
  11149.