diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index a4260ae9f6bda..e24dba63b21f7 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -331,7 +331,6 @@
#define IDC_CONTENT_CONTEXT_SPELLING_SUGGESTION 50157
#define IDC_CONTENT_CONTEXT_SPELLING_TOGGLE 50158
#define IDC_CONTENT_CONTEXT_OPEN_IN_READ_ANYTHING 50159
-#define IDC_CONTENT_CONTEXT_RUN_PDF_OCR 50160
#define IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE 50161
#define IDC_CONTENT_CONTEXT_RELOAD_PACKAGED_APP 50162
#define IDC_CONTENT_CONTEXT_RESTART_PACKAGED_APP 50163
@@ -454,7 +453,12 @@
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
// Screen AI Visual Annotations.
#define IDC_RUN_SCREEN_AI_VISUAL_ANNOTATIONS 52420
-#endif
+#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+
+// PDF OCR
+#define IDC_CONTENT_CONTEXT_PDF_OCR 52421
+#define IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS 52422
+#define IDC_CONTENT_CONTEXT_PDF_OCR_ONCE 52423
// Tab Search
#define IDC_TAB_SEARCH 52500
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 57a0a820fc35d..da40975d68b06 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -577,9 +577,6 @@ are declared in tools/grit/grit_rule.gni.
Open in Reader
-
- Recognize P&DF text
-
&Reload
@@ -749,6 +746,15 @@ are declared in tools/grit/grit_rule.gni.
Just once
+
+ Convert image to text
+
+
+ Always
+
+
+ Just once
+
Use enhanced spell check
@@ -829,9 +835,6 @@ are declared in tools/grit/grit_rule.gni.
Open in Reader
-
- Recognize P&DF text
-
&Reload
@@ -1012,6 +1015,15 @@ are declared in tools/grit/grit_rule.gni.
Just Once
+
+ Convert image to text
+
+
+ Always
+
+
+ Just once
+
Use Enhanced Spell Check
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION.png.sha1
new file mode 100644
index 0000000000000..10d0634346134
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION.png.sha1
@@ -0,0 +1 @@
+de02f6a2f36ad4994dbee7b7775b7dd264249365
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ALWAYS.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ALWAYS.png.sha1
new file mode 100644
index 0000000000000..7da00702f248a
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ALWAYS.png.sha1
@@ -0,0 +1 @@
+12a00036bde11df37a6c08ef9d9a4a3f60e5aa7d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ONCE.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ONCE.png.sha1
new file mode 100644
index 0000000000000..b935a149204f3
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ONCE.png.sha1
@@ -0,0 +1 @@
+6101413baa79ef7b1d10990bb533dd704b27003b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_RUN_PDF_OCR.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_RUN_PDF_OCR.png.sha1
deleted file mode 100644
index 43c18956f1900..0000000000000
--- a/chrome/app/generated_resources_grd/IDS_CONTENT_CONTEXT_RUN_PDF_OCR.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-718d7b708db36a99b1427ca1490283eed7c64215
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 35d51aef0e930..9b13c0906f7de 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4067,6 +4067,8 @@ static_library("browser") {
"renderer_context_menu/accessibility_labels_menu_observer.h",
"renderer_context_menu/link_to_text_menu_observer.cc",
"renderer_context_menu/link_to_text_menu_observer.h",
+ "renderer_context_menu/pdf_ocr_menu_observer.cc",
+ "renderer_context_menu/pdf_ocr_menu_observer.h",
"renderer_context_menu/render_view_context_menu.cc",
"renderer_context_menu/render_view_context_menu.h",
"renderer_context_menu/spelling_bubble_model.cc",
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 829d5faa9433f..ff5ff5315aed2 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1686,6 +1686,12 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry,
#endif
registry->RegisterTimePref(prefs::kDIPSTimerLastUpdate, base::Time());
+
+#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+ registry->RegisterBooleanPref(
+ prefs::kAccessibilityPdfOcrAlwaysActive, false,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
}
void RegisterUserProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
index 9d864a0332316..fad50ac2222f5 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
@@ -236,6 +236,28 @@ void MockRenderViewContextMenu::AddAccessibilityLabelsServiceItem(
}
}
+void MockRenderViewContextMenu::AddPdfOcrMenuItem(bool is_checked) {
+ if (is_checked) {
+ AddCheckItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR,
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION));
+ } else {
+ ui::SimpleMenuModel pdf_ocr_submenu_model_(this);
+ pdf_ocr_submenu_model_.AddItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS,
+ l10n_util::GetStringUTF16(
+ IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ALWAYS));
+ pdf_ocr_submenu_model_.AddItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR_ONCE,
+ l10n_util::GetStringUTF16(
+ IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ONCE));
+ AddSubMenu(
+ IDC_CONTENT_CONTEXT_PDF_OCR,
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION),
+ &pdf_ocr_submenu_model_);
+ }
+}
+
content::RenderViewHost* MockRenderViewContextMenu::GetRenderViewHost() const {
return nullptr;
}
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
index bbfdb48353717..cadf031956984 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
@@ -79,6 +79,7 @@ class MockRenderViewContextMenu : public ui::SimpleMenuModel::Delegate,
void RemoveSeparatorBeforeMenuItem(int command_id) override;
void AddSpellCheckServiceItem(bool is_checked) override;
void AddAccessibilityLabelsServiceItem(bool is_checked) override;
+ void AddPdfOcrMenuItem(bool is_checked) override;
content::RenderViewHost* GetRenderViewHost() const override;
content::BrowserContext* GetBrowserContext() const override;
content::WebContents* GetWebContents() const override;
diff --git a/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.cc b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.cc
new file mode 100644
index 0000000000000..0320f3f00c6bc
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.cc
@@ -0,0 +1,107 @@
+// Copyright 2022 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/renderer_context_menu/pdf_ocr_menu_observer.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/accessibility/accessibility_state_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/context_menu_params.h"
+#include "ui/accessibility/accessibility_features.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// Whether the PDF OCR menu item should be shown in the menu. It now depends on
+// whether a screen reader is running.
+bool ShouldShowPdfOcrMenuItem() {
+ return accessibility_state_utils::IsScreenReaderEnabled() &&
+ features::IsPdfOcrEnabled();
+}
+
+} // namespace
+
+PdfOcrMenuObserver::PdfOcrMenuObserver(RenderViewContextMenuProxy* proxy)
+ : proxy_(proxy) {}
+
+PdfOcrMenuObserver::~PdfOcrMenuObserver() = default;
+
+void PdfOcrMenuObserver::InitMenu(const content::ContextMenuParams& params) {
+ Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
+ DCHECK(profile != nullptr);
+ if (ShouldShowPdfOcrMenuItem()) {
+ proxy_->AddPdfOcrMenuItem(profile->GetPrefs()->GetBoolean(
+ prefs::kAccessibilityPdfOcrAlwaysActive));
+ }
+}
+
+bool PdfOcrMenuObserver::IsCommandIdSupported(int command_id) {
+ return command_id == IDC_CONTENT_CONTEXT_PDF_OCR ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ONCE;
+}
+
+bool PdfOcrMenuObserver::IsCommandIdChecked(int command_id) {
+ DCHECK(IsCommandIdSupported(command_id));
+ Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
+ DCHECK(profile != nullptr);
+ if (command_id == IDC_CONTENT_CONTEXT_PDF_OCR ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ONCE) {
+ return profile->GetPrefs()->GetBoolean(
+ prefs::kAccessibilityPdfOcrAlwaysActive);
+ }
+ return false;
+}
+
+bool PdfOcrMenuObserver::IsCommandIdEnabled(int command_id) {
+ DCHECK(IsCommandIdSupported(command_id));
+ if (command_id == IDC_CONTENT_CONTEXT_PDF_OCR ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS ||
+ command_id == IDC_CONTENT_CONTEXT_PDF_OCR_ONCE) {
+ return ShouldShowPdfOcrMenuItem();
+ }
+ return false;
+}
+
+void PdfOcrMenuObserver::ExecuteCommand(int command_id) {
+ DCHECK(IsCommandIdSupported(command_id));
+ Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
+ DCHECK(profile != nullptr);
+ bool is_always_active =
+ profile->GetPrefs()->GetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive);
+ switch (command_id) {
+ case IDC_CONTENT_CONTEXT_PDF_OCR:
+ // If the user has selected to make PDF OCR always active, we directly
+ // update the profile and change it to the original menu item when the
+ // user disables this item.
+ DCHECK(is_always_active);
+ profile->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive,
+ false);
+ // TODO(crbug.com/1393069): Stop the PDF OCR if running.
+ NOTIMPLEMENTED() << "Need to stop PDF OCR accordingly";
+ break;
+ case IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS:
+ // When a user choose "Always" to run the PDF OCR, we save this
+ // preference and change this item to a check item in the context menu.
+ if (!is_always_active) {
+ profile->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive,
+ true);
+ // TODO(crbug.com/1393069): Start the PDF OCR if true is set.
+ NOTIMPLEMENTED() << "Need to start PDF OCR accordingly";
+ }
+ break;
+ case IDC_CONTENT_CONTEXT_PDF_OCR_ONCE:
+ // TODO(crbug.com/1393069): Run PDF OCR once to convert image to text.
+ NOTIMPLEMENTED() << "Need to run PDF OCR once to convert image to text";
+ break;
+ default:
+ NOTREACHED();
+ }
+}
diff --git a/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.h b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.h
new file mode 100644
index 0000000000000..f8f1e5f5a2913
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.h
@@ -0,0 +1,41 @@
+// Copyright 2022 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_RENDERER_CONTEXT_MENU_PDF_OCR_MENU_OBSERVER_H_
+#define CHROME_BROWSER_RENDERER_CONTEXT_MENU_PDF_OCR_MENU_OBSERVER_H_
+
+#include
+#include
+
+#include "base/memory/raw_ptr.h"
+#include "components/prefs/pref_member.h"
+#include "components/renderer_context_menu/render_view_context_menu_observer.h"
+
+class RenderViewContextMenuProxy;
+
+// An observer that listens to events from the RenderViewContextMenu class and
+// shows the PDF OCR menu if a screen reader is enabled.
+class PdfOcrMenuObserver : public RenderViewContextMenuObserver {
+ public:
+ explicit PdfOcrMenuObserver(RenderViewContextMenuProxy* proxy);
+
+ PdfOcrMenuObserver(const PdfOcrMenuObserver&) = delete;
+ PdfOcrMenuObserver& operator=(const PdfOcrMenuObserver&) = delete;
+
+ ~PdfOcrMenuObserver() override;
+
+ // RenderViewContextMenuObserver implementation.
+ void InitMenu(const content::ContextMenuParams& params) override;
+ bool IsCommandIdSupported(int command_id) override;
+ bool IsCommandIdChecked(int command_id) override;
+ bool IsCommandIdEnabled(int command_id) override;
+ void ExecuteCommand(int command_id) override;
+
+ private:
+ // The interface to add a context-menu item and update it. This class uses
+ // this interface to avoid accessing context-menu items directly.
+ raw_ptr proxy_;
+};
+
+#endif // CHROME_BROWSER_RENDERER_CONTEXT_MENU_PDF_OCR_MENU_OBSERVER_H_
diff --git a/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer_browsertest.cc b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer_browsertest.cc
new file mode 100644
index 0000000000000..106aec19eddd7
--- /dev/null
+++ b/chrome/browser/renderer_context_menu/pdf_ocr_menu_observer_browsertest.cc
@@ -0,0 +1,133 @@
+// Copyright 2022 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/renderer_context_menu/pdf_ocr_menu_observer.h"
+
+#include "build/chromeos_buildflags.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/context_menu_params.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/accessibility/accessibility_features.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#else
+#include "content/public/browser/browser_accessibility_state.h"
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+
+namespace {
+
+// A test class for the PDF OCR item in the Context Menu. This test should be
+// a browser test as it accesses resources.
+class PdfOcrMenuObserverTest : public InProcessBrowserTest {
+ public:
+ PdfOcrMenuObserverTest() {
+ scoped_feature_list_.InitAndEnableFeature(features::kPdfOcr);
+ }
+
+ void SetUpOnMainThread() override { Reset(false); }
+ void TearDownOnMainThread() override {
+ // TODO(crbug.com/1401757): Clear an observer from menu before resetting.
+ // That way, we can prevent from having a dangling pointer to the reset
+ // observer.
+ observer_.reset();
+ menu_.reset();
+ }
+
+ void Reset(bool incognito) {
+ // TODO(crbug.com/1401757): Clear an observer from menu before resetting.
+ // That way, we can prevent from having a dangling pointer to observer.
+ observer_.reset();
+ menu_ = std::make_unique(incognito);
+ observer_ = std::make_unique(menu_.get());
+ menu_->SetObserver(observer_.get());
+ }
+
+ void InitMenu() {
+ content::ContextMenuParams params;
+ observer_->InitMenu(params);
+ }
+
+ PdfOcrMenuObserverTest(const PdfOcrMenuObserverTest&) = delete;
+ PdfOcrMenuObserverTest& operator=(const PdfOcrMenuObserverTest&) = delete;
+ ~PdfOcrMenuObserverTest() override;
+ MockRenderViewContextMenu* menu() { return menu_.get(); }
+ PdfOcrMenuObserver* observer() { return observer_.get(); }
+
+ private:
+ std::unique_ptr observer_;
+ std::unique_ptr menu_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+PdfOcrMenuObserverTest::~PdfOcrMenuObserverTest() = default;
+
+} // namespace
+
+// Tests that opening a context menu does not show the menu option if a
+// screen reader is not enabled, regardless of the PDF OCR setting.
+IN_PROC_BROWSER_TEST_F(PdfOcrMenuObserverTest,
+ PdfOcrItemNotShownWithoutScreenReader) {
+ menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive,
+ false);
+ InitMenu();
+ EXPECT_EQ(0u, menu()->GetMenuSize());
+
+ menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive, true);
+ InitMenu();
+ EXPECT_EQ(0u, menu()->GetMenuSize());
+}
+
+IN_PROC_BROWSER_TEST_F(PdfOcrMenuObserverTest,
+ PdfOcrItemShownWithScreenReaderEnabled) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+ // Enable Chromevox.
+ ash::AccessibilityManager::Get()->EnableSpokenFeedback(true);
+#else
+ // Spoof a screen reader.
+ content::testing::ScopedContentAXModeSetter scoped_accessibility_mode(
+ ui::AXMode::kScreenReader);
+#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+ menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive,
+ false);
+ InitMenu();
+
+ // Shows but is not checked.
+ ASSERT_EQ(3u, menu()->GetMenuSize());
+ MockRenderViewContextMenu::MockMenuItem item;
+ menu()->GetMenuItem(0, &item);
+ EXPECT_EQ(IDC_CONTENT_CONTEXT_PDF_OCR, item.command_id);
+ EXPECT_TRUE(item.enabled);
+ EXPECT_FALSE(item.checked);
+ EXPECT_FALSE(item.hidden);
+
+ // The submenu items exist.
+ menu()->GetMenuItem(1, &item);
+ EXPECT_EQ(IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS, item.command_id);
+ EXPECT_TRUE(item.enabled);
+ EXPECT_FALSE(item.checked);
+ EXPECT_FALSE(item.hidden);
+ menu()->GetMenuItem(2, &item);
+ EXPECT_EQ(IDC_CONTENT_CONTEXT_PDF_OCR_ONCE, item.command_id);
+ EXPECT_TRUE(item.enabled);
+ EXPECT_FALSE(item.checked);
+ EXPECT_FALSE(item.hidden);
+
+ Reset(false);
+ // Shows and is checked when a screen reader and the setting are both on.
+ menu()->GetPrefs()->SetBoolean(prefs::kAccessibilityPdfOcrAlwaysActive, true);
+ InitMenu();
+
+ ASSERT_EQ(1u, menu()->GetMenuSize());
+ menu()->GetMenuItem(0, &item);
+ EXPECT_EQ(IDC_CONTENT_CONTEXT_PDF_OCR, item.command_id);
+ EXPECT_TRUE(item.enabled);
+ EXPECT_TRUE(item.checked);
+ EXPECT_FALSE(item.hidden);
+}
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 35ba624afaa0d..a169d49b73f1d 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -64,6 +64,7 @@
#include "chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.h"
#include "chrome/browser/renderer_context_menu/context_menu_content_type_factory.h"
#include "chrome/browser/renderer_context_menu/link_to_text_menu_observer.h"
+#include "chrome/browser/renderer_context_menu/pdf_ocr_menu_observer.h"
#include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -136,6 +137,7 @@
#include "components/search_engines/template_url_service.h"
#include "components/send_tab_to_self/metrics_util.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
+#include "components/services/screen_ai/buildflags/buildflags.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/spellcheck/browser/spellcheck_host_metrics.h"
#include "components/spellcheck/common/spellcheck_common.h"
@@ -449,17 +451,19 @@ const std::map& GetIdcToUmaMap(UmaEnumIdLookupType type) {
{IDC_FOLLOW, 119},
{IDC_UNFOLLOW, 120},
{IDC_CONTENT_CONTEXT_AUTOFILL_CUSTOM_FIRST, 121},
- {IDC_CONTENT_CONTEXT_RUN_PDF_OCR, 122},
{IDC_CONTENT_CONTEXT_PARTIAL_TRANSLATE, 123},
{IDC_CONTENT_CONTEXT_ADD_A_NOTE, 124},
{IDC_LIVE_CAPTION, 125},
+ {IDC_CONTENT_CONTEXT_PDF_OCR, 126},
+ {IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS, 127},
+ {IDC_CONTENT_CONTEXT_PDF_OCR_ONCE, 128},
// To add new items:
// - Add one more line above this comment block, using the UMA value
// from the line below this comment block.
// - Increment the UMA value in that latter line.
// - Add the new item to the RenderViewContextMenuItem enum in
// tools/metrics/histograms/enums.xml.
- {0, 126}});
+ {0, 129}});
// These UMA values are for the the ContextMenuOptionDesktop enum, used for
// the ContextMenu.SelectedOptionDesktop histograms.
@@ -744,6 +748,9 @@ RenderViewContextMenu::RenderViewContextMenu(
? GetBrowser()->app_controller()->system_app()
: nullptr;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+ pdf_ocr_submenu_model_ = std::make_unique(this);
+#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
}
RenderViewContextMenu::~RenderViewContextMenu() = default;
@@ -1069,14 +1076,11 @@ void RenderViewContextMenu::InitMenu() {
}
}
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- if (features::IsPdfOcrEnabled() &&
- accessibility_state_utils::IsScreenReaderEnabled() &&
- screen_ai::ScreenAIInstallState::GetInstance()->IsComponentReady() &&
- IsFrameInPdfViewer(GetRenderFrameHost())) {
- AppendPdfOcrItem();
+ if (accessibility_state_utils::IsScreenReaderEnabled() &&
+ features::IsPdfOcrEnabled() && IsFrameInPdfViewer(GetRenderFrameHost())) {
+ AppendPdfOcrItems();
+ VLOG(2) << "Appended PDF OCR Items";
}
-#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
if (content_type_->SupportsGroup(
ContextMenuContentType::ITEM_GROUP_MEDIA_PLUGIN)) {
@@ -1900,13 +1904,15 @@ void RenderViewContextMenu::AppendReadAnythingItem() {
IDS_CONTENT_CONTEXT_READ_ANYTHING);
}
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-void RenderViewContextMenu::AppendPdfOcrItem() {
+void RenderViewContextMenu::AppendPdfOcrItems() {
menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
- menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_RUN_PDF_OCR,
- IDS_CONTENT_CONTEXT_RUN_PDF_OCR);
+ if (!pdf_ocr_submenu_model_observer_) {
+ pdf_ocr_submenu_model_observer_ =
+ std::make_unique(this);
+ }
+ observers_.AddObserver(pdf_ocr_submenu_model_observer_.get());
+ pdf_ocr_submenu_model_observer_->InitMenu(params_);
}
-#endif
void RenderViewContextMenu::AppendRotationItems() {
if (params_.media_flags & ContextMenuData::kMediaCanRotate) {
@@ -2486,11 +2492,6 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const {
return !!(params_.media_flags &
ContextMenuData::kMediaCanPictureInPicture);
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- case IDC_CONTENT_CONTEXT_RUN_PDF_OCR:
- return true;
-#endif
-
case IDC_CONTENT_CONTEXT_EMOJI:
return params_.is_editable;
@@ -2804,12 +2805,6 @@ void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) {
source_web_contents_->ReloadFocusedFrame();
break;
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- case IDC_CONTENT_CONTEXT_RUN_PDF_OCR:
- ExecRunPdfOcr();
- break;
-#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-
case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
if (GetRenderFrameHost())
GetRenderFrameHost()->ViewSource();
@@ -3000,6 +2995,31 @@ void RenderViewContextMenu::AddAccessibilityLabelsServiceItem(bool is_checked) {
}
}
+void RenderViewContextMenu::AddPdfOcrMenuItem(bool is_always_active) {
+ if (is_always_active) {
+ // Only a checked item needs to be added to the context menu when the user
+ // selects "Always" or toggles on PDF OCR to make it always active.
+ menu_model_.AddCheckItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR,
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION));
+ } else {
+ // Add the submenu when the user doesn't select "Always" nor toggle on the
+ // the PDF OCR.
+ pdf_ocr_submenu_model_->AddItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR_ALWAYS,
+ l10n_util::GetStringUTF16(
+ IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ALWAYS));
+ pdf_ocr_submenu_model_->AddItem(
+ IDC_CONTENT_CONTEXT_PDF_OCR_ONCE,
+ l10n_util::GetStringUTF16(
+ IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION_ONCE));
+ menu_model_.AddSubMenu(
+ IDC_CONTENT_CONTEXT_PDF_OCR,
+ l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PDF_OCR_MENU_OPTION),
+ pdf_ocr_submenu_model_.get());
+ }
+}
+
// static
void RenderViewContextMenu::RegisterMenuShownCallbackForTesting(
base::OnceCallback cb) {
@@ -3648,14 +3668,6 @@ void RenderViewContextMenu::ExecRegionSearch(
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-void RenderViewContextMenu::ExecRunPdfOcr() {
- // TODO(nektar): Modify `ui::AXMode` to signal to the renderer that OCR should
- // be performed.
- GetBrowser()->RunScreenAIAnnotator();
-}
-#endif
-
void RenderViewContextMenu::ExecSearchWebForImage() {
CoreTabHelper* core_tab_helper =
CoreTabHelper::FromWebContents(source_web_contents_);
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 203daac4b57ce..78126157f0b2b 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -24,7 +24,6 @@
#include "components/renderer_context_menu/render_view_context_menu_observer.h"
#include "components/renderer_context_menu/render_view_context_menu_proxy.h"
#include "components/search_engines/template_url.h"
-#include "components/services/screen_ai/buildflags/buildflags.h"
#include "content/public/browser/context_menu_params.h"
#include "extensions/buildflags/buildflags.h"
#include "ppapi/buildflags/buildflags.h"
@@ -52,6 +51,7 @@ class Profile;
class QuickAnswersMenuObserver;
class SpellingMenuObserver;
class SpellingOptionsSubMenuObserver;
+class PdfOcrMenuObserver;
namespace content {
class RenderFrameHost;
@@ -116,6 +116,7 @@ class RenderViewContextMenu
void ExecuteCommand(int command_id, int event_flags) override;
void AddSpellCheckServiceItem(bool is_checked) override;
void AddAccessibilityLabelsServiceItem(bool is_checked) override;
+ void AddPdfOcrMenuItem(bool is_always_active) override;
// Registers a one-time callback that will be called the next time a context
// menu is shown.
@@ -238,9 +239,6 @@ class RenderViewContextMenu
void AppendExitFullscreenItem();
void AppendCopyItem();
void AppendLinkToTextItems();
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- void AppendPdfOcrItem();
-#endif
void AppendPrintItem();
void AppendPartialTranslateItem();
void AppendMediaRouterItem();
@@ -253,6 +251,7 @@ class RenderViewContextMenu
// Returns true if the items were appended. This might not happen in all
// cases, e.g. these are only appended if a screen reader is enabled.
bool AppendAccessibilityLabelsItems();
+ void AppendPdfOcrItems();
void AppendSearchProvider();
#if BUILDFLAG(ENABLE_EXTENSIONS)
void AppendAllExtensionItems();
@@ -337,9 +336,6 @@ class RenderViewContextMenu
void ExecPictureInPicture();
// Implemented in RenderViewContextMenuViews.
void ExecOpenInReadAnything() override {}
-#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
- void ExecRunPdfOcr();
-#endif
void MediaPlayerActionAt(const gfx::Point& location,
const blink::mojom::MediaPlayerAction& action);
@@ -395,6 +391,10 @@ class RenderViewContextMenu
accessibility_labels_menu_observer_;
ui::SimpleMenuModel accessibility_labels_submenu_model_;
+ // An observer that handles PDF OCR items.
+ std::unique_ptr pdf_ocr_submenu_model_observer_;
+ std::unique_ptr pdf_ocr_submenu_model_;
+
#if !BUILDFLAG(IS_MAC)
// An observer that handles the submenu for showing spelling options. This
// submenu lets users select the spelling language, for example.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index a29d0bdc58d34..c4f3399fadffd 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -2291,6 +2291,8 @@ IN_PROC_BROWSER_TEST_F(PdfPluginContextMenuBrowserTest, Rotate) {
}
}
+// TODO(crbug.com/1393069): Consider removing this build flag when enabling
+// this browser test.
#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
class PdfOcrContextMenuBrowserTest : public PdfPluginContextMenuBrowserTest,
public ::testing::WithParamInterface {
@@ -2329,11 +2331,10 @@ class PdfOcrContextMenuBrowserTest : public PdfPluginContextMenuBrowserTest,
base::test::ScopedFeatureList scoped_feature_list_;
};
-// TODO(crbug.com/1278249): Re-enable this test once a mock OCR Service has been
-// created.
+// TODO(crbug.com/1393069): Re-enable this test.
IN_PROC_BROWSER_TEST_P(PdfOcrContextMenuBrowserTest, DISABLED_PdfOcr) {
std::unique_ptr menu = SetupAndCreateMenu();
- ASSERT_EQ(menu->IsItemPresent(IDC_CONTENT_CONTEXT_RUN_PDF_OCR),
+ ASSERT_EQ(menu->IsItemPresent(IDC_CONTENT_CONTEXT_PDF_OCR),
IsPdfOcrEnabled() && IsScreenReaderEnabled() && IsComponentReady());
}
@@ -2341,8 +2342,7 @@ INSTANTIATE_TEST_SUITE_P(All,
PdfOcrContextMenuBrowserTest,
::testing::Range(0, 8));
-#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) && (BUILDFLAG(IS_LINUX) ||
- // BUILDFLAG(IS_MAC))
+#endif // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
#endif // BUILDFLAG(ENABLE_PDF)
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 1d6be5e25a6c2..21329fb3a536e 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1354,6 +1354,11 @@ const char kAccessibilityFocusHighlightEnabled[] =
"settings.a11y.focus_highlight";
#endif
+// Whether the PDF OCR feature is set to be always active. The PDF OCR feature
+// is exposed to only screen reader users.
+const char kAccessibilityPdfOcrAlwaysActive[] =
+ "settings.a11y.pdf_ocr_always_active";
+
// Pref indicating the page colors option the user wants. Page colors is an
// accessibility feature that simulates forced colors mode at the browser level.
const char kPageColors[] = "settings.a11y.page_colors";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 1aa5fc60c0d40..ca180f385326f 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -192,6 +192,7 @@ extern const char kAccessibilityImageLabelsOnlyOnWifi[];
#if !BUILDFLAG(IS_CHROMEOS_ASH)
extern const char kAccessibilityFocusHighlightEnabled[];
#endif
+extern const char kAccessibilityPdfOcrAlwaysActive[];
#if !BUILDFLAG(IS_ANDROID)
extern const char kLiveCaptionEnabled[];
extern const char kLiveCaptionLanguageCode[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a68f6383ab99b..4d3959031c330 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2404,6 +2404,7 @@ if (!is_android) {
"../browser/accessibility/ax_screen_ai_annotator.cc",
"../browser/accessibility/ax_screen_ai_annotator.h",
"../browser/accessibility/screen_ai_service_browsertest.cc",
+ "../browser/renderer_context_menu/pdf_ocr_menu_observer_browsertest.cc",
]
deps += [
"//components/services/screen_ai/public/cpp:screen_ai_install_state",
diff --git a/components/renderer_context_menu/render_view_context_menu_proxy.h b/components/renderer_context_menu/render_view_context_menu_proxy.h
index 115004b220f12..80c06fad0848a 100644
--- a/components/renderer_context_menu/render_view_context_menu_proxy.h
+++ b/components/renderer_context_menu/render_view_context_menu_proxy.h
@@ -117,6 +117,9 @@ class RenderViewContextMenuProxy {
// Add accessibility labels service item to the context menu.
virtual void AddAccessibilityLabelsServiceItem(bool is_checked) = 0;
+ // Add PDF OCR item to the context menu.
+ virtual void AddPdfOcrMenuItem(bool is_checked) = 0;
+
// Retrieve the given associated objects with a context menu.
virtual content::RenderViewHost* GetRenderViewHost() const = 0;
virtual content::WebContents* GetWebContents() const = 0;
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ab4972532179b..78273117a480e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -87901,9 +87901,11 @@ https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
-
+
+
+