Implement PDF page count signal for contextual cueing
Change-Id: Iea3689885283853f7f62d5c0da0b2829222472bf
Bug: 389750286
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6219781
Reviewed-by: Sophie Chang <[email protected]>
Commit-Queue: Raj T <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1415359}
diff --git a/chrome/browser/contextual_cueing/BUILD.gn b/chrome/browser/contextual_cueing/BUILD.gn
index bea7fe2..ae1a71c 100644
--- a/chrome/browser/contextual_cueing/BUILD.gn
+++ b/chrome/browser/contextual_cueing/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//chrome/common/features.gni")
+import("//pdf/features.gni")
source_set("contextual_cueing") {
sources = [
@@ -24,6 +25,8 @@
sources = [
"contextual_cueing_features.cc",
"contextual_cueing_helper.cc",
+ "contextual_cueing_page_data.cc",
+ "contextual_cueing_page_data.h",
"contextual_cueing_service.cc",
"contextual_cueing_service_factory.cc",
"nudge_cap_tracker.cc",
@@ -37,9 +40,34 @@
"//components/keyed_service/core",
"//components/optimization_guide/core:core",
"//components/optimization_guide/proto:optimization_guide_proto",
+ "//components/pdf/common:constants",
+ "//pdf:buildflags",
"//url",
]
if (enable_glic) {
deps += [ "//chrome/browser/glic" ]
}
+ if (enable_pdf) {
+ deps += [ "//components/pdf/browser" ]
+ }
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "contextual_cueing_helper_unittest.cc",
+ "contextual_cueing_page_data_unittest.cc",
+ "contextual_cueing_service_unittest.cc",
+ "nudge_cap_tracker_unittest.cc",
+ ]
+ deps = [
+ ":contextual_cueing",
+ ":impl",
+ "//base/test:test_support",
+ "//chrome/browser/optimization_guide:test_support",
+ "//chrome/test:test_support",
+ "//pdf:buildflags",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
}
diff --git a/chrome/browser/contextual_cueing/DEPS b/chrome/browser/contextual_cueing/DEPS
index f0bf3d9..48c19d8 100644
--- a/chrome/browser/contextual_cueing/DEPS
+++ b/chrome/browser/contextual_cueing/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+components/keyed_service/core",
+ "+pdf/mojom/pdf.mojom.h",
]
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_features.cc b/chrome/browser/contextual_cueing/contextual_cueing_features.cc
index f85707a9..b88d51a 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_features.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_features.cc
@@ -46,4 +46,9 @@
"VisitedDomainsLimit",
20);
+const base::FeatureParam<base::TimeDelta> kPdfPageCountCaptureDelay(
+ &kContextualCueing,
+ "PdfPageCountCaptureDelay",
+ base::Seconds(4));
+
} // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_features.h b/chrome/browser/contextual_cueing/contextual_cueing_features.h
index a4b74486..e24f492e 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_features.h
+++ b/chrome/browser/contextual_cueing/contextual_cueing_features.h
@@ -38,6 +38,9 @@
// used to implement nudge constraints per-domain per 24 hour period.
extern const base::FeatureParam<int> kVisitedDomainsLimit;
+// The amount of time to wait for capturing the page count for a PDF document.
+extern const base::FeatureParam<base::TimeDelta> kPdfPageCountCaptureDelay;
+
} // namespace contextual_cueing
#endif // CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_FEATURES_H_
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper.cc b/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
index d5068f2b..dbf1a243 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
@@ -8,6 +8,7 @@
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/contextual_cueing/contextual_cueing_enums.h"
#include "chrome/browser/contextual_cueing/contextual_cueing_features.h"
+#include "chrome/browser/contextual_cueing/contextual_cueing_page_data.h"
#include "chrome/browser/contextual_cueing/contextual_cueing_service.h"
#include "chrome/browser/contextual_cueing/contextual_cueing_service_factory.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
@@ -33,29 +34,9 @@
namespace contextual_cueing {
-namespace {
-
-// Returns whether the `config` matches all the current cueing condition.
-bool DidMatchCueingConditions(
- const optimization_guide::proto::GlicCueingConfiguration& config) {
- for (const auto& condition : config.conditions()) {
- switch (condition.signal()) {
- case optimization_guide::proto::
- CONTEXTUAL_CUEING_CLIENT_SIGNAL_UNSPECIFIED:
- case optimization_guide::proto::
- CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT:
- case optimization_guide::proto::
- CONTEXTUAL_CUEING_CLIENT_SIGNAL_CONTENT_LENGTH_WORD_COUNT:
- // TODO: crbug.com/389751174 - Implement checking the client signals.
- return false;
- }
- }
- return true;
-}
-
class ScopedNudgeDecisionRecorder {
public:
- ScopedNudgeDecisionRecorder(
+ explicit ScopedNudgeDecisionRecorder(
optimization_guide::proto::OptimizationType optimization_type)
: optimization_type_(optimization_type) {}
~ScopedNudgeDecisionRecorder() {
@@ -78,8 +59,6 @@
NudgeDecision nudge_decision_ = NudgeDecision::kUnknown;
};
-} // namespace
-
ContextualCueingHelper::ContextualCueingHelper(
content::WebContents* web_contents,
OptimizationGuideKeyedService* ogks,
@@ -122,52 +101,58 @@
}
void ContextualCueingHelper::DocumentOnLoadCompletedInPrimaryMainFrame() {
- last_navigation_cue_label_.clear();
-
auto* glic_nudge_controller = GetGlicNudgeController();
if (!glic_nudge_controller) {
return;
}
- ScopedNudgeDecisionRecorder recorder(
- optimization_guide::proto::GLIC_CONTEXTUAL_CUEING);
- const GURL& url = web_contents()->GetLastCommittedURL();
+ std::unique_ptr<ScopedNudgeDecisionRecorder> recorder =
+ std::make_unique<ScopedNudgeDecisionRecorder>(
+ optimization_guide::proto::GLIC_CONTEXTUAL_CUEING);
optimization_guide::OptimizationMetadata metadata;
auto decision = optimization_guide_keyed_service_->CanApplyOptimization(
- url, optimization_guide::proto::GLIC_CONTEXTUAL_CUEING, &metadata);
- if (decision == optimization_guide::OptimizationGuideDecision::kTrue &&
- !metadata.empty()) {
- auto parsed = metadata.ParsedMetadata<
- optimization_guide::proto::GlicContextualCueingMetadata>();
- if (parsed) {
- for (const auto& config : parsed->cueing_configurations()) {
- if (!config.has_cue_label()) {
- continue;
- }
+ web_contents()->GetLastCommittedURL(),
+ optimization_guide::proto::GLIC_CONTEXTUAL_CUEING, &metadata);
+ if (decision != optimization_guide::OptimizationGuideDecision::kTrue ||
+ metadata.empty()) {
+ recorder->set_nudge_decision(NudgeDecision::kServerDataUnavailable);
+ return;
+ }
+ auto parsed = metadata.ParsedMetadata<
+ optimization_guide::proto::GlicContextualCueingMetadata>();
+ if (!parsed) {
+ recorder->set_nudge_decision(NudgeDecision::kServerDataMalformed);
+ return;
+ }
+ ContextualCueingPageData::CreateForPage(
+ web_contents()->GetPrimaryPage(), std::move(*parsed),
+ base::BindOnce(&ContextualCueingHelper::OnCueingDecision,
+ weak_ptr_factory_.GetWeakPtr(), std::move(recorder)));
+}
- if (DidMatchCueingConditions(config)) {
- last_navigation_cue_label_ = config.cue_label();
- break;
- }
- }
-
- recorder.set_nudge_decision(
- last_navigation_cue_label_.empty()
- ? NudgeDecision::kClientConditionsUnmet
- : contextual_cueing_service_->CanShowNudge(url));
- } else {
- recorder.set_nudge_decision(NudgeDecision::kServerDataMalformed);
- }
- } else {
- recorder.set_nudge_decision(NudgeDecision::kServerDataUnavailable);
+void ContextualCueingHelper::OnCueingDecision(
+ std::unique_ptr<ScopedNudgeDecisionRecorder> decision_recorder,
+ const std::string& cue_label) {
+ CHECK_EQ(NudgeDecision::kUnknown, decision_recorder->nudge_decision());
+ if (ContextualCueingPageData::GetForPage(web_contents()->GetPrimaryPage())) {
+ ContextualCueingPageData::DeleteForPage(web_contents()->GetPrimaryPage());
}
- if (recorder.nudge_decision() != NudgeDecision::kSuccess) {
- // Clear out the label since we didn't show it.
- last_navigation_cue_label_.clear();
+ if (cue_label.empty()) {
+ decision_recorder->set_nudge_decision(
+ NudgeDecision::kClientConditionsUnmet);
+ return;
}
- glic_nudge_controller->UpdateNudgeLabel(
- web_contents(), last_navigation_cue_label_,
+
+ const GURL& url = web_contents()->GetLastCommittedURL();
+ auto can_show_decision = contextual_cueing_service_->CanShowNudge(url);
+ decision_recorder->set_nudge_decision(can_show_decision);
+ if (can_show_decision != NudgeDecision::kSuccess) {
+ return;
+ }
+
+ GetGlicNudgeController()->UpdateNudgeLabel(
+ web_contents(), cue_label,
base::BindRepeating(
&ContextualCueingService::OnNudgeActivity,
contextual_cueing_service_->GetWeakPtr(), url,
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper.h b/chrome/browser/contextual_cueing/contextual_cueing_helper.h
index 525316663..3ae3ba0e 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper.h
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_HELPER_H_
#define CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_HELPER_H_
+#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -18,6 +19,7 @@
namespace contextual_cueing {
class ContextualCueingService;
+class ScopedNudgeDecisionRecorder;
class ContextualCueingHelper
: public content::WebContentsObserver,
@@ -38,15 +40,15 @@
tabs::GlicNudgeController* GetGlicNudgeController();
- const std::string& last_navigation_cue_label() const {
- return last_navigation_cue_label_;
- }
-
private:
ContextualCueingHelper(content::WebContents* contents,
OptimizationGuideKeyedService* ogks,
ContextualCueingService* ccs);
+ void OnCueingDecision(
+ std::unique_ptr<ScopedNudgeDecisionRecorder> decision_recorder,
+ const std::string& cue_label);
+
// Not owned and guaranteed to outlive `this`.
raw_ptr<OptimizationGuideKeyedService> optimization_guide_keyed_service_ =
nullptr;
@@ -54,8 +56,7 @@
// Not owned and guaranteed to outlive `this`.
raw_ptr<ContextualCueingService> contextual_cueing_service_ = nullptr;
- // Holds the cue label for the last navigation in `this`.
- std::string last_navigation_cue_label_;
+ base::WeakPtrFactory<ContextualCueingHelper> weak_ptr_factory_{this};
friend WebContentsUserData<ContextualCueingHelper>;
WEB_CONTENTS_USER_DATA_KEY_DECL();
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_page_data.cc b/chrome/browser/contextual_cueing/contextual_cueing_page_data.cc
new file mode 100644
index 0000000..9aca96d1
--- /dev/null
+++ b/chrome/browser/contextual_cueing/contextual_cueing_page_data.cc
@@ -0,0 +1,144 @@
+// 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 "chrome/browser/contextual_cueing/contextual_cueing_page_data.h"
+
+#include "base/task/single_thread_task_runner.h"
+#include "chrome/browser/contextual_cueing/contextual_cueing_features.h"
+#include "content/public/browser/web_contents.h"
+#include "pdf/buildflags.h"
+
+#if BUILDFLAG(ENABLE_PDF)
+#include "components/pdf/browser/pdf_document_helper.h"
+#endif // BUILDFLAG(ENABLE_PDF)
+
+namespace contextual_cueing {
+
+namespace {
+
+bool DidMatchCueingCondition(
+ const optimization_guide::proto::ContextualCueingConditions& condition,
+ int64_t value) {
+ if (!condition.has_cueing_operator()) {
+ return false;
+ }
+ if (!condition.has_int64_threshold()) {
+ return false;
+ }
+ switch (condition.cueing_operator()) {
+ case optimization_guide::proto::CONTEXTUAL_CUEING_OPERATOR_UNSPECIFIED:
+ return false;
+ case optimization_guide::proto::
+ CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO:
+ return value >= condition.int64_threshold();
+ case optimization_guide::proto::
+ CONTEXTUAL_CUEING_OPERATOR_LESS_THAN_OR_EQUAL_TO:
+ return value <= condition.int64_threshold();
+ }
+}
+
+} // namespace
+
+ContextualCueingPageData::ContextualCueingPageData(
+ content::Page& page,
+ optimization_guide::proto::GlicContextualCueingMetadata metadata,
+ CueingDecisionCallback cueing_decision_callback)
+ : content::PageUserData<ContextualCueingPageData>(page),
+ metadata_(metadata),
+ cueing_decision_callback_(std::move(cueing_decision_callback)) {
+ FindMatchingConfig();
+}
+
+ContextualCueingPageData::~ContextualCueingPageData() = default;
+
+PAGE_USER_DATA_KEY_IMPL(ContextualCueingPageData);
+
+// Attempts to find the matching cueing configuration.
+void ContextualCueingPageData::FindMatchingConfig() {
+ CHECK(cueing_decision_callback_);
+ bool needs_pdf_page_count = false;
+ for (const auto& config : metadata_.cueing_configurations()) {
+ if (!config.has_cue_label()) {
+ continue;
+ }
+ auto decision = DidMatchCueingConditions(config);
+ if (decision == kAllowed) {
+ std::move(cueing_decision_callback_).Run(config.cue_label());
+ return;
+ }
+ if (decision == kNeedsPdfPageCount) {
+ needs_pdf_page_count = true;
+ }
+ }
+ if (needs_pdf_page_count) {
+#if BUILDFLAG(ENABLE_PDF)
+ CHECK_EQ(pdf::kPDFMimeType, page().GetContentsMimeType());
+ base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&ContextualCueingPageData::RequestPdfPageCount,
+ weak_factory_.GetWeakPtr()),
+ kPdfPageCountCaptureDelay.Get());
+ return;
+#endif // BUILDFLAG(ENABLE_PDF)
+ }
+ // None of the config matched, and no client-signals were requested.
+ std::move(cueing_decision_callback_).Run(std::string());
+}
+
+ContextualCueingPageData::CueingConfigurationDecision
+ContextualCueingPageData::DidMatchCueingConditions(
+ const optimization_guide::proto::GlicCueingConfiguration& config) {
+ for (const auto& condition : config.conditions()) {
+ switch (condition.signal()) {
+ case optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_UNSPECIFIED:
+ return kDisallowed;
+ case optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT:
+ if (page().GetContentsMimeType() != pdf::kPDFMimeType) {
+ return kDisallowed;
+ }
+ if (!pdf_page_count_) {
+ return kNeedsPdfPageCount;
+ }
+ return DidMatchCueingCondition(condition, *pdf_page_count_)
+ ? kAllowed
+ : kDisallowed;
+ case optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_CONTENT_LENGTH_WORD_COUNT:
+ // TODO: crbug.com/389751174 - Implement checking the client signals.
+ return kDisallowed;
+ }
+ }
+ return kAllowed;
+}
+
+#if BUILDFLAG(ENABLE_PDF)
+void ContextualCueingPageData::RequestPdfPageCount() {
+ CHECK(page().GetContentsMimeType() == pdf::kPDFMimeType);
+ pdf::PDFDocumentHelper* pdf_helper =
+ pdf::PDFDocumentHelper::MaybeGetForWebContents(
+ content::WebContents::FromRenderFrameHost(&page().GetMainDocument()));
+ if (pdf_helper) {
+ // Fetch zero PDF bytes to just receive the total page count.
+ pdf_helper->GetPdfBytes(
+ /*size_limit=*/0,
+ base::BindOnce(&ContextualCueingPageData::OnPdfPageCountReceived,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void ContextualCueingPageData::OnPdfPageCountReceived(
+ pdf::mojom::PdfListener::GetPdfBytesStatus status,
+ const std::vector<uint8_t>& bytes,
+ uint32_t page_count) {
+ if (status == pdf::mojom::PdfListener::GetPdfBytesStatus::kFailed) {
+ return;
+ }
+ pdf_page_count_ = page_count;
+ FindMatchingConfig();
+}
+#endif // BUILDFLAG(ENABLE_PDF)
+
+} // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_page_data.h b/chrome/browser/contextual_cueing/contextual_cueing_page_data.h
new file mode 100644
index 0000000..0355d79
--- /dev/null
+++ b/chrome/browser/contextual_cueing/contextual_cueing_page_data.h
@@ -0,0 +1,74 @@
+// 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.
+
+#ifndef CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_PAGE_DATA_H_
+#define CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_PAGE_DATA_H_
+
+#include "components/optimization_guide/proto/contextual_cueing_metadata.pb.h"
+#include "components/pdf/common/constants.h"
+#include "content/public/browser/page_user_data.h"
+#include "pdf/buildflags.h"
+#include "pdf/mojom/pdf.mojom.h"
+
+namespace contextual_cueing {
+
+// Decider for contextual cueing that is scoped to `Page`.
+class ContextualCueingPageData
+ : public content::PageUserData<ContextualCueingPageData> {
+ public:
+ using CueingDecisionCallback = base::OnceCallback<void(const std::string&)>;
+
+ ContextualCueingPageData(const ContextualCueingPageData&) = delete;
+ ContextualCueingPageData& operator=(const ContextualCueingPageData&) = delete;
+ ~ContextualCueingPageData() override;
+
+ private:
+ friend class content::PageUserData<ContextualCueingPageData>;
+ friend class ContextualCueingPageDataTest;
+
+ // Holds the cueing condition decision for one cueing configuration.
+ enum CueingConfigurationDecision {
+ kAllowed,
+ kDisallowed,
+ kNeedsPdfPageCount,
+ };
+
+ ContextualCueingPageData(
+ content::Page& page,
+ optimization_guide::proto::GlicContextualCueingMetadata metadata,
+ CueingDecisionCallback cueing_decision_callback);
+
+ // Finds the matching config that passes all the conditions. Requests and
+ // waits for any client-signals when needed.
+ void FindMatchingConfig();
+
+ // Returns whether the `config` matches all the current cueing condition.
+ CueingConfigurationDecision DidMatchCueingConditions(
+ const optimization_guide::proto::GlicCueingConfiguration& config);
+
+#if BUILDFLAG(ENABLE_PDF)
+ void RequestPdfPageCount();
+
+ void OnPdfPageCountReceived(pdf::mojom::PdfListener::GetPdfBytesStatus status,
+ const std::vector<uint8_t>& bytes,
+ uint32_t page_count);
+#endif // BUILDFLAG(ENABLE_PDF)
+
+ const optimization_guide::proto::GlicContextualCueingMetadata metadata_;
+
+ // Holds the page count of PDF. Populated only when the mainframe of the page
+ // has a PDF renderer, and the page count has been successfully retrieved from
+ // it.
+ std::optional<size_t> pdf_page_count_;
+
+ CueingDecisionCallback cueing_decision_callback_;
+
+ base::WeakPtrFactory<ContextualCueingPageData> weak_factory_{this};
+
+ PAGE_USER_DATA_KEY_DECL();
+};
+
+} // namespace contextual_cueing
+
+#endif // CHROME_BROWSER_CONTEXTUAL_CUEING_CONTEXTUAL_CUEING_PAGE_DATA_H_
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_page_data_unittest.cc b/chrome/browser/contextual_cueing/contextual_cueing_page_data_unittest.cc
new file mode 100644
index 0000000..48d0a52
--- /dev/null
+++ b/chrome/browser/contextual_cueing/contextual_cueing_page_data_unittest.cc
@@ -0,0 +1,151 @@
+// 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 "chrome/browser/contextual_cueing/contextual_cueing_page_data.h"
+
+#include "base/test/test_future.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/test_utils.h"
+#include "content/public/test/web_contents_tester.h"
+
+namespace contextual_cueing {
+
+class ContextualCueingPageDataTest : public content::RenderViewHostTestHarness {
+ public:
+ void SetUp() override {
+ content::RenderViewHostTestHarness::SetUp();
+ web_contents_ = CreateTestWebContents();
+ }
+
+ void TearDown() override {
+ web_contents_.reset();
+ content::RenderViewHostTestHarness::TearDown();
+ }
+
+ std::unique_ptr<content::BrowserContext> CreateBrowserContext() override {
+ return std::make_unique<TestingProfile>();
+ }
+
+ void InvokePdfPageCountReceived(size_t page_count) {
+ auto* page_data =
+ ContextualCueingPageData::GetForPage(web_contents_->GetPrimaryPage());
+ page_data->OnPdfPageCountReceived(
+ pdf::mojom::PdfListener::GetPdfBytesStatus::kSuccess, {}, page_count);
+ }
+
+ protected:
+ std::unique_ptr<content::WebContents> web_contents_;
+};
+
+TEST_F(ContextualCueingPageDataTest, Basic) {
+ base::test::TestFuture<const std::string&> future;
+ optimization_guide::proto::GlicContextualCueingMetadata metadata;
+ auto* config = metadata.add_cueing_configurations();
+ config->set_cue_label("basic label");
+
+ ContextualCueingPageData::CreateForPage(web_contents_->GetPrimaryPage(),
+ std::move(metadata),
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
+ EXPECT_EQ("basic label", future.Get());
+}
+
+TEST_F(ContextualCueingPageDataTest, NonPdfPageFails) {
+ base::test::TestFuture<const std::string&> future;
+ optimization_guide::proto::GlicContextualCueingMetadata metadata;
+ auto* config = metadata.add_cueing_configurations();
+ config->set_cue_label("basic label");
+ auto* pdf_condition = config->add_conditions();
+ pdf_condition->set_cueing_operator(
+ optimization_guide::proto::ContextualCueingOperator::
+ CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO);
+ pdf_condition->set_int64_threshold(2);
+
+ ContextualCueingPageData::CreateForPage(web_contents_->GetPrimaryPage(),
+ std::move(metadata),
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
+ EXPECT_TRUE(future.Get().empty());
+}
+
+TEST_F(ContextualCueingPageDataTest, PdfPageCountFails) {
+ content::WebContentsTester::For(web_contents_.get())
+ ->SetMainFrameMimeType(pdf::kPDFMimeType);
+
+ base::test::TestFuture<const std::string&> future;
+ optimization_guide::proto::GlicContextualCueingMetadata metadata;
+ auto* config = metadata.add_cueing_configurations();
+ config->set_cue_label("pdf label");
+ auto* pdf_condition = config->add_conditions();
+ pdf_condition->set_signal(optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT);
+ pdf_condition->set_cueing_operator(
+ optimization_guide::proto::ContextualCueingOperator::
+ CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO);
+ pdf_condition->set_int64_threshold(2);
+
+ ContextualCueingPageData::CreateForPage(web_contents_->GetPrimaryPage(),
+ std::move(metadata),
+ future.GetCallback());
+ InvokePdfPageCountReceived(1);
+
+ ASSERT_TRUE(future.Wait());
+ EXPECT_TRUE(future.Get().empty());
+}
+
+TEST_F(ContextualCueingPageDataTest, PdfPageCountPasses) {
+ content::WebContentsTester::For(web_contents_.get())
+ ->SetMainFrameMimeType(pdf::kPDFMimeType);
+
+ base::test::TestFuture<const std::string&> future;
+ optimization_guide::proto::GlicContextualCueingMetadata metadata;
+ auto* config = metadata.add_cueing_configurations();
+ config->set_cue_label("pdf label");
+ auto* pdf_condition = config->add_conditions();
+ pdf_condition->set_signal(optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT);
+ pdf_condition->set_cueing_operator(
+ optimization_guide::proto::ContextualCueingOperator::
+ CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO);
+ pdf_condition->set_int64_threshold(2);
+
+ ContextualCueingPageData::CreateForPage(web_contents_->GetPrimaryPage(),
+ std::move(metadata),
+ future.GetCallback());
+ InvokePdfPageCountReceived(4);
+
+ ASSERT_TRUE(future.Wait());
+ EXPECT_EQ("pdf label", future.Get());
+}
+
+TEST_F(ContextualCueingPageDataTest, BasicAndPdfPageCountCondition) {
+ content::WebContentsTester::For(web_contents_.get())
+ ->SetMainFrameMimeType(pdf::kPDFMimeType);
+
+ base::test::TestFuture<const std::string&> future;
+ optimization_guide::proto::GlicContextualCueingMetadata metadata;
+ auto* config = metadata.add_cueing_configurations();
+ config->set_cue_label("pdf label");
+ auto* pdf_condition = config->add_conditions();
+ pdf_condition->set_signal(optimization_guide::proto::
+ CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT);
+ pdf_condition->set_cueing_operator(
+ optimization_guide::proto::ContextualCueingOperator::
+ CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO);
+ pdf_condition->set_int64_threshold(2);
+
+ // Second basic condition should get picked.
+ config = metadata.add_cueing_configurations();
+ config->set_cue_label("basic label");
+
+ ContextualCueingPageData::CreateForPage(web_contents_->GetPrimaryPage(),
+ std::move(metadata),
+ future.GetCallback());
+ ASSERT_TRUE(future.Wait());
+ EXPECT_EQ("basic label", future.Get());
+}
+
+} // namespace contextual_cueing