mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			180 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* Any copyright is dedicated to the Public Domain.
 | 
						|
 * http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
						|
"use strict";
 | 
						|
 | 
						|
var { AsyncShutdown } = ChromeUtils.importESModule(
 | 
						|
  "resource://gre/modules/AsyncShutdown.sys.mjs"
 | 
						|
);
 | 
						|
 | 
						|
var asyncShutdownService = Cc[
 | 
						|
  "@mozilla.org/async-shutdown-service;1"
 | 
						|
].getService(Ci.nsIAsyncShutdownService);
 | 
						|
 | 
						|
Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
 | 
						|
 | 
						|
/**
 | 
						|
 * Utility function used to provide the same API for various sources
 | 
						|
 * of async shutdown barriers.
 | 
						|
 *
 | 
						|
 * @param {string} kind One of
 | 
						|
 * - "phase" to test an AsyncShutdown phase;
 | 
						|
 * - "barrier" to test an instance of AsyncShutdown.Barrier;
 | 
						|
 * - "xpcom-barrier" to test an instance of nsIAsyncShutdownBarrier;
 | 
						|
 * - "xpcom-barrier-unwrapped" to test the field `jsclient` of a nsIAsyncShutdownClient.
 | 
						|
 *
 | 
						|
 * @return An object with the following methods:
 | 
						|
 *   - addBlocker() - the same method as AsyncShutdown phases and barrier clients
 | 
						|
 *   - wait() - trigger the resolution of the lock
 | 
						|
 */
 | 
						|
function makeLock(kind) {
 | 
						|
  if (kind == "phase") {
 | 
						|
    let topic = "test-Phase-" + ++makeLock.counter;
 | 
						|
    let phase = AsyncShutdown._getPhase(topic);
 | 
						|
    return {
 | 
						|
      addBlocker(...args) {
 | 
						|
        return phase.addBlocker(...args);
 | 
						|
      },
 | 
						|
      removeBlocker(blocker) {
 | 
						|
        return phase.removeBlocker(blocker);
 | 
						|
      },
 | 
						|
      wait() {
 | 
						|
        Services.obs.notifyObservers(null, topic);
 | 
						|
        return Promise.resolve();
 | 
						|
      },
 | 
						|
      get isClosed() {
 | 
						|
        return phase.isClosed;
 | 
						|
      },
 | 
						|
    };
 | 
						|
  } else if (kind == "barrier") {
 | 
						|
    let name = "test-Barrier-" + ++makeLock.counter;
 | 
						|
    let barrier = new AsyncShutdown.Barrier(name);
 | 
						|
    return {
 | 
						|
      addBlocker: barrier.client.addBlocker,
 | 
						|
      removeBlocker: barrier.client.removeBlocker,
 | 
						|
      wait() {
 | 
						|
        return barrier.wait();
 | 
						|
      },
 | 
						|
      get isClosed() {
 | 
						|
        return barrier.client.isClosed;
 | 
						|
      },
 | 
						|
    };
 | 
						|
  } else if (kind == "xpcom-barrier") {
 | 
						|
    let name = "test-xpcom-Barrier-" + ++makeLock.counter;
 | 
						|
    let barrier = asyncShutdownService.makeBarrier(name);
 | 
						|
    return {
 | 
						|
      addBlocker(blockerName, condition, state) {
 | 
						|
        if (condition == null) {
 | 
						|
          // Slight trick as `null` or `undefined` cannot be used as keys
 | 
						|
          // for `xpcomMap`. Note that this has no incidence on the result
 | 
						|
          // of the test as the XPCOM interface imposes that the condition
 | 
						|
          // is a method, so it cannot be `null`/`undefined`.
 | 
						|
          condition = "<this case can't happen with the xpcom interface>";
 | 
						|
        }
 | 
						|
        let blocker = makeLock.xpcomMap.get(condition);
 | 
						|
        if (!blocker) {
 | 
						|
          blocker = {
 | 
						|
            name: blockerName,
 | 
						|
            state,
 | 
						|
            blockShutdown(aBarrierClient) {
 | 
						|
              return (async function () {
 | 
						|
                try {
 | 
						|
                  if (typeof condition == "function") {
 | 
						|
                    await Promise.resolve(condition());
 | 
						|
                  } else {
 | 
						|
                    await Promise.resolve(condition);
 | 
						|
                  }
 | 
						|
                } finally {
 | 
						|
                  aBarrierClient.removeBlocker(blocker);
 | 
						|
                }
 | 
						|
              })();
 | 
						|
            },
 | 
						|
          };
 | 
						|
          makeLock.xpcomMap.set(condition, blocker);
 | 
						|
        }
 | 
						|
        let { fileName, lineNumber, stack } = new Error();
 | 
						|
        return barrier.client.addBlocker(blocker, fileName, lineNumber, stack);
 | 
						|
      },
 | 
						|
      removeBlocker(condition) {
 | 
						|
        let blocker = makeLock.xpcomMap.get(condition);
 | 
						|
        if (!blocker) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        barrier.client.removeBlocker(blocker);
 | 
						|
      },
 | 
						|
      wait() {
 | 
						|
        return new Promise(resolve => {
 | 
						|
          barrier.wait(resolve);
 | 
						|
        });
 | 
						|
      },
 | 
						|
      get isClosed() {
 | 
						|
        return barrier.client.isClosed;
 | 
						|
      },
 | 
						|
    };
 | 
						|
  } else if ("unwrapped-xpcom-barrier") {
 | 
						|
    let name = "unwrapped-xpcom-barrier-" + ++makeLock.counter;
 | 
						|
    let barrier = asyncShutdownService.makeBarrier(name);
 | 
						|
    let client = barrier.client.jsclient;
 | 
						|
    return {
 | 
						|
      addBlocker: client.addBlocker,
 | 
						|
      removeBlocker: client.removeBlocker,
 | 
						|
      wait() {
 | 
						|
        return new Promise(resolve => {
 | 
						|
          barrier.wait(resolve);
 | 
						|
        });
 | 
						|
      },
 | 
						|
      get isClosed() {
 | 
						|
        return client.isClosed;
 | 
						|
      },
 | 
						|
    };
 | 
						|
  }
 | 
						|
  throw new TypeError("Unknown kind " + kind);
 | 
						|
}
 | 
						|
makeLock.counter = 0;
 | 
						|
makeLock.xpcomMap = new Map(); // Note: Not a WeakMap as we wish to handle non-gc-able keys (e.g. strings)
 | 
						|
 | 
						|
/**
 | 
						|
 * An asynchronous task that takes several ticks to complete.
 | 
						|
 *
 | 
						|
 * @param {*=} resolution The value with which the resulting promise will be
 | 
						|
 * resolved once the task is complete. This may be a rejected promise,
 | 
						|
 * in which case the resulting promise will itself be rejected.
 | 
						|
 * @param {object=} outResult An object modified by side-effect during the task.
 | 
						|
 * Initially, its field |isFinished| is set to |false|. Once the task is
 | 
						|
 * complete, its field |isFinished| is set to |true|.
 | 
						|
 *
 | 
						|
 * @return {promise} A promise fulfilled once the task is complete
 | 
						|
 */
 | 
						|
function longRunningAsyncTask(resolution = undefined, outResult = {}) {
 | 
						|
  outResult.isFinished = false;
 | 
						|
  if (!("countFinished" in outResult)) {
 | 
						|
    outResult.countFinished = 0;
 | 
						|
  }
 | 
						|
  return new Promise(resolve => {
 | 
						|
    do_timeout(100, function () {
 | 
						|
      ++outResult.countFinished;
 | 
						|
      outResult.isFinished = true;
 | 
						|
      resolve(resolution);
 | 
						|
    });
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function get_exn(f) {
 | 
						|
  try {
 | 
						|
    f();
 | 
						|
    return null;
 | 
						|
  } catch (ex) {
 | 
						|
    return ex;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function do_check_exn(exn, constructor) {
 | 
						|
  Assert.notEqual(exn, null);
 | 
						|
  if (exn.name == constructor) {
 | 
						|
    Assert.equal(exn.constructor.name, constructor);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  info("Wrong error constructor");
 | 
						|
  info(exn.constructor.name);
 | 
						|
  info(exn.stack);
 | 
						|
  Assert.ok(false);
 | 
						|
}
 |