1 // Copyright 2020 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 IOS_CHROME_BROWSER_TABS_TAB_HELPER_DELEGATE_INSTALLER_H_
6 #define IOS_CHROME_BROWSER_TABS_TAB_HELPER_DELEGATE_INSTALLER_H_
7 
8 #include "base/check.h"
9 #include "base/scoped_observer.h"
10 #import "ios/chrome/browser/main/browser.h"
11 #import "ios/chrome/browser/main/browser_observer.h"
12 #import "ios/chrome/browser/web_state_list/web_state_list.h"
13 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
14 
15 // TabHelperDelegateInstaller is used to install a single delegate instance as
16 // the delegate for the tab helper associated with every WebState in a Browser's
17 // WebStateList.  When a TabHelperDelegateInstaller or its associated Browser is
18 // destroyed, all tab helpers' delegates are reset to nullptr.  Useful when a
19 // Browser-scoped object acts as the delegate for every tab helper.
20 //
21 // Usage example for a BrowserUserData that owns the tab helper delegates for
22 // FooTabHelper.  Two installers are used, one for the main delegate that is set
23 // via FooTabHelper::SetDelegate(), and one for a UI delegate that is set via
24 // FooTabHelper::SetUIDelegate():
25 //
26 //   class FooBrowserAgent : public BrowserUserData<FooBrowserAgent> {
27 //    public:
28 //     ...
29 //    private:
30 //     explicit FooBrowserAgent(Browser* browser)
31 //       : delegate_installer_(&tab_helper_delegate_, browser),
32 //         ui_delegate_installer_(&tab_helper_ui_delegate_, browser) {}
33 //
34 //     FooTabHelperDelegate tab_helper_delegate_;
35 //     TabHelperDelegateInstaller<FooTabHelper,
36 //                                FooTabHelperDelegate> delegate_installer_;
37 //
38 //     FooTabHelperUIDelegate tab_helper_ui_delegate_;
39 //     TabHelperDelegateInstaller<FooTabHelper,
40 //                                FooTabHelperUIDelegate,
41 //                                &FooTabHelper::SetUIDelegate>
42 //       ui_delegate_installer_;
43 //   };
44 template <class Helper,
45           class Delegate,
46           void (Helper::*SetDelFn)(Delegate*) = &Helper::SetDelegate>
47 class TabHelperDelegateInstaller {
48  public:
TabHelperDelegateInstaller(Delegate * delegate,Browser * browser)49   TabHelperDelegateInstaller(Delegate* delegate, Browser* browser)
50       : installer_(delegate, browser->GetWebStateList()),
51         shutdown_helper_(&installer_, browser) {}
52   ~TabHelperDelegateInstaller() = default;
53 
54   TabHelperDelegateInstaller(const TabHelperDelegateInstaller&) = delete;
55   TabHelperDelegateInstaller& operator=(const TabHelperDelegateInstaller&) =
56       delete;
57 
58  private:
59   // Helper object that sets up the delegates for all Helpers in a Browser's
60   // WebStateList.
61   class Installer : public WebStateListObserver {
62    public:
Installer(Delegate * delegate,WebStateList * web_state_list)63     Installer(Delegate* delegate, WebStateList* web_state_list)
64         : delegate_(delegate), web_state_list_(web_state_list) {
65       DCHECK(delegate_);
66       DCHECK(web_state_list_);
67       scoped_observer_.Add(web_state_list_);
68       for (int i = 0; i < web_state_list_->count(); ++i) {
69         SetTabHelperDelegate(web_state_list_->GetWebStateAt(i), delegate_);
70       }
71     }
~Installer()72     ~Installer() override { Disconnect(); }
73 
74     // Uninstalls the delegate and stops observing the WebStateList.
Disconnect()75     void Disconnect() {
76       if (!web_state_list_)
77         return;
78       for (int i = 0; i < web_state_list_->count(); ++i) {
79         SetTabHelperDelegate(web_state_list_->GetWebStateAt(i), nullptr);
80       }
81       scoped_observer_.Remove(web_state_list_);
82       web_state_list_ = nullptr;
83     }
84 
85    private:
86     // WebStateListObserver:
WebStateInsertedAt(WebStateList * web_state_list,web::WebState * web_state,int index,bool activating)87     void WebStateInsertedAt(WebStateList* web_state_list,
88                             web::WebState* web_state,
89                             int index,
90                             bool activating) override {
91       SetTabHelperDelegate(web_state, delegate_);
92     }
WebStateReplacedAt(WebStateList * web_state_list,web::WebState * old_web_state,web::WebState * new_web_state,int index)93     void WebStateReplacedAt(WebStateList* web_state_list,
94                             web::WebState* old_web_state,
95                             web::WebState* new_web_state,
96                             int index) override {
97       SetTabHelperDelegate(old_web_state, nullptr);
98       SetTabHelperDelegate(new_web_state, delegate_);
99     }
WillDetachWebStateAt(WebStateList * web_state_list,web::WebState * web_state,int index)100     void WillDetachWebStateAt(WebStateList* web_state_list,
101                               web::WebState* web_state,
102                               int index) override {
103       SetTabHelperDelegate(web_state, nullptr);
104     }
105 
106     // Sets the delegate for |web_state|'s Helper to |delegate|.
SetTabHelperDelegate(web::WebState * web_state,Delegate * delegate)107     void SetTabHelperDelegate(web::WebState* web_state, Delegate* delegate) {
108       (Helper::FromWebState(web_state)->*SetDelFn)(delegate);
109     }
110 
111     // The delegate that is installed for each WebState in the WebStateList.
112     Delegate* delegate_ = nullptr;
113     // The WebStateList whose Helpers's delegates are being installed.
114     WebStateList* web_state_list_ = nullptr;
115     // Scoped observer for |web_state_list_|.
116     ScopedObserver<WebStateList, WebStateListObserver> scoped_observer_{this};
117   };
118 
119   // Helper object that sets up the delegate installer and tears it down
120   // when the Browser is destroyed.
121   class BrowserShutdownHelper : public BrowserObserver {
122    public:
BrowserShutdownHelper(Installer * installer,Browser * browser)123     BrowserShutdownHelper(Installer* installer, Browser* browser)
124         : installer_(installer) {
125       DCHECK(installer_);
126       DCHECK(browser);
127       scoped_observer_.Add(browser);
128     }
129     ~BrowserShutdownHelper() override = default;
130 
131    private:
132     // BrowserObserver:
BrowserDestroyed(Browser * browser)133     void BrowserDestroyed(Browser* browser) override {
134       installer_->Disconnect();
135       scoped_observer_.Remove(browser);
136     }
137 
138     // The installer used to set up the Delegates.
139     Installer* installer_ = nullptr;
140     // Scoped observer for the Browser.
141     ScopedObserver<Browser, BrowserObserver> scoped_observer_{this};
142   };
143 
144   // Helper object that installs delegates for all the Browser's tab helpers.
145   Installer installer_;
146   // Helper object that sets up and tears down the Installer for a Browser.
147   BrowserShutdownHelper shutdown_helper_;
148 };
149 
150 #endif  // IOS_CHROME_BROWSER_TABS_TAB_HELPER_DELEGATE_INSTALLER_H_
151