Commit be9dc6dd authored by Adam Ettenberger's avatar Adam Ettenberger Committed by Commit Bot

Focus the Document Element when requested from Accessibility

With the Narrator setting "Sync the Narrator cursor and system focus"
enabled, and the user navigates from the browser UI into the document
frame, the root Document should be focused.

If IFrame content had focus before it was moved to the browser UI, when
returning to the document frame the last focused item within the IFrame
regains focus. This happens either when using the Tab key or Narrator
Item Navigation.

With Narrator Item Navigation however, the focus should be moved to the
root Document upon entering the document frame. This is because
Narrator explicitly requests focus on the item via
IRawElementProviderFragment::SetFocus (AXPlatformNodeWin::SetFocus).

This ends up calling BrowserAccessibilityManager::SetFocus which
attempts to do 2 things in this scenario :
(1) Return focus to the Accessibility View (AccessibilityViewSetFocus)
(2) Focus the target Element (AccessibilityPerformAction)

AccessibilityViewSetFocus ends up focusing the IFrame content with this
approximate stack :
AccessibilityViewSetFocus
  BrowserAccessibilityManager::OnWindowFocused
    BrowserAccessibilityManager::FireFocusEventsIfNeeded
      BrowserAccessibilityManager::GetFocus
        BrowserAccessibilityManager::GetFocusFromThisOrDescendantFrame
      BrowserAccessibilityManager::FireFocusEvent


After focus has been returned to the IFrame content, the call to
AccessibilityPerformAction should now focus the targeted element.
However this does not happen because it ends up calling
AXNodeObject::OnNativeFocusAction which only calls ClearFocusedElement
for the root document. This call does not affect the nested Frame,
and focus remains within the IFrame.

This change aims to fix this corner case, and allow focus to be set
on the requested Document.

Bug: 1064339
Change-Id: I5648483a4463ecd8e4105624c84293e0be68f8f7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2116541Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Adam Ettenberger <Adam.Ettenberger@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#753775}
parent 6a1114ce
...@@ -1161,5 +1161,59 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest, ...@@ -1161,5 +1161,59 @@ IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
EXPECT_EQ(text->GetId(), anchor_waiter.event_target_id()); EXPECT_EQ(text->GetId(), anchor_waiter.event_target_id());
} }
IN_PROC_BROWSER_TEST_F(CrossPlatformAccessibilityBrowserTest,
IFrameContentHadFocus_ThenRootDocumentGainedFocus) {
// Start by loading a document with iframes.
LoadInitialAccessibilityTreeFromHtmlFilePath(
"/accessibility/html/iframe-padding.html");
WaitForAccessibilityTreeToContainNodeWithName(shell()->web_contents(),
"Second Button");
// Get the root BrowserAccessibilityManager and BrowserAccessibility node.
BrowserAccessibilityManager* root_accessibility_manager = GetManager();
ASSERT_NE(nullptr, root_accessibility_manager);
BrowserAccessibility* root_browser_accessibility =
root_accessibility_manager->GetRoot();
ASSERT_NE(nullptr, root_browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kRootWebArea,
root_browser_accessibility->GetRole());
// Focus the button within the iframe.
{
BrowserAccessibility* leaf_iframe_browser_accessibility =
root_browser_accessibility->InternalDeepestLastChild();
ASSERT_NE(nullptr, leaf_iframe_browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kIframe,
leaf_iframe_browser_accessibility->GetRole());
BrowserAccessibility* second_iframe_root_browser_accessibility =
leaf_iframe_browser_accessibility->PlatformGetChild(0);
ASSERT_NE(nullptr, second_iframe_root_browser_accessibility);
ASSERT_EQ(ax::mojom::Role::kRootWebArea,
second_iframe_root_browser_accessibility->GetRole());
BrowserAccessibility* second_button = FindNodeByRole(
second_iframe_root_browser_accessibility, ax::mojom::Role::kButton);
ASSERT_NE(nullptr, second_button);
AccessibilityNotificationWaiter waiter(
shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kFocus);
second_iframe_root_browser_accessibility->manager()->SetFocus(
*second_button);
waiter.WaitForNotification();
ASSERT_EQ(second_button, root_accessibility_manager->GetFocus());
}
// Focusing the root Document should cause the iframe content to blur.
// The Document Element becomes implicitly focused when the focus is cleared,
// so there will not be a focus event.
{
AccessibilityNotificationWaiter waiter(
shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kBlur);
root_accessibility_manager->SetFocus(*root_browser_accessibility);
waiter.WaitForNotification();
ASSERT_EQ(root_browser_accessibility,
root_accessibility_manager->GetFocus());
}
}
#endif #endif
} // namespace content } // namespace content
...@@ -79,6 +79,7 @@ ...@@ -79,6 +79,7 @@
#include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_table.h" #include "third_party/blink/renderer/core/layout/layout_table.h"
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/svg/svg_element.h" #include "third_party/blink/renderer/core/svg/svg_element.h"
#include "third_party/blink/renderer/core/svg/svg_svg_element.h" #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
...@@ -3139,7 +3140,21 @@ bool AXNodeObject::OnNativeFocusAction() { ...@@ -3139,7 +3140,21 @@ bool AXNodeObject::OnNativeFocusAction() {
Document* document = GetDocument(); Document* document = GetDocument();
if (IsWebArea()) { if (IsWebArea()) {
document->ClearFocusedElement(); // If another Frame has focused content (e.g. nested iframe), then we
// need to clear focus for the other Document Frame.
// Here we set the focused element via the FocusController so that the
// other Frame loses focus, and the target Document Element gains focus.
// This fixes a scenario with Narrator Item Navigation when the user
// navigates from the outer UI to the document when the last focused
// element was within a nested iframe before leaving the document frame.
Page* page = document->GetPage();
// Elements inside a portal should not be focusable.
if (page && !page->InsidePortal()) {
page->GetFocusController().SetFocusedElement(document->documentElement(),
document->GetFrame());
} else {
document->ClearFocusedElement();
}
return true; return true;
} }
......
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