[SendTabToSelf] Show STTS infobar when a WebState becomes active.

If we receive a shared tab when the user is not on a visible WebState
(e.g. Tab Grid or Settings Page), we register as observers to 2 things:

1- The WebStateList in case the user creates a new tab, or activates
a different tab.

2- The active but not visible WebState, in case the user comes back to
it.

When either is triggered, we'll show the infobar on the relevant
WebState and then stop observing these objects.

Bug: 944602
Change-Id: I33d1505fd639b37ed82ef22c24151b6bc8a1c371
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1574118
Commit-Queue: sebsg <sebsg@chromium.org>
Reviewed-by: Peter Lee <pkl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#653570}
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
index bcc9e0f..ba386076 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.h
@@ -12,11 +12,17 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/send_tab_to_self_model_observer.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
+#import "ios/web/public/web_state/web_state_observer.h"
 
 namespace ios {
 class ChromeBrowserState;
 }
 
+namespace web {
+class WebState;
+}
+
 namespace send_tab_to_self {
 class SendTabToSelfEntry;
 class SendTabToSelfModel;
@@ -24,12 +30,15 @@
 // Service that listens for SendTabToSelf model changes and calls UI
 // handlers to update the UI accordingly.
 class SendTabToSelfClientServiceIOS : public KeyedService,
-                                      public SendTabToSelfModelObserver {
+                                      public SendTabToSelfModelObserver,
+                                      public WebStateListObserver,
+                                      public web::WebStateObserver {
  public:
   SendTabToSelfClientServiceIOS(ios::ChromeBrowserState* browser_state,
                                 SendTabToSelfModel* model);
   ~SendTabToSelfClientServiceIOS() override;
 
+  // SendTabToSelfModelObserver::
   // Keeps track of when the model is loaded so that updates to the
   // model can be pushed afterwards.
   void SendTabToSelfModelLoaded() override;
@@ -41,13 +50,41 @@
   // registered through ReceivingUIRegistry.
   void EntriesRemovedRemotely(const std::vector<std::string>& guids) override;
 
+  // WebStateListObserver::
+  void WebStateActivatedAt(WebStateList* web_state_list,
+                           web::WebState* old_web_state,
+                           web::WebState* new_web_state,
+                           int active_index,
+                           int reason) override;
+
+  // WebStateObserver::
+  void WasShown(web::WebState* web_state) override;
+  void WebStateDestroyed(web::WebState* web_state) override;
+
  private:
+  // Display an infobar for |entry| on the specified |web_state|.
+  void DisplayInfoBar(web::WebState* web_state,
+                      const SendTabToSelfEntry* entry);
+
+  // Stop observing the WebState and WebStateList and reset associated
+  // variables.
+  void CleanUpObserversAndVariables();
+
   // Owned by the SendTabToSelfSyncService which should outlive this class
   SendTabToSelfModel* model_;
 
   // The current browser state. Must outlive this object.
   ios::ChromeBrowserState* browser_state_;
 
+  // The pending SendTabToSelf entry to display an InfoBar for.
+  const SendTabToSelfEntry* entry_ = nullptr;
+
+  // The WebStateList that is being observed.
+  WebStateList* web_state_list_ = nullptr;
+
+  // The WebState that is being observed.
+  web::WebState* web_state_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(SendTabToSelfClientServiceIOS);
 };
 
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
index 52dc7e0..c30858f 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_client_service_ios.mm
@@ -40,6 +40,16 @@
 SendTabToSelfClientServiceIOS::~SendTabToSelfClientServiceIOS() {
   model_->RemoveObserver(this);
   model_ = nullptr;
+
+  if (web_state_list_) {
+    web_state_list_->RemoveObserver(this);
+    web_state_list_ = nullptr;
+  }
+
+  if (web_state_) {
+    web_state_->RemoveObserver(this);
+    web_state_ = nullptr;
+  }
 }
 
 void SendTabToSelfClientServiceIOS::SendTabToSelfModelLoaded() {
@@ -69,24 +79,37 @@
     return;
   }
 
-  // If there is no web state or it is not visible, we cannot add an infobar to
-  // it.
-  // TODO(crbug.com/944602): Wait until a web state is active and add the info
-  // bar then.
   web::WebState* web_state = web_state_list->GetActiveWebState();
   if (!web_state || !web_state->IsVisible()) {
-    NOTIMPLEMENTED();
+    // If the active WebState is not visible it means the user is in the
+    // Tab Grid screen or a Settings page. Register as an observer of the
+    // active WebState and WebStateList in order to be notified if the WebState
+    // becomes visible again, or if the user changes tab or creates a new tab.
+    if (web_state) {
+      web_state_ = web_state;
+      web_state_->AddObserver(this);
+    }
+
+    if (!web_state_list_ || web_state_list_ != web_state_list) {
+      if (web_state_list_) {
+        web_state_list_->RemoveObserver(this);
+      }
+      web_state_list_ = web_state_list;
+      web_state_list_->AddObserver(this);
+    }
+
+    // Pick the most recent entry since only one Infobar can be shown at a time.
+    // TODO(crbug.com/944602): Create a function that returns the most recently
+    // shared entry.
+    entry_ = new_entries.back();
+
     return;
   }
 
-  infobars::InfoBarManager* infobar_manager =
-      InfoBarManagerImpl::FromWebState(web_state);
-
   // Since we can only show one infobar at the time, pick the most recent entry.
   // TODO(crbug.com/944602): Create a function that returns the most recently
   // shared entry.
-  infobar_manager->AddInfoBar(CreateConfirmInfoBar(
-      IOSSendTabToSelfInfoBarDelegate::Create(new_entries.back())));
+  DisplayInfoBar(web_state, new_entries.back());
 }
 
 void SendTabToSelfClientServiceIOS::EntriesRemovedRemotely(
@@ -97,4 +120,68 @@
   NOTIMPLEMENTED();
 }
 
+void SendTabToSelfClientServiceIOS::WebStateActivatedAt(
+    WebStateList* web_state_list,
+    web::WebState* old_web_state,
+    web::WebState* new_web_state,
+    int active_index,
+    int reason) {
+  DCHECK(entry_);
+
+  // This can happen if the user close the last tab in the tab picker.
+  if (!new_web_state) {
+    return;
+  }
+
+  DisplayInfoBar(new_web_state, entry_);
+
+  CleanUpObserversAndVariables();
+}
+
+void SendTabToSelfClientServiceIOS::WasShown(web::WebState* web_state) {
+  DCHECK(entry_);
+  DCHECK(web_state_);
+
+  DisplayInfoBar(web_state_, entry_);
+
+  CleanUpObserversAndVariables();
+}
+
+void SendTabToSelfClientServiceIOS::WebStateDestroyed(
+    web::WebState* web_state) {
+  DCHECK(web_state_);
+  DCHECK(web_state_ == web_state);
+
+  web_state_->RemoveObserver(this);
+  web_state_ = nullptr;
+}
+
+void SendTabToSelfClientServiceIOS::DisplayInfoBar(
+    web::WebState* web_state,
+    const SendTabToSelfEntry* entry) {
+  infobars::InfoBarManager* infobar_manager =
+      InfoBarManagerImpl::FromWebState(web_state);
+
+  if (!infobar_manager) {
+    return;
+  }
+
+  infobar_manager->AddInfoBar(
+      CreateConfirmInfoBar(IOSSendTabToSelfInfoBarDelegate::Create(entry)));
+}
+
+void SendTabToSelfClientServiceIOS::CleanUpObserversAndVariables() {
+  DCHECK(web_state_list_);
+
+  entry_ = nullptr;
+
+  web_state_list_->RemoveObserver(this);
+  web_state_list_ = nullptr;
+
+  if (web_state_) {
+    web_state_->RemoveObserver(this);
+    web_state_ = nullptr;
+  }
+}
+
 }  // namespace send_tab_to_self