[M87 merge] weblayer: adds API to know if Browser is restoring and when done

This way embedders have API to know when they can take action
based on whether restore has completed.

BUG=1135278
TEST=covered by tests

(cherry picked from commit 3692e6bb068eed1929257e170110f39fd03a774c)

Change-Id: I72d804a86d8c4eeafdc3b72bbcf16875a78eb477
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2451550
Commit-Queue: Scott Violet <[email protected]>
Reviewed-by: Clark DuVall <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#814450}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454639
Reviewed-by: Scott Violet <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#78}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index 370eaff..09224152d 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -310,6 +310,7 @@
     "public/browser.cc",
     "public/browser.h",
     "public/browser_observer.h",
+    "public/browser_restore_observer.h",
     "public/common/switches.cc",
     "public/common/switches.h",
     "public/cookie_manager.h",
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
index 2cfb648..85bd6968 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/BrowserFragmentLifecycleTest.java
@@ -18,11 +18,14 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.Browser;
+import org.chromium.weblayer.BrowserRestoreCallback;
 import org.chromium.weblayer.Navigation;
 import org.chromium.weblayer.NavigationCallback;
 import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.Tab;
+import org.chromium.weblayer.WebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
 import java.util.HashMap;
@@ -44,6 +47,16 @@
                 () -> mActivityTestRule.getActivity().getTab());
     }
 
+    private boolean isRestoringPreviousState() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> mActivityTestRule.getActivity().getBrowser().isRestoringPreviousState());
+    }
+
+    private int getSupportedMajorVersion() {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(
+                () -> WebLayer.getSupportedMajorVersion(mActivityTestRule.getActivity()));
+    }
+
     @Test
     @SmallTest
     public void successfullyLoadsUrlAfterRecreation() {
@@ -126,12 +139,18 @@
         extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, "x");
         final String url = mActivityTestRule.getTestDataURL("simple_page.html");
         mActivityTestRule.launchShellWithUrl(url, extras);
+        if (getSupportedMajorVersion() >= 88) {
+            Assert.assertFalse(isRestoringPreviousState());
+        }
 
         mActivityTestRule.recreateActivity();
 
         Tab tab = getTab();
         Assert.assertNotNull(tab);
         waitForTabToFinishRestore(tab, url);
+        if (getSupportedMajorVersion() >= 88) {
+            Assert.assertFalse(isRestoringPreviousState());
+        }
     }
 
     @Test
@@ -200,7 +219,7 @@
 
         Map<String, String> initialData = new HashMap<>();
         initialData.put("foo", "bar");
-        restoresTabData(extras, initialData);
+        restoreTabData(extras, initialData);
     }
 
     @Test
@@ -209,10 +228,10 @@
     public void restoreTabDataAfterRecreate() throws Throwable {
         Map<String, String> initialData = new HashMap<>();
         initialData.put("foo", "bar");
-        restoresTabData(new Bundle(), initialData);
+        restoreTabData(new Bundle(), initialData);
     }
 
-    private void restoresTabData(Bundle extras, Map<String, String> initialData) {
+    private void restoreTabData(Bundle extras, Map<String, String> initialData) {
         String url = mActivityTestRule.getTestDataURL("simple_page.html");
         mActivityTestRule.launchShellWithUrl(url, extras);
 
@@ -282,4 +301,46 @@
         });
         helper.waitForCallback(callCount, 1);
     }
+
+    @Test
+    @SmallTest
+    @MinWebLayerVersion(88)
+    public void restoreUsingOnRestoreCompleted() throws Throwable {
+        final String persistenceId = "x";
+        Bundle extras = new Bundle();
+        extras.putString(InstrumentationActivity.EXTRA_PERSISTENCE_ID, persistenceId);
+        CallbackHelper callbackHelper = new CallbackHelper();
+        InstrumentationActivity.registerOnCreatedCallback(
+                new InstrumentationActivity.OnCreatedCallback() {
+                    private int mBrowserCreateCount;
+                    @Override
+                    public void onCreated(Browser browser) {
+                        if (mBrowserCreateCount == 0) {
+                            // Initial creation.
+                            mBrowserCreateCount = 1;
+                            // isRestoringPreviousState() is true for the initial creation as
+                            // persistence code has to check disk, which is async.
+                            Assert.assertTrue(browser.isRestoringPreviousState());
+                        } else if (mBrowserCreateCount == 1) {
+                            // The activity was recreated.
+                            mBrowserCreateCount = 2;
+                            Assert.assertTrue(browser.isRestoringPreviousState());
+                            browser.registerBrowserRestoreCallback(new BrowserRestoreCallback() {
+                                @Override
+                                public void onRestoreCompleted() {
+                                    Assert.assertFalse(browser.isRestoringPreviousState());
+                                    callbackHelper.notifyCalled();
+                                }
+                            });
+                        } else {
+                            Assert.fail("Unexpected phase");
+                        }
+                    }
+                });
+        final String url = mActivityTestRule.getTestDataURL("simple_page.html");
+        mActivityTestRule.launchShellWithUrl(url, extras);
+
+        mActivityTestRule.recreateActivity();
+        callbackHelper.waitForFirst();
+    }
 }
diff --git a/weblayer/browser/browser_impl.cc b/weblayer/browser/browser_impl.cc
index 4bf9dca0..11cdb81c 100644
--- a/weblayer/browser/browser_impl.cc
+++ b/weblayer/browser/browser_impl.cc
@@ -24,6 +24,7 @@
 #include "weblayer/browser/tab_impl.h"
 #include "weblayer/common/weblayer_paths.h"
 #include "weblayer/public/browser_observer.h"
+#include "weblayer/public/browser_restore_observer.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/callback_android.h"
@@ -329,6 +330,14 @@
   return CreateTab(nullptr);
 }
 
+void BrowserImpl::OnRestoreCompleted() {
+  for (BrowserRestoreObserver& obs : browser_restore_observers_)
+    obs.OnRestoreCompleted();
+#if defined(OS_ANDROID)
+  Java_BrowserImpl_onRestoreCompleted(AttachCurrentThread(), java_impl_);
+#endif
+}
+
 void BrowserImpl::PrepareForShutdown() {
   browser_persister_.reset();
 }
@@ -342,6 +351,10 @@
   return GetMinimalPersistenceState(0);
 }
 
+bool BrowserImpl::IsRestoringPreviousState() {
+  return browser_persister_ && browser_persister_->is_restore_in_progress();
+}
+
 void BrowserImpl::AddObserver(BrowserObserver* observer) {
   browser_observers_.AddObserver(observer);
 }
@@ -350,6 +363,15 @@
   browser_observers_.RemoveObserver(observer);
 }
 
+void BrowserImpl::AddBrowserRestoreObserver(BrowserRestoreObserver* observer) {
+  browser_restore_observers_.AddObserver(observer);
+}
+
+void BrowserImpl::RemoveBrowserRestoreObserver(
+    BrowserRestoreObserver* observer) {
+  browser_restore_observers_.RemoveObserver(observer);
+}
+
 void BrowserImpl::VisibleSecurityStateOfActiveTabChanged() {
   if (visible_security_state_changed_callback_for_tests_)
     std::move(visible_security_state_changed_callback_for_tests_).Run();
diff --git a/weblayer/browser/browser_impl.h b/weblayer/browser/browser_impl.h
index 00b4b67..b8ed0b6 100644
--- a/weblayer/browser/browser_impl.h
+++ b/weblayer/browser/browser_impl.h
@@ -57,6 +57,9 @@
       const std::string& guid);
   TabImpl* CreateTab(std::unique_ptr<content::WebContents> web_contents);
 
+  // Called from BrowserPersister when restore has completed.
+  void OnRestoreCompleted();
+
 #if defined(OS_ANDROID)
   bool CompositorHasSurface();
 
@@ -83,6 +86,9 @@
   void OnFragmentStart(JNIEnv* env);
   void OnFragmentResume(JNIEnv* env);
   void OnFragmentPause(JNIEnv* env);
+  bool IsRestoringPreviousState(JNIEnv* env) {
+    return IsRestoringPreviousState();
+  }
 
   bool fragment_resumed() { return fragment_resumed_; }
 #endif
@@ -118,8 +124,11 @@
   void PrepareForShutdown() override;
   std::string GetPersistenceId() override;
   std::vector<uint8_t> GetMinimalPersistenceState() override;
+  bool IsRestoringPreviousState() override;
   void AddObserver(BrowserObserver* observer) override;
   void RemoveObserver(BrowserObserver* observer) override;
+  void AddBrowserRestoreObserver(BrowserRestoreObserver* observer) override;
+  void RemoveBrowserRestoreObserver(BrowserRestoreObserver* observer) override;
   void VisibleSecurityStateOfActiveTabChanged() override;
 
  private:
@@ -149,6 +158,7 @@
   base::android::ScopedJavaGlobalRef<jobject> java_impl_;
 #endif
   base::ObserverList<BrowserObserver> browser_observers_;
+  base::ObserverList<BrowserRestoreObserver> browser_restore_observers_;
   ProfileImpl* const profile_;
   std::vector<std::unique_ptr<Tab>> tabs_;
   TabImpl* active_tab_ = nullptr;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
index 4bdd8bac..c357e62 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java
@@ -471,6 +471,16 @@
         return mUrlBarController;
     }
 
+    @Override
+    public boolean isRestoringPreviousState() {
+        return BrowserImplJni.get().isRestoringPreviousState(mNativeBrowser);
+    }
+
+    @CalledByNative
+    private void onRestoreCompleted() throws RemoteException {
+        if (WebLayerFactoryImpl.getClientMajorVersion() >= 87) mClient.onRestoreCompleted();
+    }
+
     public View getFragmentView() {
         return getViewController().getView();
     }
@@ -617,5 +627,6 @@
         void onFragmentStart(long nativeBrowserImpl);
         void onFragmentResume(long nativeBrowserImpl);
         void onFragmentPause(long nativeBrowserImpl);
+        boolean isRestoringPreviousState(long nativeBrowserImpl);
     }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
index 4759677..485e4b45 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl
@@ -40,4 +40,7 @@
   void setTopViewAndScrollingBehavior(in IObjectWrapper view, in int minHeight,
                                       in boolean onlyExpandControlsAtPageTop,
                                       in boolean animate) = 12;
+
+  // Added in 87.
+  boolean isRestoringPreviousState() = 14;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
index e3b08c2..6c7eff7 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl
@@ -14,4 +14,5 @@
 
   // Added in 87.
   IRemoteFragment createMediaRouteDialogFragment() = 3;
+  void onRestoreCompleted() = 5;
 }
diff --git a/weblayer/browser/persistence/browser_persister.cc b/weblayer/browser/persistence/browser_persister.cc
index 61ea0b1d..8e2c342 100644
--- a/weblayer/browser/persistence/browser_persister.cc
+++ b/weblayer/browser/persistence/browser_persister.cc
@@ -277,6 +277,9 @@
   ScheduleRebuildOnNextSave();
 
   RestoreBrowserState(browser_, std::move(commands));
+
+  is_restore_in_progress_ = false;
+  browser_->OnRestoreCompleted();
 }
 
 void BrowserPersister::BuildCommandsForTab(TabImpl* tab, int index_in_browser) {
diff --git a/weblayer/browser/persistence/browser_persister.h b/weblayer/browser/persistence/browser_persister.h
index d4578eea..34183dd 100644
--- a/weblayer/browser/persistence/browser_persister.h
+++ b/weblayer/browser/persistence/browser_persister.h
@@ -53,6 +53,8 @@
 
   ~BrowserPersister() override;
 
+  bool is_restore_in_progress() const { return is_restore_in_progress_; }
+
   void SaveIfNecessary();
 
   // Returns the key used to encrypt the file. Empty if not encrypted.
@@ -139,6 +141,9 @@
                  &TabImpl::RemoveDataObserver>
       data_observer_{this};
 
+  // True while asynchronously reading the state to restore.
+  bool is_restore_in_progress_ = true;
+
   base::WeakPtrFactory<BrowserPersister> weak_factory_{this};
 };
 
diff --git a/weblayer/browser/persistence/browser_persister_browsertest.cc b/weblayer/browser/persistence/browser_persister_browsertest.cc
index a8eeec2..7a7572ca 100644
--- a/weblayer/browser/persistence/browser_persister_browsertest.cc
+++ b/weblayer/browser/persistence/browser_persister_browsertest.cc
@@ -24,6 +24,7 @@
 #include "weblayer/browser/profile_impl.h"
 #include "weblayer/browser/tab_impl.h"
 #include "weblayer/common/weblayer_paths.h"
+#include "weblayer/public/browser_restore_observer.h"
 #include "weblayer/public/navigation.h"
 #include "weblayer/public/navigation_controller.h"
 #include "weblayer/public/navigation_observer.h"
@@ -47,34 +48,12 @@
 namespace {
 using testing::UnorderedElementsAre;
 
-class BrowserObserverImpl : public BrowserObserver {
- public:
-  static void WaitForNewTab(Browser* browser) {
-    BrowserObserverImpl observer(browser);
-    observer.Wait();
-  }
-
- private:
-  explicit BrowserObserverImpl(Browser* browser) : browser_(browser) {
-    browser_->AddObserver(this);
-  }
-  ~BrowserObserverImpl() override { browser_->RemoveObserver(this); }
-
-  void Wait() { run_loop_.Run(); }
-
-  // BrowserObserver:
-  void OnTabAdded(Tab* tab) override { run_loop_.Quit(); }
-
-  Browser* browser_;
-  base::RunLoop run_loop_;
-};
-
-class BrowserNavigationObserverImpl : public BrowserObserver,
+class BrowserNavigationObserverImpl : public BrowserRestoreObserver,
                                       public NavigationObserver {
  public:
   static void WaitForNewTabToCompleteNavigation(Browser* browser,
                                                 const GURL& url,
-                                                int tab_to_wait_for = 1) {
+                                                size_t tab_to_wait_for = 0) {
     BrowserNavigationObserverImpl observer(browser, url, tab_to_wait_for);
     observer.Wait();
   }
@@ -82,9 +61,9 @@
  private:
   BrowserNavigationObserverImpl(Browser* browser,
                                 const GURL& url,
-                                int tab_to_wait_for)
+                                size_t tab_to_wait_for)
       : browser_(browser), url_(url), tab_to_wait_for_(tab_to_wait_for) {
-    browser_->AddObserver(this);
+    browser_->AddBrowserRestoreObserver(this);
   }
   ~BrowserNavigationObserverImpl() override {
     tab_->GetNavigationController()->RemoveObserver(this);
@@ -98,20 +77,19 @@
       run_loop_.Quit();
   }
 
-  // BrowserObserver:
-  void OnTabAdded(Tab* tab) override {
-    if (--tab_to_wait_for_ != 0)
-      return;
-
-    browser_->RemoveObserver(this);
-    tab_ = tab;
+  // BrowserRestoreObserver:
+  void OnRestoreCompleted() override {
+    browser_->RemoveBrowserRestoreObserver(this);
+    ASSERT_LT(tab_to_wait_for_, browser_->GetTabs().size());
+    ASSERT_EQ(nullptr, tab_);
+    tab_ = browser_->GetTabs()[tab_to_wait_for_];
     tab_->GetNavigationController()->AddObserver(this);
   }
 
   Browser* browser_;
   const GURL& url_;
   Tab* tab_ = nullptr;
-  int tab_to_wait_for_;
+  const size_t tab_to_wait_for_;
   std::unique_ptr<TestNavigationObserver> navigation_observer_;
   base::RunLoop run_loop_;
 };
@@ -146,6 +124,7 @@
 
   std::unique_ptr<BrowserImpl> browser = CreateBrowser(GetProfile(), "x");
   Tab* tab = browser->CreateTab();
+  EXPECT_TRUE(browser->IsRestoringPreviousState());
   const GURL url = embedded_test_server()->GetURL("/simple_page.html");
   NavigateAndWaitForCompletion(url, tab);
   ShutdownBrowserPersisterAndWait(browser.get());
@@ -155,6 +134,7 @@
   browser = CreateBrowser(GetProfile(), "x");
   // Should be no tabs while waiting for restore.
   EXPECT_TRUE(browser->GetTabs().empty());
+  EXPECT_TRUE(browser->IsRestoringPreviousState());
   // Wait for the restore and navigation to complete.
   BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
       browser.get(), url);
@@ -164,6 +144,7 @@
   EXPECT_EQ(1, browser->GetTabs()[0]
                    ->GetNavigationController()
                    ->GetNavigationListSize());
+  EXPECT_FALSE(browser->IsRestoringPreviousState());
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, RestoresGuid) {
@@ -276,7 +257,7 @@
     // Wait for the restore and navigation to complete. This waits for the
     // second tab as that was the active one.
     BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
-        browser.get(), url2, 2);
+        browser.get(), url2, 1);
 
     ASSERT_EQ(2u, browser->GetTabs().size()) << "iteration " << i;
     // The first tab shouldn't have loaded yet, as it's not active.
@@ -331,7 +312,7 @@
   // Restore the browsers.
   browser1 = CreateBrowser(GetProfile(), "x");
   BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
-      browser1.get(), url1, 1);
+      browser1.get(), url1);
   ASSERT_EQ(1u, browser1->GetTabs().size());
   EXPECT_EQ(1, browser1->GetTabs()[0]
                    ->GetNavigationController()
@@ -339,7 +320,7 @@
 
   browser2 = CreateBrowser(GetProfile(), "y");
   BrowserNavigationObserverImpl::WaitForNewTabToCompleteNavigation(
-      browser2.get(), url2, 2);
+      browser2.get(), url2, 1);
   ASSERT_EQ(2u, browser2->GetTabs().size());
   EXPECT_EQ(1, browser2->GetTabs()[1]
                    ->GetNavigationController()
diff --git a/weblayer/public/browser.h b/weblayer/public/browser.h
index 8932998..02b1f97e 100644
--- a/weblayer/public/browser.h
+++ b/weblayer/public/browser.h
@@ -14,6 +14,7 @@
 namespace weblayer {
 
 class BrowserObserver;
+class BrowserRestoreObserver;
 class Profile;
 class Tab;
 
@@ -68,9 +69,18 @@
   // lightweight restore when full persistence is not desirable.
   virtual std::vector<uint8_t> GetMinimalPersistenceState() = 0;
 
+  // Returns true if this Browser is in the process of restoring the previous
+  // state. That is, PersistenceInfo was supplied to the constructor and
+  // the state is asynchronously being loaded.
+  virtual bool IsRestoringPreviousState() = 0;
+
   virtual void AddObserver(BrowserObserver* observer) = 0;
   virtual void RemoveObserver(BrowserObserver* observer) = 0;
 
+  virtual void AddBrowserRestoreObserver(BrowserRestoreObserver* observer) = 0;
+  virtual void RemoveBrowserRestoreObserver(
+      BrowserRestoreObserver* observer) = 0;
+
   virtual void VisibleSecurityStateOfActiveTabChanged() = 0;
 };
 
diff --git a/weblayer/public/browser_restore_observer.h b/weblayer/public/browser_restore_observer.h
new file mode 100644
index 0000000..8f85de8
--- /dev/null
+++ b/weblayer/public/browser_restore_observer.h
@@ -0,0 +1,25 @@
+// 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 WEBLAYER_PUBLIC_BROWSER_RESTORE_OBSERVER_H_
+#define WEBLAYER_PUBLIC_BROWSER_RESTORE_OBSERVER_H_
+
+#include "base/observer_list.h"
+
+namespace weblayer {
+
+// Used for observing events related to restoring the previous state of a
+// Browser.
+class BrowserRestoreObserver : public base::CheckedObserver {
+ public:
+  // Called when the Browser has completed restoring the previous state.
+  virtual void OnRestoreCompleted() {}
+
+ protected:
+  ~BrowserRestoreObserver() override = default;
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_PUBLIC_BROWSER_RESTORE_OBSERVER_H_
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index 93818eb1..fbbb25a 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -38,6 +38,7 @@
     "org/chromium/weblayer/BroadcastReceiver.java",
     "org/chromium/weblayer/Browser.java",
     "org/chromium/weblayer/BrowserFragment.java",
+    "org/chromium/weblayer/BrowserRestoreCallback.java",
     "org/chromium/weblayer/BrowsingDataType.java",
     "org/chromium/weblayer/Callback.java",
     "org/chromium/weblayer/CaptureScreenShotCallback.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/Browser.java b/weblayer/public/java/org/chromium/weblayer/Browser.java
index d568ed02..f8a02de 100644
--- a/weblayer/public/java/org/chromium/weblayer/Browser.java
+++ b/weblayer/public/java/org/chromium/weblayer/Browser.java
@@ -35,17 +35,21 @@
     private final ObserverList<TabListCallback> mTabListCallbacks;
     private final UrlBarController mUrlBarController;
 
+    private final ObserverList<BrowserRestoreCallback> mBrowserRestoreCallbacks;
+
     // Constructor for test mocking.
     protected Browser() {
         mImpl = null;
         mTabListCallbacks = null;
         mUrlBarController = null;
+        mBrowserRestoreCallbacks = null;
     }
 
     Browser(IBrowser impl, BrowserFragment fragment) {
         mImpl = impl;
         mFragment = fragment;
         mTabListCallbacks = new ObserverList<TabListCallback>();
+        mBrowserRestoreCallbacks = new ObserverList<BrowserRestoreCallback>();
 
         try {
             mImpl.setClient(new BrowserClientImpl());
@@ -209,6 +213,58 @@
     }
 
     /**
+     * Returns true if this Browser is in the process of restoring the previous state.
+     *
+     * @param True if restoring previous state.
+     *
+     * @since 87
+     */
+    public boolean isRestoringPreviousState() {
+        ThreadCheck.ensureOnUiThread();
+        if (WebLayer.getSupportedMajorVersionInternal() < 87) {
+            throw new UnsupportedOperationException();
+        }
+        throwIfDestroyed();
+        try {
+            return mImpl.isRestoringPreviousState();
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
+     * Adds a BrowserRestoreCallback.
+     *
+     * @param callback The BrowserRestoreCallback.
+     *
+     * @since 87
+     */
+    public void registerBrowserRestoreCallback(@NonNull BrowserRestoreCallback callback) {
+        ThreadCheck.ensureOnUiThread();
+        if (WebLayer.getSupportedMajorVersionInternal() < 87) {
+            throw new UnsupportedOperationException();
+        }
+        throwIfDestroyed();
+        mBrowserRestoreCallbacks.addObserver(callback);
+    }
+
+    /**
+     * Removes a BrowserRestoreCallback.
+     *
+     * @param callback The BrowserRestoreCallback.
+     *
+     * @since 87
+     */
+    public void unregisterBrowserRestoreCallback(@NonNull BrowserRestoreCallback callback) {
+        ThreadCheck.ensureOnUiThread();
+        if (WebLayer.getSupportedMajorVersionInternal() < 87) {
+            throw new UnsupportedOperationException();
+        }
+        throwIfDestroyed();
+        mBrowserRestoreCallbacks.removeObserver(callback);
+    }
+
+    /**
      * Sets the View shown at the top of the browser. A value of null removes the view. The
      * top-view is typically used to show the uri. The top-view scrolls with the page.
      *
@@ -396,5 +452,12 @@
             StrictModeWorkaround.apply();
             return MediaRouteDialogFragment.create(mFragment);
         }
+
+        @Override
+        public void onRestoreCompleted() {
+            for (BrowserRestoreCallback callback : mBrowserRestoreCallbacks) {
+                callback.onRestoreCompleted();
+            }
+        }
     }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserRestoreCallback.java b/weblayer/public/java/org/chromium/weblayer/BrowserRestoreCallback.java
new file mode 100644
index 0000000..7ecb0a1
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserRestoreCallback.java
@@ -0,0 +1,17 @@
+// Copyright 2019 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.
+
+package org.chromium.weblayer;
+
+/**
+ * An interface for observing events related to restoring the previous state of a Browser.
+ *
+ * @since 88
+ */
+public abstract class BrowserRestoreCallback {
+    /**
+     * Called when WebLayer has finished restoring the previous state.
+     */
+    public void onRestoreCompleted() {}
+}
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
index decb817..4dd633cc 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/InstrumentationActivity.java
@@ -58,6 +58,8 @@
     // that show Page Info UI on its TextView.
     public static final String EXTRA_URLBAR_TEXT_CLICKABLE = "EXTRA_URLBAR_TEXT_CLICKABLE";
 
+    private static OnCreatedCallback sOnCreatedCallback;
+
     private Profile mProfile;
     private Fragment mFragment;
     private Browser mBrowser;
@@ -85,6 +87,25 @@
         return false;
     }
 
+    /**
+     * Use this callback for tests that need to be notified synchronously when the Browser has been
+     * created.
+     */
+    public static interface OnCreatedCallback {
+        // Notification that a Browser was created.
+        // This is called on the UI thread.
+        public void onCreated(Browser browser);
+    }
+
+    // Registers a callback that is notified on the UI thread when a Browser is created.
+    public static void registerOnCreatedCallback(OnCreatedCallback callback) {
+        sOnCreatedCallback = callback;
+        // Ideally |callback| would be registered in the Intent, but that isn't possible as to do so
+        // |callback| would have to be a Parceable (which doesn't make sense). As at this time each
+        // test runs in its own process a static is used, if multiple tests were to run in the same
+        // binary, then some state would need to be put in the intent.
+    }
+
     public Tab getTab() {
         return mTab;
     }
@@ -292,6 +313,12 @@
             setTabCallbacks(mBrowser.getActiveTab());
             setTab(mBrowser.getActiveTab());
         }
+
+        if (sOnCreatedCallback != null) {
+            sOnCreatedCallback.onCreated(mBrowser);
+            // Don't reset |sOnCreatedCallback| as it's needed for tests that exercise activity
+            // recreation.
+        }
     }
 
     private void setTabCallbacks(Tab tab) {