TabAndroid supports base::SupportUserData

This CL lets TabAndroid supports base::SupportUserData, then
ChromeAutocompleteProviderClient can store stripped urls in TabAndroid
to avoid stripping url over and over again.

(cherry picked from commit 4f474f7c1cfb245a2667b783a2026b73428d7e17)

Bug: 1094056
Change-Id: If0448c825965ddc7f43bd2aacc9c1d868e27b09f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2472461
Reviewed-by: Ted Choc <[email protected]>
Commit-Queue: Gang Wu <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#818132}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2486411
Reviewed-by: Gang Wu <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#538}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8379e32d..7e37891 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2768,6 +2768,7 @@
       "android/startup_bridge.h",
       "android/tab_android.cc",
       "android/tab_android.h",
+      "android/tab_android_user_data.h",
       "android/tab_browser_controls_constraints_helper.cc",
       "android/tab_browser_controls_constraints_helper.h",
       "android/tab_favicon.cc",
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 2b8bcda..9226bda 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -15,6 +15,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/supports_user_data.h"
 #include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
 #include "chrome/browser/tab/web_contents_state.h"
 #include "components/infobars/core/infobar_manager.h"
@@ -37,7 +38,7 @@
 class WebContents;
 }
 
-class TabAndroid {
+class TabAndroid : public base::SupportsUserData {
  public:
   // A Java counterpart will be generated for this enum.
   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser
@@ -61,7 +62,7 @@
   static void AttachTabHelpers(content::WebContents* web_contents);
 
   TabAndroid(JNIEnv* env, const base::android::JavaRef<jobject>& obj);
-  ~TabAndroid();
+  ~TabAndroid() override;
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
 
diff --git a/chrome/browser/android/tab_android_user_data.h b/chrome/browser/android/tab_android_user_data.h
new file mode 100644
index 0000000..bb560a7
--- /dev/null
+++ b/chrome/browser/android/tab_android_user_data.h
@@ -0,0 +1,61 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_TAB_ANDROID_USER_DATA_H_
+#define CHROME_BROWSER_ANDROID_TAB_ANDROID_USER_DATA_H_
+
+#include "base/macros.h"
+#include "base/supports_user_data.h"
+#include "chrome/browser/android/tab_android.h"
+
+// A base class for classes attached to, and scoped to, the lifetime of a
+// TabAndroid. For example:
+//
+// --- in foo_tab_helper.h ---
+// class FooTabHelper : public web::TabAndroidUserData<FooTabHelper> {
+//  public:
+//   ~FooTabHelper() override;
+//   // ... more public stuff here ...
+//  private:
+//   explicit FooTabHelper(web::TabAndroid* tab);
+//   friend class web::TabAndroidUserData<FooTabHelper>;
+//   TAB_ANDROID_USER_DATA_KEY_DECL();
+//   // ... more private stuff here ...
+// };
+//
+// --- in foo_tab_helper.cc ---
+// TAB_ANDROID_USER_DATA_KEY_IMPL(FooTabHelper)
+template <typename T>
+class TabAndroidUserData : public base::SupportsUserData::Data {
+ public:
+  // Creates an object of type T, and attaches it to the specified TabAndroid.
+  // If an instance is already attached, does nothing.
+  static void CreateForTabAndroid(TabAndroid* tab) {
+    DCHECK(tab);
+    if (!FromTabAndroid(tab))
+      tab->SetUserData(UserDataKey(), base::WrapUnique(new T(tab)));
+  }
+
+  // Retrieves the instance of type T that was attached to the specified
+  // TabAndroid (via CreateForTabAndroid above) and returns it. If no instance
+  // of the type was attached, returns nullptr.
+  static T* FromTabAndroid(TabAndroid* tab) {
+    DCHECK(tab);
+    return static_cast<T*>(tab->GetUserData(UserDataKey()));
+  }
+
+  static const void* UserDataKey() { return &T::kUserDataKey; }
+};
+
+// This macro declares a static variable inside the class that inherits from
+// TabAndroidUserData The address of this static variable is used as the key to
+// store/retrieve an instance of the class on/from a TabAndroid.
+#define TAB_ANDROID_USER_DATA_KEY_DECL() static constexpr int kUserDataKey = 0
+
+// This macro instantiates the static variable declared by the previous macro.
+// It must live in a .cc file to ensure that there is only one instantiation
+// of the static variable.
+#define TAB_ANDROID_USER_DATA_KEY_IMPL(Type) const int Type::kUserDataKey;
+
+#endif  // CHROME_BROWSER_ANDROID_TAB_ANDROID_USER_DATA_H_
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 94dea2b5..e9b31cd 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -58,6 +58,7 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/android/tab_android_user_data.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #else
@@ -69,7 +70,37 @@
 
 namespace {
 
-#if !defined(OS_ANDROID)
+#if defined(OS_ANDROID)
+class AutocompleteClientTabAndroidUserData
+    : public TabAndroidUserData<AutocompleteClientTabAndroidUserData> {
+ public:
+  ~AutocompleteClientTabAndroidUserData() override = default;
+
+  const GURL& GetStrippedURL() { return stripped_url_; }
+
+  bool IsInitialized() { return initialized_; }
+
+  void UpdateStrippedURL(const GURL& url,
+                         TemplateURLService* template_url_service) {
+    initialized_ = true;
+    if (url.is_valid()) {
+      stripped_url_ = AutocompleteMatch::GURLToStrippedGURL(
+          url, AutocompleteInput(), template_url_service, base::string16());
+    }
+  }
+
+ private:
+  explicit AutocompleteClientTabAndroidUserData(TabAndroid* tab) {}
+  friend class TabAndroidUserData<AutocompleteClientTabAndroidUserData>;
+
+  bool initialized_ = false;
+  GURL stripped_url_;
+
+  TAB_ANDROID_USER_DATA_KEY_DECL();
+};
+TAB_ANDROID_USER_DATA_KEY_IMPL(AutocompleteClientTabAndroidUserData)
+
+#else  // defined(OS_ANDROID)
 // This list should be kept in sync with chrome/common/webui_url_constants.h.
 // Only include useful sub-pages, confirmation alerts are not useful.
 const char* const kChromeSettingsSubPages[] = {
@@ -83,7 +114,47 @@
     chrome::kManageProfileSubPage,    chrome::kPeopleSubPage,
 #endif
 };
-#endif  // !defined(OS_ANDROID)
+#endif  // defined(OS_ANDROID)
+
+class AutocompleteClientWebContentsUserData
+    : public content::WebContentsUserData<
+          AutocompleteClientWebContentsUserData> {
+ public:
+  ~AutocompleteClientWebContentsUserData() override = default;
+
+  int GetLastCommittedEntryIndex() { return last_committed_entry_index_; }
+  const GURL& GetLastCommittedStrippedURL() {
+    return last_committed_stripped_url_;
+  }
+  void UpdateLastCommittedStrippedURL(
+      int last_committed_index,
+      const GURL& last_committed_url,
+      TemplateURLService* template_url_service) {
+    if (last_committed_url.is_valid()) {
+      last_committed_entry_index_ = last_committed_index;
+      // Use blank input since we will re-use this stripped URL with other
+      // inputs.
+      last_committed_stripped_url_ = AutocompleteMatch::GURLToStrippedGURL(
+          last_committed_url, AutocompleteInput(), template_url_service,
+          base::string16());
+    }
+  }
+
+ private:
+  explicit AutocompleteClientWebContentsUserData(
+      content::WebContents* contents);
+  friend class content::WebContentsUserData<
+      AutocompleteClientWebContentsUserData>;
+
+  int last_committed_entry_index_ = -1;
+  GURL last_committed_stripped_url_;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+AutocompleteClientWebContentsUserData::AutocompleteClientWebContentsUserData(
+    content::WebContents*) {}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(AutocompleteClientWebContentsUserData)
 
 }  // namespace
 
@@ -392,47 +463,6 @@
              url2, *input, template_url_service, base::string16());
 }
 
-class AutocompleteClientWebContentsUserData
-    : public content::WebContentsUserData<
-          AutocompleteClientWebContentsUserData> {
- public:
-  ~AutocompleteClientWebContentsUserData() override = default;
-
-  int GetLastCommittedEntryIndex() { return last_committed_entry_index_; }
-  const GURL& GetLastCommittedStrippedURL() {
-    return last_committed_stripped_url_;
-  }
-  void UpdateLastCommittedStrippedURL(
-      int last_committed_index,
-      const GURL& last_committed_url,
-      TemplateURLService* template_url_service) {
-    if (last_committed_url.is_valid()) {
-      last_committed_entry_index_ = last_committed_index;
-      // Use blank input since we will re-use this stripped URL with other
-      // inputs.
-      last_committed_stripped_url_ = AutocompleteMatch::GURLToStrippedGURL(
-          last_committed_url, AutocompleteInput(), template_url_service,
-          base::string16());
-    }
-  }
-
- private:
-  explicit AutocompleteClientWebContentsUserData(
-      content::WebContents* contents);
-  friend class content::WebContentsUserData<
-      AutocompleteClientWebContentsUserData>;
-
-  int last_committed_entry_index_ = -1;
-  GURL last_committed_stripped_url_;
-  WEB_CONTENTS_USER_DATA_KEY_DECL();
-};
-
-AutocompleteClientWebContentsUserData::AutocompleteClientWebContentsUserData(
-    content::WebContents*)
-    : content::WebContentsUserData<AutocompleteClientWebContentsUserData>() {}
-
-WEB_CONTENTS_USER_DATA_KEY_IMPL(AutocompleteClientWebContentsUserData)
-
 bool ChromeAutocompleteProviderClient::IsStrippedURLEqualToWebContentsURL(
     const GURL& stripped_url,
     content::WebContents* web_contents) {
@@ -475,11 +505,16 @@
       } else {
         // Browser did not load the tab yet after Chrome started. To avoid
         // reloading WebContents, we just compare URLs.
-        // TODO(1094056) : Let's TabAndroid to support base::SupportsUserData,
-        // so we can avoid create new url over and over again.
-        const GURL tab_stripped_url = AutocompleteMatch::GURLToStrippedGURL(
-            tab->GetURL(), AutocompleteInput(), GetTemplateURLService(),
-            base::string16());
+        // TODO(crbug.com/1138729): Delete user data after WebContents is
+        // initialized.
+        AutocompleteClientTabAndroidUserData::CreateForTabAndroid(tab);
+        AutocompleteClientTabAndroidUserData* user_data =
+            AutocompleteClientTabAndroidUserData::FromTabAndroid(tab);
+        DCHECK(user_data);
+        if (!user_data->IsInitialized())
+          user_data->UpdateStrippedURL(tab->GetURL(), GetTemplateURLService());
+
+        const GURL tab_stripped_url = user_data->GetStrippedURL();
         if (tab_stripped_url == stripped_url)
           return tab;
       }