| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/accessibility/service/fake_accessibility_service.h" |
| |
| #include <tuple> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/run_loop.h" |
| #include "mojo/public/cpp/bindings/clone_traits.h" |
| #include "mojo/public/cpp/bindings/pending_associated_remote.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "services/accessibility/public/mojom/accessibility_service.mojom.h" |
| #include "services/accessibility/public/mojom/autoclick.mojom.h" |
| #include "services/accessibility/public/mojom/tts.mojom.h" |
| #include "services/accessibility/public/mojom/user_input.mojom.h" |
| #include "services/accessibility/public/mojom/user_interface.mojom.h" |
| |
| namespace ash { |
| |
| FakeAccessibilityService::FakeAccessibilityService() = default; |
| FakeAccessibilityService::~FakeAccessibilityService() = default; |
| |
| void FakeAccessibilityService::BindAccessibilityServiceClient( |
| mojo::PendingRemote<ax::mojom::AccessibilityServiceClient> |
| accessibility_service_client) { |
| accessibility_service_client_remote_.Bind( |
| std::move(accessibility_service_client)); |
| accessibility_service_client_remote_->BindAccessibilityFileLoader( |
| file_loader_remote_.BindNewPipeAndPassReceiver()); |
| } |
| |
| void FakeAccessibilityService::BindAnotherAutoclickClient() { |
| mojo::PendingReceiver<ax::mojom::AutoclickClient> autoclick_client_receiver; |
| autoclick_client_remotes_.Add( |
| autoclick_client_receiver.InitWithNewPipeAndPassRemote()); |
| accessibility_service_client_remote_->BindAutoclickClient( |
| std::move(autoclick_client_receiver)); |
| |
| // Now connect the autoclick remote in the service back to the client in the |
| // browser by getting a PendingReceiver<Autoclick> from the browser. |
| for (auto& remote : autoclick_client_remotes_) { |
| remote->BindAutoclick( |
| base::BindOnce(&FakeAccessibilityService::OnAutoclickBoundCallback, |
| base::Unretained(this))); |
| } |
| } |
| |
| void FakeAccessibilityService::BindAnotherAutomation() { |
| mojo::PendingAssociatedRemote<ax::mojom::Automation> automation_remote; |
| automation_receivers_.Add( |
| this, automation_remote.InitWithNewEndpointAndPassReceiver()); |
| accessibility_service_client_remote_->BindAutomation( |
| std::move(automation_remote)); |
| } |
| |
| void FakeAccessibilityService::BindAnotherAutomationClient() { |
| mojo::PendingReceiver<ax::mojom::AutomationClient> automation_client_receiver; |
| automation_client_remotes_.Add( |
| automation_client_receiver.InitWithNewPipeAndPassRemote()); |
| } |
| |
| void FakeAccessibilityService::BindAnotherSpeechRecognition() { |
| mojo::PendingReceiver<ax::mojom::SpeechRecognition> receiver; |
| sr_remotes_.Add(receiver.InitWithNewPipeAndPassRemote()); |
| accessibility_service_client_remote_->BindSpeechRecognition( |
| std::move(receiver)); |
| } |
| |
| void FakeAccessibilityService::BindAnotherTts() { |
| mojo::PendingReceiver<ax::mojom::Tts> tts_receiver; |
| tts_remotes_.Add(tts_receiver.InitWithNewPipeAndPassRemote()); |
| accessibility_service_client_remote_->BindTts(std::move(tts_receiver)); |
| } |
| |
| void FakeAccessibilityService::BindAnotherUserInput() { |
| mojo::PendingReceiver<ax::mojom::UserInput> ui_receiver; |
| ui_remotes_.Add(ui_receiver.InitWithNewPipeAndPassRemote()); |
| accessibility_service_client_remote_->BindUserInput(std::move(ui_receiver)); |
| } |
| |
| void FakeAccessibilityService::BindAnotherUserInterface() { |
| mojo::PendingReceiver<ax::mojom::UserInterface> ux_receiver; |
| ux_remotes_.Add(ux_receiver.InitWithNewPipeAndPassRemote()); |
| accessibility_service_client_remote_->BindUserInterface( |
| std::move(ux_receiver)); |
| } |
| |
| void FakeAccessibilityService::BindAssistiveTechnologyController( |
| mojo::PendingReceiver<ax::mojom::AssistiveTechnologyController> |
| at_controller_receiver, |
| const std::vector<ax::mojom::AssistiveTechnologyType>& enabled_features) { |
| at_controller_receivers_.Add(this, std::move(at_controller_receiver)); |
| EnableAssistiveTechnology(enabled_features); |
| } |
| |
| void FakeAccessibilityService::ConnectDevToolsAgent( |
| mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgent> agent, |
| ax::mojom::AssistiveTechnologyType type) { |
| auto it = connect_devtools_counts.find(type); |
| if (it == connect_devtools_counts.end()) { |
| connect_devtools_counts[type] = 0; |
| } |
| connect_devtools_counts[type]++; |
| } |
| |
| void FakeAccessibilityService::DispatchTreeDestroyedEvent( |
| const ui::AXTreeID& tree_id) { |
| tree_destroyed_events_.emplace_back(tree_id); |
| if (automation_events_closure_) |
| std::move(automation_events_closure_).Run(); |
| } |
| |
| void FakeAccessibilityService::DispatchActionResult( |
| const ui::AXActionData& data, |
| bool result) { |
| action_results_.emplace_back(std::make_tuple(data, result)); |
| if (automation_events_closure_) |
| std::move(automation_events_closure_).Run(); |
| } |
| |
| void FakeAccessibilityService::DispatchAccessibilityEvents( |
| const ui::AXTreeID& tree_id, |
| const std::vector<ui::AXTreeUpdate>& updates, |
| const gfx::Point& mouse_location, |
| const std::vector<ui::AXEvent>& events) { |
| accessibility_events_.emplace_back(tree_id); |
| if (automation_events_closure_) |
| std::move(automation_events_closure_).Run(); |
| } |
| |
| void FakeAccessibilityService::DispatchAccessibilityLocationChange( |
| const ui::AXTreeID& tree_id, |
| int node_id, |
| const ui::AXRelativeBounds& bounds) { |
| location_changes_.emplace_back(tree_id); |
| if (automation_events_closure_) |
| std::move(automation_events_closure_).Run(); |
| } |
| |
| void FakeAccessibilityService::DispatchGetTextLocationResult( |
| const ui::AXActionData& data, |
| const std::optional<gfx::Rect>& rect) {} |
| |
| void FakeAccessibilityService::EnableAssistiveTechnology( |
| const std::vector<ax::mojom::AssistiveTechnologyType>& enabled_features) { |
| enabled_ATs_ = std::set(enabled_features.begin(), enabled_features.end()); |
| at_change_count_++; |
| if (change_ATs_closure_ && at_change_count_ == expected_count_) { |
| expected_count_ = 0; |
| std::move(change_ATs_closure_).Run(); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestScrollableBoundsForPoint( |
| const gfx::Point& point) { |
| for (auto& remote : autoclick_client_remotes_) { |
| remote->HandleScrollableBoundsForPointFound(autoclick_scrollable_bounds_); |
| } |
| } |
| |
| void FakeAccessibilityService::WaitForATChangeCount(int count) { |
| if (count == at_change_count_) { |
| return; |
| } |
| expected_count_ = count; |
| base::RunLoop runner; |
| change_ATs_closure_ = runner.QuitClosure(); |
| runner.Run(); |
| } |
| |
| int FakeAccessibilityService::GetDevtoolsConnectionCount( |
| ax::mojom::AssistiveTechnologyType type) const { |
| auto it = connect_devtools_counts.find(type); |
| if (it == connect_devtools_counts.end()) { |
| return 0; |
| } |
| return it->second; |
| } |
| |
| bool FakeAccessibilityService::IsBound() const { |
| return accessibility_service_client_remote_.is_bound(); |
| } |
| |
| void FakeAccessibilityService::AutomationClientEnable(bool enabled) { |
| // TODO(crbug.com/1355633): Add once AutomationClient mojom is added. |
| // for (auto& automation_client : automation_client_remotes_) { |
| // enabled ? automation_client->Enable() : automation_client->Disable(); |
| // } |
| } |
| |
| void FakeAccessibilityService::WaitForAutomationEvents() { |
| base::RunLoop runner; |
| automation_events_closure_ = runner.QuitClosure(); |
| runner.Run(); |
| } |
| |
| void FakeAccessibilityService::RequestSpeechRecognitionStart( |
| ax::mojom::StartOptionsPtr options, |
| base::OnceCallback<void(ax::mojom::SpeechRecognitionStartInfoPtr)> |
| callback) { |
| CHECK_EQ(sr_remotes_.size(), 1u); |
| for (auto& remote : sr_remotes_) { |
| remote->Start(std::move(options), std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSpeechRecognitionStop( |
| ax::mojom::StopOptionsPtr options, |
| base::OnceCallback<void(const std::optional<std::string>&)> callback) { |
| CHECK_EQ(sr_remotes_.size(), 1u); |
| for (auto& remote : sr_remotes_) { |
| remote->Stop(std::move(options), std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSpeak( |
| const std::string& utterance, |
| base::OnceCallback<void(ax::mojom::TtsSpeakResultPtr)> callback) { |
| auto options = ax::mojom::TtsOptions::New(); |
| options->on_event = true; |
| RequestSpeak(utterance, std::move(options), std::move(callback)); |
| } |
| |
| void FakeAccessibilityService::RequestSpeak( |
| const std::string& utterance, |
| ax::mojom::TtsOptionsPtr options, |
| base::OnceCallback<void(ax::mojom::TtsSpeakResultPtr)> callback) { |
| CHECK_EQ(tts_remotes_.size(), 1u); |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->Speak(utterance, std::move(options), std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestStop() { |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->Stop(); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestPause() { |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->Pause(); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestResume() { |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->Resume(); |
| } |
| } |
| |
| void FakeAccessibilityService::IsTtsSpeaking( |
| base::OnceCallback<void(bool)> callback) { |
| CHECK_EQ(tts_remotes_.size(), 1u); |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->IsSpeaking(std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestTtsVoices( |
| ax::mojom::Tts::GetVoicesCallback callback) { |
| CHECK_EQ(tts_remotes_.size(), 1u); |
| for (auto& tts_client : tts_remotes_) { |
| tts_client->GetVoices(std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService:: |
| RequestSendSyntheticKeyEventForShortcutOrNavigation( |
| ax::mojom::SyntheticKeyEventPtr key_event) { |
| for (auto& ui_client : ui_remotes_) { |
| ui_client->SendSyntheticKeyEventForShortcutOrNavigation( |
| mojo::Clone(key_event)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSendSyntheticMouseEvent( |
| ax::mojom::SyntheticMouseEventPtr mouse_event) { |
| for (auto& ui_client : ui_remotes_) { |
| ui_client->SendSyntheticMouseEvent(mojo::Clone(mouse_event)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestDarkenScreen(bool darken) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->DarkenScreen(darken); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestOpenSettingsSubpage( |
| const std::string& subpage) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->OpenSettingsSubpage(subpage); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestShowConfirmationDialog( |
| const std::string& title, |
| const std::string& description, |
| const std::optional<std::string>& cancel_name, |
| ax::mojom::UserInterface::ShowConfirmationDialogCallback callback) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->ShowConfirmationDialog(title, description, cancel_name, |
| std::move(callback)); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSetFocusRings( |
| std::vector<ax::mojom::FocusRingInfoPtr> focus_rings, |
| ax::mojom::AssistiveTechnologyType at_type) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->SetFocusRings(mojo::Clone(focus_rings), at_type); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSetHighlights( |
| const std::vector<gfx::Rect>& rects, |
| SkColor color) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->SetHighlights(rects, color); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestSetVirtualKeyboardVisible( |
| bool is_visible) { |
| for (auto& ux_client : ux_remotes_) { |
| ux_client->SetVirtualKeyboardVisible(is_visible); |
| } |
| } |
| |
| void FakeAccessibilityService::RequestLoadFile( |
| base::FilePath relative_path, |
| ax::mojom::AccessibilityFileLoader::LoadCallback callback) { |
| file_loader_remote_->Load(relative_path, std::move(callback)); |
| } |
| |
| void FakeAccessibilityService::OnAutoclickBoundCallback( |
| mojo::PendingReceiver<ax::mojom::Autoclick> autoclick_receiver) { |
| autoclick_receivers_.Add(this, std::move(autoclick_receiver)); |
| } |
| |
| } // namespace ash |