910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/aura/window_occlusion_change_builder.h"

#include <memory>

#include "base/memory/raw_ptr.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"

namespace aura {

namespace {

// A delegate that remembers the occlusion info of its window.
class OcclusionTrackWindowDelegate : public test::TestWindowDelegate {
 public:
  OcclusionTrackWindowDelegate() = default;

  OcclusionTrackWindowDelegate(const OcclusionTrackWindowDelegate&) = delete;
  OcclusionTrackWindowDelegate& operator=(const OcclusionTrackWindowDelegate&) =
      delete;

  ~OcclusionTrackWindowDelegate() override = default;

  void set_window(Window* window) { window_ = window; }

  bool occlusion_change_count() const { return occlusion_change_count_; }
  Window::OcclusionState last_occlusion_state() const {
    return last_occlusion_state_;
  }
  const SkRegion& last_occluded_region() const { return last_occluded_region_; }

 private:
  // test::TestWindowDelegate:
  void OnWindowDestroyed(Window* window) override {
    if (window == window_) {
      window_ = nullptr;
    }
    test::TestWindowDelegate::OnWindowDestroyed(window);
  }

  void OnWindowOcclusionChanged(
      Window::OcclusionState old_occlusion_state,
      Window::OcclusionState new_occlusion_state) override {
    ++occlusion_change_count_;
    last_occlusion_state_ = new_occlusion_state;
    last_occluded_region_ = window_->occluded_region_in_root();
  }

  raw_ptr<Window> window_ = nullptr;
  int occlusion_change_count_ = 0;
  Window::OcclusionState last_occlusion_state_ =
      Window::OcclusionState::UNKNOWN;
  SkRegion last_occluded_region_;
};

}  // namespace

class WindowOcclusionChangeBuilderTest : public test::AuraTestBase {
 public:
  WindowOcclusionChangeBuilderTest() = default;

  WindowOcclusionChangeBuilderTest(const WindowOcclusionChangeBuilderTest&) =
      delete;
  WindowOcclusionChangeBuilderTest& operator=(
      const WindowOcclusionChangeBuilderTest&) = delete;

  ~WindowOcclusionChangeBuilderTest() override = default;

  std::unique_ptr<Window> CreateTestWindow(
      OcclusionTrackWindowDelegate* delegate) {
    auto window = std::make_unique<Window>(delegate);
    delegate->set_window(window.get());
    window->set_owned_by_parent(false);
    window->SetType(client::WINDOW_TYPE_NORMAL);
    window->Init(ui::LAYER_TEXTURED);
    window->Show();

    root_window()->AddChild(window.get());
    return window;
  }
};

// Test that window occlusion info is updated after commit.
TEST_F(WindowOcclusionChangeBuilderTest, SingleWindow) {
  SkRegion region;
  region.setRect({1, 2, 3, 4});

  for (const auto state :
       {Window::OcclusionState::VISIBLE, Window::OcclusionState::OCCLUDED,
        Window::OcclusionState::HIDDEN}) {
    OcclusionTrackWindowDelegate delegate;
    auto window = CreateTestWindow(&delegate);

    auto builder = WindowOcclusionChangeBuilder::Create();
    builder->Add(window.get(), state, region);

    // Change should not be applied before Commit call.
    EXPECT_EQ(0, delegate.occlusion_change_count());

    // All changes are committed when builder is released.
    builder.reset();

    EXPECT_EQ(1, delegate.occlusion_change_count());
    EXPECT_EQ(state, delegate.last_occlusion_state());
    EXPECT_EQ(region, delegate.last_occluded_region());
  }
}

// Test updating multiple windows.
TEST_F(WindowOcclusionChangeBuilderTest, MultipleWindow) {
  auto builder = WindowOcclusionChangeBuilder::Create();

  OcclusionTrackWindowDelegate delegate1;
  auto window1 = CreateTestWindow(&delegate1);
  const Window::OcclusionState state1 = Window::OcclusionState::VISIBLE;
  SkRegion region1;
  region1.setRect({1, 2, 3, 4});
  builder->Add(window1.get(), state1, region1);

  OcclusionTrackWindowDelegate delegate2;
  auto window2 = CreateTestWindow(&delegate2);
  const Window::OcclusionState state2 = Window::OcclusionState::OCCLUDED;
  SkRegion region2;
  region2.setRect({5, 6, 7, 8});
  builder->Add(window2.get(), state2, region2);

  // Changes should not be applied before Commit call.
  EXPECT_EQ(0, delegate1.occlusion_change_count());
  EXPECT_EQ(0, delegate2.occlusion_change_count());

  // All changes are committed when builder is released.
  builder.reset();

  EXPECT_EQ(1, delegate1.occlusion_change_count());
  EXPECT_EQ(state1, delegate1.last_occlusion_state());
  EXPECT_EQ(region1, delegate1.last_occluded_region());

  EXPECT_EQ(1, delegate2.occlusion_change_count());
  EXPECT_EQ(state2, delegate2.last_occlusion_state());
  EXPECT_EQ(region2, delegate2.last_occluded_region());
}

// Tests that the last change wins when there are multiple changes on the same
// window.
TEST_F(WindowOcclusionChangeBuilderTest, MultipleChanges) {
  OcclusionTrackWindowDelegate delegate;
  auto window = CreateTestWindow(&delegate);

  auto builder = WindowOcclusionChangeBuilder::Create();
  builder->Add(window.get(), Window::OcclusionState::VISIBLE, SkRegion());
  builder->Add(window.get(), Window::OcclusionState::HIDDEN, SkRegion());

  SkRegion region;
  region.setRect({1, 2, 3, 4});
  builder->Add(window.get(), Window::OcclusionState::OCCLUDED, region);

  // All changes are committed when builder is released.
  builder.reset();

  EXPECT_EQ(1, delegate.occlusion_change_count());
  EXPECT_EQ(Window::OcclusionState::OCCLUDED, delegate.last_occlusion_state());
  EXPECT_EQ(region, delegate.last_occluded_region());
}

// Test that occlusion info is not updated if window is destroyed before commit.
TEST_F(WindowOcclusionChangeBuilderTest, DestroyBeforeCommit) {
  OcclusionTrackWindowDelegate delegate;
  auto window = CreateTestWindow(&delegate);

  auto builder = WindowOcclusionChangeBuilder::Create();
  builder->Add(window.get(), Window::OcclusionState::VISIBLE, SkRegion());

  // Destroy window before applying the changes.
  window.reset();

  // All changes are committed when builder is released.
  builder.reset();

  // Occlusion info is not updated.
  EXPECT_EQ(0, delegate.occlusion_change_count());
}

}  // namespace aura