Skip to content

Commit b760288

Browse files
committed
Do not access web state while the CRWWebController is being destroyed.
Bug: 935555 Change-Id: I3ace36ecc6ec84205b90edb57de6c2c993201bec Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1489713 Commit-Queue: Mike Dougherty <[email protected]> Reviewed-by: Eugene But <[email protected]> Cr-Original-Commit-Position: refs/heads/master@{#639194}(cherry picked from commit 43bb9fdd8e623691e6529987777ebe8a5a2ab365) Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1518894 Reviewed-by: Mike Dougherty <[email protected]> Cr-Commit-Position: refs/branch-heads/3729@{#53} Cr-Branched-From: d4a8972-refs/heads/master@{#638880}
1 parent 419f5d5 commit b760288

13 files changed

+219
-7
lines changed

ios/web/public/test/fakes/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ source_set("fakes") {
4242
"fake_navigation_context.mm",
4343
"fake_web_frame.cc",
4444
"fake_web_frame.h",
45+
"fake_web_state_policy_decider.h",
46+
"fake_web_state_policy_decider.mm",
4547
"test_browser_state.cc",
4648
"test_browser_state.h",
4749
"test_java_script_dialog_presenter.h",
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef IOS_WEB_PUBLIC_TEST_FAKES_FAKE_WEB_STATE_POLICY_DECIDER_H_
6+
#define IOS_WEB_PUBLIC_TEST_FAKES_FAKE_WEB_STATE_POLICY_DECIDER_H_
7+
8+
#import "ios/web/public/web_state/web_state.h"
9+
#import "ios/web/public/web_state/web_state_policy_decider.h"
10+
11+
@class NSURLRequest;
12+
@class NSURLResponse;
13+
14+
namespace web {
15+
16+
class FakeWebStatePolicyDecider : public WebStatePolicyDecider {
17+
public:
18+
explicit FakeWebStatePolicyDecider(WebState* web_state);
19+
~FakeWebStatePolicyDecider() override = default;
20+
21+
// Sets the value returned from |ShouldAllowRequest|.
22+
void SetShouldAllowRequest(bool should_allow_request);
23+
24+
// WebStatePolicyDecider overrides
25+
// Returns the value set with |SetShouldAllowRequest|. Defaults to true.
26+
bool ShouldAllowRequest(NSURLRequest* request,
27+
const RequestInfo& request_info) override;
28+
// Always returns true to allow |response|.
29+
bool ShouldAllowResponse(NSURLResponse* response,
30+
bool for_main_frame) override;
31+
void WebStateDestroyed() override {}
32+
33+
private:
34+
bool should_allow_request_ = true;
35+
};
36+
37+
} // namespace web
38+
39+
#endif // IOS_WEB_PUBLIC_TEST_FAKES_FAKE_WEB_STATE_POLICY_DECIDER_H_
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "ios/web/public/test/fakes/fake_web_state_policy_decider.h"
6+
7+
#if !defined(__has_feature) || !__has_feature(objc_arc)
8+
#error "This file requires ARC support."
9+
#endif
10+
11+
namespace web {
12+
13+
FakeWebStatePolicyDecider::FakeWebStatePolicyDecider(WebState* web_state)
14+
: WebStatePolicyDecider(web_state) {}
15+
16+
void FakeWebStatePolicyDecider::SetShouldAllowRequest(
17+
bool should_allow_request) {
18+
should_allow_request_ = should_allow_request;
19+
}
20+
21+
bool FakeWebStatePolicyDecider::ShouldAllowRequest(
22+
NSURLRequest* request,
23+
const RequestInfo& request_info) {
24+
return should_allow_request_;
25+
}
26+
27+
bool FakeWebStatePolicyDecider::ShouldAllowResponse(NSURLResponse* response,
28+
bool for_main_frame) {
29+
return true;
30+
}
31+
32+
} // namespace web

ios/web/public/test/web_test_with_web_state.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ class WebState;
2222
// Base test fixture that provides WebState for testing.
2323
class WebTestWithWebState : public WebTest,
2424
public base::MessageLoop::TaskObserver {
25+
public:
26+
// Destroys underlying WebState. web_state() will return null after this call.
27+
void DestroyWebState();
28+
2529
protected:
2630
explicit WebTestWithWebState(
2731
TestWebThreadBundle::Options = TestWebThreadBundle::Options::DEFAULT);
@@ -60,8 +64,6 @@ class WebTestWithWebState : public WebTest,
6064
void WaitForCondition(ConditionBlock condition);
6165
// Synchronously executes JavaScript and returns result as id.
6266
id ExecuteJavaScript(NSString* script);
63-
// Destroys underlying WebState. web_state() will return null after this call.
64-
void DestroyWebState();
6567

6668
// Returns the base URL of the loaded page.
6769
std::string BaseUrl() const;

ios/web/test/fakes/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ source_set("fakes") {
2323
"crw_fake_nsurl_session_task.mm",
2424
"crw_fake_session_controller_delegate.h",
2525
"crw_fake_session_controller_delegate.mm",
26+
"crw_fake_wk_frame_info.h",
27+
"crw_fake_wk_frame_info.mm",
2628
"crw_fake_wk_navigation_action.h",
2729
"crw_fake_wk_navigation_action.mm",
2830
"crw_fake_wk_navigation_response.h",

ios/web/test/fakes/crw_fake_back_forward_list.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
2222
@property(nullable, nonatomic, copy)
2323
NSArray<WKBackForwardListItem*>* forwardList;
2424
@property(nullable, nonatomic, strong) WKBackForwardListItem* currentItem;
25+
@property(nullable, nonatomic, readonly, strong)
26+
WKBackForwardListItem* backItem;
27+
@property(nullable, nonatomic, readonly, strong)
28+
WKBackForwardListItem* forwardItem;
2529
- (nullable WKBackForwardListItem*)itemAtIndex:(NSInteger)index;
2630

2731
// Resets this instance to simulate a session with no back/forward history, and

ios/web/test/fakes/crw_fake_back_forward_list.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,12 @@ - (NSArray*)mockSublistWithURLArray:(NSArray<NSString*>*)URLs {
8787
return [NSArray arrayWithArray:array];
8888
}
8989

90+
- (WKBackForwardListItem*)backItem {
91+
return self.backList.lastObject;
92+
}
93+
94+
- (WKBackForwardListItem*)forwardItem {
95+
return self.forwardList.firstObject;
96+
}
97+
9098
@end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef IOS_WEB_TEST_FAKES_CRW_FAKE_WK_FRAME_INFO_H_
6+
#define IOS_WEB_TEST_FAKES_CRW_FAKE_WK_FRAME_INFO_H_
7+
8+
#import <WebKit/WebKit.h>
9+
10+
// Fake WKFrameInfo class which can be used for testing.
11+
@interface CRWFakeWKFrameInfo : WKFrameInfo
12+
// Redefined WKNavigationAction properties as readwrite.
13+
@property(nonatomic, readwrite, getter=isMainFrame) BOOL mainFrame;
14+
@property(nonatomic, readwrite, copy) NSURLRequest* request;
15+
@property(nonatomic, readwrite) WKSecurityOrigin* securityOrigin;
16+
@property(nonatomic, readwrite, weak) WKWebView* webView;
17+
@end
18+
19+
#endif // IOS_WEB_TEST_FAKES_CRW_FAKE_WK_FRAME_INFO_H_
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2019 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "ios/web/test/fakes/crw_fake_wk_frame_info.h"
6+
7+
#if !defined(__has_feature) || !__has_feature(objc_arc)
8+
#error "This file requires ARC support."
9+
#endif
10+
11+
@implementation CRWFakeWKFrameInfo
12+
@synthesize mainFrame = _mainFrame;
13+
@synthesize request = _request;
14+
@synthesize securityOrigin = _securityOrigin;
15+
@synthesize webView = _webView;
16+
@end

ios/web/test/web_test_with_web_controller.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class WebTestWithWebController : public WebTestWithWebState {
1818
WebTestWithWebController();
1919
explicit WebTestWithWebController(std::unique_ptr<web::WebClient> web_client);
2020
~WebTestWithWebController() override;
21-
// Returns web controller for testing.
21+
// Returns web controller for testing or null if |web_state()| is null.
2222
CRWWebController* web_controller();
2323
};
2424

ios/web/test/web_test_with_web_controller.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
WebTestWithWebController::~WebTestWithWebController() {}
2323

2424
CRWWebController* WebTestWithWebController::web_controller() {
25+
if (!web_state()) {
26+
return nullptr;
27+
}
2528
return static_cast<web::WebStateImpl*>(web_state())->GetWebController();
2629
}
2730

ios/web/web_state/ui/crw_web_controller.mm

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4480,6 +4480,11 @@ - (void)webView:(WKWebView*)webView
44804480

44814481
allowLoad =
44824482
self.webStateImpl->ShouldAllowRequest(action.request, requestInfo);
4483+
// The WebState may have been closed in the ShouldAllowRequest callback.
4484+
if (_isBeingDestroyed) {
4485+
decisionHandler(WKNavigationActionPolicyCancel);
4486+
return;
4487+
}
44834488
}
44844489

44854490
if (allowLoad) {
@@ -4499,8 +4504,17 @@ - (void)webView:(WKWebView*)webView
44994504
// inserting the webview.
45004505
[self discardNonCommittedItemsIfLastCommittedWasNotNativeView];
45014506

4502-
if (!_isBeingDestroyed && [self shouldClosePageOnNativeApplicationLoad])
4507+
if (!_isBeingDestroyed && [self shouldClosePageOnNativeApplicationLoad]) {
4508+
// Loading was started for user initiated navigations and should be
4509+
// stopped because no other WKWebView callbacks are called.
4510+
// TODO(crbug.com/767092): Loading should not start until
4511+
// webView.loading is changed to YES.
4512+
self.webStateImpl->SetIsLoading(false);
4513+
45034514
self.webStateImpl->CloseWebState();
4515+
decisionHandler(WKNavigationActionPolicyCancel);
4516+
return;
4517+
}
45044518
}
45054519

45064520
if (!_isBeingDestroyed) {
@@ -4562,9 +4576,12 @@ - (void)webView:(WKWebView*)webView
45624576
}));
45634577
}
45644578

4565-
WKNavigationActionPolicy allowPolicy = web::GetAllowNavigationActionPolicy(
4566-
self.webState->GetBrowserState()->IsOffTheRecord());
4567-
decisionHandler(allowLoad ? allowPolicy : WKNavigationActionPolicyCancel);
4579+
if (!allowLoad) {
4580+
decisionHandler(WKNavigationActionPolicyCancel);
4581+
return;
4582+
}
4583+
BOOL isOffTheRecord = self.webState->GetBrowserState()->IsOffTheRecord();
4584+
decisionHandler(web::GetAllowNavigationActionPolicy(isOffTheRecord));
45684585
}
45694586

45704587
- (void)webView:(WKWebView*)webView

ios/web/web_state/ui/crw_web_controller_unittest.mm

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "ios/web/public/features.h"
2727
#include "ios/web/public/referrer.h"
2828
#include "ios/web/public/test/fakes/fake_download_controller_delegate.h"
29+
#import "ios/web/public/test/fakes/fake_web_state_policy_decider.h"
2930
#include "ios/web/public/test/fakes/test_browser_state.h"
3031
#import "ios/web/public/test/fakes/test_native_content.h"
3132
#import "ios/web/public/test/fakes/test_native_content_provider.h"
@@ -41,12 +42,14 @@
4142
#include "ios/web/public/web_state/url_verification_constants.h"
4243
#include "ios/web/public/web_state/web_state_observer.h"
4344
#import "ios/web/test/fakes/crw_fake_back_forward_list.h"
45+
#import "ios/web/test/fakes/crw_fake_wk_frame_info.h"
4446
#import "ios/web/test/fakes/crw_fake_wk_navigation_action.h"
4547
#import "ios/web/test/fakes/crw_fake_wk_navigation_response.h"
4648
#include "ios/web/test/test_url_constants.h"
4749
#import "ios/web/test/web_test_with_web_controller.h"
4850
#import "ios/web/test/wk_web_view_crash_utils.h"
4951
#include "ios/web/web_state/ui/block_universal_links_buildflags.h"
52+
#import "ios/web/web_state/ui/crw_web_controller.h"
5053
#import "ios/web/web_state/ui/crw_web_controller_container_view.h"
5154
#import "ios/web/web_state/ui/web_view_js_utils.h"
5255
#import "ios/web/web_state/ui/wk_navigation_action_policy_util.h"
@@ -224,6 +227,8 @@ void SetWebViewURL(NSString* url_string) {
224227
OCMStub([result setAllowsBackForwardNavigationGestures:NO]);
225228
OCMStub([result setAllowsBackForwardNavigationGestures:YES]);
226229
OCMStub([result isLoading]);
230+
OCMStub([result stopLoading]);
231+
OCMStub([result removeFromSuperview]);
227232

228233
return result;
229234
}
@@ -772,6 +777,10 @@ bool VerifyDecidePolicyForNavigationAction(
772777
[[CRWFakeWKNavigationAction alloc] init];
773778
navigation_action.request = request;
774779

780+
CRWFakeWKFrameInfo* frame_info = [[CRWFakeWKFrameInfo alloc] init];
781+
frame_info.mainFrame = YES;
782+
navigation_action.targetFrame = frame_info;
783+
775784
// Call decidePolicyForNavigationResponse and wait for decisionHandler's
776785
// callback.
777786
__block bool policy_match = false;
@@ -867,6 +876,65 @@ bool VerifyDecidePolicyForNavigationAction(
867876
blob_url_request, WKNavigationActionPolicyAllow));
868877
}
869878

879+
// Tests that navigations which close the WebState cancels the navigation.
880+
// This occurs, for example, when a new page is opened with a link that is
881+
// handled by a native application.
882+
TEST_P(CRWWebControllerPolicyDeciderTest, ClosedWebState) {
883+
static CRWWebControllerPolicyDeciderTest* test_fixture = nullptr;
884+
test_fixture = this;
885+
class FakeWebStateDelegate : public TestWebStateDelegate {
886+
public:
887+
void CloseWebState(WebState* source) override {
888+
test_fixture->DestroyWebState();
889+
}
890+
};
891+
FakeWebStateDelegate delegate;
892+
web_state()->SetDelegate(&delegate);
893+
894+
FakeWebStatePolicyDecider policy_decider(web_state());
895+
policy_decider.SetShouldAllowRequest(false);
896+
897+
NSURL* url =
898+
[NSURL URLWithString:@"https://itunes.apple.com/us/album/american-radio/"
899+
@"1449089454?fbclid=IwAR2NKLvDGH_YY4uZbU7Cj_"
900+
@"e3h7q7DvORCI4Edvi1K9LjcUHfYObmOWl-YgE"];
901+
NSMutableURLRequest* url_request = [NSMutableURLRequest requestWithURL:url];
902+
EXPECT_TRUE(VerifyDecidePolicyForNavigationAction(
903+
url_request, WKNavigationActionPolicyCancel));
904+
}
905+
906+
// Tests that navigations are cancelled if the web state is closed in
907+
// |ShouldAllowRequest|.
908+
TEST_P(CRWWebControllerPolicyDeciderTest, ClosedWebStateInShouldAllowRequest) {
909+
static CRWWebControllerPolicyDeciderTest* test_fixture = nullptr;
910+
test_fixture = this;
911+
912+
class TestWebStatePolicyDecider : public WebStatePolicyDecider {
913+
public:
914+
explicit TestWebStatePolicyDecider(WebState* web_state)
915+
: WebStatePolicyDecider(web_state) {}
916+
~TestWebStatePolicyDecider() override = default;
917+
918+
// WebStatePolicyDecider overrides
919+
bool ShouldAllowRequest(NSURLRequest* request,
920+
const RequestInfo& request_info) override {
921+
test_fixture->DestroyWebState();
922+
return true;
923+
}
924+
bool ShouldAllowResponse(NSURLResponse* response,
925+
bool for_main_frame) override {
926+
return true;
927+
}
928+
void WebStateDestroyed() override {}
929+
};
930+
TestWebStatePolicyDecider policy_decider(web_state());
931+
932+
NSURL* url = [NSURL URLWithString:@(kTestURLString)];
933+
NSMutableURLRequest* url_request = [NSMutableURLRequest requestWithURL:url];
934+
EXPECT_TRUE(VerifyDecidePolicyForNavigationAction(
935+
url_request, WKNavigationActionPolicyCancel));
936+
}
937+
870938
INSTANTIATE_TEST_SUITES(CRWWebControllerPolicyDeciderTest);
871939

872940
// Test fixture for testing CRWWebController presenting native content.

0 commit comments

Comments
 (0)