chromium/components/exo/wayland/surface_augmenter_unittest.cc

// 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 <surface-augmenter-client-protocol.h>
#include <xdg-shell-client-protocol.h>
#include "build/build_config.h"

#include "base/memory/raw_ptr.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/sub_surface.h"
#include "components/exo/surface.h"
#include "components/exo/wayland/test/client_util.h"
#include "components/exo/wayland/test/server_util.h"
#include "components/exo/wayland/test/wayland_server_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/aura/aura_window_properties.h"
#include "ui/compositor/layer.h"

namespace exo::wayland {
namespace {

class ClientData : public test::TestClient::CustomData {
 public:
  // Don't leak augmented (sub)surface objects.
  ~ClientData() override {
    if (augmented_surface) {
      augmented_surface_destroy(augmented_surface);
    }
    if (augmented_sub_surface) {
      augmented_sub_surface_destroy(augmented_sub_surface);
    }
  }

  std::unique_ptr<wl_surface> parent_wl_surface;

  std::unique_ptr<wl_surface> child_wl_surface;
  std::unique_ptr<wl_subsurface> child_wl_subsurface;
  std::unique_ptr<wl_surface> child2_wl_surface;
  std::unique_ptr<wl_subsurface> child2_wl_subsurface;

  raw_ptr<augmented_surface, DanglingUntriaged> augmented_surface = nullptr;
  raw_ptr<augmented_sub_surface, DanglingUntriaged> augmented_sub_surface =
      nullptr;
};

using SurfaceAugmenterTest = test::WaylandServerTest;

TEST_F(SurfaceAugmenterTest, AugmentedSubSurfacesDontSendLeaveEnter) {
  // Create a surface.
  test::ResourceKey parent_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto data = std::make_unique<ClientData>();
    data->parent_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));
    parent_surface_key =
        test::client_util::GetResourceKey(data->parent_wl_surface.get());
    client->set_data(std::move(data));
  });
  Surface* parent_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), parent_surface_key);
  // Check that the surface sends enter/leave events.
  ASSERT_TRUE(parent_surface);
  EXPECT_TRUE(parent_surface->HasLeaveEnterCallbackForTesting());
  // Normal surface will create accessibility nodes.
  EXPECT_FALSE(parent_surface->window()->GetProperty(
      ui::kAXConsiderInvisibleAndIgnoreChildren));

  // Create another surface.
  test::ResourceKey child_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    ClientData* data = client->GetDataAs<ClientData>();
    data->child_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));
    child_surface_key =
        test::client_util::GetResourceKey(data->child_wl_surface.get());
  });
  Surface* child_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), child_surface_key);
  // Check that the surface sends enter/leave events.
  ASSERT_TRUE(child_surface);
  EXPECT_TRUE(child_surface->HasLeaveEnterCallbackForTesting());

  // Make it a subsurface of the first one.
  test::ResourceKey child_subsurface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    ClientData* data = client->GetDataAs<ClientData>();
    data->child_wl_subsurface.reset(wl_subcompositor_get_subsurface(
        client->subcompositor(), data->child_wl_surface.get(),
        data->parent_wl_surface.get()));
    child_subsurface_key =
        test::client_util::GetResourceKey(data->child_wl_subsurface.get());
  });
  SubSurface* child_subsurface =
      test::server_util::GetUserDataForResource<SubSurface>(
          server_.get(), child_subsurface_key);
  // Check that the surface sends enter/leave events.
  ASSERT_TRUE(child_subsurface);
  EXPECT_TRUE(child_surface->HasLeaveEnterCallbackForTesting());
  EXPECT_TRUE(child_subsurface->surface()->HasLeaveEnterCallbackForTesting());

  // Create yet another surface. Make it augmented.
  test::ResourceKey child2_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ClientData>();
    data->child2_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));
    data->augmented_surface = surface_augmenter_get_augmented_surface(
        client->surface_augmenter(), data->child2_wl_surface.get());
    child2_surface_key =
        test::client_util::GetResourceKey(data->child2_wl_surface.get());
  });
  Surface* child2_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), child2_surface_key);
  ASSERT_TRUE(child2_surface);

  // Make it a subsurface of the first one.
  test::ResourceKey child2_subsurface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ClientData>();

    data->child2_wl_subsurface.reset(wl_subcompositor_get_subsurface(
        client->subcompositor(), data->child2_wl_surface.get(),
        data->parent_wl_surface.get()));

    child2_subsurface_key =
        test::client_util::GetResourceKey(data->child2_wl_subsurface.get());
  });
  SubSurface* child2_subsurface =
      test::server_util::GetUserDataForResource<SubSurface>(
          server_.get(), child2_subsurface_key);
  ASSERT_TRUE(child2_subsurface);

  // Check that it does not send the events.
  EXPECT_FALSE(child2_surface->HasLeaveEnterCallbackForTesting());
  EXPECT_FALSE(child2_subsurface->surface()->HasLeaveEnterCallbackForTesting());
}

class ShellClientData : public ClientData {
 public:
  void CreateXdgToplevel(test::TestClient* client, wl_surface* surface) {
    ASSERT_FALSE(xdg_surface_);
    xdg_surface_.reset(
        xdg_wm_base_get_xdg_surface(client->xdg_wm_base(), surface));

    ASSERT_FALSE(xdg_toplevel_);
    xdg_toplevel_.reset(xdg_surface_get_toplevel(xdg_surface_.get()));

    ASSERT_FALSE(aura_toplevel_);
    aura_toplevel_.reset(zaura_shell_get_aura_toplevel_for_xdg_toplevel(
        client->aura_shell(), xdg_toplevel_.get()));
  }

  std::unique_ptr<test::TestBuffer> parent_buffer;
  std::unique_ptr<test::TestBuffer> child_buffer;

  std::unique_ptr<wl_surface> child2_wl_surface;
  std::unique_ptr<wl_subsurface> child2_wl_subsurface;

 private:
  std::unique_ptr<xdg_surface> xdg_surface_;
  std::unique_ptr<xdg_toplevel> xdg_toplevel_;
  std::unique_ptr<zaura_toplevel> aura_toplevel_;
};

TEST_F(SurfaceAugmenterTest, AugmentedSubSurfacesAreNotAttachedToLayerTree) {
  //----------------------------------------------------------------
  //  Create a surface (top level).
  //----------------------------------------------------------------
  test::ResourceKey parent_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    ASSERT_TRUE(client->InitShmBufferFactory(256 * 256 * 4));

    auto data = std::make_unique<ShellClientData>();

    data->parent_buffer =
        client->shm_buffer_factory()->CreateBuffer(0, 256, 256);
    data->parent_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));

    data->CreateXdgToplevel(client, data->parent_wl_surface.get());
    wl_surface_attach(data->parent_wl_surface.get(),
                      data->parent_buffer->resource(), 0, 0);
    wl_surface_commit(data->parent_wl_surface.get());

    parent_surface_key =
        test::client_util::GetResourceKey(data->parent_wl_surface.get());
    client->set_data(std::move(data));
  });
  Surface* parent_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), parent_surface_key);
  auto* parent_shell_surface = GetShellSurfaceBaseForWindow(
      parent_surface->window()->GetToplevelWindow());
  ASSERT_TRUE(parent_surface);
  ASSERT_TRUE(parent_shell_surface);
  ASSERT_TRUE(parent_shell_surface->GetWidget()->IsVisible());
  EXPECT_EQ(gfx::SizeF(256, 256), parent_surface->content_size());

  //----------------------------------------------------------------
  //  Create another surface (subsurface of the toplevel one).
  //----------------------------------------------------------------
  test::ResourceKey child_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();

    data->child_buffer =
        client->shm_buffer_factory()->CreateBuffer(0, 256, 256);
    data->child_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));

    child_surface_key =
        test::client_util::GetResourceKey(data->child_wl_surface.get());
  });
  Surface* child_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), child_surface_key);
  ASSERT_TRUE(child_surface);

  // Make it a subsurface of the first one.
  test::ResourceKey child_subsurface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();

    data->child_wl_subsurface.reset(wl_subcompositor_get_subsurface(
        client->subcompositor(), data->child_wl_surface.get(),
        data->parent_wl_surface.get()));

    child_subsurface_key =
        test::client_util::GetResourceKey(data->child_wl_subsurface.get());
  });
  SubSurface* child_subsurface =
      test::server_util::GetUserDataForResource<SubSurface>(
          server_.get(), child_subsurface_key);
  ASSERT_TRUE(child_subsurface);

  //----------------------------------------------------------------
  //  Commit the surface hierarchy.
  //----------------------------------------------------------------
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();
    wl_surface_commit(data->parent_wl_surface.get());
  });

  // Check that the surfaces's Layers are attached.
  EXPECT_TRUE(
      parent_surface->window()->layer()->cc_layer_for_testing()->IsAttached());
  EXPECT_TRUE(
      child_surface->window()->layer()->cc_layer_for_testing()->IsAttached());

  //----------------------------------------------------------------
  //  Create yet another surface. Make it augmented.
  //----------------------------------------------------------------
  test::ResourceKey child2_surface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();
    data->child2_wl_surface.reset(
        wl_compositor_create_surface(client->compositor()));
    data->augmented_surface = surface_augmenter_get_augmented_surface(
        client->surface_augmenter(), data->child2_wl_surface.get());
    child2_surface_key =
        test::client_util::GetResourceKey(data->child2_wl_surface.get());
  });
  Surface* child2_surface = test::server_util::GetUserDataForResource<Surface>(
      server_.get(), child2_surface_key);
  ASSERT_TRUE(child2_surface);

  // Make it a subsurface of the first one.
  test::ResourceKey child2_subsurface_key;
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();

    data->child2_wl_subsurface.reset(wl_subcompositor_get_subsurface(
        client->subcompositor(), data->child2_wl_surface.get(),
        data->parent_wl_surface.get()));

    child2_subsurface_key =
        test::client_util::GetResourceKey(data->child2_wl_subsurface.get());
  });
  SubSurface* child2_subsurface =
      test::server_util::GetUserDataForResource<SubSurface>(
          server_.get(), child2_subsurface_key);
  ASSERT_TRUE(child2_subsurface);

  //----------------------------------------------------------------
  //  Commit the surface hierarchy.
  //----------------------------------------------------------------
  PostToClientAndWait([&](test::TestClient* client) {
    auto* data = client->GetDataAs<ShellClientData>();
    wl_surface_commit(data->parent_wl_surface.get());
  });

  // Check that the last child's Layer is not attached.
  EXPECT_FALSE(
      child2_surface->window()->layer()->cc_layer_for_testing()->IsAttached());
}

}  // namespace
}  // namespace exo::wayland