| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| // Events test - test the hooking and dispatch of events. |
| // |
| // This is a fairly simple test for measuring event peformance. |
| // We create a DOM structure (a set of nested divs) to test with. |
| // |
| // The Hooking test measures the time to register onclick handlers for |
| // each node in the structure. This simulates conditions where applications |
| // register event handlers on many nodes programatically. |
| // |
| // The Dispatch test measures the time to dispatch events to each node |
| // in the structure. In this case, we register the event handler as part |
| // of the HTML for the structure, and then simply simulate onclick events |
| // to each node. |
| // |
| // Works in IE, FF, Safari, and Chrome. |
| |
| var Events_counter = 0; |
| function EventClickHandler() { |
| Events_counter++; |
| } |
| |
| function EventsTest(rows, cols) { |
| var me = this; |
| this.rows = rows; |
| this.cols = cols; |
| this.cell_count = 0; // Track the number of cells created in our dom tree. |
| this.proxies = []; |
| this.random_ids = []; |
| |
| // Create a DOM structure and optionally register event handlers on each node. |
| // Create the structure by setting innerHTML so that the DOM nodes are not |
| // pre-wrapped for JS access. |
| this.CreateTable = function(add_event_listeners) { |
| var html_string = '<div>'; |
| for (var i = 0; i < me.rows; i++) |
| html_string += me.CreateRow(i, me.cols, add_event_listeners); |
| return html_string + '</div>'; |
| }; |
| |
| // Returns an html string for a div with a row/column based id, with an optional onclick handler. |
| this.CreateCell = function(row_id, col_id, add_event_listeners) { |
| var str = '<div id="r' + row_id + 'c' + col_id + '"'; |
| if (add_event_listeners) |
| str += ' onclick="EventClickHandler();"'; |
| str += '>'+ me.cell_count++ + '</div>'; |
| return str; |
| }; |
| |
| // Returns an html string with an outer div containing |cols| inner divs, |
| // optionally having an onclick handler. |
| this.CreateRow = function(row_id, cols, add_event_listeners) { |
| var html_string = '<div id="r' + row_id + '">'; |
| for (var i = 0; i < cols; i++) |
| html_string += me.CreateCell(row_id, i, add_event_listeners); |
| return html_string + '</div>'; |
| }; |
| |
| // Prepares for testing with elements that have no pre-defined onclick |
| // handlers. |
| this.Setup = function() { |
| me.cell_count = 0; |
| Events_counter = 0; |
| var root_element = document.getElementById("benchmark_content"); |
| root_element.innerHTML = me.CreateTable(false); |
| return root_element; |
| }; |
| |
| // Similar to Setup, but with onclick handlers already defined in the html. |
| this.SetupWithListeners = function() { |
| me.cell_count = 0; |
| Events_counter = 0; |
| var root_element = document.getElementById("benchmark_content"); |
| root_element.innerHTML = me.CreateTable(true); |
| return root_element; |
| }; |
| |
| // Sets up for testing performance of removing event handlers. |
| this.SetupForTeardown = function() { |
| me.random_ids = []; |
| me.SetupWithListeners(); |
| var tmp = []; |
| for (var row = 0; row < me.rows; row++) { |
| for (var col = 0; col < me.cols; col++) |
| tmp.push("r" + row + "c" + col); |
| } |
| while (tmp.length > 0) { |
| var index = Math.floor(Math.random() * tmp.length); |
| me.random_ids.push(tmp.splice(index, 1)); |
| } |
| }; |
| |
| // Tests the time it takes to go through and hook all elements in our dom. |
| this.HookTest = function() { |
| var node_count = 0; |
| |
| var row_id = 0; |
| while(true) { |
| var row = document.getElementById('r' + row_id); |
| if (row == undefined) |
| break; |
| |
| var col_id = 0; |
| while(true) { |
| var col = document.getElementById('r' + row_id + 'c' + col_id); |
| if (col == undefined) |
| break; |
| |
| if (col.addEventListener) |
| col.addEventListener("click", EventClickHandler, false); |
| else if (col.attachEvent) |
| col.attachEvent("onclick", EventClickHandler); // To support IE |
| else |
| throw "FAILED TO ATTACH EVENTS"; |
| col_id++; |
| node_count++; |
| } |
| |
| row_id++; |
| } |
| |
| if (node_count != me.rows * me.cols) |
| throw "ERROR - did not iterate all nodes"; |
| }; |
| |
| // Tests the time it takes to go through and hook all elements in our dom. |
| // Creates new proxy object for each registration |
| this.HookTestProxy = function() { |
| var node_count = 0; |
| |
| var row_id = 0; |
| while(true) { |
| var row = document.getElementById('r' + row_id); |
| if (row == undefined) |
| break; |
| |
| var col_id = 0; |
| while(true) { |
| var col = document.getElementById('r' + row_id + 'c' + col_id); |
| if (col == undefined) |
| break; |
| |
| var proxy = function() {}; |
| proxy.col = col; |
| me.proxies.push(proxy); |
| if (col.addEventListener) |
| col.addEventListener("click", proxy, false); |
| else if (col.attachEvent) |
| col.attachEvent("onclick", proxy); // To support IE |
| else |
| throw "FAILED TO ATTACH EVENTS"; |
| col_id++; |
| node_count++; |
| } |
| |
| row_id++; |
| } |
| |
| if (node_count != me.rows * me.cols) |
| throw "ERROR - did not iterate all nodes"; |
| }; |
| |
| // Tests firing the events for each element in our dom. |
| this.DispatchTest = function() { |
| var node_count = 0; |
| |
| var row_id = 0; |
| while(true) { |
| var row = document.getElementById('r' + row_id); |
| if (row == undefined) |
| break; |
| |
| var col_id = 0; |
| while(true) { |
| var col = document.getElementById('r' + row_id + 'c' + col_id); |
| if (col == undefined) |
| break; |
| |
| if (document.createEvent) { |
| var event = document.createEvent("MouseEvents"); |
| event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); |
| col.dispatchEvent(event); |
| } else if (col.fireEvent) { |
| var event = document.createEventObject(); |
| col.fireEvent("onclick", event); |
| } else |
| throw "FAILED TO FIRE EVENTS"; |
| |
| col_id++; |
| node_count++; |
| } |
| |
| row_id++; |
| } |
| |
| if (Events_counter != me.rows * me.cols) |
| throw "ERROR - did not fire events on all nodes!" + Events_counter; |
| }; |
| |
| // Tests removing event handlers. |
| this.TeardownTest = function() { |
| var node_count = 0; |
| for (var i = 0; i < me.random_ids.length; i++) { |
| var col = document.getElementById(me.random_ids[i]); |
| if (col.removeEventListener) |
| col.removeEventListener("click", EventClickHandler, false); |
| else if (col.detachEvent) |
| col.detachEvent("onclick", EventClickHandler); |
| else |
| throw "FAILED TO FIRE EVENTS"; |
| node_count++; |
| } |
| |
| if (node_count != me.rows * me.cols) |
| throw "ERROR - did not remove listeners from all nodes! " + node_count; |
| }; |
| |
| // Removes event handlers and their associated proxy objects. |
| this.ProxyCleanup = function() { |
| for (var i = 0, n = me.proxies.length; i < n; i++) { |
| var proxy = me.proxies[i]; |
| var col = proxy.col; |
| if (col.removeEventListener) |
| col.removeEventListener("click", proxy, false); |
| else if (col.detachEvent) |
| col.detachEvent("onclick", proxy); // To support IE |
| } |
| me.proxies = []; |
| }; |
| } |
| |
| var small_test = new EventsTest(100, 10); |
| var large_test = new EventsTest(100, 50); |
| var extra_large_test = new EventsTest(200, 20); |
| |
| var EventTest = new BenchmarkSuite('Events', [ |
| new Benchmark("Event Hooking (1000 nodes)", small_test.HookTest, small_test.Setup), |
| new Benchmark("Event Dispatch (1000 nodes)", small_test.DispatchTest, small_test.SetupWithListeners), |
| new Benchmark("Event Hooking (5000 nodes)", large_test.HookTest, large_test.Setup), |
| new Benchmark("Event Hooking Proxy (4000 nodes)", |
| extra_large_test.HookTestProxy, extra_large_test.Setup, extra_large_test.ProxyCleanup), |
| new Benchmark("Event Dispatch (5000 nodes)", large_test.DispatchTest, large_test.SetupWithListeners), |
| new Benchmark("Event Teardown (5000 nodes)", large_test.TeardownTest, large_test.SetupForTeardown), |
| new Benchmark("Event Teardown (4000 nodes)", extra_large_test.TeardownTest, extra_large_test.SetupForTeardown) |
| ]); |