diff --git a/ios/web/js_messaging/web_frames_manager_impl.h b/ios/web/js_messaging/web_frames_manager_impl.h index 207a2d8c6478f6..0fb92fb3298e4a 100644 --- a/ios/web/js_messaging/web_frames_manager_impl.h +++ b/ios/web/js_messaging/web_frames_manager_impl.h @@ -36,27 +36,13 @@ class WebFramesManagerImpl : public WebFramesManager { explicit WebFramesManagerImpl(WebFramesManagerDelegate& delegate); ~WebFramesManagerImpl() override; - // Adds |frame| to the list of web frames associated with WebState. - // The frame must not be already in the frame manager (the frame manager must - // not have a frame with the same frame ID). If |frame| is a main frame, the - // frame manager must not have a main frame already. - void AddFrame(std::unique_ptr frame); - // Removes the web frame with |frame_id|, if one exists, from the list of - // associated web frames. - // If the frame manager does not contain a frame with this ID, operation is a - // no-op. - void RemoveFrameWithId(const std::string& frame_id); // Removes all web frames from the list of associated web frames. void RemoveAllWebFrames(); + // Broadcasts a (not encrypted) JavaScript message to get the identifiers // and keys of existing frames. void RegisterExistingFrames(); - // WebFramesManager overrides - std::set GetAllWebFrames() override; - WebFrame* GetMainWebFrame() override; - WebFrame* GetFrameWithId(const std::string& frame_id) override; - // Use |message_router| to unregister JS message handlers for |old_web_view| // and register handlers for |new_web_view|. Owner of this class should call // this method whenever associated WKWebView changes. @@ -64,7 +50,24 @@ class WebFramesManagerImpl : public WebFramesManager { WKWebView* new_web_view, CRWWKScriptMessageRouter* message_router); + // WebFramesManager overrides. + std::set GetAllWebFrames() override; + WebFrame* GetMainWebFrame() override; + WebFrame* GetFrameWithId(const std::string& frame_id) override; + private: + // Adds |frame| to the list of web frames associated with WebState and invoke + // |delegate_|.OnWebFrameAvailable with |frame|. The frame must not be already + // in the frame manager (the frame manager must not have a frame with the same + // frame ID). If |frame| is a main frame, the frame manager must not have a + // main frame already. + void AddFrame(std::unique_ptr frame); + // Removes the web frame with |frame_id|, if one exists, from the list of + // associated web frames, and invoke |delegate_|.OnWebFrameUnavailable with + // the web frame. If the frame manager does not contain a frame with + // |frame_id|, operation is a no-op. + void RemoveFrameWithId(const std::string& frame_id); + // Handles FrameBecameAvailable JS message and creates new WebFrame based on // frame info from the message (e.g. ID, encryption key, message counter, // etc.). diff --git a/ios/web/js_messaging/web_frames_manager_impl.mm b/ios/web/js_messaging/web_frames_manager_impl.mm index b0d747f1a6b478..b937a81ece8d51 100644 --- a/ios/web/js_messaging/web_frames_manager_impl.mm +++ b/ios/web/js_messaging/web_frames_manager_impl.mm @@ -33,64 +33,12 @@ RemoveAllWebFrames(); } -void WebFramesManagerImpl::AddFrame(std::unique_ptr frame) { - DCHECK(frame); - DCHECK(!frame->GetFrameId().empty()); - if (frame->IsMainFrame()) { - DCHECK(!main_web_frame_); - main_web_frame_ = frame.get(); - } - DCHECK(web_frames_.count(frame->GetFrameId()) == 0); - std::string frame_id = frame->GetFrameId(); - web_frames_[frame_id] = std::move(frame); -} - -void WebFramesManagerImpl::RemoveFrameWithId(const std::string& frame_id) { - DCHECK(!frame_id.empty()); - // If the removed frame is a main frame, it should be the current one. - DCHECK(web_frames_.count(frame_id) == 0 || - !web_frames_[frame_id]->IsMainFrame() || - main_web_frame_ == web_frames_[frame_id].get()); - if (web_frames_.count(frame_id) == 0) { - return; - } - if (main_web_frame_ && main_web_frame_->GetFrameId() == frame_id) { - main_web_frame_ = nullptr; - } - // The web::WebFrame destructor can call some callbacks that will try to - // access the frame via GetFrameWithId. This can lead to a reentrancy issue - // on |web_frames_|. - // To avoid this issue, keep the frame alive during the map operation and - // destroy it after. - auto keep_frame_alive = std::move(web_frames_[frame_id]); - web_frames_.erase(frame_id); -} - void WebFramesManagerImpl::RemoveAllWebFrames() { while (web_frames_.size()) { RemoveFrameWithId(web_frames_.begin()->first); } } -WebFrame* WebFramesManagerImpl::GetFrameWithId(const std::string& frame_id) { - DCHECK(!frame_id.empty()); - auto web_frames_it = web_frames_.find(frame_id); - return web_frames_it == web_frames_.end() ? nullptr - : web_frames_it->second.get(); -} - -std::set WebFramesManagerImpl::GetAllWebFrames() { - std::set frames; - for (const auto& it : web_frames_) { - frames.insert(it.second.get()); - } - return frames; -} - -WebFrame* WebFramesManagerImpl::GetMainWebFrame() { - return main_web_frame_; -} - void WebFramesManagerImpl::RegisterExistingFrames() { delegate_.GetWebState()->ExecuteJavaScript( base::UTF8ToUTF16("__gCrWeb.message.getExistingFrames();")); @@ -102,6 +50,7 @@ CRWWKScriptMessageRouter* message_router) { DCHECK(old_web_view != new_web_view); if (old_web_view) { + RemoveAllWebFrames(); [message_router removeScriptMessageHandlerForName:kFrameBecameAvailableMessageName webView:old_web_view]; @@ -132,6 +81,65 @@ } } +#pragma mark - WebFramesManager + +std::set WebFramesManagerImpl::GetAllWebFrames() { + std::set frames; + for (const auto& it : web_frames_) { + frames.insert(it.second.get()); + } + return frames; +} + +WebFrame* WebFramesManagerImpl::GetMainWebFrame() { + return main_web_frame_; +} + +WebFrame* WebFramesManagerImpl::GetFrameWithId(const std::string& frame_id) { + DCHECK(!frame_id.empty()); + auto web_frames_it = web_frames_.find(frame_id); + return web_frames_it == web_frames_.end() ? nullptr + : web_frames_it->second.get(); +} + +#pragma mark - Private + +void WebFramesManagerImpl::AddFrame(std::unique_ptr frame) { + DCHECK(frame); + DCHECK(!frame->GetFrameId().empty()); + if (frame->IsMainFrame()) { + DCHECK(!main_web_frame_); + main_web_frame_ = frame.get(); + } + DCHECK(web_frames_.count(frame->GetFrameId()) == 0); + std::string frame_id = frame->GetFrameId(); + web_frames_[frame_id] = std::move(frame); + + delegate_.OnWebFrameAvailable(GetFrameWithId(frame_id)); +} + +void WebFramesManagerImpl::RemoveFrameWithId(const std::string& frame_id) { + DCHECK(!frame_id.empty()); + // If the removed frame is a main frame, it should be the current one. + DCHECK(web_frames_.count(frame_id) == 0 || + !web_frames_[frame_id]->IsMainFrame() || + main_web_frame_ == web_frames_[frame_id].get()); + if (web_frames_.count(frame_id) == 0) { + return; + } + delegate_.OnWebFrameUnavailable(web_frames_[frame_id].get()); + if (main_web_frame_ && main_web_frame_->GetFrameId() == frame_id) { + main_web_frame_ = nullptr; + } + // The web::WebFrame destructor can call some callbacks that will try to + // access the frame via GetFrameWithId. This can lead to a reentrancy issue + // on |web_frames_|. + // To avoid this issue, keep the frame alive during the map operation and + // destroy it after. + auto keep_frame_alive = std::move(web_frames_[frame_id]); + web_frames_.erase(frame_id); +} + void WebFramesManagerImpl::OnFrameBecameAvailable(WKScriptMessage* message) { DCHECK(!delegate_.GetWebState()->IsBeingDestroyed()); // Validate all expected message components because any frame could falsify @@ -172,7 +180,6 @@ } AddFrame(std::move(new_frame)); - delegate_.OnWebFrameAvailable(GetFrameWithId(frame_id)); } } @@ -183,11 +190,7 @@ return; } std::string frame_id = base::SysNSStringToUTF8(message.body); - WebFrame* frame = GetFrameWithId(frame_id); - if (frame) { - delegate_.OnWebFrameUnavailable(frame); - RemoveFrameWithId(frame_id); - } + RemoveFrameWithId(frame_id); } } // namespace diff --git a/ios/web/js_messaging/web_frames_manager_impl_unittest.mm b/ios/web/js_messaging/web_frames_manager_impl_unittest.mm index fd58ce1d85bca8..1cb60336d4b63b 100644 --- a/ios/web/js_messaging/web_frames_manager_impl_unittest.mm +++ b/ios/web/js_messaging/web_frames_manager_impl_unittest.mm @@ -4,8 +4,11 @@ #include "ios/web/js_messaging/web_frames_manager_impl.h" +#import "base/strings/string_number_conversions.h" +#import "base/strings/sys_string_conversions.h" #import "ios/web/js_messaging/crw_wk_script_message_router.h" #include "ios/web/js_messaging/web_frame_impl.h" +#include "ios/web/public/test/fakes/fake_web_frame.h" #import "ios/web/public/test/fakes/test_web_state.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -17,7 +20,10 @@ namespace { -const char kFrameId[] = "1effd8f52a067c8d3a01762d3c41dfd8"; +// Frame ids are base16 string of 128 bit numbers. +const char kMainFrameId[] = "1effd8f52a067c8d3a01762d3c41dfd1"; +const char kFrame1Id[] = "1effd8f52a067c8d3a01762d3c41dfd2"; +const char kFrame2Id[] = "1effd8f52a067c8d3a01762d3c41dfd3"; // A base64 encoded sample key. const char kFrameKey[] = "R7lsXtR74c6R9A9k691gUQ8JAd0be+w//Lntgcbjwrc="; @@ -27,12 +33,6 @@ // Message command sent when a frame is unloading. NSString* const kFrameBecameUnavailableMessageName = @"FrameBecameUnavailable"; -// Returns true if |web_frame| is contained in |frames|. -bool ContainsWebFrame(std::set frames, - web::WebFrame* web_frame) { - return frames.end() != std::find(frames.begin(), frames.end(), web_frame); -} - } // namespace namespace web { @@ -40,7 +40,93 @@ bool ContainsWebFrame(std::set frames, class WebFramesManagerImplTest : public PlatformTest, public WebFramesManagerDelegate { protected: - WebFramesManagerImplTest() : frames_manager_(*this) {} + WebFramesManagerImplTest() + : frames_manager_(*this), + user_content_controller_(OCMClassMock([WKUserContentController class])), + router_([[CRWWKScriptMessageRouter alloc] + initWithUserContentController:user_content_controller_]), + web_view_(OCMClassMock([WKWebView class])), + main_frame_(kMainFrameId, + /*is_main_frame=*/true, + GURL("https://www.main.test")), + frame_1_(kFrame1Id, + /*is_main_frame=*/false, + GURL("https://www.frame1.test")), + frame_2_(kFrame2Id, + /*is_main_frame=*/false, + GURL("https://www.frame2.test")) { + // Notify |frames_manager_| that its associated WKWebView has changed from + // nil to |web_view_|. + frames_manager_.OnWebViewUpdated(nil, web_view_, router_); + } + + // Sends a JS message of a newly loaded web frame to |router_| which will + // dispatch it to |frames_manager_|. + void SendFrameBecameAvailableMessage(const FakeWebFrame& web_frame) { + // Mock WKSecurityOrigin. + WKSecurityOrigin* security_origin = OCMClassMock([WKSecurityOrigin class]); + OCMStub([security_origin host]) + .andReturn( + base::SysUTF8ToNSString(web_frame.GetSecurityOrigin().host())); + OCMStub([security_origin port]) + .andReturn(web_frame.GetSecurityOrigin().EffectiveIntPort()); + OCMStub([security_origin protocol]) + .andReturn( + base::SysUTF8ToNSString(web_frame.GetSecurityOrigin().scheme())); + + // Mock WKFrameInfo. + WKFrameInfo* frame_info = OCMClassMock([WKFrameInfo class]); + OCMStub([frame_info isMainFrame]).andReturn(web_frame.IsMainFrame()); + OCMStub([frame_info securityOrigin]).andReturn(security_origin); + + // Mock WKScriptMessage for "FrameBecameAvailable" message. + NSDictionary* body = @{ + @"crwFrameId" : base::SysUTF8ToNSString(web_frame.GetFrameId()), + @"crwFrameKey" : base::SysUTF8ToNSString(kFrameKey), + @"crwFrameLastReceivedMessageId" : @-1, + }; + WKScriptMessage* message = OCMClassMock([WKScriptMessage class]); + OCMStub([message body]).andReturn(body); + OCMStub([message frameInfo]).andReturn(frame_info); + OCMStub([message name]).andReturn(kFrameBecameAvailableMessageName); + OCMStub([message webView]).andReturn(web_view_); + + [(id)router_ + userContentController:user_content_controller_ + didReceiveScriptMessage:message]; + } + + // Sends a JS message of a newly unloaded web frame to |router_| which will + // dispatch it to |frames_manager_|. + void SendFrameBecameUnavailableMessage(const FakeWebFrame& web_frame) { + // Mock WKSecurityOrigin. + WKSecurityOrigin* security_origin = OCMClassMock([WKSecurityOrigin class]); + OCMStub([security_origin host]) + .andReturn( + base::SysUTF8ToNSString(web_frame.GetSecurityOrigin().host())); + OCMStub([security_origin port]) + .andReturn(web_frame.GetSecurityOrigin().EffectiveIntPort()); + OCMStub([security_origin protocol]) + .andReturn( + base::SysUTF8ToNSString(web_frame.GetSecurityOrigin().scheme())); + + // Mock WKFrameInfo. + WKFrameInfo* frame_info = OCMClassMock([WKFrameInfo class]); + OCMStub([frame_info isMainFrame]).andReturn(web_frame.IsMainFrame()); + OCMStub([frame_info securityOrigin]).andReturn(security_origin); + + // Mock WKScriptMessage for "FrameBecameUnavailable" message. + WKScriptMessage* message = OCMClassMock([WKScriptMessage class]); + OCMStub([message body]) + .andReturn(base::SysUTF8ToNSString(web_frame.GetFrameId())); + OCMStub([message frameInfo]).andReturn(frame_info); + OCMStub([message name]).andReturn(kFrameBecameUnavailableMessageName); + OCMStub([message webView]).andReturn(web_view_); + + [(id)router_ + userContentController:user_content_controller_ + didReceiveScriptMessage:message]; + } // WebFramesManagerDelegate. void OnWebFrameAvailable(WebFrame* frame) override {} @@ -49,232 +135,178 @@ void OnWebFrameUnavailable(WebFrame* frame) override {} TestWebState test_web_state_; WebFramesManagerImpl frames_manager_; + WKUserContentController* user_content_controller_ = nil; + CRWWKScriptMessageRouter* router_ = nil; + WKWebView* web_view_ = nil; + const FakeWebFrame main_frame_; + const FakeWebFrame frame_1_; + const FakeWebFrame frame_2_; }; -// Tests that the WebFrame for the main frame is returned. -TEST_F(WebFramesManagerImplTest, GetMainWebFrame) { - GURL security_origin; - auto web_frame = std::make_unique( - "web_frame", /*is_main_frame=*/true, security_origin, &test_web_state_); - WebFrameImpl* web_frame_ptr = web_frame.get(); - - frames_manager_.AddFrame(std::move(web_frame)); - - EXPECT_EQ(web_frame_ptr, frames_manager_.GetMainWebFrame()); - auto frames = frames_manager_.GetAllWebFrames(); - EXPECT_EQ(1ul, frames.size()); - EXPECT_TRUE(ContainsWebFrame(frames, web_frame_ptr)); -} - -// Tests that the WebFrame returns null if no main frame is known. -TEST_F(WebFramesManagerImplTest, NoMainWebFrame) { - EXPECT_EQ(nullptr, frames_manager_.GetMainWebFrame()); +// Tests main web frame construction/destruction. +TEST_F(WebFramesManagerImplTest, MainWebFrame) { + SendFrameBecameAvailableMessage(main_frame_); - GURL security_origin; - const std::string web_frame_frame_id = "web_frame"; - auto web_frame = - std::make_unique(web_frame_frame_id, /*is_main_frame=*/true, - security_origin, &test_web_state_); - - frames_manager_.AddFrame(std::move(web_frame)); - frames_manager_.RemoveFrameWithId(web_frame_frame_id); + EXPECT_EQ(1ul, frames_manager_.GetAllWebFrames().size()); + WebFrame* main_frame = frames_manager_.GetMainWebFrame(); + WebFrame* main_frame_by_id = + frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + ASSERT_TRUE(main_frame); + ASSERT_TRUE(main_frame_by_id); + EXPECT_EQ(main_frame, main_frame_by_id); + EXPECT_TRUE(main_frame->IsMainFrame()); + EXPECT_EQ(main_frame_.GetSecurityOrigin(), main_frame->GetSecurityOrigin()); - EXPECT_EQ(nullptr, frames_manager_.GetMainWebFrame()); + SendFrameBecameUnavailableMessage(main_frame_); EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); + EXPECT_FALSE(frames_manager_.GetMainWebFrame()); + EXPECT_FALSE(frames_manager_.GetFrameWithId(main_frame_.GetFrameId())); } -// Tests that the WebFramesManagerImpl returns a list of all current WebFrame -// objects. -TEST_F(WebFramesManagerImplTest, AddFrames) { - GURL security_origin; - auto main_web_frame = std::make_unique( - "main_web_frame", - /*is_main_frame=*/true, security_origin, &test_web_state_); - WebFrameImpl* main_web_frame_ptr = main_web_frame.get(); - frames_manager_.AddFrame(std::move(main_web_frame)); - - auto child_web_frame = std::make_unique( - "child_web_frame", - /*is_main_frame=*/false, security_origin, &test_web_state_); - WebFrameImpl* child_web_frame_ptr = child_web_frame.get(); - frames_manager_.AddFrame(std::move(child_web_frame)); - - auto frames = frames_manager_.GetAllWebFrames(); - EXPECT_EQ(2ul, frames.size()); - EXPECT_TRUE(ContainsWebFrame(frames, main_web_frame_ptr)); - EXPECT_TRUE(ContainsWebFrame(frames, child_web_frame_ptr)); +// Tests multiple web frames construction/destruction. +TEST_F(WebFramesManagerImplTest, MultipleWebFrame) { + // Add main frame. + SendFrameBecameAvailableMessage(main_frame_); + EXPECT_EQ(1ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + WebFrame* main_frame = frames_manager_.GetMainWebFrame(); + WebFrame* main_frame_by_id = + frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + ASSERT_TRUE(main_frame); + EXPECT_EQ(main_frame, main_frame_by_id); + EXPECT_TRUE(main_frame->IsMainFrame()); + EXPECT_EQ(main_frame_.GetSecurityOrigin(), main_frame->GetSecurityOrigin()); + + // Add frame 1. + SendFrameBecameAvailableMessage(frame_1_); + EXPECT_EQ(2ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + main_frame = frames_manager_.GetMainWebFrame(); + main_frame_by_id = frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + ASSERT_TRUE(main_frame); + EXPECT_EQ(main_frame, main_frame_by_id); + EXPECT_TRUE(main_frame->IsMainFrame()); + EXPECT_EQ(main_frame_.GetSecurityOrigin(), main_frame->GetSecurityOrigin()); + // Check frame 1. + WebFrame* frame_1 = frames_manager_.GetFrameWithId(frame_1_.GetFrameId()); + ASSERT_TRUE(frame_1); + EXPECT_FALSE(frame_1->IsMainFrame()); + EXPECT_EQ(frame_1_.GetSecurityOrigin(), frame_1->GetSecurityOrigin()); + + // Add frame 2. + SendFrameBecameAvailableMessage(frame_2_); + EXPECT_EQ(3ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + main_frame = frames_manager_.GetMainWebFrame(); + main_frame_by_id = frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + ASSERT_TRUE(main_frame); + EXPECT_EQ(main_frame, main_frame_by_id); + EXPECT_TRUE(main_frame->IsMainFrame()); + EXPECT_EQ(main_frame_.GetSecurityOrigin(), main_frame->GetSecurityOrigin()); + // Check frame 1. + frame_1 = frames_manager_.GetFrameWithId(frame_1_.GetFrameId()); + ASSERT_TRUE(frame_1); + EXPECT_FALSE(frame_1->IsMainFrame()); + EXPECT_EQ(frame_1_.GetSecurityOrigin(), frame_1->GetSecurityOrigin()); + // Check frame 2. + WebFrame* frame_2 = frames_manager_.GetFrameWithId(frame_2_.GetFrameId()); + ASSERT_TRUE(frame_2); + EXPECT_FALSE(frame_2->IsMainFrame()); + EXPECT_EQ(frame_2_.GetSecurityOrigin(), frame_2->GetSecurityOrigin()); + + // Remove frame 1. + SendFrameBecameUnavailableMessage(frame_1_); + EXPECT_EQ(2ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + main_frame = frames_manager_.GetMainWebFrame(); + main_frame_by_id = frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + ASSERT_TRUE(main_frame); + EXPECT_EQ(main_frame, main_frame_by_id); + EXPECT_TRUE(main_frame->IsMainFrame()); + EXPECT_EQ(main_frame_.GetSecurityOrigin(), main_frame->GetSecurityOrigin()); + // Check frame 1. + frame_1 = frames_manager_.GetFrameWithId(frame_1_.GetFrameId()); + EXPECT_FALSE(frame_1); + // Check frame 2. + frame_2 = frames_manager_.GetFrameWithId(frame_2_.GetFrameId()); + ASSERT_TRUE(frame_2); + EXPECT_FALSE(frame_2->IsMainFrame()); + EXPECT_EQ(frame_2_.GetSecurityOrigin(), frame_2->GetSecurityOrigin()); + + // Remove main frame. + SendFrameBecameUnavailableMessage(main_frame_); + EXPECT_EQ(1ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + main_frame = frames_manager_.GetMainWebFrame(); + main_frame_by_id = frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + EXPECT_FALSE(main_frame); + EXPECT_FALSE(main_frame_by_id); + // Check frame 1. + frame_1 = frames_manager_.GetFrameWithId(frame_1_.GetFrameId()); + EXPECT_FALSE(frame_1); + // Check frame 2. + frame_2 = frames_manager_.GetFrameWithId(frame_2_.GetFrameId()); + ASSERT_TRUE(frame_2); + EXPECT_FALSE(frame_2->IsMainFrame()); + EXPECT_EQ(frame_2_.GetSecurityOrigin(), frame_2->GetSecurityOrigin()); + + // Remove frame 2. + SendFrameBecameUnavailableMessage(frame_2_); + EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + main_frame = frames_manager_.GetMainWebFrame(); + main_frame_by_id = frames_manager_.GetFrameWithId(main_frame_.GetFrameId()); + EXPECT_FALSE(main_frame); + EXPECT_FALSE(main_frame_by_id); + // Check frame 1. + frame_1 = frames_manager_.GetFrameWithId(frame_1_.GetFrameId()); + EXPECT_FALSE(frame_1); + // Check frame 2. + frame_2 = frames_manager_.GetFrameWithId(frame_2_.GetFrameId()); + EXPECT_FALSE(frame_2); } -// Tests that the WebFramesManagerImpl correctly removes a WebFrame instance. -TEST_F(WebFramesManagerImplTest, RemoveFrame) { - GURL security_origin; - auto main_web_frame = std::make_unique( - "main_web_frame", - /*is_main_frame=*/true, security_origin, &test_web_state_); - WebFrameImpl* main_web_frame_ptr = main_web_frame.get(); - frames_manager_.AddFrame(std::move(main_web_frame)); - - const std::string child_web_frame_1_frame_id = "child_web_frame_1_frame_id"; - auto child_web_frame_1 = std::make_unique( - child_web_frame_1_frame_id, - /*is_main_frame=*/false, security_origin, &test_web_state_); - frames_manager_.AddFrame(std::move(child_web_frame_1)); - - auto child_web_frame_2 = std::make_unique( - "child_web_frame_2", - /*is_main_frame=*/false, security_origin, &test_web_state_); - WebFrameImpl* child_web_frame_2_ptr = child_web_frame_2.get(); - frames_manager_.AddFrame(std::move(child_web_frame_2)); - - frames_manager_.RemoveFrameWithId(child_web_frame_1_frame_id); - - auto frames = frames_manager_.GetAllWebFrames(); - EXPECT_EQ(2ul, frames.size()); - EXPECT_TRUE(ContainsWebFrame(frames, main_web_frame_ptr)); - EXPECT_TRUE(ContainsWebFrame(frames, child_web_frame_2_ptr)); -} +// Tests WebFramesManagerImpl::RemoveAllWebFrames. +TEST_F(WebFramesManagerImplTest, RemoveAllWebFrames) { + SendFrameBecameAvailableMessage(main_frame_); + SendFrameBecameAvailableMessage(frame_1_); + SendFrameBecameAvailableMessage(frame_2_); + EXPECT_EQ(3ul, frames_manager_.GetAllWebFrames().size()); -// Tests that all frames are removed after a call to |RemoveAllFrames|. -TEST_F(WebFramesManagerImplTest, RemoveAllFrames) { - GURL security_origin; - frames_manager_.AddFrame(std::make_unique( - "main_web_frame", - /*is_main_frame=*/true, security_origin, &test_web_state_)); - frames_manager_.AddFrame(std::make_unique( - "web_frame", - /*is_main_frame=*/false, security_origin, &test_web_state_)); - - ASSERT_EQ(2ul, frames_manager_.GetAllWebFrames().size()); frames_manager_.RemoveAllWebFrames(); - EXPECT_EQ(nullptr, frames_manager_.GetMainWebFrame()); EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); + // Check main frame. + EXPECT_FALSE(frames_manager_.GetMainWebFrame()); + EXPECT_FALSE(frames_manager_.GetFrameWithId(main_frame_.GetFrameId())); + // Check frame 1. + EXPECT_FALSE(frames_manager_.GetFrameWithId(frame_1_.GetFrameId())); + // Check frame 2. + EXPECT_FALSE(frames_manager_.GetFrameWithId(frame_2_.GetFrameId())); } -// Tests that the WebFramesManagerImpl correctly ignores attempted removal of an -// already removed WebFrame. -TEST_F(WebFramesManagerImplTest, RemoveNonexistantFrame) { - GURL security_origin; - const std::string main_web_frame_frame_id = "main_web_frame"; - auto main_web_frame = std::make_unique( - main_web_frame_frame_id, - /*is_main_frame=*/true, security_origin, &test_web_state_); - WebFrameImpl* main_web_frame_ptr = main_web_frame.get(); - - frames_manager_.AddFrame(std::move(main_web_frame)); - auto frames = frames_manager_.GetAllWebFrames(); - EXPECT_EQ(1ul, frames.size()); - EXPECT_TRUE(ContainsWebFrame(frames, main_web_frame_ptr)); - - frames_manager_.RemoveFrameWithId(main_web_frame_frame_id); - EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); - frames_manager_.RemoveFrameWithId(main_web_frame_frame_id); - EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); -} - -// Tests that a WebFrame is correctly returned by its frame id. -TEST_F(WebFramesManagerImplTest, GetFrameWithId) { - GURL security_origin; - - const std::string web_frame_frame_id = "web_frame_frame_id"; - auto web_frame = std::make_unique( - web_frame_frame_id, - /*is_main_frame=*/false, security_origin, &test_web_state_); - WebFrameImpl* web_frame_ptr = web_frame.get(); - frames_manager_.AddFrame(std::move(web_frame)); - - EXPECT_EQ(web_frame_ptr, frames_manager_.GetFrameWithId(web_frame_frame_id)); - EXPECT_EQ(nullptr, frames_manager_.GetFrameWithId("invalid_id")); -} - -// Tests that WebFramesManagerImpl will unregister callbacks for previous -// WKWebView and register callbacks for new WKWebView. Also tests that -// WebFramesManagerImpl creates new WebFrame when receiving -// "FrameBecameAvailable" message and removes WebFrame when receiving -// "FrameBecameUnavailable" message. +// Tests that WebFramesManagerImpl will ignore JS messages from previous +// WKWebView after WebFramesManagerImpl::OnWebViewUpdated is called with a new +// WKWebView, and that all web frames of previous WKWebView are removed. TEST_F(WebFramesManagerImplTest, OnWebViewUpdated) { - // Mock WKUserContentController. - WKUserContentController* user_content_controller = - OCMClassMock([WKUserContentController class]); + SendFrameBecameAvailableMessage(main_frame_); + SendFrameBecameAvailableMessage(frame_1_); + EXPECT_EQ(2ul, frames_manager_.GetAllWebFrames().size()); - // Set up CRWWKScriptMessageRouter. - CRWWKScriptMessageRouter* router = [[CRWWKScriptMessageRouter alloc] - initWithUserContentController:user_content_controller]; - - // Mock WKWebView. - WKWebView* web_view_1 = OCMClassMock([WKWebView class]); + // Notify WebFramesManagerImpl that its associated WKWebView has changed from + // |web_view_| to a new WKWebView. WKWebView* web_view_2 = OCMClassMock([WKWebView class]); + frames_manager_.OnWebViewUpdated(web_view_, web_view_2, router_); - // Mock WKSecurityOrigin. - WKSecurityOrigin* security_origin = OCMClassMock([WKSecurityOrigin class]); - OCMStub([security_origin host]).andReturn(@"www.crw.com"); - OCMStub([security_origin port]).andReturn(443); - OCMStub([security_origin protocol]).andReturn(@"https"); - - // Mock WKFrameInfo. - WKFrameInfo* frame_info = OCMClassMock([WKFrameInfo class]); - OCMStub([frame_info isMainFrame]).andReturn(YES); - OCMStub([frame_info securityOrigin]).andReturn(security_origin); - - // Mock WKScriptMessage for "FrameBecameAvailable" message. - NSDictionary* body = @{ - @"crwFrameId" : [NSString stringWithUTF8String:kFrameId], - @"crwFrameKey" : [NSString stringWithUTF8String:kFrameKey], - @"crwFrameLastReceivedMessageId" : @-1, - }; - WKScriptMessage* available_message = OCMClassMock([WKScriptMessage class]); - OCMStub([available_message body]).andReturn(body); - OCMStub([available_message frameInfo]).andReturn(frame_info); - OCMStub([available_message name]).andReturn(kFrameBecameAvailableMessageName); - OCMStub([available_message webView]).andReturn(web_view_1); - - // Mock WKScriptMessage for "FrameBecameUnavailable" message. - WKScriptMessage* unavailable_message = OCMClassMock([WKScriptMessage class]); - OCMStub([unavailable_message body]) - .andReturn([NSString stringWithUTF8String:kFrameId]); - OCMStub([unavailable_message frameInfo]).andReturn(frame_info); - OCMStub([unavailable_message name]) - .andReturn(kFrameBecameUnavailableMessageName); - OCMStub([unavailable_message webView]).andReturn(web_view_1); - - // Test begin! - - // Tell the manager to change from nil to |web_view_1|. - frames_manager_.OnWebViewUpdated(nil, web_view_1, router); - - // Send the "FrameBecameAvailable" to |router|. - [(id)router - userContentController:user_content_controller - didReceiveScriptMessage:available_message]; - - // Check that the WebFrame for main frame is created. - ASSERT_EQ(1UL, frames_manager_.GetAllWebFrames().size()); - WebFrame* main_frame = frames_manager_.GetMainWebFrame(); - ASSERT_TRUE(main_frame); - EXPECT_EQ(kFrameId, main_frame->GetFrameId()); - EXPECT_TRUE(main_frame->IsMainFrame()); - EXPECT_EQ(GURL("https://www.crw.com"), main_frame->GetSecurityOrigin()); - - // Send the "FrameBecameUnavailable" to |router|. - [(id)router - userContentController:user_content_controller - didReceiveScriptMessage:unavailable_message]; - - // Check that the WebFrame for main frame is removed. - ASSERT_EQ(0UL, frames_manager_.GetAllWebFrames().size()); - ASSERT_FALSE(frames_manager_.GetMainWebFrame()); - - // Tell the manager to change from |web_view_1| to |web_view_2|. - frames_manager_.OnWebViewUpdated(web_view_1, web_view_2, router); - - // Send the "FrameBecameAvailable" of |web_view_1| to |router| again. - [(id)router - userContentController:user_content_controller - didReceiveScriptMessage:available_message]; - - // Check that WebFramesManagerImpl doesn't reply JS messages from previous - // WKWebView. - ASSERT_EQ(0UL, frames_manager_.GetAllWebFrames().size()); - ASSERT_FALSE(frames_manager_.GetMainWebFrame()); + // Send JS message of loaded/unloaded web frames in previous WKWebView (i.e. + // web_view_). |frames_manager_| should have unregistered JS message handlers + // for |web_view_| and removed all web frames, so no web frame should be + // added. + SendFrameBecameAvailableMessage(frame_1_); + EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); + SendFrameBecameAvailableMessage(frame_2_); + EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); + SendFrameBecameUnavailableMessage(frame_1_); + EXPECT_EQ(0ul, frames_manager_.GetAllWebFrames().size()); } } // namespace web diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm index fde7d46c64120b..34cd29563ecd6d 100644 --- a/ios/web/navigation/crw_wk_navigation_handler.mm +++ b/ios/web/navigation/crw_wk_navigation_handler.mm @@ -694,7 +694,7 @@ - (void)webView:(WKWebView*)webView } } - [self removeAllWebFrames]; + self.webStateImpl->GetWebFramesManagerImpl().RemoveAllWebFrames(); // This must be reset at the end, since code above may need information about // the pending load. self.pendingNavigationInfo = nil; @@ -817,7 +817,7 @@ - (void)webView:(WKWebView*)webView [self commitPendingNavigationInfoInWebView:webView]; - [self removeAllWebFrames]; + self.webStateImpl->GetWebFramesManagerImpl().RemoveAllWebFrames(); // This point should closely approximate the document object change, so reset // the list of injected scripts to those that are automatically injected. @@ -1064,7 +1064,7 @@ - (void)webView:(WKWebView*)webView forNavigation:navigation webView:webView provisionalLoad:NO]; - [self removeAllWebFrames]; + self.webStateImpl->GetWebFramesManagerImpl().RemoveAllWebFrames(); _certVerificationErrors->Clear(); [self forgetNullWKNavigation:navigation]; } @@ -1117,7 +1117,7 @@ - (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView { _certVerificationErrors->Clear(); self.webProcessCrashed = YES; - [self removeAllWebFrames]; + self.webStateImpl->GetWebFramesManagerImpl().RemoveAllWebFrames(); [self.delegate navigationHandlerWebProcessDidCrash:self]; } @@ -1970,16 +1970,6 @@ - (void)loadErrorPageForNavigationItem:(web::NavigationItemImpl*)item self.webStateImpl->OnPageLoaded(failingURL, NO); } -// Clears the frames list. -- (void)removeAllWebFrames { - web::WebFramesManagerImpl& framesManager = - self.webStateImpl->GetWebFramesManagerImpl(); - for (auto* frame : framesManager.GetAllWebFrames()) { - self.webStateImpl->OnWebFrameUnavailable(frame); - } - framesManager.RemoveAllWebFrames(); -} - // Resets any state that is associated with a specific document object (e.g., // page interaction tracking). - (void)resetDocumentSpecificState {