// Copyright 2019 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/accessibility_labels_menu_observer.h"

#include <utility>

#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/profiles/profile.h"
#include "chrome/browser/renderer_context_menu/accessibility_labels_bubble_model.h"
#include "chrome/browser/ui/confirm_bubble.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/renderer_context_menu/render_view_context_menu_proxy.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/context_menu_params.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 "ui/accessibility/platform/ax_platform.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"

using content::BrowserThread;

AccessibilityLabelsMenuObserver::AccessibilityLabelsMenuObserver(
    RenderViewContextMenuProxy* proxy)
    : proxy_(proxy) {}

AccessibilityLabelsMenuObserver::~AccessibilityLabelsMenuObserver() = default;

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 */);
    } else {
      profile->GetPrefs()->SetBoolean(prefs::kAccessibilityImageLabelsEnabled,
                                      false);
    }
  } 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(proxy_->GetWebContents());
    }
  }
}

bool AccessibilityLabelsMenuObserver::ShouldShowLabelsItem() {
  // Disabled by policy.
  Profile* profile = Profile::FromBrowserContext(proxy_->GetBrowserContext());
  if (!profile->GetPrefs()->GetBoolean(
          prefs::kAccessibilityImageLabelsEnabled) &&
      profile->GetPrefs()->IsManagedPreference(
          prefs::kAccessibilityImageLabelsEnabled)) {
    return false;
  }

  return ui::AXPlatform::GetInstance().IsScreenReaderActive();
}

void AccessibilityLabelsMenuObserver::ShowConfirmBubble(Profile* profile,
                                                        bool enable_always) {
  content::WebContents* web_contents = proxy_->GetWebContents();
  // We use the web contents' primary main frame here rather than getting the
  // view from the local render frame host because we want to ensure that it is
  // non-null (proxy_->GetRenderFrameHost() can return nullptr if the frame goes
  // away). In these cases, the spelling preference changes are still valid
  // (tied to the BrowsingContext / WebContents) so we still want to show the
  // confirmation bubble.
  content::RenderWidgetHostView* view =
      web_contents->GetPrimaryMainFrame()->GetRenderWidgetHost()->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));
}
