progress_event_listener.js revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Class to track the progress events received by a particular plugin instance. 6function EventStateMachine() { 7 // Work around how JS binds 'this'. 8 var this_ = this; 9 // Given a particular state, what are the acceptable event types. 10 this.expectedNext = { 11 'BEGIN': { 'loadstart': 1 }, 12 'loadstart': { 'progress': 1, 'error': 1, 'abort': 1, 'load': 1 }, 13 'progress': { 'progress': 1, 'error': 1, 'abort': 1, 'load': 1 }, 14 'error': { 'loadend': 1 }, 15 'abort': { 'loadend': 1 }, 16 'load': { 'loadend': 1 }, 17 'loadend': { }, 18 'UNEXPECTED': { }, 19 }; 20 // The current state (and index into expectedNext). 21 this.currentState = 'BEGIN'; 22 // For each recognized state, a count of the times it was reached. 23 this.stateHistogram = { 24 'BEGIN': 0, 25 'loadstart': 0, 26 'progress': 0, 27 'error': 0, 28 'abort': 0, 29 'load': 0, 30 'loadend': 0, 31 'UNEXPECTED': 0 32 }; 33 // The state transition function. 34 this.transitionTo = function(event_type) { 35 // The index values of this_.expectedNext are the only valid states. 36 // Invalid event types are normalized to 'UNEXPECTED'. 37 if (this_.expectedNext[event_type] == undefined) { 38 console.log('unexpected ' + event_type); 39 event_type = 'UNEXPECTED'; 40 } 41 // Check that the next event type is expected from the current state. 42 // If not, we transition to the state 'UNEXPECTED'. 43 if (!(event_type in this_.expectedNext[this_.currentState])) { 44 console.log('unexpected ' + event_type + ' from ' + this_.currentState); 45 event_type = 'UNEXPECTED'; 46 } 47 this_.currentState = event_type; 48 this_.stateHistogram[this_.currentState]++; 49 } 50} 51 52// event_machines is a collection of EventStateMachines, one for each element 53// id that dispatches an event of a type we are listening for. 54window.event_machines = { }; 55// Look up the EventStateMachine for the id. 56function lookupEventMachine(element_id) { 57 var event_machine = window.event_machines[element_id]; 58 if (event_machine == undefined) { 59 // This is the first event for this target. Create an EventStateMachine. 60 event_machine = new EventStateMachine(); 61 window.event_machines[element_id] = event_machine; 62 } 63 return event_machine; 64} 65// Sets up event listeners on the body element for all the progress 66// event types. Delegation to the body allows this to be done only once 67// per document. 68var setListeners = function(body_element) { 69 var eventListener = function(e) { 70 // Find the target element of the event. 71 var target_element = e.target; 72 // Body only dispatches for elements having the 'naclModule' CSS class. 73 if (target_element.className != 'naclModule') { 74 return; 75 } 76 var element_id = target_element.id; 77 // Look up the EventStateMachine for the target of the event. 78 var event_machine = lookupEventMachine(element_id); 79 // Update the state of the machine. 80 event_machine.transitionTo(e.type); 81 } 82 // Add the listener for all of the ProgressEvent event types. 83 body_element.addEventListener('loadstart', eventListener, true); 84 body_element.addEventListener('progress', eventListener, true); 85 body_element.addEventListener('error', eventListener, true); 86 body_element.addEventListener('abort', eventListener, true); 87 body_element.addEventListener('load', eventListener, true); 88 body_element.addEventListener('loadend', eventListener, true); 89} 90 91// Performs some tests to make sure that progress events follow the expected 92// state transitions to end in an expected state. 93function testProgressEventStateMachine(tester, 94 embedId, 95 progressMinCount, 96 errorCount, 97 abortCount, 98 loadCount, 99 lastError) { 100 var eventMachine = lookupEventMachine(embedId); 101 // Test the expected number of occurrences, with some duplication. 102 tester.addTest('begin_count_' + embedId, function() { 103 // There should be no 'BEGIN' event. 104 assertEqual(eventMachine.stateHistogram['BEGIN'], 0); 105 }); 106 tester.addTest('loadstart_count_' + embedId, function() { 107 // There should be one 'loadstart' event. 108 assertEqual(eventMachine.stateHistogram['loadstart'], 1); 109 }); 110 tester.addTest('progress_min_count_' + embedId, function() { 111 // There should be at least one progress event when the manifest file is 112 // loaded and another when the .nexe is loaded. 113 assert(eventMachine.stateHistogram['progress'] >= progressMinCount); 114 }); 115 tester.addTest('error_count_' + embedId, function() { 116 // Check that the right number of 'error' events were dispatched. 117 assertEqual(eventMachine.stateHistogram['error'], errorCount); 118 }); 119 tester.addTest('abort_count_' + embedId, function() { 120 // Check that the right number of 'abort' events were dispatched. 121 assertEqual(eventMachine.stateHistogram['abort'], abortCount); 122 }); 123 tester.addTest('load_count_' + embedId, function() { 124 // Check that the right number of 'load' events were dispatched. 125 assertEqual(eventMachine.stateHistogram['load'], loadCount); 126 }) 127 tester.addTest('loadend_count_' + embedId, function() { 128 // There should be one 'loadend' event. 129 assertEqual(eventMachine.stateHistogram['loadend'], 1); 130 }); 131 tester.addTest('unexpected_count_' + embedId, function() { 132 // There should be no 'UNEXPECTED' event. 133 assertEqual(eventMachine.stateHistogram['UNEXPECTED'], 0); 134 }); 135 tester.addTest('end_state_' + embedId, function() { 136 // Test that the progress events followed the expected sequence to 137 // completion in the 'loadend' state. 138 assertEqual(eventMachine.currentState, 'loadend'); 139 }); 140 tester.addTest('last_error_string_' + embedId, function() { 141 // If an error or abort was reported, check that lastError is set 142 // to the correct value. 143 if ((eventMachine.stateHistogram['error'] > 0 || 144 eventMachine.stateHistogram['abort'] > 0)) { 145 var embed = $(embedId); 146 assertEqual(embed.lastError, lastError); 147 } 148 }); 149} 150