| // 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/views/interaction/element_tracker_views.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/bind.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/element_tracker.h" |
| #include "ui/base/interaction/expect_call_in_scope.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/base/metadata/metadata_impl_macros.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/event.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| #include "ui/events/types/event_type.h" |
| #include "ui/views/controls/button/label_button.h" |
| #include "ui/views/controls/button/menu_button.h" |
| #include "ui/views/interaction/interaction_test_util_views.h" |
| #include "ui/views/test/views_test_base.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/view_class_properties.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace views { |
| |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementID); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementID2); |
| DECLARE_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType); |
| DEFINE_CUSTOM_ELEMENT_EVENT_TYPE(kCustomEventType); |
| |
| namespace { |
| |
| enum ElementEventType { kShown, kActivated, kHidden, kCustom }; |
| |
| View* ElementToView(ui::TrackedElement* element) { |
| auto* const view_element = element->AsA<TrackedElementViews>(); |
| return view_element ? view_element->view() : nullptr; |
| } |
| |
| // A subclass of View that has metadata. |
| class TypedView : public View { |
| METADATA_HEADER(TypedView, View) |
| }; |
| |
| BEGIN_METADATA(TypedView) |
| END_METADATA |
| |
| // Watches events on the ElementTracker and converts the resulting values back |
| // into Views from the original ui::TrackedElement objects. Monitoring |
| // callbacks in this way could be done with gmock but the boilerplate would be |
| // unfortunately complicated (for some events, the correct parameters are not |
| // known until after the call is made, since the call itself might create the |
| // element in question). So instead we use this helper class. |
| class ElementEventWatcher { |
| public: |
| // Watches the specified `event_type` on Views with identifier `id` in |
| // `context`. |
| ElementEventWatcher(ui::ElementIdentifier id, |
| ui::ElementContext context, |
| ElementEventType event_type) |
| : id_(id), event_type_(event_type) { |
| auto callback = base::BindRepeating(&ElementEventWatcher::OnEvent, |
| base::Unretained(this)); |
| ui::ElementTracker* const tracker = ui::ElementTracker::GetElementTracker(); |
| switch (event_type_) { |
| case ElementEventType::kShown: |
| subscription_ = tracker->AddElementShownCallback(id, context, callback); |
| break; |
| case ElementEventType::kActivated: |
| subscription_ = |
| tracker->AddElementActivatedCallback(id, context, callback); |
| break; |
| case ElementEventType::kHidden: |
| subscription_ = |
| tracker->AddElementHiddenCallback(id, context, callback); |
| break; |
| case ElementEventType::kCustom: |
| subscription_ = tracker->AddCustomEventCallback(id, context, callback); |
| break; |
| } |
| } |
| |
| int event_count() const { return event_count_; } |
| View* last_view() { return last_view_; } |
| |
| private: |
| void OnEvent(ui::TrackedElement* element) { |
| if (event_type_ != ElementEventType::kCustom) { |
| EXPECT_EQ(id_, element->identifier()); |
| } |
| last_view_ = ElementToView(element); |
| ++event_count_; |
| } |
| |
| const ui::ElementIdentifier id_; |
| const ElementEventType event_type_; |
| ui::ElementTracker::Subscription subscription_; |
| int event_count_ = 0; |
| raw_ptr<View, DanglingUntriaged> last_view_ = nullptr; |
| }; |
| |
| ElementTrackerViews::ViewList ElementsToViews( |
| ui::ElementTracker::ElementList elements) { |
| ElementTrackerViews::ViewList result; |
| std::ranges::transform(elements, std::back_inserter(result), |
| [](ui::TrackedElement* element) { |
| return element->AsA<TrackedElementViews>()->view(); |
| }); |
| return result; |
| } |
| |
| } // namespace |
| |
| class ElementTrackerViewsTest : public ViewsTestBase { |
| public: |
| ElementTrackerViewsTest() = default; |
| ~ElementTrackerViewsTest() override = default; |
| |
| void SetUp() override { |
| ViewsTestBase::SetUp(); |
| widget_ = CreateWidget(); |
| widget_->Show(); |
| } |
| |
| void TearDown() override { |
| widget_.reset(); |
| ViewsTestBase::TearDown(); |
| } |
| |
| ui::ElementContext context() const { |
| return ElementTrackerViews::GetContextForWidget(widget_.get()); |
| } |
| |
| std::unique_ptr<Widget> CreateWidget() { |
| auto widget = std::make_unique<Widget>(); |
| Widget::InitParams params = |
| CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET, |
| Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| params.bounds = gfx::Rect(0, 0, 650, 650); |
| widget->Init(std::move(params)); |
| return widget; |
| } |
| |
| protected: |
| std::unique_ptr<Widget> widget_; |
| }; |
| |
| TEST_F(ElementTrackerViewsTest, ViewShownByAddingToWidgetSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, |
| ViewHiddenByRemovingFromWidgetSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kHidden); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| auto* const view = widget_->SetContentsView(std::make_unique<View>()); |
| auto* const button = view->AddChildView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| button_ptr = view->RemoveChildViewT(button); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, ViewShownAfterAddingToWidgetSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetVisible(false); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| button->SetVisible(true); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, |
| ViewHiddenAfterAddingToWidgetSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kHidden); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| button->SetVisible(false); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| // This is a regression test for a crash crbug.com/446756569. |
| TEST_F(ElementTrackerViewsTest, ViewRemovedOnHide) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kHidden); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| |
| ui::ElementTracker::Subscription sub = |
| ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( |
| kTestElementID, context(), |
| base::BindLambdaForTesting( |
| [&](ui::TrackedElement* e) { widget_->Close(); })); |
| |
| button->SetVisible(false); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, SettingIDOnVisibleViewSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, ClearingIDOnVisibleViewSendsNotification) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kHidden); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| button->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, ChangingIDOnVisibleViewSendsNotification) { |
| ElementEventWatcher shown(kTestElementID, context(), |
| ElementEventType::kShown); |
| ElementEventWatcher hidden(kTestElementID, context(), |
| ElementEventType::kHidden); |
| ElementEventWatcher shown2(kTestElementID2, context(), |
| ElementEventType::kShown); |
| ElementEventWatcher hidden2(kTestElementID2, context(), |
| ElementEventType::kHidden); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(0, hidden.event_count()); |
| EXPECT_EQ(0, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID2); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| button->SetVisible(false); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(1, hidden2.event_count()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, ButtonPressedSendsActivatedSignal) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kActivated); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| |
| // Test mouse click. |
| constexpr gfx::Point kPressPoint(10, 10); |
| button->OnMousePressed( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| button->OnMouseReleased( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| |
| // Test accessible keypress. |
| views::test::InteractionTestUtilSimulatorViews::PressButton(button); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, MenuButtonPressedSendsActivatedSignal) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kActivated); |
| size_t pressed_count = 0; |
| auto button_ptr = std::make_unique<MenuButton>( |
| base::BindLambdaForTesting([&](const ui::Event&) { ++pressed_count; })); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(0, watcher.event_count()); |
| |
| // Test mouse click. |
| constexpr gfx::Point kPressPoint(10, 10); |
| button->OnMousePressed( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| EXPECT_EQ(1U, pressed_count); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| |
| // Test accessible keypress. |
| views::test::InteractionTestUtilSimulatorViews::PressButton(button); |
| EXPECT_EQ(2U, pressed_count); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, SendCustomEventWithNamedElement) { |
| ElementEventWatcher watcher(kCustomEventType, context(), |
| ElementEventType::kCustom); |
| auto* const target = widget_->SetContentsView(std::make_unique<View>()); |
| target->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kCustomEventType, |
| target); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(target, watcher.last_view()); |
| // Send an event with a different ID (which happens to be the element's ID; |
| // this shouldn't happen but we should handle it gracefully). |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kTestElementID, target); |
| EXPECT_EQ(1, watcher.event_count()); |
| // Send another event. |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kCustomEventType, |
| target); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(target, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, SendCustomEventWithUnnamedElement) { |
| ElementEventWatcher watcher(kCustomEventType, context(), |
| ElementEventType::kCustom); |
| auto* const target = widget_->SetContentsView(std::make_unique<View>()); |
| // View has no pre-set identifier, but this should still work. |
| EXPECT_EQ(0, watcher.event_count()); |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kCustomEventType, |
| target); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(target, watcher.last_view()); |
| // Send an extraneous event. |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kTestElementID, target); |
| EXPECT_EQ(1, watcher.event_count()); |
| // Send another event. |
| ElementTrackerViews::GetInstance()->NotifyCustomEvent(kCustomEventType, |
| target); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(target, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, HandlesCreateWithTheSameIDMultipleTimes) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| button_ptr = widget_->GetRootView()->RemoveChildViewT(button); |
| |
| auto button_ptr2 = std::make_unique<LabelButton>(); |
| button_ptr2->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button2 = widget_->SetContentsView(std::move(button_ptr2)); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(button2, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, HandlesReshowingTheSameView) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* const button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| |
| button->SetVisible(false); |
| EXPECT_EQ(1, watcher.event_count()); |
| |
| button->SetVisible(true); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanLookupViewByIdentifier) { |
| // Should initially be null. |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // Because the button is not attached to a widget, it will not be returned for |
| // the current context (which is associated with a widget). |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| // Adding the view to a widget will cause it to be returned in the current |
| // context. |
| auto* button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ( |
| button, |
| ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context()))); |
| |
| // Once the view is destroyed, however, the result should be null again. |
| widget_->GetRootView()->RemoveChildViewT(button); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| // Create a second view with the same ID and verify that the new pointer is |
| // returned. |
| button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ( |
| button, |
| ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context()))); |
| |
| // When the view is deleted, the result once again becomes null. |
| widget_->GetRootView()->RemoveChildViewT(button); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanLookUpElementByIdentifier) { |
| // Should initially be null. |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // Because the button is not attached to a widget, it will not be returned for |
| // the current context (which is associated with a widget). |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| // Adding the view to a widget will cause it to be returned in the current |
| // context. |
| auto* button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_EQ(button, ui::ElementTracker::GetElementTracker() |
| ->GetUniqueElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // Hiding the view will make the view not findable through the base element |
| // tracker. |
| button->SetVisible(false); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| // Showing the view will bring it back. |
| button->SetVisible(true); |
| EXPECT_EQ(button, ui::ElementTracker::GetElementTracker() |
| ->GetUniqueElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // Once the view is destroyed, however, the result should be null again. |
| widget_->GetRootView()->RemoveChildViewT(button); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| |
| // Create a second view with the same ID and verify that the new pointer is |
| // returned. |
| button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(button, ui::ElementTracker::GetElementTracker() |
| ->GetUniqueElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // When the view is deleted, the result once again becomes null. |
| widget_->GetRootView()->RemoveChildViewT(button); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanGetFirstViewByIdentifier) { |
| // Should initially be null. |
| EXPECT_EQ(nullptr, |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context())); |
| |
| // Add two buttons with the same identifier. |
| auto* contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto* button = contents->AddChildView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* button2 = contents->AddChildView(std::make_unique<LabelButton>()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // The first button should be returned. |
| EXPECT_EQ( |
| button, |
| ElementToView( |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context()))); |
| |
| // Remove the first button. The second should now be returned. |
| contents->RemoveChildViewT(button); |
| EXPECT_EQ( |
| button2, |
| ElementToView( |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context()))); |
| |
| // Remove the second button. There will be no matching views. |
| contents->RemoveChildViewT(button2); |
| EXPECT_EQ(nullptr, |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanGetFirstElementByIdentifier) { |
| // Should initially be null. |
| EXPECT_EQ(nullptr, |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context())); |
| |
| // Add two buttons with the same identifier. |
| auto* contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto* button = contents->AddChildView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* button2 = contents->AddChildView(std::make_unique<LabelButton>()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // The first button should be returned. |
| EXPECT_EQ(button, ui::ElementTracker::GetElementTracker() |
| ->GetFirstMatchingElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // Set the buttons' visibility; this should change whether the element tracker |
| // sees them. |
| button->SetVisible(false); |
| EXPECT_EQ(button2, ui::ElementTracker::GetElementTracker() |
| ->GetFirstMatchingElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| button2->SetVisible(false); |
| EXPECT_EQ(nullptr, |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context())); |
| |
| // The second button is now the first to become visible in the base tracker. |
| button2->SetVisible(true); |
| button->SetVisible(true); |
| EXPECT_EQ(button2, ui::ElementTracker::GetElementTracker() |
| ->GetFirstMatchingElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // Remove the second button. The first should now be returned. |
| contents->RemoveChildViewT(button2); |
| EXPECT_EQ(button, ui::ElementTracker::GetElementTracker() |
| ->GetFirstMatchingElement(kTestElementID, context()) |
| ->AsA<TrackedElementViews>() |
| ->view()); |
| |
| // Remove the first button. There will be no matching views. |
| contents->RemoveChildViewT(button); |
| EXPECT_EQ(nullptr, |
| ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanGetAllViewsByIdentifier) { |
| // Should initially be empty. |
| ElementTrackerViews::ViewList expected; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Add two buttons with the same identifier. |
| auto* contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto* button = contents->AddChildView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* button2 = contents->AddChildView(std::make_unique<LabelButton>()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // All buttons should be returned. |
| expected = ElementTrackerViews::ViewList{button, button2}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Remove the first button. The second should now be returned. |
| contents->RemoveChildViewT(button); |
| expected = ElementTrackerViews::ViewList{button2}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Remove the second button. There will be no matching views. |
| contents->RemoveChildViewT(button2); |
| expected.clear(); |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanGetAllElementsByIdentifier) { |
| // Should initially be empty. |
| ElementTrackerViews::ViewList expected; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Add two buttons with the same identifier. |
| auto* contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto* button = contents->AddChildView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* button2 = contents->AddChildView(std::make_unique<LabelButton>()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // Both buttons should be returned. |
| expected = ElementTrackerViews::ViewList{button, button2}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Set the buttons' visibility; this should change whether the element tracker |
| // sees them. |
| button->SetVisible(false); |
| expected = ElementTrackerViews::ViewList{button2}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| button2->SetVisible(false); |
| expected.clear(); |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // The second button is now the first to become visible in the base tracker. |
| button2->SetVisible(true); |
| button->SetVisible(true); |
| expected = ElementTrackerViews::ViewList{button2, button}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Remove the second button. Only the first should now be returned. |
| contents->RemoveChildViewT(button2); |
| expected = ElementTrackerViews::ViewList{button}; |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| |
| // Remove the first button. There will be no matching views. |
| contents->RemoveChildViewT(button); |
| expected.clear(); |
| EXPECT_EQ(expected, |
| ElementsToViews( |
| ui::ElementTracker::GetElementTracker()->GetAllMatchingElements( |
| kTestElementID, context()))); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanGetVisibilityByIdentifier) { |
| // Should initially be false. |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // Because the button is not attached to a widget, it will not be counted as |
| // visible. |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| // Adding the view to a widget will cause it to be counted as visible. |
| auto* button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| // Once the view is destroyed, however, the result should be false again. |
| widget_->GetRootView()->RemoveChildViewT(button); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| // Create a second view with the same ID but start it as not visible. |
| button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetVisible(false); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| // Now set the visibility to true. |
| button->SetVisible(true); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| // Set visibility to false again. |
| button->SetVisible(false); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, CanLookupElementByView) { |
| // Should initially be false. |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| // The button is not attached to a widget so there is no associated element |
| // object. |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetElementForView( |
| button_ptr.get())); |
| |
| // Adding the (visible) view to a widget will cause an element to be |
| // generated. |
| auto* button = widget_->SetContentsView(std::move(button_ptr)); |
| EXPECT_NE(nullptr, |
| ElementTrackerViews::GetInstance()->GetElementForView(button)); |
| |
| // Once the view is destroyed, however, the result should be false again. |
| widget_->GetRootView()->RemoveChildView(button); |
| EXPECT_EQ(nullptr, |
| ElementTrackerViews::GetInstance()->GetElementForView(button)); |
| delete button; |
| |
| // Create a second view with the same ID but start it as not visible. |
| button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetVisible(false); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(nullptr, |
| ElementTrackerViews::GetInstance()->GetElementForView(button)); |
| |
| // Now set the visibility to true. |
| button->SetVisible(true); |
| EXPECT_NE(nullptr, |
| ElementTrackerViews::GetInstance()->GetElementForView(button)); |
| |
| // Set visibility to false again. |
| button->SetVisible(false); |
| EXPECT_EQ(nullptr, |
| ElementTrackerViews::GetInstance()->GetElementForView(button)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, AssignTemporaryId) { |
| auto* button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| DCHECK(!button->GetProperty(kElementIdentifierKey)); |
| |
| TrackedElementViews* element = |
| ElementTrackerViews::GetInstance()->GetElementForView(button); |
| EXPECT_EQ(nullptr, element); |
| element = ElementTrackerViews::GetInstance()->GetElementForView(button, true); |
| EXPECT_NE(nullptr, element); |
| EXPECT_EQ(ui::ElementTracker::kTemporaryIdentifier, |
| button->GetProperty(kElementIdentifierKey)); |
| EXPECT_EQ(element, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| ui::ElementTracker::kTemporaryIdentifier, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetNativeView) { |
| // A view not in a widget has no native view. |
| auto button = std::make_unique<LabelButton>(); |
| TrackedElementViews element_no_widget(button.get(), kTestElementID, |
| ui::ElementContext()); |
| EXPECT_EQ(gfx::NativeView(), element_no_widget.GetNativeView()); |
| |
| // Once added to a widget, it should have the widget's native view. |
| auto* button_in_widget = widget_->SetContentsView(std::move(button)); |
| EXPECT_EQ(widget_->GetNativeView(), element_no_widget.GetNativeView()); |
| |
| // The element returned from the tracker should also have the correct native |
| // view. |
| button_in_widget->SetProperty(kElementIdentifierKey, kTestElementID); |
| TrackedElementViews* tracked_element = |
| ElementTrackerViews::GetInstance()->GetElementForView(button_in_widget); |
| ASSERT_NE(nullptr, tracked_element); |
| EXPECT_EQ(widget_->GetNativeView(), tracked_element->GetNativeView()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, TestElementSetAndGetNativeView) { |
| ui::test::TestElement e(kTestElementID, context()); |
| EXPECT_EQ(gfx::NativeView(), e.GetNativeView()); |
| gfx::NativeView view = widget_->GetNativeView(); |
| e.SetNativeView(view); |
| EXPECT_EQ(view, e.GetNativeView()); |
| } |
| |
| // The following tests ensure conformity with the different platforms' Views |
| // implementation to ensure that Views are reported as visible to the user at |
| // the correct times, including during Widget close/delete. |
| |
| TEST_F(ElementTrackerViewsTest, ParentNotVisibleWhenAddedToWidget) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| contents->SetVisible(false); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->SetVisible(true); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, WidgetNotVisibleWhenAddedToWidget) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| widget_->Hide(); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| widget_->Show(); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, ParentHidden) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->SetVisible(false); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, WidgetHidden) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| widget_->Hide(); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, WidgetClosed) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| widget_->Close(); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context())); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, WidgetDestroyed) { |
| View* const contents = widget_->SetContentsView(std::make_unique<View>()); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| const auto current_context = context(); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, current_context)); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, current_context)); |
| widget_.reset(); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, current_context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, WidgetShownAfterAdd) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| auto child_ptr = std::make_unique<View>(); |
| child_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context)); |
| contents->AddChildView(std::move(child_ptr)); |
| EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context)); |
| widget->Show(); |
| EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible( |
| kTestElementID, context)); |
| } |
| |
| // ------------------------------------------------------------------ |
| // Corner Cases |
| |
| // This is a gross corner case where a Widget might not report IsVisible() |
| // during show, but we're still showing views and could conceivably add another |
| // view as part of a callback. |
| TEST_F(ElementTrackerViewsTest, AddedDuringWidgetShow) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| View* const child1 = contents->AddChildView(std::make_unique<View>()); |
| View* const child2 = contents->AddChildView(std::make_unique<View>()); |
| child1->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementShownCallback( |
| kTestElementID, |
| ElementTrackerViews::GetContextForWidget(widget.get()), |
| base::BindLambdaForTesting([&](ui::TrackedElement*) { |
| child2->SetProperty(kElementIdentifierKey, kTestElementID2); |
| })); |
| |
| bool called = false; |
| |
| auto subscription2 = |
| ui::ElementTracker::GetElementTracker()->AddElementShownCallback( |
| kTestElementID2, |
| ElementTrackerViews::GetContextForWidget(widget.get()), |
| base::BindLambdaForTesting([&](ui::TrackedElement* element) { |
| EXPECT_EQ(child2, element->AsA<TrackedElementViews>()->view()); |
| called = true; |
| })); |
| |
| test::WidgetVisibleWaiter visible_waiter(widget.get()); |
| widget->Show(); |
| visible_waiter.Wait(); |
| EXPECT_TRUE(called); |
| |
| // Now verify that hiding a widget which we engaged during initial Show(), |
| // without destroying the views, causes the elements to be hidden. |
| subscription2 = |
| ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( |
| kTestElementID2, |
| ElementTrackerViews::GetContextForWidget(widget.get()), |
| base::BindLambdaForTesting([&](ui::TrackedElement* element) { |
| EXPECT_EQ(child2, element->AsA<TrackedElementViews>()->view()); |
| called = true; |
| })); |
| |
| called = false; |
| widget->Hide(); |
| EXPECT_TRUE(called); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, AddedBeforeShowDeletedDuringShow) { |
| auto widget = CreateWidget(); |
| const auto context = ElementTrackerViews::GetContextForWidget(widget.get()); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, shown); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, hidden); |
| auto shown_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementShownCallback( |
| kTestElementID, context, shown.Get()); |
| auto hidden_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( |
| kTestElementID, context, hidden.Get()); |
| auto* const contents = widget->SetContentsView(std::make_unique<View>()); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_CALL_IN_SCOPE(shown, Run, widget->Show()); |
| EXPECT_CALL_IN_SCOPE(hidden, Run, widget.reset()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, AddedAfterShowDeletedDuringShow) { |
| auto widget = CreateWidget(); |
| const auto context = ElementTrackerViews::GetContextForWidget(widget.get()); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, shown); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, hidden); |
| auto shown_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementShownCallback( |
| kTestElementID, context, shown.Get()); |
| auto hidden_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( |
| kTestElementID, context, hidden.Get()); |
| auto* const contents = widget->SetContentsView(std::make_unique<View>()); |
| EXPECT_CALL_IN_SCOPE(shown, Run, { |
| widget->Show(); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| }); |
| EXPECT_CALL_IN_SCOPE(hidden, Run, widget.reset()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, AddedDuringHideThenDeleted) { |
| auto widget = CreateWidget(); |
| const auto context = ElementTrackerViews::GetContextForWidget(widget.get()); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, shown); |
| UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, hidden); |
| auto shown_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementShownCallback( |
| kTestElementID, context, shown.Get()); |
| auto hidden_subscription = |
| ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( |
| kTestElementID, context, hidden.Get()); |
| auto contents = std::make_unique<View>(); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| test::WidgetVisibleWaiter waiter(widget.get()); |
| widget->Show(); |
| waiter.Wait(); |
| widget->Hide(); |
| widget->SetContentsView(std::move(contents)); |
| widget.reset(); |
| } |
| |
| // End Corner Cases |
| // ------------------------------------------------------------------ |
| |
| TEST_F(ElementTrackerViewsTest, CleansUpWidgetTrackers) { |
| auto widget1 = CreateWidget(); |
| View* const contents1 = widget1->SetContentsView(std::make_unique<View>()); |
| contents1->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto widget2 = CreateWidget(); |
| View* const contents2 = widget1->SetContentsView(std::make_unique<View>()); |
| contents2->SetProperty(kElementIdentifierKey, kTestElementID); |
| |
| test::WidgetVisibleWaiter waiter1(widget1.get()); |
| test::WidgetVisibleWaiter waiter2(widget2.get()); |
| widget1->Show(); |
| widget2->Show(); |
| waiter1.Wait(); |
| waiter2.Wait(); |
| |
| widget1->Hide(); |
| test::WidgetDestroyedWaiter destroyed_waiter(widget2.get()); |
| widget2->Close(); |
| destroyed_waiter.Wait(); |
| |
| EXPECT_TRUE(ElementTrackerViews::GetInstance()->widget_trackers_.empty()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetUniqueView) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetUniqueView( |
| kTestElementID, context)); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(contents, ElementTrackerViews::GetInstance()->GetUniqueView( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetUniqueView( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetUniqueViewAs) { |
| auto widget = CreateWidget(); |
| TypedView* const contents = |
| widget->SetContentsView(std::make_unique<TypedView>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| EXPECT_EQ(nullptr, |
| ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>( |
| kTestElementID, context)); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(contents, |
| ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(nullptr, |
| ElementTrackerViews::GetInstance()->GetUniqueViewAs<TypedView>( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewWithSingleView) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(contents, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewAs) { |
| auto widget = CreateWidget(); |
| TypedView* const contents = |
| widget->SetContentsView(std::make_unique<TypedView>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| EXPECT_EQ( |
| nullptr, |
| ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>( |
| kTestElementID, context)); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ( |
| contents, |
| ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ( |
| nullptr, |
| ElementTrackerViews::GetInstance()->GetFirstMatchingViewAs<TypedView>( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewWithMultipleViews) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| View* const v1 = contents->AddChildView(std::make_unique<View>()); |
| View* const v2 = contents->AddChildView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| |
| v1->SetProperty(kElementIdentifierKey, kTestElementID); |
| v2->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(v1, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| |
| v1->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(v2, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| |
| v2->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetFirstMatchingViewWithNonViewsElements) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| |
| ui::test::TestElementPtr test_element1 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| ui::test::TestElementPtr test_element2 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| |
| test_element1->Show(); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| test_element2->Show(); |
| EXPECT_EQ(contents, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(nullptr, ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllMatchingViewsWithSingleView) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| EXPECT_EQ(ElementTrackerViews::ViewList(), |
| ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| const ElementTrackerViews::ViewList expected = {contents}; |
| EXPECT_EQ(expected, ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(ElementTrackerViews::ViewList(), |
| ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllMatchingViewsWithMultipleViews) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| View* const v1 = contents->AddChildView(std::make_unique<View>()); |
| View* const v2 = contents->AddChildView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| |
| v1->SetProperty(kElementIdentifierKey, kTestElementID); |
| v2->SetProperty(kElementIdentifierKey, kTestElementID); |
| ElementTrackerViews::ViewList expected = {v1, v2}; |
| EXPECT_EQ(expected, ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| |
| v1->ClearProperty(kElementIdentifierKey); |
| expected = {v2}; |
| EXPECT_EQ(expected, ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| |
| v2->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(ElementTrackerViews::ViewList(), |
| ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllMatchingViewsWithNonViewsElements) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| |
| ui::test::TestElementPtr test_element1 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| ui::test::TestElementPtr test_element2 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| |
| test_element1->Show(); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| test_element2->Show(); |
| const ElementTrackerViews::ViewList expected = {contents}; |
| EXPECT_EQ(expected, ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_EQ(ElementTrackerViews::ViewList(), |
| ElementTrackerViews::GetInstance()->GetAllMatchingViews( |
| kTestElementID, context)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllViewsInAnyContextWithSingleView) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::IsEmpty()); |
| |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::UnorderedElementsAre(contents)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::IsEmpty()); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllViewsInAnyContextWithMultipleViews) { |
| auto widget = CreateWidget(); |
| auto widget2 = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| View* const v1 = contents->AddChildView(std::make_unique<View>()); |
| View* const v2 = contents->AddChildView(std::make_unique<View>()); |
| View* const contents2 = widget2->SetContentsView(std::make_unique<View>()); |
| View* const v3 = contents2->AddChildView(std::make_unique<View>()); |
| View* const v4 = contents2->AddChildView(std::make_unique<View>()); |
| widget->Show(); |
| widget2->Show(); |
| |
| v1->SetProperty(kElementIdentifierKey, kTestElementID); |
| v2->SetProperty(kElementIdentifierKey, kTestElementID); |
| v3->SetProperty(kElementIdentifierKey, kTestElementID); |
| v4->SetProperty(kElementIdentifierKey, kTestElementID2); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::UnorderedElementsAre(v1, v2, v3)); |
| |
| v1->ClearProperty(kElementIdentifierKey); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::UnorderedElementsAre(v2, v3)); |
| |
| v2->ClearProperty(kElementIdentifierKey); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::UnorderedElementsAre(v3)); |
| } |
| |
| TEST_F(ElementTrackerViewsTest, GetAllViewsInAnyContextWithNonViewsElements) { |
| auto widget = CreateWidget(); |
| View* const contents = widget->SetContentsView(std::make_unique<View>()); |
| widget->Show(); |
| const ui::ElementContext context = |
| ElementTrackerViews::GetContextForView(contents); |
| |
| ui::test::TestElementPtr test_element1 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| ui::test::TestElementPtr test_element2 = |
| std::make_unique<ui::test::TestElement>(kTestElementID, context); |
| |
| test_element1->Show(); |
| contents->SetProperty(kElementIdentifierKey, kTestElementID); |
| test_element2->Show(); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::UnorderedElementsAre(contents)); |
| |
| contents->ClearProperty(kElementIdentifierKey); |
| EXPECT_THAT( |
| ElementTrackerViews::GetInstance()->GetAllMatchingViewsInAnyContext( |
| kTestElementID), |
| testing::IsEmpty()); |
| } |
| |
| // Verifies that Views on different Widgets are differentiated by the system. |
| class ElementTrackerTwoWidgetTest : public ElementTrackerViewsTest { |
| public: |
| ElementTrackerTwoWidgetTest() = default; |
| ~ElementTrackerTwoWidgetTest() override = default; |
| |
| void SetUp() override { |
| ElementTrackerViewsTest::SetUp(); |
| |
| widget2_ = CreateWidget(); |
| widget2_->Show(); |
| } |
| |
| void TearDown() override { |
| widget2_.reset(); |
| // Reset the context override callback so it doesn't affect future tests. |
| ElementTrackerViews::SetContextOverrideCallback(base::NullCallback()); |
| ElementTrackerViewsTest::TearDown(); |
| } |
| |
| ui::ElementContext context2() const { |
| return ElementTrackerViews::GetContextForWidget(widget2_.get()); |
| } |
| |
| protected: |
| std::unique_ptr<Widget> widget2_; |
| }; |
| |
| TEST_F(ElementTrackerTwoWidgetTest, ViewMovedToDifferentWidgetGeneratesEvents) { |
| ElementEventWatcher shown(kTestElementID, context(), |
| ElementEventType::kShown); |
| ElementEventWatcher hidden(kTestElementID, context(), |
| ElementEventType::kHidden); |
| ElementEventWatcher shown2(kTestElementID, context2(), |
| ElementEventType::kShown); |
| ElementEventWatcher hidden2(kTestElementID, context2(), |
| ElementEventType::kHidden); |
| auto* const view = widget_->SetContentsView(std::make_unique<View>()); |
| auto* const view2 = widget2_->SetContentsView(std::make_unique<View>()); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| // Add to first widget. |
| auto* const button = view->AddChildView(std::move(button_ptr)); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(0, hidden.event_count()); |
| EXPECT_EQ(0, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| // Move to second widget. |
| view2->AddChildViewRaw(button); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| // Destroy the second widget. |
| widget2_.reset(); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(1, hidden2.event_count()); |
| } |
| |
| TEST_F(ElementTrackerTwoWidgetTest, CanLookUpViewsOnMultipleWidgets) { |
| auto* button = widget_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| auto* button2 = widget2_->SetContentsView(std::make_unique<LabelButton>()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ( |
| button, |
| ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context()))); |
| EXPECT_EQ( |
| button2, |
| ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context2()))); |
| widget_->GetRootView()->RemoveChildViewT(button); |
| widget2_->GetRootView()->RemoveChildViewT(button2); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context())); |
| EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement( |
| kTestElementID, context2())); |
| } |
| |
| TEST_F(ElementTrackerTwoWidgetTest, |
| MakingViewsVisibleSendsNotificationsToCorrectListeners) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kShown); |
| ElementEventWatcher watcher2(kTestElementID, context2(), |
| ElementEventType::kShown); |
| auto* const button = |
| widget_->SetContentsView(std::make_unique<LabelButton>()); |
| auto* const button2 = |
| widget2_->SetContentsView(std::make_unique<LabelButton>()); |
| EXPECT_EQ(0, watcher.event_count()); |
| EXPECT_EQ(0, watcher2.event_count()); |
| |
| // Each listener should be notified when the appropriate button is shown. |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| EXPECT_EQ(0, watcher2.event_count()); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| EXPECT_EQ(1, watcher2.event_count()); |
| EXPECT_EQ(button2, watcher2.last_view()); |
| |
| // Each listener should be notified when the appropriate button is shown. |
| button->SetVisible(false); |
| button->SetVisible(true); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(1, watcher2.event_count()); |
| |
| // Hide and show several times to verify events are still set. |
| button->SetVisible(false); |
| button2->SetVisible(false); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(1, watcher2.event_count()); |
| button2->SetVisible(true); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(2, watcher2.event_count()); |
| button->SetVisible(true); |
| EXPECT_EQ(3, watcher.event_count()); |
| EXPECT_EQ(2, watcher2.event_count()); |
| } |
| |
| TEST_F(ElementTrackerTwoWidgetTest, |
| ButtonPressedSendsNotificationsToCorrectListeners) { |
| ElementEventWatcher watcher(kTestElementID, context(), |
| ElementEventType::kActivated); |
| ElementEventWatcher watcher2(kTestElementID, context2(), |
| ElementEventType::kActivated); |
| auto* const button = |
| widget_->SetContentsView(std::make_unique<LabelButton>()); |
| auto* const button2 = |
| widget2_->SetContentsView(std::make_unique<LabelButton>()); |
| button->SetProperty(kElementIdentifierKey, kTestElementID); |
| button2->SetProperty(kElementIdentifierKey, kTestElementID); |
| EXPECT_EQ(0, watcher.event_count()); |
| EXPECT_EQ(0, watcher2.event_count()); |
| |
| // Test mouse click. |
| constexpr gfx::Point kPressPoint(10, 10); |
| button->OnMousePressed( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| button->OnMouseReleased( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| EXPECT_EQ(0, watcher2.event_count()); |
| |
| // Click other button. |
| button2->OnMousePressed( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| button2->OnMouseReleased( |
| ui::MouseEvent(ui::EventType::kMousePressed, kPressPoint, kPressPoint, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON)); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(button, watcher.last_view()); |
| EXPECT_EQ(1, watcher2.event_count()); |
| EXPECT_EQ(button2, watcher2.last_view()); |
| |
| // Test accessible keypress. |
| views::test::InteractionTestUtilSimulatorViews::PressButton(button2); |
| EXPECT_EQ(1, watcher.event_count()); |
| EXPECT_EQ(2, watcher2.event_count()); |
| views::test::InteractionTestUtilSimulatorViews::PressButton(button); |
| EXPECT_EQ(2, watcher.event_count()); |
| EXPECT_EQ(2, watcher2.event_count()); |
| } |
| |
| // The following are variations on the "view's context changes when it moves |
| // between widgets" test, but with an override context callback modifying |
| // what context is returned for one or both widgets. |
| |
| TEST_F(ElementTrackerTwoWidgetTest, OverrideContextCallbackCollapsesContexts) { |
| constexpr auto kContext = ui::ElementContext::CreateFakeContextForTesting(1); |
| ElementTrackerViews::SetContextOverrideCallback( |
| base::BindLambdaForTesting([kContext](Widget*) { return kContext; })); |
| ElementEventWatcher shown(kTestElementID, kContext, ElementEventType::kShown); |
| ElementEventWatcher hidden(kTestElementID, kContext, |
| ElementEventType::kHidden); |
| auto* const view = widget_->SetContentsView(std::make_unique<View>()); |
| auto* const view2 = widget2_->SetContentsView(std::make_unique<View>()); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| // Add to first widget. |
| auto* const button = view->AddChildView(std::move(button_ptr)); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(0, hidden.event_count()); |
| // Move to second widget. |
| view2->AddChildViewRaw(button); |
| EXPECT_EQ(2, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| // Destroy the second widget. |
| widget2_.reset(); |
| EXPECT_EQ(2, shown.event_count()); |
| EXPECT_EQ(2, hidden.event_count()); |
| } |
| |
| TEST_F(ElementTrackerTwoWidgetTest, |
| OverrideContextCallbackOverridesContextSelectively) { |
| constexpr auto kContext = ui::ElementContext::CreateFakeContextForTesting(1); |
| ElementTrackerViews::SetContextOverrideCallback( |
| base::BindLambdaForTesting([this, kContext](Widget* widget) { |
| return widget == widget_.get() ? kContext : ui::ElementContext(); |
| })); |
| ElementEventWatcher shown(kTestElementID, kContext, ElementEventType::kShown); |
| ElementEventWatcher hidden(kTestElementID, kContext, |
| ElementEventType::kHidden); |
| ElementEventWatcher shown2(kTestElementID, context2(), |
| ElementEventType::kShown); |
| ElementEventWatcher hidden2(kTestElementID, context2(), |
| ElementEventType::kHidden); |
| auto* const view = widget_->SetContentsView(std::make_unique<View>()); |
| auto* const view2 = widget2_->SetContentsView(std::make_unique<View>()); |
| auto button_ptr = std::make_unique<LabelButton>(); |
| button_ptr->SetProperty(kElementIdentifierKey, kTestElementID); |
| // Add to first widget. |
| auto* const button = view->AddChildView(std::move(button_ptr)); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(0, hidden.event_count()); |
| EXPECT_EQ(0, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| // Move to second widget. |
| view2->AddChildViewRaw(button); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(0, hidden2.event_count()); |
| // Destroy the second widget. |
| widget2_.reset(); |
| EXPECT_EQ(1, shown.event_count()); |
| EXPECT_EQ(1, hidden.event_count()); |
| EXPECT_EQ(1, shown2.event_count()); |
| EXPECT_EQ(1, hidden2.event_count()); |
| } |
| |
| } // namespace views |