blob: af13a2d7a48ea20f20ca532048c459991162bc3e [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/guest_contents/browser/guest_contents_handle.h"
#include "base/location.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_renderer_host.h"
namespace guest_contents {
class GuestContentsHandleTest : public content::RenderViewHostTestHarness {
public:
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
guest_web_contents_ = CreateTestWebContents();
content::RenderFrameHostTester::For(
guest_web_contents_->GetPrimaryMainFrame())
->InitializeRenderFrameIfNeeded();
outer_web_contents_ = CreateTestWebContents();
content::RenderFrameHostTester* outer_rfh_tester =
content::RenderFrameHostTester::For(
outer_web_contents_->GetPrimaryMainFrame());
outer_rfh_tester->InitializeRenderFrameIfNeeded();
outer_delegate_rfh_ = outer_rfh_tester->AppendChild("outer_delegate");
ASSERT_TRUE(outer_delegate_rfh_);
}
void TearDown() override {
outer_delegate_rfh_ = nullptr;
guest_web_contents_.reset();
outer_web_contents_.reset();
content::RenderViewHostTestHarness::TearDown();
}
std::unique_ptr<content::WebContents>& guest_web_contents() {
return guest_web_contents_;
}
std::unique_ptr<content::WebContents>& outer_web_contents() {
return outer_web_contents_;
}
content::RenderFrameHost& outer_delegate_rfh() {
return *outer_delegate_rfh_;
}
protected:
std::unique_ptr<content::WebContents> guest_web_contents_;
std::unique_ptr<content::WebContents> outer_web_contents_;
raw_ptr<content::RenderFrameHost> outer_delegate_rfh_;
private:
base::test::ScopedFeatureList scoped_feature_list_{
features::kAttachUnownedInnerWebContents};
};
TEST_F(GuestContentsHandleTest, CreateAndGetById) {
GuestContentsHandle* handle =
GuestContentsHandle::CreateForWebContents(guest_web_contents().get());
ASSERT_TRUE(handle);
EXPECT_EQ(handle,
GuestContentsHandle::FromWebContents(guest_web_contents().get()));
GuestId id = handle->id();
EXPECT_EQ(handle, GuestContentsHandle::FromID(id));
// Test invalid ID
EXPECT_EQ(nullptr, GuestContentsHandle::FromID(id + 1));
EXPECT_EQ(nullptr, GuestContentsHandle::FromID(-1));
// Destroy the WebContents, which should destroy the handle
guest_web_contents().reset();
// Handle should no longer be findable by ID
EXPECT_EQ(nullptr, GuestContentsHandle::FromID(id));
}
TEST_F(GuestContentsHandleTest, UniqueIds) {
GuestContentsHandle* handle1 =
GuestContentsHandle::CreateForWebContents(guest_web_contents().get());
ASSERT_TRUE(handle1);
// Create a second guest WebContents and handle
std::unique_ptr<content::WebContents> guest_web_contents2 =
CreateTestWebContents();
GuestContentsHandle* handle2 =
GuestContentsHandle::CreateForWebContents(guest_web_contents2.get());
ASSERT_TRUE(handle2);
EXPECT_NE(handle1->id(), handle2->id());
// Clean up second guest
guest_web_contents2.reset();
// First handle should still exist
EXPECT_EQ(handle1, GuestContentsHandle::FromID(handle1->id()));
}
TEST_F(GuestContentsHandleTest, AttachAndDetach) {
GuestContentsHandle* handle =
GuestContentsHandle::CreateForWebContents(guest_web_contents().get());
ASSERT_TRUE(handle);
handle->AttachToOuterWebContents(&outer_delegate_rfh());
EXPECT_EQ(guest_web_contents()->GetOuterWebContents(),
outer_web_contents().get());
EXPECT_TRUE(base::Contains(outer_web_contents()->GetInnerWebContents(),
guest_web_contents().get()));
handle->DetachFromOuterWebContents();
EXPECT_EQ(nullptr, guest_web_contents()->GetOuterWebContents());
EXPECT_TRUE(outer_web_contents()->GetInnerWebContents().empty());
}
TEST_F(GuestContentsHandleTest, DestroyGuestContents) {
GuestContentsHandle* handle =
GuestContentsHandle::CreateForWebContents(guest_web_contents().get());
ASSERT_TRUE(handle);
handle->AttachToOuterWebContents(&outer_delegate_rfh());
EXPECT_EQ(guest_web_contents()->GetOuterWebContents(),
outer_web_contents().get());
EXPECT_TRUE(base::Contains(outer_web_contents()->GetInnerWebContents(),
guest_web_contents().get()));
// Destroy the guest WebContents while it is still attached to the outer
// WebContents.
guest_web_contents().reset();
EXPECT_TRUE(outer_web_contents()->GetInnerWebContents().empty());
}
TEST_F(GuestContentsHandleTest, DestroyOuterWebContents) {
GuestContentsHandle* handle =
GuestContentsHandle::CreateForWebContents(guest_web_contents().get());
ASSERT_TRUE(handle);
handle->AttachToOuterWebContents(&outer_delegate_rfh());
EXPECT_EQ(guest_web_contents()->GetOuterWebContents(),
outer_web_contents().get());
// Destroy the outer WebContents while it still has a guest WebContents.
outer_delegate_rfh_ = nullptr;
outer_web_contents().reset();
EXPECT_EQ(nullptr, guest_web_contents()->GetOuterWebContents());
}
} // namespace guest_contents