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.

#ifndef UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_
#define UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_

#include <map>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/scoped_multi_source_observation.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/views/view_utils.h"
#include "ui/views/views_export.h"

namespace views {

class View;
class Widget;

// Wraps a View in an ui::TrackedElement.
class VIEWS_EXPORT TrackedElementViews : public ui::TrackedElement {
 public:
  TrackedElementViews(View* view,
                      ui::ElementIdentifier identifier,
                      ui::ElementContext context);
  ~TrackedElementViews() override;

  View* view() { return view_; }
  const View* view() const { return view_; }

  // TrackedElement:
  gfx::Rect GetScreenBounds() const override;
  gfx::NativeView GetNativeView() const override;
  std::string ToString() const override;

  DECLARE_FRAMEWORK_SPECIFIC_METADATA()

 private:
  const raw_ptr<View> view_;
};

// Manages TrackedElements associated with View objects.
class VIEWS_EXPORT ElementTrackerViews {
 public:
  using ViewList = std::vector<View*>;

  // Returns the context to use for `primary_widget`, which will be a logical
  // top-level widget, or null to use the default computation. Used e.g. to
  // ensure that all system widgets on ChromeOS share a context.
  using ContextOverrideCallback =
      base::RepeatingCallback<ui::ElementContext(Widget* primary_widget)>;

  // Changes the computation of contexts for some or all widgets. See
  // `ContextOverrideCallback` for details.
  static void SetContextOverrideCallback(ContextOverrideCallback callback);

  // Gets the global instance of the tracker for Views.
  static ElementTrackerViews* GetInstance();

  // Returns the context associated with a particular View. The context will be
  // the same across all Views associated with a root Widget (such as an
  // application window), or as specified by the current
  // `ContextOverrideCallback` if one is set.
  static ui::ElementContext GetContextForView(View* view);

  // Returns the context associated with a particular Widget. The context will
  // be the same across all Widgets associated with a root Widget (such as an
  // application window), or as specified by the current
  // `ContextOverrideCallback` if one is set.
  static ui::ElementContext GetContextForWidget(Widget* widget);

  // ----------
  // The following methods retrieve Views directly without having to retrieve a
  // TrackedElement from ElementTracker, call, AsA<>, etc. Use if you know that
  // the element(s) in question are guaranteed to be Views.

  // Returns the corresponding TrackedElementViews for the given view, or
  // null if none exists. Note that views which are not visible or not added to
  // a Widget may not have associated elements, and that the returned object
  // may be transient.
  //
  // For the non-const version, if `assign_temporary_id` is set and `view` does
  // not have an identifier, ui::ElementTracker::kTemporaryIdentifier will be
  // assigned.
  TrackedElementViews* GetElementForView(View* view,
                                         bool assign_temporary_id = false);
  const TrackedElementViews* GetElementForView(const View* view) const;

  // Returns either the unique View matching the given `id` in the given
  // `context`, or null if there is none.
  //
  // Views which are not [yet] visible are not counted (you can use
  // `GetFirstMatchingView()` if you need to retrieve non-visible views).
  //
  // Use if you are sure there's at most one matching element in the context
  // which is visible, and (if present) is a View; will DCHECK/crash otherwise.
  View* GetUniqueView(ui::ElementIdentifier id, ui::ElementContext context);

  // Convenience method that calls GetUniqueView() and then safely converts the
  // result to `T`, which must be a View subclass with metadata. Fails if a View
  // is found but is not of the expected subtype.
  template <class T>
  T* GetUniqueViewAs(ui::ElementIdentifier id, ui::ElementContext context);

  // Returns the first View with the given `id` in the given `context`; null if
  // none is found. Ignores all other Views and any matching elements that are
  // not Views.
  //
  // Use when you just need *a* View in the given context, and don't care if
  // there's more than one.
  //
  // Unlike other methods, this is capable of retrieving views which have not
  // yet become visible (this is done for both speed and convenience). If there
  // are a mix of visible and non-visible views with `id`, you can set
  // `require_visible` to `true` to force only visible views to be returned.
  View* GetFirstMatchingView(ui::ElementIdentifier id,
                             ui::ElementContext context,
                             bool require_visible = false);

  // Convenience method that calls GetFirstMatchingView() and then safely
  // converts the result to `T`, which must be a view subclass with metadata.
  // Fails if a View is found but is not of the expected subtype.
  template <class T>
  T* GetFirstMatchingViewAs(ui::ElementIdentifier id,
                            ui::ElementContext context,
                            bool require_visible = false);

  // Returns a list of all visible Views with identifier `id` in `context`.
  // The list may be empty. Ignores any non-Views elements which might match.
  ViewList GetAllMatchingViews(ui::ElementIdentifier id,
                               ui::ElementContext context);

  // Returns a list of all visible Views with identifier `id` in any context.
  // Order is not guaranteed. Ignores any non-Views elements with the same
  // identifier.
  ViewList GetAllMatchingViewsInAnyContext(ui::ElementIdentifier id);

  // Returns a widget that matches the given context. A valid
  // TrackedElementViews must exist within the widget.
  Widget* GetWidgetForContext(ui::ElementContext context);

  // ----------
  // Notifies listeners that a specific custom event has occurred for the given
  // view. Calls GetElementForView(view, true) under the hood; returns false if
  // an element cannot be found or created for the view (e.g. in the case where
  // it is not visible or associated with a widget).
  bool NotifyCustomEvent(ui::CustomElementEventType event_type, View* view);

  // ----------
  // The following methods are used by View and derived classes to send events
  // to ElementTracker. ElementTrackerViews maintains additional observers and
  // states that make sure shown and hidden events get sent at the correct
  // times.

  // Called by View after the kUniqueElementKey property is set.
  void RegisterView(ui::ElementIdentifier element_id, View* view);

  // Called by View if the kUniqueElementKey property is changed from a non-null
  // value.
  void UnregisterView(ui::ElementIdentifier element_id, View* view);

  // Called by a View when the user activates it (clicks a button, selects a
  // menu item, etc.)
  void NotifyViewActivated(ui::ElementIdentifier element_id, View* view);

 private:
  friend class base::NoDestructor<ElementTrackerViews>;
  FRIEND_TEST_ALL_PREFIXES(ElementTrackerViewsTest, CleansUpWidgetTrackers);
  class ElementDataViews;
  class WidgetTracker;

  ElementTrackerViews();
  ~ElementTrackerViews();

  // Returns the current `ContextOverrideCallback`, which may be null.
  static ContextOverrideCallback& GetContextOverrideCallback();

  // We do not get notified at the View level if a view's widget has not yet
  // been shown. We need this notification to know when the view is actually
  // visible to the user. So if a view is added to the tracker or is added to
  // a widget, and its widget is not visible, we watch it until it is (or it is
  // destroyed).
  void MaybeTrackWidget(Widget* widget);

  // Keep track of widgets for which we've received an
  // OnWidgetVisibilityChanged(true) event for but which are still reporting
  // IsVisible() = false. This happens because visibility of native window in
  // Aura is not exactly synced with our event reporting.
  bool IsWidgetVisible(const Widget* widget) const;

  std::map<ui::ElementIdentifier, ElementDataViews> element_data_;
  std::map<const Widget*, WidgetTracker> widget_trackers_;
};

// Template implementations.

template <class T>
T* ElementTrackerViews::GetUniqueViewAs(ui::ElementIdentifier id,
                                        ui::ElementContext context) {
  views::View* const view = GetUniqueView(id, context);
  if (!view) {
    return nullptr;
  }
  T* const result = views::AsViewClass<T>(view);
  DCHECK(result);
  return result;
}

template <class T>
T* ElementTrackerViews::GetFirstMatchingViewAs(ui::ElementIdentifier id,
                                               ui::ElementContext context,
                                               bool require_visible) {
  views::View* const view = GetFirstMatchingView(id, context, require_visible);
  if (!view) {
    return nullptr;
  }
  T* const result = views::AsViewClass<T>(view);
  DCHECK(result);
  return result;
}

}  // namespace views

#endif  // UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_