update minified code and generator script

Change-Id: I3b94a13c9ba26a8e0e97f51ebd591eaf6a0e892a
diff --git a/script.js b/script.js
index 993791c..5aaeff1 100644
--- a/script.js
+++ b/script.js
@@ -1,4762 +1,159 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const cr = (function() {
-
-  /**
-   * Whether we are using a Mac or not.
-   * @type {boolean}
-   */
-  const isMac = /Mac/.test(navigator.platform);
-
-  /**
-   * Whether this is on the Windows platform or not.
-   * @type {boolean}
-   */
-  const isWindows = /Win/.test(navigator.platform);
-
-  /**
-   * Whether this is on chromeOS or not.
-   * @type {boolean}
-   */
-  const isChromeOS = /CrOS/.test(navigator.userAgent);
-
-  /**
-   * Whether this is on vanilla Linux (not chromeOS).
-   * @type {boolean}
-   */
-  const isLinux = /Linux/.test(navigator.userAgent);
-
-  /**
-   * Whether this uses GTK or not.
-   * @type {boolean}
-   */
-  const isGTK = /GTK/.test(chrome.toolkit);
-
-  /**
-   * Whether this uses the views toolkit or not.
-   * @type {boolean}
-   */
-  const isViews = /views/.test(chrome.toolkit);
-
-  /**
-   * Whether this window is optimized for touch-based input.
-   * @type {boolean}
-   */
-  const isTouchOptimized = !!chrome.touchOptimized;
-
-  /**
-   * Sets the os and toolkit attributes in the <html> element so that platform
-   * specific css rules can be applied.
-   */
-  function enablePlatformSpecificCSSRules() {
-    if (isMac)
-      doc.documentElement.setAttribute('os', 'mac');
-    if (isWindows)
-      doc.documentElement.setAttribute('os', 'windows');
-    if (isChromeOS)
-      doc.documentElement.setAttribute('os', 'chromeos');
-    if (isLinux)
-      doc.documentElement.setAttribute('os', 'linux');
-    if (isGTK)
-      doc.documentElement.setAttribute('toolkit', 'gtk');
-    if (isViews)
-      doc.documentElement.setAttribute('toolkit', 'views');
-    if (isTouchOptimized)
-      doc.documentElement.setAttribute('touch-optimized', '');
-  }
-
-  /**
-   * Builds an object structure for the provided namespace path,
-   * ensuring that names that already exist are not overwritten. For
-   * example:
-   * "a.b.c" -> a = {};a.b={};a.b.c={};
-   * @param {string} name Name of the object that this file defines.
-   * @param {*=} opt_object The object to expose at the end of the path.
-   * @param {Object=} opt_objectToExportTo The object to add the path to;
-   *     default is {@code window}.
-   * @private
-   */
-  function exportPath(name, opt_object, opt_objectToExportTo) {
-    var parts = name.split('.');
-    var cur = opt_objectToExportTo || window /* global */;
-
-    for (var part; parts.length && (part = parts.shift());) {
-      if (!parts.length && opt_object !== undefined) {
-        // last part and we have an object; use it
-        cur[part] = opt_object;
-      } else if (part in cur) {
-        cur = cur[part];
-      } else {
-        cur = cur[part] = {};
-      }
-    }
-    return cur;
-  };
-
-  // cr.Event is called CrEvent in here to prevent naming conflicts. We also
-  // store the original Event in case someone does a global alias of cr.Event.
-  const DomEvent = Event;
-
-  /**
-   * Creates a new event to be used with cr.EventTarget or DOM EventTarget
-   * objects.
-   * @param {string} type The name of the event.
-   * @param {boolean=} opt_bubbles Whether the event bubbles. Default is false.
-   * @param {boolean=} opt_preventable Whether the default action of the event
-   *     can be prevented.
-   * @constructor
-   * @extends {DomEvent}
-   */
-  function CrEvent(type, opt_bubbles, opt_preventable) {
-    var e = cr.doc.createEvent('Event');
-    e.initEvent(type, !!opt_bubbles, !!opt_preventable);
-    e.__proto__ = CrEvent.prototype;
-    return e;
-  }
-
-  CrEvent.prototype = {
-    __proto__: DomEvent.prototype
-  };
-
-  /**
-   * Fires a property change event on the target.
-   * @param {EventTarget} target The target to dispatch the event on.
-   * @param {string} propertyName The name of the property that changed.
-   * @param {*} newValue The new value for the property.
-   * @param {*} oldValue The old value for the property.
-   */
-  function dispatchPropertyChange(target, propertyName, newValue, oldValue) {
-    var e = new CrEvent(propertyName + 'Change');
-    e.propertyName = propertyName;
-    e.newValue = newValue;
-    e.oldValue = oldValue;
-    target.dispatchEvent(e);
-  }
-
-  /**
-   * Converts a camelCase javascript property name to a hyphenated-lower-case
-   * attribute name.
-   * @param {string} jsName The javascript camelCase property name.
-   * @return {string} The equivalent hyphenated-lower-case attribute name.
-   */
-  function getAttributeName(jsName) {
-    return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
-  }
-
-  /**
-   * The kind of property to define in {@code defineProperty}.
-   * @enum {number}
-   */
-  const PropertyKind = {
-    /**
-     * Plain old JS property where the backing data is stored as a "private"
-     * field on the object.
-     */
-    JS: 'js',
-
-    /**
-     * The property backing data is stored as an attribute on an element.
-     */
-    ATTR: 'attr',
-
-    /**
-     * The property backing data is stored as an attribute on an element. If the
-     * element has the attribute then the value is true.
-     */
-    BOOL_ATTR: 'boolAttr'
-  };
-
-  /**
-   * Helper function for defineProperty that returns the getter to use for the
-   * property.
-   * @param {string} name
-   * @param {cr.PropertyKind} kind
-   * @return {function():*} The getter for the property.
-   */
-  function getGetter(name, kind) {
-    switch (kind) {
-      case PropertyKind.JS:
-        var privateName = name + '_';
-        return function() {
-          return this[privateName];
-        };
-      case PropertyKind.ATTR:
-        var attributeName = getAttributeName(name);
-        return function() {
-          return this.getAttribute(attributeName);
-        };
-      case PropertyKind.BOOL_ATTR:
-        var attributeName = getAttributeName(name);
-        return function() {
-          return this.hasAttribute(attributeName);
-        };
-    }
-  }
-
-  /**
-   * Helper function for defineProperty that returns the setter of the right
-   * kind.
-   * @param {string} name The name of the property we are defining the setter
-   *     for.
-   * @param {cr.PropertyKind} kind The kind of property we are getting the
-   *     setter for.
-   * @param {function(*):void} opt_setHook A function to run after the property
-   *     is set, but before the propertyChange event is fired.
-   * @return {function(*):void} The function to use as a setter.
-   */
-  function getSetter(name, kind, opt_setHook) {
-    switch (kind) {
-      case PropertyKind.JS:
-        var privateName = name + '_';
-        return function(value) {
-          var oldValue = this[privateName];
-          if (value !== oldValue) {
-            this[privateName] = value;
-            if (opt_setHook)
-              opt_setHook.call(this, value, oldValue);
-            dispatchPropertyChange(this, name, value, oldValue);
-          }
-        };
-
-      case PropertyKind.ATTR:
-        var attributeName = getAttributeName(name);
-        return function(value) {
-          var oldValue = this[attributeName];
-          if (value !== oldValue) {
-            if (value == undefined)
-              this.removeAttribute(attributeName);
-            else
-              this.setAttribute(attributeName, value);
-            if (opt_setHook)
-              opt_setHook.call(this, value, oldValue);
-            dispatchPropertyChange(this, name, value, oldValue);
-          }
-        };
-
-      case PropertyKind.BOOL_ATTR:
-        var attributeName = getAttributeName(name);
-        return function(value) {
-          var oldValue = this[attributeName];
-          if (value !== oldValue) {
-            if (value)
-              this.setAttribute(attributeName, name);
-            else
-              this.removeAttribute(attributeName);
-            if (opt_setHook)
-              opt_setHook.call(this, value, oldValue);
-            dispatchPropertyChange(this, name, value, oldValue);
-          }
-        };
-    }
-  }
-
-  /**
-   * Defines a property on an object. When the setter changes the value a
-   * property change event with the type {@code name + 'Change'} is fired.
-   * @param {!Object} obj The object to define the property for.
-   * @param {string} name The name of the property.
-   * @param {cr.PropertyKind=} opt_kind What kind of underlying storage to use.
-   * @param {function(*):void} opt_setHook A function to run after the
-   *     property is set, but before the propertyChange event is fired.
-   */
-  function defineProperty(obj, name, opt_kind, opt_setHook) {
-    if (typeof obj == 'function')
-      obj = obj.prototype;
-
-    var kind = opt_kind || PropertyKind.JS;
-
-    if (!obj.__lookupGetter__(name)) {
-      obj.__defineGetter__(name, getGetter(name, kind));
-    }
-
-    if (!obj.__lookupSetter__(name)) {
-      obj.__defineSetter__(name, getSetter(name, kind, opt_setHook));
-    }
-  }
-
-  /**
-   * Counter for use with createUid
-   */
-  var uidCounter = 1;
-
-  /**
-   * @return {number} A new unique ID.
-   */
-  function createUid() {
-    return uidCounter++;
-  }
-
-  /**
-   * Returns a unique ID for the item. This mutates the item so it needs to be
-   * an object
-   * @param {!Object} item The item to get the unique ID for.
-   * @return {number} The unique ID for the item.
-   */
-  function getUid(item) {
-    if (item.hasOwnProperty('uid'))
-      return item.uid;
-    return item.uid = createUid();
-  }
-
-  /**
-   * Dispatches a simple event on an event target.
-   * @param {!EventTarget} target The event target to dispatch the event on.
-   * @param {string} type The type of the event.
-   * @param {boolean=} opt_bubbles Whether the event bubbles or not.
-   * @param {boolean=} opt_cancelable Whether the default action of the event
-   *     can be prevented.
-   * @return {boolean} If any of the listeners called {@code preventDefault}
-   *     during the dispatch this will return false.
-   */
-  function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
-    var e = new cr.Event(type, opt_bubbles, opt_cancelable);
-    return target.dispatchEvent(e);
-  }
-
-  /**
-   * @param {string} name
-   * @param {!Function} fun
-   */
-  function define(name, fun) {
-    var obj = exportPath(name);
-    var exports = fun();
-    for (var propertyName in exports) {
-      // Maybe we should check the prototype chain here? The current usage
-      // pattern is always using an object literal so we only care about own
-      // properties.
-      var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
-                                                               propertyName);
-      if (propertyDescriptor)
-        Object.defineProperty(obj, propertyName, propertyDescriptor);
-    }
-  }
-
-  /**
-   * Document used for various document related operations.
-   * @type {!Document}
-   */
-  var doc = document;
-
-
-  /**
-   * Allows you to run func in the context of a different document.
-   * @param {!Document} document The document to use.
-   * @param {function():*} func The function to call.
-   */
-  function withDoc(document, func) {
-    var oldDoc = doc;
-    doc = document;
-    try {
-      func();
-    } finally {
-      doc = oldDoc;
-    }
-  }
-
-  /**
-   * Adds a {@code getInstance} static method that always return the same
-   * instance object.
-   * @param {!Function} ctor The constructor for the class to add the static
-   *     method to.
-   */
-  function addSingletonGetter(ctor) {
-    ctor.getInstance = function() {
-      return ctor.instance_ || (ctor.instance_ = new ctor());
-    };
-  }
-
-  return {
-    addSingletonGetter: addSingletonGetter,
-    isChromeOS: isChromeOS,
-    isMac: isMac,
-    isWindows: isWindows,
-    isLinux: isLinux,
-    isViews: isViews,
-    isTouchOptimized: isTouchOptimized,
-    enablePlatformSpecificCSSRules: enablePlatformSpecificCSSRules,
-    define: define,
-    defineProperty: defineProperty,
-    PropertyKind: PropertyKind,
-    createUid: createUid,
-    getUid: getUid,
-    dispatchSimpleEvent: dispatchSimpleEvent,
-    dispatchPropertyChange: dispatchPropertyChange,
-
-    /**
-     * The document that we are currently using.
-     * @type {!Document}
-     */
-    get doc() {
-      return doc;
-    },
-    withDoc: withDoc,
-    Event: CrEvent
-  };
-})();
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview This contains an implementation of the EventTarget interface
- * as defined by DOM Level 2 Events.
- */
-
-cr.define('cr', function() {
-
-  /**
-   * Creates a new EventTarget. This class implements the DOM level 2
-   * EventTarget interface and can be used wherever those are used.
-   * @constructor
-   */
-  function EventTarget() {
-  }
-
-  EventTarget.prototype = {
-
-    /**
-     * Adds an event listener to the target.
-     * @param {string} type The name of the event.
-     * @param {!Function|{handleEvent:Function}} handler The handler for the
-     *     event. This is called when the event is dispatched.
-     */
-    addEventListener: function(type, handler) {
-      if (!this.listeners_)
-        this.listeners_ = Object.create(null);
-      if (!(type in this.listeners_)) {
-        this.listeners_[type] = [handler];
-      } else {
-        var handlers = this.listeners_[type];
-        if (handlers.indexOf(handler) < 0)
-          handlers.push(handler);
-      }
-    },
-
-    /**
-     * Removes an event listener from the target.
-     * @param {string} type The name of the event.
-     * @param {!Function|{handleEvent:Function}} handler The handler for the
-     *     event.
-     */
-    removeEventListener: function(type, handler) {
-      if (!this.listeners_)
-        return;
-      if (type in this.listeners_) {
-        var handlers = this.listeners_[type];
-        var index = handlers.indexOf(handler);
-        if (index >= 0) {
-          // Clean up if this was the last listener.
-          if (handlers.length == 1)
-            delete this.listeners_[type];
-          else
-            handlers.splice(index, 1);
-        }
-      }
-    },
-
-    /**
-     * Dispatches an event and calls all the listeners that are listening to
-     * the type of the event.
-     * @param {!cr.event.Event} event The event to dispatch.
-     * @return {boolean} Whether the default action was prevented. If someone
-     *     calls preventDefault on the event object then this returns false.
-     */
-    dispatchEvent: function(event) {
-      if (!this.listeners_)
-        return true;
-
-      // Since we are using DOM Event objects we need to override some of the
-      // properties and methods so that we can emulate this correctly.
-      var self = this;
-      event.__defineGetter__('target', function() {
-        return self;
-      });
-      event.preventDefault = function() {
-        this.returnValue = false;
-      };
-
-      var type = event.type;
-      var prevented = 0;
-      if (type in this.listeners_) {
-        // Clone to prevent removal during dispatch
-        var handlers = this.listeners_[type].concat();
-        for (var i = 0, handler; handler = handlers[i]; i++) {
-          if (handler.handleEvent)
-            prevented |= handler.handleEvent.call(handler, event) === false;
-          else
-            prevented |= handler.call(this, event) === false;
-        }
-      }
-
-      return !prevented && event.returnValue;
-    }
-  };
-
-  // Export
-  return {
-    EventTarget: EventTarget
-  };
-});
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('cr.ui', function() {
-
-  /**
-   * Decorates elements as an instance of a class.
-   * @param {string|!Element} source The way to find the element(s) to decorate.
-   *     If this is a string then {@code querySeletorAll} is used to find the
-   *     elements to decorate.
-   * @param {!Function} constr The constructor to decorate with. The constr
-   *     needs to have a {@code decorate} function.
-   */
-  function decorate(source, constr) {
-    var elements;
-    if (typeof source == 'string')
-      elements = cr.doc.querySelectorAll(source);
-    else
-      elements = [source];
-
-    for (var i = 0, el; el = elements[i]; i++) {
-      if (!(el instanceof constr))
-        constr.decorate(el);
-    }
-  }
-
-  /**
-   * Helper function for creating new element for define.
-   */
-  function createElementHelper(tagName, opt_bag) {
-    // Allow passing in ownerDocument to create in a different document.
-    var doc;
-    if (opt_bag && opt_bag.ownerDocument)
-      doc = opt_bag.ownerDocument;
-    else
-      doc = cr.doc;
-    return doc.createElement(tagName);
-  }
-
-  /**
-   * Creates the constructor for a UI element class.
-   *
-   * Usage:
-   * <pre>
-   * var List = cr.ui.define('list');
-   * List.prototype = {
-   *   __proto__: HTMLUListElement.prototype,
-   *   decorate: function() {
-   *     ...
-   *   },
-   *   ...
-   * };
-   * </pre>
-   *
-   * @param {string|Function} tagNameOrFunction The tagName or
-   *     function to use for newly created elements. If this is a function it
-   *     needs to return a new element when called.
-   * @return {function(Object=):Element} The constructor function which takes
-   *     an optional property bag. The function also has a static
-   *     {@code decorate} method added to it.
-   */
-  function define(tagNameOrFunction) {
-    var createFunction, tagName;
-    if (typeof tagNameOrFunction == 'function') {
-      createFunction = tagNameOrFunction;
-      tagName = '';
-    } else {
-      createFunction = createElementHelper;
-      tagName = tagNameOrFunction;
-    }
-
-    /**
-     * Creates a new UI element constructor.
-     * @param {Object=} opt_propertyBag Optional bag of properties to set on the
-     *     object after created. The property {@code ownerDocument} is special
-     *     cased and it allows you to create the element in a different
-     *     document than the default.
-     * @constructor
-     */
-    function f(opt_propertyBag) {
-      var el = createFunction(tagName, opt_propertyBag);
-      f.decorate(el);
-      for (var propertyName in opt_propertyBag) {
-        el[propertyName] = opt_propertyBag[propertyName];
-      }
-      return el;
-    }
-
-    /**
-     * Decorates an element as a UI element class.
-     * @param {!Element} el The element to decorate.
-     */
-    f.decorate = function(el) {
-      el.__proto__ = f.prototype;
-      el.decorate();
-    };
-
-    return f;
-  }
-
-  /**
-   * Input elements do not grow and shrink with their content. This is a simple
-   * (and not very efficient) way of handling shrinking to content with support
-   * for min width and limited by the width of the parent element.
-   * @param {HTMLElement} el The element to limit the width for.
-   * @param {number} parentEl The parent element that should limit the size.
-   * @param {number} min The minimum width.
-   */
-  function limitInputWidth(el, parentEl, min) {
-    // Needs a size larger than borders
-    el.style.width = '10px';
-    var doc = el.ownerDocument;
-    var win = doc.defaultView;
-    var computedStyle = win.getComputedStyle(el);
-    var parentComputedStyle = win.getComputedStyle(parentEl);
-    var rtl = computedStyle.direction == 'rtl';
-
-    // To get the max width we get the width of the treeItem minus the position
-    // of the input.
-    var inputRect = el.getBoundingClientRect();  // box-sizing
-    var parentRect = parentEl.getBoundingClientRect();
-    var startPos = rtl ? parentRect.right - inputRect.right :
-        inputRect.left - parentRect.left;
-
-    // Add up border and padding of the input.
-    var inner = parseInt(computedStyle.borderLeftWidth, 10) +
-        parseInt(computedStyle.paddingLeft, 10) +
-        parseInt(computedStyle.paddingRight, 10) +
-        parseInt(computedStyle.borderRightWidth, 10);
-
-    // We also need to subtract the padding of parent to prevent it to overflow.
-    var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) :
-        parseInt(parentComputedStyle.paddingRight, 10);
-
-    var max = parentEl.clientWidth - startPos - inner - parentPadding;
-
-    function limit() {
-      if (el.scrollWidth > max) {
-        el.style.width = max + 'px';
-      } else {
-        el.style.width = 0;
-        var sw = el.scrollWidth;
-        if (sw < min) {
-          el.style.width = min + 'px';
-        } else {
-          el.style.width = sw + 'px';
-        }
-      }
-    }
-
-    el.addEventListener('input', limit);
-    limit();
-  }
-
-  return {
-    decorate: decorate,
-    define: define,
-    limitInputWidth: limitInputWidth
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * The global object.
- * @type {!Object}
- */
-const global = this;
-
-/**
- * Alias for document.getElementById.
- * @param {string} id The ID of the element to find.
- * @return {HTMLElement} The found element or null if not found.
- */
-function $(id) {
-  return document.getElementById(id);
-}
-
-/**
- * Calls chrome.send with a callback and restores the original afterwards.
- * @param {string} name The name of the message to send.
- * @param {!Array} params The parameters to send.
- * @param {string} callbackName The name of the function that the backend calls.
- * @param {!Function} The function to call.
- */
-function chromeSend(name, params, callbackName, callback) {
-  var old = global[callbackName];
-  global[callbackName] = function() {
-    // restore
-    global[callbackName] = old;
-
-    var args = Array.prototype.slice.call(arguments);
-    return callback.apply(global, args);
-  };
-  chrome.send(name, params);
-}
-
-/**
- * Generates a CSS url string.
- * @param {string} s The URL to generate the CSS url for.
- * @return {string} The CSS url string.
- */
-function url(s) {
-  // http://www.w3.org/TR/css3-values/#uris
-  // Parentheses, commas, whitespace characters, single quotes (') and double
-  // quotes (") appearing in a URI must be escaped with a backslash
-  var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
-  // WebKit has a bug when it comes to URLs that end with \
-  // https://bugs.webkit.org/show_bug.cgi?id=28885
-  if (/\\\\$/.test(s2)) {
-    // Add a space to work around the WebKit bug.
-    s2 += ' ';
-  }
-  return 'url("' + s2 + '")';
-}
-
-/**
- * Parses query parameters from Location.
- * @param {string} s The URL to generate the CSS url for.
- * @return {object} Dictionary containing name value pairs for URL
- */
-function parseQueryParams(location) {
-  var params = {};
-  var query = unescape(location.search.substring(1));
-  var vars = query.split("&");
-  for (var i=0; i < vars.length; i++) {
-    var pair = vars[i].split("=");
-    params[pair[0]] = pair[1];
-  }
-  return params;
-}
-
-function findAncestorByClass(el, className) {
-  return findAncestor(el, function(el) {
-    if (el.classList)
-      return el.classList.contains(className);
-    return null;
-  });
-}
-
-/**
- * Return the first ancestor for which the {@code predicate} returns true.
- * @param {Node} node The node to check.
- * @param {function(Node) : boolean} predicate The function that tests the
- *     nodes.
- * @return {Node} The found ancestor or null if not found.
- */
-function findAncestor(node, predicate) {
-  var last = false;
-  while (node != null && !(last = predicate(node))) {
-    node = node.parentNode;
-  }
-  return last ? node : null;
-}
-
-function swapDomNodes(a, b) {
-  var afterA = a.nextSibling;
-  if (afterA == b) {
-    swapDomNodes(b, a);
-    return;
-  }
-  var aParent = a.parentNode;
-  b.parentNode.replaceChild(a, b);
-  aParent.insertBefore(b, afterA);
-}
-
-/**
- * Disables text selection and dragging.
- */
-function disableTextSelectAndDrag() {
-  // Disable text selection.
-  document.onselectstart = function(e) {
-    e.preventDefault();
-  }
-
-  // Disable dragging.
-  document.ondragstart = function(e) {
-    e.preventDefault();
-  }
-}
-
-/**
- * Check the directionality of the page.
- * @return {boolean} True if Chrome is running an RTL UI.
- */
-function isRTL() {
-  return document.documentElement.dir == 'rtl';
-}
-
-/**
- * Simple common assertion API
- * @param {*} condition The condition to test.  Note that this may be used to
- *     test whether a value is defined or not, and we don't want to force a
- *     cast to Boolean.
- * @param {string=} opt_message A message to use in any error.
- */
-function assert(condition, opt_message) {
-  'use strict';
-  if (!condition) {
-    var msg = 'Assertion failed';
-    if (opt_message)
-      msg = msg + ': ' + opt_message;
-    throw new Error(msg);
-  }
-}
-
-/**
- * Get an element that's known to exist by its ID. We use this instead of just
- * calling getElementById and not checking the result because this lets us
- * satisfy the JSCompiler type system.
- * @param {string} id The identifier name.
- * @return {!Element} the Element.
- */
-function getRequiredElement(id) {
-  var element = $(id);
-  assert(element, 'Missing required element: ' + id);
-  return element;
-}
-
-// Handle click on a link. If the link points to a chrome: or file: url, then
-// call into the browser to do the navigation.
-document.addEventListener('click', function(e) {
-  // Allow preventDefault to work.
-  if (!e.returnValue)
-    return;
-
-  var el = e.target;
-  if (el.nodeType == Node.ELEMENT_NODE &&
-      el.webkitMatchesSelector('A, A *')) {
-    while (el.tagName != 'A') {
-      el = el.parentElement;
-    }
-
-    if ((el.protocol == 'file:' || el.protocol == 'about:') &&
-        (e.button == 0 || e.button == 1)) {
-      chrome.send('navigateToUrl', [
-        el.href,
-        el.target,
-        e.button,
-        e.altKey,
-        e.ctrlKey,
-        e.metaKey,
-        e.shiftKey
-      ]);
-      e.preventDefault();
-    }
-  }
-});
-
-/**
- * Creates a new URL which is the old URL with a GET param of key=value.
- * @param {string} url The base URL. There is not sanity checking on the URL so
- *     it must be passed in a proper format.
- * @param {string} key The key of the param.
- * @param {string} value The value of the param.
- * @return {string}
- */
-function appendParam(url, key, value) {
-  var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
-
-  if (url.indexOf('?') == -1)
-    return url + '?' + param;
-  return url + '&' + param;
-}
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * @fileoverview TimelineModel is a parsed representation of the
- * TraceEvents obtained from base/trace_event in which the begin-end
- * tokens are converted into a hierarchy of processes, threads,
- * subrows, and slices.
- *
- * The building block of the model is a slice. A slice is roughly
- * equivalent to function call executing on a specific thread. As a
- * result, slices may have one or more subslices.
- *
- * A thread contains one or more subrows of slices. Row 0 corresponds to
- * the "root" slices, e.g. the topmost slices. Row 1 contains slices that
- * are nested 1 deep in the stack, and so on. We use these subrows to draw
- * nesting tasks.
- *
- */
-cr.define('tracing', function() {
-  /**
-   * A TimelineSlice represents an interval of time on a given resource plus
-   * parameters associated with that interval.
-   *
-   * A slice is typically associated with a specific trace event pair on a
-   * specific thread.
-   * For example,
-   *   TRACE_EVENT_BEGIN1("x","myArg", 7) at time=0.1ms
-   *   TRACE_EVENT_END()                  at time=0.3ms
-   * This results in a single timeline slice from 0.1 with duration 0.2 on a
-   * specific thread.
-   *
-   * A slice can also be an interval of time on a Cpu on a TimelineCpu.
-   *
-   * All time units are stored in milliseconds.
-   * @constructor
-   */
-  function TimelineSlice(title, colorId, start, args, opt_duration) {
-    this.title = title;
-    this.start = start;
-    this.colorId = colorId;
-    this.args = args;
-    this.didNotFinish = false;
-    this.subSlices = [];
-    if (opt_duration !== undefined)
-      this.duration = opt_duration;
-  }
-
-  TimelineSlice.prototype = {
-    selected: false,
-
-    duration: undefined,
-
-    get end() {
-      return this.start + this.duration;
-    }
-  };
-
-  /**
-   * A TimelineThread stores all the trace events collected for a particular
-   * thread. We organize the slices on a thread by "subrows," where subrow 0
-   * has all the root slices, subrow 1 those nested 1 deep, and so on. There
-   * is also a set of non-nested subrows.
-   *
-   * @constructor
-   */
-  function TimelineThread(parent, tid) {
-    this.parent = parent;
-    this.tid = tid;
-    this.subRows = [[]];
-    this.nonNestedSubRows = [];
-  }
-
-  TimelineThread.prototype = {
-    /**
-     * Name of the thread, if present.
-     */
-    name: undefined,
-
-    getSubrow: function(i) {
-      while (i >= this.subRows.length)
-        this.subRows.push([]);
-      return this.subRows[i];
-    },
-
-    addNonNestedSlice: function(slice) {
-      for (var i = 0; i < this.nonNestedSubRows.length; i++) {
-        var currSubRow = this.nonNestedSubRows[i];
-        var lastSlice = currSubRow[currSubRow.length - 1];
-        if (slice.start >= lastSlice.start + lastSlice.duration) {
-          currSubRow.push(slice);
-          return;
-        }
-      }
-      this.nonNestedSubRows.push([slice]);
-    },
-
-    /**
-     * Updates the minTimestamp and maxTimestamp fields based on the
-     * current slices and nonNestedSubRows attached to the thread.
-     */
-    updateBounds: function() {
-      var values = [];
-      var slices;
-      if (this.subRows[0].length != 0) {
-        slices = this.subRows[0];
-        values.push(slices[0].start);
-        values.push(slices[slices.length - 1].end);
-      }
-      for (var i = 0; i < this.nonNestedSubRows.length; ++i) {
-        slices = this.nonNestedSubRows[i];
-        values.push(slices[0].start);
-        values.push(slices[slices.length - 1].end);
-      }
-      if (values.length) {
-        this.minTimestamp = Math.min.apply(Math, values);
-        this.maxTimestamp = Math.max.apply(Math, values);
-      } else {
-        this.minTimestamp = undefined;
-        this.maxTimestamp = undefined;
-      }
-    },
-
-    /**
-     * @return {String} A user-friendly name for this thread.
-     */
-    get userFriendlyName() {
-      var tname = this.name || this.tid;
-      return this.parent.pid + ': ' + tname;
-    },
-
-    /**
-     * @return {String} User friendly details about this thread.
-     */
-    get userFriendlyDetials() {
-      return 'pid: ' + this.parent.pid +
-          ', tid: ' + this.tid +
-          (this.name ? ', name: ' + this.name : '');
-    }
-
-  };
-
-  /**
-   * Comparison between threads that orders first by pid,
-   * then by names, then by tid.
-   */
-  TimelineThread.compare = function(x, y) {
-    if (x.parent.pid != y.parent.pid) {
-      return TimelineProcess.compare(x.parent, y.parent.pid);
-    }
-
-    if (x.name && y.name) {
-      var tmp = x.name.localeCompare(y.name);
-      if (tmp == 0)
-        return x.tid - y.tid;
-      return tmp;
-    } else if (x.name) {
-      return -1;
-    } else if (y.name) {
-      return 1;
-    } else {
-      return x.tid - y.tid;
-    }
-  };
-
-  /**
-   * Stores all the samples for a given counter.
-   * @constructor
-   */
-  function TimelineCounter(parent, id, name) {
-    this.parent = parent;
-    this.id = id;
-    this.name = name;
-    this.seriesNames = [];
-    this.seriesColors = [];
-    this.timestamps = [];
-    this.samples = [];
-  }
-
-  TimelineCounter.prototype = {
-    __proto__: Object.prototype,
-
-    get numSeries() {
-      return this.seriesNames.length;
-    },
-
-    get numSamples() {
-      return this.timestamps.length;
-    },
-
-    /**
-     * Updates the bounds for this counter based on the samples it contains.
-     */
-    updateBounds: function() {
-      if (this.seriesNames.length != this.seriesColors.length)
-        throw 'seriesNames.length must match seriesColors.length';
-      if (this.numSeries * this.numSamples != this.samples.length)
-        throw 'samples.length must be a multiple of numSamples.';
-
-      this.totals = [];
-      if (this.samples.length == 0) {
-        this.minTimestamp = undefined;
-        this.maxTimestamp = undefined;
-        this.maxTotal = 0;
-        return;
-      }
-      this.minTimestamp = this.timestamps[0];
-      this.maxTimestamp = this.timestamps[this.timestamps.length - 1];
-
-      var numSeries = this.numSeries;
-      var maxTotal = -Infinity;
-      for (var i = 0; i < this.timestamps.length; i++) {
-        var total = 0;
-        for (var j = 0; j < numSeries; j++) {
-          total += this.samples[i * numSeries + j];
-          this.totals.push(total);
-        }
-        if (total > maxTotal)
-          maxTotal = total;
-      }
-
-      if (this.maxTotal === undefined) {
-        this.maxTotal = maxTotal;
-      }
-    }
-
-  };
-
-  /**
-   * Comparison between counters that orders by pid, then name.
-   */
-  TimelineCounter.compare = function(x, y) {
-    if (x.parent.pid != y.parent.pid) {
-      return TimelineProcess.compare(x.parent, y.parent.pid);
-    }
-    var tmp = x.name.localeCompare(y.name);
-    if (tmp == 0)
-      return x.tid - y.tid;
-    return tmp;
-  };
-
-  /**
-   * The TimelineProcess represents a single process in the
-   * trace. Right now, we keep this around purely for bookkeeping
-   * reasons.
-   * @constructor
-   */
-  function TimelineProcess(pid) {
-    this.pid = pid;
-    this.threads = {};
-    this.counters = {};
-  };
-
-  TimelineProcess.prototype = {
-    get numThreads() {
-      var n = 0;
-      for (var p in this.threads) {
-        n++;
-      }
-      return n;
-    },
-
-    /**
-     * @return {TimlineThread} The thread identified by tid on this process,
-     * creating it if it doesn't exist.
-     */
-    getOrCreateThread: function(tid) {
-      if (!this.threads[tid])
-        this.threads[tid] = new TimelineThread(this, tid);
-      return this.threads[tid];
-    },
-
-    /**
-     * @return {TimlineCounter} The counter on this process named 'name',
-     * creating it if it doesn't exist.
-     */
-    getOrCreateCounter: function(cat, name) {
-      var id = cat + '.' + name;
-      if (!this.counters[id])
-        this.counters[id] = new TimelineCounter(this, id, name);
-      return this.counters[id];
-    }
-  };
-
-  /**
-   * Comparison between processes that orders by pid.
-   */
-  TimelineProcess.compare = function(x, y) {
-    return x.pid - y.pid;
-  };
-
-  /**
-   * The TimelineCpu represents a Cpu from the kernel's point of view.
-   * @constructor
-   */
-  function TimelineCpu(number) {
-    this.cpuNumber = number;
-    this.slices = [];
-    this.counters = {};
-  };
-
-  TimelineCpu.prototype = {
-    /**
-     * @return {TimlineCounter} The counter on this process named 'name',
-     * creating it if it doesn't exist.
-     */
-    getOrCreateCounter: function(cat, name) {
-      var id;
-      if (cat.length)
-        id = cat + '.' + name;
-      else
-        id = name;
-      if (!this.counters[id])
-        this.counters[id] = new TimelineCounter(this, id, name);
-      return this.counters[id];
-    },
-
-    /**
-     * Updates the minTimestamp and maxTimestamp fields based on the
-     * current slices attached to the cpu.
-     */
-    updateBounds: function() {
-      var values = [];
-      if (this.slices.length) {
-        this.minTimestamp = this.slices[0].start;
-        this.maxTimestamp = this.slices[this.slices.length - 1].end;
-      } else {
-        this.minTimestamp = undefined;
-        this.maxTimestamp = undefined;
-      }
-    }
-  };
-
-  /**
-   * Comparison between processes that orders by cpuNumber.
-   */
-  TimelineCpu.compare = function(x, y) {
-    return x.cpuNumber - y.cpuNumber;
-  };
-
-  // The color pallette is split in half, with the upper
-  // half of the pallette being the "highlighted" verison
-  // of the base color. So, color 7's highlighted form is
-  // 7 + (pallette.length / 2).
-  //
-  // These bright versions of colors are automatically generated
-  // from the base colors.
-  //
-  // Within the color pallette, there are "regular" colors,
-  // which can be used for random color selection, and
-  // reserved colors, which are used when specific colors
-  // need to be used, e.g. where red is desired.
-  const palletteBase = [
-    {r: 138, g: 113, b: 152},
-    {r: 175, g: 112, b: 133},
-    {r: 127, g: 135, b: 225},
-    {r: 93, g: 81, b: 137},
-    {r: 116, g: 143, b: 119},
-    {r: 178, g: 214, b: 122},
-    {r: 87, g: 109, b: 147},
-    {r: 119, g: 155, b: 95},
-    {r: 114, g: 180, b: 160},
-    {r: 132, g: 85, b: 103},
-    {r: 157, g: 210, b: 150},
-    {r: 148, g: 94, b: 86},
-    {r: 164, g: 108, b: 138},
-    {r: 139, g: 191, b: 150},
-    {r: 110, g: 99, b: 145},
-    {r: 80, g: 129, b: 109},
-    {r: 125, g: 140, b: 149},
-    {r: 93, g: 124, b: 132},
-    {r: 140, g: 85, b: 140},
-    {r: 104, g: 163, b: 162},
-    {r: 132, g: 141, b: 178},
-    {r: 131, g: 105, b: 147},
-    {r: 135, g: 183, b: 98},
-    {r: 152, g: 134, b: 177},
-    {r: 141, g: 188, b: 141},
-    {r: 133, g: 160, b: 210},
-    {r: 126, g: 186, b: 148},
-    {r: 112, g: 198, b: 205},
-    {r: 180, g: 122, b: 195},
-    {r: 203, g: 144, b: 152},
-    // Reserved Entires
-    {r: 182, g: 125, b: 143},
-    {r: 126, g: 200, b: 148},
-    {r: 133, g: 160, b: 210},
-    {r: 240, g: 240, b: 240}];
-
-  // Make sure this number tracks the number of reserved entries in the
-  // pallette.
-  const numReservedColorIds = 4;
-
-  function brighten(c) {
-    var k;
-    if (c.r >= 240 && c.g >= 240 && c.b >= 240)
-      k = -0.20;
-    else
-      k = 0.45;
-
-    return {r: Math.min(255, c.r + Math.floor(c.r * k)),
-      g: Math.min(255, c.g + Math.floor(c.g * k)),
-      b: Math.min(255, c.b + Math.floor(c.b * k))};
-  }
-  function colorToString(c) {
-    return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
-  }
-
-  /**
-   * The number of color IDs that getStringColorId can choose from.
-   */
-  const numRegularColorIds = palletteBase.length - numReservedColorIds;
-  const highlightIdBoost = palletteBase.length;
-
-  const pallette = palletteBase.concat(palletteBase.map(brighten)).
-      map(colorToString);
-  /**
-   * Computes a simplistic hashcode of the provide name. Used to chose colors
-   * for slices.
-   * @param {string} name The string to hash.
-   */
-  function getStringHash(name) {
-    var hash = 0;
-    for (var i = 0; i < name.length; ++i)
-      hash = (hash + 37 * hash + 11 * name.charCodeAt(i)) % 0xFFFFFFFF;
-    return hash;
-  }
-
-  /**
-   * Gets the color pallette.
-   */
-  function getPallette() {
-    return pallette;
-  }
-
-  /**
-   * @return {Number} The value to add to a color ID to get its highlighted
-   * colro ID. E.g. 7 + getPalletteHighlightIdBoost() yields a brightened from
-   * of 7's base color.
-   */
-  function getPalletteHighlightIdBoost() {
-    return highlightIdBoost;
-  }
-
-  /**
-   * @param {String} name The color name.
-   * @return {Number} The color ID for the given color name.
-   */
-  function getColorIdByName(name) {
-    if (name == 'iowait')
-      return numRegularColorIds;
-    if (name == 'running')
-      return numRegularColorIds + 1;
-    if (name == 'runnable')
-      return numRegularColorIds + 2;
-    if (name == 'sleeping')
-      return numRegularColorIds + 3;
-    throw 'Unrecognized color ' + name;
-  }
-
-  // Previously computed string color IDs. They are based on a stable hash, so
-  // it is safe to save them throughout the program time.
-  var stringColorIdCache = {};
-
-  /**
-   * @return {Number} A color ID that is stably associated to the provided via
-   * the getStringHash method. The color ID will be chosen from the regular
-   * ID space only, e.g. no reserved ID will be used.
-   */
-  function getStringColorId(string) {
-    if (stringColorIdCache[string] === undefined) {
-      var hash = getStringHash(string);
-      stringColorIdCache[string] = hash % numRegularColorIds;
-    }
-    return stringColorIdCache[string];
-  }
-
-  /**
-   * Builds a model from an array of TraceEvent objects.
-   * @param {Object=} opt_data The event data to import into the new model.
-   *     See TimelineModel.importEvents for details and more advanced ways to
-   *     import data.
-   * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the
-   *     by 15%. Defaults to true.
-   * @constructor
-   */
-  function TimelineModel(opt_eventData, opt_zeroAndBoost) {
-    this.cpus = {};
-    this.processes = {};
-    this.importErrors = [];
-
-    if (opt_eventData)
-      this.importEvents(opt_eventData, opt_zeroAndBoost);
-  }
-
-  var importerConstructors = [];
-
-  /**
-   * Registers an importer. All registered importers are considered
-   * when processing an import request.
-   *
-   * @param {Function} importerConstructor The importer's constructor function.
-   */
-  TimelineModel.registerImporter = function(importerConstructor) {
-    importerConstructors.push(importerConstructor);
-  }
-
-  TimelineModel.prototype = {
-    __proto__: cr.EventTarget.prototype,
-
-    get numProcesses() {
-      var n = 0;
-      for (var p in this.processes)
-        n++;
-      return n;
-    },
-
-    /**
-     * @return {TimelineProcess} Gets a specific TimelineCpu or creates one if
-     * it does not exist.
-     */
-    getOrCreateCpu: function(cpuNumber) {
-      if (!this.cpus[cpuNumber])
-        this.cpus[cpuNumber] = new TimelineCpu(cpuNumber);
-      return this.cpus[cpuNumber];
-    },
-
-    /**
-     * @return {TimelineProcess} Gets a TimlineProcess for a specified pid or
-     * creates one if it does not exist.
-     */
-    getOrCreateProcess: function(pid) {
-      if (!this.processes[pid])
-        this.processes[pid] = new TimelineProcess(pid);
-      return this.processes[pid];
-    },
-
-    /**
-     * The import takes an array of json-ified TraceEvents and adds them into
-     * the TimelineModel as processes, threads, and slices.
-     */
-
-    /**
-     * Removes threads from the model that are fully empty.
-     */
-    pruneEmptyThreads: function() {
-      for (var pid in this.processes) {
-        var process = this.processes[pid];
-        var prunedThreads = {};
-        for (var tid in process.threads) {
-          var thread = process.threads[tid];
-
-          // Begin-events without matching end events leave a thread in a state
-          // where the toplevel subrows are empty but child subrows have
-          // entries. The autocloser will fix this up later. But, for the
-          // purposes of pruning, such threads need to be treated as having
-          // content.
-          var hasNonEmptySubrow = false;
-          for (var s = 0; s < thread.subRows.length; s++)
-            hasNonEmptySubrow |= thread.subRows[s].length > 0;
-
-          if (hasNonEmptySubrow || thread.nonNestedSubRows.legnth)
-            prunedThreads[tid] = thread;
-        }
-        process.threads = prunedThreads;
-      }
-    },
-
-    updateBounds: function() {
-      var wmin = Infinity;
-      var wmax = -wmin;
-      var hasData = false;
-
-      var threads = this.getAllThreads();
-      for (var tI = 0; tI < threads.length; tI++) {
-        var thread = threads[tI];
-        thread.updateBounds();
-        if (thread.minTimestamp != undefined &&
-            thread.maxTimestamp != undefined) {
-          wmin = Math.min(wmin, thread.minTimestamp);
-          wmax = Math.max(wmax, thread.maxTimestamp);
-          hasData = true;
-        }
-      }
-      var counters = this.getAllCounters();
-      for (var tI = 0; tI < counters.length; tI++) {
-        var counter = counters[tI];
-        counter.updateBounds();
-        if (counter.minTimestamp != undefined &&
-            counter.maxTimestamp != undefined) {
-          hasData = true;
-          wmin = Math.min(wmin, counter.minTimestamp);
-          wmax = Math.max(wmax, counter.maxTimestamp);
-        }
-      }
-
-      for (var cpuNumber in this.cpus) {
-        var cpu = this.cpus[cpuNumber];
-        cpu.updateBounds();
-        if (cpu.minTimestamp != undefined &&
-            cpu.maxTimestamp != undefined) {
-          hasData = true;
-          wmin = Math.min(wmin, cpu.minTimestamp);
-          wmax = Math.max(wmax, cpu.maxTimestamp);
-        }
-      }
-
-      if (hasData) {
-        this.minTimestamp = wmin;
-        this.maxTimestamp = wmax;
-      } else {
-        this.maxTimestamp = undefined;
-        this.minTimestamp = undefined;
-      }
-    },
-
-    shiftWorldToZero: function() {
-      if (this.minTimestamp === undefined)
-        return;
-      var timeBase = this.minTimestamp;
-      var threads = this.getAllThreads();
-      for (var tI = 0; tI < threads.length; tI++) {
-        var thread = threads[tI];
-        var shiftSubRow = function(subRow) {
-          for (var tS = 0; tS < subRow.length; tS++) {
-            var slice = subRow[tS];
-            slice.start = (slice.start - timeBase);
-          }
-        };
-
-        if (thread.cpuSlices)
-          shiftSubRow(thread.cpuSlices);
-
-        for (var tSR = 0; tSR < thread.subRows.length; tSR++) {
-          shiftSubRow(thread.subRows[tSR]);
-        }
-        for (var tSR = 0; tSR < thread.nonNestedSubRows.length; tSR++) {
-          shiftSubRow(thread.nonNestedSubRows[tSR]);
-        }
-      }
-      var counters = this.getAllCounters();
-      for (var tI = 0; tI < counters.length; tI++) {
-        var counter = counters[tI];
-        for (var sI = 0; sI < counter.timestamps.length; sI++)
-          counter.timestamps[sI] = (counter.timestamps[sI] - timeBase);
-      }
-      var cpus = this.getAllCpus();
-      for (var tI = 0; tI < cpus.length; tI++) {
-        var cpu = cpus[tI];
-        for (var sI = 0; sI < cpu.slices.length; sI++)
-          cpu.slices[sI].start = (cpu.slices[sI].start - timeBase);
-      }
-      this.updateBounds();
-    },
-
-    getAllThreads: function() {
-      var threads = [];
-      for (var pid in this.processes) {
-        var process = this.processes[pid];
-        for (var tid in process.threads) {
-          threads.push(process.threads[tid]);
-        }
-      }
-      return threads;
-    },
-
-    /**
-     * @return {Array} An array of all cpus in the model.
-     */
-    getAllCpus: function() {
-      var cpus = [];
-      for (var cpu in this.cpus)
-        cpus.push(this.cpus[cpu]);
-      return cpus;
-    },
-
-    /**
-     * @return {Array} An array of all processes in the model.
-     */
-    getAllProcesses: function() {
-      var processes = [];
-      for (var pid in this.processes)
-        processes.push(this.processes[pid]);
-      return processes;
-    },
-
-    /**
-     * @return {Array} An array of all the counters in the model.
-     */
-    getAllCounters: function() {
-      var counters = [];
-      for (var pid in this.processes) {
-        var process = this.processes[pid];
-        for (var tid in process.counters) {
-          counters.push(process.counters[tid]);
-        }
-      }
-      for (var cpuNumber in this.cpus) {
-        var cpu = this.cpus[cpuNumber];
-        for (var counterName in cpu.counters)
-          counters.push(cpu.counters[counterName]);
-      }
-      return counters;
-    },
-
-    /**
-     * Imports the provided events into the model. The eventData type
-     * is undefined and will be passed to all the timeline importers registered
-     * via TimelineModel.registerImporter. The first importer that returns true
-     * for canImport(events) will be used to import the events.
-     *
-     * @param {Object} events Events to import.
-     * @param {boolean} isChildImport True the eventData being imported is an
-     *     additional trace after the primary eventData.
-     */
-    importOneTrace_: function(eventData, isAdditionalImport) {
-      var importerConstructor;
-      for (var i = 0; i < importerConstructors.length; ++i) {
-        if (importerConstructors[i].canImport(eventData)) {
-          importerConstructor = importerConstructors[i];
-          break;
-        }
-      }
-      if (!importerConstructor)
-        throw 'Could not find an importer for the provided eventData.';
-
-      var importer = new importerConstructor(
-          this, eventData, isAdditionalImport);
-      importer.importEvents();
-      this.pruneEmptyThreads();
-    },
-
-    /**
-     * Imports the provided traces into the model. The eventData type
-     * is undefined and will be passed to all the timeline importers registered
-     * via TimelineModel.registerImporter. The first importer that returns true
-     * for canImport(events) will be used to import the events.
-     *
-     * The primary trace is provided via the eventData variable. If multiple
-     * traces are to be imported, specify the first one as events, and the
-     * remainder in the opt_additionalEventData array.
-     *
-     * @param {Object} eventData Events to import.
-     * @param {bool=} opt_zeroAndBoost Whether to align to zero and boost the
-     *     by 15%. Defaults to true.
-     * @param {Array=} opt_additionalEventData An array of eventData objects
-     *     (e.g. array of arrays) to
-     * import after importing the primary events.
-     */
-    importEvents: function(eventData,
-                           opt_zeroAndBoost, opt_additionalEventData) {
-      if (opt_zeroAndBoost === undefined)
-        opt_zeroAndBoost = true;
-
-      this.importOneTrace_(eventData, false);
-      if (opt_additionalEventData) {
-        for (var i = 0; i < opt_additionalEventData.length; ++i) {
-          this.importOneTrace_(opt_additionalEventData[i], true);
-        }
-      }
-
-      this.updateBounds();
-
-      if (opt_zeroAndBoost)
-        this.shiftWorldToZero();
-
-      if (opt_zeroAndBoost &&
-          this.minTimestamp !== undefined &&
-          this.maxTimestamp !== undefined) {
-        var boost = (this.maxTimestamp - this.minTimestamp) * 0.15;
-        this.minTimestamp = this.minTimestamp - boost;
-        this.maxTimestamp = this.maxTimestamp + boost;
-      }
-    }
-  };
-
-  return {
-    getPallette: getPallette,
-    getPalletteHighlightIdBoost: getPalletteHighlightIdBoost,
-    getColorIdByName: getColorIdByName,
-    getStringHash: getStringHash,
-    getStringColorId: getStringColorId,
-
-    TimelineSlice: TimelineSlice,
-    TimelineThread: TimelineThread,
-    TimelineCounter: TimelineCounter,
-    TimelineProcess: TimelineProcess,
-    TimelineCpu: TimelineCpu,
-    TimelineModel: TimelineModel
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Imports text files in the Linux event trace format into the
- * timeline model. This format is output both by sched_trace and by Linux's perf
- * tool.
- *
- * This importer assumes the events arrive as a string. The unit tests provide
- * examples of the trace format.
- *
- * Linux scheduler traces use a definition for 'pid' that is different than
- * tracing uses. Whereas tracing uses pid to identify a specific process, a pid
- * in a linux trace refers to a specific thread within a process. Within this
- * file, we the definition used in Linux traces, as it improves the importing
- * code's readability.
- */
-cr.define('tracing', function() {
-  /**
-   * Represents the scheduling state for a single thread.
-   * @constructor
-   */
-  function CpuState(cpu) {
-    this.cpu = cpu;
-  }
-
-  CpuState.prototype = {
-    __proto__: Object.prototype,
-
-    /**
-     * Switches the active pid on this Cpu. If necessary, add a TimelineSlice
-     * to the cpu representing the time spent on that Cpu since the last call to
-     * switchRunningLinuxPid.
-     */
-    switchRunningLinuxPid: function(importer, prevState, ts, pid, comm, prio) {
-      // Generate a slice if the last active pid was not the idle task
-      if (this.lastActivePid !== undefined && this.lastActivePid != 0) {
-        var duration = ts - this.lastActiveTs;
-        var thread = importer.threadsByLinuxPid[this.lastActivePid];
-        if (thread)
-          name = thread.userFriendlyName;
-        else
-          name = this.lastActiveComm;
-
-        var slice = new tracing.TimelineSlice(name,
-                                              tracing.getStringColorId(name),
-                                              this.lastActiveTs,
-                                              {comm: this.lastActiveComm,
-                                               tid: this.lastActivePid,
-                                               prio: this.lastActivePrio,
-                                               stateWhenDescheduled: prevState
-                                              },
-                                              duration);
-        this.cpu.slices.push(slice);
-      }
-
-      this.lastActiveTs = ts;
-      this.lastActivePid = pid;
-      this.lastActiveComm = comm;
-      this.lastActivePrio = prio;
-    }
-  };
-
-  function ThreadState(tid) {
-    this.openSlices = [];
-  }
-
-  /**
-   * Imports linux perf events into a specified model.
-   * @constructor
-   */
-  function LinuxPerfImporter(model, events, isAdditionalImport) {
-    this.isAdditionalImport_ = isAdditionalImport;
-    this.model_ = model;
-    this.events_ = events;
-    this.clockSyncRecords_ = [];
-    this.cpuStates_ = {};
-    this.kernelThreadStates_ = {};
-    this.buildMapFromLinuxPidsToTimelineThreads();
-
-    // To allow simple indexing of threads, we store all the threads by their
-    // kernel KPID. The KPID is a unique key for a thread in the trace.
-    this.threadStateByKPID_ = {};
-  }
-
-  TestExports = {};
-
-  // Matches the generic trace record:
-  //          <idle>-0     [001]  1.23: sched_switch
-  var lineRE = /^\s*(.+?)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
-  TestExports.lineRE = lineRE;
-
-  // Matches the sched_switch record
-  var schedSwitchRE = new RegExp(
-      'prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) prev_state=(\\S) ==> ' +
-      'next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)');
-  TestExports.schedSwitchRE = schedSwitchRE;
-
-  // Matches the sched_wakeup record
-  var schedWakeupRE =
-      /comm=(.+) pid=(\d+) prio=(\d+) success=(\d+) target_cpu=(\d+)/;
-  TestExports.schedWakeupRE = schedWakeupRE;
-
-  // Matches the trace_event_clock_sync record
-  //  0: trace_event_clock_sync: parent_ts=19581477508
-  var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
-  TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
-
-  // Matches the workqueue_execute_start record
-  //  workqueue_execute_start: work struct c7a8a89c: function MISRWrapper
-  var workqueueExecuteStartRE = /work struct (.+): function (\S+)/;
-
-  // Matches the workqueue_execute_start record
-  //  workqueue_execute_end: work struct c7a8a89c
-  var workqueueExecuteEndRE = /work struct (.+)/;
-
-  /**
-   * Guesses whether the provided events is a Linux perf string.
-   * Looks for the magic string "# tracer" at the start of the file,
-   * or the typical task-pid-cpu-timestamp-function sequence of a typical
-   * trace's body.
-   *
-   * @return {boolean} True when events is a linux perf array.
-   */
-  LinuxPerfImporter.canImport = function(events) {
-    if (!(typeof(events) === 'string' || events instanceof String))
-      return false;
-
-    if (/^# tracer:/.exec(events))
-      return true;
-
-    var m = /^(.+)\n/.exec(events);
-    if (m)
-      events = m[1];
-    if (lineRE.exec(events))
-      return true;
-
-    return false;
-  };
-
-  LinuxPerfImporter.prototype = {
-    __proto__: Object.prototype,
-
-    /**
-     * Precomputes a lookup table from linux pids back to existing
-     * TimelineThreads. This is used during importing to add information to each
-     * timeline thread about whether it was running, descheduled, sleeping, et
-     * cetera.
-     */
-    buildMapFromLinuxPidsToTimelineThreads: function() {
-      this.threadsByLinuxPid = {};
-      this.model_.getAllThreads().forEach(
-          function(thread) {
-            this.threadsByLinuxPid[thread.tid] = thread;
-          }.bind(this));
-    },
-
-    /**
-     * @return {CpuState} A CpuState corresponding to the given cpuNumber.
-     */
-    getOrCreateCpuState: function(cpuNumber) {
-      if (!this.cpuStates_[cpuNumber]) {
-        var cpu = this.model_.getOrCreateCpu(cpuNumber);
-        this.cpuStates_[cpuNumber] = new CpuState(cpu);
-      }
-      return this.cpuStates_[cpuNumber];
-    },
-
-    /**
-     * @return {number} The pid extracted from the kernel thread name.
-     */
-    parsePid: function(kernelThreadName) {
-        var pid = /.+-(\d+)/.exec(kernelThreadName)[1];
-        pid = parseInt(pid);
-        return pid;
-    },
-
-    /**
-     * @return {number} The string portion of the thread extracted from the
-     * kernel thread name.
-     */
-    parseThreadName: function(kernelThreadName) {
-        return /(.+)-\d+/.exec(kernelThreadName)[1];
-    },
-
-    /**
-     * @return {TimelinThread} A thread corresponding to the kernelThreadName
-     */
-    getOrCreateKernelThread: function(kernelThreadName) {
-      if (!this.kernelThreadStates_[kernelThreadName]) {
-        pid = this.parsePid(kernelThreadName);
-
-        var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(pid);
-        thread.name = kernelThreadName;
-        this.kernelThreadStates_[kernelThreadName] = {
-          pid: pid,
-          thread: thread,
-          openSlice: undefined,
-          openSliceTS: undefined
-        };
-        this.threadsByLinuxPid[pid] = thread;
-      }
-      return this.kernelThreadStates_[kernelThreadName];
-    },
-
-    /**
-     * Imports the data in this.events_ into model_.
-     */
-    importEvents: function() {
-      this.importCpuData();
-      if (!this.alignClocks())
-        return;
-      this.buildPerThreadCpuSlicesFromCpuState();
-    },
-
-    /**
-     * Builds the cpuSlices array on each thread based on our knowledge of what
-     * each Cpu is doing.  This is done only for TimelineThreads that are
-     * already in the model, on the assumption that not having any traced data
-     * on a thread means that it is not of interest to the user.
-     */
-    buildPerThreadCpuSlicesFromCpuState: function() {
-      // Push the cpu slices to the threads that they run on.
-      for (var cpuNumber in this.cpuStates_) {
-        var cpuState = this.cpuStates_[cpuNumber];
-        var cpu = cpuState.cpu;
-
-        for (var i = 0; i < cpu.slices.length; i++) {
-          var slice = cpu.slices[i];
-
-          var thread = this.threadsByLinuxPid[slice.args.tid];
-          if (!thread)
-            continue;
-          if (!thread.tempCpuSlices)
-            thread.tempCpuSlices = [];
-
-          // Because Chrome's Array.sort is not a stable sort, we need to keep
-          // the slice index around to keep slices with identical start times in
-          // the proper order when sorting them.
-          slice.index = i;
-
-          thread.tempCpuSlices.push(slice);
-        }
-      }
-
-      // Create slices for when the thread is not running.
-      var runningId = tracing.getColorIdByName('running');
-      var runnableId = tracing.getColorIdByName('runnable');
-      var sleepingId = tracing.getColorIdByName('sleeping');
-      var ioWaitId = tracing.getColorIdByName('iowait');
-      this.model_.getAllThreads().forEach(function(thread) {
-        if (!thread.tempCpuSlices)
-          return;
-        var origSlices = thread.tempCpuSlices;
-        delete thread.tempCpuSlices;
-
-        origSlices.sort(function(x, y) {
-          var delta = x.start - y.start;
-          if (delta == 0) {
-            // Break ties using the original slice ordering.
-            return x.index - y.index;
-          } else {
-            return delta;
-          }
-        });
-
-        // Walk the slice list and put slices between each original slice
-        // to show when the thread isn't running
-        var slices = [];
-        if (origSlices.length) {
-          var slice = origSlices[0];
-          slices.push(new tracing.TimelineSlice('Running', runningId,
-              slice.start, {}, slice.duration));
-        }
-        for (var i = 1; i < origSlices.length; i++) {
-          var prevSlice = origSlices[i - 1];
-          var nextSlice = origSlices[i];
-          var midDuration = nextSlice.start - prevSlice.end;
-          if (prevSlice.args.stateWhenDescheduled == 'S') {
-            slices.push(new tracing.TimelineSlice('Sleeping', sleepingId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'R') {
-            slices.push(new tracing.TimelineSlice('Runnable', runnableId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'D') {
-            slices.push(new tracing.TimelineSlice('I/O Wait', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'T') {
-            slices.push(new tracing.TimelineSlice('__TASK_STOPPED', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 't') {
-            slices.push(new tracing.TimelineSlice('debug', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'Z') {
-            slices.push(new tracing.TimelineSlice('Zombie', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'X') {
-            slices.push(new tracing.TimelineSlice('Exit Dead', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'x') {
-            slices.push(new tracing.TimelineSlice('Task Dead', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else if (prevSlice.args.stateWhenDescheduled == 'W') {
-            slices.push(new tracing.TimelineSlice('WakeKill', ioWaitId,
-                prevSlice.end, {}, midDuration));
-          } else {
-            throw 'Unrecognized state: ' + prevSlice.args.stateWhenDescheduled;
-          }
-
-          slices.push(new tracing.TimelineSlice('Running', runningId,
-              nextSlice.start, {}, nextSlice.duration));
-        }
-        thread.cpuSlices = slices;
-      });
-    },
-
-    /**
-     * Walks the slices stored on this.cpuStates_ and adjusts their timestamps
-     * based on any alignment metadata we discovered.
-     */
-    alignClocks: function() {
-      if (this.clockSyncRecords_.length == 0) {
-        // If this is an additional import, and no clock syncing records were
-        // found, then abort the import. Otherwise, just skip clock alignment.
-        if (!this.isAdditionalImport_)
-          return;
-
-        // Remove the newly imported CPU slices from the model.
-        this.abortImport();
-        return false;
-      }
-
-      // Shift all the slice times based on the sync record.
-      var sync = this.clockSyncRecords_[0];
-      var timeShift = sync.parentTS - sync.perfTS;
-      for (var cpuNumber in this.cpuStates_) {
-        var cpuState = this.cpuStates_[cpuNumber];
-        var cpu = cpuState.cpu;
-
-        for (var i = 0; i < cpu.slices.length; i++) {
-          var slice = cpu.slices[i];
-          slice.start = slice.start + timeShift;
-          slice.duration = slice.duration;
-        }
-
-        for (var counterName in cpu.counters) {
-          var counter = cpu.counters[counterName];
-          for (var sI = 0; sI < counter.timestamps.length; sI++)
-            counter.timestamps[sI] = (counter.timestamps[sI] + timeShift);
-        }
-      }
-      for (var kernelThreadName in this.kernelThreadStates_) {
-        var kthread = this.kernelThreadStates_[kernelThreadName];
-        var thread = kthread.thread;
-        for (var i = 0; i < thread.subRows[0].length; i++) {
-          thread.subRows[0][i].start += timeShift;
-        }
-      }
-      return true;
-    },
-
-    /**
-     * Removes any data that has been added to the model because of an error
-     * detected during the import.
-     */
-    abortImport: function() {
-      if (this.pushedEventsToThreads)
-        throw 'Cannot abort, have alrady pushedCpuDataToThreads.';
-
-      for (var cpuNumber in this.cpuStates_)
-        delete this.model_.cpus[cpuNumber];
-      for (var kernelThreadName in this.kernelThreadStates_) {
-        var kthread = this.kernelThreadStates_[kernelThreadName];
-        var thread = kthread.thread;
-        var process = thread.parent;
-        delete process.threads[thread.tid];
-        delete this.model_.processes[process.pid];
-      }
-      this.model_.importErrors.push(
-          'Cannot import kernel trace without a clock sync.');
-    },
-
-    /**
-     * Records the fact that a pid has become runnable. This data will
-     * eventually get used to derive each thread's cpuSlices array.
-     */
-    markPidRunnable: function(ts, pid, comm, prio) {
-      // TODO(nduca): implement this functionality.
-    },
-
-    /**
-     * Helper to process a 'begin' event (e.g. initiate a slice).
-     * @param {ThreadState} state Thread state (holds slices).
-     * @param {string} name The trace event name.
-     * @param {number} ts The trace event begin timestamp.
-     */
-    processBegin: function(state, tname, name, ts, pid, tid) {
-      var colorId = tracing.getStringColorId(name);
-      var slice = new tracing.TimelineSlice(name, colorId, ts, null);
-      // XXX: Should these be removed from the slice before putting it into the
-      // model?
-      slice.pid = pid;
-      slice.tid = tid;
-      slice.threadName = tname;
-      state.openSlices.push(slice);
-    },
-
-    /**
-     * Helper to process an 'end' event (e.g. close a slice).
-     * @param {ThreadState} state Thread state (holds slices).
-     * @param {number} ts The trace event begin timestamp.
-     */
-    processEnd: function(state, ts) {
-      if (state.openSlices.length == 0) {
-        // Ignore E events that are unmatched.
-        return;
-      }
-      var slice = state.openSlices.pop();
-      slice.duration = ts - slice.start;
-
-      // Store the slice on the correct subrow.
-      var thread = this.model_.getOrCreateProcess(slice.pid).
-          getOrCreateThread(slice.tid);
-      if (!thread.name)
-        thread.name = slice.threadName;
-      this.threadsByLinuxPid[slice.tid] = thread;
-      var subRowIndex = state.openSlices.length;
-      thread.getSubrow(subRowIndex).push(slice);
-
-      // Add the slice to the subSlices array of its parent.
-      if (state.openSlices.length) {
-        var parentSlice = state.openSlices[state.openSlices.length - 1];
-        parentSlice.subSlices.push(slice);
-      }
-    },
-
-    /**
-     * Helper function that closes any open slices. This happens when a trace
-     * ends before an 'E' phase event can get posted. When that happens, this
-     * closes the slice at the highest timestamp we recorded and sets the
-     * didNotFinish flag to true.
-     */
-    autoCloseOpenSlices: function() {
-      // We need to know the model bounds in order to assign an end-time to
-      // the open slices.
-      this.model_.updateBounds();
-
-      // The model's max value in the trace is wrong at this point if there are
-      // un-closed events. To close those events, we need the true global max
-      // value. To compute this, build a list of timestamps that weren't
-      // included in the max calculation, then compute the real maximum based
-      // on that.
-      var openTimestamps = [];
-      for (var kpid in this.threadStateByKPID_) {
-        var state = this.threadStateByKPID_[kpid];
-        for (var i = 0; i < state.openSlices.length; i++) {
-          var slice = state.openSlices[i];
-          openTimestamps.push(slice.start);
-          for (var s = 0; s < slice.subSlices.length; s++) {
-            var subSlice = slice.subSlices[s];
-            openTimestamps.push(subSlice.start);
-            if (subSlice.duration)
-              openTimestamps.push(subSlice.end);
-          }
-        }
-      }
-
-      // Figure out the maximum value of model.maxTimestamp and
-      // Math.max(openTimestamps). Made complicated by the fact that the model
-      // timestamps might be undefined.
-      var realMaxTimestamp;
-      if (this.model_.maxTimestamp) {
-        realMaxTimestamp = Math.max(this.model_.maxTimestamp,
-                                    Math.max.apply(Math, openTimestamps));
-      } else {
-        realMaxTimestamp = Math.max.apply(Math, openTimestamps);
-      }
-
-      // Automatically close any slices are still open. These occur in a number
-      // of reasonable situations, e.g. deadlock. This pass ensures the open
-      // slices make it into the final model.
-      for (var kpid in this.threadStateByKPID_) {
-        var state = this.threadStateByKPID_[kpid];
-        while (state.openSlices.length > 0) {
-          var slice = state.openSlices.pop();
-          slice.duration = realMaxTimestamp - slice.start;
-          slice.didNotFinish = true;
-
-          // Store the slice on the correct subrow.
-          var thread = this.model_.getOrCreateProcess(slice.pid)
-                           .getOrCreateThread(slice.tid);
-          var subRowIndex = state.openSlices.length;
-          thread.getSubrow(subRowIndex).push(slice);
-
-          // Add the slice to the subSlices array of its parent.
-          if (state.openSlices.length) {
-            var parentSlice = state.openSlices[state.openSlices.length - 1];
-            parentSlice.subSlices.push(slice);
-          }
-        }
-      }
-    },
-
-    /**
-     * Helper that creates and adds samples to a TimelineCounter object based on
-     * 'C' phase events.
-     */
-    processCounter: function(name, ts, value, pid) {
-      var ctr = this.model_.getOrCreateProcess(pid)
-          .getOrCreateCounter('', name);
-
-      // Initialize the counter's series fields if needed.
-      //
-      if (ctr.numSeries == 0) {
-        ctr.seriesNames.push('state');
-        ctr.seriesColors.push(
-            tracing.getStringColorId(ctr.name + '.' + 'state'));
-      }
-
-      // Add the sample values.
-      ctr.timestamps.push(ts);
-      ctr.samples.push(value);
-    },
-
-
-    /**
-     * Walks the this.events_ structure and creates TimelineCpu objects.
-     */
-    importCpuData: function() {
-      this.lines_ = this.events_.split('\n');
-
-      for (var lineNumber = 0; lineNumber < this.lines_.length; ++lineNumber) {
-        var line = this.lines_[lineNumber];
-        if (/^#/.exec(line) || line.length == 0)
-          continue;
-        var eventBase = lineRE.exec(line);
-        if (!eventBase) {
-          this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-              ': Unrecognized line: ' + line);
-          continue;
-        }
-
-        var cpuState = this.getOrCreateCpuState(parseInt(eventBase[2]));
-        var ts = parseFloat(eventBase[3]) * 1000;
-
-        var eventName = eventBase[4];
-
-        if (eventName == 'sched_switch') {
-          var event = schedSwitchRE.exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed sched_switch event');
-            continue;
-          }
-
-          var prevState = event[4];
-          var nextComm = event[5];
-          var nextPid = parseInt(event[6]);
-          var nextPrio = parseInt(event[7]);
-          cpuState.switchRunningLinuxPid(
-              this, prevState, ts, nextPid, nextComm, nextPrio);
-
-        } else if (eventName == 'sched_wakeup') {
-          var event = schedWakeupRE.exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed sched_wakeup event');
-            continue;
-          }
-
-          var comm = event[1];
-          var pid = parseInt(event[2]);
-          var prio = parseInt(event[3]);
-          this.markPidRunnable(ts, pid, comm, prio);
-
-        } else if (eventName == 'cpu_frequency') {
-          var event = /state=(\d+) cpu_id=(\d+)/.exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed cpu_frequency event');
-            continue;
-          }
-          var targetCpuNumber = parseInt(event[2]);
-          var targetCpu = this.getOrCreateCpuState(targetCpuNumber);
-          var freqCounter =
-              targetCpu.cpu.getOrCreateCounter('', 'Frequency');
-          if (freqCounter.numSeries == 0) {
-            freqCounter.seriesNames.push('state');
-            freqCounter.seriesColors.push(
-                tracing.getStringColorId(freqCounter.name + '.' + 'state'));
-          }
-          var freqState = parseInt(event[1]);
-          freqCounter.timestamps.push(ts);
-          freqCounter.samples.push(freqState);
-        } else if (eventName == 'cpufreq_interactive_already' ||
-                   eventName == 'cpufreq_interactive_target') {
-          var event = /cpu=(\d+) load=(\d+) cur=(\d+) targ=(\d+)/.
-              exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed cpufreq_interactive_* event');
-            continue;
-          }
-          var targetCpuNumber = parseInt(event[1]);
-          var targetCpu = this.getOrCreateCpuState(targetCpuNumber);
-          var loadCounter =
-              targetCpu.cpu.getOrCreateCounter('', 'Load');
-          if (loadCounter.numSeries == 0) {
-            loadCounter.seriesNames.push('state');
-            loadCounter.seriesColors.push(
-                tracing.getStringColorId(loadCounter.name + '.' + 'state'));
-          }
-          var loadState = parseInt(event[2]);
-          loadCounter.timestamps.push(ts);
-          loadCounter.samples.push(loadState);
-          loadCounter.maxTotal = 100;
-          loadCounter.skipUpdateBounds = true;
-        } else if (eventName == 'workqueue_execute_start') {
-          var event = workqueueExecuteStartRE.exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed workqueue_execute_start event');
-            continue;
-          }
-          var kthread = this.getOrCreateKernelThread(eventBase[1]);
-          kthread.openSliceTS = ts;
-          kthread.openSlice = event[2];
-
-        } else if (eventName == 'workqueue_execute_end') {
-          var event = workqueueExecuteEndRE.exec(eventBase[5]);
-          if (!event) {
-            this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                ': Malformed workqueue_execute_start event');
-            continue;
-          }
-          var kthread = this.getOrCreateKernelThread(eventBase[1]);
-          if (kthread.openSlice) {
-            var slice = new tracing.TimelineSlice(kthread.openSlice,
-                tracing.getStringColorId(kthread.openSlice),
-                kthread.openSliceTS,
-                {},
-                ts - kthread.openSliceTS);
-
-            kthread.thread.subRows[0].push(slice);
-          }
-          kthread.openSlice = undefined;
-
-        } else if (eventName == '0') { // trace_mark's show up with 0 prefixes.
-          var event = traceEventClockSyncRE.exec(eventBase[5]);
-          if (event)
-            this.clockSyncRecords_.push({
-              perfTS: ts,
-              parentTS: event[1] * 1000
-            });
-          else {
-            var tid = this.parsePid(eventBase[1]);
-            var tname = this.parseThreadName(eventBase[1]);
-            var kpid = tid;
-
-            if (!(kpid in this.threadStateByKPID_))
-              this.threadStateByKPID_[kpid] = new ThreadState();
-            var state = this.threadStateByKPID_[kpid];
-
-            var event = eventBase[5].split('|')
-            switch (event[0]) {
-            case 'B':
-              var pid = parseInt(event[1]);
-              var name = event[2];
-              this.processBegin(state, tname, name, ts, pid, tid);
-              break;
-            case 'E':
-              this.processEnd(state, ts);
-              break;
-            case 'C':
-              var pid = parseInt(event[1]);
-              var name = event[2];
-              var value = parseInt(event[3]);
-              this.processCounter(name, ts, value, pid);
-              break;
-            default:
-              this.model_.importErrors.push('Line ' + (lineNumber + 1) +
-                  ': Unrecognized event: ' + eventBase[5]);
-            }
-          }
-        }
-      }
-
-      // Autoclose any open slices.
-      var hasOpenSlices = false;
-      for (var kpid in this.threadStateByKPID_) {
-        var state = this.threadStateByKPID_[kpid];
-        hasOpenSlices |= state.openSlices.length > 0;
-      }
-      if (hasOpenSlices)
-        this.autoCloseOpenSlices();
-    }
-  };
-
-  tracing.TimelineModel.registerImporter(LinuxPerfImporter);
-
-  return {
-    LinuxPerfImporter: LinuxPerfImporter,
-    _LinuxPerfImporterTestExports: TestExports
-  };
-
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview TraceEventImporter imports TraceEvent-formatted data
- * into the provided timeline model.
- */
-cr.define('tracing', function() {
-  function ThreadState(tid) {
-    this.openSlices = [];
-    this.openNonNestedSlices = {};
-  }
-
-  function TraceEventImporter(model, eventData) {
-    this.model_ = model;
-
-    if (typeof(eventData) === 'string' || eventData instanceof String) {
-      // If the event data begins with a [, then we know it should end with a ].
-      // The reason we check for this is because some tracing implementations
-      // cannot guarantee that a ']' gets written to the trace file. So, we are
-      // forgiving and if this is obviously the case, we fix it up before
-      // throwing the string at JSON.parse.
-      if (eventData[0] == '[') {
-        n = eventData.length;
-        if (eventData[n - 1] != ']' && eventData[n - 1] != '\n') {
-          eventData = eventData + ']';
-        } else if (eventData[n - 2] != ']' && eventData[n - 1] == '\n') {
-          eventData = eventData + ']';
-        } else if (eventData[n - 3] != ']' && eventData[n - 2] == '\r' &&
-            eventData[n - 1] == '\n') {
-          eventData = eventData + ']';
-        }
-      }
-      this.events_ = JSON.parse(eventData);
-
-    } else {
-      this.events_ = eventData;
-    }
-
-    // Some trace_event implementations put the actual trace events
-    // inside a container. E.g { ... , traceEvents: [ ] }
-    //
-    // If we see that, just pull out the trace events.
-    if (this.events_.traceEvents)
-      this.events_ = this.events_.traceEvents;
-
-    // To allow simple indexing of threads, we store all the threads by a
-    // PTID. A ptid is a pid and tid joined together x:y fashion, eg
-    // 1024:130. The ptid is a unique key for a thread in the trace.
-    this.threadStateByPTID_ = {};
-  }
-
-  /**
-   * @return {boolean} Whether obj is a TraceEvent array.
-   */
-  TraceEventImporter.canImport = function(eventData) {
-    // May be encoded JSON. But we dont want to parse it fully yet.
-    // Use a simple heuristic:
-    //   - eventData that starts with [ are probably trace_event
-    //   - eventData that starts with { are probably trace_event
-    // May be encoded JSON. Treat files that start with { as importable by us.
-    if (typeof(eventData) === 'string' || eventData instanceof String) {
-      return eventData[0] == '{' || eventData[0] == '[';
-    }
-
-    // Might just be an array of events
-    if (eventData instanceof Array && eventData[0].ph)
-      return true;
-
-    // Might be an object with a traceEvents field in it.
-    if (eventData.traceEvents)
-      return eventData.traceEvents instanceof Array &&
-          eventData.traceEvents[0].ph;
-
-    return false;
-  };
-
-  TraceEventImporter.prototype = {
-
-    __proto__: Object.prototype,
-
-    /**
-     * Helper to process a 'begin' event (e.g. initiate a slice).
-     * @param {ThreadState} state Thread state (holds slices).
-     * @param {Object} event The current trace event.
-     */
-    processBegin: function(index, state, event) {
-      var colorId = tracing.getStringColorId(event.name);
-      var slice =
-          { index: index,
-            slice: new tracing.TimelineSlice(event.name, colorId,
-                                             event.ts / 1000,
-                                             event.args) };
-
-      if (event.uts)
-        slice.slice.startInUserTime = event.uts / 1000;
-
-      if (event.args['ui-nest'] === '0') {
-        var sliceID = event.name;
-        for (var x in event.args)
-          sliceID += ';' + event.args[x];
-        if (state.openNonNestedSlices[sliceID])
-          this.model_.importErrors.push('Event ' + sliceID + ' already open.');
-        state.openNonNestedSlices[sliceID] = slice;
-      } else {
-        state.openSlices.push(slice);
-      }
-    },
-
-    /**
-     * Helper to process an 'end' event (e.g. close a slice).
-     * @param {ThreadState} state Thread state (holds slices).
-     * @param {Object} event The current trace event.
-     */
-    processEnd: function(state, event) {
-      if (event.args['ui-nest'] === '0') {
-        var sliceID = event.name;
-        for (var x in event.args)
-          sliceID += ';' + event.args[x];
-        var slice = state.openNonNestedSlices[sliceID];
-        if (!slice)
-          return;
-        slice.slice.duration = (event.ts / 1000) - slice.slice.start;
-        if (event.uts)
-          slice.durationInUserTime = (event.uts / 1000) -
-              slice.slice.startInUserTime;
-
-        // Store the slice in a non-nested subrow.
-        var thread =
-            this.model_.getOrCreateProcess(event.pid).
-                getOrCreateThread(event.tid);
-        thread.addNonNestedSlice(slice.slice);
-        delete state.openNonNestedSlices[name];
-      } else {
-        if (state.openSlices.length == 0) {
-          // Ignore E events that are unmatched.
-          return;
-        }
-        var slice = state.openSlices.pop().slice;
-        slice.duration = (event.ts / 1000) - slice.start;
-        if (event.uts)
-          slice.durationInUserTime = (event.uts / 1000) - slice.startInUserTime;
-
-        // Store the slice on the correct subrow.
-        var thread = this.model_.getOrCreateProcess(event.pid).
-            getOrCreateThread(event.tid);
-        var subRowIndex = state.openSlices.length;
-        thread.getSubrow(subRowIndex).push(slice);
-
-        // Add the slice to the subSlices array of its parent.
-        if (state.openSlices.length) {
-          var parentSlice = state.openSlices[state.openSlices.length - 1];
-          parentSlice.slice.subSlices.push(slice);
-        }
-      }
-    },
-
-    /**
-     * Helper function that closes any open slices. This happens when a trace
-     * ends before an 'E' phase event can get posted. When that happens, this
-     * closes the slice at the highest timestamp we recorded and sets the
-     * didNotFinish flag to true.
-     */
-    autoCloseOpenSlices: function() {
-      // We need to know the model bounds in order to assign an end-time to
-      // the open slices.
-      this.model_.updateBounds();
-
-      // The model's max value in the trace is wrong at this point if there are
-      // un-closed events. To close those events, we need the true global max
-      // value. To compute this, build a list of timestamps that weren't
-      // included in the max calculation, then compute the real maximum based
-      // on that.
-      var openTimestamps = [];
-      for (var ptid in this.threadStateByPTID_) {
-        var state = this.threadStateByPTID_[ptid];
-        for (var i = 0; i < state.openSlices.length; i++) {
-          var slice = state.openSlices[i];
-          openTimestamps.push(slice.slice.start);
-          for (var s = 0; s < slice.slice.subSlices.length; s++) {
-            var subSlice = slice.slice.subSlices[s];
-            openTimestamps.push(subSlice.start);
-            if (subSlice.duration)
-              openTimestamps.push(subSlice.end);
-          }
-        }
-      }
-
-      // Figure out the maximum value of model.maxTimestamp and
-      // Math.max(openTimestamps). Made complicated by the fact that the model
-      // timestamps might be undefined.
-      var realMaxTimestamp;
-      if (this.model_.maxTimestamp) {
-        realMaxTimestamp = Math.max(this.model_.maxTimestamp,
-                                    Math.max.apply(Math, openTimestamps));
-      } else {
-        realMaxTimestamp = Math.max.apply(Math, openTimestamps);
-      }
-
-      // Automatically close any slices are still open. These occur in a number
-      // of reasonable situations, e.g. deadlock. This pass ensures the open
-      // slices make it into the final model.
-      for (var ptid in this.threadStateByPTID_) {
-        var state = this.threadStateByPTID_[ptid];
-        while (state.openSlices.length > 0) {
-          var slice = state.openSlices.pop();
-          slice.slice.duration = realMaxTimestamp - slice.slice.start;
-          slice.slice.didNotFinish = true;
-          var event = this.events_[slice.index];
-
-          // Store the slice on the correct subrow.
-          var thread = this.model_.getOrCreateProcess(event.pid)
-                           .getOrCreateThread(event.tid);
-          var subRowIndex = state.openSlices.length;
-          thread.getSubrow(subRowIndex).push(slice.slice);
-
-          // Add the slice to the subSlices array of its parent.
-          if (state.openSlices.length) {
-            var parentSlice = state.openSlices[state.openSlices.length - 1];
-            parentSlice.slice.subSlices.push(slice.slice);
-          }
-        }
-      }
-    },
-
-    /**
-     * Helper that creates and adds samples to a TimelineCounter object based on
-     * 'C' phase events.
-     */
-    processCounter: function(event) {
-      var ctr_name;
-      if (event.id !== undefined)
-        ctr_name = event.name + '[' + event.id + ']';
-      else
-        ctr_name = event.name;
-
-      var ctr = this.model_.getOrCreateProcess(event.pid)
-          .getOrCreateCounter(event.cat, ctr_name);
-      // Initialize the counter's series fields if needed.
-      if (ctr.numSeries == 0) {
-        for (var seriesName in event.args) {
-          ctr.seriesNames.push(seriesName);
-          ctr.seriesColors.push(
-              tracing.getStringColorId(ctr.name + '.' + seriesName));
-        }
-        if (ctr.numSeries == 0) {
-          this.model_.importErrors.push('Expected counter ' + event.name +
-              ' to have at least one argument to use as a value.');
-          // Drop the counter.
-          delete ctr.parent.counters[ctr.name];
-          return;
-        }
-      }
-
-      // Add the sample values.
-      ctr.timestamps.push(event.ts / 1000);
-      for (var i = 0; i < ctr.numSeries; i++) {
-        var seriesName = ctr.seriesNames[i];
-        if (event.args[seriesName] === undefined) {
-          ctr.samples.push(0);
-          continue;
-        }
-        ctr.samples.push(event.args[seriesName]);
-      }
-    },
-
-    /**
-     * Walks through the events_ list and outputs the structures discovered to
-     * model_.
-     */
-    importEvents: function() {
-      // Walk through events
-      var events = this.events_;
-      for (var eI = 0; eI < events.length; eI++) {
-        var event = events[eI];
-        var ptid = event.pid + ':' + event.tid;
-
-        if (!(ptid in this.threadStateByPTID_))
-          this.threadStateByPTID_[ptid] = new ThreadState();
-        var state = this.threadStateByPTID_[ptid];
-
-        if (event.ph == 'B') {
-          this.processBegin(eI, state, event);
-        } else if (event.ph == 'E') {
-          this.processEnd(state, event);
-        } else if (event.ph == 'I') {
-          // Treat an Instant event as a duration 0 slice.
-          // TimelineSliceTrack's redraw() knows how to handle this.
-          this.processBegin(eI, state, event);
-          this.processEnd(state, event);
-        } else if (event.ph == 'C') {
-          this.processCounter(event);
-        } else if (event.ph == 'M') {
-          if (event.name == 'thread_name') {
-            var thread = this.model_.getOrCreateProcess(event.pid)
-                             .getOrCreateThread(event.tid);
-            thread.name = event.args.name;
-          } else {
-            this.model_.importErrors.push(
-                'Unrecognized metadata name: ' + event.name);
-          }
-        } else {
-          this.model_.importErrors.push(
-              'Unrecognized event phase: ' + event.ph +
-              '(' + event.name + ')');
-        }
-      }
-
-      // Autoclose any open slices.
-      var hasOpenSlices = false;
-      for (var ptid in this.threadStateByPTID_) {
-        var state = this.threadStateByPTID_[ptid];
-        hasOpenSlices |= state.openSlices.length > 0;
-      }
-      if (hasOpenSlices)
-        this.autoCloseOpenSlices();
-    }
-  };
-
-  tracing.TimelineModel.registerImporter(TraceEventImporter);
-
-  return {
-    TraceEventImporter: TraceEventImporter
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * @fileoverview Helper functions for doing intersections and iteration
- * over sorted arrays and intervals.
- *
- */
-cr.define('tracing', function() {
-  /**
-   * Finds the first index in the array whose value is >= loVal.
-   *
-   * The key for the search is defined by the mapFn. This array must
-   * be prearranged such that ary.map(mapFn) would also be sorted in
-   * ascending order.
-   *
-   * @param {Array} ary An array of arbitrary objects.
-   * @param {function():*} mapFn Callback that produces a key value
-   *     from an element in ary.
-   * @param {number} loVal Value for which to search.
-   * @return {Number} Offset o into ary where all ary[i] for i <= o
-   *     are < loVal, or ary.length if loVal is greater than all elements in
-   *     the array.
-   */
-  function findLowIndexInSortedArray(ary, mapFn, loVal) {
-    if (ary.length == 0)
-      return 1;
-
-    var low = 0;
-    var high = ary.length - 1;
-    var i, comparison;
-    var hitPos = -1;
-    while (low <= high) {
-      i = Math.floor((low + high) / 2);
-      comparison = mapFn(ary[i]) - loVal;
-      if (comparison < 0) {
-        low = i + 1; continue;
-      } else if (comparison > 0) {
-        high = i - 1; continue;
-      } else {
-        hitPos = i;
-        high = i - 1;
-      }
-    }
-    // return where we hit, or failing that the low pos
-    return hitPos != -1 ? hitPos : low;
-  }
-
-  /**
-   * Finds an index in an array of intervals that either
-   * intersects the provided loVal, or if no intersection is found,
-   * the index of the first interval whose start is > loVal.
-   *
-   * The array of intervals is defined implicitly via two mapping functions
-   * over the provided ary. mapLoFn determines the lower value of the interval,
-   * mapWidthFn the width. Intersection is lower-inclusive, e.g. [lo,lo+w).
-   *
-   * The array of intervals formed by this mapping must be non-overlapping and
-   * sorted in ascending order by loVal.
-   *
-   * @param {Array} ary An array of objects that can be converted into sorted
-   *     nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
-   * @param {function():*} mapLoFn Callback that produces the low value for the
-   *     interval represented by an  element in the array.
-   * @param {function():*} mapLoFn Callback that produces the width for the
-   *     interval represented by an  element in the array.
-   * @param {number} loVal The low value for the search.
-   * @return {Number} An index in the array that intersects or is first-above
-   *     loVal, -1 if none found and loVal is below than all the intervals,
-   *     ary.length if loVal is greater than all the intervals.
-   */
-  function findLowIndexInSortedIntervals(ary, mapLoFn, mapWidthFn, loVal) {
-    var first = findLowIndexInSortedArray(ary, mapLoFn, loVal);
-    if (first == 0) {
-      if (loVal >= mapLoFn(ary[0]) &&
-          loVal < mapLoFn(ary[0] + mapWidthFn(ary[0]))) {
-        return 0;
-      } else {
-        return -1;
-      }
-    } else if (first <= ary.length &&
-               loVal >= mapLoFn(ary[first - 1]) &&
-               loVal < mapLoFn(ary[first - 1]) + mapWidthFn(ary[first - 1])) {
-      return first - 1;
-    } else {
-      return ary.length;
-    }
-  }
-
-  /**
-   * Calls cb for all intervals in the implicit array of intervals
-   * defnied by ary, mapLoFn and mapHiFn that intersect the range
-   * [loVal,hiVal)
-   *
-   * This function uses the same scheme as findLowIndexInSortedArray
-   * to define the intervals. The same restrictions on sortedness and
-   * non-overlappingness apply.
-   *
-   * @param {Array} ary An array of objects that can be converted into sorted
-   * nonoverlapping ranges [x,y) using the mapLoFn and mapWidth.
-   * @param {function():*} mapLoFn Callback that produces the low value for the
-   * interval represented by an element in the array.
-   * @param {function():*} mapLoFn Callback that produces the width for the
-   * interval represented by an element in the array.
-   * @param {number} The low value for the search, inclusive.
-   * @param {number} loVal The high value for the search, non inclusive.
-   * @param {function():*} cb The function to run for intersecting intervals.
-   */
-  function iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal,
-                                            hiVal, cb) {
-    if (loVal > hiVal) return;
-
-    var i = findLowIndexInSortedArray(ary, mapLoFn, loVal);
-    if (i == -1) {
-      return;
-    }
-    if (i > 0) {
-      var hi = mapLoFn(ary[i - 1]) + mapWidthFn(ary[i - 1]);
-      if (hi >= loVal) {
-        cb(ary[i - 1]);
-      }
-    }
-    if (i == ary.length) {
-      return;
-    }
-
-    for (var n = ary.length; i < n; i++) {
-      var lo = mapLoFn(ary[i]);
-      if (lo >= hiVal)
-        break;
-      cb(ary[i]);
-    }
-  }
-
-  /**
-   * Non iterative version of iterateOverIntersectingIntervals.
-   *
-   * @return {Array} Array of elements in ary that intersect loVal, hiVal.
-   */
-  function getIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal) {
-    var tmp = [];
-    iterateOverIntersectingIntervals(ary, mapLoFn, mapWidthFn, loVal, hiVal,
-                                     function(d) {
-                                       tmp.push(d);
-                                     });
-    return tmp;
-  }
-
-  return {
-    findLowIndexInSortedArray: findLowIndexInSortedArray,
-    findLowIndexInSortedIntervals: findLowIndexInSortedIntervals,
-    iterateOverIntersectingIntervals: iterateOverIntersectingIntervals,
-    getIntersectingIntervals: getIntersectingIntervals
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-cr.define('tracing', function() {
-  /**
-   * Uses an embedded iframe to measure provided elements without forcing layout
-   * on the main document.
-   * @constructor
-   * @extends {Object}
-   */
-  function MeasuringStick() {
-    var iframe = document.createElement('iframe');
-    iframe.style.cssText = 'width:100%;height:0;border:0;visibility:hidden';
-    document.body.appendChild(iframe);
-    this._doc = iframe.contentDocument;
-    this._window = iframe.contentWindow;
-    this._doc.body.style.cssText = 'padding:0;margin:0;overflow:hidden';
-
-    var stylesheets = document.querySelectorAll('link[rel=stylesheet]');
-    for (var i = 0; i < stylesheets.length; i++) {
-      var stylesheet = stylesheets[i];
-      var link = this._doc.createElement('link');
-      link.rel = 'stylesheet';
-      link.href = stylesheet.href;
-      this._doc.head.appendChild(link);
-    }
-  }
-
-  MeasuringStick.prototype = {
-    __proto__: Object.prototype,
-
-    /**
-     * Measures the provided element without forcing layout on the main
-     * document.
-     */
-    measure: function(element) {
-      this._doc.body.appendChild(element);
-      var style = this._window.getComputedStyle(element);
-      var width = parseInt(style.width, 10);
-      var height = parseInt(style.height, 10);
-      this._doc.body.removeChild(element);
-      return { width: width, height: height };
-    }
-  };
-
-  return {
-    MeasuringStick: MeasuringStick
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Interactive visualizaiton of TimelineModel objects
- * based loosely on gantt charts. Each thread in the TimelineModel is given a
- * set of TimelineTracks, one per subrow in the thread. The Timeline class
- * acts as a controller, creating the individual tracks, while TimelineTracks
- * do actual drawing.
- *
- * Visually, the Timeline produces (prettier) visualizations like the following:
- *    Thread1:  AAAAAAAAAA         AAAAA
- *                  BBBB              BB
- *    Thread2:     CCCCCC                 CCCCC
- *
- */
-cr.define('tracing', function() {
-
-  /**
-   * The TimelineViewport manages the transform used for navigating
-   * within the timeline. It is a simple transform:
-   *   x' = (x+pan) * scale
-   *
-   * The timeline code tries to avoid directly accessing this transform,
-   * instead using this class to do conversion between world and view space,
-   * as well as the math for centering the viewport in various interesting
-   * ways.
-   *
-   * @constructor
-   * @extends {cr.EventTarget}
-   */
-  function TimelineViewport(parentEl) {
-    this.parentEl_ = parentEl;
-    this.scaleX_ = 1;
-    this.panX_ = 0;
-    this.gridTimebase_ = 0;
-    this.gridStep_ = 1000 / 60;
-    this.gridEnabled_ = false;
-    this.hasCalledSetupFunction_ = false;
-
-    this.onResizeBoundToThis_ = this.onResize_.bind(this);
-
-    // The following code uses an interval to detect when the parent element
-    // is attached to the document. That is a trigger to run the setup function
-    // and install a resize listener.
-    this.checkForAttachInterval_ = setInterval(
-        this.checkForAttach_.bind(this), 250);
-  }
-
-  TimelineViewport.prototype = {
-    __proto__: cr.EventTarget.prototype,
-
-    /**
-     * Allows initialization of the viewport when the viewport's parent element
-     * has been attached to the document and given a size.
-     * @param {Function} fn Function to call when the viewport can be safely
-     * initialized.
-     */
-    setWhenPossible: function(fn) {
-      this.pendingSetFunction_ = fn;
-    },
-
-    /**
-     * @return {boolean} Whether the current timeline is attached to the
-     * document.
-     */
-    get isAttachedToDocument_() {
-      var cur = this.parentEl_;
-      while (cur.parentNode)
-        cur = cur.parentNode;
-      return cur == this.parentEl_.ownerDocument;
-    },
-
-    onResize_: function() {
-      this.dispatchChangeEvent();
-    },
-
-    /**
-     * Checks whether the parentNode is attached to the document.
-     * When it is, it installs the iframe-based resize detection hook
-     * and then runs the pendingSetFunction_, if present.
-     */
-    checkForAttach_: function() {
-      if (!this.isAttachedToDocument_ || this.clientWidth == 0)
-        return;
-
-      if (!this.iframe_) {
-        this.iframe_ = document.createElement('iframe');
-        this.iframe_.style.cssText =
-            'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
-        this.parentEl_.appendChild(this.iframe_);
-
-        this.iframe_.contentWindow.addEventListener('resize',
-                                                    this.onResizeBoundToThis_);
-      }
-
-      var curSize = this.clientWidth + 'x' + this.clientHeight;
-      if (this.pendingSetFunction_) {
-        this.lastSize_ = curSize;
-        this.pendingSetFunction_();
-        this.pendingSetFunction_ = undefined;
-      }
-
-      window.clearInterval(this.checkForAttachInterval_);
-      this.checkForAttachInterval_ = undefined;
-    },
-
-    /**
-     * Fires the change event on this viewport. Used to notify listeners
-     * to redraw when the underlying model has been mutated.
-     */
-    dispatchChangeEvent: function() {
-      cr.dispatchSimpleEvent(this, 'change');
-    },
-
-    detach: function() {
-      if (this.checkForAttachInterval_) {
-        window.clearInterval(this.checkForAttachInterval_);
-        this.checkForAttachInterval_ = undefined;
-      }
-      this.iframe_.removeEventListener('resize', this.onResizeBoundToThis_);
-      this.parentEl_.removeChild(this.iframe_);
-    },
-
-    get scaleX() {
-      return this.scaleX_;
-    },
-    set scaleX(s) {
-      var changed = this.scaleX_ != s;
-      if (changed) {
-        this.scaleX_ = s;
-        this.dispatchChangeEvent();
-      }
-    },
-
-    get panX() {
-      return this.panX_;
-    },
-    set panX(p) {
-      var changed = this.panX_ != p;
-      if (changed) {
-        this.panX_ = p;
-        this.dispatchChangeEvent();
-      }
-    },
-
-    setPanAndScale: function(p, s) {
-      var changed = this.scaleX_ != s || this.panX_ != p;
-      if (changed) {
-        this.scaleX_ = s;
-        this.panX_ = p;
-        this.dispatchChangeEvent();
-      }
-    },
-
-    xWorldToView: function(x) {
-      return (x + this.panX_) * this.scaleX_;
-    },
-
-    xWorldVectorToView: function(x) {
-      return x * this.scaleX_;
-    },
-
-    xViewToWorld: function(x) {
-      return (x / this.scaleX_) - this.panX_;
-    },
-
-    xViewVectorToWorld: function(x) {
-      return x / this.scaleX_;
-    },
-
-    xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
-      if (typeof viewX == 'string') {
-        if (viewX == 'left') {
-          viewX = 0;
-        } else if (viewX == 'center') {
-          viewX = viewWidth / 2;
-        } else if (viewX == 'right') {
-          viewX = viewWidth - 1;
-        } else {
-          throw Error('unrecognized string for viewPos. left|center|right');
-        }
-      }
-      this.panX = (viewX / this.scaleX_) - worldX;
-    },
-
-    get gridEnabled() {
-      return this.gridEnabled_;
-    },
-
-    set gridEnabled(enabled) {
-      if (this.gridEnabled_ == enabled)
-        return;
-      this.gridEnabled_ = enabled && true;
-      this.dispatchChangeEvent();
-    },
-
-    get gridTimebase() {
-      return this.gridTimebase_;
-    },
-
-    set gridTimebase(timebase) {
-      if (this.gridTimebase_ == timebase)
-        return;
-      this.gridTimebase_ = timebase;
-      cr.dispatchSimpleEvent(this, 'change');
-    },
-
-    get gridStep() {
-      return this.gridStep_;
-    },
-
-    applyTransformToCanavs: function(ctx) {
-      ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
-    }
-  };
-
-  /**
-   * Renders a TimelineModel into a div element, making one
-   * TimelineTrack for each subrow in each thread of the model, managing
-   * overall track layout, and handling user interaction with the
-   * viewport.
-   *
-   * @constructor
-   * @extends {HTMLDivElement}
-   */
-  Timeline = cr.ui.define('div');
-
-  Timeline.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    model_: null,
-
-    decorate: function() {
-      this.classList.add('timeline');
-
-      this.viewport_ = new TimelineViewport(this);
-
-      this.tracks_ = this.ownerDocument.createElement('div');
-      this.appendChild(this.tracks_);
-
-      this.dragBox_ = this.ownerDocument.createElement('div');
-      this.dragBox_.className = 'timeline-drag-box';
-      this.appendChild(this.dragBox_);
-      this.hideDragBox_();
-
-      this.bindEventListener_(document, 'keypress', this.onKeypress_, this);
-      this.bindEventListener_(document, 'keydown', this.onKeydown_, this);
-      this.bindEventListener_(document, 'mousedown', this.onMouseDown_, this);
-      this.bindEventListener_(document, 'mousemove', this.onMouseMove_, this);
-      this.bindEventListener_(document, 'mouseup', this.onMouseUp_, this);
-      this.bindEventListener_(document, 'dblclick', this.onDblClick_, this);
-
-      this.lastMouseViewPos_ = {x: 0, y: 0};
-
-      this.selection_ = [];
-    },
-
-    /**
-     * Wraps the standard addEventListener but automatically binds the provided
-     * func to the provided target, tracking the resulting closure. When detach
-     * is called, these listeners will be automatically removed.
-     */
-    bindEventListener_: function(object, event, func, target) {
-      if (!this.boundListeners_)
-        this.boundListeners_ = [];
-      var boundFunc = func.bind(target);
-      this.boundListeners_.push({object: object,
-        event: event,
-        boundFunc: boundFunc});
-      object.addEventListener(event, boundFunc);
-    },
-
-    detach: function() {
-      for (var i = 0; i < this.tracks_.children.length; i++)
-        this.tracks_.children[i].detach();
-
-      for (var i = 0; i < this.boundListeners_.length; i++) {
-        var binding = this.boundListeners_[i];
-        binding.object.removeEventListener(binding.event, binding.boundFunc);
-      }
-      this.boundListeners_ = undefined;
-      this.viewport_.detach();
-    },
-
-    get viewport() {
-      return this.viewport_;
-    },
-
-    get model() {
-      return this.model_;
-    },
-
-    set model(model) {
-      if (!model)
-        throw Error('Model cannot be null');
-      if (this.model) {
-        throw Error('Cannot set model twice.');
-      }
-      this.model_ = model;
-
-      // Figure out all the headings.
-      var allHeadings = [];
-      model.getAllThreads().forEach(function(t) {
-        allHeadings.push(t.userFriendlyName);
-      });
-      model.getAllCounters().forEach(function(c) {
-        allHeadings.push(c.name);
-      });
-      model.getAllCpus().forEach(function(c) {
-        allHeadings.push('CPU ' + c.cpuNumber);
-      });
-
-      // Figure out the maximum heading size.
-      var maxHeadingWidth = 0;
-      var measuringStick = new tracing.MeasuringStick();
-      var headingEl = document.createElement('div');
-      headingEl.style.position = 'fixed';
-      headingEl.className = 'timeline-canvas-based-track-title';
-      allHeadings.forEach(function(text) {
-        headingEl.textContent = text + ':__';
-        var w = measuringStick.measure(headingEl).width;
-        // Limit heading width to 300px.
-        if (w > 300)
-          w = 300;
-        if (w > maxHeadingWidth)
-          maxHeadingWidth = w;
-      });
-      maxHeadingWidth = maxHeadingWidth + 'px';
-
-      // Reset old tracks.
-      for (var i = 0; i < this.tracks_.children.length; i++)
-        this.tracks_.children[i].detach();
-      this.tracks_.textContent = '';
-
-      // Get a sorted list of CPUs
-      var cpus = model.getAllCpus();
-      cpus.sort(tracing.TimelineCpu.compare);
-
-      // Create tracks for each CPU.
-      cpus.forEach(function(cpu) {
-        var track = new tracing.TimelineCpuTrack();
-        track.heading = 'CPU ' + cpu.cpuNumber + ':';
-        track.headingWidth = maxHeadingWidth;
-        track.viewport = this.viewport_;
-        track.cpu = cpu;
-        this.tracks_.appendChild(track);
-
-        for (var counterName in cpu.counters) {
-          var counter = cpu.counters[counterName];
-          track = new tracing.TimelineCounterTrack();
-          track.heading = 'CPU ' + cpu.cpuNumber + ' ' + counter.name + ':';
-          track.headingWidth = maxHeadingWidth;
-          track.viewport = this.viewport_;
-          track.counter = counter;
-          this.tracks_.appendChild(track);
-        }
-      }.bind(this));
-
-      // Get a sorted list of processes.
-      var processes = model.getAllProcesses();
-      processes.sort(tracing.TimelineProcess.compare);
-
-      // Create tracks for each process.
-      processes.forEach(function(process) {
-        // Add counter tracks for this process.
-        var counters = [];
-        for (var tid in process.counters)
-          counters.push(process.counters[tid]);
-        counters.sort(tracing.TimelineCounter.compare);
-
-        // Create the counters for this process.
-        counters.forEach(function(counter) {
-          var track = new tracing.TimelineCounterTrack();
-          track.heading = counter.name + ':';
-          track.headingWidth = maxHeadingWidth;
-          track.viewport = this.viewport_;
-          track.counter = counter;
-          this.tracks_.appendChild(track);
-        }.bind(this));
-
-        // Get a sorted list of threads.
-        var threads = [];
-        for (var tid in process.threads)
-          threads.push(process.threads[tid]);
-        threads.sort(tracing.TimelineThread.compare);
-
-        // Create the threads.
-        threads.forEach(function(thread) {
-          var track = new tracing.TimelineThreadTrack();
-          track.heading = thread.userFriendlyName + ':';
-          track.tooltip = thread.userFriendlyDetials;
-          track.headingWidth = maxHeadingWidth;
-          track.viewport = this.viewport_;
-          track.thread = thread;
-          this.tracks_.appendChild(track);
-        }.bind(this));
-      }.bind(this));
-
-      // Set up a reasonable viewport.
-      this.viewport_.setWhenPossible(function() {
-        var rangeTimestamp = this.model_.maxTimestamp -
-            this.model_.minTimestamp;
-        var w = this.firstCanvas.width;
-        var scaleX = w / rangeTimestamp;
-        var panX = -this.model_.minTimestamp;
-        this.viewport_.setPanAndScale(panX, scaleX);
-      }.bind(this));
-    },
-
-    /**
-     * @return {Element} The element whose focused state determines
-     * whether to respond to keyboard inputs.
-     * Defaults to the parent element.
-     */
-    get focusElement() {
-      if (this.focusElement_)
-        return this.focusElement_;
-      return this.parentElement;
-    },
-
-    /**
-     * Sets the element whose focus state will determine whether
-     * to respond to keybaord input.
-     */
-    set focusElement(value) {
-      this.focusElement_ = value;
-    },
-
-    get listenToKeys_() {
-      if (!this.focusElement_)
-        return true;
-      if (this.focusElement.tabIndex >= 0)
-        return document.activeElement == this.focusElement;
-      return true;
-    },
-
-    onKeypress_: function(e) {
-      var vp = this.viewport_;
-      if (!this.firstCanvas)
-        return;
-      if (!this.listenToKeys_)
-        return;
-      var viewWidth = this.firstCanvas.clientWidth;
-      var curMouseV, curCenterW;
-      switch (e.keyCode) {
-        case 101: // e
-          var vX = this.lastMouseViewPos_.x;
-          var wX = vp.xViewToWorld(this.lastMouseViewPos_.x);
-          var distFromCenter = vX - (viewWidth / 2);
-          var percFromCenter = distFromCenter / viewWidth;
-          var percFromCenterSq = percFromCenter * percFromCenter;
-          vp.xPanWorldPosToViewPos(wX, 'center', viewWidth);
-          break;
-        case 119:  // w
-          this.zoomBy_(1.5);
-          break;
-        case 115:  // s
-          this.zoomBy_(1 / 1.5);
-          break;
-        case 103:  // g
-          this.onGridToggle_(true);
-          break;
-        case 71:  // G
-          this.onGridToggle_(false);
-          break;
-        case 87:  // W
-          this.zoomBy_(10);
-          break;
-        case 83:  // S
-          this.zoomBy_(1 / 10);
-          break;
-        case 97:  // a
-          vp.panX += vp.xViewVectorToWorld(viewWidth * 0.1);
-          break;
-        case 100:  // d
-          vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.1);
-          break;
-        case 65:  // A
-          vp.panX += vp.xViewVectorToWorld(viewWidth * 0.5);
-          break;
-        case 68:  // D
-          vp.panX -= vp.xViewVectorToWorld(viewWidth * 0.5);
-          break;
-      }
-    },
-
-    // Not all keys send a keypress.
-    onKeydown_: function(e) {
-      if (!this.listenToKeys_)
-        return;
-      switch (e.keyCode) {
-        case 37:   // left arrow
-          this.selectPrevious_(e);
-          e.preventDefault();
-          break;
-        case 39:   // right arrow
-          this.selectNext_(e);
-          e.preventDefault();
-          break;
-        case 9:    // TAB
-          if (this.focusElement.tabIndex == -1) {
-            if (e.shiftKey)
-              this.selectPrevious_(e);
-            else
-              this.selectNext_(e);
-            e.preventDefault();
-          }
-          break;
-      }
-    },
-
-    /**
-     * Zoom in or out on the timeline by the given scale factor.
-     * @param {integer} scale The scale factor to apply.  If <1, zooms out.
-     */
-    zoomBy_: function(scale) {
-      if (!this.firstCanvas)
-        return;
-      var vp = this.viewport_;
-      var viewWidth = this.firstCanvas.clientWidth;
-      var curMouseV = this.lastMouseViewPos_.x;
-      var curCenterW = vp.xViewToWorld(curMouseV);
-      vp.scaleX = vp.scaleX * scale;
-      vp.xPanWorldPosToViewPos(curCenterW, curMouseV, viewWidth);
-    },
-
-    /** Select the next slice on the timeline.  Applies to each track. */
-    selectNext_: function(e) {
-      this.selectAdjoining_(e, true);
-    },
-
-    /** Select the previous slice on the timeline.  Applies to each track. */
-    selectPrevious_: function(e) {
-      this.selectAdjoining_(e, false);
-    },
-
-    /**
-     * Helper for selection previous or next.
-     * @param {Event} The current event.
-     * @param {boolean} forwardp If true, select one forward (next).
-     *   Else, select previous.
-     */
-    selectAdjoining_: function(e, forwardp) {
-      var i, track, slice, adjoining;
-      var selection = [];
-      // Clear old selection; try and select next.
-      for (i = 0; i < this.selection_.length; i++) {
-        adjoining = undefined;
-        this.selection_[i].slice.selected = false;
-        track = this.selection_[i].track;
-        slice = this.selection_[i].slice;
-        if (slice) {
-          if (forwardp)
-            adjoining = track.pickNext(slice);
-          else
-            adjoining = track.pickPrevious(slice);
-        }
-        if (adjoining != undefined)
-          selection.push({track: track, slice: adjoining});
-      }
-      this.selection = selection;
-      e.preventDefault();
-    },
-
-    get keyHelp() {
-      var help = 'Keyboard shortcuts:\n' +
-          ' w/s     : Zoom in/out    (with shift: go faster)\n' +
-          ' a/d     : Pan left/right\n' +
-          ' e       : Center on mouse\n' +
-          ' g/G     : Shows grid at the start/end of the selected task\n';
-
-      if (this.focusElement.tabIndex) {
-        help += ' <-      : Select previous event on current timeline\n' +
-            ' ->      : Select next event on current timeline\n';
-      } else {
-        help += ' <-,^TAB : Select previous event on current timeline\n' +
-            ' ->, TAB : Select next event on current timeline\n';
-      }
-      help +=
-          '\n' +
-          'Dbl-click to zoom in; Shift dbl-click to zoom out\n';
-      return help;
-    },
-
-    get selection() {
-      return this.selection_;
-    },
-
-    set selection(selection) {
-      // Clear old selection.
-      for (i = 0; i < this.selection_.length; i++)
-        this.selection_[i].slice.selected = false;
-
-      this.selection_ = selection;
-
-      cr.dispatchSimpleEvent(this, 'selectionChange');
-      for (i = 0; i < this.selection_.length; i++)
-        this.selection_[i].slice.selected = true;
-      this.viewport_.dispatchChangeEvent(); // Triggers a redraw.
-    },
-
-    get firstCanvas() {
-      return this.tracks_.firstChild ?
-          this.tracks_.firstChild.firstCanvas : undefined;
-    },
-
-    hideDragBox_: function() {
-      this.dragBox_.style.left = '-1000px';
-      this.dragBox_.style.top = '-1000px';
-      this.dragBox_.style.width = 0;
-      this.dragBox_.style.height = 0;
-    },
-
-    setDragBoxPosition_: function(eDown, eCur) {
-      var loX = Math.min(eDown.clientX, eCur.clientX);
-      var hiX = Math.max(eDown.clientX, eCur.clientX);
-      var loY = Math.min(eDown.clientY, eCur.clientY);
-      var hiY = Math.max(eDown.clientY, eCur.clientY);
-
-      this.dragBox_.style.left = loX + 'px';
-      this.dragBox_.style.top = loY + 'px';
-      this.dragBox_.style.width = hiX - loX + 'px';
-      this.dragBox_.style.height = hiY - loY + 'px';
-
-      var canv = this.firstCanvas;
-      var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft);
-      var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft);
-
-      var roundedDuration = Math.round((hiWX - loWX) * 100) / 100;
-      this.dragBox_.textContent = roundedDuration + 'ms';
-
-      var e = new cr.Event('selectionChanging');
-      e.loWX = loWX;
-      e.hiWX = hiWX;
-      this.dispatchEvent(e);
-    },
-
-    onGridToggle_: function(left) {
-      var tb;
-      if (left)
-        tb = Math.min.apply(Math, this.selection_.map(
-            function(x) { return x.slice.start; }));
-      else
-        tb = Math.max.apply(Math, this.selection_.map(
-            function(x) { return x.slice.end; }));
-
-      // Shift the timebase left until its just left of minTimestamp.
-      var numInterfvalsSinceStart = Math.ceil((tb - this.model_.minTimestamp) /
-          this.viewport_.gridStep_);
-      this.viewport_.gridTimebase = tb -
-          (numInterfvalsSinceStart + 1) * this.viewport_.gridStep_;
-      this.viewport_.gridEnabled = true;
-    },
-
-    onMouseDown_: function(e) {
-      rect = this.tracks_.getClientRects()[0];
-      var inside = rect &&
-          e.clientX >= rect.left &&
-          e.clientX < rect.right &&
-          e.clientY >= rect.top &&
-          e.clientY < rect.bottom;
-      if (!inside)
-        return;
-
-      var canv = this.firstCanvas;
-      var pos = {
-        x: e.clientX - canv.offsetLeft,
-        y: e.clientY - canv.offsetTop
-      };
-
-      var wX = this.viewport_.xViewToWorld(pos.x);
-
-      this.dragBeginEvent_ = e;
-      e.preventDefault();
-      if (this.focusElement.tabIndex >= 0)
-        this.focusElement.focus();
-    },
-
-    onMouseMove_: function(e) {
-      if (!this.firstCanvas)
-        return;
-      var canv = this.firstCanvas;
-      var pos = {
-        x: e.clientX - canv.offsetLeft,
-        y: e.clientY - canv.offsetTop
-      };
-
-      // Remember position. Used during keyboard zooming.
-      this.lastMouseViewPos_ = pos;
-
-      // Update the drag box
-      if (this.dragBeginEvent_) {
-        this.setDragBoxPosition_(this.dragBeginEvent_, e);
-      }
-    },
-
-    onMouseUp_: function(e) {
-      var i;
-      if (this.dragBeginEvent_) {
-        // Stop the dragging.
-        this.hideDragBox_();
-        var eDown = this.dragBeginEvent_;
-        this.dragBeginEvent_ = null;
-
-        // Figure out extents of the drag.
-        var loX = Math.min(eDown.clientX, e.clientX);
-        var hiX = Math.max(eDown.clientX, e.clientX);
-        var loY = Math.min(eDown.clientY, e.clientY);
-        var hiY = Math.max(eDown.clientY, e.clientY);
-
-        // Convert to worldspace.
-        var canv = this.firstCanvas;
-        var loWX = this.viewport_.xViewToWorld(loX - canv.offsetLeft);
-        var hiWX = this.viewport_.xViewToWorld(hiX - canv.offsetLeft);
-
-        // Figure out what has been hit.
-        var selection = [];
-        function addHit(type, track, slice) {
-          selection.push({track: track, slice: slice});
-        }
-        for (i = 0; i < this.tracks_.children.length; i++) {
-          var track = this.tracks_.children[i];
-
-          // Only check tracks that insersect the rect.
-          var trackClientRect = track.getBoundingClientRect();
-          var a = Math.max(loY, trackClientRect.top);
-          var b = Math.min(hiY, trackClientRect.bottom);
-          if (a <= b) {
-            track.pickRange(loWX, hiWX, loY, hiY, addHit);
-          }
-        }
-        // Activate the new selection.
-        this.selection = selection;
-      }
-    },
-
-    onDblClick_: function(e) {
-      var scale = 4;
-      if (e.shiftKey)
-        scale = 1 / scale;
-      this.zoomBy_(scale);
-      e.preventDefault();
-    }
-  };
-
-  /**
-   * The TimelineModel being viewed by the timeline
-   * @type {TimelineModel}
-   */
-  cr.defineProperty(Timeline, 'model', cr.PropertyKind.JS);
-
-  return {
-    Timeline: Timeline,
-    TimelineViewport: TimelineViewport
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * @fileoverview Renders an array of slices into the provided div,
- * using a child canvas element. Uses a FastRectRenderer to draw only
- * the visible slices.
- */
-cr.define('tracing', function() {
-
-  var pallette = tracing.getPallette();
-  var highlightIdBoost = tracing.getPalletteHighlightIdBoost();
-
-  var textWidthMap = { };
-  function quickMeasureText(ctx, text) {
-    var w = textWidthMap[text];
-    if (!w) {
-      w = ctx.measureText(text).width;
-      textWidthMap[text] = w;
-    }
-    return w;
-  }
-
-  /**
-   * A generic track that contains other tracks as its children.
-   * @constructor
-   */
-  var TimelineContainerTrack = cr.ui.define('div');
-  TimelineContainerTrack.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    decorate: function() {
-      this.tracks_ = [];
-    },
-
-    detach: function() {
-      for (var i = 0; i < this.tracks_.length; i++)
-        this.tracks_[i].detach();
-    },
-
-    get viewport() {
-      return this.viewport_;
-    },
-
-    set viewport(v) {
-      this.viewport_ = v;
-      for (var i = 0; i < this.tracks_.length; i++)
-        this.tracks_[i].viewport = v;
-      this.updateChildTracks_();
-    },
-
-    get firstCanvas() {
-      if (this.tracks_.length)
-        return this.tracks_[0].firstCanvas;
-      return undefined;
-    },
-
-    /**
-     * Picks a slice, if any, at a given location.
-     * @param {number} wX X location to search at, in worldspace.
-     * @param {number} wY Y location to search at, in offset space.
-     *     offset space.
-     * @param {function():*} onHitCallback Callback to call with the slice,
-     *     if one is found.
-     * @return {boolean} true if a slice was found, otherwise false.
-     */
-    pick: function(wX, wY, onHitCallback) {
-      for (var i = 0; i < this.tracks_.length; i++) {
-        var trackClientRect = this.tracks_[i].getBoundingClientRect();
-        if (wY >= trackClientRect.top && wY < trackClientRect.bottom)
-          return this.tracks_[i].pick(wX, onHitCallback);
-      }
-      return false;
-    },
-
-    /**
-     * Finds slices intersecting the given interval.
-     * @param {number} loWX Lower X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} hiWX Upper X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} loY Lower Y bound of the interval to search, in
-     *     offset space.
-     * @param {number} hiY Upper Y bound of the interval to search, in
-     *     offset space.
-     * @param {function():*} onHitCallback Function to call for each slice
-     *     intersecting the interval.
-     */
-    pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
-      for (var i = 0; i < this.tracks_.length; i++) {
-        var trackClientRect = this.tracks_[i].getBoundingClientRect();
-        var a = Math.max(loY, trackClientRect.top);
-        var b = Math.min(hiY, trackClientRect.bottom);
-        if (a <= b)
-          this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback);
-      }
-    }
-  };
-
-  /**
-   * Visualizes a TimelineThread using a series of of TimelineSliceTracks.
-   * @constructor
-   */
-  var TimelineThreadTrack = cr.ui.define(TimelineContainerTrack);
-  TimelineThreadTrack.prototype = {
-    __proto__: TimelineContainerTrack.prototype,
-
-    decorate: function() {
-      this.classList.add('timeline-thread-track');
-    },
-
-    get thread(thread) {
-      return this.thread_;
-    },
-
-    set thread(thread) {
-      this.thread_ = thread;
-      this.updateChildTracks_();
-    },
-
-    get tooltip() {
-      return this.tooltip_;
-    },
-
-    set tooltip(value) {
-      this.tooltip_ = value;
-      this.updateChildTracks_();
-    },
-
-    get heading() {
-      return this.heading_;
-    },
-
-    set heading(h) {
-      this.heading_ = h;
-      this.updateChildTracks_();
-    },
-
-    get headingWidth() {
-      return this.headingWidth_;
-    },
-
-    set headingWidth(width) {
-      this.headingWidth_ = width;
-      this.updateChildTracks_();
-    },
-
-    addTrack_: function(slices) {
-      var track = new TimelineSliceTrack();
-      track.heading = '';
-      track.slices = slices;
-      track.headingWidth = this.headingWidth_;
-      track.viewport = this.viewport_;
-
-      this.tracks_.push(track);
-      this.appendChild(track);
-      return track;
-    },
-
-    updateChildTracks_: function() {
-      this.detach();
-      this.textContent = '';
-      this.tracks_ = [];
-      if (this.thread_) {
-        if (this.thread_.cpuSlices) {
-          var track = this.addTrack_(this.thread_.cpuSlices);
-          track.height = '4px';
-        }
-
-        for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) {
-          this.addTrack_(this.thread_.nonNestedSubRows[srI]);
-        }
-        for (var srI = 0; srI < this.thread_.subRows.length; ++srI) {
-          this.addTrack_(this.thread_.subRows[srI]);
-        }
-        if (this.tracks_.length > 0) {
-          if (this.thread_.cpuSlices) {
-            this.tracks_[1].heading = this.heading_;
-            this.tracks_[1].tooltip = this.tooltip_;
-          } else {
-            this.tracks_[0].heading = this.heading_;
-            this.tracks_[0].tooltip = this.tooltip_;
-          }
-        }
-      }
-    }
-  };
-
-  /**
-   * Visualizes a TimelineCpu using a series of of TimelineSliceTracks.
-   * @constructor
-   */
-  var TimelineCpuTrack = cr.ui.define(TimelineContainerTrack);
-  TimelineCpuTrack.prototype = {
-    __proto__: TimelineContainerTrack.prototype,
-
-    decorate: function() {
-      this.classList.add('timeline-thread-track');
-    },
-
-    get cpu(cpu) {
-      return this.cpu_;
-    },
-
-    set cpu(cpu) {
-      this.cpu_ = cpu;
-      this.updateChildTracks_();
-    },
-
-    get tooltip() {
-      return this.tooltip_;
-    },
-
-    set tooltip(value) {
-      this.tooltip_ = value;
-      this.updateChildTracks_();
-    },
-
-    get heading() {
-      return this.heading_;
-    },
-
-    set heading(h) {
-      this.heading_ = h;
-      this.updateChildTracks_();
-    },
-
-    get headingWidth() {
-      return this.headingWidth_;
-    },
-
-    set headingWidth(width) {
-      this.headingWidth_ = width;
-      this.updateChildTracks_();
-    },
-
-    updateChildTracks_: function() {
-      this.detach();
-      this.textContent = '';
-      this.tracks_ = [];
-      if (this.cpu_) {
-        var track = new TimelineSliceTrack();
-        track.slices = this.cpu_.slices;
-        track.headingWidth = this.headingWidth_;
-        track.viewport = this.viewport_;
-
-        this.tracks_.push(track);
-        this.appendChild(track);
-
-        this.tracks_[0].heading = this.heading_;
-        this.tracks_[0].tooltip = this.tooltip_;
-      }
-    }
-  };
-
-  /**
-   * A canvas-based track constructed. Provides the basic heading and
-   * invalidation-managment infrastructure. Subclasses must implement drawing
-   * and picking code.
-   * @constructor
-   * @extends {HTMLDivElement}
-   */
-  var CanvasBasedTrack = cr.ui.define('div');
-
-  CanvasBasedTrack.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    decorate: function() {
-      this.className = 'timeline-canvas-based-track';
-      this.slices_ = null;
-
-      this.headingDiv_ = document.createElement('div');
-      this.headingDiv_.className = 'timeline-canvas-based-track-title';
-      this.appendChild(this.headingDiv_);
-
-      this.canvasContainer_ = document.createElement('div');
-      this.canvasContainer_.className =
-          'timeline-canvas-based-track-canvas-container';
-      this.appendChild(this.canvasContainer_);
-      this.canvas_ = document.createElement('canvas');
-      this.canvas_.className = 'timeline-canvas-based-track-canvas';
-      this.canvasContainer_.appendChild(this.canvas_);
-
-      this.ctx_ = this.canvas_.getContext('2d');
-    },
-
-    detach: function() {
-      if (this.viewport_)
-        this.viewport_.removeEventListener('change',
-                                           this.viewportChangeBoundToThis_);
-    },
-
-    set headingWidth(width) {
-      this.headingDiv_.style.width = width;
-    },
-
-    get heading() {
-      return this.headingDiv_.textContent;
-    },
-
-    set heading(text) {
-      this.headingDiv_.textContent = text;
-    },
-
-    set tooltip(text) {
-      this.headingDiv_.title = text;
-    },
-
-    get viewport() {
-      return this.viewport_;
-    },
-
-    set viewport(v) {
-      this.viewport_ = v;
-      if (this.viewport_)
-        this.viewport_.removeEventListener('change',
-                                           this.viewportChangeBoundToThis_);
-      this.viewport_ = v;
-      if (this.viewport_) {
-        this.viewportChangeBoundToThis_ = this.viewportChange_.bind(this);
-        this.viewport_.addEventListener('change',
-                                        this.viewportChangeBoundToThis_);
-      }
-      this.invalidate();
-    },
-
-    viewportChange_: function() {
-      this.invalidate();
-    },
-
-    invalidate: function() {
-      if (this.rafPending_)
-        return;
-      webkitRequestAnimationFrame(function() {
-        this.rafPending_ = false;
-        if (!this.viewport_)
-          return;
-
-        if (this.canvas_.width != this.canvasContainer_.clientWidth)
-          this.canvas_.width = this.canvasContainer_.clientWidth;
-        if (this.canvas_.height != this.canvasContainer_.clientHeight)
-          this.canvas_.height = this.canvasContainer_.clientHeight;
-
-        this.redraw();
-      }.bind(this), this);
-      this.rafPending_ = true;
-    },
-
-    get firstCanvas() {
-      return this.canvas_;
-    }
-
-  };
-
-  /**
-   * A track that displays an array of TimelineSlice objects.
-   * @constructor
-   * @extends {CanvasBasedTrack}
-   */
-
-  var TimelineSliceTrack = cr.ui.define(CanvasBasedTrack);
-
-  TimelineSliceTrack.prototype = {
-
-    __proto__: CanvasBasedTrack.prototype,
-
-    decorate: function() {
-      this.classList.add('timeline-slice-track');
-    },
-
-    get slices() {
-      return this.slices_;
-    },
-
-    set slices(slices) {
-      this.slices_ = slices;
-      this.invalidate();
-    },
-
-    set height(height) {
-      this.style.height = height;
-    },
-
-    redraw: function() {
-      var ctx = this.ctx_;
-      var canvasW = this.canvas_.width;
-      var canvasH = this.canvas_.height;
-
-      ctx.clearRect(0, 0, canvasW, canvasH);
-
-      // Culling parameters.
-      var vp = this.viewport_;
-      var pixWidth = vp.xViewVectorToWorld(1);
-      var viewLWorld = vp.xViewToWorld(0);
-      var viewRWorld = vp.xViewToWorld(canvasW);
-
-      // Draw grid without a transform because the scale
-      // affects line width.
-      if (vp.gridEnabled) {
-        var x = vp.gridTimebase;
-        ctx.beginPath();
-        while (x < viewRWorld) {
-          if (x >= viewLWorld) {
-            // Do conversion to viewspace here rather than on
-            // x to avoid precision issues.
-            var vx = vp.xWorldToView(x);
-            ctx.moveTo(vx, 0);
-            ctx.lineTo(vx, canvasH);
-          }
-          x += vp.gridStep;
-        }
-        ctx.strokeStyle = 'rgba(255,0,0,0.25)';
-        ctx.stroke();
-      }
-
-      // Begin rendering in world space.
-      ctx.save();
-      vp.applyTransformToCanavs(ctx);
-
-      // Slices.
-      var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
-                                            2 * pixWidth, viewRWorld, pallette);
-      tr.setYandH(0, canvasH);
-      var slices = this.slices_;
-      for (var i = 0; i < slices.length; ++i) {
-        var slice = slices[i];
-        var x = slice.start;
-        // Less than 0.001 causes short events to disappear when zoomed in.
-        var w = Math.max(slice.duration, 0.001);
-        var colorId = slice.selected ?
-            slice.colorId + highlightIdBoost :
-            slice.colorId;
-
-        if (w < pixWidth)
-          w = pixWidth;
-        if (slice.duration > 0) {
-          tr.fillRect(x, w, colorId);
-        } else {
-          // Instant: draw a triangle.  If zoomed too far, collapse
-          // into the FastRectRenderer.
-          if (pixWidth > 0.001) {
-            tr.fillRect(x, pixWidth, colorId);
-          } else {
-            ctx.fillStyle = pallette[colorId];
-            ctx.beginPath();
-            ctx.moveTo(x - (4 * pixWidth), canvasH);
-            ctx.lineTo(x, 0);
-            ctx.lineTo(x + (4 * pixWidth), canvasH);
-            ctx.closePath();
-            ctx.fill();
-          }
-        }
-      }
-      tr.flush();
-      ctx.restore();
-
-      // Labels.
-      if (canvasH > 8) {
-        ctx.textAlign = 'center';
-        ctx.textBaseline = 'top';
-        ctx.font = '10px sans-serif';
-        ctx.strokeStyle = 'rgb(0,0,0)';
-        ctx.fillStyle = 'rgb(0,0,0)';
-        // Don't render text until until it is 20px wide
-        var quickDiscardThresshold = pixWidth * 20;
-        for (var i = 0; i < slices.length; ++i) {
-          var slice = slices[i];
-          if (slice.duration > quickDiscardThresshold) {
-            var title = slice.title;
-            if (slice.didNotFinish) {
-              title += ' (Did Not Finish)';
-            }
-            var labelWidth = quickMeasureText(ctx, title) + 2;
-            var labelWidthWorld = pixWidth * labelWidth;
-
-            if (labelWidthWorld < slice.duration) {
-              var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
-              ctx.fillText(title, cX, 2.5, labelWidth);
-            }
-          }
-        }
-      }
-    },
-
-    /**
-     * Picks a slice, if any, at a given location.
-     * @param {number} wX X location to search at, in worldspace.
-     * @param {number} wY Y location to search at, in offset space.
-     *     offset space.
-     * @param {function():*} onHitCallback Callback to call with the slice,
-     *     if one is found.
-     * @return {boolean} true if a slice was found, otherwise false.
-     */
-    pick: function(wX, wY, onHitCallback) {
-      var clientRect = this.getBoundingClientRect();
-      if (wY < clientRect.top || wY >= clientRect.bottom)
-        return false;
-      var x = tracing.findLowIndexInSortedIntervals(this.slices_,
-          function(x) { return x.start; },
-          function(x) { return x.duration; },
-          wX);
-      if (x >= 0 && x < this.slices_.length) {
-        onHitCallback('slice', this, this.slices_[x]);
-        return true;
-      }
-      return false;
-    },
-
-    /**
-     * Finds slices intersecting the given interval.
-     * @param {number} loWX Lower X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} hiWX Upper X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} loY Lower Y bound of the interval to search, in
-     *     offset space.
-     * @param {number} hiY Upper Y bound of the interval to search, in
-     *     offset space.
-     * @param {function():*} onHitCallback Function to call for each slice
-     *     intersecting the interval.
-     */
-    pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
-      var clientRect = this.getBoundingClientRect();
-      var a = Math.max(loY, clientRect.top);
-      var b = Math.min(hiY, clientRect.bottom);
-      if (a > b)
-        return;
-
-      var that = this;
-      function onPickHit(slice) {
-        onHitCallback('slice', that, slice);
-      }
-      tracing.iterateOverIntersectingIntervals(this.slices_,
-          function(x) { return x.start; },
-          function(x) { return x.duration; },
-          loWX, hiWX,
-          onPickHit);
-    },
-
-    /**
-     * Find the index for the given slice.
-     * @return {index} Index of the given slice, or undefined.
-     * @private
-     */
-    indexOfSlice_: function(slice) {
-      var index = tracing.findLowIndexInSortedArray(this.slices_,
-          function(x) { return x.start; },
-          slice.start);
-      while (index < this.slices_.length &&
-          slice.start == this.slices_[index].start &&
-          slice.colorId != this.slices_[index].colorId) {
-        index++;
-      }
-      return index < this.slices_.length ? index : undefined;
-    },
-
-    /**
-     * Return the next slice, if any, after the given slice.
-     * @param {slice} The previous slice.
-     * @return {slice} The next slice, or undefined.
-     * @private
-     */
-    pickNext: function(slice) {
-      var index = this.indexOfSlice_(slice);
-      if (index != undefined) {
-        if (index < this.slices_.length - 1)
-          index++;
-        else
-          index = undefined;
-      }
-      return index != undefined ? this.slices_[index] : undefined;
-    },
-
-    /**
-     * Return the previous slice, if any, before the given slice.
-     * @param {slice} A slice.
-     * @return {slice} The previous slice, or undefined.
-     */
-    pickPrevious: function(slice) {
-      var index = this.indexOfSlice_(slice);
-      if (index == 0)
-        return undefined;
-      else if ((index != undefined) && (index > 0))
-        index--;
-      return index != undefined ? this.slices_[index] : undefined;
-    }
-
-  };
-
-  /**
-   * A track that displays a TimelineCounter object.
-   * @constructor
-   * @extends {CanvasBasedTrack}
-   */
-
-  var TimelineCounterTrack = cr.ui.define(CanvasBasedTrack);
-
-  TimelineCounterTrack.prototype = {
-
-    __proto__: CanvasBasedTrack.prototype,
-
-    decorate: function() {
-      this.classList.add('timeline-counter-track');
-    },
-
-    get counter() {
-      return this.counter_;
-    },
-
-    set counter(counter) {
-      this.counter_ = counter;
-      this.invalidate();
-    },
-
-    redraw: function() {
-      var ctr = this.counter_;
-      var ctx = this.ctx_;
-      var canvasW = this.canvas_.width;
-      var canvasH = this.canvas_.height;
-
-      ctx.clearRect(0, 0, canvasW, canvasH);
-
-      // Culling parametrs.
-      var vp = this.viewport_;
-      var pixWidth = vp.xViewVectorToWorld(1);
-      var viewLWorld = vp.xViewToWorld(0);
-      var viewRWorld = vp.xViewToWorld(canvasW);
-
-      // Drop sampels that are less than skipDistancePix apart.
-      var skipDistancePix = 1;
-      var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix);
-
-      // Begin rendering in world space.
-      ctx.save();
-      vp.applyTransformToCanavs(ctx);
-
-      // Figure out where drawing should begin.
-      var numSeries = ctr.numSeries;
-      var numSamples = ctr.numSamples;
-      var startIndex = tracing.findLowIndexInSortedArray(ctr.timestamps,
-                                                         function() {
-                                                         },
-                                                         viewLWorld);
-
-      // Draw indices one by one until we fall off the viewRWorld.
-      var yScale = canvasH / ctr.maxTotal;
-      for (var seriesIndex = ctr.numSeries - 1;
-           seriesIndex >= 0; seriesIndex--) {
-        var colorId = ctr.seriesColors[seriesIndex];
-        ctx.fillStyle = pallette[colorId];
-        ctx.beginPath();
-
-        // Set iLast and xLast such that the first sample we draw is the
-        // startIndex sample.
-        var iLast = startIndex - 1;
-        var xLast = iLast >= 0 ? ctr.timestamps[iLast] - skipDistanceWorld : -1;
-        var yLastView = canvasH;
-
-        // Iterate over samples from iLast onward until we either fall off the
-        // viewRWorld or we run out of samples. To avoid drawing too much, after
-        // drawing a sample at xLast, skip subsequent samples that are less than
-        // skipDistanceWorld from xLast.
-        var hasMoved = false;
-        while (true) {
-          var i = iLast + 1;
-          if (i >= numSamples) {
-            ctx.lineTo(xLast, yLastView);
-            ctx.lineTo(xLast + 8 * pixWidth, yLastView);
-            ctx.lineTo(xLast + 8 * pixWidth, canvasH);
-            break;
-          }
-
-          var x = ctr.timestamps[i];
-
-          var y = ctr.totals[i * numSeries + seriesIndex];
-          var yView = canvasH - (yScale * y);
-
-          if (x > viewRWorld) {
-            ctx.lineTo(x, yLastView);
-            ctx.lineTo(x, canvasH);
-            break;
-          }
-
-          if (x - xLast < skipDistanceWorld) {
-            iLast = i;
-            continue;
-          }
-
-          if (!hasMoved) {
-            ctx.moveTo(viewLWorld, canvasH);
-            hasMoved = true;
-          }
-          ctx.lineTo(x, yLastView);
-          ctx.lineTo(x, yView);
-          iLast = i;
-          xLast = x;
-          yLastView = yView;
-        }
-        ctx.closePath();
-        ctx.fill();
-      }
-      ctx.restore();
-    },
-
-    /**
-     * Picks a slice, if any, at a given location.
-     * @param {number} wX X location to search at, in worldspace.
-     * @param {number} wY Y location to search at, in offset space.
-     *     offset space.
-     * @param {function():*} onHitCallback Callback to call with the slice,
-     *     if one is found.
-     * @return {boolean} true if a slice was found, otherwise false.
-     */
-    pick: function(wX, wY, onHitCallback) {
-    },
-
-    /**
-     * Finds slices intersecting the given interval.
-     * @param {number} loWX Lower X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} hiWX Upper X bound of the interval to search, in
-     *     worldspace.
-     * @param {number} loY Lower Y bound of the interval to search, in
-     *     offset space.
-     * @param {number} hiY Upper Y bound of the interval to search, in
-     *     offset space.
-     * @param {function():*} onHitCallback Function to call for each slice
-     *     intersecting the interval.
-     */
-    pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
-    }
-
-  };
-
-  return {
-    TimelineCounterTrack: TimelineCounterTrack,
-    TimelineSliceTrack: TimelineSliceTrack,
-    TimelineThreadTrack: TimelineThreadTrack,
-    TimelineCpuTrack: TimelineCpuTrack
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * @fileoverview TimelineView visualizes TRACE_EVENT events using the
- * tracing.Timeline component.
- */
-cr.define('tracing', function() {
-  function tsRound(ts) {
-    return Math.round(ts * 1000.0) / 1000.0;
-  }
-  function getPadding(text, width) {
-    width = width || 0;
-
-    if (typeof text != 'string')
-      text = String(text);
-
-    if (text.length >= width)
-      return '';
-
-    var pad = '';
-    for (var i = 0; i < width - text.length; i++)
-      pad += ' ';
-    return pad;
-  }
-
-  function leftAlign(text, width) {
-    return text + getPadding(text, width);
-  }
-
-  function rightAlign(text, width) {
-    return getPadding(text, width) + text;
-  }
-
-  /**
-   * TimelineView
-   * @constructor
-   * @extends {HTMLDivElement}
-   */
-  TimelineView = cr.ui.define('div');
-
-  TimelineView.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    decorate: function() {
-      this.classList.add('timeline-view');
-
-      this.timelineContainer_ = document.createElement('div');
-      this.timelineContainer_.className = 'timeline-container';
-
-      var summaryContainer_ = document.createElement('div');
-      summaryContainer_.className = 'summary-container';
-
-      this.summaryEl_ = document.createElement('pre');
-      this.summaryEl_.className = 'summary';
-
-      summaryContainer_.appendChild(this.summaryEl_);
-      this.appendChild(this.timelineContainer_);
-      this.appendChild(summaryContainer_);
-
-      this.onSelectionChangedBoundToThis_ = this.onSelectionChanged_.bind(this);
-    },
-
-    set traceData(traceData) {
-      this.model = new tracing.TimelineModel(traceData);
-    },
-
-    get model(model) {
-      return this.timelineModel_;
-    },
-
-    set model(model) {
-      this.timelineModel_ = model;
-
-      // remove old timeline
-      this.timelineContainer_.textContent = '';
-
-      // create new timeline if needed
-      if (this.timelineModel_.minTimestamp !== undefined) {
-        if (this.timeline_)
-          this.timeline_.detach();
-        this.timeline_ = new tracing.Timeline();
-        this.timeline_.model = this.timelineModel_;
-        this.timeline_.focusElement = this.parentElement;
-        this.timelineContainer_.appendChild(this.timeline_);
-        this.timeline_.addEventListener('selectionChange',
-                                        this.onSelectionChangedBoundToThis_);
-        this.onSelectionChanged_();
-      } else {
-        this.timeline_ = null;
-      }
-    },
-
-    get timeline() {
-      return this.timeline_;
-    },
-
-    onSelectionChanged_: function(e) {
-      var timeline = this.timeline_;
-      var selection = timeline.selection;
-      if (!selection.length) {
-        var oldScrollTop = this.timelineContainer_.scrollTop;
-        this.summaryEl_.textContent = timeline.keyHelp;
-        this.timelineContainer_.scrollTop = oldScrollTop;
-        return;
-      }
-
-      var text = '';
-      if (selection.length == 1) {
-        var c0Width = 14;
-        var slice = selection[0].slice;
-        text = 'Selected item:\n';
-        text += leftAlign('Title', c0Width) + ': ' + slice.title + '\n';
-        text += leftAlign('Start', c0Width) + ': ' +
-            tsRound(slice.start) + ' ms\n';
-        text += leftAlign('Duration', c0Width) + ': ' +
-            tsRound(slice.duration) + ' ms\n';
-        if (slice.durationInUserTime)
-          text += leftAlign('Duration (U)', c0Width) + ': ' +
-              tsRound(slice.durationInUserTime) + ' ms\n';
-
-        var n = 0;
-        for (var argName in slice.args) {
-          n += 1;
-        }
-        if (n > 0) {
-          text += leftAlign('Args', c0Width) + ':\n';
-          for (var argName in slice.args) {
-            var argVal = slice.args[argName];
-            text += leftAlign(' ' + argName, c0Width) + ': ' + argVal + '\n';
-          }
-        }
-      } else {
-        var c0Width = 55;
-        var c1Width = 12;
-        var c2Width = 5;
-        text = 'Selection summary:\n';
-        var tsLo = Math.min.apply(Math, selection.map(
-            function(s) {return s.slice.start;}));
-        var tsHi = Math.max.apply(Math, selection.map(
-            function(s) {return s.slice.end;}));
-
-        // compute total selection duration
-        var titles = selection.map(function(i) { return i.slice.title; });
-
-        var slicesByTitle = {};
-        for (var i = 0; i < selection.length; i++) {
-          var slice = selection[i].slice;
-          if (!slicesByTitle[slice.title])
-            slicesByTitle[slice.title] = {
-              slices: []
-            };
-          slicesByTitle[slice.title].slices.push(slice);
-        }
-        var totalDuration = 0;
-        for (var sliceGroupTitle in slicesByTitle) {
-          var sliceGroup = slicesByTitle[sliceGroupTitle];
-          var duration = 0;
-          for (i = 0; i < sliceGroup.slices.length; i++)
-            duration += sliceGroup.slices[i].duration;
-          totalDuration += duration;
-
-          text += ' ' +
-              leftAlign(sliceGroupTitle, c0Width) + ': ' +
-              rightAlign(tsRound(duration) + 'ms', c1Width) + '   ' +
-              rightAlign(String(sliceGroup.slices.length), c2Width) +
-              ' occurrences' + '\n';
-        }
-
-        text += leftAlign('*Totals', c0Width) + ' : ' +
-            rightAlign(tsRound(totalDuration) + 'ms', c1Width) + '   ' +
-            rightAlign(String(selection.length), c2Width) + ' occurrences' +
-            '\n';
-
-        text += '\n';
-
-        text += leftAlign('Selection start', c0Width) + ' : ' +
-            rightAlign(tsRound(tsLo) + 'ms', c1Width) +
-            '\n';
-        text += leftAlign('Selection extent', c0Width) + ' : ' +
-            rightAlign(tsRound(tsHi - tsLo) + 'ms', c1Width) +
-            '\n';
-      }
-
-      // done
-      var oldScrollTop = this.timelineContainer_.scrollTop;
-      this.summaryEl_.textContent = text;
-      this.timelineContainer_.scrollTop = oldScrollTop;
-    }
-  };
-
-  return {
-    TimelineView: TimelineView
-  };
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-/**
- * @fileoverview Provides a mechanism for drawing massive numbers of
- * colored rectangles into a canvas in an efficient manner, provided
- * they are drawn left to right with fixed y and height throughout.
- *
- * The basic idea used here is to fuse subpixel rectangles together so that
- * we never issue a canvas fillRect for them. It turns out Javascript can
- * do this quite efficiently, compared to asking Canvas2D to do the same.
- *
- * A few extra things are done by this class in the name of speed:
- * - Viewport culling: off-viewport rectangles are discarded.
- *
- * - The actual discarding operation is done in world space,
- *   e.g. pre-transform.
- *
- * - Rather than expending compute cycles trying to figure out an average
- *   color for fused rectangles from css strings, you instead draw using
- *   palletized colors. The fused rect is the max pallete index encountered.
- *
- * Make sure to flush the trackRenderer before finishing drawing in order
- * to commit any queued drawing operations.
- */
-cr.define('tracing', function() {
-
-  /**
-   * Creates a fast rect renderer with a specific set of culling rules
-   * and color pallette.
-   * @param {GraphicsContext2D} ctx Canvas2D drawing context.
-   * @param {number} vpLeft The leftmost visible part of the drawing viewport.
-   * @param {number} minRectSize Only rectangles with width < minRectSize are
-   *    considered for merging.
-   * @param {number} maxMergeDist Controls how many successive small rectangles
-   *    can be merged together before issuing a rectangle.
-   * @param {number} vpRight The rightmost visible part of the viewport.
-   * @param {Array} pallette The color pallete for drawing. Pallette slots
-   *    should map to valid Canvas fillStyle strings.
-   *
-   * @constructor
-   */
-  function FastRectRenderer(ctx, vpLeft, minRectSize, maxMergeDist, vpRight,
-                            pallette) {
-    this.ctx_ = ctx;
-    this.vpLeft_ = vpLeft;
-    this.minRectSize_ = minRectSize;
-    this.maxMergeDist_ = maxMergeDist;
-    this.vpRight_ = vpRight;
-    this.pallette_ = pallette;
-  }
-
-  FastRectRenderer.prototype = {
-    y_: 0,
-    h_: 0,
-    merging_: false,
-    mergeStartX_: 0,
-    mergeCurRight_: 0,
-
-    /**
-     * Changes the y position and height for subsequent fillRect
-     * calls. x and width are specifieid on the fillRect calls.
-     */
-    setYandH: function(y, h) {
-      this.flush();
-      this.y_ = y;
-      this.h_ = h;
-    },
-
-    /**
-     * Fills rectangle at the specified location, if visible. If the
-     * rectangle is subpixel, it will be merged with adjacent rectangles.
-     * The drawing operation may not take effect until flush is called.
-     * @param {number} colorId The color of this rectangle, as an index
-     *     in the renderer's color pallete.
-     */
-    fillRect: function(x, w, colorId) {
-      var r = x + w;
-      if (r < this.vpLeft_ || x > this.vpRight_) return;
-      if (w < this.minRectSize_) {
-        if (r - this.mergeStartX_ > this.maxMergeDist_)
-          this.flush();
-        if (!this.merging_) {
-          this.merging_ = true;
-          this.mergeStartX_ = x;
-          this.mergeCurRight_ = r;
-          this.mergedColorId = colorId;
-        } else {
-          this.mergeCurRight_ = r;
-          this.mergedColorId = Math.max(this.mergedColorId, colorId);
-        }
-      } else {
-        if (this.merging_)
-          this.flush();
-        this.ctx_.fillStyle = this.pallette_[colorId];
-        this.ctx_.fillRect(x, this.y_, w, this.h_);
-      }
-    },
-
-    /**
-     * Commits any pending fillRect operations to the underlying graphics
-     * context.
-     */
-    flush: function() {
-      if (this.merging_) {
-        this.ctx_.fillStyle = this.pallette_[this.mergedColorId];
-        this.ctx_.fillRect(this.mergeStartX_, this.y_,
-                           this.mergeCurRight_ - this.mergeStartX_, this.h_);
-        this.merging_ = false;
-      }
-    }
-  };
-
-  return {
-    FastRectRenderer: FastRectRenderer
-  };
-
-});
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Helper functions for use in tracing tests.
- */
-
-
-/**
- * goog.testing.assertion's assertEquals tweaked to do equality-to-a-constant.
- * @param {*} a First value.
- * @param {*} b Second value.
- */
-function assertAlmostEquals(a, b) {
-  _validateArguments(2, arguments);
-  var var1 = nonCommentArg(1, 2, arguments);
-  var var2 = nonCommentArg(2, 2, arguments);
-  _assert(commentArg(2, arguments), Math.abs(var1 - var2) < 0.00001,
-          'Expected ' + _displayStringForValue(var1) + ' but was ' +
-          _displayStringForValue(var2));
-}
-
-cr.define('test_utils', function() {
-  function getAsync(url, cb) {
-    var req = new XMLHttpRequest();
-    req.open('GET', url, true);
-    req.onreadystatechange = function(aEvt) {
-      if (req.readyState == 4) {
-        window.setTimeout(function() {
-          if (req.status == 200) {
-            cb(req.responseText);
-          } else {
-            console.log('Failed to load ' + url);
-          }
-        }, 0);
-      }
-    };
-    req.send(null);
-  }
-  return {
-    getAsync: getAsync
-  };
-});
+var global=this;
+this.cr=function(){function f(b,e,a,d){var c=new cr.Event(e+"Change");c.propertyName=e;c.newValue=a;c.oldValue=d;b.dispatchEvent(c)}function c(b){return b.replace(/([A-Z])/g,"-$1").toLowerCase()}function a(e,a){switch(a){case b.JS:var d=e+"_";return function(){return this[d]};case b.ATTR:var g=c(e);return function(){return this.getAttribute(g)};case b.BOOL_ATTR:return g=c(e),function(){return this.hasAttribute(g)}}}function d(e,a,d){switch(a){case b.JS:var g=e+"_";return function(b){var a=this[g];
+b!==a&&(this[g]=b,d&&d.call(this,b,a),f(this,e,b,a))};case b.ATTR:var h=c(e);return function(b){var a=this[h];b!==a&&(void 0==b?this.removeAttribute(h):this.setAttribute(h,b),d&&d.call(this,b,a),f(this,e,b,a))};case b.BOOL_ATTR:return h=c(e),function(b){var a=this[h];b!==a&&(b?this.setAttribute(h,e):this.removeAttribute(h),d&&d.call(this,b,a),f(this,e,b,a))}}}function h(b,e,a){var d=cr.doc.createEvent("Event");d.initEvent(b,!!e,!!a);d.__proto__=global.Event.prototype;return d}var b={JS:"js",ATTR:"attr",
+BOOL_ATTR:"boolAttr"},e=1;return{addSingletonGetter:function(b){b.getInstance=function(){return b.instance_||(b.instance_=new b)}},createUid:function(){return e++},define:function(b,e){var a;a=b.split(".");for(var d=global,c;a.length&&(c=a.shift());)d=c in d?d[c]:d[c]={};a=d;var d=e(),h;for(h in d)(c=Object.getOwnPropertyDescriptor(d,h))&&Object.defineProperty(a,h,c)},defineProperty:function(e,c,k,g){"function"==typeof e&&(e=e.prototype);k=k||b.JS;e.__lookupGetter__(c)||e.__defineGetter__(c,a(c,k));
+e.__lookupSetter__(c)||e.__defineSetter__(c,d(c,k,g))},dispatchPropertyChange:f,dispatchSimpleEvent:function(b,e,a,d){e=new cr.Event(e,a,d);return b.dispatchEvent(e)},Event:h,getUid:function(b){return b.hasOwnProperty("uid")?b.uid:b.uid=e++},initialize:function(){if(global.document)h.prototype={__proto__:global.Event.prototype},cr.isMac=/Mac/.test(navigator.platform),cr.isWindows=/Win/.test(navigator.platform),cr.isChromeOS=/CrOS/.test(navigator.userAgent),cr.isLinux=/Linux/.test(navigator.userAgent),
+cr.isGTK=/GTK/.test(chrome.toolkit),cr.isViews=/views/.test(chrome.toolkit),cr.isTouchOptimized=!!chrome.touchOptimized,cr.isTouchOptimized&&doc.documentElement.setAttribute("touch-optimized","");else{var b=cr;Object.defineProperty(global,"cr",{get:function(){Object.defineProperty(global,"cr",{value:b});b.initialize();return b},configurable:!0})}},PropertyKind:b,get doc(){return document}}}();cr.initialize();cr.define("cr",function(){function f(){}f.prototype={addEventListener:function(c,a){this.listeners_||(this.listeners_=Object.create(null));if(c in this.listeners_){var d=this.listeners_[c];0>d.indexOf(a)&&d.push(a)}else this.listeners_[c]=[a]},removeEventListener:function(c,a){if(this.listeners_&&c in this.listeners_){var d=this.listeners_[c],h=d.indexOf(a);0<=h&&(1==d.length?delete this.listeners_[c]:d.splice(h,1))}},dispatchEvent:function(c){if(!this.listeners_)return!0;var a=this;c.__defineGetter__("target",
+function(){return a});c.preventDefault=function(){this.returnValue=!1};var d=c.type,h=0;if(d in this.listeners_)for(var d=this.listeners_[d].concat(),b=0,e;e=d[b];b++)h=e.handleEvent?h|!1===e.handleEvent.call(e,c):h|!1===e.call(this,c);return!h&&c.returnValue}};return{EventTarget:f}});cr.define("cr.ui",function(){function f(c,a){return(a&&a.ownerDocument?a.ownerDocument:cr.doc).createElement(c)}return{decorate:function(c,a){var d;d="string"==typeof c?cr.doc.querySelectorAll(c):[c];for(var h=0,b;b=d[h];h++)b instanceof a||a.decorate(b)},define:function(c){function a(b){var e=d(h,b);a.decorate(e);for(var c in b)e[c]=b[c];return e}var d,h;"function"==typeof c?(d=c,h=""):(d=f,h=c);a.decorate=function(b){b.__proto__=a.prototype;b.decorate()};return a},limitInputWidth:function(c,a,d){function h(){if(c.scrollWidth>
+g)c.style.width=g+"px";else{c.style.width=0;var b=c.scrollWidth;c.style.width=b<d?d+"px":b+"px"}}c.style.width="10px";var b=c.ownerDocument.defaultView,e=b.getComputedStyle(c),b=b.getComputedStyle(a),j="rtl"==e.direction,f=c.getBoundingClientRect(),k=a.getBoundingClientRect(),f=j?k.right-f.right:f.left-k.left,e=parseInt(e.borderLeftWidth,10)+parseInt(e.paddingLeft,10)+parseInt(e.paddingRight,10)+parseInt(e.borderRightWidth,10),b=j?parseInt(b.paddingLeft,10):parseInt(b.paddingRight,10),g=a.clientWidth-
+f-e-b;c.addEventListener("input",h);h()}}});cr.define("cr.ui",function(){function f(a){this.classList_=a.documentElement.classList;var d=this;a.addEventListener("keydown",function(a){9==a.keyCode&&(d.visible=!0)},!0);a.addEventListener("mousedown",function(){d.visible=!1},!0)}f.prototype={set visible(a){a?this.classList_.add("focus-outline-visible"):this.classList_.remove("focus-outline-visible")},get visible(){this.classList_.contains("focus-outline-visible")}};var c=[];f.forDocument=function(a){for(var d=0;d<c.length;d++)if(a==c[d][0])return c[d][1];
+d=new f(a);c.push([a,d]);return d};return{FocusOutlineManager:f}});cr.define("cr.ui",function(){function f(){for(var e={TABBOX:c,TABS:a,TAB:d,TABPANELS:h,TABPANEL:b},j,f=0;j=this.children[f];f++){var k=e[j.tagName];k&&cr.ui.decorate(j,k)}}var c=cr.ui.define("tabbox");c.prototype={__proto__:HTMLElement.prototype,decorate:function(){f.call(this);this.addEventListener("selectedChange",this.handleSelectedChange_,!0);this.selectedIndex=0},handleSelectedChange_:function(b){var a=b.target;b.newValue&&(a.parentElement&&a.parentElement.parentElement)==this&&(this.selectedIndex=
+Array.prototype.indexOf.call(a.parentElement.children,a))},selectedIndex_:-1};cr.defineProperty(c,"selectedIndex",cr.PropertyKind.JS_PROP,function(b){for(var a,d,c=0;a=this.children[c];c++)for(var g=0;d=a.children[g];g++)d.selected=g==b});var a=cr.ui.define("tabs");a.prototype={__proto__:HTMLElement.prototype,decorate:function(){f.call(this);this.tabIndex=0;this.addEventListener("keydown",this.handleKeyDown_.bind(this));this.focusOutlineManager_=cr.ui.FocusOutlineManager.forDocument(this.ownerDocument)},
+handleKeyDown_:function(b){var a=0;switch(b.keyIdentifier){case "Left":case "Up":a=-1;break;case "Right":case "Down":a=1}a&&("rtl"==this.ownerDocument.defaultView.getComputedStyle(this).direction&&(a*=-1),b=this.children.length,this.parentElement.selectedIndex=(this.parentElement.selectedIndex+a+b)%b,this.focusOutlineManager_.visible=!0)}};var d=cr.ui.define("tab");d.prototype={__proto__:HTMLElement.prototype,decorate:function(){var b=this;this.addEventListener(cr.isMac?"click":"mousedown",function(){b.selected=
+!0})}};cr.defineProperty(d,"selected",cr.PropertyKind.BOOL_ATTR);var h=cr.ui.define("tabpanels");h.prototype={__proto__:HTMLElement.prototype,decorate:f};var b=cr.ui.define("tabpanel");b.prototype={__proto__:HTMLElement.prototype,decorate:function(){}};cr.defineProperty(b,"selected",cr.PropertyKind.BOOL_ATTR);return{TabBox:c,Tabs:a,Tab:d,TabPanels:h,TabPanel:b}});global=this;function $(f){return document.getElementById(f)}function chromeSend(f,c,a,d){var h=global[a];global[a]=function(){global[a]=h;var b=Array.prototype.slice.call(arguments);return d.apply(global,b)};chrome.send(f,c)}function url(f){f=f.replace(/(\(|\)|\,|\s|\'|\"|\\)/g,"\\$1");/\\\\$/.test(f)&&(f+=" ");return'url("'+f+'")'}function parseQueryParams(f){for(var c={},f=unescape(f.search.substring(1)).split("&"),a=0;a<f.length;a++){var d=f[a].split("=");c[d[0]]=d[1]}return c}
+function findAncestorByClass(f,c){return findAncestor(f,function(a){return a.classList?a.classList.contains(c):null})}function findAncestor(f,c){for(var a=!1;null!=f&&!(a=c(f));)f=f.parentNode;return a?f:null}function swapDomNodes(f,c){var a=f.nextSibling;if(a==c)swapDomNodes(c,f);else{var d=f.parentNode;c.parentNode.replaceChild(f,c);d.insertBefore(c,a)}}
+function disableTextSelectAndDrag(){document.onselectstart=function(f){f.preventDefault()};document.ondragstart=function(f){f.preventDefault()}}function isRTL(){return"rtl"==document.documentElement.dir}function assert(f,c){if(!f){var a="Assertion failed";c&&(a=a+": "+c);throw Error(a);}}function getRequiredElement(f){var c=$(f);assert(c,"Missing required element: "+f);return c}
+document.addEventListener("click",function(f){if(f.returnValue){var c=f.target;if(c.nodeType==Node.ELEMENT_NODE&&c.webkitMatchesSelector("A, A *")){for(;"A"!=c.tagName;)c=c.parentElement;if(("file:"==c.protocol||"about:"==c.protocol)&&(0==f.button||1==f.button))chrome.send("navigateToUrl",[c.href,c.target,f.button,f.altKey,f.ctrlKey,f.metaKey,f.shiftKey]),f.preventDefault()}}});function appendParam(f,c,a){c=encodeURIComponent(c)+"="+encodeURIComponent(a);return-1==f.indexOf("?")?f+"?"+c:f+"&"+c};cr.define("tracing",function(){var f=cr.ui.define("div");f.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.classList.add("overlay-root");this.visible=!1;this.contentHost=this.ownerDocument.createElement("div");this.contentHost.classList.add("content-host");this.tabCatcher=this.ownerDocument.createElement("span");this.tabCatcher.tabIndex=0;this.appendChild(this.contentHost);this.onKeydownBoundToThis_=this.onKeydown_.bind(this);this.onFocusInBoundToThis_=this.onFocusIn_.bind(this);
+this.addEventListener("mousedown",this.onMousedown_.bind(this))},showOverlay:function(a){a.oldParent_=a.parentNode;this.contentHost.appendChild(a);this.contentHost.appendChild(this.tabCatcher);this.ownerDocument.body.classList.add("disabled-by-overlay");this.visible=!0;a.tabIndex=0;var d=a.querySelector("button, input, list, select, a");d||(d=a);d.focus();this.ownerDocument.addEventListener("focusin",this.onFocusInBoundToThis_,!0);a.addEventListener("keydown",this.onKeydownBoundToThis_)},onMousedown_:function(a){a.target==
+this&&a.preventDefault()},onFocusIn_:function(a){a.target==this.tabCatcher&&window.setTimeout(this.focusOverlay_.bind(this),0)},focusOverlay_:function(){this.contentHost.firstChild.focus()},onKeydown_:function(a){9==a.keyCode&&(a.shiftKey&&a.target==this.contentHost.firstChild)&&a.preventDefault()},hideOverlay:function(a){this.visible=!1;this.ownerDocument.body.classList.remove("disabled-by-overlay");this.lastFocusOut_=void 0;a.parentNode.removeChild(this.tabCatcher);a.oldParent_?(a.oldParent_.appendChild(a),
+delete a.oldParent_):this.contentHost.removeChild(a);a.removeEventListener("keydown",this.onKeydownBoundToThis_);this.ownerDocument.removeEventListener("focusin",this.onFocusInBoundToThis_)}};cr.defineProperty(f,"visible",cr.PropertyKind.BOOL_ATTR);var c=cr.ui.define("div");c.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){if(!this.ownerDocument.querySelector(".overlay-root")){var a=this.ownerDocument.createElement("div");cr.ui.decorate(a,f);this.ownerDocument.body.appendChild(a)}this.classList.add("overlay");
+this.visible=!1},onVisibleChanged_:function(){var a=this.ownerDocument.querySelector(".overlay-root");this.visible?a.showOverlay(this):a.hideOverlay(this)}};cr.defineProperty(c,"visible",cr.PropertyKind.BOOL_ATTR,c.prototype.onVisibleChanged_);return{Overlay:c}});cr.define("tracing",function(){function f(){this.overlay_=document.createElement("div");this.overlay_.className="tracing-overlay";cr.ui.decorate(this.overlay_,tracing.Overlay);this.statusDiv_=document.createElement("div");this.overlay_.appendChild(this.statusDiv_);this.bufferPercentDiv_=document.createElement("div");this.overlay_.appendChild(this.bufferPercentDiv_);this.stopButton_=document.createElement("button");this.stopButton_.onclick=this.endTracing.bind(this);this.stopButton_.textContent="Stop tracing";
+this.overlay_.appendChild(this.stopButton_);this.traceEvents_=[];this.systemTraceEvents_=[];this.onKeydownBoundToThis_=this.onKeydown_.bind(this);this.onKeypressBoundToThis_=this.onKeypress_.bind(this);chrome.send("tracingControllerInitialized")}f.prototype={__proto__:cr.EventTarget.prototype,gpuInfo_:void 0,clientInfo_:void 0,tracingEnabled_:!1,tracingEnding_:!1,systemTraceDataFilename_:void 0,onRequestBufferPercentFullComplete:function(c){this.overlay_.visible&&(window.setTimeout(this.beginRequestBufferPercentFull_.bind(this),
+250),this.bufferPercentDiv_.textContent="Buffer usage: "+Math.round(100*c)+"%")},beginRequestBufferPercentFull_:function(){chrome.send("beginRequestBufferPercentFull")},beginTracing:function(c){if(this.tracingEnabled_)throw Error("Tracing already begun.");this.stopButton_.hidden=!1;this.statusDiv_.textContent="Tracing active.";this.tracingEnabled_=this.overlay_.visible=!0;console.log("Beginning to trace...");this.statusDiv_.textContent="Tracing active.";this.traceEvents_=[];this.systemTraceEvents_=
+[];chrome.send("beginTracing",[c||!1]);this.beginRequestBufferPercentFull_();c=new cr.Event("traceBegun");c.events=this.traceEvents_;this.dispatchEvent(c);c=new cr.Event("traceEventsChanged");c.numEvents=this.traceEvents_.length;this.dispatchEvent(c);window.addEventListener("keypress",this.onKeypressBoundToThis_);window.addEventListener("keydown",this.onKeydownBoundToThis_)},onKeydown_:function(c){27==c.keyCode&&this.endTracing()},onKeypress_:function(c){"Enter"==c.keyIdentifier&&this.endTracing()},
+onClientInfoUpdate:function(c){this.clientInfo_=c},onGpuInfoUpdate:function(c){this.gpuInfo_=c},get isTracingEnabled(){return this.tracingEnabled_},get traceEvents(){return this.traceEvents_},onTraceDataCollected:function(c){this.statusDiv_.textContent="Processing trace...";this.traceEvents_.push.apply(this.traceEvents_,c)},endTracing:function(){if(!this.tracingEnabled_)throw Error("Tracing not begun.");this.tracingEnding_||(this.tracingEnding_=!0,this.statusDiv_.textContent="Ending trace...",console.log("Finishing trace"),
+this.statusDiv_.textContent="Downloading trace data...",this.stopButton_.hidden=!0,window.setTimeout(function(){chrome.send("endTracingAsync")},100))},onEndTracingComplete:function(){window.removeEventListener("keydown",this.onKeydownBoundToThis_);window.removeEventListener("keypress",this.onKeypressBoundToThis_);this.tracingEnding_=this.tracingEnabled_=this.overlay_.visible=!1;console.log("onEndTracingComplete p1 with "+this.traceEvents_.length+" events.");var c=new cr.Event("traceEnded");c.events=
+this.traceEvents_;this.dispatchEvent(c)},onSystemTraceDataCollected:function(c){console.log("onSystemTraceDataCollected with "+c.length+" chars of data.");this.systemTraceEvents_=c},get systemTraceEvents(){return this.systemTraceEvents_},beginLoadTraceFile:function(){chrome.send("loadTraceFile")},onLoadTraceFileComplete:function(c){c.traceEvents?this.traceEvents_=c.traceEvents:c.length?this.traceEvents_=c:console.log("Expected an array when loading the trace file");c.systemTraceEvents?this.systemTraceEvents_=
+c.systemTraceEvents:c.length?this.systemTraceEvents_=c:console.log("Expected an array when loading the trace file");c=new cr.Event("loadTraceFileComplete");c.events=this.traceEvents_;this.dispatchEvent(c)},onLoadTraceFileCanceled:function(){cr.dispatchSimpleEvent(this,"loadTraceFileCanceled")},beginSaveTraceFile:function(){chrome.send("saveTraceFile",[JSON.stringify({traceEvents:this.traceEvents_,systemTraceEvents:this.systemTraceEvents_,clientInfo:this.clientInfo_,gpuInfo:this.gpuInfo_})])},onSaveTraceFileComplete:function(){cr.dispatchSimpleEvent(this,
+"saveTraceFileComplete")},onSaveTraceFileCanceled:function(){cr.dispatchSimpleEvent(this,"saveTraceFileCanceled")},selfTest:function(){this.beginTracing();window.setTimeout(this.endTracing.bind(This),500)}};return{TracingController:f}});cr.define("tracing",function(){function f(b,a,e,d,c){this.title=b;this.start=e;this.colorId=a;this.args=d;this.didNotFinish=!1;void 0!==c&&(this.duration=c)}function c(b,a,e,d,c){f.call(this,b,a,e,d,c);this.subSlices=[]}function a(b,a,e,d){f.call(this,b,a,e,d)}function d(b,a){if(!b)throw"Parent must be provided.";this.parent=b;this.tid=a;this.subRows=[[]];this.asyncSlices=new j(this.ptid)}function h(b,a,e){this.parent=b;this.id=a;this.name=e;this.seriesNames=[];this.seriesColors=[];this.timestamps=
+[];this.samples=[]}function b(b){this.pid=b;this.threads={};this.counters={}}function e(b){this.cpuNumber=b;this.slices=[];this.counters={}}function j(b){this.name=b;this.slices=[]}function o(b){for(var a=0,e=0;e<b.length;++e)a=(a+37*a+11*b.charCodeAt(e))%4294967295;return a}function k(b,a){this.cpus={};this.processes={};this.importErrors=[];this.asyncSliceGroups={};b&&this.importEvents(b,a)}function g(){}function q(b){this.text_=b}f.prototype={selected:!1,duration:void 0,get end(){return this.start+
+this.duration}};c.prototype={__proto__:f.prototype};a.prototype={__proto__:f.prototype,id:void 0,startThread:void 0,endThread:void 0,subSlices:void 0};var l={};d.getPTIDFromPidAndTid=function(b,a){l[b]||(l[b]={});l[b][a]||(l[b][a]=b+":"+a);return l[b][a]};d.prototype={name:void 0,get ptid(){return d.getPTIDFromPidAndTid(this.tid,this.parent.pid)},getSubrow:function(b){for(;b>=this.subRows.length;)this.subRows.push([]);return this.subRows[b]},shiftSubRow_:function(b,a){for(var e=0;e<b.length;e++){var d=
+b[e];d.start+=a}},shiftTimestampsForward:function(b){this.cpuSlices&&this.shiftSubRow_(this.cpuSlices,b);for(var a=0;a<this.subRows.length;a++)this.shiftSubRow_(this.subRows[a],b);this.asyncSlices.shiftTimestampsForward(b)},updateBounds:function(){var b=[],a;0!=this.subRows[0].length&&(a=this.subRows[0],b.push(a[0].start),b.push(a[a.length-1].end));this.asyncSlices.slices.length&&(this.asyncSlices.updateBounds(),b.push(this.asyncSlices.minTimestamp),b.push(this.asyncSlices.maxTimestamp));b.length?
+(this.minTimestamp=Math.min.apply(Math,b),this.maxTimestamp=Math.max.apply(Math,b)):this.maxTimestamp=this.minTimestamp=void 0},get userFriendlyName(){return this.parent.pid+": "+(this.name||this.tid)},get userFriendlyDetails(){return"pid: "+this.parent.pid+", tid: "+this.tid+(this.name?", name: "+this.name:"")}};d.compare=function(a,e){if(a.parent.pid!=e.parent.pid)return b.compare(a.parent,e.parent.pid);if(a.name&&e.name){var d=a.name.localeCompare(e.name);return 0==d?a.tid-e.tid:d}return a.name?
+-1:e.name?1:a.tid-e.tid};h.prototype={__proto__:Object.prototype,get numSeries(){return this.seriesNames.length},get numSamples(){return this.timestamps.length},shiftTimestampsForward:function(b){for(var a=0;a<this.timestamps.length;a++)this.timestamps[a]+=b},updateBounds:function(){if(this.seriesNames.length!=this.seriesColors.length)throw"seriesNames.length must match seriesColors.length";if(this.numSeries*this.numSamples!=this.samples.length)throw"samples.length must be a multiple of numSamples.";
+this.totals=[];if(0==this.samples.length)this.maxTimestamp=this.minTimestamp=void 0,this.maxTotal=0;else{this.minTimestamp=this.timestamps[0];this.maxTimestamp=this.timestamps[this.timestamps.length-1];for(var b=this.numSeries,a=-Infinity,e=0;e<this.timestamps.length;e++){for(var d=0,c=0;c<b;c++)d+=this.samples[e*b+c],this.totals.push(d);d>a&&(a=d)}this.maxTotal=a}}};h.compare=function(a,e){if(a.parent.pid!=e.parent.pid)return b.compare(a.parent,e.parent.pid);var d=a.name.localeCompare(e.name);return 0==
+d?a.tid-e.tid:d};b.prototype={get numThreads(){var b=0,a;for(a in this.threads)b++;return b},shiftTimestampsForward:function(b){for(var a in this.threads)this.threads[a].shiftTimestampsForward(b);for(var e in this.counters)this.counters[e].shiftTimestampsForward(b)},getOrCreateThread:function(b){this.threads[b]||(this.threads[b]=new d(this,b));return this.threads[b]},getOrCreateCounter:function(b,a){var e=b+"."+a;this.counters[e]||(this.counters[e]=new h(this,e,a));return this.counters[e]}};b.compare=
+function(b,a){return b.pid-a.pid};e.prototype={getOrCreateCounter:function(b,a){var e;e=b.length?b+"."+a:a;this.counters[e]||(this.counters[e]=new h(this,e,a));return this.counters[e]},shiftTimestampsForward:function(b){for(var a=0;a<this.slices.length;a++)this.slices[a].start+=b;for(var e in this.counters)this.counters[e].shiftTimestampsForward(b)},updateBounds:function(){this.slices.length?(this.minTimestamp=this.slices[0].start,this.maxTimestamp=this.slices[this.slices.length-1].end):this.maxTimestamp=
+this.minTimestamp=void 0}};e.compare=function(b,a){return b.cpuNumber-a.cpuNumber};j.prototype={__proto__:Object.prototype,push:function(b){this.slices.push(b)},get length(){return this.slices.length},subRows_:void 0,sortSlices_:function(){this.slices.sort(function(b,a){return b.start-a.start})},shiftTimestampsForward:function(b){for(var a=0;a<this.slices.length;a++){var e=this.slices[a];e.start+=b;for(var d=0;d<e.subSlices.length;d++)e.subSlices[d].start+=b}},updateBounds:function(){this.sortSlices_();
+this.slices.length?(this.minTimestamp=this.slices[0].start,this.maxTimestamp=this.slices[this.slices.length-1].end):this.maxTimestamp=this.minTimestamp=void 0;this.subRows_=void 0},get subRows(){this.subRows_||this.rebuildSubRows_();return this.subRows_},rebuildSubRows_:function(){this.sortSlices_();for(var b=[],a=0;a<this.slices.length;a++){for(var e=this.slices[a],d=!1,c=0;c<b.length;c++){var m=b[c];if(e.start>=m[m.length-1].end){d=!0;if(void 0===e.subSlices||1>e.subSlices.length)throw"TimelineAsyncEvent missing subSlices: "+
+e.name;for(c=0;c<e.subSlices.length;c++)m.push(e.subSlices[c]);break}}if(!d&&(m=[],void 0!==e.subSlices)){for(c=0;c<e.subSlices.length;c++)m.push(e.subSlices[c]);b.push(m)}}this.subRows_=b},computeSubGroups:function(){for(var b={},a=0;a<this.slices.length;++a){var e=this.slices[a],d=e.startThread.ptid;b[d]||(b[d]=new j(this.name));b[d].slices.push(e)}var a=[],c;for(c in b)e=b[c],e.updateBounds(),a.push(e);return a}};h.compare=function(a,e){if(a.parent.pid!=e.parent.pid)return b.compare(a.parent,e.parent.pid);
+var d=a.name.localeCompare(e.name);return 0==d?a.tid-e.tid:d};var i=[{r:138,g:113,b:152},{r:175,g:112,b:133},{r:127,g:135,b:225},{r:93,g:81,b:137},{r:116,g:143,b:119},{r:178,g:214,b:122},{r:87,g:109,b:147},{r:119,g:155,b:95},{r:114,g:180,b:160},{r:132,g:85,b:103},{r:157,g:210,b:150},{r:148,g:94,b:86},{r:164,g:108,b:138},{r:139,g:191,b:150},{r:110,g:99,b:145},{r:80,g:129,b:109},{r:125,g:140,b:149},{r:93,g:124,b:132},{r:140,g:85,b:140},{r:104,g:163,b:162},{r:132,g:141,b:178},{r:131,g:105,b:147},{r:135,
+g:183,b:98},{r:152,g:134,b:177},{r:141,g:188,b:141},{r:133,g:160,b:210},{r:126,g:186,b:148},{r:112,g:198,b:205},{r:180,g:122,b:195},{r:203,g:144,b:152},{r:182,g:125,b:143},{r:126,g:200,b:148},{r:133,g:160,b:210},{r:240,g:240,b:240}],m=i.length-4,r=i.length,v=i.concat(i.map(function(b){var a;a=240<=b.r&&240<=b.g&&240<=b.b?-0.2:0.45;return{r:Math.min(255,b.r+Math.floor(b.r*a)),g:Math.min(255,b.g+Math.floor(b.g*a)),b:Math.min(255,b.b+Math.floor(b.b*a))}})).map(function(b){return"rgb("+b.r+","+b.g+","+
+b.b+")"}),u={},y=[];k.registerImporter=function(b){y.push(b)};g.canImport=function(b){return b instanceof Array&&0==b.length?!0:"string"===typeof b||b instanceof String?0==b.length:!1};g.prototype={__proto__:Object.prototype,importEvents:function(){},finalizeImport:function(){}};k.registerImporter(g);k.prototype={__proto__:cr.EventTarget.prototype,get numProcesses(){var b=0,a;for(a in this.processes)b++;return b},getOrCreateCpu:function(b){this.cpus[b]||(this.cpus[b]=new e(b));return this.cpus[b]},
+getOrCreateProcess:function(a){this.processes[a]||(this.processes[a]=new b(a));return this.processes[a]},pruneEmptyThreads:function(){for(var b in this.processes){var a=this.processes[b],e={},d;for(d in a.threads){for(var c=a.threads[d],m=!1,g=0;g<c.subRows.length;g++)m|=0<c.subRows[g].length;if(m||0<c.asyncSlices.length)e[d]=c}a.threads=e}},updateBounds:function(){for(var b=Infinity,a=-b,e=!1,d=this.getAllThreads(),c=0;c<d.length;c++){var m=d[c];m.updateBounds();void 0!=m.minTimestamp&&void 0!=m.maxTimestamp&&
+(b=Math.min(b,m.minTimestamp),a=Math.max(a,m.maxTimestamp),e=!0)}d=this.getAllCounters();for(c=0;c<d.length;c++)m=d[c],m.updateBounds(),void 0!=m.minTimestamp&&void 0!=m.maxTimestamp&&(e=!0,b=Math.min(b,m.minTimestamp),a=Math.max(a,m.maxTimestamp));for(var g in this.cpus)c=this.cpus[g],c.updateBounds(),void 0!=c.minTimestamp&&void 0!=c.maxTimestamp&&(e=!0,b=Math.min(b,c.minTimestamp),a=Math.max(a,c.maxTimestamp));e?(this.minTimestamp=b,this.maxTimestamp=a):this.minTimestamp=this.maxTimestamp=void 0},
+shiftWorldToZero:function(){if(void 0!==this.minTimestamp){var b=this.minTimestamp,a;for(a in this.processes)this.processes[a].shiftTimestampsForward(-b);for(var e in this.cpus)this.cpus[e].shiftTimestampsForward(-b);this.updateBounds()}},getAllThreads:function(){var b=[],a;for(a in this.processes){var e=this.processes[a],d;for(d in e.threads)b.push(e.threads[d])}return b},getAllCpus:function(){var b=[],a;for(a in this.cpus)b.push(this.cpus[a]);return b},getAllProcesses:function(){var b=[],a;for(a in this.processes)b.push(this.processes[a]);
+return b},getAllCounters:function(){var b=[],a;for(a in this.processes){var e=this.processes[a],d;for(d in e.counters)b.push(e.counters[d])}for(var c in this.cpus){a=this.cpus[c];for(var m in a.counters)b.push(a.counters[m])}return b},importOneTrace_:function(b,a){for(var e,d=0;d<y.length;++d)if(y[d].canImport(b)){e=y[d];break}if(!e)throw"Could not find an importer for the provided eventData.";e=new e(this,b,a);e.importEvents();return e},importEvents:function(b,a,e){void 0===a&&(a=!0);var d=[],b=
+this.importOneTrace_(b,!1);d.push(b);if(e)for(var c=0;c<e.length;++c)b=this.importOneTrace_(e[c],!0),d.push(b);for(c=0;c<d.length;++c)d[c].finalizeImport();for(c=0;c<d.length;++c)this.pruneEmptyThreads();this.updateBounds();a&&this.shiftWorldToZero();a&&(void 0!==this.minTimestamp&&void 0!==this.maxTimestamp)&&(a=0.15*(this.maxTimestamp-this.minTimestamp),this.minTimestamp-=a,this.maxTimestamp+=a)}};q.prototype={__proto__:Object.prototype,matchSlice:function(b){return 0==this.text_.length?!1:-1!=
+b.title.indexOf(this.text_)}};return{getPallette:function(){return v},getPalletteHighlightIdBoost:function(){return r},getColorIdByName:function(b){if("iowait"==b)return m;if("running"==b)return m+1;if("runnable"==b)return m+2;if("sleeping"==b)return m+3;throw"Unrecognized color "+b;},getStringHash:o,getStringColorId:function(b){if(void 0===u[b]){var a=o(b);u[b]=a%m}return u[b]},TimelineSlice:f,TimelineThreadSlice:c,TimelineAsyncSlice:a,TimelineThread:d,TimelineCounter:h,TimelineProcess:b,TimelineCpu:e,
+TimelineAsyncSliceGroup:j,TimelineModel:k,TimelineFilter:q}});cr.define("tracing",function(){function f(b){this.cpu=b}function c(){this.openSlices=[]}function a(b,a,e){this.isAdditionalImport_=e;this.model_=b;this.events_=a;this.clockSyncRecords_=[];this.cpuStates_={};this.kernelThreadStates_={};this.buildMapFromLinuxPidsToTimelineThreads();this.lineNumber=-1;this.threadStateByKPID_={}}f.prototype={__proto__:Object.prototype,switchRunningLinuxPid:function(b,a,e,d,c,m){if(void 0!==this.lastActivePid&&0!=this.lastActivePid){var j=e-this.lastActiveTs;name=(b=b.threadsByLinuxPid[this.lastActivePid])?
+b.userFriendlyName:this.lastActiveComm;this.cpu.slices.push(new tracing.TimelineSlice(name,tracing.getStringColorId(name),this.lastActiveTs,{comm:this.lastActiveComm,tid:this.lastActivePid,prio:this.lastActivePrio,stateWhenDescheduled:a},j))}this.lastActiveTs=e;this.lastActivePid=d;this.lastActiveComm=c;this.lastActivePrio=m}};TestExports={};var d=/^\s*(.+?)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;TestExports.lineRE=d;var h=RegExp("prev_comm=(.+) prev_pid=(\\d+) prev_prio=(\\d+) prev_state=(\\S) ==> next_comm=(.+) next_pid=(\\d+) next_prio=(\\d+)");
+TestExports.schedSwitchRE=h;var b=/comm=(.+) pid=(\d+) prio=(\d+) success=(\d+) target_cpu=(\d+)/;TestExports.schedWakeupRE=b;var e=/trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;TestExports.traceEventClockSyncRE=e;var j=/work struct (.+): function (\S+)/,o=/work struct (.+)/;a.canImport=function(b){if(!("string"===typeof b||b instanceof String))return!1;if(/^# tracer:/.exec(b))return!0;var a=/^(.+)\n/.exec(b);a&&(b=a[1]);return d.exec(b)?!0:!1};a.prototype={__proto__:Object.prototype,buildMapFromLinuxPidsToTimelineThreads:function(){this.threadsByLinuxPid=
+{};this.model_.getAllThreads().forEach(function(b){this.threadsByLinuxPid[b.tid]=b}.bind(this))},getOrCreateCpuState:function(b){if(!this.cpuStates_[b]){var a=this.model_.getOrCreateCpu(b);this.cpuStates_[b]=new f(a)}return this.cpuStates_[b]},parsePid:function(b){b=/.+-(\d+)/.exec(b)[1];return b=parseInt(b)},parseThreadName:function(b){return/(.+)-\d+/.exec(b)[1]},getOrCreateKernelThread:function(b,a,e){this.kernelThreadStates_[b]||(void 0==a&&(a=this.parsePid(b)),void 0==e&&(e=a),e=this.model_.getOrCreateProcess(a).getOrCreateThread(e),
+e.name=b,this.kernelThreadStates_[b]={pid:a,thread:e,openSlice:void 0,openSliceTS:void 0},this.threadsByLinuxPid[a]=e);return this.kernelThreadStates_[b]},importEvents:function(){this.importCpuData();this.alignClocks()&&this.buildPerThreadCpuSlicesFromCpuState()},finalizeImport:function(){},buildPerThreadCpuSlicesFromCpuState:function(){for(var b in this.cpuStates_)for(var a=this.cpuStates_[b].cpu,e=0;e<a.slices.length;e++){var d=a.slices[e],c=this.threadsByLinuxPid[d.args.tid];c&&(c.tempCpuSlices||
+(c.tempCpuSlices=[]),d.index=e,c.tempCpuSlices.push(d))}var m=tracing.getColorIdByName("running"),j=tracing.getColorIdByName("runnable"),h=tracing.getColorIdByName("sleeping"),f=tracing.getColorIdByName("iowait");this.model_.getAllThreads().forEach(function(b){if(b.tempCpuSlices){var a=b.tempCpuSlices;delete b.tempCpuSlices;a.sort(function(b,a){var e=b.start-a.start;return 0==e?b.index-a.index:e});var e=[];if(a.length){var d=a[0];e.push(new tracing.TimelineSlice("Running",m,d.start,{},d.duration))}for(d=
+1;d<a.length;d++){var c=a[d-1],g=a[d],k=g.start-c.end;if("S"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("Sleeping",h,c.end,{},k));else if("R"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("Runnable",j,c.end,{},k));else if("D"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("I/O Wait",f,c.end,{},k));else if("T"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("__TASK_STOPPED",f,c.end,{},k));else if("t"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("debug",
+f,c.end,{},k));else if("Z"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("Zombie",f,c.end,{},k));else if("X"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("Exit Dead",f,c.end,{},k));else if("x"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("Task Dead",f,c.end,{},k));else if("W"==c.args.stateWhenDescheduled)e.push(new tracing.TimelineSlice("WakeKill",f,c.end,{},k));else throw"Unrecognized state: "+c.args.stateWhenDescheduled;e.push(new tracing.TimelineSlice("Running",
+m,g.start,{},g.duration))}b.cpuSlices=e}})},alignClocks:function(){if(0==this.clockSyncRecords_.length){if(!this.isAdditionalImport_)return;this.abortImport();return!1}var b=this.clockSyncRecords_[0];if(0==b.parentTS||b.parentTS==b.perfTS)return!0;var b=b.parentTS-b.perfTS,a;for(a in this.cpuStates_){for(var e=this.cpuStates_[a].cpu,d=0;d<e.slices.length;d++){var c=e.slices[d];c.start+=b;c.duration=c.duration}for(var m in e.counters){d=e.counters[m];for(c=0;c<d.timestamps.length;c++)d.timestamps[c]+=
+b}}for(var j in this.kernelThreadStates_){a=this.kernelThreadStates_[j].thread;for(d=0;d<a.subRows[0].length;d++)a.subRows[0][d].start+=b}return!0},abortImport:function(){if(this.pushedEventsToThreads)throw"Cannot abort, have alrady pushedCpuDataToThreads.";for(var b in this.cpuStates_)delete this.model_.cpus[b];for(var a in this.kernelThreadStates_){b=this.kernelThreadStates_[a].thread;var e=b.parent;delete e.threads[b.tid];delete this.model_.processes[e.pid]}this.model_.importErrors.push("Cannot import kernel trace without a clock sync.")},
+markPidRunnable:function(){},importError:function(b){this.model_.importErrors.push("Line "+(this.lineNumber+1)+": "+b)},malformedEvent:function(b){this.importError("Malformed "+b+" event")},processBegin:function(b,a,e,d,c,m){var j=tracing.getStringColorId(e),e=new tracing.TimelineThreadSlice(e,j,d,null);e.pid=c;e.tid=m;e.threadName=a;b.openSlices.push(e)},processEnd:function(b,a){if(0!=b.openSlices.length){var e=b.openSlices.pop();e.duration=a-e.start;var d=this.model_.getOrCreateProcess(e.pid).getOrCreateThread(e.tid);
+d.name||(d.name=e.threadName);this.threadsByLinuxPid[e.tid]=d;d.getSubrow(b.openSlices.length).push(e);b.openSlices.length&&b.openSlices[b.openSlices.length-1].subSlices.push(e)}},autoCloseOpenSlices:function(){this.model_.updateBounds();var b=[],a;for(a in this.threadStateByKPID_)for(var e=this.threadStateByKPID_[a],d=0;d<e.openSlices.length;d++){var c=e.openSlices[d];b.push(c.start);for(var m=0;m<c.subSlices.length;m++){var j=c.subSlices[m];b.push(j.start);j.duration&&b.push(j.end)}}b=this.model_.maxTimestamp?
+Math.max(this.model_.maxTimestamp,Math.max.apply(Math,b)):Math.max.apply(Math,b);for(a in this.threadStateByKPID_)for(e=this.threadStateByKPID_[a];0<e.openSlices.length;)c=e.openSlices.pop(),c.duration=b-c.start,c.didNotFinish=!0,this.model_.getOrCreateProcess(c.pid).getOrCreateThread(c.tid).getSubrow(e.openSlices.length).push(c),e.openSlices.length&&e.openSlices[e.openSlices.length-1].subSlices.push(c)},processCounter:function(b,a,e,d){b=this.model_.getOrCreateProcess(d).getOrCreateCounter("",b);
+0==b.numSeries&&(b.seriesNames.push("state"),b.seriesColors.push(tracing.getStringColorId(b.name+".state")));b.timestamps.push(a);b.samples.push(e)},importCpuData:function(){this.lines_=this.events_.split("\n");for(this.lineNumber=0;this.lineNumber<this.lines_.length;++this.lineNumber){var a=this.lines_[this.lineNumber];if(!(/^#/.exec(a)||0==a.length)){var g=d.exec(a);if(g){var f=this.getOrCreateCpuState(parseInt(g[2])),a=1E3*parseFloat(g[3]),l=g[4];switch(l){case "sched_switch":var i=h.exec(g[5]);
+if(!i){this.malformedEvent(l);continue}var g=i[4],m=i[5],l=parseInt(i[6]),i=parseInt(i[7]);f.switchRunningLinuxPid(this,g,a,l,m,i);break;case "sched_wakeup":i=b.exec(g[5]);if(!i){this.malformedEvent(l);continue}f=i[1];g=parseInt(i[2]);i=parseInt(i[3]);this.markPidRunnable(a,g,f,i);break;case "power_start":i=/type=(\d+) state=(\d) cpu_id=(\d)+/.exec(g[5]);if(!i){this.malformedEvent(l);continue}g=parseInt(i[3]);g=this.getOrCreateCpuState(g);if("1"==i[1])g=g.cpu.getOrCreateCounter("","C-State");else{this.importError("Don't understand power_start events of type "+
+i[1]);continue}0==g.numSeries&&(g.seriesNames.push("state"),g.seriesColors.push(tracing.getStringColorId(g.name+".state")));i=parseInt(i[2]);g.timestamps.push(a);g.samples.push(i);break;case "power_frequency":i=/type=(\d+) state=(\d+) cpu_id=(\d)+/.exec(g[5]);if(!i){this.malformedEvent(l);continue}g=parseInt(i[3]);g=this.getOrCreateCpuState(g);g=g.cpu.getOrCreateCounter("","Power Frequency");0==g.numSeries&&(g.seriesNames.push("state"),g.seriesColors.push(tracing.getStringColorId(g.name+".state")));
+i=parseInt(i[2]);g.timestamps.push(a);g.samples.push(i);break;case "cpu_frequency":i=/state=(\d+) cpu_id=(\d)+/.exec(g[5]);if(!i){this.malformedEvent(l);continue}g=parseInt(i[2]);g=this.getOrCreateCpuState(g);g=g.cpu.getOrCreateCounter("","Clock Frequency");0==g.numSeries&&(g.seriesNames.push("state"),g.seriesColors.push(tracing.getStringColorId(g.name+".state")));i=parseInt(i[1]);g.timestamps.push(a);g.samples.push(i);break;case "cpu_idle":i=/state=(\d+) cpu_id=(\d)+/.exec(g[5]);if(!i){this.malformedEvent(l);
+continue}g=parseInt(i[2]);g=this.getOrCreateCpuState(g);g=g.cpu.getOrCreateCounter("","C-State");0==g.numSeries&&(g.seriesNames.push("state"),g.seriesColors.push(tracing.getStringColorId(g.name)));i=parseInt(i[1]);4294967295!=i?g.samples.push(i):g.samples.push(0);g.timestamps.push(a);break;case "workqueue_execute_start":i=j.exec(g[5]);if(!i){this.malformedEvent(l);continue}g=this.getOrCreateKernelThread(g[1]);g.openSliceTS=a;g.openSlice=i[2];break;case "workqueue_execute_end":i=o.exec(g[5]);if(!i){this.malformedEvent(l);
+continue}g=this.getOrCreateKernelThread(g[1]);g.openSlice&&(a=new tracing.TimelineSlice(g.openSlice,tracing.getStringColorId(g.openSlice),g.openSliceTS,{},a-g.openSliceTS),g.thread.subRows[0].push(a));g.openSlice=void 0;break;case "i915_gem_object_pwrite":i=/obj=(.+), offset=(\d+), len=(\d+)/.exec(g[5]);if(!i){this.malformedEvent(l);continue}f=i[1];m=parseInt(i[2]);i=parseInt(i[3]);g=this.getOrCreateKernelThread("i915_gem",0,1);g.openSlice="pwrite:"+f;a=new tracing.TimelineSlice(g.openSlice,tracing.getStringColorId(g.openSlice),
+a,{obj:f,offset:m,len:i},0);g.thread.subRows[0].push(a);break;case "i915_flip_request":i=/plane=(\d+), obj=(.+)/.exec(g[5]);if(!i){this.malformedEvent(l);continue}m=parseInt(i[1]);f=i[2];g=this.getOrCreateKernelThread("i915_flip",0,2);g.openSliceTS=a;g.openSlice="flip:"+f+"/"+m;break;case "i915_flip_complete":i=/plane=(\d+), obj=(.+)/.exec(g[5]);if(!i){this.malformedEvent(l);continue}m=parseInt(i[1]);f=i[2];g=this.getOrCreateKernelThread("i915_flip",0,2);g.openSlice&&(a=new tracing.TimelineSlice(g.openSlice,
+tracing.getStringColorId(g.openSlice),g.openSliceTS,{obj:f,plane:m},a-g.openSliceTS),g.thread.subRows[0].push(a));g.openSlice=void 0;break;case "0":case "tracing_mark_write":if(i=e.exec(g[5]))this.clockSyncRecords_.push({perfTS:a,parentTS:1E3*i[1]});else{f=this.parsePid(g[1]);m=this.parseThreadName(g[1]);i=f;i in this.threadStateByKPID_||(this.threadStateByKPID_[i]=new c);var r=this.threadStateByKPID_[i],i=g[5].split("|");switch(i[0]){case "B":g=parseInt(i[1]);l=i[2];this.processBegin(r,m,l,a,g,f);
+break;case "E":this.processEnd(r,a);break;case "C":g=parseInt(i[1]);l=i[2];i=parseInt(i[3]);this.processCounter(l,a,i,g);break;default:this.malformedEvent(l)}}break;default:console.log("unknown event "+l)}}else this.importError("Unrecognized line: "+a)}}}};tracing.TimelineModel.registerImporter(a);return{LinuxPerfImporter:a,_LinuxPerfImporterTestExports:TestExports}});cr.define("tracing",function(){function f(){this.openSlices=[]}function c(a,d){this.model_=a;"string"===typeof d||d instanceof String?("["==d[0]&&(n=d.length,"]"!=d[n-1]&&"\n"!=d[n-1]?d+="]":"]"!=d[n-2]&&"\n"==d[n-1]?d+="]":"]"!=d[n-3]&&("\r"==d[n-2]&&"\n"==d[n-1])&&(d+="]")),this.events_=JSON.parse(d)):this.events_=d;this.events_.traceEvents&&(this.events_=this.events_.traceEvents);this.threadStateByPTID_={};this.allAsyncEvents_=[]}c.canImport=function(a){return"string"===typeof a||a instanceof String?
+"{"==a[0]||"["==a[0]:a instanceof Array&&a.length&&a[0].ph?!0:a.traceEvents?a.traceEvents instanceof Array&&a.traceEvents[0].ph:!1};c.prototype={__proto__:Object.prototype,processBeginEvent:function(a,d,c){var b=tracing.getStringColorId(c.name),a={index:a,slice:new tracing.TimelineThreadSlice(c.name,b,c.ts/1E3,c.args)};c.uts&&(a.slice.startInUserTime=c.uts/1E3);"0"===c.args["ui-nest"]?this.model_.importErrors.push("ui-nest no longer supported."):d.openSlices.push(a)},processEndEvent:function(a,d){if("0"===
+d.args["ui-nest"])this.model_.importErrors.push("ui-nest no longer supported.");else if(0!=a.openSlices.length){var c=a.openSlices.pop().slice;c.duration=d.ts/1E3-c.start;d.uts&&(c.durationInUserTime=d.uts/1E3-c.startInUserTime);for(var b in d.args)c.args[b]=d.args[b];this.model_.getOrCreateProcess(d.pid).getOrCreateThread(d.tid).getSubrow(a.openSlices.length).push(c);a.openSlices.length&&a.openSlices[a.openSlices.length-1].slice.subSlices.push(c)}},processAsyncEvent:function(a,d,c){a=this.model_.getOrCreateProcess(c.pid).getOrCreateThread(c.tid);
+this.allAsyncEvents_.push({event:c,thread:a})},autoCloseOpenSlices:function(){this.model_.updateBounds();var a=[],d;for(d in this.threadStateByPTID_)for(var c=this.threadStateByPTID_[d],b=0;b<c.openSlices.length;b++){var e=c.openSlices[b];a.push(e.slice.start);for(var j=0;j<e.slice.subSlices.length;j++){var f=e.slice.subSlices[j];a.push(f.start);f.duration&&a.push(f.end)}}a=this.model_.maxTimestamp?Math.max(this.model_.maxTimestamp,Math.max.apply(Math,a)):Math.max.apply(Math,a);for(d in this.threadStateByPTID_)for(c=
+this.threadStateByPTID_[d];0<c.openSlices.length;)e=c.openSlices.pop(),e.slice.duration=a-e.slice.start,e.slice.didNotFinish=!0,b=this.events_[e.index],this.model_.getOrCreateProcess(b.pid).getOrCreateThread(b.tid).getSubrow(c.openSlices.length).push(e.slice),c.openSlices.length&&c.openSlices[c.openSlices.length-1].slice.subSlices.push(e.slice)},processCounterEvent:function(a){var c;c=void 0!==a.id?a.name+"["+a.id+"]":a.name;c=this.model_.getOrCreateProcess(a.pid).getOrCreateCounter(a.cat,c);if(0==
+c.numSeries){for(var f in a.args)c.seriesNames.push(f),c.seriesColors.push(tracing.getStringColorId(c.name+"."+f));if(0==c.numSeries){this.model_.importErrors.push("Expected counter "+a.name+" to have at least one argument to use as a value.");delete c.parent.counters[c.name];return}}c.timestamps.push(a.ts/1E3);for(var b=0;b<c.numSeries;b++)f=c.seriesNames[b],void 0===a.args[f]?c.samples.push(0):c.samples.push(a.args[f])},importEvents:function(){for(var a=this.events_,c=0;c<a.length;c++){var h=a[c],
+b=tracing.TimelineThread.getPTIDFromPidAndTid(h.pid,h.tid);b in this.threadStateByPTID_||(this.threadStateByPTID_[b]=new f);var e=this.threadStateByPTID_[b];"B"==h.ph?this.processBeginEvent(c,e,h):"E"==h.ph?this.processEndEvent(e,h):"S"==h.ph?this.processAsyncEvent(c,e,h):"F"==h.ph?this.processAsyncEvent(c,e,h):"T"==h.ph?this.processAsyncEvent(c,e,h):"I"==h.ph?(this.processBeginEvent(c,e,h),this.processEndEvent(e,h)):"C"==h.ph?this.processCounterEvent(h):"M"==h.ph?"thread_name"==h.name?this.model_.getOrCreateProcess(h.pid).getOrCreateThread(h.tid).name=
+h.args.name:this.model_.importErrors.push("Unrecognized metadata name: "+h.name):this.model_.importErrors.push("Unrecognized event phase: "+h.ph+"("+h.name+")")}a=!1;for(b in this.threadStateByPTID_)e=this.threadStateByPTID_[b],a|=0<e.openSlices.length;a&&this.autoCloseOpenSlices()},finalizeImport:function(){if(0!=this.allAsyncEvents_.length){this.allAsyncEvents_.sort(function(b,a){return b.event.ts-a.event.ts});for(var a={},c=this.allAsyncEvents_,f=0;f<c.length;f++){var b=c[f],e=b.event,j=e.name;
+if(void 0===j)this.model_.importErrors.push("Async events (ph: S, T or F) require an name parameter.");else{var o=e.id;if(void 0===o)this.model_.importErrors.push("Async events (ph: S, T or F) require an id parameter.");else if("S"==e.ph)void 0===a[j]&&(a[j]={}),a[j][o]?this.model_.importErrors.push("At "+e.ts+", an slice of the same id "+o+" was alrady open."):(a[j][o]=[],a[j][o].push(b));else if(void 0===a[j])this.model_.importErrors.push("At "+e.ts+", no slice named "+j+" was open.");else if(void 0===
+a[j][o])this.model_.importErrors.push("At "+e.ts+", no slice named "+j+" with id="+o+" was open.");else{var k=a[j][o];k.push(b);if("F"==e.ph){var g=new tracing.TimelineAsyncSlice(j,tracing.getStringColorId(j),k[0].event.ts/1E3);g.duration=e.ts/1E3-k[0].event.ts/1E3;g.startThread=k[0].thread;g.endThread=b.thread;g.id=o;g.args=k[0].event.args;g.subSlices=[];for(b=1;b<k.length;++b){var q=j;"T"==k[b-1].event.ph&&(q=j+":"+k[b-1].event.args.step);q=new tracing.TimelineAsyncSlice(q,tracing.getStringColorId(j+
+b),k[b-1].event.ts/1E3);q.duration=k[b].event.ts/1E3-k[b-1].event.ts/1E3;q.startThread=k[b-1].thread;q.endThread=k[b].thread;q.id=o;q.args=k[b-1].event.args;g.subSlices.push(q)}var k=g.subSlices[g.subSlices.length-1],l;for(l in e.args)k.args[l]=e.args[l];g.startThread.asyncSlices.push(g);delete a[j][o]}}}}}}};tracing.TimelineModel.registerImporter(c);return{TraceEventImporter:c}});cr.define("tracing",function(){function f(a,c,f){if(0==a.length)return 1;for(var b=0,e=a.length-1,j,o,k=-1;b<=e;)j=Math.floor((b+e)/2),o=c(a[j])-f,0>o?b=j+1:(0<o||(k=j),e=j-1);return-1!=k?k:b}function c(a,c,h,b,e,j){if(!(b>e)){var o=f(a,c,b);if(-1!=o&&(0<o&&c(a[o-1])+h(a[o-1])>=b&&j(a[o-1]),o!=a.length))for(h=a.length;o<h&&!(c(a[o])>=e);o++)j(a[o])}}return{findLowIndexInSortedArray:f,findLowIndexInSortedIntervals:function(a,c,h,b){var e=f(a,c,b);return 0==e?b>=c(a[0])&&b<c(a[0]+h(a[0]))?0:-1:e<=a.length&&
+b>=c(a[e-1])&&b<c(a[e-1])+h(a[e-1])?e-1:a.length},iterateOverIntersectingIntervals:c,getIntersectingIntervals:function(a,d,f,b,e){var j=[];c(a,d,f,b,e,function(b){j.push(b)});return j}}});cr.define("tracing",function(){function f(){var c=document.createElement("iframe");c.style.cssText="width:100%;height:0;border:0;visibility:hidden";document.body.appendChild(c);this._doc=c.contentDocument;this._window=c.contentWindow;this._doc.body.style.cssText="padding:0;margin:0;overflow:hidden";for(var c=document.querySelectorAll("link[rel=stylesheet]"),a=0;a<c.length;a++){var d=c[a],f=this._doc.createElement("link");f.rel="stylesheet";f.href=d.href;this._doc.head.appendChild(f)}}f.prototype=
+{__proto__:Object.prototype,measure:function(c){this._doc.body.appendChild(c);var a=this._window.getComputedStyle(c),d=parseInt(a.width,10),a=parseInt(a.height,10);this._doc.body.removeChild(c);return{width:d,height:a}}};return{MeasuringStick:f}});cr.define("tracing",function(){function f(b){this.parentEl_=b;this.scaleX_=1;this.gridTimebase_=this.panX_=0;this.gridStep_=1E3/60;this.hasCalledSetupFunction_=this.gridEnabled_=!1;this.onResizeBoundToThis_=this.onResize_.bind(this);this.checkForAttachInterval_=setInterval(this.checkForAttach_.bind(this),250)}function c(b,a){this.track=b;this.slice=a}function a(b,a,c){this.track=b;this.counter=a;this.sampleIndex=c}function d(){this.range_dirty_=!0;this.range_={};this.length_=0}f.prototype={__proto__:cr.EventTarget.prototype,
+setWhenPossible:function(b){this.pendingSetFunction_=b},get isAttachedToDocument_(){for(var b=this.parentEl_;b.parentNode;)b=b.parentNode;return b==this.parentEl_.ownerDocument},onResize_:function(){this.dispatchChangeEvent()},checkForAttach_:function(){if(this.isAttachedToDocument_&&0!=this.clientWidth){this.iframe_||(this.iframe_=document.createElement("iframe"),this.iframe_.style.cssText="position:absolute;width:100%;height:0;border:0;visibility:hidden;",this.parentEl_.appendChild(this.iframe_),
+this.iframe_.contentWindow.addEventListener("resize",this.onResizeBoundToThis_));var b=this.clientWidth+"x"+this.clientHeight;this.pendingSetFunction_&&(this.lastSize_=b,this.pendingSetFunction_(),this.pendingSetFunction_=void 0);window.clearInterval(this.checkForAttachInterval_);this.checkForAttachInterval_=void 0}},dispatchChangeEvent:function(){cr.dispatchSimpleEvent(this,"change")},detach:function(){this.checkForAttachInterval_&&(window.clearInterval(this.checkForAttachInterval_),this.checkForAttachInterval_=
+void 0);this.iframe_.removeEventListener("resize",this.onResizeBoundToThis_);this.parentEl_.removeChild(this.iframe_)},get scaleX(){return this.scaleX_},set scaleX(b){this.scaleX_!=b&&(this.scaleX_=b,this.dispatchChangeEvent())},get panX(){return this.panX_},set panX(b){this.panX_!=b&&(this.panX_=b,this.dispatchChangeEvent())},setPanAndScale:function(b,a){if(this.scaleX_!=a||this.panX_!=b)this.scaleX_=a,this.panX_=b,this.dispatchChangeEvent()},xWorldToView:function(b){return(b+this.panX_)*this.scaleX_},
+xWorldVectorToView:function(b){return b*this.scaleX_},xViewToWorld:function(b){return b/this.scaleX_-this.panX_},xViewVectorToWorld:function(b){return b/this.scaleX_},xPanWorldPosToViewPos:function(b,a,c){if("string"==typeof a)if("left"==a)a=0;else if("center"==a)a=c/2;else if("right"==a)a=c-1;else throw Error("unrecognized string for viewPos. left|center|right");this.panX=a/this.scaleX_-b},xPanWorldRangeIntoView:function(b,a,c){0>this.xWorldToView(b)?this.xPanWorldPosToViewPos(b,"left",c):this.xWorldToView(a)>
+c&&this.xPanWorldPosToViewPos(a,"right",c)},xSetWorldRange:function(b,a,c){this.setPanAndScale(-b,c/(a-b))},get gridEnabled(){return this.gridEnabled_},set gridEnabled(b){this.gridEnabled_!=b&&(this.gridEnabled_=b&&!0,this.dispatchChangeEvent())},get gridTimebase(){return this.gridTimebase_},set gridTimebase(b){this.gridTimebase_!=b&&(this.gridTimebase_=b,cr.dispatchSimpleEvent(this,"change"))},get gridStep(){return this.gridStep_},applyTransformToCanavs:function(b){b.transform(this.scaleX_,0,0,1,
+this.panX_*this.scaleX_,0)}};c.prototype={get selected(){return this.slice.selected},set selected(b){this.slice.selected=b}};a.prototype={get selected(){return!0==this.track.selectedSamples[this.sampleIndex]},set selected(b){this.track.selectedSamples[this.sampleIndex]=b?!0:!1;this.track.invalidate()}};d.prototype={__proto__:Object.prototype,get range(){if(this.range_dirty_){for(var b=Infinity,a=-b,c=0;c<this.length_;c++){var d=this[c];d.slice&&(b=Math.min(b,d.slice.start),a=Math.max(a,d.slice.end))}this.range_=
+{min:b,max:a};this.range_dirty_=!1}return this.range_},get duration(){return this.range.max-this.range.min},get length(){return this.length_},clear:function(){for(var b=0;b<this.length_;++b)delete this[b];this.length_=0;this.range_dirty_=!0},push_:function(b){this[this.length_++]=b;this.range_dirty_=!0;return b},addSlice:function(b,a){return this.push_(new c(b,a))},addCounterSample:function(b,e,c){return this.push_(new a(b,e,c))},subSelection:function(b,a){var a=a||1,c=new d;c.range_dirty_=!0;if(0>
+b||b+a>this.length_)throw"Index out of bounds";for(var f=b;f<b+a;f++)c.push_(this[f]);return c},getCounterSampleHits:function(){for(var b=new d,e=0;e<this.length_;e++)this[e]instanceof a&&b.push_(this[e]);return b},getSliceHits:function(){for(var b=new d,a=0;a<this.length_;a++)this[a]instanceof c&&b.push_(this[a]);return b},map:function(b){for(var a=0;a<this.length_;a++)b(this[a])},getShiftedSelection:function(b){for(var a=new d,c=0;c<this.length_;c++){var f=this[c];f.track.addItemNearToProvidedHitToSelection(f,
+b,a)}return 0==a.length?void 0:a}};var h=cr.ui.define("div");h.prototype={__proto__:HTMLDivElement.prototype,model_:null,decorate:function(){this.classList.add("timeline");this.viewport_=new f(this);this.viewportTrack=new tracing.TimelineViewportTrack;this.tracks_=this.ownerDocument.createElement("div");this.appendChild(this.tracks_);this.dragBox_=this.ownerDocument.createElement("div");this.dragBox_.className="timeline-drag-box";this.appendChild(this.dragBox_);this.hideDragBox_();this.bindEventListener_(document,
+"keypress",this.onKeypress_,this);this.bindEventListener_(document,"keydown",this.onKeydown_,this);this.bindEventListener_(document,"mousedown",this.onMouseDown_,this);this.bindEventListener_(document,"mousemove",this.onMouseMove_,this);this.bindEventListener_(document,"mouseup",this.onMouseUp_,this);this.bindEventListener_(document,"dblclick",this.onDblClick_,this);this.lastMouseViewPos_={x:0,y:0};this.selection_=new d},bindEventListener_:function(b,a,c,d){this.boundListeners_||(this.boundListeners_=
+[]);c=c.bind(d);this.boundListeners_.push({object:b,event:a,boundFunc:c});b.addEventListener(a,c)},detach:function(){for(var b=0;b<this.tracks_.children.length;b++)this.tracks_.children[b].detach();for(b=0;b<this.boundListeners_.length;b++){var a=this.boundListeners_[b];a.object.removeEventListener(a.event,a.boundFunc)}this.boundListeners_=void 0;this.viewport_.detach()},get viewport(){return this.viewport_},get model(){return this.model_},set model(b){if(!b)throw Error("Model cannot be null");if(this.model)throw Error("Cannot set model twice.");
+this.model_=b;var a=[];b.getAllThreads().forEach(function(b){a.push(b.userFriendlyName)});b.getAllCounters().forEach(function(b){a.push(b.name)});b.getAllCpus().forEach(function(b){a.push("CPU "+b.cpuNumber)});var c=0,d=new tracing.MeasuringStick,f=document.createElement("div");f.style.position="fixed";f.className="timeline-canvas-based-track-title";a.forEach(function(b){f.textContent=b+":__";b=d.measure(f).width;300<b&&(b=300);b>c&&(c=b)});for(var c=c+"px",g=0;g<this.tracks_.children.length;g++)this.tracks_.children[g].detach();
+this.tracks_.textContent="";this.viewportTrack.headingWidth=c;this.viewportTrack.viewport=this.viewport_;g=b.getAllCpus();g.sort(tracing.TimelineCpu.compare);g.forEach(function(b){var a=new tracing.TimelineCpuTrack;a.heading="CPU "+b.cpuNumber+":";a.headingWidth=c;a.viewport=this.viewport_;a.cpu=b;this.tracks_.appendChild(a);for(var e in b.counters){var d=b.counters[e],a=new tracing.TimelineCounterTrack;a.heading="CPU "+b.cpuNumber+" "+d.name+":";a.headingWidth=c;a.viewport=this.viewport_;a.counter=
+d;this.tracks_.appendChild(a)}}.bind(this));b=b.getAllProcesses();b.sort(tracing.TimelineProcess.compare);b.forEach(function(b){var a=[],e;for(e in b.counters)a.push(b.counters[e]);a.sort(tracing.TimelineCounter.compare);a.forEach(function(b){var a=new tracing.TimelineCounterTrack;a.heading=b.name+":";a.headingWidth=c;a.viewport=this.viewport_;a.counter=b;this.tracks_.appendChild(a)}.bind(this));a=[];for(e in b.threads)a.push(b.threads[e]);a.sort(tracing.TimelineThread.compare);a.forEach(function(b){var a=
+new tracing.TimelineThreadTrack;a.heading=b.userFriendlyName+":";a.tooltip=b.userFriendlyDetails;a.headingWidth=c;a.viewport=this.viewport_;a.thread=b;this.tracks_.appendChild(a)}.bind(this))}.bind(this));this.viewport_.setWhenPossible(function(){this.viewport_.xSetWorldRange(this.model_.minTimestamp,this.model_.maxTimestamp,this.firstCanvas.width)}.bind(this))},addAllObjectsMatchingFilterToSelection:function(b,a){for(var c=0;c<this.tracks_.children.length;++c)this.tracks_.children[c].addAllObjectsMatchingFilterToSelection(b,
+a)},get focusElement(){return this.focusElement_?this.focusElement_:this.parentElement},set focusElement(b){this.focusElement_=b},get listenToKeys_(){return!this.viewport_.isAttachedToDocument_?!1:!this.focusElement_?!0:0<=this.focusElement.tabIndex?document.activeElement==this.focusElement:!0},onKeypress_:function(b){var a=this.viewport_;if(this.firstCanvas&&this.listenToKeys_){var c=this.firstCanvas.clientWidth;switch(b.keyCode){case 101:b=a.xViewToWorld(this.lastMouseViewPos_.x);a.xPanWorldPosToViewPos(b,
+"center",c);break;case 119:this.zoomBy_(1.5);break;case 115:this.zoomBy_(1/1.5);break;case 103:this.onGridToggle_(!0);break;case 71:this.onGridToggle_(!1);break;case 87:this.zoomBy_(10);break;case 83:this.zoomBy_(0.1);break;case 97:a.panX+=a.xViewVectorToWorld(0.1*c);break;case 100:a.panX-=a.xViewVectorToWorld(0.1*c);break;case 65:a.panX+=a.xViewVectorToWorld(0.5*c);break;case 68:a.panX-=a.xViewVectorToWorld(0.5*c)}}},onKeydown_:function(b){if(this.listenToKeys_){var a;switch(b.keyCode){case 37:if(a=
+this.selection.getShiftedSelection(-1))this.setSelectionAndMakeVisible(a),b.preventDefault();break;case 39:if(a=this.selection.getShiftedSelection(1))this.setSelectionAndMakeVisible(a),b.preventDefault();break;case 9:-1==this.focusElement.tabIndex&&(b.shiftKey?this.selectPrevious_(b):this.selectNext_(b),b.preventDefault())}}},zoomBy_:function(b){if(this.firstCanvas){var a=this.viewport_,c=this.firstCanvas.clientWidth,d=this.lastMouseViewPos_.x,f=a.xViewToWorld(d);a.scaleX*=b;a.xPanWorldPosToViewPos(f,
+d,c)}},get keyHelp(){var b="Keyboard shortcuts:\n w/s     : Zoom in/out    (with shift: go faster)\n a/d     : Pan left/right\n e       : Center on mouse\n g/G     : Shows grid at the start/end of the selected task\n",b=this.focusElement.tabIndex?b+" <-      : Select previous event on current timeline\n ->      : Select next event on current timeline\n":b+" <-,^TAB : Select previous event on current timeline\n ->, TAB : Select next event on current timeline\n";return b+"\nDbl-click to zoom in; Shift dbl-click to zoom out\n"},
+get selection(){return this.selection_},set selection(b){if(!(b instanceof d))throw"Expected TimelineSelection";var a;for(a=0;a<this.selection_.length;a++)this.selection_[a].selected=!1;this.selection_=b;cr.dispatchSimpleEvent(this,"selectionChange");for(a=0;a<this.selection_.length;a++)this.selection_[a].selected=!0;this.viewport_.dispatchChangeEvent()},setSelectionAndMakeVisible:function(b,a){if(!(b instanceof d))throw"Expected TimelineSelection";this.selection=b;var c=this.selection.range,f=this.viewport_.xWorldVectorToView(c.max-
+c.min);a&&50>f?(f=c.min+0.5*(c.max-c.min),c=5*(c.max-c.min),this.viewport_.xSetWorldRange(f-0.5*c,f+0.5*c,this.firstCanvas.width)):this.viewport_.xPanWorldRangeIntoView(c.min,c.max,this.firstCanvas.width)},get firstCanvas(){return this.tracks_.firstChild?this.tracks_.firstChild.firstCanvas:void 0},hideDragBox_:function(){this.dragBox_.style.left="-1000px";this.dragBox_.style.top="-1000px";this.dragBox_.style.width=0;this.dragBox_.style.height=0},setDragBoxPosition_:function(a,c){var d=Math.min(a.clientX,
+c.clientX),f=Math.max(a.clientX,c.clientX),h=Math.min(a.clientY,c.clientY),g=Math.max(a.clientY,c.clientY);this.dragBox_.style.left=d+"px";this.dragBox_.style.top=h+"px";this.dragBox_.style.width=f-d+"px";this.dragBox_.style.height=g-h+"px";h=this.firstCanvas;d=this.viewport_.xViewToWorld(d-h.offsetLeft);f=this.viewport_.xViewToWorld(f-h.offsetLeft);this.dragBox_.textContent=Math.round(100*(f-d))/100+"ms";h=new cr.Event("selectionChanging");h.loWX=d;h.hiWX=f;this.dispatchEvent(h)},onGridToggle_:function(a){var a=
+a?this.selection_.range.min:this.selection_.range.max,c=Math.ceil((a-this.model_.minTimestamp)/this.viewport_.gridStep_);this.viewport_.gridTimebase=a-(c+1)*this.viewport_.gridStep_;this.viewport_.gridEnabled=!0},onMouseDown_:function(a){var c=this.firstCanvas,d=this.tracks_.getClientRects()[0];d&&(a.clientX>=d.left&&a.clientX<d.right&&a.clientY>=d.top&&a.clientY<d.bottom&&a.x>=c.offsetLeft)&&(this.viewport_.xViewToWorld(a.clientX-c.offsetLeft),this.dragBeginEvent_=a,a.preventDefault(),0<=this.focusElement.tabIndex&&
+this.focusElement.focus())},onMouseMove_:function(a){if(this.firstCanvas){var c=this.firstCanvas;this.lastMouseViewPos_={x:a.clientX-c.offsetLeft,y:a.clientY-c.offsetTop};this.dragBeginEvent_&&this.setDragBoxPosition_(this.dragBeginEvent_,a)}},onMouseUp_:function(a){var c;if(this.dragBeginEvent_){this.hideDragBox_();var f=this.dragBeginEvent_;this.dragBeginEvent_=null;var h=Math.min(f.clientX,a.clientX);c=Math.max(f.clientX,a.clientX);var k=Math.min(f.clientY,a.clientY),a=Math.max(f.clientY,a.clientY),
+f=this.firstCanvas,h=this.viewport_.xViewToWorld(h-f.offsetLeft),f=this.viewport_.xViewToWorld(c-f.offsetLeft),g=new d;for(c=0;c<this.tracks_.children.length;c++){var q=this.tracks_.children[c],l=q.getBoundingClientRect(),i=Math.max(k,l.top),l=Math.min(a,l.bottom);i<=l&&q.addIntersectingItemsInRangeToSelection(h,f,k,a,g)}this.selection=g}},onDblClick_:function(a){if(!(a.x<this.firstCanvas.offsetLeft)){var c=4;a.shiftKey&&(c=1/c);this.zoomBy_(c);a.preventDefault()}}};cr.defineProperty(h,"model",cr.PropertyKind.JS);
+return{Timeline:h,TimelineSelectionSliceHit:c,TimelineSelectionCounterSampleHit:a,TimelineSelection:d,TimelineViewport:f}});cr.define("tracing",function(){function f(a){return Math.round(1E3*a)/1E3}function c(a,b){b=b||0;"string"!=typeof a&&(a=""+a);if(a.length>=b)return"";for(var c="",d=0;d<b-a.length;d++)c+=" ";return c}function a(a,b){return a+c(a,b)}function d(a,b){return c(a,b)+a}function h(b){var c="",h=b.getSliceHits(),k=b.getCounterSampleHits();if(1==h.length){var b=14,g=h[0].slice,c="Selected item:\n"+(a("Title",b)+": "+g.title+"\n"),c=c+(a("Start",b)+": "+f(g.start)+" ms\n"),c=c+(a("Duration",b)+": "+f(g.duration)+
+" ms\n");g.durationInUserTime&&(c+=a("Duration (U)",b)+": "+f(g.durationInUserTime)+" ms\n");var q=0,l;for(l in g.args)q+=1;if(0<q)for(l in c+=a("Args",b)+":\n",g.args)q=g.args[l],c+=a(" "+l,b)+": "+q+"\n"}else if(1<h.length){var b=55,c="Slices:\n",i=h.range.min,m=h.range.max;h.map(function(a){return a.slice.title});var r={};for(l=0;l<h.length;l++)g=h[l].slice,r[g.title]||(r[g.title]={slices:[]}),r[g.title].slices.push(g);g=0;for(q in r){var v=r[q],u=0;for(l=0;l<v.slices.length;l++)u+=v.slices[l].duration;
+g+=u;c+=" "+a(q,b)+": "+d(f(u)+"ms",12)+"   "+d(""+v.slices.length,5)+" occurrences\n"}c+=a("*Totals",b)+" : "+d(f(g)+"ms",12)+"   "+d(""+h.length,5)+" occurrences\n";c=c+"\n"+(a("Selection start",b)+" : "+d(f(i)+"ms",12)+"\n");c+=a("Selection extent",b)+" : "+d(f(m-i)+"ms",12)+"\n"}if(1==k.length){c="Selected counter:\n";b=55;k=k[0];h=k.counter;k=k.sampleIndex;q=[];for(l=0;l<h.numSeries;++l)q.push(h.samples[h.numSeries*k+l]);c+=a("Title",b)+": "+h.name+"\n";c+=a("Timestamp",b)+": "+f(h.timestamps[k])+
+" ms\n";c=1<h.numSeries?c+(a("Values",b)+": "+q.join("\n")+"\n"):c+(a("Value",b)+": "+q.join("\n")+"\n")}else 1<k.length&&0==h.length&&(c+="Analysis of multiple counters not yet implemented. Pick a single counter.");return c}var b=cr.ui.define("div");b.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.className="timeline-analysis"},set selection(a){this.textContent=h(a)}};return{TimelineAnalysisView:b}});cr.define("tracing",function(){function f(a,b){var c=document.createElement("div");c.classList.add("timeline-track-button");c.classList.add("timeline-track-close-button");c.textContent=String.fromCharCode(215);c.addEventListener("click",function(){a.style.display="None"});a.appendChild(c);if(b){var d=document.createElement("div");d.classList.add("timeline-track-button");d.classList.add("timeline-track-collapse-button");d.textContent="\u2212";var e=!1;d.addEventListener("click",function(){e=!e;a.collapsedDidChange(e);
+d.textContent=e?"+":"\u2212"});a.appendChild(d)}}function c(a,b){this.string=a;this.width=b}function a(){}var d=tracing.getPallette(),h=tracing.getPalletteHighlightIdBoost(),b={},e={},j=cr.ui.define("div");j.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.tracks_=[]},detach:function(){for(var a=0;a<this.tracks_.length;a++)this.tracks_[a].detach()},get viewport(){return this.viewport_},set viewport(a){this.viewport_=a;for(var b=0;b<this.tracks_.length;b++)this.tracks_[b].viewport=
+a;this.updateChildTracks_()},get firstCanvas(){if(this.tracks_.length)return this.tracks_[0].firstCanvas},addIntersectingItemsToSelection:function(a,b,c){for(var d=0;d<this.tracks_.length;d++){var e=this.tracks_[d].getBoundingClientRect();b>=e.top&&b<e.bottom&&this.tracks_[d].addIntersectingItemsToSelection(a,b,c)}return!1},addIntersectingItemsInRangeToSelection:function(a,b,c,d,e){for(var f=0;f<this.tracks_.length;f++){var g=this.tracks_[f].getBoundingClientRect(),h=Math.max(c,g.top),g=Math.min(d,
+g.bottom);h<=g&&this.tracks_[f].addIntersectingItemsInRangeToSelection(a,b,c,d,e)}},addAllObjectsMatchingFilterToSelection:function(a,b){for(var c=0;c<this.tracks_.length;c++)this.tracks_[c].addAllObjectsMatchingFilterToSelection(a,b)}};var o=cr.ui.define(j);o.prototype={__proto__:j.prototype,decorate:function(){this.classList.add("timeline-thread-track")},get thread(){return this.thread_},set thread(a){this.thread_=a;this.updateChildTracks_()},get tooltip(){return this.tooltip_},set tooltip(a){this.tooltip_=
+a;this.updateChildTracks_()},get heading(){return this.heading_},set heading(a){this.heading_=a;this.updateChildTracks_()},get headingWidth(){return this.headingWidth_},set headingWidth(a){this.headingWidth_=a;this.updateChildTracks_()},addTrack_:function(a){var b=new g;b.heading="";b.slices=a;b.headingWidth=this.headingWidth_;b.viewport=this.viewport_;this.tracks_.push(b);this.appendChild(b);return b},updateChildTracks_:function(){this.detach();this.textContent="";this.tracks_=[];if(this.thread_){if(this.thread_.cpuSlices){var a=
+this.addTrack_(this.thread_.cpuSlices);a.height="4px";a.decorateHit=function(a){a.thread=this.thread_}}if(this.thread_.asyncSlices.length)for(var b=this.thread_.asyncSlices.subRows,c=0;c<b.length;c++)a=this.addTrack_(b[c]),a.decorateHit=function(){},a.asyncStyle=!0;for(c=0;c<this.thread_.subRows.length;c++)a=this.addTrack_(this.thread_.subRows[c]),a.decorateHit=function(a){a.thread=this.thread_};0<this.tracks_.length&&(this.thread_.cpuSlices?(this.tracks_[1].heading=this.heading_,this.tracks_[1].tooltip=
+this.tooltip_):(this.tracks_[0].heading=this.heading_,this.tracks_[0].tooltip=this.tooltip_))}f(this,4<=this.tracks_.length)},collapsedDidChange:function(a){if(a)for(var a=parseInt(this.tracks_[0].height),b=0;b<this.tracks_.length;++b)2<a?this.tracks_[b].height=Math.floor(a)+"px":this.tracks_[b].style.display="None",a*=0.5;else for(b=0;b<this.tracks_.length;++b)this.tracks_[b].height=this.tracks_[0].height,this.tracks_[b].style.display=""}};var k=cr.ui.define(j);k.prototype={__proto__:j.prototype,
+decorate:function(){this.classList.add("timeline-thread-track")},get cpu(){return this.cpu_},set cpu(a){this.cpu_=a;this.updateChildTracks_()},get tooltip(){return this.tooltip_},set tooltip(a){this.tooltip_=a;this.updateChildTracks_()},get heading(){return this.heading_},set heading(a){this.heading_=a;this.updateChildTracks_()},get headingWidth(){return this.headingWidth_},set headingWidth(a){this.headingWidth_=a;this.updateChildTracks_()},updateChildTracks_:function(){this.detach();this.textContent=
+"";this.tracks_=[];if(this.cpu_){var a=new g;a.slices=this.cpu_.slices;a.headingWidth=this.headingWidth_;a.viewport=this.viewport_;this.tracks_.push(a);this.appendChild(a);this.tracks_[0].heading=this.heading_;this.tracks_[0].tooltip=this.tooltip_}f(this,!1)}};j=cr.ui.define("div");j.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.className="timeline-canvas-based-track";this.slices_=null;this.headingDiv_=document.createElement("div");this.headingDiv_.className="timeline-canvas-based-track-title";
+this.headingDiv_.onselectstart=function(){return!1};this.appendChild(this.headingDiv_);this.canvasContainer_=document.createElement("div");this.canvasContainer_.className="timeline-canvas-based-track-canvas-container";this.appendChild(this.canvasContainer_);this.canvas_=document.createElement("canvas");this.canvas_.className="timeline-canvas-based-track-canvas";this.canvasContainer_.appendChild(this.canvas_);this.ctx_=this.canvas_.getContext("2d")},detach:function(){this.viewport_&&this.viewport_.removeEventListener("change",
+this.viewportChangeBoundToThis_)},set headingWidth(a){this.headingDiv_.style.width=a},get heading(){return this.headingDiv_.textContent},set heading(a){this.headingDiv_.textContent=a},set tooltip(a){this.headingDiv_.title=a},get viewport(){return this.viewport_},set viewport(a){(this.viewport_=a)&&this.viewport_.removeEventListener("change",this.viewportChangeBoundToThis_);if(this.viewport_=a)this.viewportChangeBoundToThis_=this.viewportChange_.bind(this),this.viewport_.addEventListener("change",
+this.viewportChangeBoundToThis_);this.invalidate()},viewportChange_:function(){this.invalidate()},invalidate:function(){this.rafPending_||(webkitRequestAnimationFrame(function(){this.rafPending_=!1;if(this.viewport_){var a=window.getComputedStyle(this.canvasContainer_),b=parseInt(a.width),a=parseInt(a.height);this.canvas_.width!=b&&(this.canvas_.width=b);this.canvas_.height!=a&&(this.canvas_.height=a);this.redraw()}}.bind(this),this),this.rafPending_=!0)},get firstCanvas(){return this.canvas_}};a.prototype=
+{get:function(a,b,d,f,g){var h=e[d];h||(h={},e[d]=h);f=h[b];f||(h[b]={},f=h[b]);h=f[g];if(void 0===h){for(h=!1;a.labelWidthWorld(d,b)>g;)d=d.substring(0,0.75*d.length),h=!0;h&&3<d.length&&(d=d.substring(0,d.length-3)+"...");h=new c(d,a.labelWidth(d));f[g]=h}return h}};var g=cr.ui.define(j);g.prototype={__proto__:j.prototype,SHOULD_ELIDE_TEXT:!0,decorate:function(){this.classList.add("timeline-slice-track");this.elidedTitleCache=new a;this.asyncStyle_=!1},decorateHit:function(){},get asyncStyle(){return this.asyncStyle_},
+set asyncStyle(a){this.asyncStyle_=!!a;this.invalidate()},get slices(){return this.slices_},set slices(a){this.slices_=a;this.invalidate()},get height(){return window.getComputedStyle(this).height},set height(a){this.style.height=a;this.invalidate()},labelWidth:function(a){var c=b[a];c||(c=this.ctx_.measureText(a).width,b[a]=c);return c+2},labelWidthWorld:function(a,b){return this.labelWidth(a)*b},redraw:function(){var a=this.ctx_,b=this.canvas_.width,c=this.canvas_.height;a.clearRect(0,0,b,c);var e=
+this.viewport_,f=e.xViewVectorToWorld(1),g=e.xViewToWorld(0),b=e.xViewToWorld(b);if(e.gridEnabled){var i=e.gridTimebase;for(a.beginPath();i<b;){if(i>=g){var j=e.xWorldToView(i);a.moveTo(j,0);a.lineTo(j,c)}i+=e.gridStep}a.strokeStyle="rgba(255,0,0,0.25)";a.stroke()}a.save();e.applyTransformToCanavs(a);this.asyncStyle_&&(a.globalAlpha=0.25);var k=new tracing.FastRectRenderer(a,g,2*f,2*f,b,d);k.setYandH(0,c);g=this.slices_;for(b=0;b<g.length;++b){var j=g[b],i=j.start,l=Math.max(j.duration,0.001),o=j.selected?
+j.colorId+h:j.colorId;l<f&&(l=f);0<j.duration?k.fillRect(i,l,o):0.001<f?k.fillRect(i,f,o):(a.fillStyle=d[o],a.beginPath(),a.moveTo(i-4*f,c),a.lineTo(i,0),a.lineTo(i+4*f,c),a.closePath(),a.fill())}k.flush();a.restore();if(8<c){a.textAlign="center";a.textBaseline="top";a.font="10px sans-serif";a.strokeStyle="rgb(0,0,0)";a.fillStyle="rgb(0,0,0)";c=20*f;i=this.SHOULD_ELIDE_TEXT;for(b=0;b<g.length;++b)if(j=g[b],j.duration>c&&(k=j.title,j.didNotFinish&&(k+=" (Did Not Finish)"),l=this.labelWidth(k),i&&this.labelWidthWorld(k,
+f)>j.duration&&(l=this.elidedTitleCache.get(this,f,k,l,j.duration),k=l.string,l=l.width),l*f<j.duration))j=e.xWorldToView(j.start+0.5*j.duration),a.fillText(k,j,2.5,l)}},addIntersectingItemsToSelection:function(a,b,c){var d=this.getBoundingClientRect();if(b<d.top||b>=d.bottom)return!1;a=tracing.findLowIndexInSortedIntervals(this.slices_,function(a){return a.start},function(a){return a.duration},a);return 0<=a&&a<this.slices_.length?(this.decorateHit(c.addSlice(this,this.slices_[a])),!0):!1},addIntersectingItemsInRangeToSelection:function(a,
+b,c,d,e){function f(a){a=e.addSlice(h,a);h.decorateHit(a)}var g=this.getBoundingClientRect(),c=Math.max(c,g.top),d=Math.min(d,g.bottom);if(!(c>d)){var h=this;tracing.iterateOverIntersectingIntervals(this.slices_,function(a){return a.start},function(a){return a.duration},a,b,f)}},indexOfSlice_:function(a){for(var b=tracing.findLowIndexInSortedArray(this.slices_,function(a){return a.start},a.start);b<this.slices_.length&&a.start==this.slices_[b].start&&a.colorId!=this.slices_[b].colorId;)b++;return b<
+this.slices_.length?b:void 0},addItemNearToProvidedHitToSelection:function(a,b,c){if(!a.slice)return!1;a=this.indexOfSlice_(a.slice);if(void 0===a)return!1;b=a+b;if(0>b||b>=this.slices_.length)return!1;a=c.addSlice(this,this.slices_[b]);this.decorateHit(a);return!0},addAllObjectsMatchingFilterToSelection:function(a,b){for(var c=0;c<this.slices_.length;++c)a.matchSlice(this.slices_[c])&&this.decorateHit(b.addSlice(this,this.slices_[c]))}};var q=cr.ui.define(j),l=Math.log(10);q.prototype={__proto__:j.prototype,
+decorate:function(){this.classList.add("timeline-viewport-track");this.strings_secs_=[];this.strings_msecs_=[]},redraw:function(){var a=this.ctx_,b=this.canvas_.width,c=this.canvas_.height;a.clearRect(0,0,b,c);var d=this.viewport_;d.xViewVectorToWorld(1);for(var e=d.xViewToWorld(0),f=d.xViewToWorld(b),g=d.xViewVectorToWorld(150),h=Math.pow(10,Math.ceil(Math.log(g)/l)),i=[10,5,2,1],g=0;g<i.length;++g)if(!(150>d.xWorldVectorToView(h/i[g]))){majorMarkDistanceWorld=h/i[g-1];break}h=void 0;100>majorMarkDistanceWorld?
+(unit="ms",unitDivisor=1,h=this.strings_msecs_):(unit="s",unitDivisor=1E3,h=this.strings_secs_);i=d.xWorldVectorToView(majorMarkDistanceWorld/5);g=Math.floor(e/majorMarkDistanceWorld)*majorMarkDistanceWorld;e=Math.floor(0.25*c);a.fillStyle="rgb(0, 0, 0)";a.strokeStyle="rgb(0, 0, 0)";a.textAlign="left";a.textBaseline="top";a.font="9px sans-serif";for(var j=g;j<f;j+=majorMarkDistanceWorld){var k=Math.floor(d.xWorldToView(j)),g=Math.floor(1E5*(j/unitDivisor))/1E5;h[g]||(h[g]=g+" "+unit);a.fillText(h[g],
+k+2,0);a.beginPath();a.moveTo(k,0);a.lineTo(k,b);for(g=1;5>g;++g){var o=Math.floor(k+i*g);a.moveTo(o,c-e);a.lineTo(o,c)}a.stroke()}},addIntersectingItemsToSelection:function(){},addIntersectingItemsInRangeToSelection:function(){},addAllObjectsMatchingFilterToSelection:function(){}};var i=cr.ui.define(j);i.prototype={__proto__:j.prototype,decorate:function(){this.classList.add("timeline-counter-track");f(this,!1);this.selectedSamples_={}},decorateHit:function(){},get counter(){return this.counter_},
+set counter(a){this.counter_=a;this.invalidate()},get selectedSamples(){return this.selectedSamples_},redraw:function(){var a=this.counter_,b=this.ctx_,c=this.canvas_.width,e=this.canvas_.height;b.clearRect(0,0,c,e);var f=this.viewport_,g=f.xViewVectorToWorld(1),h=f.xViewToWorld(0),c=f.xViewToWorld(c),i=f.xViewVectorToWorld(1);b.save();f.applyTransformToCanavs(b);for(var f=a.numSeries,j=a.numSamples,k=tracing.findLowIndexInSortedArray(a.timestamps,function(){},h),l=e/a.maxTotal,o=a.numSeries-1;0<=
+o;o--){b.fillStyle=d[a.seriesColors[o]];b.beginPath();for(var q=k-1,w=0<=q?a.timestamps[q]-i:-1,x=e,A=!1;;){var s=q+1;if(s>=j){b.lineTo(w,x);b.lineTo(w+8*g,x);b.lineTo(w+8*g,e);break}var t=a.timestamps[s],q=a.totals[s*f+o],z=e-l*q;if(t>c){b.lineTo(t,x);b.lineTo(t,e);break}t-w<i?q=s:(A||(b.moveTo(h,e),A=!0),b.lineTo(t,x),b.lineTo(t,z),q=s,w=t,x=z)}b.closePath();b.fill()}b.fillStyle="rgba(255, 0, 0, 1)";for(s in this.selectedSamples_)if(this.selectedSamples_[s]){t=a.timestamps[s];for(o=a.numSeries-
+1;0<=o;o--)q=a.totals[s*f+o],z=e-l*q,b.fillRect(t-g,z-1,3*g,3)}b.restore()},addIntersectingItemsToSelection:function(a,b,c){var d=this.getBoundingClientRect();if(b<d.top||b>=d.bottom)return!1;b=this.counter_;if(a<this.counter_.timestamps[0])return!1;d=tracing.findLowIndexInSortedArray(b.timestamps,function(a){return a},a);if(0>d||d>=b.timestamps.length)return!1;0<d&&a>this.counter_.timestamps[d-1]&&d--;this.getBoundingClientRect();this.decorateHit(c.addCounterSample(this,this.counter,d));return!0},
+addIntersectingItemsInRangeToSelection:function(a,b,c,d,e){var f=this.getBoundingClientRect(),c=Math.max(c,f.top),d=Math.min(d,f.bottom);if(!(c>d)){d=this.counter_;c=tracing.findLowIndexInSortedArray(d.timestamps,function(a){return a},a);f=tracing.findLowIndexInSortedArray(d.timestamps,function(a){return a},b);0<c&&a>d.timestamps[c-1]&&c--;0<f&&b>d.timestamps[f-1]&&f--;for(a=c;a<=f;a++)a>=d.timestamps.length||this.decorateHit(e.addCounterSample(this,this.counter,a))}},addAllObjectsMatchingFilterToSelection:function(){}};
+return{TimelineCounterTrack:i,TimelineSliceTrack:g,TimelineThreadTrack:o,TimelineViewportTrack:q,TimelineCpuTrack:k}});cr.define("tracing",function(){function f(c,a,d,f,b,e){this.ctx_=c;this.vpLeft_=a;this.minRectSize_=d;this.maxMergeDist_=f;this.vpRight_=b;this.pallette_=e}f.prototype={y_:0,h_:0,merging_:!1,mergeStartX_:0,mergeCurRight_:0,setYandH:function(c,a){this.flush();this.y_=c;this.h_=a},fillRect:function(c,a,d){var f=c+a;f<this.vpLeft_||c>this.vpRight_||(a<this.minRectSize_?(f-this.mergeStartX_>this.maxMergeDist_&&this.flush(),this.merging_?(this.mergeCurRight_=f,this.mergedColorId=Math.max(this.mergedColorId,
+d)):(this.merging_=!0,this.mergeStartX_=c,this.mergeCurRight_=f,this.mergedColorId=d)):(this.merging_&&this.flush(),this.ctx_.fillStyle=this.pallette_[d],this.ctx_.fillRect(c,this.y_,a,this.h_)))},flush:function(){this.merging_&&(this.ctx_.fillStyle=this.pallette_[this.mergedColorId],this.ctx_.fillRect(this.mergeStartX_,this.y_,this.mergeCurRight_-this.mergeStartX_,this.h_),this.merging_=!1)}};return{FastRectRenderer:f}});cr.define("tracing",function(){var f=cr.ui.define(cr.ui.TabPanel);f.prototype={__proto__:cr.ui.TabPanel.prototype,traceEvents_:[],systemTraceEvents_:[],decorate:function(){cr.ui.TabPanel.prototype.decorate.apply(this);this.classList.add("profiling-view");this.recordBn_=document.createElement("button");this.recordBn_.className="record";this.recordBn_.textContent="Record";this.recordBn_.addEventListener("click",this.onRecord_.bind(this));this.saveBn_=document.createElement("button");this.saveBn_.textContent=
+"Save";this.saveBn_.addEventListener("click",this.onSave_.bind(this));this.loadBn_=document.createElement("button");this.loadBn_.textContent="Load";this.loadBn_.addEventListener("click",this.onLoad_.bind(this));if(cr.isChromeOS){this.systemTracingBn_=document.createElement("input");this.systemTracingBn_.type="checkbox";this.systemTracingBn_.checked=!0;var c=document.createElement("div");c.className="label";c.textContent="System events";c.appendChild(this.systemTracingBn_)}this.timelineView_=new tracing.TimelineView;
+this.timelineView_.leftControls.appendChild(this.recordBn_);this.timelineView_.leftControls.appendChild(this.saveBn_);this.timelineView_.leftControls.appendChild(this.loadBn_);cr.isChromeOS&&this.timelineView_.leftControls.appendChild(this.systemTracingBn_);this.appendChild(this.timelineView_);document.addEventListener("keypress",this.onKeypress_.bind(this));this.refresh_()},didSetTracingController_:function(c,a){if(a)throw"Can only set tracing controller once.";this.tracingController_.addEventListener("traceEnded",
+this.onRecordDone_.bind(this));this.tracingController_.addEventListener("loadTraceFileComplete",this.onLoadTraceFileComplete_.bind(this));this.tracingController_.addEventListener("saveTraceFileComplete",this.onSaveTraceFileComplete_.bind(this));this.tracingController_.addEventListener("loadTraceFileCanceled",this.onLoadTraceFileCanceled_.bind(this));this.tracingController_.addEventListener("saveTraceFileCanceled",this.onSaveTraceFileCanceled_.bind(this));this.refresh_()},refresh_:function(){if(this.tracingController_){var c=
+this.tracingController_.traceEvents,a=c&&c.length;this.saveBn_.disabled=!a;a&&(a=new tracing.TimelineModel,a.importEvents(c,!0,[this.tracingController_.systemTraceEvents]),this.timelineView_.model=a)}},onKeypress_:function(c){if(114==c.keyCode&&!this.tracingController_.isTracingEnabled)this.onRecord_()},get timelineView(){return this.timelineView_},onRecord_:function(){this.tracingController_.beginTracing(this.systemTracingBn_?this.systemTracingBn_.checked:!1)},onRecordDone_:function(){this.refresh_()},
+onSave_:function(){this.overlayEl_=new tracing.Overlay;this.overlayEl_.className="profiling-overlay";var c=document.createElement("div");c.className="label";c.textContent="Saving...";this.overlayEl_.appendChild(c);this.overlayEl_.visible=!0;this.tracingController_.beginSaveTraceFile()},onSaveTraceFileComplete_:function(){this.overlayEl_.visible=!1;this.overlayEl_=void 0},onSaveTraceFileCanceled_:function(){this.overlayEl_.visible=!1;this.overlayEl_=void 0},onLoad_:function(){this.overlayEl_=new tracing.Overlay;
+this.overlayEl_.className="profiling-overlay";var c=document.createElement("div");c.className="label";c.textContent="Loading...";this.overlayEl_.appendChild(c);this.overlayEl_.visible=!0;this.tracingController_.beginLoadTraceFile()},onLoadTraceFileComplete_:function(){this.overlayEl_.visible=!1;this.overlayEl_=void 0;this.refresh_()},onLoadTraceFileCanceled_:function(){this.overlayEl_.visible=!1;this.overlayEl_=void 0}};cr.defineProperty(f,"tracingController",cr.PropertyKind.JS,f.prototype.didSetTracingController_);
+return{ProfilingView:f}});cr.define("tracing",function(){function f(){this.model_=this.timeline_=void 0;this.filterText_="";this.filterHits_=new tracing.TimelineSelection;this.filterHitsDirty_=!0;this.currentHitIndex_=0}var c=cr.ui.define("div");c.prototype={__proto__:tracing.Overlay.prototype,decorate:function(){tracing.Overlay.prototype.decorate.call(this);this.className="timeline-find-control";this.hitCountEl_=document.createElement("div");this.hitCountEl_.className="hit-count-label";this.hitCountEl_.textContent="1 of 7";
+var a=document.createElement("div");a.className="timeline-button find-previous";a.textContent="\u2190";a.addEventListener("click",function(){this.controller.findPrevious();this.updateHitCountEl_()}.bind(this));var c=document.createElement("div");c.className="timeline-button find-next";c.textContent="\u2192";c.addEventListener("click",function(){this.controller.findNext();this.updateHitCountEl_()}.bind(this));this.filterEl_=document.createElement("input");this.filterEl_.type="input";this.filterEl_.addEventListener("input",
+function(){this.controller.filterText=this.filterEl_.value;this.updateHitCountEl_()}.bind(this));this.filterEl_.addEventListener("keydown",function(a){13==a.keyCode?c.click():27==a.keyCode&&(this.filterEl_.blur(),this.updateHitCountEl_())}.bind(this));this.filterEl_.addEventListener("blur",function(){this.updateHitCountEl_()}.bind(this));this.filterEl_.addEventListener("focus",function(){this.updateHitCountEl_()}.bind(this));this.appendChild(this.filterEl_);this.appendChild(a);this.appendChild(c);
+this.appendChild(this.hitCountEl_);this.updateHitCountEl_()},get controller(){return this.controller_},set controller(a){this.controller_=a;this.updateHitCountEl_()},focus:function(){this.filterEl_.selectionStart=0;this.filterEl_.selectionEnd=this.filterEl_.value.length;this.filterEl_.focus()},updateHitCountEl_:function(){if(!this.controller||document.activeElement!=this.filterEl_)this.hitCountEl_.textContent="";else{var a=this.controller.currentHitIndex,c=this.controller.filterHits.length;this.hitCountEl_.textContent=
+0==c?"0 of 0":a+1+" of "+c}}};f.prototype={__proto__:Object.prototype,get timeline(){return this.timeline_},set timeline(a){this.timeline_=a;this.filterHitsDirty_=!0},get filterText(){return this.filterText_},set filterText(a){a!=this.filterText_&&(this.filterText_=a,this.filterHitsDirty_=!0,this.findNext())},get filterHits(){if(this.filterHitsDirty_)if(this.filterHitsDirty_=!1,this.timeline_){var a=new tracing.TimelineFilter(this.filterText);this.filterHits_.clear();this.timeline.addAllObjectsMatchingFilterToSelection(a,
+this.filterHits_);this.currentHitIndex_=this.filterHits_.length-1}else this.filterHits_.clear(),this.currentHitIndex_=0;return this.filterHits_},get currentHitIndex(){return this.currentHitIndex_},find_:function(a){if(this.timeline){var c=this.filterHits.length;this.currentHitIndex_+=a;0>this.currentHitIndex_&&(this.currentHitIndex_=c-1);this.currentHitIndex_>=c&&(this.currentHitIndex_=0);0>this.currentHitIndex_||this.currentHitIndex_>=c?this.timeline.selection=new tracing.TimelineSelection:(a=0==
+this.currentHitIndex_,this.timeline.setSelectionAndMakeVisible(this.filterHits.subSelection(this.currentHitIndex_),a))}},findNext:function(){this.find_(1)},findPrevious:function(){this.find_(-1)}};var a=cr.ui.define("div");a.prototype={__proto__:HTMLDivElement.prototype,decorate:function(){this.classList.add("timeline-view");this.titleEl_=document.createElement("div");this.titleEl_.textContent="Tracing: ";this.controlDiv_=document.createElement("div");this.controlDiv_.className="control";this.leftControlsEl_=
+document.createElement("div");this.leftControlsEl_.className="controls";this.rightControlsEl_=document.createElement("div");this.rightControlsEl_.className="controls";var a=document.createElement("div");a.className="spacer";this.timelineContainer_=document.createElement("div");this.timelineContainer_.className="timeline-container";var h=document.createElement("div");h.className="analysis-container";this.analysisEl_=new tracing.TimelineAnalysisView;this.findCtl_=new c;this.findCtl_.controller=new f;
+this.rightControls.appendChild(this.findCtl_);this.controlDiv_.appendChild(this.titleEl_);this.controlDiv_.appendChild(this.leftControlsEl_);this.controlDiv_.appendChild(a);this.controlDiv_.appendChild(this.rightControlsEl_);this.appendChild(this.controlDiv_);this.appendChild(this.timelineContainer_);h.appendChild(this.analysisEl_);this.appendChild(h);this.rightControls.appendChild(this.createHelpButton_());this.onSelectionChangedBoundToThis_=this.onSelectionChanged_.bind(this);document.addEventListener("keypress",
+this.onKeypress_.bind(this),!0)},createHelpButton_:function(){function a(b){if(c.visible&&(27==b.keyCode||63==b.keyCode))b.preventDefault(),document.removeEventListener("keydown",a),c.visible=!1}var c=new tracing.Overlay;c.classList.add("timeline-view-help-overlay");var b=document.createElement("div");b.className="timeline-button timeline-view-help-button";b.textContent="?";var e=document.createElement("div");e.style.whiteSpace="pre";e.style.fontFamily="monospace";b.addEventListener("click",function(){c.visible=
+!0;e.textContent=this.timeline_.keyHelp;document.addEventListener("keydown",a,!0)}.bind(this));c.appendChild(e);return b},get leftControls(){return this.leftControlsEl_},get rightControls(){return this.rightControlsEl_},get title(){return this.titleEl_.textContent.substring(this.titleEl_.textContent.length-2)},set title(a){this.titleEl_.textContent=a+":"},set traceData(a){this.model=new tracing.TimelineModel(a)},get model(){return this.timelineModel_},set model(a){this.timelineModel_=a;this.timelineContainer_.textContent=
+"";void 0!==this.timelineModel_.minTimestamp?(this.timeline_&&(this.timeline_.viewportTrack.detach(),this.timeline_.detach()),this.timeline_=new tracing.Timeline,this.timeline_.model=this.timelineModel_,this.timeline_.focusElement=this.focusElement_?this.focusElement_:this.parentElement,this.insertBefore(this.timeline_.viewportTrack,this.timelineContainer_),this.timelineContainer_.appendChild(this.timeline_),this.timeline_.addEventListener("selectionChange",this.onSelectionChangedBoundToThis_),this.findCtl_.controller.timeline=
+this.timeline_,this.onSelectionChanged_()):(this.timeline_=void 0,this.findCtl_.controller.timeline=void 0)},get timeline(){return this.timeline_},set focusElement(a){this.focusElement_=a;this.timeline_&&(this.timeline_.focusElement=a)},get focusElement(){return this.focusElement_?this.focusElement_:this.parentElement},get isAttachedToDocument_(){for(var a=this;a.parentNode;)a=a.parentNode;return a==this.ownerDocument},get listenToKeys_(){if(this.isAttachedToDocument_)return!this.focusElement_?!0:
+0<=this.focusElement.tabIndex?document.activeElement==this.focusElement:!0},onKeypress_:function(a){this.listenToKeys_&&(47==event.keyCode?(this.findCtl_.focus(),event.preventDefault()):63==a.keyCode&&(this.querySelector(".timeline-view-help-button").click(),a.preventDefault()))},beginFind:function(){if(!this.findInProgress_){this.findInProgress_=!0;var a=c();a.controller=new f;a.controller.timeline=this.timeline;a.visible=!0;a.addEventListener("close",function(){this.findInProgress_=!1}.bind(this));
+a.addEventListener("findNext",function(){});a.addEventListener("findPrevious",function(){})}},onSelectionChanged_:function(){var a=this.timelineContainer_.scrollTop;this.analysisEl_.selection=this.timeline_.selection;this.timelineContainer_.scrollTop=a}};return{TimelineFindControl:c,TimelineFindController:f,TimelineView:a}});