| // 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_FLUTTER_TESTS_FAKES_SCENIC_FAKE_FLATLAND_H_ |
| #define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_TESTS_FAKES_SCENIC_FAKE_FLATLAND_H_ |
| |
| #include <fuchsia/math/cpp/fidl.h> |
| #include <fuchsia/scenic/scheduling/cpp/fidl.h> |
| #include <fuchsia/sysmem/cpp/fidl.h> |
| #include <fuchsia/ui/composition/cpp/fidl.h> |
| #include <fuchsia/ui/composition/cpp/fidl_test_base.h> |
| #include <fuchsia/ui/views/cpp/fidl.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/fidl/cpp/interface_request.h> |
| |
| #include <cstdint> |
| #include <functional> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "flutter/fml/logging.h" |
| #include "flutter/fml/macros.h" |
| |
| #include "fake_flatland_types.h" |
| |
| namespace flutter_runner::testing { |
| |
| // A lightweight fake implementation of the Flatland API. |
| // |
| // The fake has no side effects besides mutating its own internal state |
| // according to the rules of interacting with the Flatland API. It makes that |
| // internal state available for inspection by a test. |
| // |
| // Thus it allows tests to do a few things that would be difficult using either |
| // a mock implementation or the real implementation: |
| // + It allows the user to hook `Present` invocations and respond with |
| // stubbed-out `FuturePresentationTimes`, but more crucially it mimics the |
| // real Flatland behavior of only processing commands when a `Present` is |
| // invoked. |
| // + It allows the user to inspect a snapshot of the scene graph at any moment |
| // in time, via the `SceneGraph()` accessor. |
| // + It stores the various Flatland transforms and content generated by |
| // commands into a std::unordered_map, and also correctly manages the |
| // transform and content lifetimes via reference counting. This allows a |
| // given transform or content to stay alive if its parent still holds a |
| // reference to it, in the same way the real Flatland implementation would. |
| // + The resources returned by `SceneGraph()` that the test uses for |
| // inspection are decoupled from the resources managed internally by the |
| // `FakeFlatland` itself -- they are a snapshot of the scene graph at that |
| // moment in time, with all snapshot state being cloned from the underlying |
| // scene graph state. This allows the `FakeFlatland` and test to naturally |
| // use `shared_ptr` for reference counting and mimic the real Flatland |
| // behavior exactly, instead of an awkward index-based API. |
| // + TODO(fxb/85619): Allow injecting {touch,mouse,focus,views} events using |
| // the ViewBoundProtocols and {ParentViewport,ChildView}Watcher. |
| // + TODO(fxb/85619): Error handling / flatland disconnection is still WIP. |
| // FakeFlatland will likely generate a CHECK in any place where the real |
| // Flatland would disconnect or send a FlatlandError. |
| // |
| // The fake deliberately does not attempt to handle certain aspects of the real |
| // Flatland implemntation which are complex or burdensome to implement: |
| // +Rendering/interacting with Vulkan in any way |
| // +Cross-flatland links between Views and Viewports. |
| class FakeFlatland |
| : public fuchsia::ui::composition::testing::Allocator_TestBase, |
| public fuchsia::ui::composition::testing::Flatland_TestBase { |
| public: |
| using PresentHandler = |
| std::function<void(fuchsia::ui::composition::PresentArgs)>; |
| |
| FakeFlatland(); |
| ~FakeFlatland() override; |
| |
| bool is_allocator_connected() const { return allocator_binding_.is_bound(); } |
| |
| bool is_flatland_connected() const { return flatland_binding_.is_bound(); } |
| |
| const std::string& debug_name() const { return debug_name_; } |
| |
| const FakeGraph& graph() { return current_graph_; } |
| |
| // Bind this instance's Flatland FIDL channel to the `dispatcher` and allow |
| // processing of incoming FIDL requests. |
| // |
| // This can only be called once. |
| fuchsia::ui::composition::AllocatorHandle ConnectAllocator( |
| async_dispatcher_t* dispatcher = nullptr); |
| |
| // Bind this instance's Allocator FIDL channel to the `dispatcher` and allow |
| // processing of incoming FIDL requests. |
| // |
| // This can only be called once. |
| fuchsia::ui::composition::FlatlandHandle ConnectFlatland( |
| async_dispatcher_t* dispatcher = nullptr); |
| |
| // Disconnect the Flatland's FIDL channel with an error. |
| // TODO(fxb/85619): Call this internally upon command error, instead of |
| // CHECK'ing. |
| void Disconnect(fuchsia::ui::composition::FlatlandError error); |
| |
| // Set a handler for `Present`-related FIDL calls' return values. |
| void SetPresentHandler(PresentHandler present_handler); |
| |
| // Fire an `OnNextFrameBegin` event. Call this first after a `Present` in |
| // order to give additional present tokens to the client and simulate scenic's |
| // normal event flow. |
| void FireOnNextFrameBeginEvent( |
| fuchsia::ui::composition::OnNextFrameBeginValues |
| on_next_frame_begin_values); |
| |
| // Fire an `OnFramePresented` event. Call this second after a `Present` in |
| // order to inform the client of retured frames and simulate scenic's normal |
| // event flow. |
| void FireOnFramePresentedEvent( |
| fuchsia::scenic::scheduling::FramePresentedInfo frame_presented_info); |
| |
| private: |
| struct ParentViewportWatcher : public fuchsia::ui::composition::testing:: |
| ParentViewportWatcher_TestBase { |
| public: |
| ParentViewportWatcher( |
| fuchsia::ui::views::ViewCreationToken view_token, |
| fuchsia::ui::views::ViewIdentityOnCreation view_identity, |
| fuchsia::ui::composition::ViewBoundProtocols view_protocols, |
| fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher> |
| parent_viewport_watcher, |
| async_dispatcher_t* dispatcher); |
| ParentViewportWatcher(ParentViewportWatcher&& other); |
| ~ParentViewportWatcher() override; |
| |
| // |fuchsia::ui::composition::testing::ParentViewportWatcher_TestBase| |
| void NotImplemented_(const std::string& name) override; |
| |
| // |fuchsia::ui::composition::ParentViewportWatcher| |
| void GetLayout(GetLayoutCallback callback) override; |
| |
| // |fuchsia::ui::composition::ParentViewportWatcher| |
| void GetStatus(GetStatusCallback callback) override; |
| |
| fuchsia::ui::views::ViewCreationToken view_token; |
| fuchsia::ui::views::ViewIdentityOnCreation view_identity; |
| fuchsia::ui::composition::ViewBoundProtocols view_protocols; |
| fidl::Binding<fuchsia::ui::composition::ParentViewportWatcher> |
| parent_viewport_watcher; |
| }; |
| |
| struct ChildViewWatcher |
| : public fuchsia::ui::composition::testing::ChildViewWatcher_TestBase { |
| public: |
| ChildViewWatcher( |
| fuchsia::ui::views::ViewportCreationToken viewport_token, |
| fidl::InterfaceRequest<fuchsia::ui::composition::ChildViewWatcher> |
| child_view_watcher, |
| async_dispatcher_t* dispatcher); |
| ChildViewWatcher(ChildViewWatcher&& other); |
| ~ChildViewWatcher() override; |
| |
| // |fuchsia::ui::composition::testing::ChildViewWatcher_TestBase| |
| void NotImplemented_(const std::string& name) override; |
| |
| // |fuchsia::ui::composition::ChildViewWatcher| |
| void GetStatus(GetStatusCallback callback) override; |
| |
| fuchsia::ui::views::ViewportCreationToken viewport_token; |
| fidl::Binding<fuchsia::ui::composition::ChildViewWatcher> |
| child_view_watcher; |
| }; |
| |
| struct ImageBinding { |
| fuchsia::ui::composition::BufferCollectionImportToken import_token; |
| }; |
| |
| struct BufferCollectionBinding { |
| fuchsia::ui::composition::BufferCollectionExportToken export_token; |
| fuchsia::sysmem::BufferCollectionTokenHandle sysmem_token; |
| |
| fuchsia::ui::composition::RegisterBufferCollectionUsage usage; |
| }; |
| |
| struct GraphBindings { |
| std::optional<std::pair<zx_koid_t, ParentViewportWatcher>> viewport_watcher; |
| std::unordered_map<zx_koid_t, ChildViewWatcher> view_watchers; |
| std::unordered_map<zx_koid_t, ImageBinding> images; |
| std::unordered_map<zx_koid_t, BufferCollectionBinding> buffer_collections; |
| }; |
| |
| // |fuchsia::ui::composition::testing::Allocator_TestBase| |
| // |fuchsia::ui::composition::testing::Flatland_TestBase| |
| void NotImplemented_(const std::string& name) override; |
| |
| // |fuchsia::ui::composition::testing::Allocator| |
| void RegisterBufferCollection( |
| fuchsia::ui::composition::RegisterBufferCollectionArgs args, |
| RegisterBufferCollectionCallback callback) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void Present(fuchsia::ui::composition::PresentArgs args) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void CreateView( |
| fuchsia::ui::views::ViewCreationToken token, |
| fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher> |
| parent_viewport_watcher) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void CreateView2( |
| fuchsia::ui::views::ViewCreationToken token, |
| fuchsia::ui::views::ViewIdentityOnCreation view_identity, |
| fuchsia::ui::composition::ViewBoundProtocols view_protocols, |
| fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher> |
| parent_viewport_watcher) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void CreateTransform( |
| fuchsia::ui::composition::TransformId transform_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetTranslation(fuchsia::ui::composition::TransformId transform_id, |
| fuchsia::math::Vec translation) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetOrientation( |
| fuchsia::ui::composition::TransformId transform_id, |
| fuchsia::ui::composition::Orientation orientation) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetClipBoundary(fuchsia::ui::composition::TransformId transform_id, |
| std::unique_ptr<fuchsia::math::Rect> bounds) override; |
| |
| // TODO(fxbug.dev/89111): Re-enable once SDK rolls. |
| // // |fuchsia::ui::composition::Flatland| |
| // void SetImageOpacity(fuchsia::ui::composition::ContentId image_id, |
| // float opacity) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void AddChild( |
| fuchsia::ui::composition::TransformId parent_transform_id, |
| fuchsia::ui::composition::TransformId child_transform_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void RemoveChild( |
| fuchsia::ui::composition::TransformId parent_transform_id, |
| fuchsia::ui::composition::TransformId child_transform_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetContent(fuchsia::ui::composition::TransformId transform_id, |
| fuchsia::ui::composition::ContentId content_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetRootTransform( |
| fuchsia::ui::composition::TransformId transform_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void CreateViewport( |
| fuchsia::ui::composition::ContentId viewport_id, |
| fuchsia::ui::views::ViewportCreationToken token, |
| fuchsia::ui::composition::ViewportProperties properties, |
| fidl::InterfaceRequest<fuchsia::ui::composition::ChildViewWatcher> |
| child_view_watcher) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void CreateImage( |
| fuchsia::ui::composition::ContentId image_id, |
| fuchsia::ui::composition::BufferCollectionImportToken import_token, |
| uint32_t vmo_index, |
| fuchsia::ui::composition::ImageProperties properties) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetImageSampleRegion(fuchsia::ui::composition::ContentId image_id, |
| fuchsia::math::RectF rect) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetImageDestinationSize(fuchsia::ui::composition::ContentId image_id, |
| fuchsia::math::SizeU size) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetImageBlendingFunction( |
| fuchsia::ui::composition::ContentId image_id, |
| fuchsia::ui::composition::BlendMode blend_mode) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetViewportProperties( |
| fuchsia::ui::composition::ContentId viewport_id, |
| fuchsia::ui::composition::ViewportProperties properties) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void ReleaseTransform( |
| fuchsia::ui::composition::TransformId transform_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void ReleaseViewport(fuchsia::ui::composition::ContentId viewport_id, |
| ReleaseViewportCallback callback) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void ReleaseImage(fuchsia::ui::composition::ContentId image_id) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetHitRegions( |
| fuchsia::ui::composition::TransformId transform_id, |
| std::vector<fuchsia::ui::composition::HitRegion> regions) override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void Clear() override; |
| |
| // |fuchsia::ui::composition::Flatland| |
| void SetDebugName(std::string debug_name) override; |
| |
| fidl::Binding<fuchsia::ui::composition::Allocator> allocator_binding_; |
| fidl::Binding<fuchsia::ui::composition::Flatland> flatland_binding_; |
| GraphBindings graph_bindings_; |
| PresentHandler present_handler_; |
| |
| FakeGraph pending_graph_; |
| FakeGraph current_graph_; |
| |
| // This map is used to cache parent refs for `AddChild` and `RemoveChild`. |
| // |
| // Ideally we would like to map weak(child) -> weak(parent), but std::weak_ptr |
| // cannot be the Key for an associative container. Instead we key on the raw |
| // parent pointer and store pair(weak(parent), weak(child)) in the map. |
| std::unordered_map< |
| FakeGraph::TransformIdKey, |
| std::pair<std::weak_ptr<FakeTransform>, std::weak_ptr<FakeTransform>>> |
| parents_map_; |
| |
| std::string debug_name_; |
| |
| FML_DISALLOW_COPY_AND_ASSIGN(FakeFlatland); |
| }; |
| |
| } // namespace flutter_runner::testing |
| |
| #endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_TESTS_FAKES_SCENIC_FAKE_FLATLAND_H_ |