text: /* 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/. */
  
  const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
  Cu.import("resource://gre/modules/Services.jsm");
  
  const MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
  
  function AsyncRunner() {
    do_test_pending();
    do_register_cleanup((function () this.destroy()).bind(this));
  
    this._callbacks = {
      done: do_test_finished,
      error: function (err) {
        // xpcshell test functions like do_check_eq throw NS_ERROR_ABORT on
        // failure.  Ignore those so they aren't rethrown here.
        if (err !== Cr.NS_ERROR_ABORT) {
          if (err.stack) {
            err = err + " - See following stack:\n" + err.stack +
                        "\nUseless do_throw stack";
          }
          do_throw(err);
        }
      },
      consoleError: function (scriptErr) {
        // Try to ensure the error is related to the test.
        let filename = scriptErr.sourceName || scriptErr.toString() || "";
        if (filename.indexOf("/toolkit/components/social/") >= 0)
          do_throw(scriptErr);
      },
    };
    this._iteratorQueue = [];
  
    // This catches errors reported to the console, e.g., via Cu.reportError, but
    // not on the runner's stack.
    Cc["@mozilla.org/consoleservice;1"].
      getService(Ci.nsIConsoleService).
      registerListener(this);
  }
  
  AsyncRunner.prototype = {
  
    appendIterator: function appendIterator(iter) {
      this._iteratorQueue.push(iter);
    },
  
    next: function next(/* ... */) {
      if (!this._iteratorQueue.length) {
        this.destroy();
        this._callbacks.done();
        return;
      }
  
      // send() discards all arguments after the first, so there's no choice here
      // but to send only one argument to the yielder.
      let args = [arguments.length <= 1 ? arguments[0] : Array.slice(arguments)];
      try {
        var val = this._iteratorQueue[0].send.apply(this._iteratorQueue[0], args);
      }
      catch (err if err instanceof StopIteration) {
        this._iteratorQueue.shift();
        this.next();
        return;
      }
      catch (err) {
        this._callbacks.error(err);
      }
  
      // val is an iterator => prepend it to the queue and start on it
      // val is otherwise truthy => call next
      if (val) {
        if (typeof(val) != "boolean")
          this._iteratorQueue.unshift(val);
        this.next();
      }
    },
  
    destroy: function destroy() {
      Cc["@mozilla.org/consoleservice;1"].
        getService(Ci.nsIConsoleService).
        unregisterListener(this);
      this.destroy = function alreadyDestroyed() {};
    },
  
    observe: function observe(msg) {
      if (msg instanceof Ci.nsIScriptError)
        this._callbacks.consoleError(msg);
    },
  };
  