// Copyright 2020 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef UI_VIEWS_WIDGET_ANY_WIDGET_OBSERVER_H_ #define UI_VIEWS_WIDGET_ANY_WIDGET_OBSERVER_H_ #include <string> #include "base/functional/callback.h" #include "base/memory/weak_ptr.h" #include "base/observer_list_types.h" #include "base/run_loop.h" #include "ui/views/views_export.h" namespace views { namespace internal { class AnyWidgetObserverSingleton; } namespace test { class AnyWidgetTestPasskey; } class AnyWidgetPasskey; class Widget; // AnyWidgetObserver is used when you want to observe widget changes but don't // have an easy way to get a reference to the Widget in question, usually // because it is created behind a layer of abstraction that hides the Widget. // // This class should only be used as a last resort - if you find yourself // reaching for it in production code, it probably means some parts of your // system aren't coupled enough or your API boundaries are hiding too much. You // will need review from an owner of this class to add new uses of it, because // it requires a Passkey to construct it - see //docs/patterns/passkey.md for // details. Uses in tests can be done freely using // views::test::AnyWidgetTestPasskey. // // This class can be used for waiting for a particular View being shown, as in: // // RunLoop run_loop; // AnyWidgetObserver observer(views::test::AnyWidgetTestPasskey{}); // Widget* widget; // observer.set_initialized_callback( // base::BindLambdaForTesting([&](Widget* w) { // if (w->GetName() == "MyWidget") { // widget = w; // run_loop.Quit(); // } // })); // ThingThatCreatesWidget(); // run_loop.Run(); // // with your widget getting the name "MyWidget" via InitParams::name. // // Note: if you're trying to wait for a named widget to be *shown*, there is an // ergonomic wrapper for that - see NamedWidgetShownWaiter below. You use it // like this: // // NamedWidgetShownWaiter waiter( // views::test::AnyWidgetTestPasskey{}, "MyWidget"); // ThingThatCreatesAndShowsWidget(); // Widget* widget = waiter.WaitIfNeededAndGet(); // // This class can also be used to make sure a named widget is _not_ shown, as // this particular example (intended for testing code) shows: // // AnyWidgetObserver observer(views::test::AnyWidgetTestPasskey{}); // observer.set_shown_callback( // base::BindLambdaForTesting([&](views::Widget* widget) { // ASSERT_FALSE(widget->GetName() == "MyWidget"); // })); // // TODO(ellyjones): Add Widget::SetDebugName and add a remark about that here. // // This allows you to create a test that is robust even if // ThingThatCreatesAndShowsWidget() later becomes asynchronous, takes a few // milliseconds, etc. class VIEWS_EXPORT AnyWidgetObserver : public base::CheckedObserver { … }; // NamedWidgetShownWaiter provides a more ergonomic way to do the most common // thing clients want to use AnyWidgetObserver for: waiting for a Widget with a // specific name to be shown. Use it like: // // NamedWidgetShownWaiter waiter(Passkey{}, "MyDialogView"); // ThingThatShowsDialog(); // Widget* dialog_widget = waiter.WaitIfNeededAndGet(); // // It is important that NamedWidgetShownWaiter be constructed before any code // that might show the named Widget, because if the Widget is shown before the // waiter is constructed, the waiter won't have observed the show and will // instead hang forever. Worse, if the widget *sometimes* shows before the // waiter is constructed and sometimes doesn't, you will be introducing flake. // THIS IS A DANGEROUS PATTERN, DON'T DO THIS: // // ThingThatShowsDialogAsynchronously(); // NamedWidgetShownWaiter waiter(...); // waiter.WaitIfNeededAndGet(); class VIEWS_EXPORT NamedWidgetShownWaiter { … }; class AnyWidgetPasskey { … }; namespace test { // A passkey class to allow tests to use AnyWidgetObserver without needing a // views owner signoff. class AnyWidgetTestPasskey { … }; } // namespace test } // namespace views #endif // UI_VIEWS_WIDGET_ANY_WIDGET_OBSERVER_H_