blob: b6b06392cd11c7aa6c3474b9032be4f74c5eb409 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/optimization_guide/content/browser/page_text_observer.h"
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "components/optimization_guide/content/mojom/page_text_service.mojom.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
namespace optimization_guide {
namespace {
FrameTextDumpResult MakeFrameDump(mojom::TextDumpEvent event,
content::GlobalRenderFrameHostId rfh_id,
bool amp_frame,
int unique_navigation_id,
const std::u16string& contents) {
return FrameTextDumpResult::Initialize(event, rfh_id, amp_frame,
unique_navigation_id)
.CompleteWithContents(contents);
}
class TestConsumer : public PageTextObserver::Consumer {
public:
TestConsumer() = default;
~TestConsumer() = default;
void Reset() { was_called_ = false; }
void PopulateRequest(uint32_t max_size,
const std::set<mojom::TextDumpEvent>& events,
bool request_amp = false) {
request_ = std::make_unique<PageTextObserver::ConsumerTextDumpRequest>();
request_->max_size = max_size;
request_->events = events;
request_->callback =
base::BindOnce(&TestConsumer::OnGotTextDump, base::Unretained(this));
request_->dump_amp_subframes = request_amp;
}
void WaitForPageText() {
if (result_) {
return;
}
base::RunLoop run_loop;
on_page_text_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
bool was_called() const { return was_called_; }
absl::optional<PageTextDumpResult> result() {
return base::OptionalFromPtr(result_.get());
}
// PageTextObserver::Consumer:
std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest>
MaybeRequestFrameTextDump(content::NavigationHandle* handle) override {
was_called_ = true;
return std::move(request_);
}
private:
void OnGotTextDump(const PageTextDumpResult& result) {
result_ = std::make_unique<PageTextDumpResult>(result);
if (on_page_text_closure_) {
std::move(on_page_text_closure_).Run();
}
}
bool was_called_ = false;
std::unique_ptr<PageTextObserver::ConsumerTextDumpRequest> request_;
base::OnceClosure on_page_text_closure_;
std::unique_ptr<PageTextDumpResult> result_;
};
class FakePageTextService : public mojom::PageTextService {
public:
FakePageTextService() = default;
~FakePageTextService() override = default;
void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::PageTextService>(
std::move(handle)));
if (disconnect_all_) {
receiver_.reset();
}
}
void set_disconnect_all(bool disconnect_all) {
disconnect_all_ = disconnect_all;
}
const std::set<mojom::PageTextDumpRequest>& requests() { return requests_; }
// Sets a sequence of responses to send on the next page dump request for the
// given |event|. If an element has a value, |OnTextDumpChunk| is called with
// the text chunk. If an element does not have a value, |OnChunksEnd| is
// called.
void SetRemoteResponsesForEvent(
mojom::TextDumpEvent event,
const std::vector<absl::optional<std::u16string>> responses) {
responses_.emplace(event, responses);
}
// A single request that matches the given |event| will hang until the class
// is destroyed, or another request for the same event is sent.
void SetEventToHangForver(mojom::TextDumpEvent event) { hang_event_ = event; }
// mojom::PageTextService:
void RequestPageTextDump(
mojom::PageTextDumpRequestPtr request,
mojo::PendingRemote<mojom::PageTextConsumer> consumer) override {
auto responses_iter = responses_.find(request->event);
requests_.emplace(*request);
if (hang_event_ && hang_event_.value() == request->event) {
hung_consumer_remote_.Bind(std::move(consumer));
return;
}
if (responses_iter == responses_.end()) {
return;
}
mojo::Remote<mojom::PageTextConsumer> consumer_remote;
consumer_remote.Bind(std::move(consumer));
for (const absl::optional<std::u16string>& resp : responses_iter->second) {
if (resp) {
consumer_remote->OnTextDumpChunk(*resp);
} else {
consumer_remote->OnChunksEnd();
}
}
}
private:
mojo::AssociatedReceiver<mojom::PageTextService> receiver_{this};
std::set<mojom::PageTextDumpRequest> requests_;
bool disconnect_all_ = false;
// Used to timeout a request.
absl::optional<mojom::TextDumpEvent> hang_event_;
mojo::Remote<mojom::PageTextConsumer> hung_consumer_remote_;
// For each event, a sequence of responses to send on the next page dump
// request. If an element has a value, |OnTextDumpChunk| is called with the
// text chunk. If an element does not have a value, |OnChunksEnd| is called.
std::map<mojom::TextDumpEvent, std::vector<absl::optional<std::u16string>>>
responses_;
};
class TestPageTextObserver : public PageTextObserver {
public:
explicit TestPageTextObserver(content::WebContents* web_contents)
: PageTextObserver(web_contents) {}
~TestPageTextObserver() override = default;
void SetIsOOPIF(content::RenderFrameHost* rfh, bool is_oopif) {
oopif_overrides_.erase(rfh);
oopif_overrides_.emplace(rfh, is_oopif);
}
bool IsOOPIF(content::RenderFrameHost* rfh) const override {
auto iter = oopif_overrides_.find(rfh);
if (iter != oopif_overrides_.end()) {
return iter->second;
}
return false;
}
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override {
// Intentionally do nothing so that subframes can be added in tests.
}
void CallDidFinishLoad() { PageTextObserver::DidFinishLoad(nullptr, GURL()); }
private:
std::map<content::RenderFrameHost*, bool> oopif_overrides_;
};
} // namespace
class PageTextObserverTest : public content::RenderViewHostTestHarness {
public:
PageTextObserverTest() = default;
~PageTextObserverTest() override = default;
// content::RenderViewHostTestHarness:
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
observer_ = std::make_unique<TestPageTextObserver>(web_contents());
ASSERT_TRUE(observer());
}
TestPageTextObserver* observer() const { return observer_.get(); }
private:
std::unique_ptr<TestPageTextObserver> observer_;
};
TEST_F(PageTextObserverTest, ConsumerCalled) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
EXPECT_FALSE(consumer.was_called());
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
}
TEST_F(PageTextObserverTest, ConsumerCalledSameDocument) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
content::NavigationSimulator::CreateRendererInitiated(
GURL("http://test.com/"), main_rfh())
->Commit();
EXPECT_TRUE(consumer.was_called());
consumer.Reset();
content::NavigationSimulator::CreateRendererInitiated(
GURL("http://test.com/#fragment"), main_rfh())
->CommitSameDocument();
observer()->CallDidFinishLoad();
EXPECT_TRUE(consumer.was_called());
}
TEST_F(PageTextObserverTest, ConsumerNotCalledSubframe) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
content::NavigationSimulator::CreateRendererInitiated(
GURL("http://test.com/"), main_rfh())
->Commit();
consumer.Reset();
content::NavigationSimulator::NavigateAndCommitFromDocument(
GURL("http://subframe.com"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
EXPECT_FALSE(consumer.was_called());
}
TEST_F(PageTextObserverTest, ConsumerNotCalledNoCommit) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
auto simulator = content::NavigationSimulator::CreateRendererInitiated(
GURL("http://test.com/document.pdf"), main_rfh());
simulator->Start();
simulator->SetResponseHeaders(base::MakeRefCounted<net::HttpResponseHeaders>(
"HTTP/1.1 204 No Content\r\n"));
simulator->Commit();
EXPECT_FALSE(consumer.was_called());
}
TEST_F(PageTextObserverTest, MojoPlumbingSuccessCase) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"a",
u"b",
u"c",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, CompletedFrameDumpMetrics_Empty) {
base::HistogramTester histogram_tester;
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
ASSERT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageTextDump.FrameDumpLength.FirstLayout", 0, 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.OutstandingRequests.DidFinishLoad", 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDumpCompleted.FirstLayout",
1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDisconnected.FirstLayout",
0);
}
TEST_F(PageTextObserverTest, CompletedFrameDumpMetrics_NotEmpty) {
base::HistogramTester histogram_tester;
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"a",
u"b",
u"c",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
ASSERT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageTextDump.FrameDumpLength.FirstLayout", 3, 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.OutstandingRequests.DidFinishLoad", 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDumpCompleted.FirstLayout",
1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDisconnected.FirstLayout",
0);
}
TEST_F(PageTextObserverTest, DisconnectedFrameDumpMetrics) {
base::HistogramTester histogram_tester;
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.set_disconnect_all(true);
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
ASSERT_TRUE(consumer.was_called());
base::RunLoop().RunUntilIdle();
observer()->CallDidFinishLoad();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.FrameDumpLength.FirstLayout", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.OutstandingRequests.DidFinishLoad", 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDumpCompleted.FirstLayout",
0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.PageTextDump.TimeUntilFrameDisconnected.FirstLayout",
1);
}
TEST_F(PageTextObserverTest, MaxLengthOnChunkBorder) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/3,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(3U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, MaxLengthWithinChunk) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abcd"),
}));
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, MaxLengthWithoutOnEnd) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abcd"),
}));
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, TwoConsumers) {
TestConsumer consumer1;
TestConsumer consumer2;
observer()->AddConsumer(&consumer1);
observer()->AddConsumer(&consumer2);
consumer1.PopulateRequest(/*max_size=*/2,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
consumer2.PopulateRequest(/*max_size=*/3,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"a",
u"b",
u"c",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer1.was_called());
EXPECT_TRUE(consumer2.was_called());
observer()->CallDidFinishLoad();
consumer1.WaitForPageText();
consumer2.WaitForPageText();
ASSERT_TRUE(consumer1.result());
EXPECT_THAT(
consumer1.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
ASSERT_TRUE(consumer2.result());
EXPECT_THAT(
consumer2.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(3U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, RemoveConsumer) {
TestConsumer consumer1;
TestConsumer consumer2;
observer()->AddConsumer(&consumer1);
observer()->AddConsumer(&consumer2);
observer()->RemoveConsumer(&consumer2);
consumer1.PopulateRequest(/*max_size=*/3,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
consumer2.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"a",
u"b",
u"c",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer1.was_called());
EXPECT_FALSE(consumer2.was_called());
observer()->CallDidFinishLoad();
consumer1.WaitForPageText();
ASSERT_TRUE(consumer1.result());
EXPECT_THAT(
consumer1.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
EXPECT_FALSE(consumer2.result());
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(3U, mojom::TextDumpEvent::kFirstLayout),
}));
}
TEST_F(PageTextObserverTest, TwoEventsRequested) {
TestConsumer consumer1;
TestConsumer consumer2;
observer()->AddConsumer(&consumer1);
observer()->AddConsumer(&consumer2);
consumer1.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
consumer2.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFinishedLoad});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
absl::nullopt,
});
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFinishedLoad, {
u"xyz",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer1.was_called());
EXPECT_TRUE(consumer2.was_called());
observer()->CallDidFinishLoad();
consumer1.WaitForPageText();
consumer2.WaitForPageText();
ASSERT_TRUE(consumer1.result());
EXPECT_THAT(
consumer1.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
MakeFrameDump(
mojom::TextDumpEvent::kFinishedLoad, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"xyz"),
}));
EXPECT_EQ(consumer1.result(), consumer2.result());
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFirstLayout),
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFinishedLoad),
}));
}
TEST_F(PageTextObserverTest, AbandonedRequest) {
TestConsumer consumer1;
TestConsumer consumer2;
observer()->AddConsumer(&consumer1);
observer()->AddConsumer(&consumer2);
consumer1.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFirstLayout});
consumer2.PopulateRequest(/*max_size=*/4,
/*events=*/{mojom::TextDumpEvent::kFinishedLoad});
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
absl::nullopt,
});
fake_renderer_service.SetEventToHangForver(
mojom::TextDumpEvent::kFinishedLoad);
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
base::HistogramTester histogram_tester;
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer1.was_called());
EXPECT_TRUE(consumer2.was_called());
observer()->CallDidFinishLoad();
consumer1.WaitForPageText();
consumer2.WaitForPageText();
ASSERT_TRUE(consumer1.result());
EXPECT_THAT(
consumer1.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abc"),
}));
EXPECT_EQ(consumer1.result(), consumer2.result());
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFirstLayout),
mojom::PageTextDumpRequest(4U, mojom::TextDumpEvent::kFinishedLoad),
}));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.PageTextDump.AbandonedRequests", 1, 1);
}
TEST_F(PageTextObserverTest, AMPRequestedOnOOPIF) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(
/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout},
/*request_amp=*/true);
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
// Add an OOPIF subframe.
content::RenderFrameHost* oopif_subframe =
content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
observer()->SetIsOOPIF(oopif_subframe, true);
FakePageTextService subframe_fake_renderer_service;
blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
oopif_subframe->GetRemoteAssociatedInterfaces();
subframe_remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&subframe_fake_renderer_service)));
subframe_fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFinishedLoad, {
u"amp",
absl::nullopt,
});
observer()->RenderFrameCreated(oopif_subframe);
observer()->CallDidFinishLoad();
consumer.WaitForPageText();
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
}));
EXPECT_THAT(subframe_fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(
1024U, mojom::TextDumpEvent::kFinishedLoad),
}));
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFinishedLoad,
oopif_subframe->GetGlobalId(),
/*amp_frame=*/true,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"amp"),
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abcdef"),
}));
}
TEST_F(PageTextObserverTest, AMPNotRequestedOnOOPIF) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(
/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout},
/*request_amp=*/false);
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
// Add an OOPIF subframe.
content::RenderFrameHost* oopif_subframe =
content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
observer()->SetIsOOPIF(oopif_subframe, true);
FakePageTextService subframe_fake_renderer_service;
blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
oopif_subframe->GetRemoteAssociatedInterfaces();
subframe_remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&subframe_fake_renderer_service)));
subframe_fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFinishedLoad, {
u"\n",
u"amp",
absl::nullopt,
});
observer()->RenderFrameCreated(oopif_subframe);
observer()->CallDidFinishLoad();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
}));
EXPECT_TRUE(subframe_fake_renderer_service.requests().empty());
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abcdef"),
}));
}
TEST_F(PageTextObserverTest, AMPRequestedOnNonOOPIF) {
TestConsumer consumer;
observer()->AddConsumer(&consumer);
consumer.PopulateRequest(
/*max_size=*/1024,
/*events=*/{mojom::TextDumpEvent::kFirstLayout},
/*request_amp=*/true);
FakePageTextService fake_renderer_service;
fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFirstLayout, {
u"abc",
u"def",
absl::nullopt,
});
blink::AssociatedInterfaceProvider* remote_interfaces =
main_rfh()->GetRemoteAssociatedInterfaces();
remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&fake_renderer_service)));
content::NavigationSimulator::NavigateAndCommitFromBrowser(
web_contents(), GURL("http://test.com"));
EXPECT_TRUE(consumer.was_called());
// Add a non-OOPIF subframe.
content::RenderFrameHost* subframe =
content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
observer()->SetIsOOPIF(subframe, false);
FakePageTextService subframe_fake_renderer_service;
blink::AssociatedInterfaceProvider* subframe_remote_interfaces =
subframe->GetRemoteAssociatedInterfaces();
subframe_remote_interfaces->OverrideBinderForTesting(
mojom::PageTextService::Name_,
base::BindRepeating(&FakePageTextService::BindPendingReceiver,
base::Unretained(&subframe_fake_renderer_service)));
subframe_fake_renderer_service.SetRemoteResponsesForEvent(
mojom::TextDumpEvent::kFinishedLoad, {
u"\n",
u"amp",
absl::nullopt,
});
observer()->RenderFrameCreated(subframe);
observer()->CallDidFinishLoad();
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
fake_renderer_service.requests(),
::testing::UnorderedElementsAreArray({
mojom::PageTextDumpRequest(1024U, mojom::TextDumpEvent::kFirstLayout),
}));
EXPECT_TRUE(subframe_fake_renderer_service.requests().empty());
ASSERT_TRUE(consumer.result());
EXPECT_THAT(
consumer.result()->frame_results(),
::testing::UnorderedElementsAreArray({
MakeFrameDump(
mojom::TextDumpEvent::kFirstLayout, main_rfh()->GetGlobalId(),
/*amp_frame=*/false,
web_contents()->GetController().GetVisibleEntry()->GetUniqueID(),
u"abcdef"),
}));
}
} // namespace optimization_guide