Ensure that WebFramesManager->GetMainWebFrame() always returns a valid WebFrame.

Remove extraneous |removeAllWebFrames| call to ensure a WebFrame exists for the main frame when navigating back then forward on a same page (ex:example.com#link) link.

Additionally, add tests to validate the returned WebFrame object.

Bug: 913825
Change-Id: I5abe3a50769564c1c95157eb713ee435093cbab4
Reviewed-on: https://chromium-review.googlesource.com/c/1368768
Reviewed-by: Eugene But <eugenebut@chromium.org>
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#616004}
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index af77a15..680b858 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -1981,8 +1981,6 @@
   DCHECK(!_isHalted);
   _webStateImpl->ClearTransientContent();
 
-  // Reset tracked frames because JavaScript unload handler will not be called.
-  [self removeAllWebFrames];
   web::NavigationItem* item = self.currentNavItem;
   const GURL currentURL = item ? item->GetURL() : GURL::EmptyGURL();
   const bool isCurrentURLAppSpecific =
@@ -2117,7 +2115,6 @@
   [_webView stopLoading];
   [_pendingNavigationInfo setCancelled:YES];
   _certVerificationErrors->Clear();
-  [self removeAllWebFrames];
   [self loadCancelled];
 }
 
diff --git a/ios/web/web_state/web_frames_manager_inttest.mm b/ios/web/web_state/web_frames_manager_inttest.mm
index f785028..5598145 100644
--- a/ios/web/web_state/web_frames_manager_inttest.mm
+++ b/ios/web/web_state/web_frames_manager_inttest.mm
@@ -2,36 +2,232 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/test/web_test_with_web_state.h"
-
 #include "base/ios/ios_util.h"
+#include "base/test/scoped_feature_list.h"
+#include "ios/web/public/features.h"
+#import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/test/web_view_content_test_util.h"
+#import "ios/web/public/test/web_view_interaction_test_util.h"
 #include "ios/web/public/web_state/web_frame.h"
+#import "ios/web/test/web_int_test.h"
 #include "ios/web/web_state/web_frames_manager_impl.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+// WebFramesManagerTest is parameterized on this enum to test both
+// LegacyNavigationManager and WKBasedNavigationManager.
+enum class NavigationManagerChoice {
+  LEGACY,
+  WK_BASED,
+};
+
+// URL of a page with test links.
+const char kLinksPageURL[] = "/links.html";
+
+// Text of the link to |kPonyPageURL| on |kLinksPageURL|.
+const char kLinksPagePonyLinkText[] = "Normal Link";
+
+// ID of the link to |kPonyPageURL| on |kLinksPageURL|.
+const char kLinksPagePonyLinkID[] = "normal-link";
+
+// Text of the same page link on |kLinksPageURL|.
+const char kLinksPageSamePageLinkText[] = "Same-page Link";
+
+// ID of the same page link on |kLinksPageURL|.
+const char kLinksPageSamePageLinkID[] = "same-page-link";
+
+// URL of a page linked to by a link on |kLinksPageURL|.
+const char kPonyPageURL[] = "/pony.html";
+
+// Text on |kPonyPageURL|.
+const char kPonyPageText[] = "Anyone know any good pony jokes?";
+}  // namespace
+
 namespace web {
 
-typedef WebTestWithWebState WebFramesManagerTest;
+// Test fixture for integration tests involving html5 window.history state
+// operations.
+class WebFramesManagerTest
+    : public WebIntTest,
+      public ::testing::WithParamInterface<NavigationManagerChoice> {
+ protected:
+  void SetUp() override {
+    WebIntTest::SetUp();
 
-// Tests that the WebFramesManager correctly adds a WebFrame for a webpage with
-// a single frame.
-TEST_F(WebFramesManagerTest, SingleWebFrameAdded) {
-  LoadHtml(@"<p></p>");
+    if (GetParam() == NavigationManagerChoice::LEGACY) {
+      scoped_feature_list_.InitAndDisableFeature(
+          web::features::kSlimNavigationManager);
+    } else {
+      scoped_feature_list_.InitAndEnableFeature(
+          web::features::kSlimNavigationManager);
+    }
 
+    test_server_ = std::make_unique<net::test_server::EmbeddedTestServer>();
+    net::test_server::RegisterDefaultHandlers(test_server_.get());
+    test_server_->ServeFilesFromSourceDirectory(
+        base::FilePath("ios/testing/data/http_server_files/"));
+    ASSERT_TRUE(test_server_->Start());
+  }
+
+ protected:
+  // Embedded test server which hosts sample pages.
+  std::unique_ptr<net::EmbeddedTestServer> test_server_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests that the WebFramesManager correctly adds a WebFrame for a webpage.
+TEST_P(WebFramesManagerTest, SingleWebFrame) {
   WebFramesManagerImpl* frames_manager =
       WebFramesManagerImpl::FromWebState(web_state());
 
-  EXPECT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  GURL url = test_server_->GetURL("/echo");
+  ASSERT_TRUE(LoadUrl(url));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
 
   WebFrame* main_web_frame = frames_manager->GetMainWebFrame();
   ASSERT_TRUE(main_web_frame);
   EXPECT_TRUE(main_web_frame->IsMainFrame());
   EXPECT_FALSE(main_web_frame->GetFrameId().empty());
-
-  EXPECT_EQ(GURL(BaseUrl()).GetOrigin(), main_web_frame->GetSecurityOrigin());
+  EXPECT_EQ(url.GetOrigin(), main_web_frame->GetSecurityOrigin());
 };
 
+// Tests that the WebFramesManager correctly adds a unique WebFrame after a
+// webpage navigates back.
+TEST_P(WebFramesManagerTest, SingleWebFrameBack) {
+  WebFramesManagerImpl* frames_manager =
+      WebFramesManagerImpl::FromWebState(web_state());
+
+  // Load first page.
+  GURL url = test_server_->GetURL("/echo");
+  ASSERT_TRUE(LoadUrl(url));
+
+  WebFrame* main_web_frame = frames_manager->GetMainWebFrame();
+  ASSERT_TRUE(main_web_frame);
+  std::string frame_id = main_web_frame->GetFrameId();
+  EXPECT_FALSE(frame_id.empty());
+
+  // Load second page.
+  GURL pony_url = test_server_->GetURL(kPonyPageURL);
+  ASSERT_TRUE(LoadUrl(pony_url));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  WebFrame* pony_main_web_frame = frames_manager->GetMainWebFrame();
+  ASSERT_TRUE(pony_main_web_frame);
+  EXPECT_TRUE(pony_main_web_frame->IsMainFrame());
+  EXPECT_EQ(pony_url.GetOrigin(), pony_main_web_frame->GetSecurityOrigin());
+
+  std::string pony_frame_id = pony_main_web_frame->GetFrameId();
+  EXPECT_FALSE(pony_frame_id.empty());
+  EXPECT_NE(frame_id, pony_frame_id);
+
+  // Navigate back to first page.
+  navigation_manager()->GoBack();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+
+  EXPECT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_EQ(frame_id, frames_manager->GetMainWebFrame()->GetFrameId());
+};
+
+// Tests that the WebFramesManager correctly adds a unique WebFrame after a
+// webpage navigates back from a clicked link.
+TEST_P(WebFramesManagerTest, SingleWebFrameLinkNavigationBackForward) {
+  WebFramesManagerImpl* frames_manager =
+      WebFramesManagerImpl::FromWebState(web_state());
+
+  // Load page with links.
+  GURL url = test_server_->GetURL(kLinksPageURL);
+  ASSERT_TRUE(LoadUrl(url));
+
+  WebFrame* main_web_frame = frames_manager->GetMainWebFrame();
+  ASSERT_TRUE(main_web_frame);
+  EXPECT_TRUE(main_web_frame->IsMainFrame());
+  std::string frame_id = main_web_frame->GetFrameId();
+
+  // Navigate to a linked page.
+  GURL pony_url = test_server_->GetURL(kPonyPageURL);
+  ASSERT_TRUE(ExecuteBlockAndWaitForLoad(pony_url, ^{
+    ASSERT_TRUE(
+        web::test::TapWebViewElementWithId(web_state(), kLinksPagePonyLinkID));
+  }));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  WebFrame* pony_main_web_frame = frames_manager->GetMainWebFrame();
+  ASSERT_TRUE(pony_main_web_frame);
+  EXPECT_TRUE(pony_main_web_frame->IsMainFrame());
+  EXPECT_EQ(pony_url.GetOrigin(), pony_main_web_frame->GetSecurityOrigin());
+
+  std::string pony_frame_id = pony_main_web_frame->GetFrameId();
+  EXPECT_FALSE(pony_frame_id.empty());
+  EXPECT_NE(frame_id, pony_frame_id);
+
+  // Go back to the links page.
+  navigation_manager()->GoBack();
+  ASSERT_TRUE(
+      test::WaitForWebViewContainingText(web_state(), kLinksPagePonyLinkText));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_FALSE(frames_manager->GetMainWebFrame()->GetFrameId().empty());
+  EXPECT_EQ(frame_id, frames_manager->GetMainWebFrame()->GetFrameId());
+
+  // Go forward to the linked page.
+  navigation_manager()->GoForward();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), kPonyPageText));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_FALSE(frames_manager->GetMainWebFrame()->GetFrameId().empty());
+  EXPECT_NE(frame_id, frames_manager->GetMainWebFrame()->GetFrameId());
+};
+
+// Tests that the WebFramesManager correctly adds a unique WebFrame after a
+// webpage navigates back from a clicked same page link.
+TEST_P(WebFramesManagerTest, SingleWebFrameSamePageNavigationBackForward) {
+  WebFramesManagerImpl* frames_manager =
+      WebFramesManagerImpl::FromWebState(web_state());
+
+  GURL url = test_server_->GetURL(kLinksPageURL);
+  ASSERT_TRUE(LoadUrl(url));
+
+  WebFrame* main_web_frame = frames_manager->GetMainWebFrame();
+  ASSERT_TRUE(main_web_frame);
+  EXPECT_TRUE(main_web_frame->IsMainFrame());
+  std::string frame_id = main_web_frame->GetFrameId();
+
+  ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(),
+                                                 kLinksPageSamePageLinkID));
+
+  // WebFrame should not have changed.
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_EQ(main_web_frame, frames_manager->GetMainWebFrame());
+
+  navigation_manager()->GoBack();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(),
+                                                 kLinksPageSamePageLinkText));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_EQ(main_web_frame, frames_manager->GetMainWebFrame());
+
+  navigation_manager()->GoForward();
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(),
+                                                 kLinksPageSamePageLinkText));
+
+  ASSERT_EQ(1ul, frames_manager->GetAllWebFrames().size());
+  EXPECT_FALSE(frames_manager->GetMainWebFrame()->GetFrameId().empty());
+  EXPECT_EQ(main_web_frame, frames_manager->GetMainWebFrame());
+};
+
+INSTANTIATE_TEST_CASE_P(ProgrammaticWebFramesManagerTest,
+                        WebFramesManagerTest,
+                        ::testing::Values(NavigationManagerChoice::LEGACY,
+                                          NavigationManagerChoice::WK_BASED));
+
 }  // namespace web