diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm')
-rw-r--r-- | toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm b/toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm new file mode 100644 index 0000000000..ca4ebaa73e --- /dev/null +++ b/toolkit/components/taskscheduler/TaskSchedulerWinImpl.jsm @@ -0,0 +1,229 @@ +/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ +/* 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"; + +var EXPORTED_SYMBOLS = ["_TaskSchedulerWinImpl"]; + +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyServiceGetters(this, { + WinTaskSvc: [ + "@mozilla.org/win-task-scheduler-service;1", + "nsIWinTaskSchedulerService", + ], + XreDirProvider: [ + "@mozilla.org/xre/directory-provider;1", + "nsIXREDirProvider", + ], +}); + +XPCOMUtils.defineLazyGlobalGetters(this, ["XMLSerializer"]); + +/** + * Task generation and management for Windows, using Task Scheduler 2.0 (taskschd). + * + * Implements the API exposed in TaskScheduler.jsm + * Not intended for external use, this is in a separate module to ship the code only + * on Windows, and to expose for testing. + */ +var _TaskSchedulerWinImpl = { + registerTask(id, command, intervalSeconds, options) { + // The folder might not yet exist. + this._createFolderIfNonexistent(); + + const xml = this._formatTaskDefinitionXML( + command, + intervalSeconds, + options + ); + const updateExisting = true; + + WinTaskSvc.registerTask( + this._taskFolderName(), + this._formatTaskName(id), + xml, + updateExisting + ); + }, + + deleteTask(id) { + WinTaskSvc.deleteTask(this._taskFolderName(), this._formatTaskName(id)); + }, + + deleteAllTasks() { + const taskFolderName = this._taskFolderName(); + + let allTasks; + try { + allTasks = WinTaskSvc.getFolderTasks(taskFolderName); + } catch (ex) { + if (ex.result == Cr.NS_ERROR_FILE_NOT_FOUND) { + // Folder doesn't exist, nothing to delete. + return; + } + throw ex; + } + + const tasksToDelete = allTasks.filter(name => this._matchAppTaskName(name)); + + for (const taskName of tasksToDelete) { + WinTaskSvc.deleteTask(taskFolderName, taskName); + } + + if (allTasks.length == tasksToDelete.length) { + // Deleted every task, remove the folder. + this._deleteFolderIfEmpty(); + } + }, + + _formatTaskDefinitionXML(command, intervalSeconds, options) { + const startTime = new Date(Date.now() + intervalSeconds * 1000); + const xmlns = "http://schemas.microsoft.com/windows/2004/02/mit/task"; + + // Fill in the constant parts of the task, and those that don't require escaping. + const docBase = `<Task xmlns="${xmlns}"> + <Triggers> + <TimeTrigger> + <StartBoundary>${startTime.toISOString()}</StartBoundary> + <Repetition> + <Interval>PT${intervalSeconds}S</Interval> + </Repetition> + </TimeTrigger> + </Triggers> + <Actions> + <Exec /> + </Actions> + <Settings> + <StartWhenAvailable>true</StartWhenAvailable> + <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> + </Settings> + <RegistrationInfo> + <Author /> + </RegistrationInfo> +</Task>`; + const doc = new DOMParser().parseFromString(docBase, "text/xml"); + + const execAction = doc.querySelector("Actions Exec"); + + const commandNode = doc.createElementNS(xmlns, "Command"); + commandNode.textContent = command; + execAction.appendChild(commandNode); + + if (options?.args) { + const args = doc.createElementNS(xmlns, "Arguments"); + args.textContent = options.args.map(this._quoteString).join(" "); + execAction.appendChild(args); + } + + if (options?.workingDirectory) { + const workingDirectory = doc.createElementNS(xmlns, "WorkingDirectory"); + workingDirectory.textContent = options.workingDirectory; + execAction.appendChild(workingDirectory); + } + + if (options?.disabled) { + const enabled = doc.createElementNS(xmlns, "Enabled"); + enabled.textContent = "false"; + doc.querySelector("Settings").appendChild(enabled); + } + + // Other settings to consider for the future: + // Idle + // Battery + // Max run time + + doc.querySelector("RegistrationInfo Author").textContent = + Services.appinfo.vendor; + + if (options?.description) { + const registrationInfo = doc.querySelector("RegistrationInfo"); + const description = doc.createElementNS(xmlns, "Description"); + description.textContent = options.description; + registrationInfo.appendChild(description); + } + + const serializer = new XMLSerializer(); + return serializer.serializeToString(doc); + }, + + _createFolderIfNonexistent() { + const { parentName, subName } = this._taskFolderNameParts(); + + try { + WinTaskSvc.createFolder(parentName, subName); + } catch (e) { + if (e.result != Cr.NS_ERROR_FILE_ALREADY_EXISTS) { + throw e; + } + } + }, + + _deleteFolderIfEmpty() { + const { parentName, subName } = this._taskFolderNameParts(); + + try { + WinTaskSvc.deleteFolder(parentName, subName); + } catch (e) { + // Missed one somehow, possibly a subfolder? + if (e.result != Cr.NS_ERROR_FILE_DIR_NOT_EMPTY) { + throw e; + } + } + }, + + /** + * Quotes a string for use as a single command argument, using Windows quoting + * conventions. + * + * copied from quoteString() in toolkit/modules/subproces/subprocess_worker_win.js + * + * + * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx + * + * @param {string} str + * The argument string to quote. + * @returns {string} + */ + _quoteString(str) { + if (!/[\s"]/.test(str)) { + return str; + } + + let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => { + if (m2) { + m2 = `\\${m2}`; + } + return `${m1}${m1}${m2}`; + }); + + return `"${escaped}"`; + }, + + _taskFolderName() { + return `\\${Services.appinfo.vendor}`; + }, + + _taskFolderNameParts() { + return { + parentName: "\\", + subName: Services.appinfo.vendor, + }; + }, + + _formatTaskName(id) { + const installHash = XreDirProvider.getInstallHash(); + return `${id} ${installHash}`; + }, + + _matchAppTaskName(name) { + const installHash = XreDirProvider.getInstallHash(); + return name.endsWith(` ${installHash}`); + }, +}; |