// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/js_messaging/web_frames_manager_impl.h"
#import "base/strings/string_number_conversions.h"
#import "ios/web/js_messaging/web_frame_impl.h"
#import "ios/web/public/js_messaging/web_frame.h"
#import "ios/web/public/test/fakes/fake_web_frame.h"
#import "ios/web/public/test/web_test_with_web_state.h"
#import "ios/web/web_state/web_state_impl.h"
#import "testing/gtest/include/gtest/gtest.h"
namespace web {
namespace {
const std::string kLowercaseFrameId = "abba1234beef1234cafe1234deed1234";
const std::string kUppercaseFrameId = "ABBA1234BEEF1234CAFE1234DEED1234";
class FakeWebFramesManagerObserver : public WebFramesManagerImpl::Observer {
public:
// The current available frames as tracked by the WebFramesManage Observer
// calls.
const std::map<std::string, WebFrame*> frames() const { return frames_; }
// WebFramesManagerImpl::Observer
void WebFrameBecameAvailable(WebFramesManager* web_frames_manager,
WebFrame* web_frame) override {
frames_[web_frame->GetFrameId()] = web_frame;
}
void WebFrameBecameUnavailable(WebFramesManager* web_frames_manager,
const std::string& frame_id) override {
frames_.erase(frame_id);
}
private:
std::map<std::string, WebFrame*> frames_;
};
} // namespace
class WebFramesManagerImplTest : public WebTestWithWebState {
protected:
void SetUp() override {
WebTestWithWebState::SetUp();
GetPageWorldWebFramesManager().AddObserver(&observer_);
}
void TearDown() override {
GetPageWorldWebFramesManager().RemoveObserver(&observer_);
WebTestWithWebState::TearDown();
}
// Notifies `web_state()` of a newly available `web_frame`.
void SendFrameBecameAvailableMessage(std::unique_ptr<WebFrame> web_frame) {
GetPageWorldWebFramesManager().AddFrame(std::move(web_frame));
}
// Notifies `web_state()` that the web frame with `frame_id` will become
// unavailable.
void SendFrameBecameUnavailableMessage(const std::string& frame_id) {
GetPageWorldWebFramesManager().RemoveFrameWithId(frame_id);
}
WebFramesManagerImpl& GetPageWorldWebFramesManager() {
WebStateImpl* web_state_impl = WebStateImpl::FromWebState(web_state());
return web_state_impl->GetWebFramesManagerImpl(
ContentWorld::kPageContentWorld);
}
protected:
FakeWebFramesManagerObserver observer_;
};
// Tests main web frame construction/destruction.
TEST_F(WebFramesManagerImplTest, MainWebFrame) {
auto frame = FakeWebFrame::Create(kMainFakeFrameId,
/*is_main_frame=*/true,
GURL("https://www.main.test"));
FakeWebFrame* main_frame_ptr = frame.get();
SendFrameBecameAvailableMessage(std::move(frame));
EXPECT_EQ(1ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
WebFrame* main_frame = GetPageWorldWebFramesManager().GetMainWebFrame();
WebFrame* main_frame_by_id =
GetPageWorldWebFramesManager().GetFrameWithId(kMainFakeFrameId);
ASSERT_TRUE(main_frame);
ASSERT_TRUE(main_frame_by_id);
EXPECT_EQ(main_frame, main_frame_by_id);
EXPECT_EQ(main_frame_ptr, main_frame_by_id);
EXPECT_TRUE(main_frame->IsMainFrame());
EXPECT_EQ(main_frame_ptr->GetSecurityOrigin(),
main_frame->GetSecurityOrigin());
const std::map<std::string, WebFrame*> observed_frames = observer_.frames();
ASSERT_EQ(1ul, observed_frames.size());
auto main_frame_it = observed_frames.find(kMainFakeFrameId);
ASSERT_NE(main_frame_it, observed_frames.end());
WebFrame* observed_main_frame = main_frame_it->second;
EXPECT_TRUE(observed_main_frame);
EXPECT_EQ(main_frame, observed_main_frame);
SendFrameBecameUnavailableMessage(kMainFakeFrameId);
EXPECT_EQ(0ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
EXPECT_FALSE(GetPageWorldWebFramesManager().GetMainWebFrame());
EXPECT_FALSE(GetPageWorldWebFramesManager().GetFrameWithId(kMainFakeFrameId));
EXPECT_EQ(0ul, observer_.frames().size());
}
// Tests duplicate registration of the main web frame.
TEST_F(WebFramesManagerImplTest, DuplicateMainWebFrame) {
auto frame = FakeWebFrame::Create(kMainFakeFrameId,
/*is_main_frame=*/true,
GURL("https://www.main.test"));
FakeWebFrame* main_frame_ptr = frame.get();
auto second_main_frame = FakeWebFrame::Create(kChildFakeFrameId,
/*is_main_frame=*/true,
GURL("https://www.main2.test"));
SendFrameBecameAvailableMessage(std::move(frame));
SendFrameBecameAvailableMessage(std::move(second_main_frame));
// Validate that `frame` remains the main frame and `second_main_frame` is
// ignored.
EXPECT_EQ(1ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
WebFrame* main_frame = GetPageWorldWebFramesManager().GetMainWebFrame();
WebFrame* main_frame_by_id =
GetPageWorldWebFramesManager().GetFrameWithId(kMainFakeFrameId);
ASSERT_TRUE(main_frame);
ASSERT_TRUE(main_frame_by_id);
EXPECT_EQ(main_frame, main_frame_by_id);
EXPECT_EQ(main_frame_ptr, main_frame_by_id);
EXPECT_TRUE(main_frame->IsMainFrame());
EXPECT_EQ(main_frame_ptr->GetSecurityOrigin(),
main_frame->GetSecurityOrigin());
const std::map<std::string, WebFrame*> observed_frames = observer_.frames();
ASSERT_EQ(1ul, observed_frames.size());
auto main_frame_it = observed_frames.find(kMainFakeFrameId);
ASSERT_NE(main_frame_it, observed_frames.end());
WebFrame* observed_main_frame = main_frame_it->second;
EXPECT_TRUE(observed_main_frame);
EXPECT_EQ(main_frame, observed_main_frame);
}
// Tests WebStateImpl::RemoveAllWebFrames. Removing all frames must go through
// the web state in order to notify observers.
TEST_F(WebFramesManagerImplTest, RemoveAllWebFrames) {
SendFrameBecameAvailableMessage(FakeWebFrame::Create(
kMainFakeFrameId,
/*is_main_frame=*/true, GURL("https://www.main.test")));
SendFrameBecameAvailableMessage(FakeWebFrame::Create(
kChildFakeFrameId,
/*is_main_frame=*/false, GURL("https://www.frame1.test")));
SendFrameBecameAvailableMessage(FakeWebFrame::Create(
kChildFakeFrameId2,
/*is_main_frame=*/false, GURL("https://www.frame2.test")));
EXPECT_EQ(3ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
WebStateImpl* web_state_impl = WebStateImpl::FromWebState(web_state());
web_state_impl->RemoveAllWebFrames();
EXPECT_EQ(0ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
// Check main frame.
EXPECT_FALSE(GetPageWorldWebFramesManager().GetMainWebFrame());
EXPECT_FALSE(GetPageWorldWebFramesManager().GetFrameWithId(kMainFakeFrameId));
// Check frame 1.
EXPECT_FALSE(
GetPageWorldWebFramesManager().GetFrameWithId(kChildFakeFrameId));
// Check frame 2.
EXPECT_FALSE(
GetPageWorldWebFramesManager().GetFrameWithId(kChildFakeFrameId2));
const std::map<std::string, WebFrame*> observed_frames = observer_.frames();
ASSERT_EQ(0ul, observed_frames.size());
// Check main frame.
auto main_frame_it = observed_frames.find(kMainFakeFrameId);
ASSERT_EQ(main_frame_it, observed_frames.end());
// Check frame 1.
auto frame_1_it = observed_frames.find(kChildFakeFrameId);
ASSERT_EQ(frame_1_it, observed_frames.end());
// Check frame 2.
auto frame_2_it = observed_frames.find(kChildFakeFrameId2);
ASSERT_EQ(frame_2_it, observed_frames.end());
}
// Tests removing a frame which doesn't exist.
TEST_F(WebFramesManagerImplTest, RemoveNonexistantFrame) {
auto frame = FakeWebFrame::Create(kMainFakeFrameId,
/*is_main_frame=*/true,
GURL("https://www.main.test"));
FakeWebFrame* main_frame_ptr = frame.get();
SendFrameBecameAvailableMessage(std::move(frame));
SendFrameBecameUnavailableMessage(kChildFakeFrameId);
EXPECT_EQ(1ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
WebFrame* main_frame = GetPageWorldWebFramesManager().GetMainWebFrame();
WebFrame* main_frame_by_id =
GetPageWorldWebFramesManager().GetFrameWithId(kMainFakeFrameId);
ASSERT_TRUE(main_frame);
ASSERT_TRUE(main_frame_by_id);
EXPECT_EQ(main_frame, main_frame_by_id);
EXPECT_EQ(main_frame_ptr, main_frame_by_id);
EXPECT_TRUE(main_frame->IsMainFrame());
EXPECT_EQ(main_frame_ptr->GetSecurityOrigin(),
main_frame->GetSecurityOrigin());
const std::map<std::string, WebFrame*> observed_frames = observer_.frames();
ASSERT_EQ(1ul, observed_frames.size());
auto main_frame_it = observed_frames.find(kMainFakeFrameId);
ASSERT_NE(main_frame_it, observed_frames.end());
WebFrame* observed_main_frame = main_frame_it->second;
EXPECT_TRUE(observed_main_frame);
EXPECT_EQ(main_frame, observed_main_frame);
}
// Tests that frame lookup is not case-sensitive.
TEST_F(WebFramesManagerImplTest, CaseInsensitiveLookup) {
auto frame = FakeWebFrame::Create(kLowercaseFrameId,
/*is_main_frame=*/true,
GURL("https://www.main.test"));
SendFrameBecameAvailableMessage(std::move(frame));
EXPECT_EQ(1ul, GetPageWorldWebFramesManager().GetAllWebFrames().size());
WebFrame* frame_by_uppercase_id =
GetPageWorldWebFramesManager().GetFrameWithId(kUppercaseFrameId);
EXPECT_TRUE(frame_by_uppercase_id);
WebFrame* frame_by_lowercase_id =
GetPageWorldWebFramesManager().GetFrameWithId(kLowercaseFrameId);
EXPECT_TRUE(frame_by_lowercase_id);
EXPECT_EQ(frame_by_uppercase_id, frame_by_lowercase_id);
}
// By convention, the frame ID should be stored in lowercase internally, even if
// it was passed as uppercase at construct-time.
TEST_F(WebFramesManagerImplTest, CaseInsensitiveConstruct) {
auto frame_with_uppercase_id = FakeWebFrame::Create(
kUppercaseFrameId, /*is_main_frame=*/true, GURL("https://www.main.test"));
SendFrameBecameAvailableMessage(std::move(frame_with_uppercase_id));
WebFrame* frame_by_lowercase_id =
GetPageWorldWebFramesManager().GetFrameWithId(kLowercaseFrameId);
ASSERT_TRUE(frame_by_lowercase_id);
EXPECT_EQ(frame_by_lowercase_id->GetFrameId(), kLowercaseFrameId);
}
} // namespace web