[Side Search] Window and Tab state restoration logic.

Adds fields to the TabRestoreService's Window and Tab structs to support
storing and restoring optional data that might be present only on
certain platforms. The `extra_data` fields leverage the base::Value type
that can be suitable for different types of data.

Proposal document:
https://docs.google.com/document/d/1vnCNPDGY5MQNND0_q4HxrsGI9SvX47q8S2Bmkv05-Vo/edit?usp=sharing

Bug: 1262614
Change-Id: I52b1423f480c8adcaab6f44ea1c144a9d3a61625
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3242842
Reviewed-by: Rohit Rao <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Thomas Lukaszewicz <[email protected]>
Commit-Queue: Roman Arora <[email protected]>
Cr-Commit-Position: refs/heads/main@{#941003}
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
index d418bc41..8dbf6003 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/values.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
@@ -63,9 +64,14 @@
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
 
-bool AndroidLiveTabContext::IsTabPinned(int index) const {
-  // Not applicable to android.
-  return false;
+std::map<std::string, base::Value> AndroidLiveTabContext::GetExtraDataForTab(
+    int index) const {
+  return std::map<std::string, base::Value>();
+}
+
+std::map<std::string, base::Value>
+AndroidLiveTabContext::GetExtraDataForWindow() const {
+  return std::map<std::string, base::Value>();
 }
 
 absl::optional<tab_groups::TabGroupId> AndroidLiveTabContext::GetTabGroupForTab(
@@ -83,6 +89,11 @@
   return nullptr;
 }
 
+bool AndroidLiveTabContext::IsTabPinned(int index) const {
+  // Not applicable to android.
+  return false;
+}
+
 void AndroidLiveTabContext::SetVisualDataForGroup(
     const tab_groups::TabGroupId& group,
     const tab_groups::TabGroupVisualData& group_visual_data) {
@@ -120,6 +131,7 @@
     bool pin,
     const sessions::PlatformSpecificTabData* tab_platform_data,
     const sessions::SerializedUserAgentOverride& user_agent_override,
+    const std::map<std::string, base::Value>& extra_data,
     const SessionID* tab_id) {
   Profile* profile = tab_model_->GetProfile();
 
@@ -149,7 +161,8 @@
     int selected_navigation,
     const std::string& extension_app_id,
     const sessions::PlatformSpecificTabData* tab_platform_data,
-    const sessions::SerializedUserAgentOverride& user_agent_override) {
+    const sessions::SerializedUserAgentOverride& user_agent_override,
+    const std::map<std::string, base::Value>& extra_data) {
   NOTIMPLEMENTED();
   return nullptr;
 }
diff --git a/chrome/browser/ui/android/tab_model/android_live_tab_context.h b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
index 24feb5f..0562d88 100644
--- a/chrome/browser/ui/android/tab_model/android_live_tab_context.h
+++ b/chrome/browser/ui/android/tab_model/android_live_tab_context.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_ANDROID_TAB_MODEL_ANDROID_LIVE_TAB_CONTEXT_H_
 #define CHROME_BROWSER_UI_ANDROID_TAB_MODEL_ANDROID_LIVE_TAB_CONTEXT_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -13,6 +14,10 @@
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
 
+namespace base {
+class Value;
+}
+
 namespace content {
 class WebContents;
 }
@@ -38,11 +43,14 @@
   std::string GetUserTitle() const override;
   sessions::LiveTab* GetLiveTabAt(int index) const override;
   sessions::LiveTab* GetActiveLiveTab() const override;
-  bool IsTabPinned(int index) const override;
+  std::map<std::string, base::Value> GetExtraDataForTab(
+      int index) const override;
+  std::map<std::string, base::Value> GetExtraDataForWindow() const override;
   absl::optional<tab_groups::TabGroupId> GetTabGroupForTab(
       int index) const override;
   const tab_groups::TabGroupVisualData* GetVisualDataForGroup(
       const tab_groups::TabGroupId& group) const override;
+  bool IsTabPinned(int index) const override;
   void SetVisualDataForGroup(
       const tab_groups::TabGroupId& group,
       const tab_groups::TabGroupVisualData& visual_data) override;
@@ -60,6 +68,7 @@
       bool pin,
       const sessions::PlatformSpecificTabData* storage_namespace,
       const sessions::SerializedUserAgentOverride& user_agent_override,
+      const std::map<std::string, base::Value>& extra_data,
       const SessionID* tab_id) override;
   sessions::LiveTab* ReplaceRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
@@ -67,8 +76,8 @@
       int selected_navigation,
       const std::string& extension_app_id,
       const sessions::PlatformSpecificTabData* tab_platform_data,
-      const sessions::SerializedUserAgentOverride& user_agent_override)
-      override;
+      const sessions::SerializedUserAgentOverride& user_agent_override,
+      const std::map<std::string, base::Value>& extra_data) override;
   void CloseTab() override;
 
   static LiveTabContext* FindContextForWebContents(
diff --git a/chrome/browser/ui/browser_live_tab_context.cc b/chrome/browser/ui/browser_live_tab_context.cc
index 150ee51..fa8fefb7 100644
--- a/chrome/browser/ui/browser_live_tab_context.cc
+++ b/chrome/browser/ui/browser_live_tab_context.cc
@@ -9,6 +9,7 @@
 
 #include "base/feature_list.h"
 #include "base/token.h"
+#include "base/values.h"
 #include "chrome/browser/apps/app_service/web_contents_app_id_utils.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/profiles/profile.h"
@@ -25,6 +26,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/common/buildflags.h"
 #include "components/sessions/content/content_live_tab.h"
 #include "components/sessions/content/content_platform_specific_tab_data.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -36,6 +38,10 @@
 #include "chrome/browser/sessions/tab_loader.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+#include "chrome/browser/ui/side_search/side_search_utils.h"
+#endif
+
 using content::NavigationController;
 using content::SessionStorageNamespace;
 using content::WebContents;
@@ -96,8 +102,32 @@
       browser_->tab_strip_model()->GetActiveWebContents());
 }
 
-bool BrowserLiveTabContext::IsTabPinned(int index) const {
-  return browser_->tab_strip_model()->IsTabPinned(index);
+std::map<std::string, base::Value> BrowserLiveTabContext::GetExtraDataForTab(
+    int index) const {
+  std::map<std::string, base::Value> extra_data;
+
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  if (IsSideSearchEnabled(browser_->profile())) {
+    side_search::MaybeAddSideSearchTabRestoreData(
+        browser_->tab_strip_model()->GetWebContentsAt(index), extra_data);
+  }
+#endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
+
+  return extra_data;
+}
+
+std::map<std::string, base::Value>
+BrowserLiveTabContext::GetExtraDataForWindow() const {
+  std::map<std::string, base::Value> extra_data;
+
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  if (IsSideSearchEnabled(browser_->profile())) {
+    side_search::MaybeAddSideSearchWindowRestoreData(
+        browser_->window()->IsSideSearchPanelVisible(), extra_data);
+  }
+#endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
+
+  return extra_data;
 }
 
 absl::optional<tab_groups::TabGroupId> BrowserLiveTabContext::GetTabGroupForTab(
@@ -114,6 +144,10 @@
       ->visual_data();
 }
 
+bool BrowserLiveTabContext::IsTabPinned(int index) const {
+  return browser_->tab_strip_model()->IsTabPinned(index);
+}
+
 void BrowserLiveTabContext::SetVisualDataForGroup(
     const tab_groups::TabGroupId& group,
     const tab_groups::TabGroupVisualData& visual_data) {
@@ -144,6 +178,7 @@
     bool pin,
     const sessions::PlatformSpecificTabData* tab_platform_data,
     const sessions::SerializedUserAgentOverride& user_agent_override,
+    const std::map<std::string, base::Value>& extra_data,
     const SessionID* tab_id) {
   SessionStorageNamespace* storage_namespace =
       tab_platform_data
@@ -212,6 +247,10 @@
 #endif  // BUILDFLAG(ENABLE_SESSION_SERVICE)
   }
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  side_search::SetSideSearchStateFromRestoreData(web_contents, extra_data);
+#endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
+
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
 
@@ -221,7 +260,8 @@
     int selected_navigation,
     const std::string& extension_app_id,
     const sessions::PlatformSpecificTabData* tab_platform_data,
-    const sessions::SerializedUserAgentOverride& user_agent_override) {
+    const sessions::SerializedUserAgentOverride& user_agent_override,
+    const std::map<std::string, base::Value>& extra_data) {
   SessionStorageNamespace* storage_namespace =
       tab_platform_data
           ? static_cast<const sessions::ContentPlatformSpecificTabData*>(
@@ -233,6 +273,10 @@
       browser_, navigations, selected_navigation, extension_app_id,
       storage_namespace, user_agent_override, false /* from_session_restore */);
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  side_search::SetSideSearchStateFromRestoreData(web_contents, extra_data);
+#endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
+
   return sessions::ContentLiveTab::GetForWebContents(web_contents);
 }
 
@@ -247,7 +291,8 @@
     const gfx::Rect& bounds,
     ui::WindowShowState show_state,
     const std::string& workspace,
-    const std::string& user_title) {
+    const std::string& user_title,
+    const std::map<std::string, base::Value>& extra_data) {
   std::unique_ptr<Browser::CreateParams> create_params;
   if (ShouldCreateAppWindowForAppName(profile, app_name)) {
     // Only trusted app popup windows should ever be restored.
@@ -265,6 +310,11 @@
   create_params->initial_workspace = workspace;
   create_params->user_title = user_title;
   Browser* browser = Browser::Create(*create_params.get());
+
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  browser->window()->MaybeRestoreSideSearchStatePerWindow(extra_data);
+#endif  // BUILDFLAG(ENABLE_SIDE_SEARCH)
+
   return browser->live_tab_context();
 }
 
diff --git a/chrome/browser/ui/browser_live_tab_context.h b/chrome/browser/ui/browser_live_tab_context.h
index f1b4a51..80b77a37 100644
--- a/chrome/browser/ui/browser_live_tab_context.h
+++ b/chrome/browser/ui/browser_live_tab_context.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_BROWSER_LIVE_TAB_CONTEXT_H_
 #define CHROME_BROWSER_UI_BROWSER_LIVE_TAB_CONTEXT_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -17,6 +18,10 @@
 class Browser;
 class Profile;
 
+namespace base {
+class Value;
+}
+
 namespace content {
 class WebContents;
 }
@@ -45,18 +50,20 @@
   std::string GetUserTitle() const override;
   sessions::LiveTab* GetLiveTabAt(int index) const override;
   sessions::LiveTab* GetActiveLiveTab() const override;
-  bool IsTabPinned(int index) const override;
+  std::map<std::string, base::Value> GetExtraDataForTab(
+      int index) const override;
+  std::map<std::string, base::Value> GetExtraDataForWindow() const override;
   absl::optional<tab_groups::TabGroupId> GetTabGroupForTab(
       int index) const override;
   const tab_groups::TabGroupVisualData* GetVisualDataForGroup(
       const tab_groups::TabGroupId& group) const override;
+  bool IsTabPinned(int index) const override;
   void SetVisualDataForGroup(
       const tab_groups::TabGroupId& group,
       const tab_groups::TabGroupVisualData& visual_data) override;
   const gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
   std::string GetWorkspace() const override;
-
   sessions::LiveTab* AddRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
       int tab_index,
@@ -68,6 +75,7 @@
       bool pin,
       const sessions::PlatformSpecificTabData* storage_namespace,
       const sessions::SerializedUserAgentOverride& user_agent_override,
+      const std::map<std::string, base::Value>& extra_data,
       const SessionID* tab_id) override;
   sessions::LiveTab* ReplaceRestoredTab(
       const std::vector<sessions::SerializedNavigationEntry>& navigations,
@@ -75,17 +83,19 @@
       int selected_navigation,
       const std::string& extension_app_id,
       const sessions::PlatformSpecificTabData* tab_platform_data,
-      const sessions::SerializedUserAgentOverride& user_agent_override)
-      override;
+      const sessions::SerializedUserAgentOverride& user_agent_override,
+      const std::map<std::string, base::Value>& extra_data) override;
   void CloseTab() override;
 
   // see Browser::Create
-  static sessions::LiveTabContext* Create(Profile* profile,
-                                          const std::string& app_name,
-                                          const gfx::Rect& bounds,
-                                          ui::WindowShowState show_state,
-                                          const std::string& workspace,
-                                          const std::string& user_title);
+  static sessions::LiveTabContext* Create(
+      Profile* profile,
+      const std::string& app_name,
+      const gfx::Rect& bounds,
+      ui::WindowShowState show_state,
+      const std::string& workspace,
+      const std::string& user_title,
+      const std::map<std::string, base::Value>& extra_data);
 
   // see browser::FindBrowserForWebContents
   static sessions::LiveTabContext* FindContextForWebContents(
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 14673581..5c7e1ff 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -557,6 +557,12 @@
   // Shows an Incognito history disclaimer dialog.
   virtual void ShowIncognitoHistoryDisclaimerDialog() = 0;
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  virtual bool IsSideSearchPanelVisible() const = 0;
+  virtual void MaybeRestoreSideSearchStatePerWindow(
+      const std::map<std::string, base::Value>& extra_data) = 0;
+#endif
+
  protected:
   friend class BrowserCloseManager;
   friend class BrowserView;
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper.h b/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
index 0246591..b1d6fe6 100644
--- a/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
+++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper.h
@@ -45,6 +45,8 @@
     // delegates that they should close the feature when something exceptional
     // has happened.
     virtual void SidePanelAvailabilityChanged(bool should_close) = 0;
+
+    virtual void OpenSidePanel() = 0;
   };
 
   ~SideSearchTabContentsHelper() override;
@@ -91,8 +93,9 @@
     return side_panel_contents_.get();
   }
 
-  const absl::optional<GURL>& last_search_url_for_testing() {
-    return last_search_url_;
+  const absl::optional<GURL>& last_search_url() { return last_search_url_; }
+  void set_last_search_url(GURL url) {
+    last_search_url_ = absl::optional<GURL>({url});
   }
 
  private:
diff --git a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
index 4433e06b..a861c46 100644
--- a/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
+++ b/chrome/browser/ui/side_search/side_search_tab_contents_helper_unittest.cc
@@ -110,81 +110,71 @@
 
 TEST_F(SideSearchTabContentsHelperTest, LastSearchURLUpdatesCorrectly) {
   // When a tab is first opened there should be no last encountered Google SRP.
-  EXPECT_FALSE(helper()->last_search_url_for_testing().has_value());
+  EXPECT_FALSE(helper()->last_search_url().has_value());
   EXPECT_EQ(nullptr, GetLastCommittedSideContentsEntry());
 
   // Navigating to a Google SRP should update the `last_search_url`.
   LoadURL(kGoogleSearchURL1);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Navigating to a non-Google SRP URL should not change the `last_search_url`.
   LoadURL(kNonGoogleURL);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Navigating again to a Google SRP should update the `last_search_url`.
   LoadURL(kGoogleSearchURL2);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Going backwards to the non-Google SRP URL should not update the last search
   // url.
   GoBack();
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Going back to the original Google SRP should update the `last_search_url`
   // to that Google SRP URL.
   GoBack();
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Going forward to the non-Google URL shouldn't change the `last_search_url`.
   GoForward();
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Going forward to the Google SRP URL should update the `last_search_url` to
   // that Google SRP URL.
   GoForward();
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 }
 
 TEST_F(SideSearchTabContentsHelperTest, LastSearchURLIgnoresGoogleHomePage) {
-  EXPECT_FALSE(helper()->last_search_url_for_testing().has_value());
+  EXPECT_FALSE(helper()->last_search_url().has_value());
 
   LoadURL(kGoogleSearchURL1);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   // Navigating to the Google home page should not update the `last_search_url`.
   LoadURL(kGoogleSearchHomePageURL);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL1,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 
   LoadURL(kGoogleSearchURL2);
-  EXPECT_TRUE(
-      DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url_for_testing()));
+  EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2, helper()->last_search_url()));
   EXPECT_TRUE(DoesURLMatch(kGoogleSearchURL2,
                            GetLastCommittedSideContentsEntry()->GetURL()));
 }
diff --git a/chrome/browser/ui/side_search/side_search_utils.cc b/chrome/browser/ui/side_search/side_search_utils.cc
index fc252f6..b1db2096 100644
--- a/chrome/browser/ui/side_search/side_search_utils.cc
+++ b/chrome/browser/ui/side_search/side_search_utils.cc
@@ -4,10 +4,85 @@
 
 #include "chrome/browser/ui/side_search/side_search_utils.h"
 
+#include "base/containers/contains.h"
+#include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/side_search/side_search_prefs.h"
+#include "chrome/browser/ui/side_search/side_search_tab_contents_helper.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/browser/web_contents.h"
+
+namespace side_search {
+
+const char kSideSearchExtraDataKey[] = "side_search";
+const char kLastSearchUrl[] = "last_search_url";
+const char kToggledOpen[] = "toggled_open";
+
+void MaybeAddSideSearchTabRestoreData(
+    content::WebContents* web_contents,
+    std::map<std::string, base::Value>& extra_data) {
+  SideSearchTabContentsHelper* helper =
+      SideSearchTabContentsHelper::FromWebContents(web_contents);
+  if (helper && helper->last_search_url().has_value()) {
+    base::Value side_search_tab_data(base::Value::Type::DICTIONARY);
+    side_search_tab_data.SetStringKey(kLastSearchUrl,
+                                      helper->last_search_url().value().spec());
+    side_search_tab_data.SetBoolKey(kToggledOpen, helper->toggled_open());
+
+    extra_data[kSideSearchExtraDataKey] = std::move(side_search_tab_data);
+  }
+}
+
+void MaybeAddSideSearchWindowRestoreData(
+    bool toggled_open,
+    std::map<std::string, base::Value>& extra_data) {
+  if (base::FeatureList::IsEnabled(features::kSideSearchStatePerTab))
+    return;
+
+  base::Value side_search_window_data(base::Value::Type::DICTIONARY);
+  side_search_window_data.SetBoolKey(kToggledOpen, toggled_open);
+
+  extra_data[kSideSearchExtraDataKey] = std::move(side_search_window_data);
+}
+
+void MaybeRestoreSideSearchWindowState(
+    SideSearchTabContentsHelper::Delegate* delegate,
+    const std::map<std::string, base::Value>& extra_data) {
+  if (base::FeatureList::IsEnabled(features::kSideSearchStatePerTab))
+    return;
+
+  if (base::Contains(extra_data, kSideSearchExtraDataKey)) {
+    absl::optional<bool> toggled_open =
+        extra_data.at(kSideSearchExtraDataKey).FindBoolKey(kToggledOpen);
+
+    if (toggled_open.has_value() && toggled_open.value())
+      delegate->OpenSidePanel();
+  }
+}
+
+void SetSideSearchStateFromRestoreData(
+    content::WebContents* web_contents,
+    const std::map<std::string, base::Value>& extra_data) {
+  if (base::Contains(extra_data, kSideSearchExtraDataKey)) {
+    auto* side_search_tab_contents_helper =
+        SideSearchTabContentsHelper::FromWebContents(web_contents);
+
+    const std::string* last_search_url_str =
+        extra_data.at(kSideSearchExtraDataKey).FindStringKey(kLastSearchUrl);
+    if (last_search_url_str)
+      side_search_tab_contents_helper->set_last_search_url(
+          GURL(*last_search_url_str));
+
+    absl::optional<bool> toggled_open =
+        extra_data.at(kSideSearchExtraDataKey).FindBoolKey(kToggledOpen);
+    if (toggled_open.has_value()) {
+      side_search_tab_contents_helper->set_toggled_open(toggled_open.value());
+    }
+  }
+}
+
+}  // namespace side_search
 
 bool IsSideSearchEnabled(const Profile* profile) {
   return !profile->IsOffTheRecord() &&
diff --git a/chrome/browser/ui/side_search/side_search_utils.h b/chrome/browser/ui/side_search/side_search_utils.h
index 9553633d..54ba742 100644
--- a/chrome/browser/ui/side_search/side_search_utils.h
+++ b/chrome/browser/ui/side_search/side_search_utils.h
@@ -5,8 +5,45 @@
 #ifndef CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_UTILS_H_
 #define CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_UTILS_H_
 
+#include <map>
+#include <tuple>
+
+#include "chrome/browser/ui/side_search/side_search_tab_contents_helper.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
 class Profile;
 
+namespace base {
+class Value;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace side_search {
+
+// Adds side search state data to a tab's state restore data if applicable.
+void MaybeAddSideSearchTabRestoreData(
+    content::WebContents* web_contents,
+    std::map<std::string, base::Value>& extra_data);
+
+// Add side search state data for a window's state restore data if applicable.
+void MaybeAddSideSearchWindowRestoreData(
+    bool toggled_open,
+    std::map<std::string, base::Value>& extra_data);
+
+void MaybeRestoreSideSearchWindowState(
+    SideSearchTabContentsHelper::Delegate* delegate,
+    const std::map<std::string, base::Value>& extra_data);
+
+void SetSideSearchStateFromRestoreData(
+    content::WebContents* web_contents,
+    const std::map<std::string, base::Value>& extra_data);
+
+}  // namespace side_search
+
 bool IsSideSearchEnabled(const Profile* profile);
 
 #endif  // CHROME_BROWSER_UI_SIDE_SEARCH_SIDE_SEARCH_UTILS_H_
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index c5f39fd..c84082f5 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -3081,6 +3081,23 @@
     tab_search_host->CloseTabSearchBubble();
 }
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+bool BrowserView::IsSideSearchPanelVisible() const {
+  if (side_search_controller_)
+    return side_search_controller_->GetSidePanelToggledOpen();
+
+  return false;
+}
+
+void BrowserView::MaybeRestoreSideSearchStatePerWindow(
+    const std::map<std::string, base::Value>& extra_data) {
+  if (side_search_controller_) {
+    side_search::MaybeRestoreSideSearchWindowState(
+        side_search_controller_.get(), extra_data);
+  }
+}
+#endif
+
 void BrowserView::RevealTabStripIfNeeded() {
   if (!immersive_mode_controller_->IsEnabled())
     return;
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 9c99d9de..20bf11d 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -698,6 +698,12 @@
     return accessibility_focus_highlight_.get();
   }
 
+#if BUILDFLAG(ENABLE_SIDE_SEARCH)
+  bool IsSideSearchPanelVisible() const override;
+  void MaybeRestoreSideSearchStatePerWindow(
+      const std::map<std::string, base::Value>& extra_data) override;
+#endif
+
  private:
   // Do not friend BrowserViewLayout. Use the BrowserViewLayoutDelegate
   // interface to keep these two classes decoupled and testable.
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.h b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
index f7ada88..774762e 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.h
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.h
@@ -49,6 +49,7 @@
       content::WebContents* source,
       const content::OpenURLParams& params) override;
   void SidePanelAvailabilityChanged(bool should_close) override;
+  void OpenSidePanel() override;
 
   // content::WebContentsObserver:
   void DidFinishNavigation(
@@ -64,11 +65,12 @@
 
   views::WebView* web_view_for_testing() { return web_view_; }
 
+  bool GetSidePanelToggledOpen() const;
+
  private:
   // Gets and sets the toggled state of the side panel. If called with
   // kSideSearchStatePerTab enabled this determines whether the side panel
   // should be open for the currently active tab.
-  bool GetSidePanelToggledOpen() const;
   void SetSidePanelToggledOpen(bool toggled_open);
 
   // Toggles panel visibility on side panel toolbar button press.
@@ -77,8 +79,6 @@
   // Closes side panel on close button press.
   void SidePanelCloseButtonPressed();
 
-  void OpenSidePanel();
-
   void CloseSidePanel(
       absl::optional<SideSearchCloseActionType> action = absl::nullopt);