Verified Commit 8708b47a authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame 🎃
Browse files

fixup! Bug 40597: Implement TorSettings module

Bug 41114: Refactor TorConnect.

Add a specialized class for every state.
Removed TorConnectStateTransitions and store the valid transitions
inside the new classes.
parent 0c5e98ab
Loading
Loading
Loading
Loading
+103 −93
Original line number Diff line number Diff line
@@ -117,52 +117,6 @@ XPCOMUtils.defineLazyGetter(
            └───────────────────────┘
*/

/* Maps allowed state transitions
   TorConnectStateTransitions[state] maps to an array of allowed states to transition to
   This is just an encoding of the above transition diagram that we verify at runtime
*/
const TorConnectStateTransitions = Object.freeze(
  new Map([
    [
      TorConnectState.Initial,
      [
        TorConnectState.Disabled,
        TorConnectState.Bootstrapping,
        TorConnectState.Configuring,
        TorConnectState.Error,
      ],
    ],
    [
      TorConnectState.Configuring,
      [
        TorConnectState.AutoBootstrapping,
        TorConnectState.Bootstrapping,
        TorConnectState.Error,
      ],
    ],
    [
      TorConnectState.AutoBootstrapping,
      [
        TorConnectState.Configuring,
        TorConnectState.Bootstrapped,
        TorConnectState.Error,
      ],
    ],
    [
      TorConnectState.Bootstrapping,
      [
        TorConnectState.Configuring,
        TorConnectState.Bootstrapped,
        TorConnectState.Error,
      ],
    ],
    [TorConnectState.Error, [TorConnectState.Configuring]],
    [TorConnectState.Bootstrapped, [TorConnectState.Configuring]],
    // terminal states
    [TorConnectState.Disabled, []],
  ])
);

/* Topics Notified by the TorConnect module */
export const TorConnectTopics = Object.freeze({
  StateChange: "torconnect:state-change",
@@ -178,9 +132,8 @@ export const TorConnectTopics = Object.freeze({
// state (for example, from Bootstrapping to Configuring in the event the user
// cancels a bootstrap attempt)
class StateCallback {
  constructor(state, callback) {
  constructor(state) {
    this._state = state;
    this._callback = callback;
    this._init();
  }

@@ -246,6 +199,23 @@ const debug_sleep = async ms => {
  });
};

// The initial state doesn't actually do anything, so here is a
// skeleton for other states which do perform work
class InitialState extends StateCallback {
  allowedTransitions = Object.freeze([
    TorConnectState.Disabled,
    TorConnectState.Bootstrapping,
    TorConnectState.Configuring,
    TorConnectState.Error,
  ]);

  _callback = initialCallback;

  constructor() {
    super(TorConnectState.Initial);
  }
}

async function initialCallback() {
  // The initial state doesn't actually do anything, so here is a skeleton for other
  // states which do perform work
@@ -280,6 +250,20 @@ async function initialCallback() {
  });
}

class ConfiguringState extends StateCallback {
  allowedTransitions = Object.freeze([
    TorConnectState.AutoBootstrapping,
    TorConnectState.Bootstrapping,
    TorConnectState.Error,
  ]);

  _callback = configuringCallback;

  constructor() {
    super(TorConnectState.Configuring);
  }
}

async function configuringCallback() {
  await new Promise(async (resolve, reject) => {
    this.on_transition = nextState => {
@@ -288,6 +272,20 @@ async function configuringCallback() {
  });
}

class BootstrappingState extends StateCallback {
  allowedTransitions = Object.freeze([
    TorConnectState.Configuring,
    TorConnectState.Bootstrapped,
    TorConnectState.Error,
  ]);

  _callback = bootstrappingCallback;

  constructor() {
    super(TorConnectState.Bootstrapping);
  }
}

async function bootstrappingCallback() {
  // wait until bootstrap completes or we get an error
  await new Promise(async (resolve, reject) => {
@@ -404,6 +402,20 @@ async function bootstrappingCallback() {
  });
}

class AutoBootstrappingState extends StateCallback {
  allowedTransitions = Object.freeze([
    TorConnectState.Configuring,
    TorConnectState.Bootstrapped,
    TorConnectState.Error,
  ]);

  _callback = autoBootstrappingCallback;

  constructor() {
    super(TorConnectState.AutoBootstrapping);
  }
}

async function autoBootstrappingCallback(countryCode) {
  await new Promise(async (resolve, reject) => {
    this.on_transition = nextState => {
@@ -621,6 +633,16 @@ async function autoBootstrappingCallback(countryCode) {
  });
}

class BootstrappedState extends StateCallback {
  allowedTransitions = Object.freeze([TorConnectState.Configuring]);

  _callback = bootstrappedCallback;

  constructor() {
    super(TorConnectState.Bootstrapped);
  }
}

async function bootstrappedCallback() {
  await new Promise((resolve, reject) => {
    // We may need to leave the bootstrapped state if the tor daemon
@@ -633,6 +655,16 @@ async function bootstrappedCallback() {
  });
}

class ErrorState extends StateCallback {
  allowedTransitions = Object.freeze([TorConnectState.Configuring]);

  _callback = errorCallback;

  constructor() {
    super(TorConnectState.Error);
  }
}

async function errorCallback(errorMessage, errorDetails, bootstrappingFailure) {
  await new Promise((resolve, reject) => {
    this.on_transition = async nextState => {
@@ -654,6 +686,16 @@ async function errorCallback(errorMessage, errorDetails, bootstrappingFailure) {
  });
}

class DisabledState extends StateCallback {
  allowedTransitions = Object.freeze([]);

  _callback = disabledCallback;

  constructor() {
    super(TorConnectState.DisabledState);
  }
}

async function disabledCallback() {
  await new Promise((resolve, reject) => {
    // no-op, on_transition not defined because no way to leave Disabled state
@@ -789,47 +831,14 @@ export const TorConnect = (() => {
           on_transition function used to resolve their Promise */
    _stateCallbacks: Object.freeze(
      new Map([
        /* Initial is never transitioned to */
        [
          TorConnectState.Initial,
          new StateCallback(TorConnectState.Initial, initialCallback),
        ],
        /* Configuring */
        [
          TorConnectState.Configuring,
          new StateCallback(TorConnectState.Configuring, configuringCallback),
        ],
        /* Bootstrapping */
        [
          TorConnectState.Bootstrapping,
          new StateCallback(
            TorConnectState.Bootstrapping,
            bootstrappingCallback
          ),
        ],
        /* AutoBootstrapping */
        [
          TorConnectState.AutoBootstrapping,
          new StateCallback(
            TorConnectState.AutoBootstrapping,
            autoBootstrappingCallback
          ),
        ],
        /* Bootstrapped */
        [
          TorConnectState.Bootstrapped,
          new StateCallback(TorConnectState.Bootstrapped, bootstrappedCallback),
        ],
        /* Error */
        [
          TorConnectState.Error,
          new StateCallback(TorConnectState.Error, errorCallback),
        ],
        /* Disabled */
        [
          TorConnectState.Disabled,
          new StateCallback(TorConnectState.Disabled, disabledCallback),
        ],
        // Initial is never transitioned to
        [TorConnectState.Initial, new InitialState()],
        [TorConnectState.Configuring, new ConfiguringState()],
        [TorConnectState.Bootstrapping, new BootstrappingState()],
        [TorConnectState.AutoBootstrapping, new AutoBootstrappingState()],
        [TorConnectState.Bootstrapped, new BootstrappedState()],
        [TorConnectState.Error, new ErrorState()],
        [TorConnectState.Disabled, new DisabledState()],
      ])
    ),

@@ -842,9 +851,10 @@ export const TorConnect = (() => {
        this._hasEverFailed = true;
      }
      const prevState = this._state;
      const prevCallback = this._callback(prevState);

      // ensure this is a valid state transition
      if (!TorConnectStateTransitions.get(prevState)?.includes(newState)) {
      if (!prevCallback?.allowedTransitions.includes(newState)) {
        throw Error(
          `TorConnect: Attempted invalid state transition from ${prevState} to ${newState}`
        );
@@ -857,7 +867,7 @@ export const TorConnect = (() => {
      this._state = newState;

      // call our state function and forward any args
      this._callback(prevState).transition(newState, ...args);
      prevCallback.transition(newState, ...args);
    },

    _updateBootstrapStatus(progress, status) {
@@ -1001,7 +1011,7 @@ export const TorConnect = (() => {
     * @param {boolean}
     */
    get canBeginBootstrap() {
      return TorConnectStateTransitions.get(this.state).includes(
      return this._callback(this.state)?.allowedTransitions.includes(
        TorConnectState.Bootstrapping
      );
    },
@@ -1014,7 +1024,7 @@ export const TorConnect = (() => {
     * @param {boolean}
     */
    get canBeginAutoBootstrap() {
      return TorConnectStateTransitions.get(this.state).includes(
      return this._callback(this.state)?.allowedTransitions.includes(
        TorConnectState.AutoBootstrapping
      );
    },