weblayer: changes Tab ownership

Browser now owns the Tabs. DCHECKs are added for the Android side, as the
Android side should always add/remove tests before destroying them.

BUG=1046406
TEST=covered by tests

Change-Id: I55660c528ae85224e42747446dab54e15ab307f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2028297
Reviewed-by: Clark DuVall <[email protected]>
Commit-Queue: Scott Violet <[email protected]>
Cr-Commit-Position: refs/heads/master@{#736613}
diff --git a/weblayer/browser/browser_impl.cc b/weblayer/browser/browser_impl.cc
index ed9b3e5..a32364f9 100644
--- a/weblayer/browser/browser_impl.cc
+++ b/weblayer/browser/browser_impl.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/containers/unique_ptr_adapters.h"
 #include "base/path_service.h"
 #include "components/base32/base32.h"
 #include "content/public/browser/browser_context.h"
@@ -50,37 +51,49 @@
 }
 
 BrowserImpl::~BrowserImpl() {
-  while (!tabs_.empty()) {
-    Tab* last_tab = tabs_.back();
-    RemoveTab(last_tab);
-    DCHECK(!base::Contains(tabs_, last_tab));
-  }
+#if defined(OS_ANDROID)
+  // Android side should always remove tabs first (because the Java Tab class
+  // owns the C++ Tab). See BrowserImpl.destroy() (in the Java BrowserImpl
+  // class).
+  DCHECK(tabs_.empty());
+#else
+  while (!tabs_.empty())
+    RemoveTab(tabs_.back().get());
+#endif
 }
 
 TabImpl* BrowserImpl::CreateTabForSessionRestore(
     std::unique_ptr<content::WebContents> web_contents) {
-  TabImpl* tab = new TabImpl(profile_, std::move(web_contents));
+  std::unique_ptr<TabImpl> tab =
+      std::make_unique<TabImpl>(profile_, std::move(web_contents));
 #if defined(OS_ANDROID)
-  // The Java side takes ownership of Tab.
   Java_BrowserImpl_createTabForSessionRestore(
       base::android::AttachCurrentThread(), java_impl_,
-      reinterpret_cast<jlong>(tab));
+      reinterpret_cast<jlong>(tab.get()));
 #endif
-  AddTab(tab);
-  return tab;
+  TabImpl* tab_ptr = tab.get();
+  AddTab(std::move(tab));
+  return tab_ptr;
 }
 
 #if defined(OS_ANDROID)
 void BrowserImpl::AddTab(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& caller,
                          long native_tab) {
-  AddTab(reinterpret_cast<TabImpl*>(native_tab));
+  TabImpl* tab = reinterpret_cast<TabImpl*>(native_tab);
+  std::unique_ptr<Tab> owned_tab;
+  if (tab->browser())
+    owned_tab = tab->browser()->RemoveTab(tab);
+  else
+    owned_tab.reset(tab);
+  AddTab(std::move(owned_tab));
 }
 
 void BrowserImpl::RemoveTab(JNIEnv* env,
                             const base::android::JavaParamRef<jobject>& caller,
                             long native_tab) {
-  RemoveTab(reinterpret_cast<TabImpl*>(native_tab));
+  // The Java side owns the Tab.
+  RemoveTab(reinterpret_cast<TabImpl*>(native_tab)).release();
 }
 
 base::android::ScopedJavaLocalRef<jobjectArray> BrowserImpl::GetTabs(
@@ -93,7 +106,7 @@
   base::android::CheckException(env);
 
   for (size_t i = 0; i < tabs_.size(); ++i) {
-    TabImpl* tab = static_cast<TabImpl*>(tabs_[i]);
+    TabImpl* tab = static_cast<TabImpl*>(tabs_[i].get());
     env->SetObjectArrayElement(tabs, i, tab->GetJavaTab().obj());
   }
   return base::android::ScopedJavaLocalRef<jobjectArray>(env, tabs);
@@ -145,26 +158,30 @@
 
 #endif
 
-void BrowserImpl::AddTab(Tab* tab) {
+Tab* BrowserImpl::AddTab(std::unique_ptr<Tab> tab) {
   DCHECK(tab);
-  TabImpl* tab_impl = static_cast<TabImpl*>(tab);
-  if (tab_impl->browser() != this && tab_impl->browser())
-    tab_impl->browser()->RemoveTab(tab);
-  tabs_.push_back(tab);
+  TabImpl* tab_impl = static_cast<TabImpl*>(tab.get());
+  DCHECK(!tab_impl->browser());
+  tabs_.push_back(std::move(tab));
   tab_impl->set_browser(this);
 #if defined(OS_ANDROID)
   Java_BrowserImpl_onTabAdded(base::android::AttachCurrentThread(), java_impl_,
-                              tab ? tab_impl->GetJavaTab() : nullptr);
+                              tab_impl->GetJavaTab());
 #endif
   for (BrowserObserver& obs : browser_observers_)
-    obs.OnTabAdded(tab);
+    obs.OnTabAdded(tab_impl);
+  return tab_impl;
 }
 
-void BrowserImpl::RemoveTab(Tab* tab) {
+std::unique_ptr<Tab> BrowserImpl::RemoveTab(Tab* tab) {
   TabImpl* tab_impl = static_cast<TabImpl*>(tab);
   DCHECK_EQ(this, tab_impl->browser());
   static_cast<TabImpl*>(tab)->set_browser(nullptr);
-  tabs_.erase(std::find(tabs_.begin(), tabs_.end(), tab));
+  auto iter =
+      std::find_if(tabs_.begin(), tabs_.end(), base::MatchesUniquePtr(tab));
+  DCHECK(iter != tabs_.end());
+  std::unique_ptr<Tab> owned_tab = std::move(*iter);
+  tabs_.erase(iter);
   const bool active_tab_changed = active_tab_ == tab;
   if (active_tab_changed)
     active_tab_ = nullptr;
@@ -185,6 +202,7 @@
   }
   for (BrowserObserver& obs : browser_observers_)
     obs.OnTabRemoved(tab, active_tab_changed);
+  return owned_tab;
 }
 
 void BrowserImpl::SetActiveTab(Tab* tab) {
@@ -208,8 +226,11 @@
   return active_tab_;
 }
 
-const std::vector<Tab*>& BrowserImpl::GetTabs() {
-  return tabs_;
+std::vector<Tab*> BrowserImpl::GetTabs() {
+  std::vector<Tab*> tabs(tabs_.size());
+  for (size_t i = 0; i < tabs_.size(); ++i)
+    tabs[i] = tabs_[i].get();
+  return tabs;
 }
 
 void BrowserImpl::PrepareForShutdown() {