Home | History | Annotate | Download | only in dom-perf
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 // Template Test
     32 // This test uses a simple JS templating system for injecting JSON data into
     33 // an HTML page.  It is designed to use the kind of DOM manipulation common
     34 // in templating systems.
     35 //
     36 // Code from http://code.google.com/p/google-jstemplate/source/browse/trunk/jstemplate.js
     37 
     38 // This is the HTML code to use in the template test
     39 var content ="\
     40 <style type=\"text/css\"> \
     41 body {\
     42   border-top: 10px solid #3B85E3;\
     43   color: #333;\
     44   font-family: Verdana,Arial,Helvetica,sans-serif;\
     45 }\
     46 body, td {\
     47   font-size: 11px;\
     48 }\
     49 a:link, a:visited {\
     50   color: #2C3EBA;\
     51   text-decoration: none;\
     52 }\
     53 a:hover {\
     54   color: red;\
     55   text-decoration: underline;\
     56 }\
     57 h1 {\
     58   border-left: 10px solid #FFF;\
     59   font-size: 16px;\
     60   font-weight: bold;\
     61   margin: 0;\
     62   padding: 0.2em;\
     63   color: #3B85E3;\
     64 }\
     65 h2 {\
     66   border-left: 10px solid #FFF;\
     67   font-size: 11px;\
     68   font-weight: normal;\
     69   margin: 0;\
     70   padding: 0 6em 0.2em 0.2em;\
     71 }\
     72 .details {\
     73   margin: 0.4em 1.9em 0 1.2em;\
     74   padding: 0 0.4em 0.3em 0;\
     75   white-space: nowrap;\
     76 }\
     77 .details .outer {\
     78   padding-right: 0;\
     79   vertical-align: top;\
     80 }\
     81 .details .top {\
     82   border-top: 2px solid #333;\
     83   font-weight: bold;\
     84   margin-top: 0.4em;\
     85 }\
     86 .details .header2 {\
     87   font-weight: bold;\
     88   padding-left: 0.9em;\
     89 }\
     90 .details .key {\
     91   padding-left: 1.1em;\
     92   vertical-align: top;\
     93 }\
     94 .details .value {\
     95   text-align: right;\
     96   color: #333;\
     97   font-weight: bold;\
     98 }\
     99 .details .zebra {\
    100   background: #EEE;\
    101 }\
    102 .lower {\
    103   text-transform: lowercase;\
    104 }\
    105 </style> \
    106   <h1 class=\"lower\">About Stats</h1> \
    107   <table class=\"details\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \
    108     <tbody> \
    109       <tr> \
    110         <td class=\"outer\"> \
    111           <table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \
    112             <tbody> \
    113               <tr> \
    114                 <td class=\"top\" width=\"100\">Counters</td> \
    115                 <td class=\"top value\" colspan=2></td> \
    116               </tr> \
    117               <tr> \
    118                 <td class=\"header2 lower\" width=\"200\">name</td> \
    119                 <td class=\"header2 lower\">value</td> \
    120                 <td class=\"header2 lower\">delta</td> \
    121               </tr> \
    122               <tr jsselect=\"counters\" name=\"counter\"> \
    123                 <td class=\"key\" width=\"200\" jscontent=\"name\"></td> \
    124                 <td class=\"value\" jscontent=\"value\"></td> \
    125                 <td class=\"value\" jscontent=\"delta\"></td> \
    126               </tr> \
    127             </tbody> \
    128           </table> \
    129         </td> \
    130         <td width=\"15\"/> \
    131         <td class=\"outer\"> \
    132           <table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"> \
    133             <tbody> \
    134               <tr> \
    135                 <td class=\"top\" width=\"100\">Timers</td> \
    136                 <td class=\"top value\"></td> \
    137                 <td class=\"top value\" colspan=3></td> \
    138               </tr> \
    139               <tr> \
    140                 <td class=\"header2 lower\" width=\"200\">name</td> \
    141                 <td class=\"header2 lower\">count</td> \
    142                 <td class=\"header2 lower\">time (ms)</td> \
    143                 <td class=\"header2 lower\">avg time (ms)</td> \
    144               </tr> \
    145               <tr jsselect=\"timers\" name=\"timer\"> \
    146                 <td class=\"key\" width=\"200\" jscontent=\"name\"></td> \
    147                 <td class=\"value\" jscontent=\"value\"></td> \
    148                 <td class=\"value\" jscontent=\"time\"></td> \
    149                 <td class=\"value\"></td> \
    150               </tr> \
    151             </tbody> \
    152           </table> \
    153         </td> \
    154       </tr> \
    155     </tbody> \
    156   </table><br/> \
    157 </body> \
    158 </html> \
    159 ";
    160 
    161 // Generic Template Library
    162 
    163 /**
    164  * @fileoverview This file contains miscellaneous basic functionality.
    165  *
    166  */
    167 
    168 /**
    169  * Returns the document owner of the given element. In particular,
    170  * returns window.document if node is null or the browser does not
    171  * support ownerDocument.
    172  *
    173  * @param {Node} node  The node whose ownerDocument is required.
    174  * @returns {Document|Null}  The owner document or null if unsupported.
    175  */
    176 function Template_ownerDocument(node) {
    177     return (node ? node.ownerDocument : null) || document;
    178 }
    179 
    180 /**
    181  * Wrapper function to create CSS units (pixels) string
    182  *
    183  * @param {Number} numPixels  Number of pixels, may be floating point.
    184  * @returns {String}  Corresponding CSS units string.
    185  */
    186 function Template_px(numPixels) {
    187     return round(numPixels) + "px";
    188 }
    189 
    190 /**
    191  * Sets display to none. Doing this as a function saves a few bytes for
    192  * the 'style.display' property and the 'none' literal.
    193  *
    194  * @param {Element} node  The dom element to manipulate.
    195  */
    196 function Template_displayNone(node) {
    197     node.style.display = 'none';
    198 }
    199 
    200 /**
    201  * Sets display to default.
    202  *
    203  * @param {Element} node  The dom element to manipulate.
    204  */
    205 function Template_displayDefault(node) {
    206     node.style.display = '';
    207 }
    208 
    209 var DOM_ELEMENT_NODE = 1;
    210 var DOM_ATTRIBUTE_NODE = 2;
    211 var DOM_TEXT_NODE = 3;
    212 var DOM_CDATA_SECTION_NODE = 4;
    213 var DOM_ENTITY_REFERENCE_NODE = 5;
    214 var DOM_ENTITY_NODE = 6;
    215 var DOM_PROCESSING_INSTRUCTION_NODE = 7;
    216 var DOM_COMMENT_NODE = 8;
    217 var DOM_DOCUMENT_NODE = 9;
    218 var DOM_DOCUMENT_TYPE_NODE = 10;
    219 var DOM_DOCUMENT_FRAGMENT_NODE = 11;
    220 var DOM_NOTATION_NODE = 12;
    221 
    222 /**
    223  * Get an attribute from the DOM.  Simple redirect, exists to compress code.
    224  *
    225  * @param {Element} node  Element to interrogate.
    226  * @param {String} name  Name of parameter to extract.
    227  * @return {String}  Resulting attribute.
    228  */
    229 function Template_domGetAttribute(node, name) {
    230     return node.getAttribute(name);
    231 }
    232 
    233 /**
    234  * Set an attribute in the DOM.  Simple redirect to compress code.
    235  *
    236  * @param {Element} node  Element to interrogate.
    237  * @param {String} name  Name of parameter to set.
    238  * @param {String} value  Set attribute to this value.
    239  */
    240 function Template_domSetAttribute(node, name, value) {
    241     node.setAttribute(name, value);
    242 }
    243 
    244 /**
    245  * Remove an attribute from the DOM.  Simple redirect to compress code.
    246  *
    247  * @param {Element} node  Element to interrogate.
    248  * @param {String} name  Name of parameter to remove.
    249  */
    250 function Template_domRemoveAttribute(node, name) {
    251     node.removeAttribute(name);
    252 }
    253 
    254 /**
    255  * Clone a node in the DOM.
    256  *
    257  * @param {Node} node  Node to clone.
    258  * @return {Node}  Cloned node.
    259  */
    260 function Template_domCloneNode(node) {
    261     return node.cloneNode(true);
    262 }
    263 
    264 /**
    265  * Return a safe string for the className of a node.
    266  * If className is not a string, returns "".
    267  *
    268  * @param {Element} node  DOM element to query.
    269  * @return {String}
    270  */
    271 function Template_domClassName(node) {
    272     return node.className ? "" + node.className : "";
    273 }
    274 
    275 /**
    276  * Inserts a new child before a given sibling.
    277  *
    278  * @param {Node} newChild  Node to insert.
    279  * @param {Node} oldChild  Sibling node.
    280  * @return {Node}  Reference to new child.
    281  */
    282 function Template_domInsertBefore(newChild, oldChild) {
    283     return oldChild.parentNode.insertBefore(newChild, oldChild);
    284 }
    285 
    286 /**
    287  * Appends a new child to the specified (parent) node.
    288  *
    289  * @param {Element} node  Parent element.
    290  * @param {Node} child  Child node to append.
    291  * @return {Node}  Newly appended node.
    292  */
    293 function Template_domAppendChild(node, child) {
    294     return node.appendChild(child);
    295 }
    296 
    297 /**
    298  * Remove a new child from the specified (parent) node.
    299  *
    300  * @param {Element} node  Parent element.
    301  * @param {Node} child  Child node to remove.
    302  * @return {Node}  Removed node.
    303  */
    304 function Template_domRemoveChild(node, child) {
    305     return node.removeChild(child);
    306 }
    307 
    308 /**
    309  * Replaces an old child node with a new child node.
    310  *
    311  * @param {Node} newChild  New child to append.
    312  * @param {Node} oldChild  Old child to remove.
    313  * @return {Node}  Replaced node.
    314  */
    315 function Template_domReplaceChild(newChild, oldChild) {
    316     return oldChild.parentNode.replaceChild(newChild, oldChild);
    317 }
    318 
    319 /**
    320  * Removes a node from the DOM.
    321  *
    322  * @param {Node} node  The node to remove.
    323  * @return {Node}  The removed node.
    324  */
    325 function Template_domRemoveNode(node) {
    326     return Template_domRemoveChild(node.parentNode, node);
    327 }
    328 
    329 /**
    330  * Creates a new text node in the given document.
    331  *
    332  * @param {Document} doc  Target document.
    333  * @param {String} text  Text composing new text node.
    334  * @return {Text}  Newly constructed text node.
    335  */
    336 function Template_domCreateTextNode(doc, text) {
    337     return doc.createTextNode(text);
    338 }
    339 
    340 /**
    341  * Redirect to document.getElementById
    342  *
    343  * @param {Document} doc  Target document.
    344  * @param {String} id  Id of requested node.
    345  * @return {Element|Null}  Resulting element.
    346  */
    347 function Template_domGetElementById(doc, id) {
    348     return doc.getElementById(id);
    349 }
    350 
    351 /**
    352  * @fileoverview This file contains javascript utility functions that
    353  * do not depend on anything defined elsewhere.
    354  *
    355  */
    356 
    357 /**
    358  * Returns the value of the length property of the given object. Used
    359  * to reduce compiled code size.
    360  *
    361  * @param {Array | String} a  The string or array to interrogate.
    362  * @return {Number}  The value of the length property.
    363  */
    364 function Template_jsLength(a) {
    365     return a.length;
    366 }
    367 
    368 var min = Math.min;
    369 var max = Math.max;
    370 var ceil = Math.ceil;
    371 var floor = Math.floor;
    372 var round = Math.round;
    373 var abs = Math.abs;
    374 
    375 /**
    376  * Copies all properties from second object to the first.  Modifies to.
    377  *
    378  * @param {Object} to  The target object.
    379  * @param {Object} from  The source object.
    380  */
    381 function Template_copyProperties(to, from) {
    382     foreachin(from, function(p) { to[p] = from[p]; });
    383 }
    384 
    385 /**
    386  * Iterates over the array, calling the given function for each
    387  * element.
    388  *
    389  * @param {Array} array
    390  * @param {Function} fn
    391  */
    392 function foreach(array, fn) {
    393     var I = Template_jsLength(array);
    394     for (var i = 0; i < I; ++i)
    395         fn(array[i], i);
    396 }
    397 
    398 /**
    399  * Safely iterates over all properties of the given object, calling
    400  * the given function for each property. If opt_all isn't true, uses
    401  * hasOwnProperty() to assure the property is on the object, not on
    402  * its prototype.
    403  *
    404  * @param {Object} object
    405  * @param {Function} fn
    406  * @param {Boolean} opt_all  If true, also iterates over inherited properties.
    407  */
    408 function foreachin(object, fn, opt_all) {
    409     for (var i in object) {
    410         if (opt_all || !object.hasOwnProperty || object.hasOwnProperty(i))
    411             fn(i, object[i]);
    412     }
    413 }
    414 
    415 /**
    416  * Trim whitespace from begin and end of string.
    417  *
    418  * @see testStringTrim();
    419  *
    420  * @param {String} str  Input string.
    421  * @return {String}  Trimmed string.
    422  */
    423 function Template_stringTrim(str) {
    424     return Template_stringTrimRight(stringTrimLeft(str));
    425 }
    426 
    427 /**
    428  * Trim whitespace from beginning of string.
    429  *
    430  * @see testStringTrimLeft();
    431  *
    432  * @param {String} str  Input string.
    433  * @return {String}  Trimmed string.
    434  */
    435 function Template_stringTrimLeft(str) {
    436     return str.replace(/^\s+/, "");
    437 }
    438 
    439 /**
    440  * Trim whitespace from end of string.
    441  *
    442  * @see testStringTrimRight();
    443  *
    444  * @param {String} str  Input string.
    445  * @return {String}  Trimmed string.
    446  */
    447 function Template_stringTrimRight(str) {
    448     return str.replace(/\s+$/, "");
    449 }
    450 
    451 /**
    452  * Jscompiler wrapper for parseInt() with base 10.
    453  *
    454  * @param {String} s String repersentation of a number.
    455  *
    456  * @return {Number} The integer contained in s, converted on base 10.
    457  */
    458 function Template_parseInt10(s) {
    459     return parseInt(s, 10);
    460 }
    461 /**
    462  * @fileoverview A simple formatter to project JavaScript data into
    463  * HTML templates. The template is edited in place. I.e. in order to
    464  * instantiate a template, clone it from the DOM first, and then
    465  * process the cloned template. This allows for updating of templates:
    466  * If the templates is processed again, changed values are merely
    467  * updated.
    468  *
    469  * NOTE: IE DOM doesn't have importNode().
    470  *
    471  * NOTE: The property name "length" must not be used in input
    472  * data, see comment in jstSelect_().
    473  */
    474 
    475 /**
    476  * Names of jstemplate attributes. These attributes are attached to
    477  * normal HTML elements and bind expression context data to the HTML
    478  * fragment that is used as template.
    479  */
    480 var ATT_select = 'jsselect';
    481 var ATT_instance = 'jsinstance';
    482 var ATT_display = 'jsdisplay';
    483 var ATT_values = 'jsvalues';
    484 var ATT_eval = 'jseval';
    485 var ATT_transclude = 'transclude';
    486 var ATT_content = 'jscontent';
    487 
    488 /**
    489  * Names of special variables defined by the jstemplate evaluation
    490  * context. These can be used in js expression in jstemplate
    491  * attributes.
    492  */
    493 var VAR_index = '$index';
    494 var VAR_this = '$this';
    495 
    496 /**
    497  * Context for processing a jstemplate. The context contains a context
    498  * object, whose properties can be referred to in jstemplate
    499  * expressions, and it holds the locally defined variables.
    500  *
    501  * @param {Object} opt_data The context object. Null if no context.
    502  *
    503  * @param {Object} opt_parent The parent context, from which local
    504  * variables are inherited. Normally the context object of the parent
    505  * context is the object whose property the parent object is. Null for the
    506  * context of the root object.
    507  *
    508  * @constructor
    509  */
    510 function JsExprContext(opt_data, opt_parent) {
    511     var me = this;
    512 
    513     /**
    514      * The local context of the input data in which the jstemplate
    515      * expressions are evaluated. Notice that this is usually an Object,
    516      * but it can also be a scalar value (and then still the expression
    517      * $this can be used to refer to it). Notice this can be a scalar
    518      * value, including undefined.
    519      *
    520      * @type {Object}
    521      */
    522     me.data_ = opt_data;
    523 
    524     /**
    525      * The context for variable definitions in which the jstemplate
    526      * expressions are evaluated. Other than for the local context,
    527      * which replaces the parent context, variable definitions of the
    528      * parent are inherited. The special variable $this points to data_.
    529      *
    530      * @type {Object}
    531      */
    532     me.vars_ = {};
    533     if (opt_parent)
    534         Template_copyProperties(me.vars_, opt_parent.vars_);
    535     this.vars_[VAR_this] = me.data_;
    536 }
    537 
    538 /**
    539  * Evaluates the given expression in the context of the current
    540  * context object and the current local variables.
    541  *
    542  * @param {String} expr A javascript expression.
    543  *
    544  * @param {Element} template DOM node of the template.
    545  *
    546  * @return The value of that expression.
    547  */
    548 JsExprContext.prototype.jseval = function(expr, template) {
    549     with (this.vars_) {
    550         with (this.data_) {
    551             try {
    552                 return (function() { return eval('[' + expr + '][0]'); }).call(template);
    553             } catch (e) {
    554                 return null;
    555             }
    556         }
    557     }
    558 };
    559 
    560 /**
    561  * Clones the current context for a new context object. The cloned
    562  * context has the data object as its context object and the current
    563  * context as its parent context. It also sets the $index variable to
    564  * the given value. This value usually is the position of the data
    565  * object in a list for which a template is instantiated multiply.
    566  *
    567  * @param {Object} data The new context object.
    568  *
    569  * @param {Number} index Position of the new context when multiply
    570  * instantiated. (See implementation of jstSelect().)
    571  *
    572  * @return {JsExprContext}
    573  */
    574 JsExprContext.prototype.clone = function(data, index) {
    575     var ret = new JsExprContext(data, this);
    576     ret.setVariable(VAR_index, index);
    577     if (this.resolver_)
    578         ret.setSubTemplateResolver(this.resolver_);
    579     return ret;
    580 };
    581 
    582 /**
    583  * Binds a local variable to the given value. If set from jstemplate
    584  * jsvalue expressions, variable names must start with $, but in the
    585  * API they only have to be valid javascript identifier.
    586  *
    587  * @param {String} name
    588  *
    589  * @param {Object} value
    590  */
    591 JsExprContext.prototype.setVariable = function(name, value) {
    592     this.vars_[name] = value;
    593 };
    594 
    595 /**
    596  * Sets the function used to resolve the values of the transclude
    597  * attribute into DOM nodes. By default, this is jstGetTemplate(). The
    598  * value set here is inherited by clones of this context.
    599  *
    600  * @param {Function} resolver The function used to resolve transclude
    601  * ids into a DOM node of a subtemplate. The DOM node returned by this
    602  * function will be inserted into the template instance being
    603  * processed. Thus, the resolver function must instantiate the
    604  * subtemplate as necessary.
    605  */
    606 JsExprContext.prototype.setSubTemplateResolver = function(resolver) {
    607     this.resolver_ = resolver;
    608 };
    609 
    610 /**
    611  * Resolves a sub template from an id. Used to process the transclude
    612  * attribute. If a resolver function was set using
    613  * setSubTemplateResolver(), it will be used, otherwise
    614  * jstGetTemplate().
    615  *
    616  * @param {String} id The id of the sub template.
    617  *
    618  * @return {Node} The root DOM node of the sub template, for direct
    619  * insertion into the currently processed template instance.
    620  */
    621 JsExprContext.prototype.getSubTemplate = function(id) {
    622     return (this.resolver_ || jstGetTemplate).call(this, id);
    623 };
    624 
    625 /**
    626  * HTML template processor. Data values are bound to HTML templates
    627  * using the attributes transclude, jsselect, jsdisplay, jscontent,
    628  * jsvalues. The template is modifed in place. The values of those
    629  * attributes are JavaScript expressions that are evaluated in the
    630  * context of the data object fragment.
    631  *
    632  * @param {JsExprContext} context Context created from the input data
    633  * object.
    634  *
    635  * @param {Element} template DOM node of the template. This will be
    636  * processed in place. After processing, it will still be a valid
    637  * template that, if processed again with the same data, will remain
    638  * unchanged.
    639  */
    640 function jstProcess(context, template) {
    641     var processor = new JstProcessor();
    642     processor.run_([ processor, processor.jstProcess_, context, template ]);
    643 }
    644 
    645 /**
    646  * Internal class used by jstemplates to maintain context.
    647  * NOTE: This is necessary to process deep templates in Safari
    648  * which has a relatively shallow stack.
    649  * @class
    650  */
    651 function JstProcessor() {
    652 }
    653 
    654 /**
    655  * Runs the state machine, beginning with function "start".
    656  *
    657  * @param {Array} start The first function to run, in the form
    658  * [object, method, args ...]
    659  */
    660 JstProcessor.prototype.run_ = function(start) {
    661     var me = this;
    662 
    663     me.queue_ = [ start ];
    664     while (Template_jsLength(me.queue_)) {
    665         var f = me.queue_.shift();
    666         f[1].apply(f[0], f.slice(2));
    667     }
    668 };
    669 
    670 /**
    671  * Appends a function to be called later.
    672  * Analogous to calling that function on a subsequent line, or a subsequent
    673  * iteration of a loop.
    674  *
    675  * @param {Array} f  A function in the form [object, method, args ...]
    676  */
    677 JstProcessor.prototype.enqueue_ = function(f) {
    678     this.queue_.push(f);
    679 };
    680 
    681 /**
    682  * Implements internals of jstProcess.
    683  *
    684  * @param {JsExprContext} context
    685  *
    686  * @param {Element} template
    687  */
    688 JstProcessor.prototype.jstProcess_ = function(context, template) {
    689     var me = this;
    690 
    691     var transclude = Template_domGetAttribute(template, ATT_transclude);
    692     if (transclude) {
    693         var tr = context.getSubTemplate(transclude);
    694         if (tr) {
    695             Template_domReplaceChild(tr, template);
    696             me.enqueue_([ me, me.jstProcess_, context, tr ]);
    697         } else
    698             Template_domRemoveNode(template);
    699         return;
    700     }
    701 
    702     var select = Template_domGetAttribute(template, ATT_select);
    703     if (select) {
    704         me.jstSelect_(context, template, select);
    705         return;
    706     }
    707 
    708     var display = Template_domGetAttribute(template, ATT_display);
    709     if (display) {
    710         if (!context.jseval(display, template)) {
    711             Template_displayNone(template);
    712             return;
    713         }
    714         Template_displayDefault(template);
    715     }
    716 
    717     var values = Template_domGetAttribute(template, ATT_values);
    718     if (values)
    719         me.jstValues_(context, template, values);
    720 
    721     var expressions = Template_domGetAttribute(template, ATT_eval);
    722     if (expressions) {
    723         foreach(expressions.split(/\s*;\s*/), function(expression) {
    724             expression = Template_stringTrim(expression);
    725             if (Template_jsLength(expression))
    726                 context.jseval(expression, template);
    727         });
    728     }
    729 
    730     var content = Template_domGetAttribute(template, ATT_content);
    731     if (content)
    732         me.jstContent_(context, template, content);
    733     else {
    734         var childnodes = [];
    735         for (var i = 0; i < Template_jsLength(template.childNodes); ++i) {
    736             if (template.childNodes[i].nodeType == DOM_ELEMENT_NODE)
    737                 me.enqueue_([me, me.jstProcess_, context, template.childNodes[i]]);
    738         }
    739     }
    740 };
    741 
    742 /**
    743  * Implements the jsselect attribute: evalutes the value of the
    744  * jsselect attribute in the current context, with the current
    745  * variable bindings (see JsExprContext.jseval()). If the value is an
    746  * array, the current template node is multiplied once for every
    747  * element in the array, with the array element being the context
    748  * object. If the array is empty, or the value is undefined, then the
    749  * current template node is dropped. If the value is not an array,
    750  * then it is just made the context object.
    751  *
    752  * @param {JsExprContext} context The current evaluation context.
    753  *
    754  * @param {Element} template The currently processed node of the template.
    755  *
    756  * @param {String} select The javascript expression to evaluate.
    757  *
    758  * @param {Function} process The function to continue processing with.
    759  */
    760 JstProcessor.prototype.jstSelect_ = function(context, template, select) {
    761     var me = this;
    762 
    763     var value = context.jseval(select, template);
    764     Template_domRemoveAttribute(template, ATT_select);
    765 
    766     var instance = Template_domGetAttribute(template, ATT_instance);
    767     var instance_last = false;
    768     if (instance) {
    769         if (instance.charAt(0) == '*') {
    770             instance = Template_parseInt10(instance.substr(1));
    771             instance_last = true;
    772         } else
    773             instance = Template_parseInt10(instance);
    774     }
    775 
    776     var multiple = (value !== null && typeof value == 'object' && typeof value.length == 'number');
    777     var multiple_empty = (multiple && value.length == 0);
    778 
    779     if (multiple) {
    780         if (multiple_empty) {
    781             if (!instance) {
    782                 Template_domSetAttribute(template, ATT_select, select);
    783                 Template_domSetAttribute(template, ATT_instance, '*0');
    784                 Template_displayNone(template);
    785             } else
    786                 Template_domRemoveNode(template);
    787         } else {
    788             Template_displayDefault(template);
    789             if (instance === null || instance === "" || instance === undefined ||
    790                 (instance_last && instance < Template_jsLength(value) - 1)) {
    791                 var templatenodes = [];
    792                 var instances_start = instance || 0;
    793                 for (var i = instances_start + 1; i < Template_jsLength(value); ++i) {
    794                     var node = Template_domCloneNode(template);
    795                     templatenodes.push(node);
    796                     Template_domInsertBefore(node, template);
    797                 }
    798                 templatenodes.push(template);
    799 
    800                 for (var i = 0; i < Template_jsLength(templatenodes); ++i) {
    801                     var ii = i + instances_start;
    802                     var v = value[ii];
    803                     var t = templatenodes[i];
    804 
    805                     me.enqueue_([me, me.jstProcess_, context.clone(v, ii), t]);
    806                     var instanceStr = (ii == Template_jsLength(value) - 1 ? '*' : '') + ii;
    807                     me.enqueue_([null, postProcessMultiple_, t, select, instanceStr]);
    808                 }
    809             } else if (instance < Template_jsLength(value)) {
    810                 var v = value[instance];
    811 
    812                 me.enqueue_([me, me.jstProcess_, context.clone(v, instance), template]);
    813                 var instanceStr = (instance == Template_jsLength(value) - 1 ? '*' : '') + instance;
    814                 me.enqueue_([null, postProcessMultiple_, template, select, instanceStr]);
    815             } else
    816                 Template_domRemoveNode(template);
    817         }
    818     } else {
    819         if (value == null) {
    820             Template_domSetAttribute(template, ATT_select, select);
    821             Template_displayNone(template);
    822         } else {
    823             me.enqueue_([me, me.jstProcess_, context.clone(value, 0), template]);
    824             me.enqueue_([null, postProcessSingle_, template, select]);
    825         }
    826     }
    827 };
    828 
    829 /**
    830  * Sets ATT_select and ATT_instance following recursion to jstProcess.
    831  *
    832  * @param {Element} template  The template
    833  *
    834  * @param {String} select  The jsselect string
    835  *
    836  * @param {String} instanceStr  The new value for the jsinstance attribute
    837  */
    838 function postProcessMultiple_(template, select, instanceStr) {
    839     Template_domSetAttribute(template, ATT_select, select);
    840     Template_domSetAttribute(template, ATT_instance, instanceStr);
    841 }
    842 
    843 /**
    844  * Sets ATT_select and makes the element visible following recursion to
    845  * jstProcess.
    846  *
    847  * @param {Element} template  The template
    848  *
    849  * @param {String} select  The jsselect string
    850  */
    851 function postProcessSingle_(template, select) {
    852     Template_domSetAttribute(template, ATT_select, select);
    853     Template_displayDefault(template);
    854 }
    855 
    856 /**
    857  * Implements the jsvalues attribute: evaluates each of the values and
    858  * assigns them to variables in the current context (if the name
    859  * starts with '$', javascript properties of the current template node
    860  * (if the name starts with '.'), or DOM attributes of the current
    861  * template node (otherwise). Since DOM attribute values are always
    862  * strings, the value is coerced to string in the latter case,
    863  * otherwise it's the uncoerced javascript value.
    864  *
    865  * @param {JsExprContext} context Current evaluation context.
    866  *
    867  * @param {Element} template Currently processed template node.
    868  *
    869  * @param {String} valuesStr Value of the jsvalues attribute to be
    870  * processed.
    871  */
    872 JstProcessor.prototype.jstValues_ = function(context, template, valuesStr) {
    873     var values = valuesStr.split(/\s*;\s*/);
    874     for (var i = 0; i < Template_jsLength(values); ++i) {
    875         var colon = values[i].indexOf(':');
    876         if (colon < 0)
    877             continue;
    878         var label = Template_stringTrim(values[i].substr(0, colon));
    879         var value = context.jseval(values[i].substr(colon + 1), template);
    880 
    881         if (label.charAt(0) == '$')
    882             context.setVariable(label, value);
    883         else if (label.charAt(0) == '.')
    884             template[label.substr(1)] = value;
    885         else if (label) {
    886             if (typeof value == 'boolean') {
    887                 if (value)
    888                     Template_domSetAttribute(template, label, label);
    889                 else
    890                     Template_domRemoveAttribute(template, label);
    891             } else
    892                 Template_domSetAttribute(template, label, '' + value);
    893         }
    894     }
    895 };
    896 
    897 /**
    898  * Implements the jscontent attribute. Evalutes the expression in
    899  * jscontent in the current context and with the current variables,
    900  * and assigns its string value to the content of the current template
    901  * node.
    902  *
    903  * @param {JsExprContext} context Current evaluation context.
    904  *
    905  * @param {Element} template Currently processed template node.
    906  *
    907  * @param {String} content Value of the jscontent attribute to be
    908  * processed.
    909  */
    910 JstProcessor.prototype.jstContent_ = function(context, template, content) {
    911     var value = '' + context.jseval(content, template);
    912     if (template.innerHTML == value)
    913         return;
    914     while (template.firstChild)
    915         Template_domRemoveNode(template.firstChild);
    916     var t = Template_domCreateTextNode(Template_ownerDocument(template), value);
    917     Template_domAppendChild(template, t);
    918 };
    919 
    920 /**
    921  * Helps to implement the transclude attribute, and is the initial
    922  * call to get hold of a template from its ID.
    923  *
    924  * @param {String} name The ID of the HTML element used as template.
    925  *
    926  * @returns {Element} The DOM node of the template. (Only element
    927  * nodes can be found by ID, hence it's a Element.)
    928  */
    929 function jstGetTemplate(name) {
    930     var section = Template_domGetElementById(document, name);
    931     if (section) {
    932         var ret = Template_domCloneNode(section);
    933         Template_domRemoveAttribute(ret, 'id');
    934         return ret;
    935     } else
    936         return null;
    937 }
    938 
    939 window['jstGetTemplate'] = jstGetTemplate;
    940 window['jstProcess'] = jstProcess;
    941 window['JsExprContext'] = JsExprContext;
    942 
    943 function TemplateTest() {
    944     // Find the location to insert the content
    945     var tp = document.getElementById('benchmark_content');
    946 
    947     // Inject the content
    948     tp.innerHTML = content;
    949 
    950     // Run the template
    951     var cx = new JsExprContext(
    952     {"counters": [
    953         {"name":"Chrome:Init","time":5},
    954         {"delta":0,"name":"Shutdown:window_close:time","time":111,"value":1},
    955         {"delta":0,"name":" Shutdown:window_close:timeMA","value":111},
    956         {"delta":0,"name":"Shutdown:window_close:time_pe","time":111,"value":1},
    957         {"delta":0,"name":" Shutdown:window_close:time_p","value":111},
    958         {"delta":0,"name":"Shutdown:renderers:total","time":1,"value":1},
    959         {"delta":0,"name":" Shutdown:renderers:totalMAX","value":1},
    960         {"delta":0,"name":"Shutdown:renderers:slow","time":0,"value":1},
    961         {"delta":0,"name":" Shutdown:renderers:slowMAX","value":10},
    962         {"delta":0,"name":"DNS:PrefetchQueue","time":2,"value":6},
    963         {"delta":0,"name":" DNS:PrefetchQueueMAX","value":1},
    964         {"delta":0,"name":"DNS:PrefetchFoundNameL","time":1048,"value":1003},
    965         {"delta":0,"name":" DNS:PrefetchFoundNameLMAX","value":46},
    966         {"delta":0,"name":"SB:QueueDepth","time":0,"value":1},
    967         {"delta":0,"name":" SB:QueueDepthMAX","value":0},
    968         {"delta":102,"name":"IPC:SendMsgCount","value":1016},
    969         {"delta":98,"name":"Chrome:ProcMsgL UI","time":2777,"value":1378},
    970         {"delta":2381,"name":" Chrome:ProcMsgL UIMAX","value":2409},
    971         {"delta":98,"name":"Chrome:TotalMsgL UI","time":5715,"value":1378},
    972         {"delta":1518,"name":" Chrome:TotalMsgL UIMAX","value":2409},
    973         {"name":"Chrome:RendererInit","time":9},
    974         {"delta":0,"name":"WebFrameActiveCount","value":2},
    975         {"delta":0,"name":"Gears:LoadTime","time":1,"value":1},
    976         {"delta":0,"name":" Gears:LoadTimeMAX","value":1},
    977         {"delta":1,"name":"URLRequestCount","value":41},
    978         {"delta":1,"name":"mime_sniffer:ShouldSniffMimeT","time":27,"value":27},
    979         {"delta":0,"name":" mime_sniffer:ShouldSniffMime","value":1},
    980         {"delta":3,"name":"ResourceLoadServer","time":1065,"value":73},
    981         {"delta":0,"name":" ResourceLoadServerMAX","value":51},
    982         {"delta":11,"name":"WebFramePaintTime","time":232,"value":42},
    983         {"delta":9,"name":" WebFramePaintTimeMAX","value":41},
    984         {"delta":11,"name":"MPArch:RWH_OnMsgPaintRect","time":136,"value":42},
    985         {"delta":0,"name":" MPArch:RWH_OnMsgPaintRectMAX","value":9},
    986         {"delta":0,"name":"NPObjects","value":2},
    987         {"delta":6008832,"name":"V8:OsMemoryAllocated","value":28422144},
    988         {"delta":7905,"name":"V8:GlobalHandles","value":16832},
    989         {"delta":0,"name":"V8:PcreMallocCount","value":0},
    990         {"delta":1,"name":"V8:ObjectPropertiesToDictiona","value":16},
    991         {"delta":0,"name":"V8:ObjectElementsToDictionary","value":0},
    992         {"delta":1128652,"name":"V8:AliveAfterLastGC","value":4467596},
    993         {"delta":0,"name":"V8:ObjsSinceLastYoung","value":0},
    994         {"delta":0,"name":"V8:ObjsSinceLastFull","value":0},
    995         {"delta":2048,"name":"V8:SymbolTableCapacity","value":12288},
    996         {"delta":1493,"name":"V8:NumberOfSymbols","value":6865},
    997         {"delta":100442,"name":"V8:TotalExternalStringMemory","value":359184},
    998         {"delta":0,"name":"V8:ScriptWrappers","value":0},
    999         {"delta":3,"name":"V8:CallInitializeStubs","value":20},
   1000         {"delta":0,"name":"V8:CallPreMonomorphicStubs","value":4},
   1001         {"delta":0,"name":"V8:CallNormalStubs","value":0},
   1002         {"delta":6,"name":"V8:CallMegamorphicStubs","value":44},
   1003         {"delta":0,"name":"V8:ArgumentsAdaptors","value":0},
   1004         {"delta":647,"name":"V8:CompilationCacheHits","value":1269},
   1005         {"delta":9,"name":"V8:CompilationCacheMisses","value":57},
   1006         {"delta":0,"name":"V8:RegExpCacheHits","value":2},
   1007         {"delta":0,"name":"V8:RegExpCacheMisses","value":6},
   1008         {"delta":6260,"name":"V8:TotalEvalSize","value":12621},
   1009         {"delta":50221,"name":"V8:TotalLoadSize","value":217362},
   1010         {"delta":63734,"name":"V8:TotalParseSize","value":299135},
   1011         {"delta":15174,"name":"V8:TotalPreparseSkipped","value":61824},
   1012         {"delta":69932,"name":"V8:TotalCompileSize","value":313048},
   1013         {"delta":22,"name":"V8:CodeStubs","value":117},
   1014         {"delta":1185,"name":"V8:TotalStubsCodeSize","value":6456},
   1015         {"delta":45987,"name":"V8:TotalCompiledCodeSize","value":169546},
   1016         {"delta":0,"name":"V8:GCCompactorCausedByRequest","value":0},
   1017         {"delta":0,"name":"V8:GCCompactorCausedByPromote","value":0},
   1018         {"delta":0,"name":"V8:GCCompactorCausedByOldspac","value":0},
   1019         {"delta":0,"name":"V8:GCCompactorCausedByWeakHan","value":0},
   1020         {"delta":0,"name":"V8:GCLastResortFromJS","value":0},
   1021         {"delta":0,"name":"V8:GCLastResortFromHandles","value":0},
   1022         {"delta":0,"name":"V8:KeyedLoadGenericSmi","value":0},
   1023         {"delta":0,"name":"V8:KeyedLoadGenericSymbol","value":0},
   1024         {"delta":0,"name":"V8:KeyedLoadGenericSlow","value":0},
   1025         {"delta":0,"name":"V8:KeyedLoadFunctionPrototype","value":0},
   1026         {"delta":0,"name":"V8:KeyedLoadStringLength","value":0},
   1027         {"delta":0,"name":"V8:KeyedLoadArrayLength","value":0},
   1028         {"delta":0,"name":"V8:KeyedLoadConstantFunction","value":0},
   1029         {"delta":0,"name":"V8:KeyedLoadField","value":0},
   1030         {"delta":0,"name":"V8:KeyedLoadCallback","value":0},
   1031         {"delta":0,"name":"V8:KeyedLoadInterceptor","value":0},
   1032         {"delta":0,"name":"V8:KeyedStoreField","value":0},
   1033         {"delta":0,"name":"V8:ForIn","value":0},
   1034         {"delta":2,"name":"V8:EnumCacheHits","value":9},
   1035         {"delta":4,"name":"V8:EnumCacheMisses","value":23},
   1036         {"delta":3724,"name":"V8:RelocInfoCount","value":18374},
   1037         {"delta":6080,"name":"V8:RelocInfoSize","value":30287},
   1038         {"delta":0,"name":"History:InitTime","time":12,"value":1},
   1039         {"delta":0,"name":" History:InitTimeMAX","value":12},
   1040         {"delta":1,"name":"History:GetFavIconForURL","time":0,"value":22},
   1041         {"delta":0,"name":" History:GetFavIconForURLMAX","value":0},
   1042         {"delta":2,"name":"V8:PreParse","time":9,"value":11},
   1043         {"delta":9,"name":"V8:Parse","time":9,"value":57},
   1044         {"delta":3,"name":"V8:Compile","time":3,"value":22},
   1045         {"delta":49,"name":"V8:ParseLazy","time":17,"value":231},
   1046         {"delta":47,"name":"V8:CompileLazy","time":3,"value":221},
   1047         {"delta":12,"name":"V8:GCScavenger","time":13,"value":28},
   1048         {"delta":0,"name":"NewTabPage:SearchURLs:Total","time":0,"value":1},
   1049         {"delta":0,"name":" NewTabPage:SearchURLs:TotalM","value":0},
   1050         {"delta":6,"name":"V8:CompileEval","time":1,"value":35},
   1051         {"delta":0,"name":"Memory:CachedFontAndDC","time":3,"value":3},
   1052         {"delta":0,"name":" Memory:CachedFontAndDCMAX","value":2},
   1053         {"delta":0,"name":"ResourceLoaderWait","time":1296,"value":48},
   1054         {"delta":0,"name":" ResourceLoaderWaitMAX","value":55},
   1055         {"delta":0,"name":"History:GetPageThumbnail","time":15,"value":9},
   1056         {"delta":0,"name":" History:GetPageThumbnailMAX","value":10},
   1057         {"delta":9,"name":"MPArch:RWH_InputEventDelta","time":327,"value":170},
   1058         {"delta":0,"name":" MPArch:RWH_InputEventDeltaMA","value":154},
   1059         {"delta":0,"name":"Omnibox:QueryBookmarksTime","time":2,"value":44},
   1060         {"delta":0,"name":" Omnibox:QueryBookmarksTimeMA","value":1},
   1061         {"delta":0,"name":"Chrome:DelayMsgUI","value":3},
   1062         {"delta":0,"name":"Autocomplete:HistoryAsyncQuer","time":351,"value":86},
   1063         {"delta":0,"name":" Autocomplete:HistoryAsyncQue","value":10},
   1064         {"delta":0,"name":"History:QueryHistory","time":1018,"value":44},
   1065         {"delta":0,"name":" History:QueryHistoryMAX","value":233},
   1066         {"delta":0,"name":"DiskCache:GetFileForNewBlock","time":0,"value":34},
   1067         {"delta":0,"name":" DiskCache:GetFileForNewBlock","value":0},
   1068         {"delta":0,"name":"DiskCache:CreateBlock","time":0,"value":34},
   1069         {"delta":0,"name":" DiskCache:CreateBlockMAX","value":0},
   1070         {"delta":0,"name":"DiskCache:CreateTime","time":0,"value":10},
   1071         {"delta":0,"name":" DiskCache:CreateTimeMAX","value":0},
   1072         {"delta":0,"name":"DNS:PrefetchPositiveHitL","time":1048,"value":2},
   1073         {"delta":0,"name":" DNS:PrefetchPositiveHitLMAX","value":1002},
   1074         {"delta":0,"name":"DiskCache:GetRankings","time":0,"value":27},
   1075         {"delta":0,"name":" DiskCache:GetRankingsMAX","value":0},
   1076         {"delta":0,"name":"DiskCache:DeleteHeader","time":0,"value":3},
   1077         {"delta":0,"name":" DiskCache:DeleteHeaderMAX","value":0},
   1078         {"delta":0,"name":"DiskCache:DeleteData","time":0,"value":3},
   1079         {"delta":0,"name":" DiskCache:DeleteDataMAX","value":0},
   1080         {"delta":0,"name":"DiskCache:DeleteBlock","time":0,"value":6},
   1081         {"delta":0,"name":" DiskCache:DeleteBlockMAX","value":0},
   1082         {"delta":0,"name":"SessionRestore:last_session_f","time":0,"value":1},
   1083         {"delta":0,"name":" SessionRestore:last_session_","value":0},
   1084         {"delta":3,"name":"SessionRestore:command_size","time":2940,"value":36},
   1085         {"delta":0,"name":" SessionRestore:command_sizeM","value":277},
   1086         {"delta":0,"name":"DNS:IndependentNavigation","time":2,"value":4},
   1087         {"delta":0,"name":" DNS:IndependentNavigationMAX","value":1},
   1088         {"delta":0,"name":"DiskCache:UpdateRank","time":1,"value":25},
   1089         {"delta":0,"name":" DiskCache:UpdateRankMAX","value":1},
   1090         {"delta":0,"name":"DiskCache:WriteTime","time":1,"value":21},
   1091         {"delta":0,"name":" DiskCache:WriteTimeMAX","value":1},
   1092         {"delta":0,"name":"Net:Transaction_Latency","time":183,"value":7},
   1093         {"delta":0,"name":" Net:Transaction_LatencyMAX","value":37},
   1094         {"delta":0,"name":"Net:Transaction_Bandwidth","time":40,"value":7},
   1095         {"delta":0,"name":" Net:Transaction_BandwidthMAX","value":8},
   1096         {"delta":0,"name":"NewTabUI load","time":564,"value":1},
   1097         {"delta":0,"name":" NewTabUI loadMAX","value":564},
   1098         {"delta":0,"name":"DiskCache:OpenTime","time":0,"value":2},
   1099         {"delta":0,"name":" DiskCache:OpenTimeMAX","value":0},
   1100         {"delta":0,"name":"DiskCache:ReadTime","time":0,"value":4},
   1101         {"delta":0,"name":" DiskCache:ReadTimeMAX","value":0},
   1102         {"delta":0,"name":"MPArch:RWHH_WhiteoutDuration_","time":27,"value":1},
   1103         {"delta":0,"name":" MPArch:RWHH_WhiteoutDuration","value":27},
   1104         {"delta":1,"name":"AsyncIO:IPCChannelClose","time":0,"value":4},
   1105         {"delta":0,"name":" AsyncIO:IPCChannelCloseMAX","value":0},
   1106         {"name":"GetHistoryTimer","time":0},
   1107         {"delta":0,"name":"DiskCache:Entries","time":7,"value":1},
   1108         {"delta":0,"name":" DiskCache:EntriesMAX","value":7},
   1109         {"delta":0,"name":"DiskCache:Size","time":0,"value":1},
   1110         {"delta":0,"name":" DiskCache:SizeMAX","value":0},
   1111         {"delta":0,"name":"DiskCache:MaxSize","time":80,"value":1},
   1112         {"delta":0,"name":" DiskCache:MaxSizeMAX","value":80},
   1113         {"delta":0,"name":"History:AddFTSData","time":1,"value":1},
   1114         {"delta":0,"name":" History:AddFTSDataMAX","value":1},
   1115         {"delta":0,"name":"Chrome:SlowMsgUI","value":1}
   1116     ],
   1117     "timers":[
   1118         {"name":"Chrome:Init","time":5},
   1119         {"delta":0,"name":"Shutdown:window_close:time","time":111,"value":1},
   1120         {"delta":0,"name":"Shutdown:window_close:time_pe","time":111,"value":1},
   1121         {"delta":0,"name":"Shutdown:renderers:total","time":1,"value":1},
   1122         {"delta":0,"name":"Shutdown:renderers:slow","time":0,"value":1},
   1123         {"delta":0,"name":"DNS:PrefetchQueue","time":2,"value":6},
   1124         {"delta":0,"name":"DNS:PrefetchFoundNameL","time":1048,"value":1003},
   1125         {"delta":0,"name":"SB:QueueDepth","time":0,"value":1},
   1126         {"delta":98,"name":"Chrome:ProcMsgL UI","time":2777,"value":1378},
   1127         {"delta":98,"name":"Chrome:TotalMsgL UI","time":5715,"value":1378},
   1128         {"name":"Chrome:RendererInit","time":9},
   1129         {"delta":0,"name":"Gears:LoadTime","time":1,"value":1},
   1130         {"delta":1,"name":"mime_sniffer:ShouldSniffMimeT","time":27,"value":27},
   1131         {"delta":3,"name":"ResourceLoadServer","time":1065,"value":73},
   1132         {"delta":11,"name":"WebFramePaintTime","time":232,"value":42},
   1133         {"delta":11,"name":"MPArch:RWH_OnMsgPaintRect","time":136,"value":42},
   1134         {"delta":0,"name":"History:InitTime","time":12,"value":1},
   1135         {"delta":1,"name":"History:GetFavIconForURL","time":0,"value":22},
   1136         {"delta":2,"name":"V8:PreParse","time":9,"value":11},
   1137         {"delta":9,"name":"V8:Parse","time":9,"value":57},
   1138         {"delta":3,"name":"V8:Compile","time":3,"value":22},
   1139         {"delta":49,"name":"V8:ParseLazy","time":17,"value":231},
   1140         {"delta":47,"name":"V8:CompileLazy","time":3,"value":221},
   1141         {"delta":12,"name":"V8:GCScavenger","time":13,"value":28},
   1142         {"delta":0,"name":"NewTabPage:SearchURLs:Total","time":0,"value":1},
   1143         {"delta":6,"name":"V8:CompileEval","time":1,"value":35},
   1144         {"delta":0,"name":"Memory:CachedFontAndDC","time":3,"value":3},
   1145         {"delta":0,"name":"ResourceLoaderWait","time":1296,"value":48},
   1146         {"delta":0,"name":"History:GetPageThumbnail","time":15,"value":9},
   1147         {"delta":9,"name":"MPArch:RWH_InputEventDelta","time":327,"value":170},
   1148         {"delta":0,"name":"Omnibox:QueryBookmarksTime","time":2,"value":44},
   1149         {"delta":0,"name":"Autocomplete:HistoryAsyncQuer","time":351,"value":86},
   1150         {"delta":0,"name":"History:QueryHistory","time":1018,"value":44},
   1151         {"delta":0,"name":"DiskCache:GetFileForNewBlock","time":0,"value":34},
   1152         {"delta":0,"name":"DiskCache:CreateBlock","time":0,"value":34},
   1153         {"delta":0,"name":"DiskCache:CreateTime","time":0,"value":10},
   1154         {"delta":0,"name":"DNS:PrefetchPositiveHitL","time":1048,"value":2},
   1155         {"delta":0,"name":"DiskCache:GetRankings","time":0,"value":27},
   1156         {"delta":0,"name":"DiskCache:DeleteHeader","time":0,"value":3},
   1157         {"delta":0,"name":"DiskCache:DeleteData","time":0,"value":3},
   1158         {"delta":0,"name":"DiskCache:DeleteBlock","time":0,"value":6},
   1159         {"delta":0,"name":"SessionRestore:last_session_f","time":0,"value":1},
   1160         {"delta":3,"name":"SessionRestore:command_size","time":2940,"value":36},
   1161         {"delta":0,"name":"DNS:IndependentNavigation","time":2,"value":4},
   1162         {"delta":0,"name":"DiskCache:UpdateRank","time":1,"value":25},
   1163         {"delta":0,"name":"DiskCache:WriteTime","time":1,"value":21},
   1164         {"delta":0,"name":"Net:Transaction_Latency","time":183,"value":7},
   1165         {"delta":0,"name":"Net:Transaction_Bandwidth","time":40,"value":7},
   1166         {"delta":0,"name":"NewTabUI load","time":564,"value":1},
   1167         {"delta":0,"name":"DiskCache:OpenTime","time":0,"value":2},
   1168         {"delta":0,"name":"DiskCache:ReadTime","time":0,"value":4},
   1169         {"delta":0,"name":"MPArch:RWHH_WhiteoutDuration_","time":27,"value":1},
   1170         {"delta":1,"name":"AsyncIO:IPCChannelClose","time":0,"value":4},
   1171         {"name":"GetHistoryTimer","time":0},
   1172         {"delta":0,"name":"DiskCache:Entries","time":7,"value":1},
   1173         {"delta":0,"name":"DiskCache:Size","time":0,"value":1},
   1174         {"delta":0,"name":"DiskCache:MaxSize","time":80,"value":1},
   1175         {"delta":0,"name":"History:AddFTSData","time":1,"value":1}
   1176     ]});
   1177     jstProcess(cx, tp);
   1178 }
   1179 
   1180 var TemplateTest = new BenchmarkSuite('Template', [new Benchmark("Template",TemplateTest)]);
   1181