blob: 0823f29b385ae23e0ec0bc05363258ca887c8317 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/browser/accessibility/flutter/flutter_accessibility_helper_bridge.h"
#include <utility>
#include "base/logging.h"
#include "chromecast/browser/accessibility/accessibility_manager.h"
#include "chromecast/browser/accessibility/proto/gallium_server_accessibility.grpc.pb.h"
#include "chromecast/browser/cast_browser_process.h"
#include "components/exo/fullscreen_shell_surface.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "ui/accessibility/aura/aura_window_properties.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace chromecast {
namespace gallium {
namespace accessibility {
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_CUSTOM_ACTION;
using ::gallium::castos::
// NOLINTNEXTLINE(whitespace/line_length)
OnAccessibilityActionRequest_AccessibilityActionType_DID_GAIN_ACCESSIBILITY_FOCUS;
using ::gallium::castos::
// NOLINTNEXTLINE(whitespace/line_length)
OnAccessibilityActionRequest_AccessibilityActionType_DID_LOSE_ACCESSIBILITY_FOCUS;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_DOWN;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_LEFT;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_RIGHT;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_UP;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SET_SELECTION;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_SHOW_ON_SCREEN;
using ::gallium::castos::
OnAccessibilityActionRequest_AccessibilityActionType_TAP;
FlutterAccessibilityHelperBridge::FlutterAccessibilityHelperBridge(
Delegate* bridge_delegate,
content::BrowserContext* browser_context)
: tree_source_(
std::make_unique<AXTreeSourceFlutter>(this, browser_context)),
bridge_delegate_(bridge_delegate) {}
FlutterAccessibilityHelperBridge::~FlutterAccessibilityHelperBridge() = default;
void FlutterAccessibilityHelperBridge::AccessibilityStateChanged(bool value) {
tree_source_->SetAccessibilityEnabled(value);
if (value) {
aura::Window* window = chromecast::shell::CastBrowserProcess::GetInstance()
->accessibility_manager()
->window_tree_host()
->window();
// Find the full screen shell surface for the exo::Surface representing
// the ui. We must ensure our tree id is the child ax tree id for
// that view so when the root window is serialized, the flutter ax tree
// will be parented by that view.
bool found = false;
if (window) {
for (aura::Window* child : window->children()) {
exo::Surface* surface = exo::GetShellRootSurface(child);
if (surface) {
views::Widget* widget =
views::Widget::GetWidgetForNativeWindow(child);
if (widget) {
exo::FullscreenShellSurface* full_screen_shell_surface =
static_cast<exo::FullscreenShellSurface*>(
widget->widget_delegate());
full_screen_shell_surface->SetChildAxTreeId(
tree_source_->ax_tree_id());
full_screen_shell_surface->GetContentsView()
->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
false);
child->Focus();
found = true;
}
break;
}
}
}
if (!found) {
LOG(ERROR) << "Could not find full screen shell surface for ax tree.";
}
}
}
void FlutterAccessibilityHelperBridge::OnAccessibilityEventRequestInternal(
std::unique_ptr<::gallium::castos::OnAccessibilityEventRequest>
event_data) {
// Tell the tree source to serialize these changes.
tree_source_->NotifyAccessibilityEvent(event_data.get());
}
bool FlutterAccessibilityHelperBridge::OnAccessibilityEventRequest(
const ::gallium::castos::OnAccessibilityEventRequest* event_data) {
std::unique_ptr<::gallium::castos::OnAccessibilityEventRequest> event =
std::make_unique<::gallium::castos::OnAccessibilityEventRequest>(
*event_data);
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&FlutterAccessibilityHelperBridge::
OnAccessibilityEventRequestInternal,
base::Unretained(this), std::move(event)));
return true;
}
void FlutterAccessibilityHelperBridge::OnAction(const ui::AXActionData& data) {
// Called by tree source to dispatch ax action to flutter. Translate this
// to gallium accessibility proto and forward to the delegate for
// dispatching.
::gallium::castos::OnAccessibilityActionRequest request;
request.set_node_id(data.target_node_id);
switch (data.action) {
case ax::mojom::Action::kDoDefault:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_TAP);
break;
case ax::mojom::Action::kScrollToMakeVisible:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SHOW_ON_SCREEN);
break;
case ax::mojom::Action::kScrollBackward:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_LEFT);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kScrollForward:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_RIGHT);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kScrollUp:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_UP);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kScrollDown:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_DOWN);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kScrollLeft:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_LEFT);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kScrollRight:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SCROLL_RIGHT);
tree_source_->NotifyActionResult(data, false);
break;
case ax::mojom::Action::kCustomAction:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_CUSTOM_ACTION);
request.set_custom_action_id(data.custom_action_id);
break;
case ax::mojom::Action::kSetAccessibilityFocus:
request.set_action_type(
// NOLINTNEXTLINE(whitespace/line_length)
OnAccessibilityActionRequest_AccessibilityActionType_DID_GAIN_ACCESSIBILITY_FOCUS);
break;
case ax::mojom::Action::kClearAccessibilityFocus:
request.set_action_type(
// NOLINTNEXTLINE(whitespace/line_length)
OnAccessibilityActionRequest_AccessibilityActionType_DID_LOSE_ACCESSIBILITY_FOCUS);
break;
case ax::mojom::Action::kGetTextLocation:
request.set_action_type(
OnAccessibilityActionRequest_AccessibilityActionType_SET_SELECTION);
request.set_start_index(data.start_index);
request.set_end_index(data.end_index);
break;
default:
LOG(WARNING) << "Cast ax action " << data.action
<< " not mapped to flutter action - dropped.";
return;
}
bridge_delegate_->SendAccessibilityAction(request);
}
void FlutterAccessibilityHelperBridge::OnVirtualKeyboardBoundsChange(
const gfx::Rect& bounds) {
chromecast::shell::CastBrowserProcess::GetInstance()
->accessibility_manager()
->SetVirtualKeyboardBounds(bounds);
}
} // namespace accessibility
} // namespace gallium
} // namespace chromecast