[ATP] Adds API for AutoClick to Accessibility Service V8
chrome.accessibilityPrivate extension API has methods used
only by the autoclick feature. To support autoclick logic
running in the new Accessibility Service, this change adds
the autoclick-specific methods to chrome.accessibilityPrivate
in ATP, and plumbs those methods to the browser process.
For more about implementing the AccessibilityPrivate extension
API in ATP, see go/chromeos-atp-api-accessibilityprivate.
For more about the Accessibility Service running a11y features
in V8 (instead of the extension process), see
go/chromeos-atp-v8-design.
Test: New
AX-Relnotes: N/A
Bug: b/290971224
Change-Id: I1a3f82bf50512f5283743d49e7f57f9987245cd9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4952729
Auto-Submit: Katie Dektar <katie@chromium.org>
Commit-Queue: Katie Dektar <katie@chromium.org>
Reviewed-by: Anastasia Helfinstein <anastasi@google.com>
Commit-Queue: Anastasia Helfinstein <anastasi@google.com>
Cr-Commit-Position: refs/heads/main@{#1214507}
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index e420b54..894380fa 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -58,6 +58,8 @@
"accessibility/service/accessibility_service_client.h",
"accessibility/service/accessibility_service_devtools_delegate.cc",
"accessibility/service/accessibility_service_devtools_delegate.h",
+ "accessibility/service/autoclick_client_impl.cc",
+ "accessibility/service/autoclick_client_impl.h",
"accessibility/service/automation_client_impl.cc",
"accessibility/service/automation_client_impl.h",
"accessibility/service/speech_recognition_impl.cc",
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index 5bfaa60..69fffe4 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -901,6 +901,12 @@
if (!profile_)
return;
+ if (::features::IsAccessibilityServiceEnabled()) {
+ accessibility_service_client_->RequestScrollableBoundsForPoint(
+ point_in_screen);
+ return;
+ }
+
extensions::EventRouter* event_router =
extensions::EventRouter::Get(profile_);
auto event_args = extensions::api::accessibility_private::
diff --git a/chrome/browser/ash/accessibility/autoclick_browsertest.cc b/chrome/browser/ash/accessibility/autoclick_browsertest.cc
index a066f8c..eb0def2c 100644
--- a/chrome/browser/ash/accessibility/autoclick_browsertest.cc
+++ b/chrome/browser/ash/accessibility/autoclick_browsertest.cc
@@ -10,10 +10,12 @@
#include "ash/shell.h"
#include "base/test/bind.h"
#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/accessibility/service/accessibility_service_router_factory.h"
#include "chrome/browser/ash/accessibility/accessibility_feature_browsertest.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/autoclick_test_utils.h"
+#include "chrome/browser/ash/accessibility/service/fake_accessibility_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
@@ -24,6 +26,7 @@
#include "content/public/test/accessibility_notification_waiter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
+#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/test/event_generator.h"
#include "url/url_constants.h"
@@ -228,4 +231,81 @@
runner.Run();
}
+class AutoclickWithAccessibilityServiceTest : public AutoclickBrowserTest {
+ public:
+ AutoclickWithAccessibilityServiceTest() = default;
+ ~AutoclickWithAccessibilityServiceTest() override = default;
+ AutoclickWithAccessibilityServiceTest(
+ const AutoclickWithAccessibilityServiceTest&) = delete;
+ AutoclickWithAccessibilityServiceTest& operator=(
+ const AutoclickWithAccessibilityServiceTest&) = delete;
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ scoped_feature_list_.InitAndEnableFeature(
+ ::features::kAccessibilityService);
+ }
+
+ void SetUpOnMainThread() override {
+ AutoclickBrowserTest::SetUpOnMainThread();
+ // Replaces normal AccessibilityService with a fake one.
+ ax::AccessibilityServiceRouterFactory::GetInstanceForTest()
+ ->SetTestingFactoryAndUse(
+ ash::AccessibilityManager::Get()->profile(),
+ base::BindRepeating(&AutoclickWithAccessibilityServiceTest::
+ CreateTestAccessibilityService,
+ base::Unretained(this)));
+ }
+
+ protected:
+ // Unowned.
+ raw_ptr<FakeAccessibilityService, DanglingUntriaged | ExperimentalAsh>
+ fake_service_ = nullptr;
+
+ private:
+ std::unique_ptr<KeyedService> CreateTestAccessibilityService(
+ content::BrowserContext* context) {
+ std::unique_ptr<FakeAccessibilityService> fake_service =
+ std::make_unique<FakeAccessibilityService>();
+ fake_service_ = fake_service.get();
+ return std::move(fake_service);
+ }
+
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// TODO(b/262637071): When the AccessibilityService is on (instead of a fake),
+// check the focus ring bounds too, as autoclick JS should set these.
+IN_PROC_BROWSER_TEST_F(AutoclickWithAccessibilityServiceTest,
+ ScrollableBoundsPlumbing) {
+ const std::string kQuoteText =
+ "'Whatever you choose to do, leave tracks. That means don't do it just "
+ "for yourself. You will want to leave the world a little better for your "
+ "having lived.'";
+
+ LoadURLAndAutoclick(
+ "data:text/html;charset=utf-8,"
+ "<textarea id='test_textarea' class='scrollableField' rows='2'' "
+ "cols='20'>" +
+ kQuoteText + "</textarea>");
+ gfx::Rect bounds =
+ utils()->GetBoundsForNodeInRootByClassName("scrollableField");
+
+ fake_service_->BindAnotherAutoclickClient();
+
+ utils()->SetAutoclickEventTypeWithHover(generator(),
+ AutoclickEventType::kScroll);
+
+ fake_service_->set_autoclick_scrollable_bounds(bounds);
+ base::RunLoop waiter;
+ Shell::Get()->autoclick_controller()->SetScrollableBoundsCallbackForTesting(
+ base::BindLambdaForTesting(
+ [&waiter, &bounds](const gfx::Rect& scrollable_bounds) {
+ if (scrollable_bounds == bounds) {
+ waiter.Quit();
+ }
+ }));
+ utils()->HoverOverHtmlElement(generator(), kQuoteText, "staticText");
+ waiter.Run();
+}
+
} // namespace ash
diff --git a/chrome/browser/ash/accessibility/service/accessibility_service_client.cc b/chrome/browser/ash/accessibility/service/accessibility_service_client.cc
index 2a821d7..2392da0 100644
--- a/chrome/browser/ash/accessibility/service/accessibility_service_client.cc
+++ b/chrome/browser/ash/accessibility/service/accessibility_service_client.cc
@@ -19,6 +19,7 @@
#include "chrome/browser/accessibility/service/accessibility_service_router_factory.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/service/accessibility_service_devtools_delegate.h"
+#include "chrome/browser/ash/accessibility/service/autoclick_client_impl.h"
#include "chrome/browser/ash/accessibility/service/automation_client_impl.h"
#include "chrome/browser/ash/accessibility/service/speech_recognition_impl.h"
#include "chrome/browser/ash/accessibility/service/tts_client_impl.h"
@@ -65,6 +66,11 @@
automation_client_->Bind(std::move(automation), std::move(automation_client));
}
+void AccessibilityServiceClient::BindAutoclickClient(
+ mojo::PendingReceiver<ax::mojom::AutoclickClient> autoclick_receiver) {
+ autoclick_client_->Bind(std::move(autoclick_receiver));
+}
+
void AccessibilityServiceClient::BindSpeechRecognition(
mojo::PendingReceiver<ax::mojom::SpeechRecognition> sr_receiver) {
speech_recognition_impl_->Bind(std::move(sr_receiver));
@@ -140,8 +146,14 @@
enabled);
}
+void AccessibilityServiceClient::RequestScrollableBoundsForPoint(
+ const gfx::Point& point) {
+ autoclick_client_->RequestScrollableBoundsForPoint(point);
+}
+
void AccessibilityServiceClient::Reset() {
at_controller_.reset();
+ autoclick_client_.reset();
file_loader_.reset();
automation_client_.reset();
speech_recognition_impl_.reset();
@@ -206,6 +218,7 @@
return;
}
+ autoclick_client_ = std::make_unique<AutoclickClientImpl>();
automation_client_ = std::make_unique<AutomationClientImpl>();
speech_recognition_impl_ = std::make_unique<SpeechRecognitionImpl>(profile_);
tts_client_ = std::make_unique<TtsClientImpl>(profile_);
diff --git a/chrome/browser/ash/accessibility/service/accessibility_service_client.h b/chrome/browser/ash/accessibility/service/accessibility_service_client.h
index 7c0ad02..93620c8 100644
--- a/chrome/browser/ash/accessibility/service/accessibility_service_client.h
+++ b/chrome/browser/ash/accessibility/service/accessibility_service_client.h
@@ -20,6 +20,7 @@
#include "services/accessibility/public/mojom/speech_recognition.mojom-forward.h"
#include "services/accessibility/public/mojom/tts.mojom-forward.h"
#include "services/accessibility/public/mojom/user_interface.mojom-forward.h"
+#include "ui/gfx/geometry/point.h"
namespace content {
class BrowserContext;
@@ -27,6 +28,7 @@
}
namespace ash {
+class AutoclickClientImpl;
class AutomationClientImpl;
class SpeechRecognitionImpl;
class TtsClientImpl;
@@ -51,6 +53,8 @@
mojo::PendingAssociatedRemote<ax::mojom::Automation> automation,
mojo::PendingReceiver<ax::mojom::AutomationClient> automation_client)
override;
+ void BindAutoclickClient(mojo::PendingReceiver<ax::mojom::AutoclickClient>
+ autoclick_receiver) override;
void BindSpeechRecognition(
mojo::PendingReceiver<ax::mojom::SpeechRecognition> sr_receiver) override;
void BindTts(mojo::PendingReceiver<ax::mojom::Tts> tts_receiver) override;
@@ -73,6 +77,9 @@
void SetMagnifierEnabled(bool enabled);
void SetDictationEnabled(bool enabled);
+ // Sends information into the accessibility service.
+ void RequestScrollableBoundsForPoint(const gfx::Point& point);
+
private:
friend class AccessibilityServiceClientTest;
friend class AccessibilityManagerWithAccessibilityServiceTest;
@@ -102,6 +109,7 @@
std::unique_ptr<SpeechRecognitionImpl> speech_recognition_impl_;
std::unique_ptr<TtsClientImpl> tts_client_;
std::unique_ptr<UserInterfaceImpl> user_interface_client_;
+ std::unique_ptr<AutoclickClientImpl> autoclick_client_;
// Track the currently enabled features in case we disconnect from the service
// and need to reconnect, for example when the profile changes.
diff --git a/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
new file mode 100644
index 0000000..e17e617
--- /dev/null
+++ b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
@@ -0,0 +1,42 @@
+// Copyright 2023 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/autoclick_client_impl.h"
+
+#include "ash/public/cpp/accessibility_controller.h"
+#include "base/debug/stack_trace.h"
+#include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "services/accessibility/public/mojom/autoclick.mojom.h"
+
+namespace ash {
+
+AutoclickClientImpl::AutoclickClientImpl() = default;
+
+AutoclickClientImpl::~AutoclickClientImpl() = default;
+
+void AutoclickClientImpl::Bind(mojo::PendingReceiver<ax::mojom::AutoclickClient>
+ autoclick_client_receiver) {
+ autoclick_client_receivers_.Add(this, std::move(autoclick_client_receiver));
+}
+
+void AutoclickClientImpl::HandleScrollableBoundsForPointFound(
+ const gfx::Rect& bounds) {
+ AccessibilityController::Get()->HandleAutoclickScrollableBoundsFound(bounds);
+}
+
+void AutoclickClientImpl::BindAutoclick(BindAutoclickCallback callback) {
+ mojo::Remote<ax::mojom::Autoclick> remote;
+ std::move(callback).Run(remote.BindNewPipeAndPassReceiver());
+ autoclick_remotes_.Add(std::move(remote));
+}
+
+void AutoclickClientImpl::RequestScrollableBoundsForPoint(
+ const gfx::Point& point) {
+ for (auto& remote : autoclick_remotes_) {
+ remote->RequestScrollableBoundsForPoint(point);
+ }
+}
+
+} // namespace ash
diff --git a/chrome/browser/ash/accessibility/service/autoclick_client_impl.h b/chrome/browser/ash/accessibility/service/autoclick_client_impl.h
new file mode 100644
index 0000000..d90db381
--- /dev/null
+++ b/chrome/browser/ash/accessibility/service/autoclick_client_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_ACCESSIBILITY_SERVICE_AUTOCLICK_CLIENT_IMPL_H_
+#define CHROME_BROWSER_ASH_ACCESSIBILITY_SERVICE_AUTOCLICK_CLIENT_IMPL_H_
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
+#include "services/accessibility/public/mojom/autoclick.mojom.h"
+
+namespace ash {
+
+// The AutoclickClientImpl requests information on behalf of the
+// Autoclick feature from the Accessibility Service and sends
+// results to the Chrome OS Autoclick feature which is implemented
+// in Ash.
+class AutoclickClientImpl : public ax::mojom::AutoclickClient {
+ public:
+ AutoclickClientImpl();
+ AutoclickClientImpl(const AutoclickClientImpl&) = delete;
+ AutoclickClientImpl& operator=(const AutoclickClientImpl&) = delete;
+ ~AutoclickClientImpl() override;
+
+ void Bind(
+ mojo::PendingReceiver<ax::mojom::AutoclickClient> autoclick_receiver);
+
+ // ax::mojom::AutoclickClient:
+ void HandleScrollableBoundsForPointFound(const gfx::Rect& bounds) override;
+ void BindAutoclick(BindAutoclickCallback callback) override;
+
+ void RequestScrollableBoundsForPoint(const gfx::Point& point);
+
+ private:
+ mojo::ReceiverSet<ax::mojom::AutoclickClient> autoclick_client_receivers_;
+ mojo::RemoteSet<ax::mojom::Autoclick> autoclick_remotes_;
+};
+
+} // namespace ash
+
+#endif // CHROME_BROWSER_ASH_ACCESSIBILITY_SERVICE_AUTOCLICK_CLIENT_IMPL_H_
diff --git a/chrome/browser/ash/accessibility/service/fake_accessibility_service.cc b/chrome/browser/ash/accessibility/service/fake_accessibility_service.cc
index d3eea28..59e387a8 100644
--- a/chrome/browser/ash/accessibility/service/fake_accessibility_service.cc
+++ b/chrome/browser/ash/accessibility/service/fake_accessibility_service.cc
@@ -6,12 +6,14 @@
#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_interface.mojom.h"
@@ -55,6 +57,22 @@
accessibility_service_client_remote_->BindTts(std::move(tts_receiver));
}
+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::BindAnotherUserInterface() {
mojo::PendingReceiver<ax::mojom::UserInterface> ux_receiver;
ux_remotes_.Add(ux_receiver.InitWithNewPipeAndPassRemote());
@@ -128,6 +146,13 @@
}
}
+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;
@@ -277,4 +302,9 @@
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
diff --git a/chrome/browser/ash/accessibility/service/fake_accessibility_service.h b/chrome/browser/ash/accessibility/service/fake_accessibility_service.h
index b06bd7db..0f15c8c 100644
--- a/chrome/browser/ash/accessibility/service/fake_accessibility_service.h
+++ b/chrome/browser/ash/accessibility/service/fake_accessibility_service.h
@@ -11,15 +11,16 @@
#include "chrome/browser/accessibility/service/accessibility_service_router.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "services/accessibility/public/mojom/accessibility_service.mojom.h"
+#include "services/accessibility/public/mojom/autoclick.mojom.h"
#include "services/accessibility/public/mojom/automation.mojom.h"
#include "services/accessibility/public/mojom/file_loader.mojom.h"
#include "services/accessibility/public/mojom/speech_recognition.mojom-forward.h"
#include "services/accessibility/public/mojom/speech_recognition.mojom.h"
#include "services/accessibility/public/mojom/tts.mojom.h"
-#include "services/accessibility/public/mojom/user_interface.mojom-forward.h"
#include "services/accessibility/public/mojom/user_interface.mojom.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_event.h"
@@ -33,7 +34,8 @@
class FakeAccessibilityService
: public ax::AccessibilityServiceRouter,
public ax::mojom::Automation,
- public ax::mojom::AssistiveTechnologyController {
+ public ax::mojom::AssistiveTechnologyController,
+ public ax::mojom::Autoclick {
public:
FakeAccessibilityService();
FakeAccessibilityService(const FakeAccessibilityService&) = delete;
@@ -74,6 +76,10 @@
const std::vector<ax::mojom::AssistiveTechnologyType>& enabled_features)
override;
+ // In the service, V8 JS implements autoclick.
+ // ax::mojom::Autoclick:
+ void RequestScrollableBoundsForPoint(const gfx::Point& point) override;
+
//
// Methods for testing.
//
@@ -94,6 +100,7 @@
// Allows tests to bind APIs multiple times, mimicking multiple
// V8 instances in the service.
void BindAnotherAutomation();
+ void BindAnotherAutoclickClient();
void BindAnotherSpeechRecognition();
void BindAnotherTts();
void BindAnotherUserInterface();
@@ -187,7 +194,15 @@
base::FilePath relative_path,
ax::mojom::AccessibilityFileLoader::LoadCallback callback);
+ void set_autoclick_scrollable_bounds(const gfx::Rect& bounds) {
+ autoclick_scrollable_bounds_ = bounds;
+ }
+
private:
+ // Emulates V8 getting the autoclick receiver in the service process.
+ void OnAutoclickBoundCallback(
+ mojo::PendingReceiver<ax::mojom::Autoclick> autoclick_receiver);
+
base::OnceClosure change_ATs_closure_;
std::set<ax::mojom::AssistiveTechnologyType> enabled_ATs_;
base::OnceClosure automation_events_closure_;
@@ -197,6 +212,8 @@
std::vector<ui::AXTreeID> accessibility_events_;
std::vector<ui::AXTreeID> location_changes_;
+ gfx::Rect autoclick_scrollable_bounds_;
+
std::map<ax::mojom::AssistiveTechnologyType, int> connect_devtools_counts;
// Number of times ATs changed state.
@@ -207,6 +224,8 @@
mojo::RemoteSet<ax::mojom::AutomationClient> automation_client_remotes_;
mojo::RemoteSet<ax::mojom::SpeechRecognition> sr_remotes_;
+ mojo::RemoteSet<ax::mojom::AutoclickClient> autoclick_client_remotes_;
+ mojo::ReceiverSet<ax::mojom::Autoclick> autoclick_receivers_;
mojo::RemoteSet<ax::mojom::Tts> tts_remotes_;
mojo::RemoteSet<ax::mojom::UserInterface> ux_remotes_;
diff --git a/services/accessibility/BUILD.gn b/services/accessibility/BUILD.gn
index 5b97bc7..6460173 100644
--- a/services/accessibility/BUILD.gn
+++ b/services/accessibility/BUILD.gn
@@ -23,6 +23,8 @@
sources = [
"assistive_technology_controller_impl.cc",
"assistive_technology_controller_impl.h",
+ "features/autoclick_client_interface_binder.cc",
+ "features/autoclick_client_interface_binder.h",
"features/automation_internal_bindings.cc",
"features/automation_internal_bindings.h",
"features/bindings_isolate_holder.cc",
diff --git a/services/accessibility/assistive_technology_controller_impl.cc b/services/accessibility/assistive_technology_controller_impl.cc
index 3e4f2baf2..0d38d39 100644
--- a/services/accessibility/assistive_technology_controller_impl.cc
+++ b/services/accessibility/assistive_technology_controller_impl.cc
@@ -49,6 +49,12 @@
std::move(automation), std::move(automation_client));
}
+void AssistiveTechnologyControllerImpl::BindAutoclickClient(
+ mojo::PendingReceiver<mojom::AutoclickClient> autoclick_client) {
+ accessibility_service_client_remote_->BindAutoclickClient(
+ std::move(autoclick_client));
+}
+
void AssistiveTechnologyControllerImpl::BindSpeechRecognition(
mojo::PendingReceiver<mojom::SpeechRecognition> sr_receiver) {
accessibility_service_client_remote_->BindSpeechRecognition(
@@ -155,6 +161,9 @@
if (type == mojom::AssistiveTechnologyType::kDictation) {
manager.ConfigureSpeechRecognition(this);
}
+ if (type == mojom::AssistiveTechnologyType::kAutoClick) {
+ manager.ConfigureAutoclick(this);
+ }
// TODO(b/262637071): Configure other bindings based on the type
// once they are implemented.
diff --git a/services/accessibility/assistive_technology_controller_impl.h b/services/accessibility/assistive_technology_controller_impl.h
index 63f28fca..4e1e165 100644
--- a/services/accessibility/assistive_technology_controller_impl.h
+++ b/services/accessibility/assistive_technology_controller_impl.h
@@ -14,6 +14,7 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/accessibility/public/mojom/accessibility_service.mojom.h"
+#include "services/accessibility/public/mojom/autoclick.mojom-forward.h"
#include "services/accessibility/public/mojom/file_loader.mojom.h"
#include "services/accessibility/public/mojom/user_interface.mojom-forward.h"
@@ -50,6 +51,8 @@
mojo::PendingAssociatedRemote<mojom::Automation> automation,
mojo::PendingReceiver<mojom::AutomationClient> automation_client)
override;
+ void BindAutoclickClient(
+ mojo::PendingReceiver<mojom::AutoclickClient> autoclick_client) override;
void BindSpeechRecognition(
mojo::PendingReceiver<mojom::SpeechRecognition> sr_receiver) override;
void BindTts(mojo::PendingReceiver<mojom::Tts> tts_receiver) override;
diff --git a/services/accessibility/fake_service_client.cc b/services/accessibility/fake_service_client.cc
index d17efbc6..63d040f 100644
--- a/services/accessibility/fake_service_client.cc
+++ b/services/accessibility/fake_service_client.cc
@@ -25,6 +25,11 @@
}
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
+void FakeServiceClient::BindAutoclickClient(
+ mojo::PendingReceiver<ax::mojom::AutoclickClient>
+ autoclick_client_reciever) {
+ autoclick_client_recievers_.Add(this, std::move(autoclick_client_reciever));
+}
void FakeServiceClient::BindSpeechRecognition(
mojo::PendingReceiver<ax::mojom::SpeechRecognition> sr_receiver) {
sr_receivers_.Add(this, std::move(sr_receiver));
@@ -40,6 +45,17 @@
ux_receivers_.Add(this, std::move(ux_receiver));
}
+void FakeServiceClient::HandleScrollableBoundsForPointFound(
+ const gfx::Rect& bounds) {
+ if (scrollable_bounds_for_point_callback_) {
+ scrollable_bounds_for_point_callback_.Run(bounds);
+ }
+}
+
+void FakeServiceClient::BindAutoclick(BindAutoclickCallback callback) {
+ std::move(callback).Run(autoclick_remote_.BindNewPipeAndPassReceiver());
+}
+
void FakeServiceClient::Start(ax::mojom::StartOptionsPtr options,
StartCallback callback) {
auto info = mojom::SpeechRecognitionStartInfo::New();
@@ -192,6 +208,16 @@
}
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
+void FakeServiceClient::RequestScrollableBoundsForPoint(
+ const gfx::Point& point) {
+ autoclick_remote_->RequestScrollableBoundsForPoint(point);
+}
+
+void FakeServiceClient::SetScrollableBoundsForPointFoundCallback(
+ base::RepeatingCallback<void(const gfx::Rect&)> callback) {
+ scrollable_bounds_for_point_callback_ = std::move(callback);
+}
+
void FakeServiceClient::SetSpeechRecognitionStartCallback(
base::RepeatingCallback<void()> callback) {
speech_recognition_start_callback_ = std::move(callback);
diff --git a/services/accessibility/fake_service_client.h b/services/accessibility/fake_service_client.h
index 1c6a6e0..5cc32d9 100644
--- a/services/accessibility/fake_service_client.h
+++ b/services/accessibility/fake_service_client.h
@@ -18,6 +18,7 @@
#include "services/accessibility/public/mojom/automation.mojom.h"
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
+#include "services/accessibility/public/mojom/autoclick.mojom.h"
#include "services/accessibility/public/mojom/file_loader.mojom.h"
#include "services/accessibility/public/mojom/speech_recognition.mojom.h"
#include "services/accessibility/public/mojom/tts.mojom.h"
@@ -34,6 +35,7 @@
class FakeServiceClient : public mojom::AccessibilityServiceClient,
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
public mojom::AccessibilityFileLoader,
+ public mojom::AutoclickClient,
public mojom::SpeechRecognition,
public mojom::Tts,
public mojom::UserInterface,
@@ -55,12 +57,18 @@
void BindAccessibilityFileLoader(
mojo::PendingReceiver<ax::mojom::AccessibilityFileLoader>
file_loader_receiver) override;
+ void BindAutoclickClient(mojo::PendingReceiver<ax::mojom::AutoclickClient>
+ autoclick_client_reciever) override;
void BindSpeechRecognition(
mojo::PendingReceiver<ax::mojom::SpeechRecognition> sr_receiver) override;
void BindTts(mojo::PendingReceiver<ax::mojom::Tts> tts_receiver) override;
void BindUserInterface(
mojo::PendingReceiver<ax::mojom::UserInterface> ux_receiver) override;
+ // ax::mojom::AutoclickClient:
+ void HandleScrollableBoundsForPointFound(const gfx::Rect& bounds) override;
+ void BindAutoclick(BindAutoclickCallback callback) override;
+
// ax::mojom::SpeechRecognition:
void Start(ax::mojom::StartOptionsPtr options,
StartCallback callback) override;
@@ -96,6 +104,10 @@
bool AutomationIsBound() const;
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
+ void RequestScrollableBoundsForPoint(const gfx::Point& point);
+ void SetScrollableBoundsForPointFoundCallback(
+ base::RepeatingCallback<void(const gfx::Rect&)> callback);
+
void SetSpeechRecognitionStartCallback(
base::RepeatingCallback<void()> callback);
void SendSpeechRecognitionStopEvent();
@@ -131,6 +143,11 @@
mojo::AssociatedRemoteSet<mojom::Automation> automation_remotes_;
mojo::ReceiverSet<mojom::AutomationClient> automation_client_receivers_;
#if BUILDFLAG(SUPPORTS_OS_ACCESSIBILITY_SERVICE)
+ mojo::ReceiverSet<ax::mojom::AutoclickClient> autoclick_client_recievers_;
+ mojo::Remote<ax::mojom::Autoclick> autoclick_remote_;
+ base::RepeatingCallback<void(const gfx::Rect&)>
+ scrollable_bounds_for_point_callback_;
+
mojo::ReceiverSet<mojom::SpeechRecognition> sr_receivers_;
mojo::Remote<ax::mojom::SpeechRecognitionEventObserver> sr_event_observer_;
base::RepeatingCallback<void()> speech_recognition_start_callback_;
diff --git a/services/accessibility/features/atp_js_api_test.cc b/services/accessibility/features/atp_js_api_test.cc
index 1f578d9..a753c1c 100644
--- a/services/accessibility/features/atp_js_api_test.cc
+++ b/services/accessibility/features/atp_js_api_test.cc
@@ -519,6 +519,7 @@
"services/accessibility/public/mojom/"
"assistive_technology_type.mojom-lite.js",
"services/accessibility/public/mojom/user_interface.mojom-lite.js",
+ "services/accessibility/features/javascript/chrome_event.js",
"services/accessibility/features/javascript/accessibility_private.js",
};
}
@@ -703,6 +704,55 @@
waiter.Run();
}
+class AutoclickA11yPrivateJSApiTest : public AtpJSApiTest {
+ public:
+ AutoclickA11yPrivateJSApiTest() = default;
+ AutoclickA11yPrivateJSApiTest(const AutoclickA11yPrivateJSApiTest&) = delete;
+ AutoclickA11yPrivateJSApiTest& operator=(
+ const AutoclickA11yPrivateJSApiTest&) = delete;
+ ~AutoclickA11yPrivateJSApiTest() override = default;
+
+ mojom::AssistiveTechnologyType GetATTypeForTest() const override {
+ return mojom::AssistiveTechnologyType::kAutoClick;
+ }
+
+ const std::vector<std::string> GetJSFilePathsToLoad() const override {
+ return std::vector<std::string>{
+ "services/accessibility/features/mojo/test/mojom_test_support.js",
+ "ui/gfx/geometry/mojom/geometry.mojom-lite.js",
+ "services/accessibility/public/mojom/autoclick.mojom-lite.js",
+ "services/accessibility/features/javascript/chrome_event.js",
+ "services/accessibility/features/javascript/accessibility_private.js",
+ };
+ }
+};
+
+TEST_F(AutoclickA11yPrivateJSApiTest, AutoclickApis) {
+ base::RunLoop waiter;
+ client_->SetScrollableBoundsForPointFoundCallback(
+ base::BindLambdaForTesting([&waiter](const gfx::Rect& rect) {
+ waiter.Quit();
+ ASSERT_EQ(rect, gfx::Rect(2, 4, 6, 8));
+ }));
+ ExecuteJS(R"JS(
+ const remote = axtest.mojom.TestBindingInterface.getRemote();
+ chrome.accessibilityPrivate.onScrollableBoundsForPointRequested.addListener(
+ (point) => {
+ if (point.x !== 42 || point.y !== 84) {
+ remote.testComplete(/*success=*/false);
+ }
+ const rect = {left: 2, top: 4, width: 6, height: 8};
+ chrome.accessibilityPrivate.handleScrollableBoundsForPointFound(rect);
+ });
+ // Exit the JS portion of the test; the callback created above will
+ // run after the test C++ executes RequestScrollableBoundsForPoint.
+ remote.testComplete(/*success=*/true);
+ )JS");
+ WaitForJSTestComplete();
+ client_->RequestScrollableBoundsForPoint(gfx::Point(42, 84));
+ waiter.Run();
+}
+
TEST_F(AccessibilityPrivateJSApiTest, SetVirtualKeyboardVisible) {
base::RunLoop waiter;
client_->SetVirtualKeyboardVisibleCallback(
diff --git a/services/accessibility/features/autoclick_client_interface_binder.cc b/services/accessibility/features/autoclick_client_interface_binder.cc
new file mode 100644
index 0000000..19b6347
--- /dev/null
+++ b/services/accessibility/features/autoclick_client_interface_binder.cc
@@ -0,0 +1,29 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/accessibility/features/autoclick_client_interface_binder.h"
+
+#include "services/accessibility/public/mojom/accessibility_service.mojom.h"
+#include "services/accessibility/public/mojom/autoclick.mojom.h"
+
+namespace ax {
+
+AutoclickClientInterfaceBinder::AutoclickClientInterfaceBinder(
+ mojom::AccessibilityServiceClient* ax_service_client)
+ : ax_service_client_(ax_service_client) {}
+
+AutoclickClientInterfaceBinder::~AutoclickClientInterfaceBinder() = default;
+
+bool AutoclickClientInterfaceBinder::MatchesInterface(
+ const std::string& interface_name) {
+ return interface_name == "ax.mojom.AutoclickClient";
+}
+
+void AutoclickClientInterfaceBinder::BindReceiver(
+ mojo::GenericPendingReceiver autoclick_receiver) {
+ ax_service_client_->BindAutoclickClient(
+ autoclick_receiver.As<ax::mojom::AutoclickClient>());
+}
+
+} // namespace ax
diff --git a/services/accessibility/features/autoclick_client_interface_binder.h b/services/accessibility/features/autoclick_client_interface_binder.h
new file mode 100644
index 0000000..b97902a4
--- /dev/null
+++ b/services/accessibility/features/autoclick_client_interface_binder.h
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_ACCESSIBILITY_FEATURES_AUTOCLICK_CLIENT_INTERFACE_BINDER_H_
+#define SERVICES_ACCESSIBILITY_FEATURES_AUTOCLICK_CLIENT_INTERFACE_BINDER_H_
+
+#include "services/accessibility/features/interface_binder.h"
+#include "services/accessibility/public/mojom/accessibility_service.mojom.h"
+
+namespace ax {
+
+// Binds one end of a mojom AutoclickClient pipe hosted in Javascript to the
+// AccessibilityServiceClient that connects back to the main OS process.
+class AutoclickClientInterfaceBinder : public InterfaceBinder {
+ public:
+ explicit AutoclickClientInterfaceBinder(
+ mojom::AccessibilityServiceClient* ax_service_client);
+ ~AutoclickClientInterfaceBinder() override;
+ AutoclickClientInterfaceBinder(const AutoclickClientInterfaceBinder&) =
+ delete;
+ AutoclickClientInterfaceBinder& operator=(
+ const AutoclickClientInterfaceBinder&) = delete;
+
+ // InterfaceBinder:
+ bool MatchesInterface(const std::string& interface_name) override;
+ void BindReceiver(mojo::GenericPendingReceiver receiver) override;
+
+ private:
+ // The caller must ensure the client outlives `this`. Here, this is guaranteed
+ // because the client is always a `AssistiveTechnologyControllerImpl`, which
+ // transitively owns `this` via `V8Manager`.
+ raw_ptr<mojom::AccessibilityServiceClient> ax_service_client_;
+};
+
+} // namespace ax
+
+#endif // SERVICES_ACCESSIBILITY_FEATURES_AUTOCLICK_CLIENT_INTERFACE_BINDER_H_
diff --git a/services/accessibility/features/javascript/accessibility_private.js b/services/accessibility/features/javascript/accessibility_private.js
index e4618817..d14f5f3 100644
--- a/services/accessibility/features/javascript/accessibility_private.js
+++ b/services/accessibility/features/javascript/accessibility_private.js
@@ -2,6 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+/**
+ * One AutoclickObserver is created the first time
+ * chrome.accessibilityPrivate.onScrollableBoundsForPointRequested is
+ * called. This allows the browser to send events to
+ * accessibility service JS.
+ * @implements {ax.mojom.AutoclickInterface}
+ */
+class AutoclickObserver {
+ constructor(pendingReceiver, callback) {
+ this.receiver_ = new ax.mojom.AutoclickReceiver(this);
+ this.receiver_.$.bindHandle(pendingReceiver.handle);
+ this.callback_ = callback;
+ }
+
+ /** @override */
+ requestScrollableBoundsForPoint(point) {
+ if (this.callback_) {
+ this.callback_(point);
+ }
+ }
+}
// Massages some mojo interfaces into the chrome.accessibilityPrivate extension
// API surface used by a11y component extensions.
@@ -40,8 +61,61 @@
constructor() {
// This is a singleton.
console.assert(!chrome.accessibilityPrivate);
- const UserInterfaceApi = ax.mojom.UserInterface;
- this.userInterfaceRemote_ = UserInterfaceApi.getRemote();
+
+ // Create AccessibilityPrivate's ChromeEvents.
+
+ /** @public {ChromeEvent} */
+ this.onScrollableBoundsForPointRequested = new ChromeEvent(() => {
+ // Construct remote and Autoclick receiver on-demand.
+ // This way other users of AccessibilityPrivate that do not need
+ // Autoclick do not need to try to access ax.mojom.Autoclick*,
+ // meaning we do not need to import the generated JS mojom bindings
+ // when they are not used.
+ if (!this.autoclickRemote_) {
+ this.autoclickRemote_ = ax.mojom.AutoclickClient.getRemote();
+ this.autoclickRemote_.bindAutoclick().then(bindAutoclickResult => {
+ if (!bindAutoclickResult.autoclickReceiver) {
+ console.error('autoclickReceiver was unexpectedly missing');
+ return;
+ }
+ const autoclickObserver = new AutoclickObserver(
+ bindAutoclickResult.autoclickReceiver, (point) => {
+ this.onScrollableBoundsForPointRequested.callListeners(point);
+ });
+ });
+ }
+ });
+
+ // Private members.
+
+ this.userInterfaceRemote_ = null;
+ this.autoclickRemote_ = null;
+ }
+
+ /**
+ * Load user interface remote on-demand. Not every consumer of
+ * AccessibilityPrivate will need this; this way we don't need
+ * to load the generated JS bindings for UserInterface for
+ * every consumer.
+ * @return {!ax.mojom.UserInterfaceRemote}
+ */
+ getUserInterfaceRemote_() {
+ if (!this.userInterfaceRemote_) {
+ this.userInterfaceRemote_ = ax.mojom.UserInterface.getRemote();
+ }
+ return this.userInterfaceRemote_;
+ }
+
+ /**
+ * Called by Autoclick JS when onScrollableBoundsForPointRequested has found a
+ * scrolling container. `rect` will be the bounds of the nearest scrollable
+ * ancestor of the node at the point requested using
+ * onScrollableBoundsForPointRequested.
+ * @param {chrome.accessibilityPrivate.ScreenRect} rect
+ */
+ handleScrollableBoundsForPointFound(rect) {
+ this.autoclickRemote_.handleScrollableBoundsForPointFound(
+ AtpAccessibilityPrivate.convertRectToMojom_(rect));
}
/**
@@ -49,7 +123,7 @@
* @param {boolean} darken
*/
darkenScreen(darken) {
- this.userInterfaceRemote_.darkenScreen(darken);
+ this.getUserInterfaceRemote_().darkenScreen(darken);
}
/**
@@ -59,7 +133,7 @@
* @param {string} subpage
*/
openSettingsSubpage(subpage) {
- this.userInterfaceRemote_.openSettingsSubpage(subpage);
+ this.getUserInterfaceRemote_().openSettingsSubpage(subpage);
}
/**
@@ -151,7 +225,7 @@
}
mojomFocusRings.push(mojomFocusRing)
}
- this.userInterfaceRemote_.setFocusRings(mojomFocusRings, mojomAtType);
+ this.getUserInterfaceRemote_().setFocusRings(mojomFocusRings, mojomAtType);
}
/**
@@ -162,7 +236,7 @@
setHighlights(rects, color) {
const mojomColor = AtpAccessibilityPrivate.convertColorToMojom_(color);
const mojomRects = AtpAccessibilityPrivate.convertRectsToMojom_(rects);
- this.userInterfaceRemote_.setHighlights(mojomRects, mojomColor);
+ this.getUserInterfaceRemote_().setHighlights(mojomRects, mojomColor);
}
/**
@@ -170,7 +244,7 @@
* @param {boolean} is_visible
*/
setVirtualKeyboardVisible(is_visible) {
- this.userInterfaceRemote_.setVirtualKeyboardVisible(is_visible);
+ this.getUserInterfaceRemote_().setVirtualKeyboardVisible(is_visible);
}
/**
@@ -182,17 +256,27 @@
static convertRectsToMojom_(rects) {
let mojomRects = [];
for (const rect of rects) {
- let mojomRect = new gfx.mojom.Rect();
- mojomRect.x = rect.left;
- mojomRect.y = rect.top;
- mojomRect.width = rect.width;
- mojomRect.height = rect.height;
- mojomRects.push(mojomRect);
+ mojomRects.push(AtpAccessibilityPrivate.convertRectToMojom_(rect));
}
return mojomRects;
}
/**
+ * Convert an accessibilityPrivate.ScreenRect to gfx.mojom.Rect.
+ * @param {chrome.accessibilityPrivate.ScreenRect} rect
+ * @return {gfx.mojom.Rect}
+ * @private
+ */
+ static convertRectToMojom_(rect) {
+ let mojomRect = new gfx.mojom.Rect();
+ mojomRect.x = rect.left;
+ mojomRect.y = rect.top;
+ mojomRect.width = rect.width;
+ mojomRect.height = rect.height;
+ return mojomRect;
+ }
+
+ /**
* Converts a hex string to SkColor object.
* @param {string} colorString
* @return {skia.mojom.SkColor}
diff --git a/services/accessibility/features/v8_manager.cc b/services/accessibility/features/v8_manager.cc
index 8acf5fe..94fbc26 100644
--- a/services/accessibility/features/v8_manager.cc
+++ b/services/accessibility/features/v8_manager.cc
@@ -18,6 +18,7 @@
#include "gin/public/isolate_holder.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "services/accessibility/assistive_technology_controller_impl.h"
+#include "services/accessibility/features/autoclick_client_interface_binder.h"
#include "services/accessibility/features/automation_internal_bindings.h"
#include "services/accessibility/features/devtools/os_devtools_agent.h"
#include "services/accessibility/features/interface_binder.h"
@@ -238,6 +239,15 @@
.WithArgs(std::move(automation), std::move(automation_client));
}
+void V8Manager::ConfigureAutoclick(
+ mojom::AccessibilityServiceClient* ax_service_client) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // TODO(b/262637071): load the Autoclick JS shim into V8 using
+ // v8_env_.AsyncCall.
+ interface_binders_.push_back(
+ std::make_unique<AutoclickClientInterfaceBinder>(ax_service_client));
+}
+
void V8Manager::ConfigureSpeechRecognition(
mojom::AccessibilityServiceClient* ax_service_client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/services/accessibility/features/v8_manager.h b/services/accessibility/features/v8_manager.h
index 8fe3665..bcd59be 100644
--- a/services/accessibility/features/v8_manager.h
+++ b/services/accessibility/features/v8_manager.h
@@ -132,6 +132,7 @@
void ConfigureAutomation(
mojo::PendingAssociatedReceiver<mojom::Automation> automation,
mojo::PendingRemote<mojom::AutomationClient> automation_client);
+ void ConfigureAutoclick(mojom::AccessibilityServiceClient* ax_service_client);
void ConfigureSpeechRecognition(
mojom::AccessibilityServiceClient* ax_service_client);
void ConfigureTts(mojom::AccessibilityServiceClient* ax_service_client);
diff --git a/services/accessibility/public/mojom/BUILD.gn b/services/accessibility/public/mojom/BUILD.gn
index 9c61211f..5637a8b4 100644
--- a/services/accessibility/public/mojom/BUILD.gn
+++ b/services/accessibility/public/mojom/BUILD.gn
@@ -41,6 +41,7 @@
if (supports_os_accessibility_service) {
mojom("os_service_mojom") {
sources = [
+ "autoclick.mojom",
"file_loader.mojom",
"speech_recognition.mojom",
"tts.mojom",
@@ -60,6 +61,7 @@
"$root_gen_dir/skia/public/mojom/skcolor.mojom-lite.js",
"$root_gen_dir/ui/gfx/geometry/mojom/geometry.mojom-lite.js",
"$target_gen_dir/assistive_technology_type.mojom-lite.js",
+ "$target_gen_dir/autoclick.mojom-lite.js",
"$target_gen_dir/speech_recognition.mojom-lite.js",
"$target_gen_dir/tts.mojom-lite.js",
"$target_gen_dir/user_interface.mojom-lite.js",
diff --git a/services/accessibility/public/mojom/accessibility_service.mojom b/services/accessibility/public/mojom/accessibility_service.mojom
index d3c6f34..c3c9a81 100644
--- a/services/accessibility/public/mojom/accessibility_service.mojom
+++ b/services/accessibility/public/mojom/accessibility_service.mojom
@@ -19,6 +19,8 @@
import "services/accessibility/public/mojom/assistive_technology_type.mojom";
[EnableIf=supports_os_accessibility_service]
import "services/accessibility/public/mojom/file_loader.mojom";
+[EnableIf=supports_os_accessibility_service]
+import "services/accessibility/public/mojom/autoclick.mojom";
// Implemented by the accessibility service. Turns on and off features.
// The caller is the client OS, for example, Chrome OS Ash.
@@ -77,6 +79,12 @@
pending_associated_remote<Automation> automation,
pending_receiver<AutomationClient> automation_client);
+ // Binds to a Autoclick client implemented in the main OS process, and
+ // an Autoclick implemented in the service process, called by the service.
+ [EnableIf=supports_os_accessibility_service]
+ BindAutoclickClient(
+ pending_receiver<AutoclickClient> autoclick_client);
+
// Binds to a Speech Recognition implementation in the main OS process,
// called by the service.
[EnableIf=supports_os_accessibility_service]
diff --git a/services/accessibility/public/mojom/autoclick.mojom b/services/accessibility/public/mojom/autoclick.mojom
new file mode 100644
index 0000000..0ea239d
--- /dev/null
+++ b/services/accessibility/public/mojom/autoclick.mojom
@@ -0,0 +1,33 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module ax.mojom;
+
+import "ui/gfx/geometry/mojom/geometry.mojom";
+
+// Implemented in Accessibility Service Javascript and called
+// by the OS browser process, this informs JS that it needs to
+// find the bounds for the given point in screen coordinates.
+interface Autoclick {
+ // Called when the OS browser process needs the bounds for
+ // the closest scrollable ancestor of the node at the
+ // given screen point.
+ RequestScrollableBoundsForPoint(gfx.mojom.Point point);
+};
+
+// Implemented by the OS browser process, this is called by
+// Accessibility Service javascript when scrollable bounds have
+// been found.
+interface AutoclickClient {
+ // Called by the Accessibility Service when
+ // Autoclick::onScrollableBoundsForPointRequested has found a
+ // scrolling container. `bounds` will be the bounds of the nearest
+ // scrollable ancestor of the node at the point requested using
+ // onScrollableBoundsForPointRequested.
+ HandleScrollableBoundsForPointFound(gfx.mojom.Rect bounds);
+
+ // Send a pending receiver from the OS browser process to
+ // the accessibility service for binding.
+ BindAutoclick() => (pending_receiver<Autoclick> autoclick_receiver);
+};
diff --git a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
index 009b06f..1c2ac0d 100644
--- a/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
+++ b/testing/buildbot/filters/linux-chromeos.browser_tests.require_lacros.filter
@@ -38,6 +38,8 @@
-DesksTemplatesClientLacrosTest.SystemUILaunchBrowser
# TODO(b/277821091) Flaky
-ChromeVoxEditingTest.GiantTextAreaPerformance
+# TODO(crbug.com/1478586): Flaky.
+-AutoclickWithAccessibilityServiceTest.ScrollableBoundsPlumbing
# TODO(accessibility): these are native C++ tests that need investigation.
-TestAsNormalAndGuestUser/SpokenFeedbackTest.LandmarkNavigation*
-TestAsNormalAndGuestUser/SpokenFeedbackTest.NavigateChromeVoxMenu*