1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/wifi/StateMachine.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,205 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +"use strict"; 1.11 + 1.12 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; 1.13 + 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 + 1.16 +this.EXPORTED_SYMBOLS = ["StateMachine"]; 1.17 + 1.18 +const DEBUG = false; 1.19 + 1.20 +this.StateMachine = function(aDebugTag) { 1.21 + function debug(aMsg) { 1.22 + dump('-------------- StateMachine:' + aDebugTag + ': ' + aMsg); 1.23 + } 1.24 + 1.25 + var sm = {}; 1.26 + 1.27 + var _initialState; 1.28 + var _curState; 1.29 + var _prevState; 1.30 + var _paused; 1.31 + var _eventQueue = []; 1.32 + var _deferredEventQueue = []; 1.33 + var _defaultEventHandler; 1.34 + 1.35 + // Public interfaces. 1.36 + 1.37 + sm.setDefaultEventHandler = function(aDefaultEventHandler) { 1.38 + _defaultEventHandler = aDefaultEventHandler; 1.39 + }; 1.40 + 1.41 + sm.start = function(aInitialState) { 1.42 + _initialState = aInitialState; 1.43 + sm.gotoState(_initialState); 1.44 + }; 1.45 + 1.46 + sm.sendEvent = function (aEvent) { 1.47 + if (!_initialState) { 1.48 + if (DEBUG) { 1.49 + debug('StateMachine is not running. Call StateMachine.start() first.'); 1.50 + } 1.51 + return; 1.52 + } 1.53 + _eventQueue.push(aEvent); 1.54 + asyncCall(handleFirstEvent); 1.55 + }; 1.56 + 1.57 + sm.getPreviousState = function() { 1.58 + return _prevState; 1.59 + }; 1.60 + 1.61 + sm.getCurrentState = function() { 1.62 + return _curState; 1.63 + }; 1.64 + 1.65 + // State object maker. 1.66 + // @param aName string for this state's name. 1.67 + // @param aDelegate object: 1.68 + // .handleEvent: required. 1.69 + // .enter: called before entering this state (optional). 1.70 + // .exit: called before exiting this state (optional). 1.71 + sm.makeState = function (aName, aDelegate) { 1.72 + if (!aDelegate.handleEvent) { 1.73 + throw "handleEvent is a required delegate function."; 1.74 + } 1.75 + var nop = function() {}; 1.76 + return { 1.77 + name: aName, 1.78 + enter: (aDelegate.enter || nop), 1.79 + exit: (aDelegate.exit || nop), 1.80 + handleEvent: aDelegate.handleEvent 1.81 + }; 1.82 + }; 1.83 + 1.84 + sm.deferEvent = function (aEvent) { 1.85 + // The definition of a 'deferred event' is: 1.86 + // We are not able to handle this event now but after receiving 1.87 + // certain event or entering a new state, we might be able to handle 1.88 + // it. For example, we couldn't handle CONNECT_EVENT in the 1.89 + // diconnecting state. But once we finish doing "disconnecting", we 1.90 + // could then handle CONNECT_EVENT! 1.91 + // 1.92 + // So, the deferred event may be handled in the following cases: 1.93 + // 1. Once we entered a new state. 1.94 + // 2. Once we handled a regular event. 1.95 + if (DEBUG) { 1.96 + debug('Deferring event: ' + JSON.stringify(aEvent)); 1.97 + } 1.98 + _deferredEventQueue.push(aEvent); 1.99 + }; 1.100 + 1.101 + // Goto the new state. If the current state is null, the exit 1.102 + // function won't be called. 1.103 + sm.gotoState = function (aNewState) { 1.104 + if (_curState) { 1.105 + if (DEBUG) { 1.106 + debug("exiting state: " + _curState.name); 1.107 + } 1.108 + _curState.exit(); 1.109 + } 1.110 + 1.111 + _prevState = _curState; 1.112 + _curState = aNewState; 1.113 + 1.114 + if (DEBUG) { 1.115 + debug("entering state: " + _curState.name); 1.116 + } 1.117 + _curState.enter(); 1.118 + 1.119 + // We are in the new state now. We got a chance to handle the 1.120 + // deferred events. 1.121 + handleDeferredEvents(); 1.122 + 1.123 + sm.resume(); 1.124 + }; 1.125 + 1.126 + // No incoming event will be handled after you call pause(). 1.127 + // (But they will be queued.) 1.128 + sm.pause = function() { 1.129 + _paused = true; 1.130 + }; 1.131 + 1.132 + // Continue to handle incoming events. 1.133 + sm.resume = function() { 1.134 + _paused = false; 1.135 + asyncCall(handleFirstEvent); 1.136 + }; 1.137 + 1.138 + //---------------------------------------------------------- 1.139 + // Private stuff 1.140 + //---------------------------------------------------------- 1.141 + 1.142 + function asyncCall(f) { 1.143 + Services.tm.currentThread.dispatch(f, Ci.nsIThread.DISPATCH_NORMAL); 1.144 + } 1.145 + 1.146 + function handleFirstEvent() { 1.147 + var hadDeferredEvents; 1.148 + 1.149 + if (0 === _eventQueue.length) { 1.150 + return; 1.151 + } 1.152 + 1.153 + if (_paused) { 1.154 + return; // The state machine is paused now. 1.155 + } 1.156 + 1.157 + hadDeferredEvents = _deferredEventQueue.length > 0; 1.158 + 1.159 + handleOneEvent(_eventQueue.shift()); // The handler may defer this event. 1.160 + 1.161 + // We've handled one event. If we had deferred events before, now is 1.162 + // a good chance to handle them. 1.163 + if (hadDeferredEvents) { 1.164 + handleDeferredEvents(); 1.165 + } 1.166 + 1.167 + // Continue to handle the next regular event. 1.168 + handleFirstEvent(); 1.169 + } 1.170 + 1.171 + function handleDeferredEvents() { 1.172 + if (_deferredEventQueue.length && DEBUG) { 1.173 + debug('Handle deferred events: ' + _deferredEventQueue.length); 1.174 + } 1.175 + for (let i = 0; i < _deferredEventQueue.length; i++) { 1.176 + handleOneEvent(_deferredEventQueue.shift()); 1.177 + } 1.178 + } 1.179 + 1.180 + function handleOneEvent(aEvent) 1.181 + { 1.182 + if (DEBUG) { 1.183 + debug('Handling event: ' + JSON.stringify(aEvent)); 1.184 + } 1.185 + 1.186 + var handled = _curState.handleEvent(aEvent); 1.187 + 1.188 + if (undefined === handled) { 1.189 + throw "handleEvent returns undefined: " + _curState.name; 1.190 + } 1.191 + if (!handled) { 1.192 + // Event is not handled in the current state. Try handleEventCommon(). 1.193 + handled = (_defaultEventHandler ? _defaultEventHandler(aEvent) : handled); 1.194 + } 1.195 + if (undefined === handled) { 1.196 + throw "handleEventCommon returns undefined: " + _curState.name; 1.197 + } 1.198 + if (!handled) { 1.199 + if (DEBUG) { 1.200 + debug('!!!!!!!!! FIXME !!!!!!!!! Event not handled: ' + JSON.stringify(aEvent)); 1.201 + } 1.202 + } 1.203 + 1.204 + return handled; 1.205 + } 1.206 + 1.207 + return sm; 1.208 +};