文档可访问性生命周期

1. DocAccessible 创建

当创建 DocAccessible 时,它最初是空的。DocAccessible 可以通过多种方式创建。

场景 1:辅助功能服务已启动,布局为新文档触发 a11y 通知

  1. 布局 初始化 PresShell (PresShell::Initialize)

  2. 作为其中的一部分,布局 从根内容对象向下插入内容

  3. 触发辅助功能插入通知 (nsAccessibilityService::ContentRangeInserted)

  4. 该通知 获取 DocAccessible (DocManager::GetDocAccessible)

  5. 由于它尚不存在, DocAccessible 被创建 (DocManager::CreateDocOrRootAccessible)

场景 2:辅助功能服务已启动,新文档的 DOM 加载完成

对于顶级内容文档,如果辅助功能服务已启动,布局应触发 a11y 通知,从而导致上述场景 1。对于子文档(例如,进程内 iframe),这可能不会发生,因为子文档的容器 Accessible 可能尚未创建。在这种情况下,我们无法在触发布局通知时创建 DocAccessible。如果在子文档的 DOM 加载完成后创建容器 Accessible,则可能发生这种情况。

  1. a11y::DocManager 收到通知,表明文档已停止加载 (DocManager::OnStateChange).

  2. 它尝试获取现有的 DocAccessible,但它尚不存在,因此它 创建 DocAccessible (DocManager::CreateDocOrRootAccessible)

场景 3:辅助功能服务已启动,在构建父文档的可访问性树时遇到子文档

此处的子文档指的是同一进程中的子文档;即进程内 iframe 或父进程文档(如 about: 页面)。请注意,场景 1 或 2 也可能适用于子文档。

  1. 在构建父文档的可访问性树时,会创建 OuterDocAccessible(例如,用于 XUL 浏览器或 iframe)。

  2. The OuterDocAccessible 构造函数获取子文档的 DocAccessible (DocManager::GetDocAccessible)

  3. 由于它尚不存在, DocAccessible 被创建 (DocManager::CreateDocOrRootAccessible)

场景 4:顶级文档加载完成后辅助功能服务启动

  1. 当辅助功能服务启动时,它 初始化 ApplicationAccessible (ApplicationAccessible::Init)

  2. 作为其中的一部分,会遍历所有文档。

  3. 对于每个顶级文档, 检索 DocAccessible (DocManager::GetDocAccessible)因此创建 (DocManager::CreateDocOrRootAccessible)

然后,场景 3 将适用于在构建这些顶级 DocAccessibles 的可访问性树时遇到的任何子文档。

场景 5:文档在布局开始前获得焦点

文档可能在布局开始前以及 DOM 加载完成前获得焦点。在这种情况下,将存在 PresShell,但它将没有根框架。尽管如此,仍然需要创建文档,否则,当文档具有 DOM 焦点时,a11y 焦点将无处可去。

  1. a11y::FocusManager 收到 DOM 焦点更改的通知 (FocusManager::NotifyOfDOMFocus).

  2. 它获取子文档的 DocAccessible (DocManager::GetDocAccessible)。

  3. 由于它尚不存在, DocAccessible 被创建 (DocManager::CreateDocOrRootAccessible)

2. 初始树创建

  1. 当创建 DocAccessible 时,它 创建刷新观察者 (NotificationController),该观察者异步执行各种处理。

  2. 当创建 NotificationController 时,它 安排在下一个可能的刷新周期进行处理

  3. 一旦发生刷新周期, 在没有进行可中断的重排的情况下 并且存在已初始化的 PresShell,DocAccessible 的 初始更新被触发 (DocAccessible::DoInitialUpdate)

  4. 对于顶级文档, 创建 DocAccessibleChild IPC Actor。请参阅下面关于 IPC Actor 创建的部分。

  5. The 遍历 DOM 树并为文档向下构建可访问性树 (DocAccessible::CacheChildrenInSubtree)

请注意,如果 PresShell 仍然没有框架,则文档可能仍然没有布局框架;请参阅上面 DocAccessible 创建中的场景 5。尽管如此,必须调用 DoInitialUpdate,否则,我们将不会创建 IPC Actor,这反过来意味着处于此状态的远程文档无法获得 a11y 焦点。

3. 子文档绑定

此处的子文档指的是同一进程中的子文档;例如,进程内 iframe 或父进程文档(如 about: 页面)。

子文档需要成为其 OuterDocAccessible 的子项;例如,iframe。但是,子文档可能在父文档准备好之前就已准备好。为了处理这种情况

  1. 当为子文档创建 DocAccessible (DocManager::CreateDocOrRootAccessible) 时,它 被安排绑定到其父项 (DocAccessible::BindChildDocument)

  2. NotificationController 在子文档绑定到其父项之前不处理任何更新

  3. 在父文档的初始树创建后,NotificationController 绑定在 1) 中安排的文档

4. IPC Actor (DocAccessibleChild/Parent) 创建

场景 1:顶级文档

  1. 作为 DocAccessible 初始更新 (DocAccessible::DoInitialUpdate) 的第一部分,如果文档是顶级文档,则它 创建 DocAccessibleChild

  2. 然后,它 向父进程发送消息以构造 DocAccessibleParent (BrowserChild::SendPDocAccessibleConstructor)

场景 2:子文档

The 子文档的 DocAccessibleChild 被创建,当子文档由 NotificationController 绑定到其父项时。还有一个 代码路径来处理 DocAccessibleChild 已创建的情况。但是,这似乎不应该发生,代码覆盖率信息表明它没有发生。

5. 文档加载事件

场景 1:DocAccessible 已创建,DOM 加载完成

  1. a11y::DocManager 收到通知,表明文档已停止加载 (DocManager::OnStateChange).

  2. 通知 DocAccessible (DocAccessible::NotifyOfLoad),传递 EVENT_DOCUMENT_LOAD_COMPLETE。

  3. That 设置 DocAccessible 上的 eDOMLoaded LoadState 和 mLoadEventType。

  4. 某些内容安排 NotificationController 处理。这可能是初始更新、插入等。

  5. 因为 DocAccessible 已标记为已加载,初始树已构建并且所有子文档都已加载, NotificationController 调用 DocAccessible::ProcessLoad

  6. ProcessLoad 触发 EVENT_DOCUMENT_LOAD_COMPLETE 事件,如 3) 中设置的那样。

场景 2:DocAccessible 在 DOM 加载完成后一段时间创建

如果辅助功能服务启动较晚,则可能发生这种情况。如果由于 PresShell 或 containre Accessible 尚未创建而无法早先创建 DocAccessible,则也可能发生这种情况。

  1. The DocAccessible 被初始化 (DocAccessible::Init)

  2. 检测到 DOM 加载已完成

  3. 作为响应,它 设置 DocAccessible 上的 eDOMLoaded 状态和 mLoadEventType

  4. 某些内容安排 NotificationController 处理。这可能是初始更新、插入等。

  5. 因为 DocAccessible 已标记为已加载,初始树已构建并且所有子文档都已加载, NotificationController 调用 DocAccessible::ProcessLoad

  6. ProcessLoad 触发 EVENT_DOCUMENT_LOAD_COMPLETE 事件,如 3) 中设置的那样。

如果 iframe 位于父进程,则抑制文档加载事件

请注意,对于直接在父进程中加载的文档,如果其父文档仍在加载中,ProcessLoad 不会为子文档触发加载事件。这是一种旧的行为,在内容进程中不起作用,并且可能在将来被移除。请参阅 错误 1700362