blob: ffd61704a7a5b133b8bc4372eccdc11b6e8bab33 [file] [log] [blame]
// Copyright 2014 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_view/browser/guest_view_manager.h"
#include <memory>
#include <utility>
#include "base/test/scoped_feature_list.h"
#include "components/guest_view/browser/guest_view.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "content/public/browser/guest_page_holder.h"
#include "content/public/browser/site_instance.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::WebContents;
using content::WebContentsTester;
namespace guest_view {
namespace {
class StubGuestView : public GuestView<StubGuestView> {
public:
explicit StubGuestView(content::RenderFrameHost* owner_rfh,
std::unique_ptr<WebContents> guest_web_contents)
: GuestView<StubGuestView>(owner_rfh) {
EXPECT_FALSE(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
WebContentsObserver::Observe(guest_web_contents.get());
TakeGuestContentsOwnership(std::move(guest_web_contents));
}
explicit StubGuestView(content::RenderFrameHost* owner_rfh)
: GuestView<StubGuestView>(owner_rfh) {}
StubGuestView(const StubGuestView&) = delete;
StubGuestView& operator=(const StubGuestView&) = delete;
~StubGuestView() override = default;
static const char Type[];
static const GuestViewHistogramValue HistogramValue =
GuestViewHistogramValue::kInvalid;
void AssignNewGuestContents(std::unique_ptr<WebContents> guest_web_contents) {
EXPECT_FALSE(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
ClearOwnedGuestContents();
WebContentsObserver::Observe(guest_web_contents.get());
TakeGuestContentsOwnership(std::move(guest_web_contents));
}
void AssignNewGuestPage() {
EXPECT_TRUE(base::FeatureList::IsEnabled(features::kGuestViewMPArch));
ClearOwnedGuestPage();
std::unique_ptr<content::GuestPageHolder> guest_page_holder =
content::GuestPageHolder::Create(
owner_web_contents(),
content::SiteInstance::Create(browser_context()),
GetGuestPageHolderDelegateWeakPtr());
SetGuestPageHolder(guest_page_holder.get());
TakeGuestPageOwnership(std::move(guest_page_holder));
}
// Stub implementations of GuestViewBase's pure virtual methods:
const char* GetAPINamespace() const override {
ADD_FAILURE();
return "";
}
int GetTaskPrefix() const override {
ADD_FAILURE();
return 0;
}
void MaybeRecreateGuestContents(
content::RenderFrameHost* outer_contents_frame) override {
ADD_FAILURE();
}
void CreateInnerPage(std::unique_ptr<GuestViewBase> owned_this,
scoped_refptr<content::SiteInstance> site_instance,
const base::Value::Dict& create_params,
GuestPageCreatedCallback callback) override {
ADD_FAILURE();
}
bool GuestHandleContextMenu(
content::RenderFrameHost& render_frame_host,
const content::ContextMenuParams& params) override {
return false;
}
};
const char StubGuestView::Type[] = "stubguestview";
class GuestViewManagerTest : public content::RenderViewHostTestHarness,
public testing::WithParamInterface<bool> {
public:
static std::string DescribeParams(
const testing::TestParamInfo<ParamType>& info) {
return info.param ? "MPArch" : "InnerWebContents";
}
GuestViewManagerTest() {
scoped_feature_list_.InitWithFeatureState(features::kGuestViewMPArch,
GetParam());
}
GuestViewManagerTest(const GuestViewManagerTest&) = delete;
GuestViewManagerTest& operator=(const GuestViewManagerTest&) = delete;
~GuestViewManagerTest() override = default;
TestGuestViewManager* CreateManager() {
return factory_.GetOrCreateTestGuestViewManager(
browser_context(), std::make_unique<GuestViewManagerDelegate>());
}
std::unique_ptr<WebContents> CreateWebContents() {
return WebContentsTester::CreateTestWebContents(browser_context(), nullptr);
}
std::unique_ptr<StubGuestView> CreateGuest(
content::RenderFrameHost* owner_rfh) {
if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
std::unique_ptr<StubGuestView> guest =
std::make_unique<StubGuestView>(owner_rfh);
guest->AssignNewGuestPage();
return guest;
} else {
return std::make_unique<StubGuestView>(owner_rfh, CreateWebContents());
}
}
private:
TestGuestViewManagerFactory factory_;
base::test::ScopedFeatureList scoped_feature_list_;
};
} // namespace
INSTANTIATE_TEST_SUITE_P(/* no prefix */,
GuestViewManagerTest,
testing::Bool(),
GuestViewManagerTest::DescribeParams);
TEST_P(GuestViewManagerTest, AddRemove) {
TestGuestViewManager* manager = CreateManager();
std::unique_ptr<WebContents> owned_owner_web_contents(CreateWebContents());
content::RenderFrameHost* owner_rfh =
owned_owner_web_contents->GetPrimaryMainFrame();
EXPECT_EQ(0, manager->last_instance_id_removed());
EXPECT_TRUE(manager->CanUseGuestInstanceID(1));
EXPECT_TRUE(manager->CanUseGuestInstanceID(2));
EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
std::unique_ptr<StubGuestView> guest1(CreateGuest(owner_rfh));
// The test assumes assigned IDs start at 1.
ASSERT_EQ(guest1->guest_instance_id(), 1);
std::unique_ptr<StubGuestView> guest2(CreateGuest(owner_rfh));
std::unique_ptr<StubGuestView> guest3(CreateGuest(owner_rfh));
manager->AddGuest(guest1.get());
manager->AddGuest(guest2.get());
manager->RemoveGuest(guest2.get(), /*invalidate_id=*/true);
// Since we removed 2, it would be an invalid ID.
EXPECT_TRUE(manager->CanUseGuestInstanceID(1));
EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
EXPECT_EQ(0, manager->last_instance_id_removed());
EXPECT_TRUE(manager->CanUseGuestInstanceID(3));
manager->AddGuest(guest3.get());
manager->RemoveGuest(guest1.get(), /*invalidate_id=*/true);
EXPECT_FALSE(manager->CanUseGuestInstanceID(1));
EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
EXPECT_EQ(2, manager->last_instance_id_removed());
manager->RemoveGuest(guest3.get(), /*invalidate_id=*/true);
EXPECT_EQ(3, manager->last_instance_id_removed());
EXPECT_FALSE(manager->CanUseGuestInstanceID(1));
EXPECT_FALSE(manager->CanUseGuestInstanceID(2));
EXPECT_FALSE(manager->CanUseGuestInstanceID(3));
EXPECT_EQ(0u, manager->GetNumRemovedInstanceIDs());
std::unique_ptr<StubGuestView> guest4(
std::make_unique<StubGuestView>(owner_rfh));
std::unique_ptr<StubGuestView> guest5(CreateGuest(owner_rfh));
EXPECT_TRUE(manager->CanUseGuestInstanceID(4));
EXPECT_TRUE(manager->CanUseGuestInstanceID(5));
// Suppose a GuestView (id=4) is created, but never initialized with a guest
// WebContents. We should be able to invalidate the id it used.
manager->AddGuest(guest5.get());
manager->RemoveGuest(guest5.get(), /*invalidate_id=*/true);
EXPECT_EQ(3, manager->last_instance_id_removed());
EXPECT_EQ(1u, manager->GetNumRemovedInstanceIDs());
manager->RemoveGuest(guest4.get(), /*invalidate_id=*/true);
EXPECT_FALSE(manager->CanUseGuestInstanceID(4));
EXPECT_FALSE(manager->CanUseGuestInstanceID(5));
EXPECT_EQ(5, manager->last_instance_id_removed());
EXPECT_EQ(0u, manager->GetNumRemovedInstanceIDs());
}
// This covers the case where a guest needs to recreate its guest WebContents
// before attachment. In this case, the same guest instance ID will be
// associated with different WebContents over time.
TEST_P(GuestViewManagerTest, ReuseIdForRecreatedGuestPage) {
TestGuestViewManager* manager = CreateManager();
std::unique_ptr<WebContents> owned_owner_web_contents(CreateWebContents());
content::RenderFrameHost* owner_rfh =
owned_owner_web_contents->GetPrimaryMainFrame();
EXPECT_EQ(0, manager->last_instance_id_removed());
ASSERT_TRUE(manager->CanUseGuestInstanceID(1));
std::unique_ptr<StubGuestView> guest1(CreateGuest(owner_rfh));
manager->AddGuest(guest1.get());
EXPECT_EQ(1U, manager->GetCurrentGuestCount());
manager->RemoveGuest(guest1.get(), /*invalidate_id=*/false);
EXPECT_EQ(1U, manager->GetCurrentGuestCount());
EXPECT_EQ(0, manager->last_instance_id_removed());
ASSERT_TRUE(manager->CanUseGuestInstanceID(1));
if (base::FeatureList::IsEnabled(features::kGuestViewMPArch)) {
guest1->AssignNewGuestPage();
} else {
guest1->AssignNewGuestContents(CreateWebContents());
}
manager->AddGuest(guest1.get());
EXPECT_EQ(1U, manager->GetCurrentGuestCount());
manager->RemoveGuest(guest1.get(), /*invalidate_id=*/true);
EXPECT_EQ(0U, manager->GetCurrentGuestCount());
EXPECT_EQ(1, manager->last_instance_id_removed());
ASSERT_FALSE(manager->CanUseGuestInstanceID(1));
}
} // namespace guest_view