[iOS] Migrate ManageAccountsDelegate to C++

In a prior CL, the primary ManageAccountsDelegate implementation was
moved out of the BVC and into a browser agent. Since both the
originating code for the delegate calls, and the delegate implementation
are now C++ classes, it makes sense to update the delegate to be defined
as a C++ abstract class instead of an Objective-C protocol.


fixed: 1293395
Change-Id: I817629af39f903bcab685d985c3d1784b4427951
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3440130
Reviewed-by: Jérôme Lebel <jlebel@chromium.org>
Commit-Queue: Mark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#967638}
diff --git a/components/signin/ios/browser/account_consistency_service.h b/components/signin/ios/browser/account_consistency_service.h
index 6f72ce6f..04b9fa5 100644
--- a/components/signin/ios/browser/account_consistency_service.h
+++ b/components/signin/ios/browser/account_consistency_service.h
@@ -51,7 +51,7 @@
   // Sets the handler for |web_state| that reacts on Gaia responses with the
   // X-Chrome-Manage-Accounts header and notifies |delegate|.
   void SetWebStateHandler(web::WebState* web_state,
-                          id<ManageAccountsDelegate> delegate);
+                          ManageAccountsDelegate* delegate);
   // Removes the handler associated with |web_state|.
   void RemoveWebStateHandler(web::WebState* web_state);
 
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm
index de8d047f..03de401f 100644
--- a/components/signin/ios/browser/account_consistency_service.mm
+++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -107,7 +107,7 @@
                             AccountConsistencyService* service,
                             AccountReconcilor* account_reconcilor,
                             signin::IdentityManager* identity_manager,
-                            id<ManageAccountsDelegate> delegate);
+                            ManageAccountsDelegate* delegate);
 
   void WebStateDestroyed(web::WebState* web_state) override;
 
@@ -145,7 +145,7 @@
   AccountReconcilor* account_reconcilor_;                   // Weak.
   signin::IdentityManager* identity_manager_;
   web::WebState* web_state_;
-  __weak id<ManageAccountsDelegate> delegate_;
+  ManageAccountsDelegate* delegate_;  // Weak.
   base::WeakPtrFactory<AccountConsistencyHandler> weak_ptr_factory_;
 };
 
@@ -154,7 +154,7 @@
     AccountConsistencyService* service,
     AccountReconcilor* account_reconcilor,
     signin::IdentityManager* identity_manager,
-    id<ManageAccountsDelegate> delegate)
+    ManageAccountsDelegate* delegate)
     : web::WebStatePolicyDecider(web_state),
       account_consistency_service_(service),
       account_reconcilor_(account_reconcilor),
@@ -232,7 +232,8 @@
       GURL continue_url = GURL(params.continue_url);
       DLOG_IF(ERROR, !params.continue_url.empty() && !continue_url.is_valid())
           << "Invalid continuation URL: \"" << continue_url << "\"";
-      [delegate_ onGoIncognito:continue_url];
+      if (delegate_)
+        delegate_->OnGoIncognito(continue_url);
       break;
     }
     case signin::GAIA_SERVICE_TYPE_SIGNUP:
@@ -253,11 +254,13 @@
           return;
         }
       }
-      [delegate_ onAddAccount];
+      if (delegate_)
+        delegate_->OnAddAccount();
       break;
     case signin::GAIA_SERVICE_TYPE_SIGNOUT:
     case signin::GAIA_SERVICE_TYPE_DEFAULT:
-      [delegate_ onManageAccounts];
+      if (delegate_)
+        delegate_->OnManageAccounts();
       break;
     case signin::GAIA_SERVICE_TYPE_NONE:
       NOTREACHED();
@@ -281,13 +284,15 @@
     // is not in an inconsistent state (where the identities on the device
     // are different than those on the web). Fallback to asking the user to
     // add an account.
-    [delegate_ onAddAccount];
+    if (delegate_)
+      delegate_->OnAddAccount();
     return;
   }
   web_state_->OpenURL(web::WebState::OpenURLParams(
       url, web::Referrer(), WindowOpenDisposition::CURRENT_TAB,
       ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
-  [delegate_ onRestoreGaiaCookies];
+  if (delegate_)
+    delegate_->OnRestoreGaiaCookies();
   LogIOSGaiaCookiesState(
       GaiaCookieStateOnSignedInNavigation::kGaiaCookieRestoredOnShowInfobar);
 }
@@ -303,9 +308,9 @@
     return;
   }
 
-  if (show_consistency_web_signin_ &&
+  if (delegate_ && show_consistency_web_signin_ &&
       gaia::IsGaiaSignonRealm(url.DeprecatedGetOriginAsURL())) {
-    [delegate_ onShowConsistencyPromo:url webState:web_state];
+    delegate_->OnShowConsistencyPromo(url, web_state);
   }
   show_consistency_web_signin_ = false;
 }
@@ -397,7 +402,7 @@
 
 void AccountConsistencyService::SetWebStateHandler(
     web::WebState* web_state,
-    id<ManageAccountsDelegate> delegate) {
+    ManageAccountsDelegate* delegate) {
   DCHECK(!is_shutdown_) << "SetWebStateHandler called after Shutdown";
   DCHECK(handlers_map_.find(web_state) == handlers_map_.end());
   handlers_map_.insert(std::make_pair(
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm
index 21906c9..4621cd6 100644
--- a/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -44,6 +44,8 @@
 #error "This file requires ARC support."
 #endif
 
+using testing::NiceMock;
+
 namespace {
 // Fake identity email.
 const char* kFakeEmail = "janedoe@gmail.com";
@@ -94,6 +96,34 @@
   MOCK_METHOD1(OnReceivedManageAccountsResponse, void(signin::GAIAServiceType));
 };
 
+// Fake delegate implementation; all it does it count delegate calls.
+class FakeManageAccountsDelegate : public ManageAccountsDelegate {
+ public:
+  FakeManageAccountsDelegate() {}
+  ~FakeManageAccountsDelegate() override {}
+
+  void OnRestoreGaiaCookies() override { restore_cookies_call_count_++; }
+  void OnManageAccounts() override { manage_accounts_call_count_++; }
+  void OnAddAccount() override { add_account_call_count_++; }
+  void OnShowConsistencyPromo(const GURL& url,
+                              web::WebState* webState) override {
+    show_promo_call_count_++;
+  }
+  void OnGoIncognito(const GURL& url) override { go_incognito_call_count_++; }
+
+  int total_call_count() {
+    return restore_cookies_call_count_ + manage_accounts_call_count_ +
+           add_account_call_count_ + show_promo_call_count_ +
+           go_incognito_call_count_;
+  }
+
+  int restore_cookies_call_count_ = 0;
+  int manage_accounts_call_count_ = 0;
+  int add_account_call_count_ = 0;
+  int show_promo_call_count_ = 0;
+  int go_incognito_call_count_ = 0;
+};
+
 // FakeWebState that allows control over its policy decider.
 class FakeWebState : public web::FakeWebState {
  public:
@@ -155,8 +185,9 @@
         false /* restore_session */);
     cookie_settings_ = new content_settings::CookieSettings(settings_map_.get(),
                                                             &prefs_, false, "");
+    // Use a NiceMock here to suppress "uninteresting call" warnings.
     account_reconcilor_ =
-        std::make_unique<MockAccountReconcilor>(signin_client_.get());
+        std::make_unique<NiceMock<MockAccountReconcilor>>(signin_client_.get());
     ResetAccountConsistencyService();
   }
 
@@ -230,7 +261,7 @@
 
   // Navigation APIs.
   void SimulateNavigateToURL(NSURLResponse* response,
-                             id<ManageAccountsDelegate> delegate) {
+                             ManageAccountsDelegate* delegate) {
     SimulateNavigateToURL(response, delegate,
                           web::PageLoadCompletionStatus::SUCCESS,
                           /* expected_allowed_response=*/true);
@@ -238,15 +269,14 @@
 
   void SimulateNavigateToURLWithPageLoadFailure(
       NSURLResponse* response,
-      id<ManageAccountsDelegate> delegate) {
+      ManageAccountsDelegate* delegate) {
     SimulateNavigateToURL(response, delegate,
                           web::PageLoadCompletionStatus::FAILURE,
                           /* expected_allowed_response=*/true);
   }
 
-  void SimulateNavigateToURLWithInterruption(
-      NSURLResponse* response,
-      id<ManageAccountsDelegate> delegate) {
+  void SimulateNavigateToURLWithInterruption(NSURLResponse* response,
+                                             ManageAccountsDelegate* delegate) {
     SimulateNavigateToURL(response, delegate,
                           web::PageLoadCompletionStatus::SUCCESS,
                           /* expected_allowed_response=*/false);
@@ -283,7 +313,7 @@
                                   base::OnceCallback<void(uint)>());
   }
 
-  void SetWebStateHandler(id<ManageAccountsDelegate> delegate) {
+  void SetWebStateHandler(ManageAccountsDelegate* delegate) {
     // If we have already added the |web_state_| with a previous |delegate|,
     // remove it to enforce a one-to-one mapping between web state handler and
     // web state.
@@ -301,6 +331,7 @@
   web::FakeBrowserState browser_state_;
   sync_preferences::TestingPrefServiceSyncable prefs_;
   FakeWebState web_state_;
+  FakeManageAccountsDelegate delegate_;
   network::TestURLLoaderFactory test_url_loader_factory_;
 
   std::unique_ptr<signin::IdentityTestEnvironment> identity_test_env_;
@@ -309,7 +340,7 @@
 
  private:
   void SimulateNavigateToURL(NSURLResponse* response,
-                             id<ManageAccountsDelegate> delegate,
+                             ManageAccountsDelegate* delegate,
                              web::PageLoadCompletionStatus page_status,
                              bool expect_allowed_response) {
     SetWebStateHandler(delegate);
@@ -361,8 +392,6 @@
   CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
   CheckNoChromeConnectedCookieForDomain(kCountryGoogleDomain);
 
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
   NSDictionary* headers = [NSDictionary dictionary];
 
   NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
@@ -371,7 +400,7 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
 
   // Check that cookies was also added for |kCountryGoogleDomain|.
   CheckDomainHasChromeConnectedCookie(kGoogleDomain);
@@ -393,8 +422,6 @@
 // Tests that the X-Chrome-Manage-Accounts header is ignored unless it comes
 // from Gaia signon realm.
 TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsNotOnGaia) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
   NSDictionary* headers =
       [NSDictionary dictionaryWithObject:@"action=DEFAULT"
                                   forKey:@"X-Chrome-Manage-Accounts"];
@@ -404,17 +431,13 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURL(response, delegate);
-
-  EXPECT_OCMOCK_VERIFY(delegate);
+  SimulateNavigateToURL(response, &delegate_);
+  EXPECT_EQ(0, delegate_.total_call_count());
 }
 
 // Tests that navigation to Gaia signon realm with no X-Chrome-Manage-Accounts
 // header in the response are simply untouched.
 TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsNoHeader) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-
   NSDictionary* headers = [NSDictionary dictionary];
   NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
        initWithURL:[NSURL URLWithString:@"https://accounts.google.com/"]
@@ -422,20 +445,14 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURL(response, delegate);
-
-  EXPECT_OCMOCK_VERIFY(delegate);
+  SimulateNavigateToURL(response, &delegate_);
+  EXPECT_EQ(0, delegate_.total_call_count());
 }
 
 // Tests that the ManageAccountsDelegate is notified when a navigation on Gaia
 // signon realm returns with a X-Chrome-Manage-Accounts header with action
 // DEFAULT.
 TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsDefault) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  // Default action is |onManageAccounts|.
-  [[delegate expect] onManageAccounts];
-
   NSDictionary* headers =
       [NSDictionary dictionaryWithObject:@"action=DEFAULT"
                                   forKey:@"X-Chrome-Manage-Accounts"];
@@ -448,19 +465,15 @@
                                         signin::GAIA_SERVICE_TYPE_DEFAULT))
       .Times(1);
 
-  SimulateNavigateToURLWithInterruption(response, delegate);
+  SimulateNavigateToURLWithInterruption(response, &delegate_);
 
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.manage_accounts_call_count_);
 }
 
 // Tests that the ManageAccountsDelegate is notified when a navigation on Gaia
 // signon realm returns with a X-Auto-Login header.
 TEST_F(AccountConsistencyServiceTest, ChromeShowConsistencyPromo) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[[delegate expect] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()
-                                                           webState:nullptr];
-
   NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused"
                                                       forKey:@"X-Auto-Login"];
   NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
@@ -469,19 +482,15 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
 
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.show_promo_call_count_);
 }
 
 // Tests that the consistency promo is not displayed when a page fails to load.
 TEST_F(AccountConsistencyServiceTest,
        ChromeNotShowConsistencyPromoOnPageLoadFailure) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[[delegate reject] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()
-                                                           webState:nullptr];
-
   NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused"
                                                       forKey:@"X-Auto-Login"];
   NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
@@ -490,21 +499,14 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURLWithPageLoadFailure(response, delegate);
-
-  EXPECT_OCMOCK_VERIFY(delegate);
+  SimulateNavigateToURLWithPageLoadFailure(response, &delegate_);
+  EXPECT_EQ(0, delegate_.total_call_count());
 }
 
 // Tests that the consistency promo is not displayed when a page fails to load
 // and user chooses another action.
 TEST_F(AccountConsistencyServiceTest,
        ChromeNotShowConsistencyPromoOnPageLoadFailureRedirect) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[delegate expect] onAddAccount];
-  [[[delegate reject] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()
-                                                           webState:nullptr];
-
   EXPECT_CALL(*account_reconcilor_, OnReceivedManageAccountsResponse(
                                         signin::GAIA_SERVICE_TYPE_ADDSESSION));
 
@@ -516,7 +518,7 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURLWithPageLoadFailure(responseSignin, delegate);
+  SimulateNavigateToURLWithPageLoadFailure(responseSignin, &delegate_);
 
   NSDictionary* headersAddAccount =
       [NSDictionary dictionaryWithObject:@"action=ADDSESSION"
@@ -527,19 +529,17 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headersAddAccount];
 
-  SimulateNavigateToURLWithInterruption(responseAddAccount, delegate);
+  SimulateNavigateToURLWithInterruption(responseAddAccount, &delegate_);
 
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.add_account_call_count_);
+  EXPECT_EQ(0, delegate_.show_promo_call_count_);
 }
 
 // Tests that the ManageAccountsDelegate is notified when a navigation on Gaia
 // signon realm returns with a X-Chrome-Manage-Accounts header with ADDSESSION
 // action.
 TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsShowAddAccount) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[delegate expect] onAddAccount];
-
   NSDictionary* headers =
       [NSDictionary dictionaryWithObject:@"action=ADDSESSION"
                                   forKey:@"X-Chrome-Manage-Accounts"];
@@ -552,9 +552,9 @@
                                         signin::GAIA_SERVICE_TYPE_ADDSESSION))
       .Times(1);
 
-  SimulateNavigateToURLWithInterruption(response, delegate);
-
-  EXPECT_OCMOCK_VERIFY(delegate);
+  SimulateNavigateToURLWithInterruption(response, &delegate_);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.add_account_call_count_);
 }
 
 // Tests that domains with cookie are correctly loaded from the prefs on service
@@ -619,8 +619,6 @@
 TEST_F(AccountConsistencyServiceTest, SetChromeConnectedCookie) {
   SignIn();
 
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
   NSDictionary* headers = [NSDictionary dictionary];
 
   // HTTP response URL is eligible for Mirror (the test does not use google.com
@@ -631,10 +629,10 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
   SimulateExternalSourceRemovesAllGoogleDomainCookies();
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
 
   CheckDomainHasChromeConnectedCookie(kGoogleDomain);
   CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
@@ -645,10 +643,6 @@
 TEST_F(AccountConsistencyServiceTest, GAIACookieMissingOnSignin) {
   SignIn();
 
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[delegate expect] onAddAccount];
-
   NSDictionary* headers =
       [NSDictionary dictionaryWithObject:@"action=ADDSESSION"
                                   forKey:@"X-Chrome-Manage-Accounts"];
@@ -661,17 +655,18 @@
                                         signin::GAIA_SERVICE_TYPE_ADDSESSION))
       .Times(2);
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(kGAIACookieOnNavigationHistogram, 0);
 
   SimulateExternalSourceRemovesAllGoogleDomainCookies();
 
   // Gaia cookie is not restored due to one-hour time restriction.
-  SimulateNavigateToURLWithInterruption(response, delegate);
+  SimulateNavigateToURLWithInterruption(response, &delegate_);
   histogram_tester.ExpectTotalCount(kGAIACookieOnNavigationHistogram, 1);
 
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.add_account_call_count_);
 }
 
 // Ensures that set and remove cookie operations are handled in the order
@@ -716,9 +711,6 @@
 // is signed out and navigating to google.com.
 TEST_F(AccountConsistencyServiceTest,
        SetChromeConnectedCookiesSignedOutGoogleVisitor) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-
   NSDictionary* headers =
       [NSDictionary dictionaryWithObject:@"action=ADDSESSION"
                                   forKey:@"X-Chrome-Manage-Accounts"];
@@ -730,21 +722,16 @@
 
   CheckNoChromeConnectedCookies();
 
-  SimulateNavigateToURL(response, delegate);
+  SimulateNavigateToURL(response, &delegate_);
 
   CheckNoChromeConnectedCookies();
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(0, delegate_.total_call_count());
 }
 
 // Ensures that CHROME_CONNECTED cookies are not set when the user is signed out
 // after the sign-in promo is shown.
 TEST_F(AccountConsistencyServiceTest,
        SetChromeConnectedCookiesSignedOutGaiaVisitor) {
-  id delegate =
-      [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
-  [[[delegate expect] ignoringNonObjectArgs] onShowConsistencyPromo:GURL()
-                                                           webState:nullptr];
-
   NSDictionary* headers = [NSDictionary dictionaryWithObject:@"args=unused"
                                                       forKey:@"X-Auto-Login"];
   NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc]
@@ -753,7 +740,7 @@
        HTTPVersion:@"HTTP/1.1"
       headerFields:headers];
 
-  SetWebStateHandler(delegate);
+  SetWebStateHandler(&delegate_);
   EXPECT_TRUE(web_state_.ShouldAllowResponse(response,
                                              /* for_main_frame = */ true));
 
@@ -761,7 +748,8 @@
   web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
 
   CheckNoChromeConnectedCookies();
-  EXPECT_OCMOCK_VERIFY(delegate);
+  EXPECT_EQ(1, delegate_.total_call_count());
+  EXPECT_EQ(1, delegate_.show_promo_call_count_);
 }
 
 TEST_F(AccountConsistencyServiceTest, SetGaiaCookieUpdateBeforeDelay) {
@@ -781,7 +769,7 @@
   // Advance clock, but stay within the one-hour Gaia update time.
   base::TimeDelta oneMinuteDelta = base::Minutes(1);
   task_environment_.FastForwardBy(oneMinuteDelta);
-  SimulateNavigateToURLWithInterruption(response, nil);
+  SimulateNavigateToURLWithInterruption(response, nullptr);
 
   // Does not process the second Gaia restore event.
   CheckGaiaCookieWithUpdateTime(base::Time::Now() - oneMinuteDelta);
diff --git a/components/signin/ios/browser/manage_accounts_delegate.h b/components/signin/ios/browser/manage_accounts_delegate.h
index 878f3e0..4a4c4ac 100644
--- a/components/signin/ios/browser/manage_accounts_delegate.h
+++ b/components/signin/ios/browser/manage_accounts_delegate.h
@@ -10,34 +10,40 @@
 class WebState;
 }
 
-// TODO(crbug.com/1293395): Refactor ManageAccountsDelegate into a C++ abstract
-// class.
-@protocol ManageAccountsDelegate<NSObject>
+class ManageAccountsDelegate {
+ public:
+  ManageAccountsDelegate(const ManageAccountsDelegate&) = delete;
+  ManageAccountsDelegate& operator=(const ManageAccountsDelegate&) = delete;
 
-// Called when Gaia cookies have been regenerated for a specific user sign-in.
-// This occurs when a SAPISID cookie has been deleted by the operating system.
-- (void)onRestoreGaiaCookies;
+  virtual ~ManageAccountsDelegate() = default;
 
-// Called when the user taps on a manage accounts button in a Google web
-// property.
-- (void)onManageAccounts;
+  // Called when Gaia cookies have been regenerated for a specific user sign-in.
+  // This occurs when a SAPISID cookie has been deleted by the operating system.
+  virtual void OnRestoreGaiaCookies() = 0;
 
-// Called when the user taps on an add account button in a Google web property.
-- (void)onAddAccount;
+  // Called when the user taps on a manage accounts button in a Google web
+  // property.
+  virtual void OnManageAccounts() = 0;
 
-// Called when the user taps a sign-in or add account button in a Google web
-// property.
-// |url| is the continuation URL received from the server. If it is valid,
-// then this delegate should navigate to |url|.
-- (void)onShowConsistencyPromo:(const GURL&)url
-                      webState:(web::WebState*)webState;
+  // Called when the user taps on an add account button in a Google web
+  // property.
+  virtual void OnAddAccount() = 0;
 
-// Called when the user taps on go incognito button in a Google web property.
-// |url| is the continuation URL received from the server. If it is valid,
-// then this delegate should open an incognito tab and navigate to |url|.
-// If it is not valid, then this delegate should open a new incognito tab.
-- (void)onGoIncognito:(const GURL&)url;
+  // Called when the user taps a sign-in or add account button in a Google web
+  // property.
+  // |url| is the continuation URL received from the server. If it is valid,
+  // then this delegate should navigate to |url|.
+  virtual void OnShowConsistencyPromo(const GURL& url,
+                                      web::WebState* webState) = 0;
 
-@end
+  // Called when the user taps on go incognito button in a Google web property.
+  // |url| is the continuation URL received from the server. If it is valid,
+  // then this delegate should open an incognito tab and navigate to |url|.
+  // If it is not valid, then this delegate should open a new incognito tab.
+  virtual void OnGoIncognito(const GURL& url) = 0;
+
+ protected:
+  ManageAccountsDelegate() = default;
+};
 
 #endif  // COMPONENTS_SIGNIN_IOS_BROWSER_MANAGE_ACCOUNTS_DELEGATE_H_
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 2b953a7..fc1da7c 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -141,6 +141,27 @@
   __weak id<PreloadCancelling> cancel_handler_ = nil;
 };
 
+class PreloadManageAccountsDelegate : public ManageAccountsDelegate {
+ public:
+  explicit PreloadManageAccountsDelegate(id<PreloadCancelling> canceler)
+      : canceler_(canceler) {}
+  ~PreloadManageAccountsDelegate() override {}
+
+  void OnRestoreGaiaCookies() override { [canceler_ schedulePrerenderCancel]; }
+  void OnManageAccounts() override { [canceler_ schedulePrerenderCancel]; }
+  void OnAddAccount() override { [canceler_ schedulePrerenderCancel]; }
+  void OnShowConsistencyPromo(const GURL& url,
+                              web::WebState* webState) override {
+    [canceler_ schedulePrerenderCancel];
+  }
+  void OnGoIncognito(const GURL& url) override {
+    [canceler_ schedulePrerenderCancel];
+  }
+
+ private:
+  __weak id<PreloadCancelling> canceler_;
+};
+
 // Maximum time to let a cancelled webState attempt to finish restore.
 static const size_t kMaximumCancelledWebStateDelay = 2;
 
@@ -206,7 +227,6 @@
                                  CRWWebStateDelegate,
                                  CRWWebStateObserver,
                                  CRWWebStatePolicyDecider,
-                                 ManageAccountsDelegate,
                                  PrefObserverDelegate,
                                  PreloadCancelling> {
   std::unique_ptr<web::WebStateDelegateBridge> _webStateDelegate;
@@ -232,6 +252,8 @@
   // one. This is needed by |startPrerender| to build the new webstate with the
   // same sessions.
   web::WebState* _webStateToReplace;
+
+  std::unique_ptr<PreloadManageAccountsDelegate> _manageAccountsDelegate;
 }
 
 // The ChromeBrowserState passed on initialization.
@@ -310,6 +332,8 @@
     _observerBridge->ObserveChangesForPreference(
         prefs::kNetworkPredictionSetting, &_prefChangeRegistrar);
     _dialogPresenter = std::make_unique<PreloadJavaScriptDialogPresenter>(self);
+    _manageAccountsDelegate =
+        std::make_unique<PreloadManageAccountsDelegate>(self);
     if (_networkPredictionSetting ==
         prerender_prefs::NetworkPredictionSetting::kEnabledWifiOnly) {
       _connectionTypeObserver =
@@ -580,29 +604,6 @@
   decisionHandler(WebStatePolicyDecider::PolicyDecision::Allow());
 }
 
-#pragma mark - ManageAccountsDelegate
-
-- (void)onRestoreGaiaCookies {
-  [self schedulePrerenderCancel];
-}
-
-- (void)onManageAccounts {
-  [self schedulePrerenderCancel];
-}
-
-- (void)onShowConsistencyPromo:(const GURL&)url
-                      webState:(web::WebState*)webState {
-  [self schedulePrerenderCancel];
-}
-
-- (void)onAddAccount {
-  [self schedulePrerenderCancel];
-}
-
-- (void)onGoIncognito:(const GURL&)url {
-  [self schedulePrerenderCancel];
-}
-
 #pragma mark - PrefObserverDelegate
 
 - (void)onPreferenceChanged:(const std::string&)preferenceName {
@@ -724,7 +725,8 @@
   if (AccountConsistencyService* accountConsistencyService =
           ios::AccountConsistencyServiceFactory::GetForBrowserState(
               self.browserState)) {
-    accountConsistencyService->SetWebStateHandler(_webState.get(), self);
+    accountConsistencyService->SetWebStateHandler(
+        _webState.get(), _manageAccountsDelegate.get());
   }
 
   HistoryTabHelper::FromWebState(_webState.get())
diff --git a/ios/chrome/browser/signin/account_consistency_browser_agent.h b/ios/chrome/browser/signin/account_consistency_browser_agent.h
index 6e5adc3..6b22dc6 100644
--- a/ios/chrome/browser/signin/account_consistency_browser_agent.h
+++ b/ios/chrome/browser/signin/account_consistency_browser_agent.h
@@ -5,6 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_SIGNIN_ACCOUNT_CONSISTENCY_BROWSER_AGENT_H_
 #define IOS_CHROME_BROWSER_SIGNIN_ACCOUNT_CONSISTENCY_BROWSER_AGENT_H_
 
+#import "components/signin/ios/browser/manage_accounts_delegate.h"
 #import "ios/chrome/browser/main/browser_observer.h"
 #import "ios/chrome/browser/main/browser_user_data.h"
 #import "ios/chrome/browser/web_state_list/web_state_dependency_installation_observer.h"
@@ -21,6 +22,7 @@
 class AccountConsistencyBrowserAgent
     : public BrowserUserData<AccountConsistencyBrowserAgent>,
       public DependencyInstaller,
+      public ManageAccountsDelegate,
       BrowserObserver {
  public:
   // |browser| is the browser this agent is attached to.
@@ -41,12 +43,13 @@
   void InstallDependency(web::WebState* web_state) override;
   void UninstallDependency(web::WebState* web_state) override;
 
-  // Handlers for the corresponding protocol methods in ManageAccountDelegate.
-  void OnRestoreGaiaCookies();
-  void OnManageAccounts();
-  void OnAddAccount();
-  void OnShowConsistencyPromo(const GURL& url, web::WebState* webState);
-  void OnGoIncognito(const GURL& url);
+  // ManageAccountsDelegate
+  void OnRestoreGaiaCookies() override;
+  void OnManageAccounts() override;
+  void OnAddAccount() override;
+  void OnShowConsistencyPromo(const GURL& url,
+                              web::WebState* webState) override;
+  void OnGoIncognito(const GURL& url) override;
 
  private:
   AccountConsistencyBrowserAgent(Browser* browser,
diff --git a/ios/chrome/browser/signin/account_consistency_browser_agent.mm b/ios/chrome/browser/signin/account_consistency_browser_agent.mm
index cc8dfc6..cbf8c08 100644
--- a/ios/chrome/browser/signin/account_consistency_browser_agent.mm
+++ b/ios/chrome/browser/signin/account_consistency_browser_agent.mm
@@ -8,7 +8,6 @@
 
 #import "components/signin/core/browser/account_reconcilor.h"
 #import "components/signin/ios/browser/account_consistency_service.h"
-#import "components/signin/ios/browser/manage_accounts_delegate.h"
 #import "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #import "ios/chrome/browser/signin/account_reconcilor_factory.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
@@ -23,50 +22,6 @@
 
 BROWSER_USER_DATA_KEY_IMPL(AccountConsistencyBrowserAgent)
 
-// Obj-C bridge object to forward ManageAccountsDelegate methods to the browser
-// agent.
-// TODO(crbug.com/1293395): Refactor ManageAccountsDelegate into C++, removing
-// the need for this object.
-@interface ManageAccountsDelegateBridge : NSObject <ManageAccountsDelegate>
-
-- (instancetype)initWithBrowserAgent:(AccountConsistencyBrowserAgent*)agent;
-
-@end
-
-@implementation ManageAccountsDelegateBridge {
-  AccountConsistencyBrowserAgent* _agent;
-}
-
-- (instancetype)initWithBrowserAgent:(AccountConsistencyBrowserAgent*)agent {
-  if (self = [super init]) {
-    _agent = agent;
-  }
-  return self;
-}
-
-- (void)onRestoreGaiaCookies {
-  _agent->OnRestoreGaiaCookies();
-}
-
-- (void)onManageAccounts {
-  _agent->OnManageAccounts();
-}
-
-- (void)onAddAccount {
-  _agent->OnAddAccount();
-}
-
-- (void)onShowConsistencyPromo:(const GURL&)url
-                      webState:(web::WebState*)webState {
-  _agent->OnShowConsistencyPromo(url, webState);
-}
-
-- (void)onGoIncognito:(const GURL&)url {
-  _agent->OnGoIncognito(url);
-}
-
-@end
-
 void AccountConsistencyBrowserAgent::CreateForBrowser(
     Browser* browser,
     UIViewController* base_view_controller,
@@ -86,7 +41,6 @@
     : base_view_controller_(base_view_controller),
       handler_(handler),
       browser_(browser) {
-  bridge_ = [[ManageAccountsDelegateBridge alloc] initWithBrowserAgent:this];
   installation_observer_ =
       std::make_unique<WebStateDependencyInstallationObserver>(
           browser->GetWebStateList(), this);
@@ -100,7 +54,7 @@
   if (AccountConsistencyService* accountConsistencyService =
           ios::AccountConsistencyServiceFactory::GetForBrowserState(
               browser_->GetBrowserState())) {
-    accountConsistencyService->SetWebStateHandler(web_state, bridge_);
+    accountConsistencyService->SetWebStateHandler(web_state, this);
   }
 }