blob: 828ec9a4484ecda7f13e6d413ef7f27ab608325b [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/renderer_context_menu/accessibility_labels_menu_observer.h"
#include <utility>
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/accessibility/accessibility_labels_service.h"
#include "chrome/browser/accessibility/accessibility_labels_service_factory.h"
#include "chrome/browser/accessibility/accessibility_state_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_context_menu/accessibility_labels_bubble_model.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
#include "chrome/browser/ui/confirm_bubble.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/context_menu_params.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"
using content::BrowserThread;
namespace {
// These enums are logged and must match AccessibilityImageLabelMode in
// enums.xml.
enum class AccessibilityImageLabelMode {
kModeEnabled = 1,
kModeEnabledOnce = 2,
kModeDisabled = 3,
kMaxValue = kModeDisabled,
};
// Static
void RecordContextMenuOptionSelected(AccessibilityImageLabelMode option) {
UMA_HISTOGRAM_ENUMERATION("Accessibility.ImageLabels.ContextMenuOption",
option);
}
} // namespace
AccessibilityLabelsMenuObserver::AccessibilityLabelsMenuObserver(
RenderViewContextMenuProxy* proxy)
: proxy_(proxy) {}
AccessibilityLabelsMenuObserver::~AccessibilityLabelsMenuObserver() {}
void AccessibilityLabelsMenuObserver::InitMenu(
const content::ContextMenuParams& params) {
Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
if (ShouldShowLabelsItem()) {
proxy_->AddAccessibilityLabelsServiceItem(profile->GetPrefs()->GetBoolean(
prefs::kAccessibilityImageLabelsEnabled));
}
}
bool AccessibilityLabelsMenuObserver::IsCommandIdSupported(int command_id) {
return command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE;
}
bool AccessibilityLabelsMenuObserver::IsCommandIdChecked(int command_id) {
DCHECK(IsCommandIdSupported(command_id));
Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
if (command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE) {
return profile->GetPrefs()->GetBoolean(
prefs::kAccessibilityImageLabelsEnabled);
}
return false;
}
bool AccessibilityLabelsMenuObserver::IsCommandIdEnabled(int command_id) {
DCHECK(IsCommandIdSupported(command_id));
if (command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS ||
command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE) {
return ShouldShowLabelsItem();
}
return false;
}
void AccessibilityLabelsMenuObserver::ExecuteCommand(int command_id) {
DCHECK(IsCommandIdSupported(command_id));
Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
if (command_id == IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE) {
// When a user enables the accessibility labeling item, we
// show a bubble to confirm it. On the other hand, when a user disables this
// item, we directly update/ the profile and stop integrating the labels
// service immediately.
if (!profile->GetPrefs()->GetBoolean(
prefs::kAccessibilityImageLabelsEnabled)) {
// Always show the confirm bubble when enabling the full feature,
// regardless of whether it's been shown before.
ShowConfirmBubble(profile, true /* enable always */);
RecordContextMenuOptionSelected(
AccessibilityImageLabelMode::kModeEnabled);
} else {
profile->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsEnabled,
false);
RecordContextMenuOptionSelected(
AccessibilityImageLabelMode::kModeDisabled);
}
} else if (command_id ==
IDC_CONTENT_CONTEXT_ACCESSIBILITY_LABELS_TOGGLE_ONCE) {
// Only show the confirm bubble if it's never been shown before.
if (!profile->GetPrefs()->GetBoolean(
prefs::kAccessibilityImageLabelsOptInAccepted)) {
ShowConfirmBubble(profile, false /* enable once only */);
} else {
AccessibilityLabelsServiceFactory::GetForProfile(profile)
->EnableLabelsServiceOnce();
}
RecordContextMenuOptionSelected(
AccessibilityImageLabelMode::kModeEnabledOnce);
}
}
bool AccessibilityLabelsMenuObserver::ShouldShowLabelsItem() {
// Hidden behind a feature flag.
if (!base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels))
return false;
return accessibility_state_utils::IsScreenReaderEnabled();
}
void AccessibilityLabelsMenuObserver::ShowConfirmBubble(Profile* profile,
bool enable_always) {
content::WebContents* web_contents = proxy_->GetWebContents();
content::RenderWidgetHostView* view =
proxy_->GetRenderViewHost()->GetWidget()->GetView();
gfx::Rect rect = view->GetViewBounds();
auto model = std::make_unique<AccessibilityLabelsBubbleModel>(
profile, web_contents, enable_always);
chrome::ShowConfirmBubble(
web_contents->GetTopLevelNativeWindow(), view->GetNativeView(),
gfx::Point(rect.CenterPoint().x(), rect.y()), std::move(model));
}