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