forked from mirrors/gecko-dev
Bug 1667276 - Part 2: Add BackgroundTasksManager to invoke task defined in JS. r=mossop
Differential Revision: https://phabricator.services.mozilla.com/D97512
This commit is contained in:
parent
93f94ef1a1
commit
02c231aa60
12 changed files with 351 additions and 5 deletions
|
|
@ -11,6 +11,11 @@ Classes = [
|
|||
'type': 'nsReadConfig',
|
||||
'headers': ['/extensions/pref/autoconfig/src/nsReadConfig.h'],
|
||||
'init_method': 'Init',
|
||||
'categories': {'pref-config-startup': 'ReadConfig Module'},
|
||||
'categories': {
|
||||
'pref-config-startup': {
|
||||
'name': 'ReadConfig Module',
|
||||
'backgroundtasks': BackgroundTasksSelector.ALL_TASKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -96,7 +96,12 @@ Classes = [
|
|||
'cid': '{fc886801-e768-11d4-9885-00c04fa0cf4b}',
|
||||
'contract_ids': ['@mozilla.org/content/document-loader-factory;1'],
|
||||
'type': 'nsIDocumentLoaderFactory',
|
||||
'categories': {'Gecko-Content-Viewers': content_types},
|
||||
'categories': {
|
||||
'Gecko-Content-Viewers': {
|
||||
'name': content_types,
|
||||
'backgroundtasks': BackgroundTasksSelector.ALL_TASKS,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
'cid': '{0ddf4df8-4dbb-4133-8b79-9afb966514f5}',
|
||||
|
|
@ -289,8 +294,14 @@ Classes = [
|
|||
'type': 'nsMixedContentBlocker',
|
||||
'headers': ['mozilla/dom/nsMixedContentBlocker.h'],
|
||||
'categories': {
|
||||
'content-policy': '@mozilla.org/mixedcontentblocker;1',
|
||||
'net-channel-event-sinks': '@mozilla.org/mixedcontentblocker;1',
|
||||
'content-policy': {
|
||||
'name': '@mozilla.org/mixedcontentblocker;1',
|
||||
'backgroundtasks': BackgroundTasksSelector.ALL_TASKS,
|
||||
},
|
||||
'net-channel-event-sinks': {
|
||||
'name': '@mozilla.org/mixedcontentblocker;1',
|
||||
'backgroundtasks': BackgroundTasksSelector.ALL_TASKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/* -*- 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 = ["runBackgroundTask"];
|
||||
|
||||
async function runBackgroundTask() {
|
||||
console.error("runBackgroundTask: exception");
|
||||
|
||||
throw new Error("test");
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* -*- 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 = ["runBackgroundTask"];
|
||||
|
||||
async function runBackgroundTask() {
|
||||
console.error("runBackgroundTask: failure");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* -*- 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 = ["runBackgroundTask"];
|
||||
|
||||
async function runBackgroundTask() {
|
||||
console.error("runBackgroundTask: success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -8,9 +8,11 @@
|
|||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIBackgroundTasks.h"
|
||||
#include "nsIBackgroundTasksManager.h"
|
||||
#include "nsICommandLine.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsImportModule.h"
|
||||
#include "nsString.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
|
|
@ -111,7 +113,14 @@ class BackgroundTasks final : public nsIBackgroundTasks {
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// For now, do nothing.
|
||||
nsCOMPtr<nsIBackgroundTasksManager> manager =
|
||||
do_ImportModule("resource://gre/modules/BackgroundTasksManager.jsm",
|
||||
"BackgroundTasksManager");
|
||||
|
||||
NS_ENSURE_TRUE(manager, NS_ERROR_FAILURE);
|
||||
|
||||
NS_ConvertASCIItoUTF16 name(task.ref().get());
|
||||
Unused << manager->RunBackgroundTaskNamed(name, aCmdLine);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
|||
134
toolkit/components/backgroundtasks/BackgroundTasksManager.jsm
Normal file
134
toolkit/components/backgroundtasks/BackgroundTasksManager.jsm
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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.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`
|
||||
* and return its `runBackgroundTask` function.
|
||||
*
|
||||
* 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 {function} `runBackgroundTask` function.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if a background task with the given `name` is
|
||||
* not found.
|
||||
*/
|
||||
function findRunBackgroundTask(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 { runBackgroundTask } = ChromeUtils.import(URI);
|
||||
log.info(`Found background task at URI: ${URI}`);
|
||||
return runBackgroundTask;
|
||||
} 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 = 2;
|
||||
try {
|
||||
let runBackgroundTask = findRunBackgroundTask(name);
|
||||
addMarker("BackgroundTasksManager:AfterFindRunBackgroundTask");
|
||||
|
||||
try {
|
||||
// TODO: timeout tasks that run too long.
|
||||
exitCode = await runBackgroundTask(commandLine);
|
||||
log.info(
|
||||
`Backgroundtask named '${name}' completed with exit code ${exitCode}`
|
||||
);
|
||||
} catch (e) {
|
||||
log.error(`Backgroundtask named '${name}' threw exception`, e);
|
||||
exitCode = 3;
|
||||
}
|
||||
} finally {
|
||||
addMarker("BackgroundTasksManager:AfterAwaitRunBackgroundTask");
|
||||
|
||||
log.info(`Invoking Services.startup.quit(..., ${exitCode})`);
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit, exitCode);
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
},
|
||||
};
|
||||
|
|
@ -22,8 +22,19 @@ XPCOM_MANIFESTS += [
|
|||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIBackgroundTasks.idl",
|
||||
"nsIBackgroundTasksManager.idl",
|
||||
]
|
||||
|
||||
XPIDL_MODULE = "toolkit_backgroundtasks"
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
"BackgroundTasksManager.jsm",
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.backgroundtasks += [
|
||||
"BackgroundTask_exception.jsm",
|
||||
"BackgroundTask_failure.jsm",
|
||||
"BackgroundTask_success.jsm",
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.ini"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsICommandLine;
|
||||
|
||||
/**
|
||||
* Import and run named backgroundtask implementations.
|
||||
*/
|
||||
[scriptable, function, uuid(4d48c536-e16f-4699-8f9c-add4f28f92f0)]
|
||||
interface nsIBackgroundTasksManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Run the named background task.
|
||||
*
|
||||
* @param aTaskName the name of the task to be run.
|
||||
* @param aCommandLine the command line of this invocation.
|
||||
*
|
||||
* This returns a promise which resolves to an integer exit code, 0 when the
|
||||
* task succeeded, >0 otherwise. The task manager will quit after this
|
||||
* promise resolves.
|
||||
*/
|
||||
void runBackgroundTaskNamed(in AString aTaskName,
|
||||
in nsICommandLine aCommandLine);
|
||||
};
|
||||
86
toolkit/components/backgroundtasks/tests/xpcshell/head.js
Normal file
86
toolkit/components/backgroundtasks/tests/xpcshell/head.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
* vim: sw=4 ts=4 sts=4 et
|
||||
* 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 { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { Subprocess } = ChromeUtils.import(
|
||||
"resource://gre/modules/Subprocess.jsm"
|
||||
);
|
||||
|
||||
function getFirefoxExecutableFilename() {
|
||||
if (AppConstants.platform === "win") {
|
||||
return AppConstants.MOZ_APP_NAME + ".exe";
|
||||
}
|
||||
return AppConstants.MOZ_APP_NAME;
|
||||
}
|
||||
|
||||
// Returns a nsIFile to the firefox.exe (really, application) executable file.
|
||||
function getFirefoxExecutableFile() {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file = Services.dirsvc.get("GreBinD", Ci.nsIFile);
|
||||
|
||||
file.append(getFirefoxExecutableFilename());
|
||||
return file;
|
||||
}
|
||||
|
||||
async function do_backgroundtask(
|
||||
task,
|
||||
options = { extraArgs: [], extraEnv: {} }
|
||||
) {
|
||||
options = Object.assign({}, options);
|
||||
options.extraArgs = options.extraArgs || [];
|
||||
options.extraEnv = options.extraEnv || {};
|
||||
|
||||
let command = getFirefoxExecutableFile().path;
|
||||
let args = ["--backgroundtask", task];
|
||||
args.push(...options.extraArgs);
|
||||
|
||||
// Ensure `resource://testing-common` gets mapped.
|
||||
let protocolHandler = Services.io
|
||||
.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
|
||||
let uri = protocolHandler.getSubstitution("testing-common");
|
||||
Assert.ok(uri, "resource://testing-common is not substituted");
|
||||
|
||||
// The equivalent of _TESTING_MODULES_DIR in xpcshell.
|
||||
options.extraEnv.XPCSHELL_TESTING_MODULES_URI = uri.spec;
|
||||
|
||||
// Now we can actually invoke the process.
|
||||
info(
|
||||
`launching child process ${command} with args: ${args} and extra environment: ${JSON.stringify(
|
||||
options.extraEnv
|
||||
)}`
|
||||
);
|
||||
let proc = await Subprocess.call({
|
||||
command,
|
||||
arguments: args,
|
||||
environment: options.extraEnv,
|
||||
environmentAppend: true,
|
||||
stderr: "stdout",
|
||||
}).then(p => {
|
||||
p.stdin.close();
|
||||
const dumpPipe = async pipe => {
|
||||
let data = await pipe.readString();
|
||||
while (data) {
|
||||
for (let line of data.split(/\r\n|\r|\n/).slice(0, -1)) {
|
||||
dump("> " + line + "\n");
|
||||
}
|
||||
data = await pipe.readString();
|
||||
}
|
||||
};
|
||||
dumpPipe(p.stdout);
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
let { exitCode } = await proc.wait();
|
||||
return exitCode;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
* vim: sw=4 ts=4 sts=4 et
|
||||
* 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/. */
|
||||
|
||||
add_task(async function test_success() {
|
||||
let exitCode = await do_backgroundtask("success");
|
||||
Assert.equal(0, exitCode);
|
||||
});
|
||||
|
||||
add_task(async function test_failure() {
|
||||
let exitCode = await do_backgroundtask("failure");
|
||||
Assert.equal(1, exitCode);
|
||||
});
|
||||
|
||||
add_task(async function test_exception() {
|
||||
let exitCode = await do_backgroundtask("exception");
|
||||
Assert.equal(3, exitCode);
|
||||
});
|
||||
|
||||
add_task(async function test_not_found() {
|
||||
let exitCode = await do_backgroundtask("not_found");
|
||||
Assert.equal(2, exitCode);
|
||||
});
|
||||
|
|
@ -7,5 +7,6 @@ skip-if = toolkit == 'android'
|
|||
support-files =
|
||||
CatBackgroundTaskRegistrationComponents.manifest
|
||||
|
||||
[test_backgroundtask_exitcodes.js]
|
||||
[test_manifest_with_backgroundtask.js]
|
||||
[test_manifest_without_backgroundtask.js]
|
||||
|
|
|
|||
Loading…
Reference in a new issue