910e62b5创建于 1月15日历史提交
// Copyright 2021 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/base/interaction/element_tracker.h"

#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_test_util.h"
#include "ui/base/interaction/expect_call_in_scope.h"

namespace ui {

namespace {

// Use DECLARE/DEFINE_ELEMENT instead of DEFINE_LOCAL_ELEMENT so that this
// ElementIdentifier's name is predictable.
DECLARE_ELEMENT_IDENTIFIER_VALUE(kElementIdentifier1);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kElementIdentifier1);
const char* const kElementIdentifier1Name = "kElementIdentifier1";
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kElementIdentifier2);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType1);
constexpr ElementContext kElementContext1 =
    ElementContext::CreateFakeContextForTesting(1);
constexpr ElementContext kElementContext2 =
    ElementContext::CreateFakeContextForTesting(2);

struct EventTestStruct {
  DECLARE_CLASS_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType2);
};
DEFINE_CLASS_CUSTOM_ELEMENT_EVENT_TYPE(EventTestStruct, kCustomEventType2);

}  // namespace

TEST(TrackedElementTest, IsATest) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext1);

  EXPECT_TRUE(e1->IsA<test::TestElement>());
  EXPECT_FALSE(e1->IsA<test::TestElementOtherFramework>());
  EXPECT_FALSE(e2->IsA<test::TestElement>());
  EXPECT_TRUE(e2->IsA<test::TestElementOtherFramework>());
}

TEST(TrackedElementTest, AsATest) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext2);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext2);

  EXPECT_EQ(e1.get(), e1->AsA<test::TestElement>());
  EXPECT_EQ(nullptr, e1->AsA<test::TestElementOtherFramework>());
  EXPECT_EQ(nullptr, e2->AsA<test::TestElement>());
  EXPECT_EQ(e2.get(), e2->AsA<test::TestElementOtherFramework>());
}

TEST(ElementTrackerTest, GetUniqueElement) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier2, kElementContext1));
  e1->Show();
  EXPECT_EQ(e1.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
                          kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier2, kElementContext1));
  e2->Show();
  EXPECT_EQ(e1.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
                          kElementIdentifier1, kElementContext1));
  EXPECT_EQ(e2.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
                          kElementIdentifier2, kElementContext1));
  e1->Hide();
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier1, kElementContext1));
  EXPECT_EQ(e2.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
                          kElementIdentifier2, kElementContext1));
  e2->Hide();
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
                         kElementIdentifier2, kElementContext1));
}

TEST(ElementTrackerTest, GetFirstMatchingElement) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier2, kElementContext1));
  e1->Show();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier2, kElementContext1));
  e2->Show();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  EXPECT_EQ(e2.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier2, kElementContext1));
  e1->Hide();
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  EXPECT_EQ(e2.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier2, kElementContext1));
  e2->Hide();
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier2, kElementContext1));
}

TEST(ElementTrackerTest, GetFirstMatchingElementWithMultipleElements) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext1);
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e1->Show();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e2->Show();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e1->Hide();
  EXPECT_EQ(e2.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e1->Show();
  EXPECT_EQ(e2.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e2->Hide();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
  e1->Hide();
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
                kElementIdentifier1, kElementContext1));
}

TEST(ElementTrackerTest, GetAllMatchingElements) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext1);
  ElementTracker::ElementList expected;
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e1->Show();
  expected = ElementTracker::ElementList{e1.get()};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e2->Show();
  expected = ElementTracker::ElementList{e1.get(), e2.get()};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e1->Hide();
  expected = ElementTracker::ElementList{e2.get()};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e1->Show();
  expected = ElementTracker::ElementList{e2.get(), e1.get()};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e2->Hide();
  expected = ElementTracker::ElementList{e1.get()};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
  e1->Hide();
  expected = ElementTracker::ElementList{};
  EXPECT_EQ(expected,
            ElementTracker::GetElementTracker()->GetAllMatchingElements(
                kElementIdentifier1, kElementContext1));
}

TEST(ElementTrackerTest, GetAllMatchingElementsInAnyContext) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext1);

  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier1),
      testing::IsEmpty());

  e1->Show();
  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier1),
      testing::UnorderedElementsAre(e1.get()));

  e2->Show();
  e3->Show();
  e4->Show();
  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier1),
      testing::UnorderedElementsAre(e1.get(), e2.get(), e3.get()));

  e1->Hide();
  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier1),
      testing::UnorderedElementsAre(e2.get(), e3.get()));

  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier2),
      testing::UnorderedElementsAre(e4.get()));

  e4->Hide();
  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllMatchingElementsInAnyContext(
          kElementIdentifier2),
      testing::IsEmpty());
}

TEST(ElementTrackerTest, GetElementInAnyContext) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext1);

  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetElementInAnyContext(
                kElementIdentifier1));

  e1->Show();
  EXPECT_EQ(e1.get(),
            ElementTracker::GetElementTracker()->GetElementInAnyContext(
                kElementIdentifier1));

  e2->Show();
  e3->Show();
  e4->Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetElementInAnyContext(
                  kElementIdentifier1),
              testing::AnyOf(testing::Eq(e1.get()), testing::Eq(e2.get()),
                             testing::Eq(e3.get())));

  e1->Hide();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetElementInAnyContext(
                  kElementIdentifier1),
              testing::AnyOf(testing::Eq(e2.get()), testing::Eq(e3.get())));

  EXPECT_EQ(e4.get(),
            ElementTracker::GetElementTracker()->GetElementInAnyContext(
                kElementIdentifier2));

  e4->Hide();
  EXPECT_EQ(nullptr,
            ElementTracker::GetElementTracker()->GetElementInAnyContext(
                kElementIdentifier2));
}

TEST(ElementTrackerTest, IsElementVisible) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e1->Show();
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e2->Show();
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e3->Show();
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e2->Hide();
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e1->Hide();
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
  e3->Hide();
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier2, kElementContext1));
  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
      kElementIdentifier1, kElementContext2));
}

TEST(ElementTrackerTest, AddElementShownCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementShownCallback(
          kElementIdentifier1, kElementContext1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Show());
  e2->Show();
  e3->Show();
  e1->Activate();
  e1->Hide();
  EXPECT_CALL_IN_SCOPE(callback, Run(e4.get()), e4->Show());
}

TEST(ElementTrackerTest, AddElementShownInAnyContextCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementShownInAnyContextCallback(
          kElementIdentifier1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Show());
  e2->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e3.get()), e3->Show());
  e1->Activate();
  e1->Hide();
  EXPECT_CALL_IN_SCOPE(callback, Run(e4.get()), e4->Show());
}

TEST(ElementTrackerTest, AddElementActivatedCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementActivatedCallback(
          kElementIdentifier1, kElementContext1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  e1->Show();
  e2->Show();
  e3->Show();
  e4->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Activate());
  EXPECT_CALL_IN_SCOPE(callback, Run(e4.get()), e4->Activate());
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Activate());
  e2->Activate();
  e3->Activate();
}

TEST(ElementTrackerTest, AddElementActivatedInAnyContextCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription = ElementTracker::GetElementTracker()
                          ->AddElementActivatedInAnyContextCallback(
                              kElementIdentifier1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  e1->Show();
  e2->Show();
  e3->Show();

  // Two of these elements have the specified identifier, but in different
  // contexts, while the other element does not share the identifier and should
  // not trigger the callback.
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Activate());
  e2->Activate();
  EXPECT_CALL_IN_SCOPE(callback, Run(e3.get()), e3->Activate());
}

TEST(ElementTrackerTest, AddElementHiddenCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          kElementIdentifier1, kElementContext1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e4 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  e1->Show();
  e2->Show();
  e3->Show();
  e4->Show();
  e1->Activate();
  e4->Activate();
  e2->Hide();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
  e3->Hide();
  EXPECT_CALL_IN_SCOPE(callback, Run(e4.get()), e4->Hide());
  e1->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
}

TEST(ElementTrackerTest, AddElementHiddenInAnyContextCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenInAnyContextCallback(
          kElementIdentifier1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier2, kElementContext1);
  test::TestElementPtr e3 = std::make_unique<test::TestElementOtherFramework>(
      kElementIdentifier1, kElementContext2);
  e1->Show();
  e2->Show();
  e3->Show();

  // Two of these elements have the specified identifier, but in different
  // contexts, while the other element does not share the identifier and should
  // not trigger the callback.
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
  e2->Hide();
  EXPECT_CALL_IN_SCOPE(callback, Run(e3.get()), e3->Hide());
}

TEST(ElementTrackerTest, AddCustomEventCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventCallback(
          kCustomEventType1, kElementContext1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext2);
  e1->Show();
  e2->Show();
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType1));
  ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(e2.get(),
                                                            kCustomEventType1);
}

TEST(ElementTrackerTest, AddCustomEventCallbackWithId) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventCallback(
          kCustomEventType1, kElementIdentifier1, kElementContext1,
          callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext1);
  e1->Show();
  e2->Show();
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType1));
  ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(e2.get(),
                                                            kCustomEventType1);
}

TEST(ElementTrackerTest, AddCustomEventInAnyContextCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventInAnyContextCallback(
          kCustomEventType1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext2);
  e1->Show();
  e2->Show();

  // These elements have different identifiers and contexts, but should both
  // trigger the callback.
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType1));
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e2.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e2.get(), kCustomEventType1));
}

TEST(ElementTrackerTest, AddCustomEventInAnyContextCallbackWithId) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventInAnyContextCallback(
          kCustomEventType1, kElementIdentifier1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  test::TestElementPtr e2 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext2);
  test::TestElementPtr e3 = std::make_unique<test::TestElement>(
      kElementIdentifier2, kElementContext2);
  e1->Show();
  e2->Show();
  e3->Show();

  // Two of these elements have the specified identifier, but in different
  // contexts, while the other element does not share the identifier and should
  // not trigger the callback.
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType1));
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e2.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e2.get(), kCustomEventType1));
  ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(e3.get(),
                                                            kCustomEventType1);
}

TEST(ElementTrackerTest, AddClassCustomEventCallback) {
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventCallback(
          EventTestStruct::kCustomEventType2, kElementContext1, callback.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  e1->Show();
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), EventTestStruct::kCustomEventType2));
}

TEST(ElementTrackerTest, MultipleCustomEventCallbacks) {
  // We will test that custom events work with multiple event types, including
  // in the edge case that the event type is the same as an element identifier
  // (this should never happen, but should also never break).
  const CustomElementEventType kCustomEventType2 = kElementIdentifier1;
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback2);
  auto subscription =
      ElementTracker::GetElementTracker()->AddCustomEventCallback(
          kCustomEventType1, kElementContext1, callback.Get());
  auto subscription2 =
      ElementTracker::GetElementTracker()->AddCustomEventCallback(
          kCustomEventType2, kElementContext1, callback2.Get());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  e1->Show();
  EXPECT_CALL_IN_SCOPE(
      callback, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType1));
  EXPECT_CALL_IN_SCOPE(
      callback2, Run(e1.get()),
      ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
          e1.get(), kCustomEventType2));
}

TEST(ElementTrackerTest, CleanupAfterElementHidden) {
  // Because tests can run concurrently, we have to create our own tracker to
  // avoid other tests messing with the data here.
  ElementTracker element_tracker;
  EXPECT_TRUE(element_tracker.element_data_.empty());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  element_tracker.NotifyElementShown(e1.get());
  EXPECT_EQ(1U, element_tracker.element_data_.size());
  {
    UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
    auto subscription = element_tracker.AddElementShownCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  element_tracker.NotifyElementHidden(e1.get());
  EXPECT_TRUE(element_tracker.element_data_.empty());
}

TEST(ElementTrackerTest, CleanupAfterCallbacksRemoved) {
  // Because tests can run concurrently, we have to create our own tracker to
  // avoid other tests messing with the data here.
  ElementTracker element_tracker;
  EXPECT_TRUE(element_tracker.element_data_.empty());
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);

  // Add element shown callback. An element will be shown transiently during the
  // subscription.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto subscription = element_tracker.AddElementShownCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
    element_tracker.NotifyElementShown(e1.get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
    element_tracker.NotifyElementHidden(e1.get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());

  // Add element shown in any context callback.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto subscription = element_tracker.AddElementShownInAnyContextCallback(
        kElementIdentifier1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());

  // Add element activated callback.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto subscription = element_tracker.AddElementActivatedCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());

  // Add element hidden callback.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto subscription = element_tracker.AddElementHiddenCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());

  // Add custom event callback.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto subscription = element_tracker.AddCustomEventCallback(
        kCustomEventType1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());

  // Add and remove multiple callbacks.
  {
    base::MockCallback<ElementTracker::Callback> callback;
    EXPECT_CALL(callback, Run).Times(testing::AnyNumber());
    auto sub1 = element_tracker.AddElementShownCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    auto sub2 = element_tracker.AddElementActivatedCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    auto sub3 = element_tracker.AddElementHiddenCallback(
        kElementIdentifier1, kElementContext1, callback.Get());
    EXPECT_EQ(1U, element_tracker.element_data_.size());
  }
  EXPECT_TRUE(element_tracker.element_data_.empty());
}

// The following test specific conditions that could trigger a UAF or cause
// similar instability due to changing callback lists during callbacks. These
// tests may fail all or some builds (specifically asan/msan) if the logic is
// implemented incorrectly.

TEST(ElementTrackerTest, RemoveCallbackDuringRemove) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  ElementTracker::Subscription subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          e1->identifier(), e1->context(), callback.Get());

  ON_CALL(callback, Run).WillByDefault([&](TrackedElement*) {
    subscription = ElementTracker::Subscription();
  });

  e1->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
  e1->Show();
  e1->Hide();
}

TEST(ElementTrackerTest, RemoveAndThenAddCallbackDuringRemove) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  ElementTracker::Subscription subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          e1->identifier(), e1->context(), callback.Get());

  ON_CALL(callback, Run).WillByDefault([&](TrackedElement*) {
    subscription = ElementTracker::Subscription();
    subscription =
        ElementTracker::GetElementTracker()->AddElementHiddenCallback(
            e1->identifier(), e1->context(), callback.Get());
  });
  e1->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
  e1->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
}

TEST(ElementTrackerTest, RemoveAndThenAddDifferentCallbackDuringRemove) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  ElementTracker::Subscription subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          e1->identifier(), e1->context(), callback.Get());

  ON_CALL(callback, Run).WillByDefault([&](TrackedElement*) {
    subscription = ElementTracker::Subscription();
    subscription = ElementTracker::GetElementTracker()->AddElementShownCallback(
        e1->identifier(), e1->context(), callback.Get());
  });

  e1->Show();
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Hide());
  EXPECT_CALL_IN_SCOPE(callback, Run(e1.get()), e1->Show());
  e1->Hide();
}

TEST(ElementTrackerTest, MultipleCallbacksForSameEvent) {
  test::TestElementPtr e1 = std::make_unique<test::TestElement>(
      kElementIdentifier1, kElementContext1);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback);
  UNCALLED_MOCK_CALLBACK(ElementTracker::Callback, callback2);
  ElementTracker::Subscription subscription =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          e1->identifier(), e1->context(), callback.Get());
  ElementTracker::Subscription subscription2 =
      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
          e1->identifier(), e1->context(), callback2.Get());

  e1->Show();

  // Note: these calls are not ordered.
  EXPECT_CALL(callback, Run(e1.get())).Times(1);
  EXPECT_CALL(callback2, Run(e1.get())).Times(1);
  e1->Hide();
}

TEST(ElementTrackerTest, HideDuringShowCallback) {
  // Because we have to test for correct cleanup, we have to use an isolated
  // ElementTracker.
  ElementTracker element_tracker;
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  ElementTracker::Subscription subscription;
  auto callback = base::BindLambdaForTesting([&](TrackedElement* element) {
    subscription = ElementTracker::Subscription();
    element_tracker.NotifyElementHidden(&e1);
  });
  subscription = element_tracker.AddElementShownCallback(
      e1.identifier(), e1.context(), callback);
  element_tracker.NotifyElementShown(&e1);

  // Verify that cleanup still happens after all callbacks return.
  EXPECT_TRUE(element_tracker.element_data_.empty());
}

// Regression test for a case where an element disappears in the middle of shown
// callbacks.
TEST(ElementTrackerTest, HideDuringShowCallbackMultipleListeners) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  bool called1 = false;
  bool called2 = false;

  // The first callback should be called normally.
  ElementTracker::Subscription subscription1 =
      ui::ElementTracker::GetElementTracker()->AddElementShownCallback(
          e1.identifier(), e1.context(),
          base::BindLambdaForTesting([&](TrackedElement* element) {
            EXPECT_EQ(&e1, element);
            called1 = true;
            e1.Hide();
          }));

  // The second callback will be called because of limitations with CallbackList
  // but will receive a null argument.
  ElementTracker::Subscription subscription2 =
      ui::ElementTracker::GetElementTracker()->AddElementShownCallback(
          e1.identifier(), e1.context(),
          base::BindLambdaForTesting([&](TrackedElement* element) {
            EXPECT_EQ(nullptr, element);
            called2 = true;
          }));

  // The any context callbacks happen after context-specific callbacks, so we
  // don't bother calling them at all if the element is gone.
  ElementTracker::Subscription subscription3 =
      ui::ElementTracker::GetElementTracker()
          ->AddElementShownInAnyContextCallback(
              e1.identifier(),
              base::BindLambdaForTesting(
                  [&](TrackedElement* element) { NOTREACHED(); }));
  e1.Show();
  EXPECT_TRUE(called1);
  EXPECT_TRUE(called2);
}

TEST(ElementTrackerTest, GetAllContextsForTesting) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  test::TestElement e2(kElementIdentifier1, kElementContext2);
  test::TestElement e3(kElementIdentifier2, kElementContext2);

  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllContextsForTesting(),
              testing::IsEmpty());

  e1.Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllContextsForTesting(),
              testing::UnorderedElementsAre(kElementContext1));

  e2.Show();
  e3.Show();
  EXPECT_THAT(
      ElementTracker::GetElementTracker()->GetAllContextsForTesting(),
      testing::UnorderedElementsAre(kElementContext1, kElementContext2));

  e1.Hide();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllContextsForTesting(),
              testing::UnorderedElementsAre(kElementContext2));
}

TEST(ElementTrackerTest, GetAllElementsForTestingInAnyContext) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  test::TestElement e2(kElementIdentifier1, kElementContext2);
  test::TestElement e3(kElementIdentifier2, kElementContext2);

  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(),
              testing::IsEmpty());

  e1.Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(),
              testing::UnorderedElementsAre(&e1));

  e2.Show();
  e3.Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(),
              testing::UnorderedElementsAre(&e1, &e2, &e3));

  e1.Hide();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(),
              testing::UnorderedElementsAre(&e2, &e3));
}

TEST(ElementTrackerTest, GetAllElementsForTestingInSpecificContexts) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  test::TestElement e2(kElementIdentifier1, kElementContext2);
  test::TestElement e3(kElementIdentifier2, kElementContext2);

  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext1),
              testing::IsEmpty());
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext2),
              testing::IsEmpty());

  e1.Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext1),
              testing::UnorderedElementsAre(&e1));
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext2),
              testing::IsEmpty());

  e2.Show();
  e3.Show();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext1),
              testing::UnorderedElementsAre(&e1));
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext2),
              testing::UnorderedElementsAre(&e2, &e3));

  e1.Hide();
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext1),
              testing::IsEmpty());
  EXPECT_THAT(ElementTracker::GetElementTracker()->GetAllElementsForTesting(
                  kElementContext2),
              testing::UnorderedElementsAre(&e2, &e3));
}

TEST(SafeElementReferenceTest, ElementRemainsVisible) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  SafeElementReference ref(&e1);
  EXPECT_TRUE(ref);
  EXPECT_FALSE(!ref);
  EXPECT_EQ(&e1, ref.get());
  e1.Activate();
  EXPECT_TRUE(ref);
  EXPECT_FALSE(!ref);
  EXPECT_EQ(&e1, ref.get());
}

TEST(SafeElementReferenceTest, ElementHidden) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  SafeElementReference ref(&e1);
  EXPECT_TRUE(ref);
  EXPECT_FALSE(!ref);
  EXPECT_EQ(&e1, ref.get());
  e1.Hide();
  EXPECT_FALSE(ref);
  EXPECT_TRUE(!ref);
  EXPECT_EQ(nullptr, ref.get());
}

TEST(SafeElementReferenceTest, GetAs) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  SafeElementReference ref(&e1);
  EXPECT_EQ(&e1, ref.get_as<test::TestElement>());
  EXPECT_EQ(nullptr, ref.get_as<test::TestElementOtherFramework>());
  e1.Hide();
  EXPECT_EQ(nullptr, ref.get_as<test::TestElement>());
}

TEST(SafeElementReferenceTest, MoveConstructor) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  std::unique_ptr<SafeElementReference> ref;
  {
    SafeElementReference ref2(&e1);
    ref = std::make_unique<SafeElementReference>(std::move(ref2));
  }
  EXPECT_EQ(&e1, ref->get());
  e1.Hide();
  EXPECT_EQ(nullptr, ref->get());
}

TEST(SafeElementReferenceTest, MoveOperator) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  SafeElementReference ref;
  {
    SafeElementReference ref2(&e1);
    ref = std::move(ref2);
  }
  EXPECT_EQ(&e1, ref.get());
  e1.Hide();
  EXPECT_EQ(nullptr, ref.get());
}

TEST(SafeElementReferenceTest, CopyConstructor) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  std::unique_ptr<SafeElementReference> ref;
  SafeElementReference ref2(&e1);
  ref = std::make_unique<SafeElementReference>(ref2);
  EXPECT_EQ(&e1, ref2.get());
  EXPECT_EQ(&e1, ref->get());
  e1.Hide();
  EXPECT_EQ(nullptr, ref->get());
  EXPECT_EQ(nullptr, ref2.get());
}

TEST(SafeElementReferenceTest, CopyOperator) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  e1.Show();
  SafeElementReference ref;
  SafeElementReference ref2(&e1);
  ref = ref2;
  EXPECT_EQ(&e1, ref2.get());
  EXPECT_EQ(&e1, ref.get());
  e1.Hide();
  EXPECT_EQ(nullptr, ref.get());
  EXPECT_EQ(nullptr, ref2.get());
}

class ElementTrackerIdentifierTest : public testing::Test {
 public:
  void SetUp() override { ElementIdentifier::GetKnownIdentifiers().clear(); }
};

TEST_F(ElementTrackerIdentifierTest, ShowElementRegistersIdentifier) {
  test::TestElement e1(kElementIdentifier1, kElementContext1);
  EXPECT_FALSE(ElementIdentifier::FromName(kElementIdentifier1Name));
  e1.Show();
  EXPECT_EQ(kElementIdentifier1,
            ElementIdentifier::FromName(kElementIdentifier1Name));
  e1.Hide();
  EXPECT_EQ(kElementIdentifier1,
            ElementIdentifier::FromName(kElementIdentifier1Name));
}

TEST_F(ElementTrackerIdentifierTest, AddListenerRegistersIdentifier) {
  EXPECT_FALSE(ElementIdentifier::FromName(kElementIdentifier1Name));
  auto subscription =
      ElementTracker::GetElementTracker()->AddElementShownCallback(
          kElementIdentifier1, kElementContext1, base::DoNothing());
  EXPECT_EQ(kElementIdentifier1,
            ElementIdentifier::FromName(kElementIdentifier1Name));
  subscription = ElementTracker::Subscription();
  EXPECT_EQ(kElementIdentifier1,
            ElementIdentifier::FromName(kElementIdentifier1Name));
}

}  // namespace ui