summaryrefslogtreecommitdiffstats
path: root/dom/wifi/StateMachine.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'dom/wifi/StateMachine.jsm')
-rw-r--r--dom/wifi/StateMachine.jsm205
1 files changed, 205 insertions, 0 deletions
diff --git a/dom/wifi/StateMachine.jsm b/dom/wifi/StateMachine.jsm
new file mode 100644
index 000000000..94b876f82
--- /dev/null
+++ b/dom/wifi/StateMachine.jsm
@@ -0,0 +1,205 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = ["StateMachine"];
+
+const DEBUG = false;
+
+this.StateMachine = function(aDebugTag) {
+ function debug(aMsg) {
+ dump('-------------- StateMachine:' + aDebugTag + ': ' + aMsg);
+ }
+
+ var sm = {};
+
+ var _initialState;
+ var _curState;
+ var _prevState;
+ var _paused;
+ var _eventQueue = [];
+ var _deferredEventQueue = [];
+ var _defaultEventHandler;
+
+ // Public interfaces.
+
+ sm.setDefaultEventHandler = function(aDefaultEventHandler) {
+ _defaultEventHandler = aDefaultEventHandler;
+ };
+
+ sm.start = function(aInitialState) {
+ _initialState = aInitialState;
+ sm.gotoState(_initialState);
+ };
+
+ sm.sendEvent = function (aEvent) {
+ if (!_initialState) {
+ if (DEBUG) {
+ debug('StateMachine is not running. Call StateMachine.start() first.');
+ }
+ return;
+ }
+ _eventQueue.push(aEvent);
+ asyncCall(handleFirstEvent);
+ };
+
+ sm.getPreviousState = function() {
+ return _prevState;
+ };
+
+ sm.getCurrentState = function() {
+ return _curState;
+ };
+
+ // State object maker.
+ // @param aName string for this state's name.
+ // @param aDelegate object:
+ // .handleEvent: required.
+ // .enter: called before entering this state (optional).
+ // .exit: called before exiting this state (optional).
+ sm.makeState = function (aName, aDelegate) {
+ if (!aDelegate.handleEvent) {
+ throw "handleEvent is a required delegate function.";
+ }
+ var nop = function() {};
+ return {
+ name: aName,
+ enter: (aDelegate.enter || nop),
+ exit: (aDelegate.exit || nop),
+ handleEvent: aDelegate.handleEvent
+ };
+ };
+
+ sm.deferEvent = function (aEvent) {
+ // The definition of a 'deferred event' is:
+ // We are not able to handle this event now but after receiving
+ // certain event or entering a new state, we might be able to handle
+ // it. For example, we couldn't handle CONNECT_EVENT in the
+ // diconnecting state. But once we finish doing "disconnecting", we
+ // could then handle CONNECT_EVENT!
+ //
+ // So, the deferred event may be handled in the following cases:
+ // 1. Once we entered a new state.
+ // 2. Once we handled a regular event.
+ if (DEBUG) {
+ debug('Deferring event: ' + JSON.stringify(aEvent));
+ }
+ _deferredEventQueue.push(aEvent);
+ };
+
+ // Goto the new state. If the current state is null, the exit
+ // function won't be called.
+ sm.gotoState = function (aNewState) {
+ if (_curState) {
+ if (DEBUG) {
+ debug("exiting state: " + _curState.name);
+ }
+ _curState.exit();
+ }
+
+ _prevState = _curState;
+ _curState = aNewState;
+
+ if (DEBUG) {
+ debug("entering state: " + _curState.name);
+ }
+ _curState.enter();
+
+ // We are in the new state now. We got a chance to handle the
+ // deferred events.
+ handleDeferredEvents();
+
+ sm.resume();
+ };
+
+ // No incoming event will be handled after you call pause().
+ // (But they will be queued.)
+ sm.pause = function() {
+ _paused = true;
+ };
+
+ // Continue to handle incoming events.
+ sm.resume = function() {
+ _paused = false;
+ asyncCall(handleFirstEvent);
+ };
+
+ //----------------------------------------------------------
+ // Private stuff
+ //----------------------------------------------------------
+
+ function asyncCall(f) {
+ Services.tm.currentThread.dispatch(f, Ci.nsIThread.DISPATCH_NORMAL);
+ }
+
+ function handleFirstEvent() {
+ var hadDeferredEvents;
+
+ if (0 === _eventQueue.length) {
+ return;
+ }
+
+ if (_paused) {
+ return; // The state machine is paused now.
+ }
+
+ hadDeferredEvents = _deferredEventQueue.length > 0;
+
+ handleOneEvent(_eventQueue.shift()); // The handler may defer this event.
+
+ // We've handled one event. If we had deferred events before, now is
+ // a good chance to handle them.
+ if (hadDeferredEvents) {
+ handleDeferredEvents();
+ }
+
+ // Continue to handle the next regular event.
+ handleFirstEvent();
+ }
+
+ function handleDeferredEvents() {
+ if (_deferredEventQueue.length && DEBUG) {
+ debug('Handle deferred events: ' + _deferredEventQueue.length);
+ }
+ for (let i = 0; i < _deferredEventQueue.length; i++) {
+ handleOneEvent(_deferredEventQueue.shift());
+ }
+ }
+
+ function handleOneEvent(aEvent)
+ {
+ if (DEBUG) {
+ debug('Handling event: ' + JSON.stringify(aEvent));
+ }
+
+ var handled = _curState.handleEvent(aEvent);
+
+ if (undefined === handled) {
+ throw "handleEvent returns undefined: " + _curState.name;
+ }
+ if (!handled) {
+ // Event is not handled in the current state. Try handleEventCommon().
+ handled = (_defaultEventHandler ? _defaultEventHandler(aEvent) : handled);
+ }
+ if (undefined === handled) {
+ throw "handleEventCommon returns undefined: " + _curState.name;
+ }
+ if (!handled) {
+ if (DEBUG) {
+ debug('!!!!!!!!! FIXME !!!!!!!!! Event not handled: ' + JSON.stringify(aEvent));
+ }
+ }
+
+ return handled;
+ }
+
+ return sm;
+};