зеркало из https://github.com/mozilla/gecko-dev.git
203 строки
6.5 KiB
JavaScript
203 строки
6.5 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
|
* 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/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["BackgroundTasksManager"];
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
);
|
|
|
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
|
setTimeout: "resource://gre/modules/Timer.jsm",
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
|
let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {})
|
|
.ConsoleAPI;
|
|
let consoleOptions = {
|
|
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
|
|
// messages during development. See LOG_LEVELS in Console.jsm for details.
|
|
maxLogLevel: "error",
|
|
maxLogLevelPref: "toolkit.backgroundtasks.loglevel",
|
|
prefix: "BackgroundTasksManager",
|
|
};
|
|
return new ConsoleAPI(consoleOptions);
|
|
});
|
|
|
|
// Map resource://testing-common/ to the shared test modules directory. This is
|
|
// a transliteration of `register_modules_protocol_handler` from
|
|
// https://searchfox.org/mozilla-central/rev/f081504642a115cb8236bea4d8250e5cb0f39b02/testing/xpcshell/head.js#358-389.
|
|
function registerModulesProtocolHandler() {
|
|
let env = Cc["@mozilla.org/process/environment;1"].getService(
|
|
Ci.nsIEnvironment
|
|
);
|
|
let _TESTING_MODULES_URI = env.get("XPCSHELL_TESTING_MODULES_URI", "");
|
|
if (!_TESTING_MODULES_URI) {
|
|
return false;
|
|
}
|
|
|
|
let protocolHandler = Services.io
|
|
.getProtocolHandler("resource")
|
|
.QueryInterface(Ci.nsIResProtocolHandler);
|
|
|
|
protocolHandler.setSubstitution(
|
|
"testing-common",
|
|
Services.io.newURI(_TESTING_MODULES_URI)
|
|
);
|
|
// Log loudly so that when testing, we always actually use the
|
|
// console logging mechanism and therefore deterministically load that code.
|
|
log.error(
|
|
`Substitution set: resource://testing-common aliases ${_TESTING_MODULES_URI}`
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Find a JSM named like `backgroundtasks/BackgroundTask_${name}.jsm`,
|
|
* import it, and return the whole module.
|
|
*
|
|
* When testing, allow to load from `XPCSHELL_TESTING_MODULES_URI`,
|
|
* which is registered at `resource://testing-common`, the standard
|
|
* location for test-only modules.
|
|
*
|
|
* @return {Object} The imported module.
|
|
* @throws NS_ERROR_NOT_AVAILABLE if a background task with the given `name` is
|
|
* not found.
|
|
*/
|
|
function findBackgroundTaskModule(name) {
|
|
const subModules = [
|
|
"resource:///modules", // App-specific first.
|
|
"resource://gre/modules", // Toolkit/general second.
|
|
];
|
|
|
|
if (registerModulesProtocolHandler()) {
|
|
subModules.push("resource://testing-common"); // Test-only third.
|
|
}
|
|
|
|
for (const subModule of subModules) {
|
|
let URI = `${subModule}/backgroundtasks/BackgroundTask_${name}.jsm`;
|
|
log.debug(`Looking for background task at URI: ${URI}`);
|
|
|
|
try {
|
|
const taskModule = ChromeUtils.import(URI);
|
|
log.info(`Found background task at URI: ${URI}`);
|
|
return taskModule;
|
|
} catch (ex) {
|
|
if (ex.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|
|
|
|
log.warn(`No backgroundtask named '${name}' registered`);
|
|
throw new Components.Exception(
|
|
`No backgroundtask named '${name}' registered`,
|
|
Cr.NS_ERROR_NOT_AVAILABLE
|
|
);
|
|
}
|
|
|
|
var BackgroundTasksManager = {
|
|
async runBackgroundTaskNamed(name, commandLine) {
|
|
function addMarker(markerName) {
|
|
return ChromeUtils.addProfilerMarker(markerName, undefined, name);
|
|
}
|
|
addMarker("BackgroundTasksManager:AfterRunBackgroundTaskNamed");
|
|
|
|
log.info(
|
|
`Running background task named '${name}' (with ${commandLine.length} arguments)`
|
|
);
|
|
|
|
let exitCode = BackgroundTasksManager.EXIT_CODE.NOT_FOUND;
|
|
try {
|
|
let taskModule = findBackgroundTaskModule(name);
|
|
addMarker("BackgroundTasksManager:AfterFindRunBackgroundTask");
|
|
|
|
let timeoutSec = Services.prefs.getIntPref(
|
|
"toolkit.backgroundtasks.defaultTimeoutSec",
|
|
10 * 60
|
|
);
|
|
if (taskModule.backgroundTaskTimeoutSec) {
|
|
timeoutSec = taskModule.backgroundTaskTimeoutSec;
|
|
}
|
|
|
|
try {
|
|
exitCode = await Promise.race([
|
|
new Promise(resolve =>
|
|
setTimeout(() => {
|
|
log.error(`Background task named '${name}' timed out`);
|
|
resolve(BackgroundTasksManager.EXIT_CODE.TIMEOUT);
|
|
}, timeoutSec * 1000)
|
|
),
|
|
taskModule.runBackgroundTask(commandLine),
|
|
]);
|
|
log.info(
|
|
`Backgroundtask named '${name}' completed with exit code ${exitCode}`
|
|
);
|
|
} catch (e) {
|
|
log.error(`Backgroundtask named '${name}' threw exception`, e);
|
|
exitCode = BackgroundTasksManager.EXIT_CODE.EXCEPTION;
|
|
}
|
|
} finally {
|
|
addMarker("BackgroundTasksManager:AfterAwaitRunBackgroundTask");
|
|
|
|
log.info(`Invoking Services.startup.quit(..., ${exitCode})`);
|
|
Services.startup.quit(Ci.nsIAppStartup.eForceQuit, exitCode);
|
|
}
|
|
|
|
return exitCode;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Background tasks should standard exit code conventions where 0 denotes
|
|
* success and non-zero denotes failure and/or an error. In addition, since
|
|
* background tasks have limited channels to communicate with consumers, the
|
|
* special values `NOT_FOUND` (integer 2) and `THREW_EXCEPTION` (integer 3) are
|
|
* distinguished.
|
|
*
|
|
* If you extend this to add background task-specific exit codes, use exit codes
|
|
* greater than 10 to allow for additional shared exit codes to be added here.
|
|
* Exit codes should be between 0 and 127 to be safe across platforms.
|
|
*/
|
|
BackgroundTasksManager.EXIT_CODE = {
|
|
/**
|
|
* The task succeeded.
|
|
*
|
|
* The `runBackgroundTask(...)` promise resolved to 0.
|
|
*/
|
|
SUCCESS: 0,
|
|
|
|
/**
|
|
* The task with the specified name could not be found or imported.
|
|
*
|
|
* The corresponding `runBackgroundTask` method could not be found.
|
|
*/
|
|
NOT_FOUND: 2,
|
|
|
|
/**
|
|
* The task failed with an uncaught exception.
|
|
*
|
|
* The `runBackgroundTask(...)` promise rejected with an exception.
|
|
*/
|
|
EXCEPTION: 3,
|
|
|
|
/**
|
|
* The task took too long and timed out.
|
|
*
|
|
* The default timeout is controlled by the pref:
|
|
* "toolkit.backgroundtasks.defaultTimeoutSec", but tasks can override this
|
|
* by exporting a non-zero `backgroundTaskTimeoutSec` value.
|
|
*/
|
|
TIMEOUT: 4,
|
|
|
|
/**
|
|
* The last exit code reserved by this structure. Use codes larger than this
|
|
* code for background task-specific exit codes.
|
|
*/
|
|
LAST_RESERVED: 10,
|
|
};
|