- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / tests / ppapi_browser / progress_event_listener.js
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.
6 function 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   // True if an event with lengthComputable is ever triggered.
52   this.stateSawLengthComputable = false;
53   // The last event.total seen from an event with lengthComputable being true.
54   this.stateProgressTotal = -1;
55   // The last event.loaded seen from an event with lengthComputable being true.
56   this.stateProgressPrev = -1;
57   // Function to record progress stats.
58   this.recordProgress = function(event) {
59     // Can either record progress from a progress event with lengthComputable,
60     // or from a loadend event.
61     if (event.type == 'progress' && event.lengthComputable) {
62       this.stateSawLengthComputable = true;
63       this.stateProgressTotal = event.total;
64       this.stateProgressPrev = event.loaded;
65     } else if (event.type == 'loadend' && event.lengthComputable) {
66       this.stateProgressTotal = event.total;
67       this.stateProgressPrev = event.loaded;
68     }
69   }
70 }
71
72 // event_machines is a collection of EventStateMachines, one for each element
73 // id that dispatches an event of a type we are listening for.
74 window.event_machines = { };
75 // Look up the EventStateMachine for the id.
76 function lookupEventMachine(element_id) {
77   var event_machine = window.event_machines[element_id];
78   if (event_machine == undefined) {
79     // This is the first event for this target.  Create an EventStateMachine.
80     event_machine = new EventStateMachine();
81     window.event_machines[element_id] = event_machine;
82   }
83   return event_machine;
84 }
85 // Sets up event listeners on the body element for all the progress
86 // event types.  Delegation to the body allows this to be done only once
87 // per document.
88 var setListeners = function(body_element) {
89   var eventListener = function(e) {
90     // Find the target element of the event.
91     var target_element = e.target;
92     // Body only dispatches for elements having the 'naclModule' CSS class.
93     if (target_element.className != 'naclModule') {
94       return;
95     }
96     var element_id = target_element.id;
97     // Look up the EventStateMachine for the target of the event.
98     var event_machine = lookupEventMachine(element_id);
99     // Update the state of the machine.
100     event_machine.transitionTo(e.type);
101     // Record progress information if possible.
102     event_machine.recordProgress(e);
103   }
104   // Add the listener for all of the ProgressEvent event types.
105   body_element.addEventListener('loadstart', eventListener, true);
106   body_element.addEventListener('progress', eventListener, true);
107   body_element.addEventListener('error', eventListener, true);
108   body_element.addEventListener('abort', eventListener, true);
109   body_element.addEventListener('load', eventListener, true);
110   body_element.addEventListener('loadend', eventListener, true);
111 }
112
113 // Performs some tests to make sure that progress events follow the expected
114 // state transitions to end in an expected state.
115 function testProgressEventStateMachine(tester,
116                                        embedId,
117                                        progressMinCount,
118                                        errorCount,
119                                        abortCount,
120                                        loadCount,
121                                        lastError) {
122   var eventMachine = lookupEventMachine(embedId);
123   // Test the expected number of occurrences, with some duplication.
124   tester.addTest('begin_count_' + embedId, function() {
125     // There should be no 'BEGIN' event.
126     assertEqual(eventMachine.stateHistogram['BEGIN'], 0);
127   });
128   tester.addTest('loadstart_count_' + embedId, function() {
129     // There should be one 'loadstart' event.
130     assertEqual(eventMachine.stateHistogram['loadstart'], 1);
131   });
132   tester.addTest('progress_min_count_' + embedId, function() {
133     // There should be at least one progress event when the manifest file is
134     // loaded and another when the .nexe is loaded.
135     assert(eventMachine.stateHistogram['progress'] >= progressMinCount);
136   });
137   tester.addTest('progress_samples_' + embedId, function() {
138     console.log('stateSawLengthComputable ' +
139         eventMachine.stateSawLengthComputable);
140     console.log('stateProgressPrev ' +
141         eventMachine.stateProgressPrev);
142     console.log('stateProgressTotal ' +
143         eventMachine.stateProgressTotal);
144
145     assert(eventMachine.stateSawLengthComputable);
146     // Progress events are not necessarily monotonic.  For glibc, each DSO
147     // will trigger a different series of progress events with different totals.
148     // For glibc, the final loadend progress event may even correspond to
149     // the very first load event, instead of corresponding to the last...
150     // So, all we check is that the latest values make some sense.
151     assert(eventMachine.stateProgressPrev > 0);
152     assert(eventMachine.stateProgressTotal > 0);
153     assert(eventMachine.stateProgressPrev <= eventMachine.stateProgressTotal);
154   });
155   tester.addTest('error_count_' + embedId, function() {
156     // Check that the right number of 'error' events were dispatched.
157     assertEqual(eventMachine.stateHistogram['error'], errorCount);
158   });
159   tester.addTest('abort_count_' + embedId, function() {
160     // Check that the right number of 'abort' events were dispatched.
161     assertEqual(eventMachine.stateHistogram['abort'], abortCount);
162   });
163   tester.addTest('load_count_' + embedId, function() {
164     // Check that the right number of 'load' events were dispatched.
165     assertEqual(eventMachine.stateHistogram['load'], loadCount);
166   })
167   tester.addTest('loadend_count_' + embedId, function() {
168     // There should be one 'loadend' event.
169     assertEqual(eventMachine.stateHistogram['loadend'], 1);
170   });
171   tester.addTest('unexpected_count_' + embedId, function() {
172     // There should be no 'UNEXPECTED' event.
173     assertEqual(eventMachine.stateHistogram['UNEXPECTED'], 0);
174   });
175   tester.addTest('end_state_' + embedId, function() {
176     // Test that the progress events followed the expected sequence to
177     // completion in the 'loadend' state.
178     assertEqual(eventMachine.currentState, 'loadend');
179   });
180   tester.addTest('last_error_string_' + embedId, function() {
181     // If an error or abort was reported, check that lastError is set
182     // to the correct value.
183     if ((eventMachine.stateHistogram['error'] > 0 ||
184          eventMachine.stateHistogram['abort'] > 0)) {
185       var embed = $(embedId);
186       assertEqual(embed.lastError, lastError);
187     }
188   });
189 }