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 - + + +