mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[fuchsia][scenic] Reland Move pointer injector to embedder. (flutter/engine#34692)
This PR moves the pointer injector library from fuchsia views library present in fuchsia.git to the flutter embedder. Test: ffx test run "fuchsia-pkg://fuchsia.com/flutter_runner_tests#meta/flutter_runner_tests.cm"
This commit is contained in:
parent
3dfca9f974
commit
271b59d7a6
@ -2076,6 +2076,9 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/platform_view_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_delegate_unittests.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_injector_delegate.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_injector_delegate.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/pointer_injector_delegate_unittest.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/program_metadata.h
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc
|
||||
FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h
|
||||
|
||||
@ -92,6 +92,8 @@ template("runner_sources") {
|
||||
"platform_view.h",
|
||||
"pointer_delegate.cc",
|
||||
"pointer_delegate.h",
|
||||
"pointer_injector_delegate.cc",
|
||||
"pointer_injector_delegate.h",
|
||||
"program_metadata.h",
|
||||
"runner.cc",
|
||||
"runner.h",
|
||||
@ -158,6 +160,7 @@ template("runner_sources") {
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.pointer",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.views",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.scenic",
|
||||
"$fuchsia_sdk_root/fidl:fuchsia.ui.pointerinjector",
|
||||
"$fuchsia_sdk_root/pkg:async-cpp",
|
||||
"$fuchsia_sdk_root/pkg:async-default",
|
||||
"$fuchsia_sdk_root/pkg:async-loop",
|
||||
@ -472,6 +475,7 @@ if (enable_unittests) {
|
||||
"keyboard_unittest.cc",
|
||||
"platform_view_unittest.cc",
|
||||
"pointer_delegate_unittests.cc",
|
||||
"pointer_injector_delegate_unittest.cc",
|
||||
"tests/engine_unittests.cc",
|
||||
"tests/fake_flatland_unittests.cc",
|
||||
"tests/fake_session_unittests.cc",
|
||||
|
||||
@ -213,6 +213,17 @@ void Engine::Initialize(
|
||||
<< "fuchsia::ui::input3::Keyboard connection failed: "
|
||||
<< zx_status_get_string(keyboard_status);
|
||||
|
||||
// Connect to Pointerinjector service.
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry;
|
||||
zx_status_t pointerinjector_registry_status =
|
||||
runner_services->Connect<fuchsia::ui::pointerinjector::Registry>(
|
||||
pointerinjector_registry.NewRequest());
|
||||
if (pointerinjector_registry_status != ZX_OK) {
|
||||
FML_LOG(WARNING)
|
||||
<< "fuchsia::ui::pointerinjector::Registry connection failed: "
|
||||
<< zx_status_get_string(pointerinjector_registry_status);
|
||||
}
|
||||
|
||||
// Make clones of the `ViewRef` before sending it to various places.
|
||||
fuchsia::ui::views::ViewRef platform_view_ref;
|
||||
view_ref_pair.view_ref.Clone(&platform_view_ref);
|
||||
@ -401,6 +412,7 @@ void Engine::Initialize(
|
||||
view_ref_focused = std::move(view_ref_focused),
|
||||
touch_source = std::move(touch_source),
|
||||
mouse_source = std::move(mouse_source),
|
||||
pointerinjector_registry = std::move(pointerinjector_registry),
|
||||
on_session_listener_error_callback =
|
||||
std::move(on_session_listener_error_callback),
|
||||
on_enable_wireframe_callback =
|
||||
@ -479,6 +491,7 @@ void Engine::Initialize(
|
||||
std::move(mouse_source), std::move(focuser),
|
||||
std::move(view_ref_focused),
|
||||
std::move(parent_viewport_watcher),
|
||||
std::move(pointerinjector_registry),
|
||||
std::move(on_enable_wireframe_callback),
|
||||
std::move(on_create_flatland_view_callback),
|
||||
std::move(on_update_view_callback),
|
||||
@ -496,6 +509,7 @@ void Engine::Initialize(
|
||||
std::move(keyboard), std::move(touch_source),
|
||||
std::move(mouse_source), std::move(focuser),
|
||||
std::move(view_ref_focused),
|
||||
std::move(pointerinjector_registry),
|
||||
std::move(session_listener_request),
|
||||
std::move(on_session_listener_error_callback),
|
||||
std::move(on_enable_wireframe_callback),
|
||||
|
||||
@ -358,7 +358,7 @@ void FlatlandExternalViewEmbedder::CreateView(
|
||||
FlatlandView new_view = {.transform_id = transform_id,
|
||||
.viewport_id = viewport_id};
|
||||
flatland_->flatland()->CreateTransform(new_view.transform_id);
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher;
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle child_view_watcher;
|
||||
new_view.pending_create_viewport_callback =
|
||||
[this, transform_id, viewport_id, view_id,
|
||||
child_view_watcher_request =
|
||||
|
||||
@ -35,7 +35,7 @@ namespace flutter_runner {
|
||||
using ViewCallback = std::function<void()>;
|
||||
using FlatlandViewCreatedCallback = std::function<void(
|
||||
fuchsia::ui::composition::ContentId,
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher)>;
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle child_view_watcher)>;
|
||||
using FlatlandViewIdCallback =
|
||||
std::function<void(fuchsia::ui::composition::ContentId)>;
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ FlatlandPlatformView::FlatlandPlatformView(
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::composition::ParentViewportWatcherHandle
|
||||
parent_viewport_watcher,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnCreateFlatlandView on_create_view_callback,
|
||||
OnUpdateView on_update_view_callback,
|
||||
@ -43,6 +44,7 @@ FlatlandPlatformView::FlatlandPlatformView(
|
||||
std::move(mouse_source),
|
||||
std::move(focuser),
|
||||
std::move(view_ref_focused),
|
||||
std::move(pointerinjector_registry),
|
||||
std::move(wireframe_enabled_callback),
|
||||
std::move(on_update_view_callback),
|
||||
std::move(on_create_surface_callback),
|
||||
@ -148,6 +150,10 @@ void FlatlandPlatformView::OnChildViewViewRef(
|
||||
|
||||
focus_delegate_->OnChildViewViewRef(view_id, std::move(view_ref));
|
||||
|
||||
fuchsia::ui::views::ViewRef view_ref_clone;
|
||||
fidl::Clone(view_ref, &view_ref_clone);
|
||||
pointer_injector_delegate_->OnCreateView(view_id, std::move(view_ref_clone));
|
||||
|
||||
child_view_info_.at(content_id)
|
||||
.child_view_watcher->GetViewRef(
|
||||
[this, content_id, view_id](fuchsia::ui::views::ViewRef view_ref) {
|
||||
@ -164,16 +170,32 @@ void FlatlandPlatformView::OnCreateView(ViewCallback on_view_created,
|
||||
task_runners_.GetPlatformTaskRunner(),
|
||||
view_id = view_id_raw](
|
||||
fuchsia::ui::composition::ContentId content_id,
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr
|
||||
child_view_watcher) {
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle
|
||||
child_view_watcher_handle) {
|
||||
FML_CHECK(weak);
|
||||
FML_CHECK(weak->child_view_info_.count(content_id.value) == 0);
|
||||
|
||||
// Bind the child view watcher to the platform thread so that the FIDL calls
|
||||
// are handled on the platform thread.
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher =
|
||||
child_view_watcher_handle.Bind();
|
||||
FML_CHECK(child_view_watcher);
|
||||
|
||||
child_view_watcher.set_error_handler([](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: ChildViewWatcher status: "
|
||||
<< status;
|
||||
});
|
||||
child_view_watcher.set_error_handler(
|
||||
[weak, view_id](zx_status_t status) {
|
||||
FML_LOG(ERROR) << "Interface error on: ChildViewWatcher status: "
|
||||
<< status;
|
||||
|
||||
if (!weak) {
|
||||
FML_LOG(WARNING)
|
||||
<< "Flatland View bound to PlatformView after PlatformView was "
|
||||
"destroyed; ignoring.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Disconnected views cannot listen to pointer events.
|
||||
weak->pointer_injector_delegate_->OnDestroyView(view_id);
|
||||
});
|
||||
|
||||
platform_task_runner->PostTask(
|
||||
fml::MakeCopyable([weak, view_id, content_id,
|
||||
@ -226,6 +248,7 @@ void FlatlandPlatformView::OnDisposeView(int64_t view_id_raw) {
|
||||
FML_DCHECK(weak->child_view_info_.count(content_id.value) == 1);
|
||||
weak->child_view_info_.erase(content_id.value);
|
||||
weak->focus_delegate_->OnDisposeChildView(view_id_raw);
|
||||
weak->pointer_injector_delegate_->OnDestroyView(view_id_raw);
|
||||
});
|
||||
};
|
||||
on_destroy_view_callback_(view_id_raw, std::move(on_view_unbound));
|
||||
|
||||
@ -35,6 +35,7 @@ class FlatlandPlatformView final : public flutter_runner::PlatformView {
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::composition::ParentViewportWatcherHandle
|
||||
parent_viewport_watcher,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnCreateFlatlandView on_create_view_callback,
|
||||
OnUpdateView on_update_view_callback,
|
||||
|
||||
@ -19,6 +19,7 @@ GfxPlatformView::GfxPlatformView(
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source,
|
||||
fuchsia::ui::views::FocuserHandle focuser,
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request,
|
||||
fit::closure on_session_listener_error_callback,
|
||||
@ -44,6 +45,7 @@ GfxPlatformView::GfxPlatformView(
|
||||
std::move(mouse_source),
|
||||
std::move(focuser),
|
||||
std::move(view_ref_focused),
|
||||
std::move(pointerinjector_registry),
|
||||
std::move(wireframe_enabled_callback),
|
||||
std::move(on_update_view_callback),
|
||||
std::move(on_create_surface_callback),
|
||||
@ -295,6 +297,9 @@ bool GfxPlatformView::OnChildViewDisconnected(
|
||||
<< "}";
|
||||
auto call = out.str();
|
||||
|
||||
// A disconnected view cannot listen to pointer events.
|
||||
pointer_injector_delegate_->OnDestroyView(view_id_mapping->second);
|
||||
|
||||
std::unique_ptr<flutter::PlatformMessage> message =
|
||||
std::make_unique<flutter::PlatformMessage>(
|
||||
"flutter/platform_views",
|
||||
@ -350,6 +355,7 @@ void GfxPlatformView::OnCreateView(ViewCallback on_view_created,
|
||||
|
||||
FML_DCHECK(weak->child_view_ids_.count(resource_id) == 0);
|
||||
weak->child_view_ids_[resource_id] = view_id;
|
||||
weak->pointer_injector_delegate_->OnCreateView(view_id);
|
||||
});
|
||||
};
|
||||
on_create_view_callback_(view_id_raw, std::move(on_view_created),
|
||||
@ -358,10 +364,10 @@ void GfxPlatformView::OnCreateView(ViewCallback on_view_created,
|
||||
|
||||
void GfxPlatformView::OnDisposeView(int64_t view_id_raw) {
|
||||
auto on_view_unbound =
|
||||
[weak = weak_factory_.GetWeakPtr(),
|
||||
[weak = weak_factory_.GetWeakPtr(), view_id = view_id_raw,
|
||||
platform_task_runner = task_runners_.GetPlatformTaskRunner()](
|
||||
scenic::ResourceId resource_id) {
|
||||
platform_task_runner->PostTask([weak, resource_id]() {
|
||||
platform_task_runner->PostTask([weak, resource_id, view_id]() {
|
||||
if (!weak) {
|
||||
FML_LOG(WARNING)
|
||||
<< "ViewHolder unbound from PlatformView after PlatformView"
|
||||
@ -371,6 +377,7 @@ void GfxPlatformView::OnDisposeView(int64_t view_id_raw) {
|
||||
|
||||
FML_DCHECK(weak->child_view_ids_.count(resource_id) == 1);
|
||||
weak->child_view_ids_.erase(resource_id);
|
||||
weak->pointer_injector_delegate_->OnDestroyView(view_id);
|
||||
});
|
||||
};
|
||||
on_destroy_view_callback_(view_id_raw, std::move(on_view_unbound));
|
||||
|
||||
@ -36,6 +36,7 @@ class GfxPlatformView final : public flutter_runner::PlatformView,
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source,
|
||||
fuchsia::ui::views::FocuserHandle focuser,
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request,
|
||||
fit::closure on_session_listener_error_callback,
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia.vulkan.loader.Loader" // Copied from vulkan/client.shard.cml.
|
||||
]
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.input3.Keyboard",
|
||||
"fuchsia.ui.scenic.Scenic",
|
||||
"fuchsia.ui.pointerinjector.Registry",
|
||||
"fuchsia.vulkan.loader.Loader"
|
||||
]
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flow/embedded_views.h"
|
||||
#include "pointer_injector_delegate.h"
|
||||
#define RAPIDJSON_HAS_STDSTRING 1
|
||||
|
||||
#include "platform_view.h"
|
||||
@ -62,6 +63,7 @@ PlatformView::PlatformView(
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source,
|
||||
fuchsia::ui::views::FocuserHandle focuser,
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnUpdateView on_update_view_callback,
|
||||
OnCreateSurface on_create_surface_callback,
|
||||
@ -101,6 +103,9 @@ PlatformView::PlatformView(
|
||||
SetInterfaceErrorHandler(keyboard_listener_binding_, "Keyboard Listener");
|
||||
SetInterfaceErrorHandler(keyboard_, "Keyboard");
|
||||
|
||||
fuchsia::ui::views::ViewRef view_ref_clone;
|
||||
fidl::Clone(view_ref, &view_ref_clone);
|
||||
|
||||
// Configure keyboard listener.
|
||||
keyboard_->AddListener(std::move(view_ref),
|
||||
keyboard_listener_binding_.NewBinding(), [] {});
|
||||
@ -150,6 +155,11 @@ PlatformView::PlatformView(
|
||||
});
|
||||
}
|
||||
|
||||
// Configure the pointer injector delegate.
|
||||
pointer_injector_delegate_ = std::make_unique<PointerInjectorDelegate>(
|
||||
std::move(pointerinjector_registry), std::move(view_ref_clone),
|
||||
is_flatland);
|
||||
|
||||
// Finally! Register the native platform message handlers.
|
||||
RegisterPlatformMessageHandlers();
|
||||
}
|
||||
@ -845,6 +855,10 @@ bool PlatformView::HandleFlutterPlatformViewsChannelPlatformMessage(
|
||||
}
|
||||
} else if (method.rfind("View.focus", 0) == 0) {
|
||||
return focus_delegate_->HandlePlatformMessage(root, message->response());
|
||||
} else if (method.rfind(PointerInjectorDelegate::kPointerInjectorMethodPrefix,
|
||||
0) == 0) {
|
||||
return pointer_injector_delegate_->HandlePlatformMessage(
|
||||
root, message->response());
|
||||
} else {
|
||||
FML_LOG(ERROR) << "Unknown " << message->channel() << " method " << method;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "flutter/shell/platform/fuchsia/flutter/vsync_waiter.h"
|
||||
#include "focus_delegate.h"
|
||||
#include "pointer_delegate.h"
|
||||
#include "pointer_injector_delegate.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
@ -75,6 +76,7 @@ class PlatformView : public flutter::PlatformView,
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source,
|
||||
fuchsia::ui::views::FocuserHandle focuser,
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused,
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry,
|
||||
OnEnableWireframe wireframe_enabled_callback,
|
||||
OnUpdateView on_update_view_callback,
|
||||
OnCreateSurface on_create_surface_callback,
|
||||
@ -187,6 +189,7 @@ class PlatformView : public flutter::PlatformView,
|
||||
|
||||
std::shared_ptr<FocusDelegate> focus_delegate_;
|
||||
std::shared_ptr<PointerDelegate> pointer_delegate_;
|
||||
std::unique_ptr<PointerInjectorDelegate> pointer_injector_delegate_;
|
||||
|
||||
fidl::Binding<fuchsia::ui::input::InputMethodEditorClient> ime_client_;
|
||||
fuchsia::ui::input::InputMethodEditorPtr ime_;
|
||||
|
||||
@ -271,6 +271,12 @@ class PlatformViewBuilder {
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetPointerinjectorRegistry(
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry) {
|
||||
pointerinjector_registry_ = std::move(pointerinjector_registry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetSessionListenerRequest(
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> request) {
|
||||
session_listener_request_ = std::move(request);
|
||||
@ -316,7 +322,8 @@ class PlatformViewBuilder {
|
||||
external_external_view_embedder_, std::move(ime_service_),
|
||||
std::move(keyboard_), std::move(touch_source_),
|
||||
std::move(mouse_source_), std::move(focuser_),
|
||||
std::move(view_ref_focused_), std::move(session_listener_request_),
|
||||
std::move(view_ref_focused_), std::move(pointerinjector_registry_),
|
||||
std::move(session_listener_request_),
|
||||
std::move(on_session_listener_error_callback_),
|
||||
std::move(wireframe_enabled_callback_),
|
||||
std::move(on_create_view_callback_),
|
||||
@ -343,6 +350,7 @@ class PlatformViewBuilder {
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source_;
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused_;
|
||||
fuchsia::ui::views::FocuserHandle focuser_;
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry_;
|
||||
fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
|
||||
session_listener_request_;
|
||||
fit::closure on_session_listener_error_callback_;
|
||||
|
||||
@ -0,0 +1,308 @@
|
||||
// Copyright 2013 The Flutter 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 "pointer_injector_delegate.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
using fup_Config = fuchsia::ui::pointerinjector::Config;
|
||||
using fup_Context = fuchsia::ui::pointerinjector::Context;
|
||||
using fup_Data = fuchsia::ui::pointerinjector::Data;
|
||||
using fup_DeviceType = fuchsia::ui::pointerinjector::DeviceType;
|
||||
using fup_DispatchPolicy = fuchsia::ui::pointerinjector::DispatchPolicy;
|
||||
using fup_Event = fuchsia::ui::pointerinjector::Event;
|
||||
using fup_EventPhase = fuchsia::ui::pointerinjector::EventPhase;
|
||||
using fup_PointerSample = fuchsia::ui::pointerinjector::PointerSample;
|
||||
using fup_Target = fuchsia::ui::pointerinjector::Target;
|
||||
using fup_Viewport = fuchsia::ui::pointerinjector::Viewport;
|
||||
using fuv_ViewRef = fuchsia::ui::views::ViewRef;
|
||||
const auto fup_MAX_INJECT = fuchsia::ui::pointerinjector::MAX_INJECT;
|
||||
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
static constexpr std::array<float, 9> kIdentityMatrix = {
|
||||
1, 0, 0, // column one
|
||||
0, 1, 0, // column two
|
||||
0, 0, 1, // column three
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PointerInjectorDelegate::HandlePlatformMessage(
|
||||
rapidjson::Value request,
|
||||
fml::RefPtr<flutter::PlatformMessageResponse> response) {
|
||||
if (!registry_->is_bound()) {
|
||||
FML_LOG(WARNING)
|
||||
<< "Lost connection to fuchsia.ui.pointerinjector.Registry";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto method = request.FindMember("method");
|
||||
if (method == request.MemberEnd() || !method->value.IsString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method->value == kPointerInjectorMethodPrefix) {
|
||||
auto args_it = request.FindMember("args");
|
||||
if (args_it == request.MemberEnd() || !args_it->value.IsObject()) {
|
||||
FML_LOG(ERROR) << "No arguments found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& args = args_it->value;
|
||||
|
||||
auto view_id = args.FindMember("viewId");
|
||||
if (!view_id->value.IsUint64()) {
|
||||
FML_LOG(ERROR) << "Argument 'viewId' is not a uint64";
|
||||
return false;
|
||||
}
|
||||
auto id = view_id->value.GetUint64();
|
||||
|
||||
auto phase = args.FindMember("phase");
|
||||
if (!phase->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'phase' is not a int";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pointer_x = args.FindMember("x");
|
||||
if (!pointer_x->value.IsFloat() && !pointer_x->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'Pointer.X' is not a float";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pointer_y = args.FindMember("y");
|
||||
if (!pointer_y->value.IsFloat() && !pointer_y->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'Pointer.Y' is not a float";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pointer_id = args.FindMember("pointerId");
|
||||
if (!pointer_id->value.IsUint()) {
|
||||
FML_LOG(ERROR) << "Argument 'pointerId' is not a uint32";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto trace_flow_id = args.FindMember("traceFlowId");
|
||||
if (!trace_flow_id->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'traceFlowId' is not a int";
|
||||
return false;
|
||||
}
|
||||
|
||||
// For GFX, the viewRef for the view is provided through the platform
|
||||
// message. For flatland, the viewRef is provided through |OnCreateView|.
|
||||
std::optional<fuv_ViewRef> view_ref;
|
||||
if (!is_flatland_) {
|
||||
auto view_ref_arg = args.FindMember("viewRef");
|
||||
if (!view_ref_arg->value.IsUint64()) {
|
||||
FML_LOG(ERROR) << "Argument 'viewRef' is not a uint64";
|
||||
return false;
|
||||
}
|
||||
|
||||
zx_handle_t handle = view_ref_arg->value.GetUint64();
|
||||
zx_handle_t out_handle;
|
||||
zx_status_t status =
|
||||
zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &out_handle);
|
||||
if (status != ZX_OK) {
|
||||
FML_LOG(ERROR) << "Argument 'viewRef' is not valid";
|
||||
return false;
|
||||
}
|
||||
auto ref = fuv_ViewRef({
|
||||
.reference = zx::eventpair(out_handle),
|
||||
});
|
||||
view_ref = std::move(ref);
|
||||
}
|
||||
|
||||
auto width = args.FindMember("logicalWidth");
|
||||
if (!width->value.IsFloat() && !width->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'logicalWidth' is not a float";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto height = args.FindMember("logicalHeight");
|
||||
if (!height->value.IsFloat() && !height->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'logicalHeight' is not a float";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto timestamp = args.FindMember("timestamp");
|
||||
if (!timestamp->value.IsInt()) {
|
||||
FML_LOG(ERROR) << "Argument 'timestamp' is not a int";
|
||||
return false;
|
||||
}
|
||||
|
||||
PointerInjectorRequest request = {
|
||||
.x = pointer_x->value.GetFloat(),
|
||||
.y = pointer_y->value.GetFloat(),
|
||||
.pointer_id = pointer_id->value.GetUint(),
|
||||
.phase = static_cast<fup_EventPhase>(phase->value.GetInt()),
|
||||
.trace_flow_id = trace_flow_id->value.GetUint64(),
|
||||
.view_ref = std::move(view_ref),
|
||||
.logical_size = {width->value.GetFloat(), height->value.GetFloat()},
|
||||
.timestamp = timestamp->value.GetInt()};
|
||||
|
||||
// Inject the pointer event if the view has been created.
|
||||
if (valid_views_.count(id) > 0) {
|
||||
valid_views_.at(id).InjectEvent(std::move(request));
|
||||
Complete(std::move(response), "[0]");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// All of our methods complete the platform message response.
|
||||
return true;
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::OnCreateView(
|
||||
uint64_t view_id,
|
||||
std::optional<fuv_ViewRef> view_ref) {
|
||||
FML_CHECK(valid_views_.count(view_id) == 0);
|
||||
|
||||
auto [_, success] = valid_views_.try_emplace(
|
||||
view_id, registry_, host_view_ref_, std::move(view_ref));
|
||||
|
||||
FML_CHECK(success);
|
||||
}
|
||||
|
||||
fup_Event PointerInjectorDelegate::ExtractPointerEvent(
|
||||
PointerInjectorRequest request) {
|
||||
fup_Event event;
|
||||
event.set_timestamp(request.timestamp);
|
||||
event.set_trace_flow_id(request.trace_flow_id);
|
||||
|
||||
fup_PointerSample pointer_sample;
|
||||
pointer_sample.set_pointer_id(request.pointer_id);
|
||||
pointer_sample.set_phase(request.phase);
|
||||
pointer_sample.set_position_in_viewport({request.x, request.y});
|
||||
|
||||
fup_Data data;
|
||||
data.set_pointer_sample(std::move(pointer_sample));
|
||||
|
||||
event.set_data(std::move(data));
|
||||
return event;
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::Complete(
|
||||
fml::RefPtr<flutter::PlatformMessageResponse> response,
|
||||
std::string value) {
|
||||
if (response) {
|
||||
response->Complete(std::make_unique<fml::DataMapping>(
|
||||
std::vector<uint8_t>(value.begin(), value.end())));
|
||||
}
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::PointerInjectorEndpoint::InjectEvent(
|
||||
PointerInjectorRequest request) {
|
||||
if (!registered_) {
|
||||
RegisterInjector(request);
|
||||
}
|
||||
|
||||
auto event = ExtractPointerEvent(std::move(request));
|
||||
|
||||
// Add the event to |injector_events_| and dispatch it to the view.
|
||||
EnqueueEvent(std::move(event));
|
||||
|
||||
DispatchPendingEvents();
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::PointerInjectorEndpoint::DispatchPendingEvents() {
|
||||
// Return if there is already a |fuchsia.ui.pointerinjector.Device.Inject|
|
||||
// call in flight. The new pointer events will be dispatched once the
|
||||
// in-progress call terminates.
|
||||
if (injection_in_flight_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch the events present in |injector_events_|. Note that we recursively
|
||||
// call |DispatchPendingEvents| in the callback passed to the
|
||||
// |f.u.p.Device.Inject| call. This ensures that there is only one
|
||||
// |f.u.p.Device.Inject| call at a time. If a new pointer event comes when
|
||||
// there is a |f.u.p.Device.Inject| call in progress, it gets buffered in
|
||||
// |injector_events_| and is picked up later.
|
||||
if (!injector_events_.empty()) {
|
||||
auto events = std::move(injector_events_.front());
|
||||
injector_events_.pop();
|
||||
injection_in_flight_ = true;
|
||||
|
||||
FML_CHECK(device_.is_bound());
|
||||
FML_CHECK(events.size() <= fup_MAX_INJECT);
|
||||
|
||||
device_->Inject(std::move(events), [weak = weak_factory_.GetWeakPtr()] {
|
||||
if (!weak) {
|
||||
FML_LOG(WARNING) << "Use after free attempted.";
|
||||
return;
|
||||
}
|
||||
weak->injection_in_flight_ = false;
|
||||
weak->DispatchPendingEvents();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::PointerInjectorEndpoint::EnqueueEvent(
|
||||
fup_Event event) {
|
||||
// Add |event| in |injector_events_| keeping in mind that the vector size does
|
||||
// not exceed |fup_MAX_INJECT|.
|
||||
if (!injector_events_.empty() &&
|
||||
injector_events_.back().size() < fup_MAX_INJECT) {
|
||||
injector_events_.back().push_back(std::move(event));
|
||||
} else {
|
||||
std::vector<fup_Event> vec;
|
||||
vec.reserve(fup_MAX_INJECT);
|
||||
vec.push_back(std::move(event));
|
||||
injector_events_.push(std::move(vec));
|
||||
}
|
||||
}
|
||||
|
||||
void PointerInjectorDelegate::PointerInjectorEndpoint::RegisterInjector(
|
||||
const PointerInjectorRequest& request) {
|
||||
if (registered_) {
|
||||
return;
|
||||
}
|
||||
|
||||
fup_Config config;
|
||||
config.set_device_id(1);
|
||||
config.set_device_type(fup_DeviceType::TOUCH);
|
||||
config.set_dispatch_policy(fup_DispatchPolicy::EXCLUSIVE_TARGET);
|
||||
|
||||
fup_Context context;
|
||||
fuv_ViewRef context_clone;
|
||||
fidl::Clone(*host_view_ref_, &context_clone);
|
||||
context.set_view(std::move(context_clone));
|
||||
config.set_context(std::move(context));
|
||||
|
||||
FML_CHECK(request.view_ref.has_value() || view_ref_.has_value());
|
||||
fup_Target target;
|
||||
fuv_ViewRef target_clone;
|
||||
|
||||
// GFX.
|
||||
if (request.view_ref.has_value()) {
|
||||
fidl::Clone(*request.view_ref, &target_clone);
|
||||
}
|
||||
// Flatland.
|
||||
else {
|
||||
fidl::Clone(*view_ref_, &target_clone);
|
||||
}
|
||||
target.set_view(std::move(target_clone));
|
||||
config.set_target(std::move(target));
|
||||
|
||||
fup_Viewport viewport;
|
||||
viewport.set_viewport_to_context_transform(kIdentityMatrix);
|
||||
std::array<std::array<float, 2>, 2> extents{
|
||||
{/*min*/ {0, 0},
|
||||
/*max*/ {request.logical_size[0], request.logical_size[1]}}};
|
||||
viewport.set_extents(std::move(extents));
|
||||
config.set_viewport(std::move(viewport));
|
||||
|
||||
FML_CHECK(registry_->is_bound());
|
||||
|
||||
(*registry_)->Register(std::move(config), device_.NewRequest(), [] {});
|
||||
|
||||
registered_ = true;
|
||||
}
|
||||
|
||||
} // namespace flutter_runner
|
||||
@ -0,0 +1,189 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_INJECTOR_DELEGATE_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_INJECTOR_DELEGATE_H_
|
||||
|
||||
#include <fuchsia/ui/pointerinjector/cpp/fidl.h>
|
||||
#include <fuchsia/ui/views/cpp/fidl.h>
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/lib/ui/window/platform_message.h"
|
||||
#include "third_party/rapidjson/include/rapidjson/document.h"
|
||||
|
||||
namespace flutter_runner {
|
||||
|
||||
// This class is responsible for handling the platform messages related to
|
||||
// pointer events and managing the lifecycle of
|
||||
// |fuchsia.ui.pointerinjector.Device| client side endpoint for embedded views.
|
||||
class PointerInjectorDelegate {
|
||||
public:
|
||||
static constexpr auto kPointerInjectorMethodPrefix =
|
||||
"View.pointerinjector.inject";
|
||||
|
||||
PointerInjectorDelegate(fuchsia::ui::pointerinjector::RegistryHandle registry,
|
||||
fuchsia::ui::views::ViewRef host_view_ref,
|
||||
bool is_flatland)
|
||||
: registry_(std::make_shared<fuchsia::ui::pointerinjector::RegistryPtr>(
|
||||
registry.Bind())),
|
||||
host_view_ref_(std::make_shared<fuchsia::ui::views::ViewRef>(
|
||||
std::move(host_view_ref))),
|
||||
is_flatland_(is_flatland) {}
|
||||
|
||||
// Handles the following pointer event related platform message requests:
|
||||
// View.Pointerinjector.inject
|
||||
// - Attempts to dispatch a pointer event to the given viewRef. Completes
|
||||
// with [0] when the pointer event is sent to the given viewRef.
|
||||
bool HandlePlatformMessage(
|
||||
rapidjson::Value request,
|
||||
fml::RefPtr<flutter::PlatformMessageResponse> response);
|
||||
|
||||
// Adds an endpoint for |view_id| in |valid_views_| for lifecycle management.
|
||||
// Called in |GFXPlatformView::OnCreateView()| and
|
||||
// |FlatlandPlatformView::OnChildViewViewRef()|.
|
||||
void OnCreateView(
|
||||
uint64_t view_id,
|
||||
std::optional<fuchsia::ui::views::ViewRef> view_ref = std::nullopt);
|
||||
|
||||
// Closes the |fuchsia.ui.pointerinjector.Device| channel for |view_id| and
|
||||
// cleans up resources.
|
||||
void OnDestroyView(uint64_t view_id) { valid_views_.erase(view_id); };
|
||||
|
||||
private:
|
||||
using ViewId = int64_t;
|
||||
|
||||
struct PointerInjectorRequest {
|
||||
// The position of the pointer event in viewport's coordinate system.
|
||||
float x = 0.f, y = 0.f;
|
||||
|
||||
// |fuchsia.ui.pointerinjector.PointerSample.pointer_id|.
|
||||
uint32_t pointer_id = 0;
|
||||
|
||||
// |fuchsia.ui.pointerinjector.PointerSample.phase|.
|
||||
fuchsia::ui::pointerinjector::EventPhase phase =
|
||||
fuchsia::ui::pointerinjector::EventPhase::ADD;
|
||||
|
||||
// |fuchsia.ui.pointerinjector.Event.trace_flow_id|.
|
||||
uint64_t trace_flow_id = 0;
|
||||
|
||||
// The view for which dispatch is attempted for the pointer event. For
|
||||
// flatland views, this value is set as std::nullopt.
|
||||
std::optional<fuchsia::ui::views::ViewRef> view_ref;
|
||||
|
||||
// Logical size of the view's coordinate system.
|
||||
std::array<float, 2> logical_size = {0.f, 0.f};
|
||||
|
||||
// |fuchsia.ui.pointerinjector.Event.timestamp|.
|
||||
zx_time_t timestamp = 0;
|
||||
};
|
||||
|
||||
// This class is responsible for dispatching pointer events to a view by first
|
||||
// registering the injector device using
|
||||
// |fuchsia.ui.pointerinjector.Registry.Register| and then injecting the
|
||||
// pointer event using |fuchsia.ui.pointerinjector.Device.Inject|.
|
||||
class PointerInjectorEndpoint {
|
||||
public:
|
||||
PointerInjectorEndpoint(
|
||||
std::shared_ptr<fuchsia::ui::pointerinjector::RegistryPtr> registry,
|
||||
std::shared_ptr<fuchsia::ui::views::ViewRef> host_view_ref,
|
||||
std::optional<fuchsia::ui::views::ViewRef> view_ref)
|
||||
: registry_(std::move(registry)),
|
||||
host_view_ref_(std::move(host_view_ref)),
|
||||
view_ref_(std::move(view_ref)),
|
||||
weak_factory_(this) {
|
||||
// Try to re-register the |device_| if the |device_| gets closed due to
|
||||
// some error.
|
||||
device_.set_error_handler(
|
||||
[weak = weak_factory_.GetWeakPtr()](auto status) {
|
||||
FML_LOG(WARNING)
|
||||
<< "fuchsia.ui.pointerinjector.Device closed " << status;
|
||||
if (!weak) {
|
||||
return;
|
||||
}
|
||||
weak->registered_ = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Registers |device_| if it has not been registered and calls
|
||||
// |DispatchPendingEvents()| to dispatch |request| to the view.
|
||||
void InjectEvent(PointerInjectorRequest request);
|
||||
|
||||
private:
|
||||
// Registers with the pointer injector service.
|
||||
//
|
||||
// Sets |registered_| to true immediately after submitting the registration
|
||||
// request. This means that the registration request may still be in-flight
|
||||
// on the server side when the function returns. Events can safely be
|
||||
// injected into the channel while registration is pending ("feed forward").
|
||||
void RegisterInjector(const PointerInjectorRequest& request);
|
||||
|
||||
// Recursively calls |fuchsia.ui.pointerinjector.Device.Inject| to dispatch
|
||||
// the pointer events in |injector_events_| to the view.
|
||||
void DispatchPendingEvents();
|
||||
|
||||
void EnqueueEvent(fuchsia::ui::pointerinjector::Event event);
|
||||
|
||||
// Set to true if there is a |fuchsia.ui.pointerinjector.Device.Inject| call
|
||||
// in progress. If true, the |fuchsia.ui.pointerinjector.Event| is buffered
|
||||
// in |injector_events_|.
|
||||
bool injection_in_flight_ = false;
|
||||
|
||||
// Set to true if |device_| has been registered using
|
||||
// |fuchsia.ui.pointerinjector.Registry.Register|. False otherwise.
|
||||
bool registered_ = false;
|
||||
|
||||
std::shared_ptr<fuchsia::ui::pointerinjector::RegistryPtr> registry_;
|
||||
|
||||
// ViewRef for the main flutter app launching the embedded child views.
|
||||
std::shared_ptr<fuchsia::ui::views::ViewRef> host_view_ref_;
|
||||
|
||||
// ViewRef for a flatland view. For GFX this value is set as std::nullopt.
|
||||
// Set in |OnCreateView|.
|
||||
std::optional<fuchsia::ui::views::ViewRef> view_ref_;
|
||||
|
||||
fuchsia::ui::pointerinjector::DevicePtr device_;
|
||||
|
||||
// A queue containing all the pending |fuchsia.ui.pointerinjector.Event|s
|
||||
// which have to be dispatched to the view.
|
||||
// Note: The size of a vector inside |injector_events_| should not exceed
|
||||
// |fuchsia.ui.pointerinjector.MAX_INJECT|.
|
||||
std::queue<std::vector<fuchsia::ui::pointerinjector::Event>>
|
||||
injector_events_;
|
||||
|
||||
fml::WeakPtrFactory<PointerInjectorEndpoint>
|
||||
weak_factory_; // Must be the last member.
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PointerInjectorEndpoint);
|
||||
};
|
||||
|
||||
void Complete(fml::RefPtr<flutter::PlatformMessageResponse> response,
|
||||
std::string value);
|
||||
|
||||
// Generates a |fuchsia.ui.pointerinjector.Event| from |request| by extracting
|
||||
// information like timestamp, trace flow id and pointer sample from
|
||||
// |request|.
|
||||
static fuchsia::ui::pointerinjector::Event ExtractPointerEvent(
|
||||
PointerInjectorRequest request);
|
||||
|
||||
// A map of valid views keyed by its view id. A view can receive pointer
|
||||
// events only if it is present in |valid_views_|.
|
||||
std::unordered_map<ViewId, PointerInjectorEndpoint> valid_views_;
|
||||
|
||||
std::shared_ptr<fuchsia::ui::pointerinjector::RegistryPtr> registry_;
|
||||
|
||||
// ViewRef for the main flutter app launching the embedded child views.
|
||||
std::shared_ptr<fuchsia::ui::views::ViewRef> host_view_ref_;
|
||||
|
||||
bool is_flatland_ = false;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PointerInjectorDelegate);
|
||||
};
|
||||
|
||||
} // namespace flutter_runner
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_POINTER_INJECTOR_DELEGATE_H_
|
||||
@ -0,0 +1,688 @@
|
||||
// Copyright 2013 The Flutter 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 <fuchsia/ui/pointerinjector/cpp/fidl.h>
|
||||
#include <fuchsia/ui/views/cpp/fidl.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/async-loop/default.h>
|
||||
#include <lib/async/cpp/task.h>
|
||||
|
||||
#include <lib/fidl/cpp/binding_set.h>
|
||||
#include <lib/ui/scenic/cpp/view_ref_pair.h>
|
||||
|
||||
#include "pointer_injector_delegate.h"
|
||||
#include "tests/fakes/mock_injector_registry.h"
|
||||
#include "tests/fakes/platform_message.h"
|
||||
|
||||
namespace flutter_runner::testing {
|
||||
|
||||
using fup_DeviceType = fuchsia::ui::pointerinjector::DeviceType;
|
||||
using fup_DispatchPolicy = fuchsia::ui::pointerinjector::DispatchPolicy;
|
||||
using fup_EventPhase = fuchsia::ui::pointerinjector::EventPhase;
|
||||
using fup_RegistryHandle = fuchsia::ui::pointerinjector::RegistryHandle;
|
||||
using fuv_ViewRef = fuchsia::ui::views::ViewRef;
|
||||
|
||||
namespace {
|
||||
|
||||
// clang-format off
|
||||
static constexpr std::array<float, 9> kIdentityMatrix = {
|
||||
1, 0, 0, // column one
|
||||
0, 1, 0, // column two
|
||||
0, 0, 1, // column three
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
rapidjson::Value ParsePlatformMessage(std::string json) {
|
||||
rapidjson::Document document;
|
||||
document.Parse(json);
|
||||
if (document.HasParseError() || !document.IsObject()) {
|
||||
FML_LOG(ERROR) << "Could not parse document";
|
||||
return rapidjson::Value();
|
||||
}
|
||||
return document.GetObject();
|
||||
}
|
||||
|
||||
zx_koid_t ExtractKoid(const zx::object_base& object) {
|
||||
zx_info_handle_basic_t info{};
|
||||
if (object.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr,
|
||||
nullptr) != ZX_OK) {
|
||||
return ZX_KOID_INVALID; // no info
|
||||
}
|
||||
|
||||
return info.koid;
|
||||
}
|
||||
|
||||
zx_koid_t ExtractKoid(const fuv_ViewRef& view_ref) {
|
||||
return ExtractKoid(view_ref.reference);
|
||||
}
|
||||
|
||||
class PlatformMessageBuilder {
|
||||
public:
|
||||
PlatformMessageBuilder& SetViewId(uint64_t view_id) {
|
||||
view_id_ = view_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetPointerX(float x) {
|
||||
pointer_x_ = x;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetPointerY(float y) {
|
||||
pointer_y_ = y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetPhase(int phase) {
|
||||
phase_ = phase;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetPointerId(int pointer_id) {
|
||||
pointer_id_ = pointer_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetTraceFlowId(int trace_flow_id) {
|
||||
trace_flow_id_ = trace_flow_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetViewRefMaybe(std::optional<fuv_ViewRef> view_ref) {
|
||||
if (view_ref.has_value()) {
|
||||
view_ref_ = std::move(*view_ref);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetLogicalWidth(float width) {
|
||||
width_ = width;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetLogicalHeight(float height) {
|
||||
height_ = height;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformMessageBuilder& SetTimestamp(int timestamp) {
|
||||
timestamp_ = timestamp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
rapidjson::Value Build() {
|
||||
std::ostringstream message;
|
||||
message << "{"
|
||||
<< " \"method\":\""
|
||||
<< PointerInjectorDelegate::kPointerInjectorMethodPrefix << "\","
|
||||
<< " \"args\": {"
|
||||
<< " \"viewId\":" << view_id_ << ","
|
||||
<< " \"x\":" << pointer_x_ << ","
|
||||
<< " \"y\":" << pointer_y_ << ","
|
||||
<< " \"phase\":" << phase_ << ","
|
||||
<< " \"pointerId\":" << pointer_id_ << ","
|
||||
<< " \"traceFlowId\":" << trace_flow_id_ << ","
|
||||
<< " \"viewRef\":" << view_ref_.reference.get() << ","
|
||||
<< " \"logicalWidth\":" << width_ << ","
|
||||
<< " \"logicalHeight\":" << height_ << ","
|
||||
<< " \"timestamp\":" << timestamp_ << " }"
|
||||
<< "}";
|
||||
return ParsePlatformMessage(message.str());
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t view_id_ = 0;
|
||||
float pointer_x_ = 0.f, pointer_y_ = 0.f;
|
||||
int phase_ = 1, pointer_id_ = 0, trace_flow_id_ = 0;
|
||||
fuv_ViewRef view_ref_;
|
||||
float width_ = 0.f, height_ = 0.f;
|
||||
int timestamp_ = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class PointerInjectorDelegateTest : public ::testing::Test,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
PointerInjectorDelegateTest()
|
||||
: loop_(&kAsyncLoopConfigAttachToCurrentThread) {}
|
||||
|
||||
// TODO(fxbug.dev/104285): Replace the RunLoop methods with the one provided
|
||||
// by the sdk.
|
||||
void RunLoopUntilIdle() { loop_.RunUntilIdle(); }
|
||||
|
||||
bool RunGivenLoopWithTimeout(async::Loop* loop, zx::duration timeout) {
|
||||
// This cannot be a local variable because the delayed task below can
|
||||
// execute after this function returns.
|
||||
auto canceled = std::make_shared<bool>(false);
|
||||
bool timed_out = false;
|
||||
async::PostDelayedTask(
|
||||
loop->dispatcher(),
|
||||
[loop, canceled, &timed_out] {
|
||||
if (*canceled) {
|
||||
return;
|
||||
}
|
||||
timed_out = true;
|
||||
loop->Quit();
|
||||
},
|
||||
timeout);
|
||||
loop->Run();
|
||||
loop->ResetQuit();
|
||||
|
||||
if (!timed_out) {
|
||||
*canceled = true;
|
||||
}
|
||||
return timed_out;
|
||||
}
|
||||
|
||||
bool RunLoopWithTimeoutOrUntil(fit::function<bool()> condition,
|
||||
zx::duration timeout,
|
||||
zx::duration step) {
|
||||
const zx::time timeout_deadline = zx::deadline_after(timeout);
|
||||
|
||||
while (zx::clock::get_monotonic() < timeout_deadline &&
|
||||
loop_.GetState() == ASYNC_LOOP_RUNNABLE) {
|
||||
if (condition()) {
|
||||
loop_.ResetQuit();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (step == zx::duration::infinite()) {
|
||||
// Performs a single unit of work, possibly blocking until there is work
|
||||
// to do or the timeout deadline arrives.
|
||||
loop_.Run(timeout_deadline, true);
|
||||
} else {
|
||||
// Performs work until the step deadline arrives.
|
||||
RunGivenLoopWithTimeout(&loop_, step);
|
||||
}
|
||||
}
|
||||
|
||||
loop_.ResetQuit();
|
||||
return condition();
|
||||
}
|
||||
|
||||
void RunLoopUntil(fit::function<bool()> condition,
|
||||
zx::duration step = zx::msec(10)) {
|
||||
RunLoopWithTimeoutOrUntil(std::move(condition), zx::duration::infinite(),
|
||||
step);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
host_view_ref_ = std::move(view_ref_pair.view_ref);
|
||||
|
||||
fup_RegistryHandle registry;
|
||||
registry_ = std::make_unique<MockInjectorRegistry>(registry.NewRequest());
|
||||
|
||||
fuv_ViewRef host_view_ref_clone;
|
||||
fidl::Clone(host_view_ref_, &host_view_ref_clone);
|
||||
|
||||
is_flatland_ = GetParam();
|
||||
pointer_injector_delegate_ = std::make_unique<PointerInjectorDelegate>(
|
||||
std::move(registry), std::move(host_view_ref_clone), is_flatland_);
|
||||
}
|
||||
|
||||
void CreateView(uint64_t view_id,
|
||||
std::optional<fuv_ViewRef> view_ref = std::nullopt) {
|
||||
if (!is_flatland_) {
|
||||
pointer_injector_delegate_->OnCreateView(view_id);
|
||||
} else {
|
||||
fuv_ViewRef ref;
|
||||
if (view_ref.has_value()) {
|
||||
ref = std::move(*view_ref);
|
||||
} else {
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
ref = std::move(view_ref_pair.view_ref);
|
||||
}
|
||||
pointer_injector_delegate_->OnCreateView(view_id, std::move(ref));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<PointerInjectorDelegate> pointer_injector_delegate_;
|
||||
std::unique_ptr<MockInjectorRegistry> registry_;
|
||||
fuv_ViewRef host_view_ref_;
|
||||
bool is_flatland_ = false;
|
||||
|
||||
private:
|
||||
async::Loop loop_;
|
||||
};
|
||||
|
||||
TEST_P(PointerInjectorDelegateTest, IncorrectPlatformMessage_ShouldFail) {
|
||||
const uint64_t view_id = 1;
|
||||
|
||||
// Create a view.
|
||||
CreateView(view_id);
|
||||
|
||||
// A platform message in incorrect JSON format should fail.
|
||||
{
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
ParsePlatformMessage("{Incorrect Json}"), response));
|
||||
}
|
||||
|
||||
// |PointerInjectorDelegate| only handles "View.Pointerinjector.inject"
|
||||
// platform messages.
|
||||
{
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
ParsePlatformMessage("{\"method\":\"View.focus.getCurrent\"}"),
|
||||
response));
|
||||
}
|
||||
|
||||
// A platform message with no args should fail.
|
||||
{
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
ParsePlatformMessage("{\"method\":\"View.Pointerinjector.inject\"}"),
|
||||
response));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(PointerInjectorDelegateTest, ViewsReceiveInjectedEvents) {
|
||||
const uint64_t num_events = 150;
|
||||
|
||||
// Inject |num_events| platform messages for view 1.
|
||||
{
|
||||
const uint64_t view_id = 1;
|
||||
|
||||
CreateView(view_id);
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
}
|
||||
}
|
||||
|
||||
// Inject |num_events| platform messages for view 2.
|
||||
{
|
||||
const uint64_t view_id = 2;
|
||||
|
||||
CreateView(view_id);
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
}
|
||||
}
|
||||
|
||||
// The mock Pointerinjector registry server receives |num_events| pointer
|
||||
// events from |f.u.p.Device.Inject| calls for each view.
|
||||
RunLoopUntil(
|
||||
[this] { return registry_->num_events_received() == 2 * num_events; });
|
||||
|
||||
// The mock Pointerinjector registry server receives a
|
||||
// |f.u.p.Registry.Register| call for each view.
|
||||
EXPECT_TRUE(registry_->num_register_calls() == 2);
|
||||
}
|
||||
|
||||
TEST_P(PointerInjectorDelegateTest,
|
||||
ViewsDontReceivePointerEventsBeforeCreation) {
|
||||
const uint64_t num_events = 150;
|
||||
const uint64_t view_id_1 = 1;
|
||||
|
||||
// Inject |num_events| platform messages for |view_id_1|.
|
||||
{
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id_1)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t view_id_2 = 2;
|
||||
|
||||
// Inject |num_events| platform messages for |view_id_2|.
|
||||
{
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id_2)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
}
|
||||
}
|
||||
|
||||
RunLoopUntilIdle();
|
||||
|
||||
// The views do not receive any pointer events till they get created.
|
||||
EXPECT_TRUE(registry_->num_events_received() == 0);
|
||||
}
|
||||
|
||||
// PointerInjectorDelegate should generate a correct |f.u.p.Config| from a
|
||||
// platform message.
|
||||
TEST_P(PointerInjectorDelegateTest, ValidRegistrationConfigTest) {
|
||||
const uint64_t view_id = 1;
|
||||
|
||||
const float x = 2.f, y = 2.f, width = 5.f, height = 5.f;
|
||||
const int phase = 2, pointer_id = 5, trace_flow_id = 5, timestamp = 10;
|
||||
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
|
||||
// Create the view.
|
||||
if (!is_flatland_) {
|
||||
CreateView(view_id);
|
||||
fuv_ViewRef temp_ref;
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
} else {
|
||||
fuv_ViewRef view_ref;
|
||||
fidl::Clone(view_ref_pair.view_ref, &view_ref);
|
||||
CreateView(view_id, std::move(view_ref));
|
||||
}
|
||||
|
||||
// Inject a platform message.
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetPointerX(x)
|
||||
.SetPointerY(y)
|
||||
.SetPhase(phase)
|
||||
.SetPointerId(pointer_id)
|
||||
.SetTraceFlowId(trace_flow_id)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.SetLogicalWidth(width)
|
||||
.SetLogicalHeight(height)
|
||||
.SetTimestamp(timestamp)
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
|
||||
// The mock Pointerinjector registry server receives a pointer event from
|
||||
// |f.u.p.Device.Inject| call for the view.
|
||||
RunLoopUntil([this] { return registry_->num_events_received() == 1; });
|
||||
|
||||
// The mock Pointerinjector registry server receives a
|
||||
// |f.u.p.Registry.Register| call for the view.
|
||||
ASSERT_TRUE(registry_->num_register_calls() == 1);
|
||||
|
||||
const auto& config = registry_->config();
|
||||
|
||||
ASSERT_TRUE(config.has_device_id());
|
||||
EXPECT_EQ(config.device_id(), 1u);
|
||||
|
||||
ASSERT_TRUE(config.has_device_type());
|
||||
EXPECT_EQ(config.device_type(), fup_DeviceType::TOUCH);
|
||||
|
||||
ASSERT_TRUE(config.has_dispatch_policy());
|
||||
EXPECT_EQ(config.dispatch_policy(), fup_DispatchPolicy::EXCLUSIVE_TARGET);
|
||||
|
||||
ASSERT_TRUE(config.has_context());
|
||||
ASSERT_TRUE(config.context().is_view());
|
||||
EXPECT_EQ(ExtractKoid(config.context().view()), ExtractKoid(host_view_ref_));
|
||||
|
||||
ASSERT_TRUE(config.has_target());
|
||||
ASSERT_TRUE(config.target().is_view());
|
||||
EXPECT_EQ(ExtractKoid(config.target().view()),
|
||||
ExtractKoid(view_ref_pair.view_ref));
|
||||
|
||||
ASSERT_TRUE(config.has_viewport());
|
||||
ASSERT_TRUE(config.viewport().has_viewport_to_context_transform());
|
||||
EXPECT_EQ(config.viewport().viewport_to_context_transform(), kIdentityMatrix);
|
||||
|
||||
std::array<std::array<float, 2>, 2> extents{{{0, 0}, {width, height}}};
|
||||
ASSERT_TRUE(config.viewport().has_extents());
|
||||
EXPECT_EQ(config.viewport().extents(), extents);
|
||||
}
|
||||
|
||||
// PointerInjectorDelegate generates a correct f.u.p.Event from the platform
|
||||
// message.
|
||||
TEST_P(PointerInjectorDelegateTest, ValidPointerEventTest) {
|
||||
const uint64_t view_id = 1;
|
||||
|
||||
const float x = 2.f, y = 2.f, width = 5.f, height = 5.f;
|
||||
const int phase = 2, pointer_id = 5, trace_flow_id = 5, timestamp = 10;
|
||||
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
|
||||
// Create the view.
|
||||
if (!is_flatland_) {
|
||||
CreateView(view_id);
|
||||
fuv_ViewRef temp_ref;
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
} else {
|
||||
fuv_ViewRef view_ref;
|
||||
fidl::Clone(view_ref_pair.view_ref, &view_ref);
|
||||
CreateView(view_id, std::move(view_ref));
|
||||
}
|
||||
|
||||
// Inject a platform message.
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetPointerX(x)
|
||||
.SetPointerY(y)
|
||||
.SetPhase(phase)
|
||||
.SetPointerId(pointer_id)
|
||||
.SetTraceFlowId(trace_flow_id)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.SetLogicalWidth(width)
|
||||
.SetLogicalHeight(height)
|
||||
.SetTimestamp(timestamp)
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
|
||||
// The mock Pointerinjector registry server receives a pointer event from
|
||||
// |f.u.p.Device.Inject| call for the view.
|
||||
RunLoopUntil([this] { return registry_->num_events_received() == 1; });
|
||||
|
||||
// The mock Pointerinjector registry server receives a
|
||||
// |f.u.p.Registry.Register| call for the view.
|
||||
ASSERT_TRUE(registry_->num_register_calls() == 1);
|
||||
|
||||
const auto& events = registry_->events();
|
||||
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
|
||||
const auto& event = events[0];
|
||||
|
||||
ASSERT_TRUE(event.has_timestamp());
|
||||
EXPECT_EQ(event.timestamp(), timestamp);
|
||||
|
||||
ASSERT_TRUE(event.has_trace_flow_id());
|
||||
EXPECT_EQ(event.trace_flow_id(), static_cast<uint64_t>(trace_flow_id));
|
||||
|
||||
ASSERT_TRUE(event.has_data());
|
||||
ASSERT_TRUE(event.data().is_pointer_sample());
|
||||
|
||||
const auto& pointer_sample = event.data().pointer_sample();
|
||||
|
||||
ASSERT_TRUE(pointer_sample.has_pointer_id());
|
||||
ASSERT_TRUE(pointer_sample.has_phase());
|
||||
ASSERT_TRUE(pointer_sample.has_position_in_viewport());
|
||||
EXPECT_EQ(pointer_sample.pointer_id(), static_cast<uint32_t>(pointer_id));
|
||||
EXPECT_EQ(pointer_sample.phase(), static_cast<fup_EventPhase>(phase));
|
||||
EXPECT_THAT(pointer_sample.position_in_viewport(),
|
||||
::testing::ElementsAre(x, y));
|
||||
}
|
||||
|
||||
TEST_P(PointerInjectorDelegateTest, DestroyedViewsDontGetPointerEvents) {
|
||||
const uint64_t view_id = 1, num_events = 150;
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
// Create the view.
|
||||
CreateView(view_id);
|
||||
|
||||
// Inject |num_events| platform messages.
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
}
|
||||
|
||||
// Destroy the view.
|
||||
pointer_injector_delegate_->OnDestroyView(view_id);
|
||||
|
||||
// The view does not receive |num_events| pointer events as it gets destroyed
|
||||
// before all the pointer events could be dispatched.
|
||||
const zx::duration timeout = zx::sec(1), step = zx::msec(10);
|
||||
EXPECT_FALSE(RunLoopWithTimeoutOrUntil(
|
||||
[this] { return registry_->num_events_received() == num_events; },
|
||||
timeout, step));
|
||||
|
||||
EXPECT_LT(registry_->num_events_received(), num_events);
|
||||
}
|
||||
|
||||
TEST_P(PointerInjectorDelegateTest, ViewsGetPointerEventsInFIFO) {
|
||||
const uint64_t view_id = 1, num_events = 150;
|
||||
|
||||
auto view_ref_pair = scenic::ViewRefPair::New();
|
||||
|
||||
// Create the view.
|
||||
CreateView(view_id);
|
||||
|
||||
// Inject |num_events| platform messages.
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
auto response = FakePlatformMessageResponse::Create();
|
||||
|
||||
// Flatland views do not rely on ViewRef to be passed in the platform
|
||||
// message.
|
||||
std::optional<fuv_ViewRef> view_ref_clone;
|
||||
if (fuv_ViewRef temp_ref; !is_flatland_) {
|
||||
fidl::Clone(view_ref_pair.view_ref, &temp_ref);
|
||||
view_ref_clone = std::move(temp_ref);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage(
|
||||
PlatformMessageBuilder()
|
||||
.SetViewId(view_id)
|
||||
.SetPointerId(static_cast<uint32_t>(i))
|
||||
.SetViewRefMaybe(std::move(view_ref_clone))
|
||||
.Build(),
|
||||
response));
|
||||
|
||||
response->ExpectCompleted("[0]");
|
||||
}
|
||||
|
||||
// The mock Pointerinjector registry server receives |num_events| pointer
|
||||
// events from |f.u.p.Device.Inject| call for the view.
|
||||
RunLoopUntil(
|
||||
[this] { return registry_->num_events_received() == num_events; });
|
||||
|
||||
// The mock Pointerinjector registry server receives a
|
||||
// |f.u.p.Registry.Register| call for the view.
|
||||
ASSERT_TRUE(registry_->num_register_calls() == 1);
|
||||
|
||||
auto& events = registry_->events();
|
||||
|
||||
// The view should receive the pointer events in a FIFO order. As we injected
|
||||
// platform messages with an increasing |pointer_id|, the received pointer
|
||||
// events should also have the |pointer_id| in an increasing order.
|
||||
for (size_t i = 0; i < events.size() - 1; i++) {
|
||||
ASSERT_TRUE(events[i].has_data());
|
||||
ASSERT_TRUE(events[i + 1].has_data());
|
||||
ASSERT_TRUE(events[i].data().is_pointer_sample());
|
||||
ASSERT_TRUE(events[i + 1].data().is_pointer_sample());
|
||||
|
||||
const auto& pointer_sample_1 = events[i].data().pointer_sample();
|
||||
const auto& pointer_sample_2 = events[i + 1].data().pointer_sample();
|
||||
|
||||
ASSERT_TRUE(pointer_sample_1.has_pointer_id());
|
||||
ASSERT_TRUE(pointer_sample_2.has_pointer_id());
|
||||
|
||||
EXPECT_TRUE(pointer_sample_1.pointer_id() < pointer_sample_2.pointer_id());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(PointerInjectorDelegateParameterizedTest,
|
||||
PointerInjectorDelegateTest,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace flutter_runner::testing
|
||||
@ -9,6 +9,7 @@ group("fakes") {
|
||||
|
||||
public_deps = [
|
||||
":focus",
|
||||
":pointer",
|
||||
"scenic",
|
||||
]
|
||||
}
|
||||
@ -30,3 +31,16 @@ source_set("focus") {
|
||||
"//third_party/rapidjson",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("pointer") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "mock_injector_registry.h" ]
|
||||
|
||||
deps = [
|
||||
"//build/fuchsia/pkg:sys_cpp_testing",
|
||||
"//flutter/lib/ui",
|
||||
"//flutter/testing",
|
||||
"//third_party/rapidjson",
|
||||
]
|
||||
}
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_TESTS_MOCK_INJECTOR_REGISTRY_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_TESTS_MOCK_INJECTOR_REGISTRY_H_
|
||||
|
||||
#include <fuchsia/ui/pointerinjector/cpp/fidl.h>
|
||||
#include <lib/fidl/cpp/binding_set.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace flutter_runner::testing {
|
||||
|
||||
// A test stub to act as the protocol server. A test can control what is sent
|
||||
// back by this server implementation, via the ScheduleCallback call.
|
||||
class MockInjectorRegistry : public fuchsia::ui::pointerinjector::Registry,
|
||||
public fuchsia::ui::pointerinjector::Device {
|
||||
public:
|
||||
explicit MockInjectorRegistry(
|
||||
fidl::InterfaceRequest<fuchsia::ui::pointerinjector::Registry> registry)
|
||||
: registry_(this, std::move(registry)) {}
|
||||
|
||||
// |fuchsia.ui.pointerinjector.Registry.Register|.
|
||||
void Register(
|
||||
fuchsia::ui::pointerinjector::Config config,
|
||||
fidl::InterfaceRequest<fuchsia::ui::pointerinjector::Device> injector,
|
||||
RegisterCallback callback) override {
|
||||
num_register_calls_++;
|
||||
const uint32_t id = next_id_++;
|
||||
|
||||
auto [it, success] = bindings_.try_emplace(id, this, std::move(injector));
|
||||
|
||||
it->second.set_error_handler(
|
||||
[this, id](zx_status_t status) { bindings_.erase(id); });
|
||||
|
||||
config_ = std::move(config);
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
// |fuchsia.ui.pointerinjector.Device.Inject|.
|
||||
void Inject(std::vector<fuchsia::ui::pointerinjector::Event> events,
|
||||
InjectCallback callback) override {
|
||||
num_events_received_ += events.size();
|
||||
|
||||
for (auto& event : events) {
|
||||
events_.push_back(std::move(event));
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
|
||||
// Returns the |fuchsia::ui::pointerinjector::Config| received in the last
|
||||
// |Register(...)| call.
|
||||
const fuchsia::ui::pointerinjector::Config& config() const { return config_; }
|
||||
|
||||
// Returns all the |fuchsia::ui::pointerinjector::Event|s received from the
|
||||
// |Inject(...)| calls.
|
||||
const std::vector<fuchsia::ui::pointerinjector::Event>& events() const {
|
||||
return events_;
|
||||
}
|
||||
|
||||
uint32_t num_register_calls() { return num_register_calls_; }
|
||||
|
||||
size_t num_registered() { return bindings_.size(); }
|
||||
|
||||
uint32_t num_events_received() const { return num_events_received_; }
|
||||
|
||||
private:
|
||||
uint32_t next_id_ = 0;
|
||||
|
||||
uint32_t num_events_received_ = 0;
|
||||
|
||||
uint32_t num_register_calls_ = 0;
|
||||
|
||||
fuchsia::ui::pointerinjector::Config config_;
|
||||
|
||||
std::vector<fuchsia::ui::pointerinjector::Event> events_;
|
||||
|
||||
std::unordered_map<uint32_t,
|
||||
fidl::Binding<fuchsia::ui::pointerinjector::Device>>
|
||||
bindings_;
|
||||
|
||||
fidl::Binding<fuchsia::ui::pointerinjector::Registry> registry_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(MockInjectorRegistry);
|
||||
};
|
||||
} // namespace flutter_runner::testing
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_TESTS_MOCK_INJECTOR_REGISTRY_H_
|
||||
@ -514,7 +514,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
|
||||
external_view_embedder.CreateView(
|
||||
child_view_id, []() {},
|
||||
[](fuchsia::ui::composition::ContentId,
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr) {});
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle) {});
|
||||
|
||||
// Draw the scene. The scene graph shouldn't change yet.
|
||||
const SkISize frame_size_signed = SkISize::Make(512, 512);
|
||||
@ -645,7 +645,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) {
|
||||
external_view_embedder.CreateView(
|
||||
child_view_id, []() {},
|
||||
[](fuchsia::ui::composition::ContentId,
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr) {});
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle) {});
|
||||
|
||||
// Draw the scene. The scene graph shouldn't change yet.
|
||||
const SkISize frame_size_signed = SkISize::Make(512, 512);
|
||||
@ -758,7 +758,7 @@ TEST_F(FlatlandExternalViewEmbedderTest,
|
||||
external_view_embedder.CreateView(
|
||||
child_view_id, []() {},
|
||||
[](fuchsia::ui::composition::ContentId,
|
||||
fuchsia::ui::composition::ChildViewWatcherPtr) {});
|
||||
fuchsia::ui::composition::ChildViewWatcherHandle) {});
|
||||
|
||||
// Draw the scene without the view. The scene graph shouldn't change yet.
|
||||
const SkISize frame_size_signed = SkISize::Make(512, 512);
|
||||
|
||||
@ -375,6 +375,12 @@ class PlatformViewBuilder {
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetPointerInjectorRegistry(
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry) {
|
||||
pointerinjector_registry_ = std::move(pointerinjector_registry);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PlatformViewBuilder& SetEnableWireframeCallback(OnEnableWireframe callback) {
|
||||
wireframe_enabled_callback_ = std::move(callback);
|
||||
return *this;
|
||||
@ -422,6 +428,7 @@ class PlatformViewBuilder {
|
||||
std::move(keyboard_), std::move(touch_source_),
|
||||
std::move(mouse_source_), std::move(focuser_),
|
||||
std::move(view_ref_focused_), std::move(parent_viewport_watcher_),
|
||||
std::move(pointerinjector_registry_),
|
||||
std::move(wireframe_enabled_callback_),
|
||||
std::move(on_create_view_callback_),
|
||||
std::move(on_update_view_callback_),
|
||||
@ -447,6 +454,7 @@ class PlatformViewBuilder {
|
||||
fuchsia::ui::pointer::MouseSourceHandle mouse_source_;
|
||||
fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused_;
|
||||
fuchsia::ui::views::FocuserHandle focuser_;
|
||||
fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry_;
|
||||
fit::closure on_session_listener_error_callback_;
|
||||
OnEnableWireframe wireframe_enabled_callback_;
|
||||
fuchsia::ui::composition::ParentViewportWatcherHandle
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user