Commit 468098d5 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Ensure that window titles are always up to date

A sample window tree might look like:
node id=116 role=window state={} parentID=108 childIds=[] className=HomeScreenContainer
node id=117 role=window state={"invisible":true} parentID=108 childIds=[] className=UnparentedControlContainer
node id=118 role=window state={} parentID=108 childIds=[128] className=Desk_Container_A
  node id=128 role=window state={} parentID=118 childIds=[129,102] name=Chrome - New Tab className=BrowserFrame
    node id=129 role=window state={} parentID=128 childIds=[130] className=NativeViewHostAuraClip
      node id=130 role=window state={} parentID=129 childIds=[131] name=New Tab className=WebContentsViewAura
        node id=131 role=window state={} parentID=130 childIds=[] name=New Tab
    node id=102 role=window state={} parentID=128 childIds=[132] name=New Tab - Google Chrome (Guest) className=Widget
      node id=132 role=window state={} parentID=102 childIds=[133] name=New Tab - Google Chrome (Guest) className=BrowserRootView
        node id=133 role=client state={} parentID=132 childIds=[134,86] name=Google Chrome className=NonClientView
          node id=134 role=titleBar state={} parentID=133 childIds=[135] className=BrowserNonClientFrameViewAsh
            node id=135 role=unknown state={} parentID=134 childIds=[136,137,138] className=FrameCaptionButtonContainerView
              node id=136 role=button state={"focusable":true} parentID=135 childIds=[] name=Minimize className=FrameCaptionButton
              node id=137 role=button state={"focusable":true} parentID=135 childIds=[] name=Maximize className=FrameCaptionButton
              node id=138 role=button state={"focusable":true} parentID=135 childIds=[] name=Close className=FrameCaptionButton
          node id=86 role=client state={} parentID=133 childIds=[88,89,139] className=BrowserView
            node id=88 role=unknown state={} parentID=86 childIds=[140,85,141] className=TopContainerView
              node id=140 role=unknown state={} parentID=88 childIds=[142] className=TabStripRegionView
                node id=142 role=tabList state={} parentID=140 childIds=[87,143] className=TabStrip
                  node id=87 role=tab state={"focusable":true,"multiselectable":true} parentID=142 childIds=[144,145] name=New Tab className=Tab
                    node id=144 role=staticText state={} parentID=87 childIds=[] name=New Tab className=Label
                    node id=145 role=button state={"focusable":true} parentID=87 childIds=[] name=Close className=TabCloseButton
                  node id=143 role=button state={"focusable":true} parentID=142 childIds=[146] name=New Tab className=NewTabButton
                    node id=146 role=unknown state={} parentID=143 childIds=[] className=View
              node id=85 role=pane state={} parentID=88 childIds=[11,12,147,84,13,14] className=ToolbarView
                node id=11 role=button state={} parentID=85 childIds=[90] name=Back className=ToolbarButton
                  node id=90 role=staticText state={} parentID=11 childIds=[] className=Label
                node id=12 role=button state={} parentID=85 childIds=[91] name=Forward className=ToolbarButton
                  node id=91 role=staticText state={} parentID=12 childIds=[] className=Label
                node id=147 role=button state={"focusable":true} parentID=85 childIds=[151] name=Reload className=ReloadButton
                  node id=151 role=staticText state={} parentID=147 childIds=[] className=Label
                node id=84 role=group state={} parentID=85 childIds=[152,16,83,153] className=LocationBarView
                  node id=152 role=unknown state={} parentID=84 childIds=[] className=FocusRing
                  node id=16 role=image state={} parentID=84 childIds=[15] name=Search icon className=IconLabelBubbleView
                    node id=15 role=alert state={"offscreen":true} parentID=16 childIds=[] className=AXVirtualView
                  node id=83 role=textField state={"editable":true,"focusable":true,"focused":true} parentID=84 childIds=[] name=Address and search bar className=OmniboxViewViews
                  node id=153 role=unknown state={} parentID=84 childIds=[] className=PageActionIconContainerView
                node id=13 role=button state={} parentID=85 childIds=[156] name=Guest className=AvatarToolbarButton
                  node id=156 role=staticText state={} parentID=13 childIds=[] name=Guest className=Label
                node id=14 role=popUpButton state={"focusable":true} parentID=85 childIds=[158] name=Chrome className=BrowserAppMenuButton
                  node id=158 role=staticText state={} parentID=14 childIds=[] className=Label
              node id=141 role=unknown state={} parentID=88 childIds=[] className=Separator
            node id=89 role=unknown state={} parentID=86 childIds=[159] className=View
              node id=159 role=webView state={"focusable":true} parentID=89 childIds=[1] childTreeID=DCF0C2E5B3BC5F0DDDF52090169AEB49 className=WebView

From the root downward, we have a tree of role=window nodes (most backed by aura::Window, and others backed by view::Widget, or views::RootView).

The issue this change addresses is that the views/aura system makes no guarantees about which of these windows gets a WindowObserver::OnWindowTitleChanged call. Namely, we do not receive it on the window backed by views::Widget.
As a result, in some instances, some of the windows (as seen in an accessibility extension) will have a stale title (see below bug).
This change also ensures that we only fire events on actual window nodes.
Bug: 1058613

Change-Id: I46b2205f6ed7df08fb09399cbc2ffff433ce1801
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2090023
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarAnastasia Helfinstein <anastasi@google.com>
Cr-Commit-Position: refs/heads/master@{#748303}
parent 60b660ab
...@@ -2449,3 +2449,37 @@ TEST_F('ChromeVoxBackgroundTest', 'NavigateOutOfMultiline', function() { ...@@ -2449,3 +2449,37 @@ TEST_F('ChromeVoxBackgroundTest', 'NavigateOutOfMultiline', function() {
.replay(); .replay();
}); });
}); });
TEST_F('ChromeVoxBackgroundTest', 'ReadWindowTitle', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
`
<p>start</p>
<button id="click"></button>
<script>
document.title = 'foo';
const button = document.getElementById('click');
button.addEventListener('click', _ => document.title = 'bar');
</script>
`,
function(root) {
const clickButtonThenReadCurrentTitle = () => {
const desktop = root.parent.root;
desktop.addEventListener(EventType.TREE_CHANGED, (evt) => {
if (evt.target.role == RoleType.WINDOW &&
/bar/.test(evt.target.name)) {
doCmd('readCurrentTitle')();
}
});
const button = root.find({role: RoleType.BUTTON});
button.doDefault();
};
mockFeedback.clearPendingOutput()
.call(doCmd('readCurrentTitle'))
.expectSpeech(/^foo/)
.call(clickButtonThenReadCurrentTitle)
.expectSpeech(/^bar/)
.replay();
});
});
...@@ -36,23 +36,28 @@ Widget* GetWidgetForWindow(aura::Window* window) { ...@@ -36,23 +36,28 @@ Widget* GetWidgetForWindow(aura::Window* window) {
return widget; return widget;
} }
// Fires location change events on a window, taking into account its // Fires |event| on the |window|, and the Widget and RootView associated with
// associated widget, that widget's root view, and descendant windows. // |window|.
void FireLocationChangesRecursively(aura::Window* window, void FireEventOnWindowChildWidgetAndRootView(aura::Window* window,
AXAuraObjCache* cache) { ax::mojom::Event event,
cache->FireEvent(cache->GetOrCreate(window), AXAuraObjCache* cache) {
ax::mojom::Event::kLocationChanged); cache->FireEvent(cache->GetOrCreate(window), event);
Widget* widget = GetWidgetForWindow(window); Widget* widget = GetWidgetForWindow(window);
if (widget) { if (widget) {
cache->FireEvent(cache->GetOrCreate(widget), cache->FireEvent(cache->GetOrCreate(widget), event);
ax::mojom::Event::kLocationChanged);
views::View* root_view = widget->GetRootView(); views::View* root_view = widget->GetRootView();
if (root_view) if (root_view)
root_view->NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, root_view->NotifyAccessibilityEvent(event, true);
true);
} }
}
// Fires location change events on a window, taking into account its
// associated widget, that widget's root view, and descendant windows.
void FireLocationChangesRecursively(aura::Window* window,
AXAuraObjCache* cache) {
FireEventOnWindowChildWidgetAndRootView(
window, ax::mojom::Event::kLocationChanged, cache);
for (auto* child : window->children()) for (auto* child : window->children())
FireLocationChangesRecursively(child, cache); FireLocationChangesRecursively(child, cache);
...@@ -186,7 +191,8 @@ void AXWindowObjWrapper::OnWindowTransformed(aura::Window* window, ...@@ -186,7 +191,8 @@ void AXWindowObjWrapper::OnWindowTransformed(aura::Window* window,
} }
void AXWindowObjWrapper::OnWindowTitleChanged(aura::Window* window) { void AXWindowObjWrapper::OnWindowTitleChanged(aura::Window* window) {
FireEvent(ax::mojom::Event::kTextChanged); FireEventOnWindowChildWidgetAndRootView(
window_, ax::mojom::Event::kTreeChanged, aura_obj_cache_);
} }
void AXWindowObjWrapper::FireEvent(ax::mojom::Event event_type) { void AXWindowObjWrapper::FireEvent(ax::mojom::Event event_type) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment