背景更新

背景更新系统的目的是在 Firefox 未运行时执行应用程序更新。它最初是在 bug 1689520 中实现的。

该系统需要处理三个主要任务

  1. 确定是否可以进行背景更新

  2. 安排后台任务

  3. 检查更新

从架构上讲,后台任务是在特殊后台模式下运行的 Firefox 实例,而不是单独的工具。这使得它能够利用 Firefox 中现有的功能,包括现有的更新代码,并且还可以通过控制和限制加载的 Firefox 部分来保持后台任务的可接受的性能特征。

本文档中的所有内容仅适用于 Microsoft Windows 系统。将来,我们希望将背景更新支持扩展到 macOS(参见 bug 1653435),但是由于不同发行版/配置之间 OS 级别的调度功能存在差异,因此不计划支持 Linux 和其他 Unix 变体。

生命周期

如果可以进行背景更新,则背景更新任务将每 7 小时(默认)调用一次。第一次调用会启动更新下载,该下载在任务退出后使用 Windows BITS 继续进行。第二次调用准备并分阶段进行更新。自从 bug 1704855 以来,第二次调用会自动重新启动并安装更新,然后检查是否有更新的更新,可能会启动另一个更新下载。然后循环继续。如果用户在此过程中的任何时候启动 Firefox,它将接管。如果在 Firefox 正确运行时调用后台更新任务,则任务将退出而不执行任何操作。

确定是否可以进行背景更新

配置

根据定义,更新 Firefox 是一个应用于 Firefox 安装的操作。但是,Firefox 配置通常通过首选项值和其他存储在 Firefox 配置文件中的文件完成,并且通常配置文件不会与安装一一对应。这就提出了一个问题,即如何管理背景更新程序之类的配置。我们通过两种不同的方式来处理这个问题。

有两个主要首选项与更新特别相关。分别是 app.update.auto,它控制是否应完全自动下载更新,即使 Firefox 正在运行,以及 app.update.background.enabled,专门控制是否使用背景更新系统。我们将这些首选项存储在更新根目录中,该目录位于每个安装位置的外部,位于任何配置文件之外。在该安装中加载的任何配置文件都可以观察和控制这些设置。

但是,还有一些其他状态必须来自配置文件,例如遥测客户端 ID 和日志记录级别设置(参见 BackgroundTasksUtils.sys.mjs)。

这意味着除了每个安装的偏好设置外,我们还需要能够识别和加载配置文件。为此,我们利用 配置文件服务 来确定如果我们正在运行正常的浏览器会话,安装的默认配置文件是什么,并且后台更新程序始终使用它。

标准

默认配置文件必须满足几个条件才能安排后台更新。在完全默认的配置中,这些混淆因素都不存在,但有些因素比较常见。有关所有详细信息,请参见 BackgroundUpdate.REASON

为了安排后台任务

  • 每个安装的 app.update.background.enabled 首选项必须为真

  • 每个安装的 app.update.auto 首选项必须为真(默认值)

  • 安装必须由安装程序可执行文件创建,而不是通过手动解压缩归档文件创建

  • 当前操作系统用户必须能够根据其文件系统权限更新安装,要么具有直接写入应用程序文件的权限,要么使用 Mozilla 维护服务(这也要求它已安装并启用,就像默认情况下一样)

  • 必须通过 app.update.BITS.enabled 启用 BITS(默认值)

  • 不得配置 Firefox 代理服务器设置(默认值)

  • app.update.langpack.enabled 必须为假,否则不得安装语言包。后台任务无法更新语言包等附加组件,因为它们安装在配置文件中,并且与安装的 Firefox 版本不完全匹配的语言包会导致 YSOD 故障(参见 bug 1647443),因此在存在语言包的情况下进行后台更新风险太大。

如果在默认配置文件未运行时更改了任何每个安装的偏好设置,则后台更新任务将在其下次计划运行期间看到已更改的偏好设置,并在适当情况下退出。此时不会取消安排后台任务;这将延迟到使用默认配置文件运行浏览器会话(后台更新任务应该能够取消安排自身,但目前我们更喜欢从单个位置处理所有安排任务的简单性)。

在默认配置文件所属的偏好设置在 Firefox 外部(例如使用文本编辑器)修改的极不寻常的情况下,后台任务通常会获取这些更改,无需任何操作,因为它会直接从配置文件中获取已更改的设置。

安排后台任务

我们使用 OS 级别的调度机制来安排命令 firefox --backgroundtask backgroundupdate 以特定频率运行。此频率由 app.update.background.interval 首选项控制,默认为 7 小时。

在 Windows 上,我们使用 任务计划程序 API;在 macOS 上,这将使用 launchd。有关特定于平台的调度详细信息,请参见 TaskScheduler.sys.mjs 模块。

这些后台任务按操作系统用户安排,并以该用户的权限运行。无论用户帐户的状态如何,都不需要请求或需要其他权限,因为我们已经验证了用户要么拥有他们需要的所有权限,要么可以使用维护服务。

调度是在 Firefox(或后台任务)本身内部完成的。为了减少共享状态,只有默认 Firefox 配置文件将与 OS 级别的任务调度机制交互。

检查更新

在验证所有先决条件并在任何先决条件不成立时立即退出后,backgroundupdate 任务然后验证它是唯一运行的 Firefox 实例(由多实例锁确定,参见 bug 1553982),因为否则执行任何更新工作都是不安全的。

然后,该任务从默认配置文件中获取配置设置,即

  • 更新特定首选项的子集,例如 app.update.log

  • 数据报告首选项,以确保任务尊重用户的选择

  • (传统) 遥测客户端 ID,以便可以将背景更新遥测与其他 Firefox 遥测相关联

后台任务为自己创建了一个不同的配置文件以加载,因为必须存在配置文件才能使它依赖的大多数 Firefox 代码正常工作。此不同的配置文件是非短暂的,即持久性的,但对用户不可见:参见 bug 1775132

在设置此配置文件并将我们需要的全部配置读取到其中后,将启动常规 UpdateService.sys.mjs 检查过程。在最大程度上,此过程与任何常规浏览会话期间发生的过程相同。

具体主题

用户界面

后台更新任务不得产生任何用户可见的界面。如果它确实做到了,那么出现的任何内容都将是*无实体的*,与任何 Firefox 使用本身无关,并且对用户来说像是从无处出现的奇怪、可怕的弹出窗口。为此,我们在从后台任务调用时禁用更新程序中的所有 UI。参见 bug 1696276

这一点还意味着我们无法从任务内部提示用户提升权限(在 Windows 上,这意味着 UAC 提示),因此我们必须确保能够在无需提升权限的情况下执行更新。在 Windows 上,默认情况下我们能够做到这一点,因为存在维护服务,但它可能被禁用或未安装,因此我们仍然需要检查。

分阶段

后台更新任务将遵循用户默认配置文件中的更新分阶段设置。默认设置是启用分阶段,因此大多数用户都会拥有它。后台更新任务会识别更新何时已分阶段进行,并尝试重新启动以完成分阶段更新。但是,后台任务并非在所有情况下都能完成分阶段更新;例如,参见 bug 1695797,我们确保后台任务在应用程序的其他实例正在运行时不会完成分阶段更新。

默认情况下启用分阶段,因为它可以显着改善浏览会话的启动时间。如果没有分阶段,在检索更新后浏览器启动将被阻止提取更新归档文件并修补每个单独的应用程序文件。分阶段会提前完成所有这些工作,因此完成更新(以及因此在启动路径中需要完成的所有工作)所需要做的就是将已修补(即已分阶段)的文件移动到适当位置,这是一个快得多且资源消耗更少的工作。