1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_OBSERVER_H_
6 #define CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_OBSERVER_H_
7 
8 #include <memory>
9 #include <vector>
10 
11 #include "base/optional.h"
12 #include "chrome/browser/ui/tabs/tab_change_type.h"
13 #include "components/tab_groups/tab_group_id.h"
14 #include "ui/base/models/list_selection_model.h"
15 
16 class TabStripModel;
17 
18 namespace content {
19 class WebContents;
20 }
21 
22 ////////////////////////////////////////////////////////////////////////////////
23 //
24 // TabStripModelChange / TabStripSelectionChange
25 //
26 // The following class and structures are used to inform TabStripModelObservers
27 // of changes to:
28 // 1) selection model
29 // 2) activated tab
30 // 3) inserted/removed/moved tabs.
31 // These changes must be bundled together because (1) and (2) consist of indices
32 // into a list of tabs [determined by (3)]. All three must be kept synchronized.
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 class TabStripModelChange {
36  public:
37   enum Type { kSelectionOnly, kInserted, kRemoved, kMoved, kReplaced };
38 
39   // Base class for all changes.
40   // TODO(dfried): would love to change this whole thing into a std::variant,
41   // but C++17 features are not yet approved for use in chromium.
42   struct Delta {
43     virtual ~Delta() = default;
44   };
45 
46   struct ContentsWithIndex {
47     content::WebContents* contents;
48     int index;
49   };
50 
51   // WebContents were inserted. This implicitly changes the existing selection
52   // model by calling IncrementFrom(index) on each index in |contents[i].index|.
53   struct Insert : public Delta {
54     Insert();
55     ~Insert() override;
56     Insert(Insert&& other);
57     Insert& operator=(Insert&& other);
58 
59     // Contains the web contents that were inserted, along with their indexes at
60     // the time of insertion. For example, if we inserted elements:
61     //
62     // Before insertion:
63     // A B C D
64     // 0 1 2 3
65     //
66     // After insertion:
67     // A X Y B C Z D
68     // 0 1 2 3 4 5 6
69     //
70     // If the tabs were inserted in the order X, Y, Z, |contents| would contain:
71     // { X, 1 }, { Y, 2 }, { Z, 5 }
72     //
73     // But if the contents were inserted in the order Z, Y, X, |contents| would
74     // contain:
75     // { Z, 3 }, { Y, 1 }, { X, 1 }
76     //
77     // Therefore all observers which store indices of web contents should update
78     // them in the order the web contents appear in |contents|. Observers should
79     // not do index-based queries based on their own internally-stored indices
80     // until after processing all of |contents|.
81     std::vector<ContentsWithIndex> contents;
82   };
83 
84   // WebContents were removed at |indices_before_removal|. This implicitly
85   // changes the existing selection model by calling DecrementFrom(index).
86   struct Remove : public Delta {
87     Remove();
88     ~Remove() override;
89     Remove(Remove&& other);
90     Remove& operator=(Remove&& other);
91 
92     // Contains the list of web contents removed, along with their indexes at
93     // the time of removal. For example, if we removed elements:
94     //
95     // Before removal:
96     // A B C D E F G
97     // 0 1 2 3 4 5 6
98     //
99     // After removal:
100     // A D E G
101     // 0 1 2 3
102     //
103     // If the tabs were removed in the order B, C, F, |contents| would contain:
104     // { B, 1 }, { C, 1 }, { F, 3 }
105     //
106     // But if the tabs were removed in the order F, C, B, then |contents| would
107     // contain:
108     // { F, 5 }, { C, 2 }, { B, 1 }
109     //
110     // Therefore all observers which store indices of web contents should update
111     // them in the order the web contents appear in |contents|. Observers should
112     // not do index-based queries based on their own internally-stored indices
113     // until after processing all of |contents|.
114     std::vector<ContentsWithIndex> contents;
115 
116     // The specified WebContents are being closed (and eventually destroyed).
117     // |tab_strip_model| is the TabStripModel that contained the tab.
118     bool will_be_deleted;
119   };
120 
121   // A WebContents was moved from |from_index| to |to_index|. This implicitly
122   // changes the existing selection model by calling
123   // Move(from_index, to_index, 1).
124   struct Move : public Delta {
125     content::WebContents* contents;
126     int from_index;
127     int to_index;
128   };
129 
130   // The WebContents was replaced at the specified index. This is invoked when
131   // prerendering swaps in a prerendered WebContents.
132   struct Replace : public Delta {
133     content::WebContents* old_contents;
134     content::WebContents* new_contents;
135     int index;
136   };
137 
138   TabStripModelChange();
139   explicit TabStripModelChange(Insert delta);
140   explicit TabStripModelChange(Remove delta);
141   explicit TabStripModelChange(Replace delta);
142   explicit TabStripModelChange(Move delta);
143   TabStripModelChange(const TabStripModelChange&) = delete;
144   TabStripModelChange& operator=(const TabStripModelChange&) = delete;
145   ~TabStripModelChange();
146 
type()147   Type type() const { return type_; }
148   const Insert* GetInsert() const;
149   const Remove* GetRemove() const;
150   const Move* GetMove() const;
151   const Replace* GetReplace() const;
152 
153  private:
154   TabStripModelChange(Type type, std::unique_ptr<Delta> delta);
155 
156   const Type type_ = kSelectionOnly;
157   std::unique_ptr<Delta> delta_;
158 };
159 
160 // Struct to carry changes on selection/activation.
161 struct TabStripSelectionChange {
162   TabStripSelectionChange();
163   TabStripSelectionChange(const TabStripSelectionChange& other);
164   ~TabStripSelectionChange();
165 
166   TabStripSelectionChange& operator=(const TabStripSelectionChange& other);
167 
168   // Fill TabStripSelectionChange with given |contents| and |selection_model|.
169   // note that |new_contents| and |new_model| will be filled too so that
170   // selection_changed() and active_tab_changed() won't return true.
171   TabStripSelectionChange(content::WebContents* contents,
172                           const ui::ListSelectionModel& model);
173 
active_tab_changedTabStripSelectionChange174   bool active_tab_changed() const { return old_contents != new_contents; }
175 
176   // TODO(sangwoo.ko) Do we need something to indicate that the change
177   // was made implicitly?
selection_changedTabStripSelectionChange178   bool selection_changed() const {
179     return selected_tabs_were_removed || old_model != new_model;
180   }
181 
182   content::WebContents* old_contents = nullptr;
183   content::WebContents* new_contents = nullptr;
184 
185   ui::ListSelectionModel old_model;
186   ui::ListSelectionModel new_model;
187 
188   bool selected_tabs_were_removed = false;
189 
190   int reason = 0;
191 };
192 
193 // Struct to carry changes to tab groups. The tab group model is independent of
194 // the tab strip model, so these changes are not bundled with
195 // TabStripModelChanges or TabStripSelectionChanges.
196 struct TabGroupChange {
197   // A group is created when the first tab is added to it and closed when the
198   // last tab is removed from it. Whenever the set of tabs in the group changes,
199   // a kContentsChange event is fired. Whenever the group's visual data changes,
200   // such as its title or color, a kVisualsChange event is fired. Whenever the
201   // group is moved by interacting with its header, a kMoved event is fired.
202   enum Type {
203     kCreated,
204     kEditorOpened,
205     kContentsChanged,
206     kVisualsChanged,
207     kMoved,
208     kClosed
209   };
210 
211   TabGroupChange(tab_groups::TabGroupId group, Type type);
212   ~TabGroupChange();
213 
214   tab_groups::TabGroupId group;
215   Type type;
216 };
217 
218 ////////////////////////////////////////////////////////////////////////////////
219 //
220 // TabStripModelObserver
221 //
222 //  Objects implement this interface when they wish to be notified of changes
223 //  to the TabStripModel.
224 //
225 //  Two major implementers are the TabStrip, which uses notifications sent
226 //  via this interface to update the presentation of the strip, and the Browser
227 //  object, which updates bookkeeping and shows/hides individual WebContentses.
228 //
229 //  Register your TabStripModelObserver with the TabStripModel using its
230 //  Add/RemoveObserver methods.
231 //
232 ////////////////////////////////////////////////////////////////////////////////
233 class TabStripModelObserver {
234  public:
235   enum ChangeReason {
236     // Used to indicate that none of the reasons below are responsible for the
237     // active tab change.
238     CHANGE_REASON_NONE = 0,
239     // The active tab changed because the tab's web contents was replaced.
240     CHANGE_REASON_REPLACED = 1 << 0,
241     // The active tab changed due to a user input event.
242     CHANGE_REASON_USER_GESTURE = 1 << 1,
243   };
244 
245   enum CloseAllStoppedReason {
246     // Used to indicate that CloseAllTab event is canceled.
247     kCloseAllCanceled = 0,
248     // Used to indicate that CloseAllTab event complete successfully.
249     kCloseAllCompleted = 1,
250   };
251 
252   TabStripModelObserver(const TabStripModelObserver&) = delete;
253   TabStripModelObserver& operator=(const TabStripModelObserver&) = delete;
254 
255   // |change| is a series of changes in tabstrip model. |change| consists
256   // of changes with same type and those changes may have caused selection or
257   // activation changes. |selection| is determined by comparing the state of
258   // TabStripModel before the |change| and after the |change| are applied.
259   // When only selection/activation was changed without any change about
260   // WebContents, |change| can be empty.
261   virtual void OnTabStripModelChanged(TabStripModel* tab_strip_model,
262                                       const TabStripModelChange& change,
263                                       const TabStripSelectionChange& selection);
264 
265   // |change| is a change in the Tab Group model or metadata. These
266   // changes may cause repainting of some Tab Group UI. They are
267   // independent of the tabstrip model and do not affect any tab state.
268   virtual void OnTabGroupChanged(const TabGroupChange& change);
269 
270   // The specified WebContents at |index| changed in some way. |contents|
271   // may be an entirely different object and the old value is no longer
272   // available by the time this message is delivered.
273   //
274   // See tab_change_type.h for a description of |change_type|.
275   virtual void TabChangedAt(content::WebContents* contents,
276                             int index,
277                             TabChangeType change_type);
278 
279   // Invoked when the pinned state of a tab changes.
280   virtual void TabPinnedStateChanged(TabStripModel* tab_strip_model,
281                                      content::WebContents* contents,
282                                      int index);
283 
284   // Invoked when the blocked state of a tab changes.
285   // NOTE: This is invoked when a tab becomes blocked/unblocked by a tab modal
286   // window.
287   virtual void TabBlockedStateChanged(content::WebContents* contents,
288                                       int index);
289 
290   // Called when the tab at |index| is added to the group with id |group|.
291   virtual void TabGroupedStateChanged(
292       base::Optional<tab_groups::TabGroupId> group,
293       content::WebContents* contents,
294       int index);
295 
296   // The TabStripModel now no longer has any tabs. The implementer may
297   // use this as a trigger to try and close the window containing the
298   // TabStripModel, for example...
299   virtual void TabStripEmpty();
300 
301   // Sent any time an attempt is made to close all the tabs. This is not
302   // necessarily the result of CloseAllTabs(). For example, if the user closes
303   // the last tab WillCloseAllTabs() is sent. If the close does not succeed
304   // during the current event (say unload handlers block it) then
305   // CloseAllTabsStopped() is sent with reason 'CANCELED'. On the other hand if
306   // the close does finish then CloseAllTabsStopped() is sent with reason
307   // 'COMPLETED'. Also note that if the last tab is detached
308   // (DetachWebContentsAt()) then this is not sent.
309   virtual void WillCloseAllTabs(TabStripModel* tab_strip_model);
310   virtual void CloseAllTabsStopped(TabStripModel* tab_strip_model,
311                                    CloseAllStoppedReason reason);
312 
313   // The specified tab at |index| requires the display of a UI indication to the
314   // user that it needs their attention. The UI indication is set iff
315   // |attention| is true.
316   virtual void SetTabNeedsAttentionAt(int index, bool attention);
317 
318   // Called when an observed TabStripModel is beginning destruction.
319   virtual void OnTabStripModelDestroyed(TabStripModel* tab_strip_model);
320 
321   static void StopObservingAll(TabStripModelObserver* observer);
322   static bool IsObservingAny(TabStripModelObserver* observer);
323   static int CountObservedModels(TabStripModelObserver* observer);
324 
325   // A passkey for TabStripModel to access some methods on this class - see
326   // </docs/patterns/passkey.md>.
327   class ModelPasskey {
328    private:
329     friend class TabStripModel;
330     ModelPasskey() = default;
331     ~ModelPasskey() = default;
332   };
333 
334   // These methods are used by TabStripModel to notify this class of lifecycle
335   // events on the TabStripModelObserver or the TabStripModel itself. The first
336   // two are used to allow TabStripModelObserver to track which models it is
337   // observing. The third is used to allow TabStripModelObserver to clean up
338   // when an observed TabStripModel is destroyed, and to send the
339   // OnTabStripModelDestroyed notification above.
340   void StartedObserving(ModelPasskey, TabStripModel* model);
341   void StoppedObserving(ModelPasskey, TabStripModel* model);
342   void ModelDestroyed(ModelPasskey, TabStripModel* model);
343 
344  protected:
345   TabStripModelObserver();
346   virtual ~TabStripModelObserver();
347 
348  private:
349   std::set<TabStripModel*> observed_models_;
350 };
351 
352 #endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_OBSERVER_H_
353