910e62b5创建于 1月15日历史提交
// 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 breadcrumbs {
class ApplicationBreadcrumbsLogger;
}

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 {
 public:
  using AnyWidgetCallback = base::RepeatingCallback<void(Widget*)>;

  // Using this in production requires an AnyWidgetPasskey, which can only be
  // constructed by a list of allowed friend classes...
  explicit AnyWidgetObserver(AnyWidgetPasskey passkey);

  // ... but for tests or debugging, anyone can construct a
  // views::test::AnyWidgetTestPasskey.
  explicit AnyWidgetObserver(test::AnyWidgetTestPasskey passkey);

  ~AnyWidgetObserver() override;

  AnyWidgetObserver(const AnyWidgetObserver& other) = delete;
  AnyWidgetObserver& operator=(const AnyWidgetObserver& other) = delete;

  // This sets the callback for when the Widget has finished initialization and
  // is ready to use.  This is the first point at which the widget is useable.
  void set_initialized_callback(const AnyWidgetCallback& callback) {
    initialized_callback_ = callback;
  }

  // These set the callbacks for when the backing native widget has just been
  // asked to change visibility. Note that the native widget may or may not
  // actually be drawn on the screen when these callbacks are run, because there
  // are more layers of asynchronousness at the OS layer.
  void set_shown_callback(const AnyWidgetCallback& callback) {
    // Check out NamedWidgetShownWaiter for an alternative to this method.
    shown_callback_ = callback;
  }
  void set_hidden_callback(const AnyWidgetCallback& callback) {
    hidden_callback_ = callback;
  }

  // This sets the callback for when the Widget has decided that it is about to
  // close, but not yet begun to tear down the backing native Widget or the
  // RootView. This is the last point at which the Widget is in a consistent,
  // useable state.
  void set_closing_callback(const AnyWidgetCallback& callback) {
    closing_callback_ = callback;
  }

  // This sets the callback for when the widget becomes active.
  void set_activated_callback(const AnyWidgetCallback& callback) {
    activated_callback_ = callback;
  }

  // These two methods deliberately don't exist:
  //   void set_created_callback(...)
  //   void set_destroyed_callback(...)
  // They would be called respectively too early and too late in the Widget's
  // lifecycle for it to be usefully identified - at construction time the
  // Widget has no properties or contents yet (and therefore can't be
  // meaningfully identified as "your widget" for test purposes), and at
  // destruction time the Widget's state is already partly destroyed by the
  // closure process. Both methods are deliberately left out to avoid dangerous
  // designs based on them.

 private:
  friend class internal::AnyWidgetObserverSingleton;

  AnyWidgetObserver();

  // Called from the singleton to propagate events to each AnyWidgetObserver.
  void OnAnyWidgetInitialized(Widget* widget);
  void OnAnyWidgetShown(Widget* widget);
  void OnAnyWidgetHidden(Widget* widget);
  void OnAnyWidgetClosing(Widget* widget);
  void OnAnyWidgetActivated(Widget* widget);

  AnyWidgetCallback initialized_callback_;
  AnyWidgetCallback shown_callback_;
  AnyWidgetCallback hidden_callback_;
  AnyWidgetCallback closing_callback_;
  AnyWidgetCallback activated_callback_;
};

// 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 {
 public:
  NamedWidgetShownWaiter(AnyWidgetPasskey passkey, const std::string& name);
  NamedWidgetShownWaiter(test::AnyWidgetTestPasskey passkey,
                         const std::string& name);

  virtual ~NamedWidgetShownWaiter();

  Widget* WaitIfNeededAndGet();

 private:
  explicit NamedWidgetShownWaiter(const std::string& name);

  void OnAnyWidgetShown(Widget* widget);

  AnyWidgetObserver observer_;
  base::WeakPtr<Widget> widget_;
  base::RunLoop run_loop_;
  const std::string name_;
};

class AnyWidgetPasskey {
 private:
  AnyWidgetPasskey() = default;  // NOLINT

  // Add friend classes here that are allowed to use AnyWidgetObserver in
  // production code.
  friend class NamedWidgetShownWaiter;
  friend class breadcrumbs::ApplicationBreadcrumbsLogger;
};

namespace test {

// A passkey class to allow tests to use AnyWidgetObserver without needing a
// views owner signoff.
class AnyWidgetTestPasskey {
 public:
  AnyWidgetTestPasskey() = default;
};

}  // namespace test

}  // namespace views

#endif  // UI_VIEWS_WIDGET_ANY_WIDGET_OBSERVER_H_