| // Copyright 2017 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. |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/ios/ios_util.h" |
| #include "base/scoped_observer.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #import "base/test/ios/wait_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "ios/testing/embedded_test_server_handlers.h" |
| #import "ios/web/public/crw_navigation_item_storage.h" |
| #import "ios/web/public/crw_session_storage.h" |
| #include "ios/web/public/features.h" |
| #import "ios/web/public/navigation_item.h" |
| #import "ios/web/public/navigation_manager.h" |
| #import "ios/web/public/test/fakes/test_native_content.h" |
| #import "ios/web/public/test/fakes/test_native_content_provider.h" |
| #import "ios/web/public/test/navigation_test_util.h" |
| #import "ios/web/public/test/web_view_content_test_util.h" |
| #import "ios/web/public/test/web_view_interaction_test_util.h" |
| #import "ios/web/public/web_client.h" |
| #import "ios/web/public/web_state/navigation_context.h" |
| #include "ios/web/public/web_state/web_state_observer.h" |
| #import "ios/web/public/web_state/web_state_policy_decider.h" |
| #include "ios/web/test/test_url_constants.h" |
| #import "ios/web/test/web_int_test.h" |
| #import "ios/web/web_state/ui/crw_web_controller.h" |
| #import "ios/web/web_state/web_state_impl.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/test/embedded_test_server/default_handlers.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/embedded_test_server/request_handler_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #import "testing/gtest_mac.h" |
| #include "ui/base/page_transition_types.h" |
| #include "url/gurl.h" |
| #include "url/scheme_host_port.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace web { |
| |
| namespace { |
| |
| const char kExpectedMimeType[] = "text/html"; |
| |
| const char kFailedTitle[] = "failed_title"; |
| |
| // WebStateObserverTest is parameterized on this enum to test both |
| // LegacyNavigationManagerImpl and WKBasedNavigationManagerImpl. |
| enum NavigationManagerChoice { |
| TEST_LEGACY_NAVIGATION_MANAGER, |
| TEST_WK_BASED_NAVIGATION_MANAGER, |
| }; |
| |
| // Verifies correctness of WebState's title. |
| ACTION_P(VerifyTitle, expected_title) { |
| WebState* web_state = arg0; |
| EXPECT_EQ(expected_title, base::UTF16ToASCII(web_state->GetTitle())); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation |
| // passed to |DidStartNavigation|. Stores |NavigationContext| in |context| |
| // pointer. |
| ACTION_P5(VerifyPageStartedContext, |
| web_state, |
| url, |
| transition, |
| context, |
| nav_id) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| ui::PageTransition actual_transition = (*context)->GetPageTransition(); |
| EXPECT_TRUE(PageTransitionCoreTypeIs(transition, actual_transition)) |
| << "Got unexpected transition: " << actual_transition; |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| ASSERT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetPendingItem(); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation |
| // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as |
| // |context|. |
| ACTION_P5(VerifyNewPageFinishedContext, |
| web_state, |
| url, |
| mime_type, |
| context, |
| nav_id) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_TRUE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| ASSERT_TRUE((*context)->GetResponseHeaders()); |
| std::string actual_mime_type; |
| (*context)->GetResponseHeaders()->GetMimeType(&actual_mime_type); |
| ASSERT_TRUE(web_state->IsLoading()); |
| EXPECT_EQ(mime_type, actual_mime_type); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for failed navigation |
| // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as |
| // |context|. |
| ACTION_P5(VerifyErrorFinishedContext, |
| web_state, |
| url, |
| context, |
| nav_id, |
| error_code) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| // The error code will be different on bots and for local runs. Allow both. |
| NSInteger actual_error_code = (*context)->GetError().code; |
| EXPECT_EQ(error_code, actual_error_code); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_FALSE(item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) passed to |
| // |DidFinishNavigation| for navigation canceled due to a rejected response. |
| // Asserts that |NavigationContext| the same as |context|. |
| ACTION_P4(VerifyResponseRejectedFinishedContext, |
| web_state, |
| url, |
| context, |
| nav_id) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| // When the response is rejected discard non committed items is called and |
| // no item should be committed. |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_FALSE(web_state->IsLoading()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for navigations via POST |
| // HTTP methods passed to |DidStartNavigation|. Stores |NavigationContext| in |
| // |context| pointer. |
| ACTION_P6(VerifyPostStartedContext, |
| web_state, |
| url, |
| has_user_gesture, |
| context, |
| nav_id, |
| renderer_initiated) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_EQ(has_user_gesture, (*context)->HasUserGesture()); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_TRUE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_EQ(renderer_initiated, (*context)->IsRendererInitiated()); |
| ASSERT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| // TODO(crbug.com/676129): Reload does not create a pending item. Remove this |
| // workaround once the bug is fixed. The slim navigation manager fixes this |
| // bug. |
| if (GetWebClient()->IsSlimNavigationManagerEnabled() || |
| !ui::PageTransitionTypeIncludingQualifiersIs( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| (*context)->GetPageTransition())) { |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetPendingItem(); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for navigations via POST |
| // HTTP methods passed to |DidFinishNavigation|. Stores |NavigationContext| in |
| // |context| pointer. |
| ACTION_P6(VerifyPostFinishedContext, |
| web_state, |
| url, |
| has_user_gesture, |
| context, |
| nav_id, |
| renderer_initiated) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_EQ(has_user_gesture, (*context)->HasUserGesture()); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_TRUE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_TRUE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_EQ(renderer_initiated, (*context)->IsRendererInitiated()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for same page navigation |
| // passed to |DidFinishNavigation|. Stores |NavigationContext| in |context| |
| // pointer. |
| ACTION_P7(VerifySameDocumentStartedContext, |
| web_state, |
| url, |
| has_user_gesture, |
| context, |
| nav_id, |
| page_transition, |
| renderer_initiated) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_EQ(has_user_gesture, (*context)->HasUserGesture()); |
| EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs( |
| page_transition, (*context)->GetPageTransition())); |
| EXPECT_TRUE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for same page navigation |
| // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as |
| // |context|. |
| ACTION_P7(VerifySameDocumentFinishedContext, |
| web_state, |
| url, |
| has_user_gesture, |
| context, |
| nav_id, |
| page_transition, |
| renderer_initiated) { |
| ASSERT_EQ(*context, arg1); |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_EQ(has_user_gesture, (*context)->HasUserGesture()); |
| EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs( |
| page_transition, (*context)->GetPageTransition())); |
| EXPECT_TRUE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation |
| // to native URLs passed to |DidStartNavigation|. Stores |NavigationContext| in |
| // |context| pointer. |
| ACTION_P4(VerifyNewNativePageStartedContext, web_state, url, context, nav_id) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetPendingItem(); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation |
| // to native URLs passed to |DidFinishNavigation|. Asserts that |
| // |NavigationContext| the same as |context|. |
| ACTION_P4(VerifyNewNativePageFinishedContext, web_state, url, context, nav_id) { |
| ASSERT_EQ(*context, arg1); |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_TRUE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for reload navigation |
| // passed to |DidStartNavigation|. Stores |NavigationContext| in |context| |
| // pointer. |
| ACTION_P4(VerifyReloadStartedContext, web_state, url, context, nav_id) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| // TODO(crbug.com/676129): Reload does not create a pending item. Check |
| // pending item once the bug is fixed. The slim navigation manager fixes this |
| // bug. |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetPendingItem(); |
| EXPECT_EQ(url, item->GetURL()); |
| } else { |
| EXPECT_FALSE(web_state->GetNavigationManager()->GetPendingItem()); |
| } |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for reload navigation |
| // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as |
| // |context|. |
| ACTION_P5(VerifyReloadFinishedContext, |
| web_state, |
| url, |
| context, |
| nav_id, |
| is_web_page) { |
| ASSERT_EQ(*context, arg1); |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_TRUE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| if (is_web_page) { |
| ASSERT_TRUE((*context)->GetResponseHeaders()); |
| std::string mime_type; |
| (*context)->GetResponseHeaders()->GetMimeType(&mime_type); |
| EXPECT_EQ(kExpectedMimeType, mime_type); |
| } else { |
| EXPECT_FALSE((*context)->GetResponseHeaders()); |
| } |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for download navigation |
| // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as |
| // |context|. |
| ACTION_P4(VerifyDownloadFinishedContext, web_state, url, context, nav_id) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| EXPECT_TRUE( |
| PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED, |
| (*context)->GetPageTransition())); |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_TRUE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for restoration |
| // navigation passed to |DidStartNavigation|. Stores |NavigationContext| in |
| // |context| pointer. |
| ACTION_P4(VerifyRestorationStartedContext, web_state, url, context, nav_id) { |
| *context = arg1; |
| ASSERT_TRUE(*context); |
| EXPECT_EQ(web_state, arg0); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| *nav_id = (*context)->GetNavigationId(); |
| EXPECT_NE(0, *nav_id); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be |
| // browser-initiated and should have user gesture. |
| EXPECT_FALSE((*context)->HasUserGesture()); |
| } else { |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| } |
| ui::PageTransition actual_transition = (*context)->GetPageTransition(); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be reload. |
| EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs( |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, actual_transition)) |
| << "Got unexpected transition: " << actual_transition; |
| } else { |
| EXPECT_TRUE(PageTransitionCoreTypeIs( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, actual_transition)) |
| << "Got unexpected transition: " << actual_transition; |
| } |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_FALSE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be |
| // browser-initiated. |
| EXPECT_TRUE((*context)->IsRendererInitiated()); |
| } else { |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| } |
| ASSERT_FALSE((*context)->GetResponseHeaders()); |
| ASSERT_TRUE(web_state->IsLoading()); |
| |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| ASSERT_TRUE(navigation_manager->GetPendingItem()); |
| EXPECT_EQ(url, navigation_manager->GetPendingItem()->GetURL()); |
| } |
| |
| // Verifies correctness of |NavigationContext| (|arg1|) for restoration |
| // navigation passed to |DidFinishNavigation|. Asserts that |NavigationContext| |
| // the same as |context|. |
| ACTION_P5(VerifyRestorationFinishedContext, |
| web_state, |
| url, |
| mime_type, |
| context, |
| nav_id) { |
| ASSERT_EQ(*context, arg1); |
| EXPECT_EQ(web_state, arg0); |
| ASSERT_TRUE((*context)); |
| EXPECT_EQ(web_state, (*context)->GetWebState()); |
| EXPECT_EQ(*nav_id, (*context)->GetNavigationId()); |
| EXPECT_EQ(url, (*context)->GetUrl()); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be |
| // browser-initiated and should have user gesture. |
| EXPECT_FALSE((*context)->HasUserGesture()); |
| } else { |
| EXPECT_TRUE((*context)->HasUserGesture()); |
| } |
| ui::PageTransition actual_transition = (*context)->GetPageTransition(); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be reload. |
| EXPECT_TRUE(PageTransitionTypeIncludingQualifiersIs( |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, actual_transition)) |
| << "Got unexpected transition: " << actual_transition; |
| } else { |
| EXPECT_TRUE(PageTransitionCoreTypeIs( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, actual_transition)) |
| << "Got unexpected transition: " << actual_transition; |
| } |
| EXPECT_FALSE((*context)->IsSameDocument()); |
| EXPECT_TRUE((*context)->HasCommitted()); |
| EXPECT_FALSE((*context)->IsDownload()); |
| EXPECT_FALSE((*context)->IsPost()); |
| EXPECT_FALSE((*context)->GetError()); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // TODO(crbug.com/877671): restoration navigation should be |
| // browser-initiated. |
| EXPECT_TRUE((*context)->IsRendererInitiated()); |
| } else { |
| EXPECT_FALSE((*context)->IsRendererInitiated()); |
| } |
| ASSERT_TRUE((*context)->GetResponseHeaders()); |
| std::string actual_mime_type; |
| (*context)->GetResponseHeaders()->GetMimeType(&actual_mime_type); |
| ASSERT_TRUE(web_state->IsLoading()); |
| EXPECT_EQ(mime_type, actual_mime_type); |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| NavigationItem* item = navigation_manager->GetLastCommittedItem(); |
| EXPECT_TRUE(!item->GetTimestamp().is_null()); |
| EXPECT_EQ(url, item->GetURL()); |
| } |
| |
| // A Google Mock matcher which matches |target_frame_is_main| member of |
| // WebStatePolicyDecider::RequestInfo. This is needed because |
| // WebStatePolicyDecider::RequestInfo doesn't support operator==. |
| MATCHER_P(RequestInfoMatch, expected_request_info, /* argument_name = */ "") { |
| return ui::PageTransitionTypeIncludingQualifiersIs( |
| arg.transition_type, expected_request_info.transition_type) && |
| arg.target_frame_is_main == |
| expected_request_info.target_frame_is_main && |
| arg.has_user_gesture == expected_request_info.has_user_gesture; |
| } |
| |
| // Mocks WebStateObserver navigation callbacks. |
| class WebStateObserverMock : public WebStateObserver { |
| public: |
| WebStateObserverMock() = default; |
| |
| MOCK_METHOD2(DidStartNavigation, void(WebState*, NavigationContext*)); |
| MOCK_METHOD2(DidFinishNavigation, void(WebState*, NavigationContext*)); |
| MOCK_METHOD1(DidStartLoading, void(WebState*)); |
| MOCK_METHOD1(DidStopLoading, void(WebState*)); |
| MOCK_METHOD2(PageLoaded, void(WebState*, PageLoadCompletionStatus)); |
| MOCK_METHOD1(DidChangeBackForwardState, void(WebState*)); |
| MOCK_METHOD1(TitleWasSet, void(WebState*)); |
| void WebStateDestroyed(WebState* web_state) override { NOTREACHED(); } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(WebStateObserverMock); |
| }; |
| |
| // Mocks WebStatePolicyDecider decision callbacks. |
| class PolicyDeciderMock : public WebStatePolicyDecider { |
| public: |
| PolicyDeciderMock(WebState* web_state) : WebStatePolicyDecider(web_state) {} |
| MOCK_METHOD2(ShouldAllowRequest, |
| bool(NSURLRequest*, |
| const WebStatePolicyDecider::RequestInfo& request_info)); |
| MOCK_METHOD2(ShouldAllowResponse, bool(NSURLResponse*, bool for_main_frame)); |
| }; |
| |
| } // namespace |
| |
| using testing::Return; |
| using testing::StrictMock; |
| using testing::_; |
| using base::test::ios::kWaitForPageLoadTimeout; |
| using base::test::ios::WaitUntilConditionOrTimeout; |
| using test::WaitForWebViewContainingText; |
| |
| // Test fixture to test navigation and load callbacks from WebStateObserver and |
| // WebStatePolicyDecider. |
| class WebStateObserverTest |
| : public WebIntTest, |
| public ::testing::WithParamInterface<NavigationManagerChoice> { |
| public: |
| WebStateObserverTest() : scoped_observer_(&observer_) {} |
| |
| void SetUp() override { |
| if (GetParam() == TEST_LEGACY_NAVIGATION_MANAGER) { |
| scoped_feature_list_.InitAndDisableFeature( |
| features::kSlimNavigationManager); |
| } else { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kSlimNavigationManager); |
| } |
| |
| WebIntTest::SetUp(); |
| decider_ = std::make_unique<StrictMock<PolicyDeciderMock>>(web_state()); |
| scoped_observer_.Add(web_state()); |
| |
| // Stub out NativeContent objects. |
| provider_ = [[TestNativeContentProvider alloc] init]; |
| content_ = [[TestNativeContent alloc] initWithURL:GURL::EmptyGURL() |
| virtualURL:GURL::EmptyGURL()]; |
| |
| WebStateImpl* web_state_impl = reinterpret_cast<WebStateImpl*>(web_state()); |
| web_state_impl->GetWebController().nativeProvider = provider_; |
| |
| test_server_ = std::make_unique<net::test_server::EmbeddedTestServer>(); |
| test_server_->RegisterRequestHandler( |
| base::BindRepeating(&net::test_server::HandlePrefixedRequest, "/form", |
| base::BindRepeating(&testing::HandleForm))); |
| test_server_->RegisterRequestHandler(base::BindRepeating( |
| &net::test_server::HandlePrefixedRequest, "/download", |
| base::BindRepeating(&testing::HandleDownload))); |
| RegisterDefaultHandlers(test_server_.get()); |
| test_server_->ServeFilesFromSourceDirectory( |
| base::FilePath("ios/testing/data/http_server_files/")); |
| ASSERT_TRUE(test_server_->Start()); |
| } |
| |
| void TearDown() override { |
| scoped_observer_.RemoveAll(); |
| WebIntTest::TearDown(); |
| } |
| |
| protected: |
| TestNativeContentProvider* provider_; |
| TestNativeContent* content_; |
| std::unique_ptr<StrictMock<PolicyDeciderMock>> decider_; |
| StrictMock<WebStateObserverMock> observer_; |
| std::unique_ptr<net::test_server::EmbeddedTestServer> test_server_; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| ScopedObserver<WebState, WebStateObserver> scoped_observer_; |
| testing::InSequence callbacks_sequence_checker_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WebStateObserverTest); |
| }; |
| |
| // Tests successful navigation to a new page. |
| TEST_P(WebStateObserverTest, NewPageNavigation) { |
| const GURL url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| } |
| |
| // Tests that if web usage is already enabled, enabling it again would not cause |
| // any page loads (related to restoring cached session). This is a regression |
| // test for crbug.com/781916. |
| TEST_P(WebStateObserverTest, EnableWebUsageTwice) { |
| const GURL url = test_server_->GetURL("/echo"); |
| |
| // Only expect one set of load events from the first LoadUrl(), not subsequent |
| // SetWebUsageEnabled(true) calls. Web usage is already enabled, so the |
| // subsequent calls should be noops. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| |
| ASSERT_TRUE(LoadUrl(url)); |
| web_state()->SetWebUsageEnabled(true); |
| web_state()->SetWebUsageEnabled(true); |
| } |
| |
| // Tests failed navigation to a new page. |
| TEST_P(WebStateObserverTest, FailedNavigation) { |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| |
| const GURL url = test_server_->GetURL("/close-socket"); |
| |
| // Perform a navigation to url with unsupported scheme, which will fail. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyErrorFinishedContext(web_state(), url, &context, &nav_id, |
| NSURLErrorNetworkConnectionLost)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::FAILURE)); |
| test::LoadUrl(web_state(), url); |
| |
| // Ensure that title is not overridden by a placeholder navigation. |
| web::NavigationManager* manager = web_state()->GetNavigationManager(); |
| web::NavigationItem* item = manager->GetPendingItem(); |
| item->SetTitle(base::UTF8ToUTF16(kFailedTitle)); |
| ASSERT_TRUE(test::WaitForWebViewContainingText( |
| web_state(), "The network connection was lost.")); |
| DCHECK_EQ(item->GetTitle(), base::UTF8ToUTF16(kFailedTitle)); |
| } |
| |
| // Tests failed navigation because URL scheme is not supported. |
| TEST_P(WebStateObserverTest, UnsupportedSchemeNavigation) { |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| |
| GURL url(url::SchemeHostPort(kTestAppSpecificScheme, "foo", 0).Serialize()); |
| |
| // Perform a navigation to url with unsupported scheme, which will fail. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyErrorFinishedContext(web_state(), url, &context, &nav_id, |
| NSURLErrorUnsupportedURL)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::FAILURE)); |
| test::LoadUrl(web_state(), url); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests web page reload navigation. |
| TEST_P(WebStateObserverTest, WebPageReloadNavigation) { |
| const GURL url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Reload web page. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo reload_request_info( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| /*target_main_frame=*/true, |
| /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(reload_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce( |
| VerifyReloadStartedContext(web_state(), url, &context, &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context, &nav_id, |
| true /* is_web_page */)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(url, ^{ |
| navigation_manager()->Reload(ReloadType::NORMAL, |
| /*check_for_repost=*/false); |
| })); |
| } |
| |
| // Tests web page reload with user agent override. |
| TEST_P(WebStateObserverTest, ReloadWithUserAgentType) { |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| const GURL url = test_server_->GetURL("/echo"); |
| |
| // Perform new page navigation. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Reload web page with desktop user agent. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_reload_request_info( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, ShouldAllowRequest( |
| _, RequestInfoMatch(expected_reload_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| &context, &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| // TODO(crbug.com/798836): verify the correct User-Agent header is sent. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| web_state()->SetDelegate(nullptr); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(url, ^{ |
| navigation_manager()->ReloadWithUserAgentType(UserAgentType::DESKTOP); |
| })); |
| } |
| |
| // Tests user-initiated hash change. |
| TEST_P(WebStateObserverTest, UserInitiatedHashChangeNavigation) { |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| const GURL url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Perform same-document navigation. |
| const GURL hash_url = test_server_->GetURL("/echoall#1"); |
| WebStatePolicyDecider::RequestInfo hash_url_expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL( |
| *decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(hash_url_expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentStartedContext( |
| web_state(), hash_url, /*has_user_gesture=*/true, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*renderer_initiated=*/false)); |
| // No ShouldAllowResponse callback for same-document navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentFinishedContext( |
| web_state(), hash_url, /*has_user_gesture=*/true, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(hash_url)); |
| |
| // Perform same-document navigation by going back. |
| // No ShouldAllowRequest callback for same-document back-forward navigations. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentStartedContext( |
| web_state(), url, /*has_user_gesture=*/true, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, |
| /*renderer_initiated=*/false)); |
| // No ShouldAllowResponse callbacks for same-document back-forward |
| // navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentFinishedContext( |
| web_state(), url, /*has_user_gesture=*/true, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK, |
| /*renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(url, ^{ |
| navigation_manager()->GoBack(); |
| })); |
| } |
| |
| // Tests renderer-initiated hash change. |
| TEST_P(WebStateObserverTest, RendererInitiatedHashChangeNavigation) { |
| const GURL url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Perform same-page navigation using JavaScript. |
| const GURL hash_url = test_server_->GetURL("/echoall#1"); |
| WebStatePolicyDecider::RequestInfo expected_hash_request_info( |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, ShouldAllowRequest( |
| _, RequestInfoMatch(expected_hash_request_info))) |
| .WillOnce(Return(true)); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentStartedContext( |
| web_state(), hash_url, /*has_user_gesture=*/false, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| // No ShouldAllowResponse callback for same-document navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentFinishedContext( |
| web_state(), hash_url, /*has_user_gesture=*/false, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ExecuteJavaScript(@"window.location.hash = '#1'"); |
| } |
| |
| // Tests state change. |
| TEST_P(WebStateObserverTest, StateNavigation) { |
| const GURL url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Perform push state using JavaScript. |
| const GURL push_url = test_server_->GetURL("/test.html"); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentStartedContext( |
| web_state(), push_url, /*has_user_gesture=*/false, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| // No ShouldAllowRequest/ShouldAllowResponse callbacks for same-document push |
| // state navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentFinishedContext( |
| web_state(), push_url, /*has_user_gesture=*/false, &context, &nav_id, |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| ExecuteJavaScript(@"window.history.pushState('', 'Test', 'test.html')"); |
| |
| // Perform replace state using JavaScript. |
| const GURL replace_url = test_server_->GetURL("/1.html"); |
| // No ShouldAllowRequest callbacks for same-document push state navigations. |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentStartedContext( |
| web_state(), replace_url, /*has_user_gesture=*/false, &context, |
| &nav_id, ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| // No ShouldAllowResponse callbacks for same-document push state navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifySameDocumentFinishedContext( |
| web_state(), replace_url, /*has_user_gesture=*/false, &context, |
| &nav_id, ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*renderer_initiated=*/true)); |
| ExecuteJavaScript(@"window.history.replaceState('', 'Test', '1.html')"); |
| } |
| |
| // Tests native content navigation. |
| TEST_P(WebStateObserverTest, NativeContentNavigation) { |
| GURL url(url::SchemeHostPort(kTestNativeContentScheme, "ui", 0).Serialize()); |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyNewNativePageStartedContext(web_state(), url, &context, |
| &nav_id)); |
| // No ShouldAllowRequest/ShouldAllowResponse callbacks for native content |
| // navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewNativePageFinishedContext(web_state(), url, &context, |
| &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("Test Title")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| [provider_ setController:content_ forURL:url]; |
| ASSERT_TRUE(LoadUrl(url)); |
| } |
| |
| // Tests native content reload navigation. |
| TEST_P(WebStateObserverTest, NativeContentReload) { |
| GURL url(url::SchemeHostPort(kTestNativeContentScheme, "ui", 0).Serialize()); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| // No ShouldAllowRequest/ShouldAllowResponse callbacks for native content |
| // navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("Test Title")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| [provider_ setController:content_ forURL:url]; |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Reload native content. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| // No ShouldAllowRequest callbacks for native content navigations. |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce( |
| VerifyReloadStartedContext(web_state(), url, &context, &nav_id)); |
| // No ShouldAllowResponse callbacks for native content navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyReloadFinishedContext(web_state(), url, &context, &nav_id, |
| false /* is_web_page */)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| web_state()->GetNavigationManager()->Reload(ReloadType::NORMAL, |
| /*check_for_repost=*/false); |
| } |
| |
| // Tests WasTitleSet callback triggered when navigating back to native content |
| // from web content. |
| TEST_P(WebStateObserverTest, GoBackToNativeContent) { |
| // Load a native content URL. |
| GURL url(url::SchemeHostPort(kTestNativeContentScheme, "ui", 0).Serialize()); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| // No ShouldAllowRequest/ShouldAllowResponse callbacks for native content |
| // navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("Test Title")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| [provider_ setController:content_ forURL:url]; |
| ASSERT_TRUE(LoadUrl(url)); |
| |
| // Load a web navigation. |
| const GURL web_url = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), web_url, ui::PageTransition::PAGE_TRANSITION_TYPED, |
| &context, &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), web_url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(web_url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(web_url)); |
| |
| // Going back to native content should trigger TitleWasSet. |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())).Times(2); |
| // TODO(crbug.com/867095): fix this extra callback triggered by placeholder |
| // URL load. |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| } |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| // No ShouldAllowRequest/ShouldAllowResponse callbacks for native content |
| // navigations. |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| // This call is required to make sure WebStateObservers update their cached |
| // version of current title. |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("Test Title")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(url, ^{ |
| navigation_manager()->GoBack(); |
| })); |
| } |
| |
| // Tests successful navigation to a new page with post HTTP method. |
| TEST_P(WebStateObserverTest, UserInitiatedPostNavigation) { |
| // Prior to iOS 11, POST navigation is implemented as an XMLHttpRequest in |
| // JavaScript. This doesn't create a WKBackForwardListItem in WKWebView on |
| // which to attach the pending NavigationItem, if WKBasedNavigationManager is |
| // used. When POST is the first navigation in an empty web view, this causes a |
| // DCHECK in WKBasedNavigationManagerImpl::CommitPendingItem(). When it is not |
| // the first navigation, it attaches the pending NavigationItem to the wrong |
| // WKBackForwardListItem. However, this is not worth fixing now. Load an |
| // initial request to the web view to stop this test from crashing, even |
| // though this doesn't fix the underlying bug. |
| // TODO(crbug.com/740987): remove this hack once support for iOS10 is dropped. |
| if (!base::ios::IsRunningOnIOS11OrLater() && |
| GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| const GURL url = test_server_->GetURL("/echoall?bar"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, |
| &context, &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| ASSERT_TRUE(WaitForWebViewContainingText(web_state(), "bar")); |
| } |
| |
| const GURL url = test_server_->GetURL("/echo"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_GENERATED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPostStartedContext( |
| web_state(), url, /*has_user_gesture=*/true, &context, &nav_id, |
| /*renderer_initiated=*/false)); |
| if (@available(iOS 11, *)) { |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyPostFinishedContext( |
| web_state(), url, /*has_user_gesture=*/true, &context, &nav_id, |
| /*renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| |
| // Load request using POST HTTP method. |
| NavigationManager::WebLoadParams params(url); |
| params.post_data = [@"foo" dataUsingEncoding:NSUTF8StringEncoding]; |
| params.extra_headers = @{@"Content-Type" : @"text/html"}; |
| params.transition_type = ui::PageTransition::PAGE_TRANSITION_GENERATED; |
| ASSERT_TRUE(LoadWithParams(params)); |
| ASSERT_TRUE(WaitForWebViewContainingText(web_state(), "foo")); |
| } |
| |
| // Tests successful navigation to a new page with post HTTP method. |
| TEST_P(WebStateObserverTest, RendererInitiatedPostNavigation) { |
| const GURL url = test_server_->GetURL("/form?echoall"); |
| const GURL action = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| ; |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormPage)); |
| |
| // Submit the form using JavaScript. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| WebStatePolicyDecider::RequestInfo form_request_info( |
| ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(form_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPostStartedContext( |
| web_state(), action, /*has_user_gesture=*/false, &context, &nav_id, |
| /*renderer_initiated=*/true)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyPostFinishedContext( |
| web_state(), action, /*has_user_gesture=*/false, &context, &nav_id, |
| /*renderer_initiated=*/true)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(action.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ExecuteJavaScript(@"document.getElementById('form').submit();"); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormFieldValue)); |
| } |
| |
| // Tests successful reload of a page returned for post request. |
| TEST_P(WebStateObserverTest, ReloadPostNavigation) { |
| const GURL url = test_server_->GetURL("/form?echoall"); |
| const GURL action = test_server_->GetURL("/echoall"); |
| |
| // Perform new page navigation. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormPage)); |
| |
| // Submit the form using JavaScript. |
| WebStatePolicyDecider::RequestInfo form_request_info( |
| ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(form_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(action.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ExecuteJavaScript(@"window.document.getElementById('form').submit();"); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormFieldValue)); |
| |
| // Reload the page. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| // ShouldAllowRequest() not called because SlimNavigationManager catches |
| // repost before calling policy decider. |
| } else { |
| WebStatePolicyDecider::RequestInfo form_reload_request_info( |
| ui::PageTransition::PAGE_TRANSITION_RELOAD, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| |
| EXPECT_CALL(*decider_, ShouldAllowRequest( |
| _, RequestInfoMatch(form_reload_request_info))) |
| .WillOnce(Return(true)); |
| } |
| |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPostStartedContext( |
| web_state(), action, /*has_user_gesture=*/true, &context, &nav_id, |
| /*reload_is_renderer_initiated=*/false)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyPostFinishedContext( |
| web_state(), action, /*has_user_gesture=*/true, &context, &nav_id, |
| /*reload_is_renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(action.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| // TODO(crbug.com/700958): ios/web ignores |check_for_repost| flag and current |
| // delegate does not run callback for ShowRepostFormWarningDialog. Clearing |
| // the delegate will allow form resubmission. Remove this workaround (clearing |
| // the delegate, once |check_for_repost| is supported). |
| web_state()->SetDelegate(nullptr); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(action, ^{ |
| navigation_manager()->Reload(ReloadType::NORMAL, |
| false /*check_for_repost*/); |
| })); |
| } |
| |
| // Tests going forward to a page rendered from post response. |
| TEST_P(WebStateObserverTest, ForwardPostNavigation) { |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| const GURL url = test_server_->GetURL("/form?echo"); |
| const GURL action = test_server_->GetURL("/echo"); |
| |
| // Perform new page navigation. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormPage)); |
| |
| // Submit the form using JavaScript. |
| WebStatePolicyDecider::RequestInfo form_request_info( |
| ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(form_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(action.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ExecuteJavaScript(@"window.document.getElementById('form').submit();"); |
| ASSERT_TRUE( |
| WaitForWebViewContainingText(web_state(), testing::kTestFormFieldValue)); |
| |
| // Go Back. |
| WebStatePolicyDecider::RequestInfo back_request_info( |
| static_cast<ui::PageTransition>( |
| ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK | |
| ui::PageTransition::PAGE_TRANSITION_TYPED), |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())).Times(2); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(back_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| } else { |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(back_request_info))) |
| .WillOnce(Return(true)); |
| } |
| |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| if (@available(iOS 12, *)) { |
| // On iOS 11 and earlier, ShouldAllowResponse is not called when going back |
| // after form submission. |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| } |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(url, ^{ |
| navigation_manager()->GoBack(); |
| })); |
| |
| // Go forward. |
| WebStatePolicyDecider::RequestInfo forward_request_info( |
| static_cast<ui::PageTransition>( |
| ui::PageTransition::PAGE_TRANSITION_FORM_SUBMIT | |
| ui::PageTransition::PAGE_TRANSITION_FORWARD_BACK), |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| if (GetWebClient()->IsSlimNavigationManagerEnabled()) { |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())).Times(2); |
| // ShouldAllowRequest() not called on repost. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| } else { |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(forward_request_info))) |
| .WillOnce(Return(true)); |
| } |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPostStartedContext( |
| web_state(), action, /*has_user_gesture=*/true, &context, &nav_id, |
| /*renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyPostFinishedContext( |
| web_state(), action, /*has_user_gesture=*/true, &context, &nav_id, |
| /*renderer_initiated=*/false)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(action.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| // TODO(crbug.com/700958): ios/web ignores |check_for_repost| flag and current |
| // delegate does not run callback for ShowRepostFormWarningDialog. Clearing |
| // the delegate will allow form resubmission. Remove this workaround (clearing |
| // the delegate, once |check_for_repost| is supported). |
| web_state()->SetDelegate(nullptr); |
| ASSERT_TRUE(ExecuteBlockAndWaitForLoad(action, ^{ |
| navigation_manager()->GoForward(); |
| })); |
| } |
| |
| // Tests server redirect navigation. |
| TEST_P(WebStateObserverTest, RedirectNavigation) { |
| const GURL url = test_server_->GetURL("/server-redirect?echoall"); |
| const GURL redirect_url = test_server_->GetURL("/echoall"); |
| |
| // Load url which replies with redirect. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| // Second ShouldAllowRequest call is for redirect_url. |
| WebStatePolicyDecider::RequestInfo expected_redirect_request_info( |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL( |
| *decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext( |
| web_state(), redirect_url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(redirect_url.GetContent())); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle("EmbeddedTestServer - EchoAll")); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| ASSERT_TRUE(LoadUrl(url)); |
| } |
| |
| // Tests download navigation. |
| TEST_P(WebStateObserverTest, DownloadNavigation) { |
| GURL url = test_server_->GetURL("/download"); |
| |
| // Perform download navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce( |
| VerifyDownloadFinishedContext(web_state(), url, &context, &nav_id)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| |
| test::LoadUrl(web_state(), url); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| |
| EXPECT_FALSE(web_state()->GetNavigationManager()->GetPendingItem()); |
| } |
| |
| // Tests failed load after the navigation is sucessfully finished. |
| // TODO(crbug.com/845879): test is flaky (probably since crrev.com/1056203). |
| #if TARGET_IPHONE_SIMULATOR |
| #define MAYBE_FailedLoad FailedLoad |
| #else |
| #define MAYBE_FailedLoad FLAKY_FailedLoad |
| #endif |
| TEST_P(WebStateObserverTest, FLAKY_FailedLoad) { |
| GURL url = test_server_->GetURL("/exabyte_response"); |
| |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext(web_state(), url, /*mime_type=*/"", |
| &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::FAILURE)); |
| test::LoadUrl(web_state(), url); |
| |
| // Server will never stop responding. Wait until the navigation is committed. |
| EXPECT_FALSE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return context && context->HasCommitted(); |
| })); |
| |
| // It this point the navigation should be finished. Shutdown the server and |
| // wait until web state stop loading. |
| ASSERT_TRUE(test_server_->ShutdownAndWaitUntilComplete()); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests rejecting the navigation from ShouldAllowRequest. The load should stop, |
| // but no other callbacks are called. |
| TEST_P(WebStateObserverTest, DisallowRequest) { |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| test::LoadUrl(web_state(), test_server_->GetURL("/echo")); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests rejecting the navigation from ShouldAllowResponse. PageLoaded callback |
| // is not called. |
| TEST_P(WebStateObserverTest, DisallowResponse) { |
| const GURL url = test_server_->GetURL("/echo"); |
| |
| // Perform new page navigation. |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyResponseRejectedFinishedContext(web_state(), url, |
| &context, &nav_id)); |
| test::LoadUrl(web_state(), test_server_->GetURL("/echo")); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests stopping a navigation. Did FinishLoading and PageLoaded are never |
| // called. |
| TEST_P(WebStateObserverTest, StopNavigation) { |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| test::LoadUrl(web_state(), test_server_->GetURL("/hung")); |
| web_state()->Stop(); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests stopping a finished navigation. PageLoaded is never called. |
| TEST_P(WebStateObserverTest, StopFinishedNavigation) { |
| GURL url = test_server_->GetURL("/exabyte_response"); |
| |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)) |
| .WillOnce(VerifyPageStartedContext( |
| web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context, |
| &nav_id)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)) |
| .WillOnce(VerifyNewPageFinishedContext(web_state(), url, /*mime_type=*/"", |
| &context, &nav_id)); |
| EXPECT_CALL(observer_, TitleWasSet(web_state())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| |
| test::LoadUrl(web_state(), url); |
| |
| // Server will never stop responding. Wait until the navigation is committed. |
| EXPECT_FALSE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return context && context->HasCommitted(); |
| })); |
| |
| // Stop the loading. |
| web_state()->Stop(); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| } |
| |
| // Tests that iframe navigation triggers DidChangeBackForwardState. |
| TEST_P(WebStateObserverTest, IframeNavigation) { |
| // LegacyNavigationManager doesn't support iframe navigation history. |
| if (!GetWebClient()->IsSlimNavigationManagerEnabled()) |
| return; |
| |
| // TODO(crbug.com/851119): temporarily disable this failing test. |
| if (GetParam() == TEST_WK_BASED_NAVIGATION_MANAGER) { |
| return; |
| } |
| |
| GURL url = test_server_->GetURL("/iframe_host.html"); |
| |
| // Callbacks due to loading of the main frame. |
| EXPECT_CALL(observer_, DidStartLoading(web_state())); |
| WebStatePolicyDecider::RequestInfo expected_request_info( |
| ui::PageTransition::PAGE_TRANSITION_TYPED, |
| /*target_main_frame=*/true, /*has_user_gesture=*/false); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(expected_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStartNavigation(web_state(), _)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _)); |
| // Callbacks due to initial loading of iframe. |
| WebStatePolicyDecider::RequestInfo iframe_request_info( |
| ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT, |
| /*target_main_frame=*/false, /*has_user_gesture=*/true); |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidStopLoading(web_state())); |
| EXPECT_CALL(observer_, |
| PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS)); |
| |
| test::LoadUrl(web_state(), url); |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state())); |
| |
| // Trigger different-document load in iframe. |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/false)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())); |
| test::TapWebViewElementWithIdInIframe(web_state(), "normal-link"); |
| EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return web_state()->GetNavigationManager()->CanGoBack(); |
| })); |
| id history_length = ExecuteJavaScript(@"history.length;"); |
| ASSERT_NSEQ(@2, history_length); |
| EXPECT_FALSE(web_state()->GetNavigationManager()->CanGoForward()); |
| |
| // Go back to top. |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())) |
| .Times(2); // called once each for canGoBack and canGoForward |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info))) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/false)) |
| .WillOnce(Return(true)); |
| web_state()->GetNavigationManager()->GoBack(); |
| EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return web_state()->GetNavigationManager()->CanGoForward(); |
| })); |
| EXPECT_FALSE(web_state()->GetNavigationManager()->CanGoBack()); |
| |
| // Trigger same-document load in iframe. |
| EXPECT_CALL(*decider_, |
| ShouldAllowRequest(_, RequestInfoMatch(iframe_request_info))) |
| .WillOnce(Return(true)); |
| // ShouldAllowResponse() is not called for same-document navigation. |
| EXPECT_CALL(observer_, DidChangeBackForwardState(web_state())) |
| .Times(2); // called once each for canGoBack and canGoForward |
| test::TapWebViewElementWithIdInIframe(web_state(), "same-page-link"); |
| EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return web_state()->GetNavigationManager()->CanGoBack(); |
| })); |
| EXPECT_FALSE(web_state()->GetNavigationManager()->CanGoForward()); |
| } |
| |
| // Verifies that WebState::CreateWithStorageSession does not call any |
| // WebStateObserver callbacks. |
| TEST_P(WebStateObserverTest, RestoreSession) { |
| // Create session storage. |
| CRWNavigationItemStorage* item = [[CRWNavigationItemStorage alloc] init]; |
| GURL url(test_server_->GetURL("/echo")); |
| item.virtualURL = url; |
| NSArray<CRWNavigationItemStorage*>* item_storages = @[ item ]; |
| |
| // Create the session with storage and add observer. |
| WebState::CreateParams params(GetBrowserState()); |
| CRWSessionStorage* session_storage = [[CRWSessionStorage alloc] init]; |
| session_storage.itemStorages = item_storages; |
| auto web_state = WebState::CreateWithStorageSession(params, session_storage); |
| StrictMock<WebStateObserverMock> observer; |
| ScopedObserver<WebState, WebStateObserver> scoped_observer(&observer); |
| scoped_observer.Add(web_state.get()); |
| |
| NavigationContext* context = nullptr; |
| int32_t nav_id = 0; |
| EXPECT_CALL(observer, DidStartLoading(web_state.get())); |
| EXPECT_CALL(observer, DidStartNavigation(web_state.get(), _)) |
| .WillOnce(VerifyRestorationStartedContext(web_state.get(), url, &context, |
| &nav_id)); |
| EXPECT_CALL(observer, DidFinishNavigation(web_state.get(), _)) |
| .WillOnce(VerifyRestorationFinishedContext( |
| web_state.get(), url, kExpectedMimeType, &context, &nav_id)); |
| EXPECT_CALL(observer, TitleWasSet(web_state.get())) |
| .WillOnce(VerifyTitle(url.GetContent())); |
| EXPECT_CALL(observer, DidStopLoading(web_state.get())); |
| EXPECT_CALL(observer, |
| PageLoaded(web_state.get(), PageLoadCompletionStatus::SUCCESS)); |
| |
| // Trigger the session restoration. |
| NavigationManager* navigation_manager = web_state->GetNavigationManager(); |
| // TODO(crbug.com/873729): The session will not be restored until |
| // LoadIfNecessary call. Fix the bug and replace this call with |
| // SessionStorageBuilder::ExtractSessionState(). |
| navigation_manager->LoadIfNecessary(); |
| |
| // Wait until the session is restored. |
| EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return navigation_manager->GetItemCount() == 1; |
| })); |
| EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ |
| return navigation_manager->GetLastCommittedItem()->GetURL() == url; |
| })); |
| |
| // Wait until the page finishes loading. |
| ASSERT_TRUE(test::WaitForPageToFinishLoading(web_state.get())); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| ProgrammaticWebStateObserverTest, |
| WebStateObserverTest, |
| ::testing::Values( |
| NavigationManagerChoice::TEST_LEGACY_NAVIGATION_MANAGER, |
| NavigationManagerChoice::TEST_WK_BASED_NAVIGATION_MANAGER)); |
| |
| } // namespace web |