File: picture_in_picture_occlusion_tracker.h

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (167 lines) | stat: -rw-r--r-- 6,959 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_PICTURE_IN_PICTURE_PICTURE_IN_PICTURE_OCCLUSION_TRACKER_H_
#define CHROME_BROWSER_PICTURE_IN_PICTURE_PICTURE_IN_PICTURE_OCCLUSION_TRACKER_H_

#include <vector>

#include "base/containers/flat_map.h"
#include "base/observer_list.h"
#include "base/scoped_multi_source_observation.h"
#include "base/timer/timer.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker_observer.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"

// The PictureInPictureOcclusionTracker keeps track of picture-in-picture
// widgets (both video picture-in-picture and document picture-in-picture) and
// notifies observers when their given widget has started or stopped being
// occluded by a picture-in-picture widget. This can be useful for
// security-sensitive widgets which may want to close/disable themselves when
// occluded.
//
// The simplest way to use the PictureInPictureOcclusionTracker is to use a
// ScopedPictureInPictureOcclusionObservation in a class that implements
// PictureInPictureOcclusionObserver:
//
// // Example where the widget itself observes occlusions:
// class FooWidget : public views::Widget,
//                   public PictureInPictureOcclusionObserver {
//  public:
//   FooWidget() {
//     occlusion_observation_.Observe(this);
//   }
//
//   void OnOcclusionStateChanged(bool occluded) {
//     // Only enable the Baz input field when we're not occluded.
//     SetBazEnabled(!occluded);
//   }
//
//  private:
//   ScopedPictureInPictureOcclusionObservation occlusion_observation_{this};
// };
//
// // Example where a separate class that owns the widget observes occlusions:
// class BarWidgetOwner : public PictureInPictureOcclusionObserver {
//  public:
//   ...
//
//   void OpenBarWidget() {
//     bar_widget_ = ...;
//     bar_widget_.Show();
//     occlusion_observation_.Observe(bar_widget_);
//   }
//
//   void OnOcclusionStateChanged(bool occluded) {
//     // Close the widget if it gets occluded. This will automatically stop
//     // observing it for occlusions.
//     if (occluded) {
//       CloseBarWidget();
//     }
//   }
//
//  private:
//   ScopedPictureInPictureOcclusionObservation occlusion_observation_{this};
// };
class PictureInPictureOcclusionTracker : public views::WidgetObserver {
 public:
  PictureInPictureOcclusionTracker();
  PictureInPictureOcclusionTracker(const PictureInPictureOcclusionTracker&) =
      delete;
  PictureInPictureOcclusionTracker& operator=(
      const PictureInPictureOcclusionTracker&) = delete;
  ~PictureInPictureOcclusionTracker() override;

  // Informs the PictureInPictureOcclusionTracker of a new picture-in-picture
  // widget that can potenitally occlude other widgets. The
  // PictureInPictureOcclusionTracker will automatically stop tracking it
  // when we receive an associated `OnWidgetDestroying()` call from the widget
  // itself.
  void OnPictureInPictureWidgetOpened(views::Widget* picture_in_picture_widget);

  // Start observing occlusion state changes for
  // `observer->occludable_widget()`.
  void AddObserver(PictureInPictureOcclusionTrackerObserver* observer);

  // Stop observing occlusion state changes.
  void RemoveObserver(PictureInPictureOcclusionTrackerObserver* observer);

  // views::WidgetObserver:
  void OnWidgetDestroying(views::Widget* widget) override;
  void OnWidgetVisibilityChanged(views::Widget* widget,
                                 bool visibility) override;
  void OnWidgetBoundsChanged(views::Widget* widget,
                             const gfx::Rect& new_bounds) override;

  // Allows tests to check which picture-in-picture widgets are currently being
  // observed.
  std::vector<views::Widget*> GetPictureInPictureWidgetsForTesting();

  // Allows tests to manually set an occlusion state for an observed widget.
  void SetWidgetOcclusionStateForTesting(views::Widget* observed_widget,
                                         bool occluded);

 private:
  struct ObservedWidgetData {
    // True if the widget associated with this observation is a
    // picture-in-picture widget.
    bool is_picture_in_picture_widget = false;

    // True if the widget associated with this observation is occluded by a
    // picture-in-picture widget. Only set for widgets which have an associated
    // PictureInPictureOcclusionObserver (i.e. when `number_of_direct_observers`
    // is greater than zero).
    bool occluded = false;

    // The number of PictureInPictureOcclusionObservers directly observing the
    // occlusion state of this widget.
    int number_of_direct_observers = 0;

    // The number of PictureInPictureOcclusionObservers whose observation
    // depends on this widget, which means either:
    // 1) The PictureInPictureOcclusionObserver is directly observing the
    // occlusion state of this widget, OR
    // 2) The PictureInPictureOcclusionObserver is observing the occlusion state
    // of a child of this widget.
    int number_of_dependent_observers = 0;

    // If set and true, we treat the widget as occluded. If set and false, we
    // treat the widget as unoccluded. For testing only.
    std::optional<bool> forced_occlusion_state;
  };

  // Start observing this widget, plus any of its parent widgets.
  // `directly_observe_this_widget` is true if `widget` should be observed for
  // occlusion and false if we only observe `widget` due to its child widget
  // being observed for occlusion.
  void ObserveWidgetAndParents(views::Widget* widget,
                               bool directly_observe_this_widget = true);

  // The reverse operation of `ObserveWidgetAndParents()`.
  void UnobserveWidgetAndParents(views::Widget* widget,
                                 bool directly_unobserve_this_widget = true);

  // Calculates the occlusion state for all currently observed widgets and
  // informs the observers that have changed state.
  void UpdateAllObserverStates();

  // Calculates the occlusion state for the given widget and informs the
  // observers if it has changed state. When |force_update| is true, it will
  // inform observers even if the state has not changed.
  void UpdateObserverStateForWidget(views::Widget* widget,
                                    bool force_update = false);

  base::ObserverList<PictureInPictureOcclusionTrackerObserver> observers_;

  base::ScopedMultiSourceObservation<views::Widget, views::WidgetObserver>
      widget_observations_{this};
  base::flat_map<views::Widget*, ObservedWidgetData> observed_widget_data_;

  // Used to ensure that frequent `OnWidgetBoundsChanged()` calls from dragging
  // a window don't calculate occlusion too often.
  base::OneShotTimer bounds_changed_throttle_timer_;
};

#endif  // CHROME_BROWSER_PICTURE_IN_PICTURE_PICTURE_IN_PICTURE_OCCLUSION_TRACKER_H_