chromium/chrome/browser/safe_browsing/tailored_security/unconsented_message_android.cc

// Copyright 2021 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/safe_browsing/tailored_security/unconsented_message_android.h"

#include "base/metrics/histogram_functions.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/android/android_theme_resources.h"
#include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/android/safe_browsing_settings_launcher_android.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/messages/android/message_dispatcher_bridge.h"
#include "components/messages/android/message_enums.h"
#include "components/safe_browsing/core/browser/tailored_security_service/tailored_security_outcome.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "skia/ext/image_operations.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"

namespace safe_browsing {

namespace {

void LogMessageOutcome(TailoredSecurityOutcome outcome, bool is_in_flow) {
  if (is_in_flow) {
    base::UmaHistogramEnumeration(
        "SafeBrowsing.TailoredSecurityUnconsentedInFlowMessageOutcome",
        outcome);
  } else {
    base::UmaHistogramEnumeration(
        "SafeBrowsing.TailoredSecurityUnconsentedOutOfFlowMessageOutcome",
        outcome);
  }
}

class CircleImageSource : public gfx::CanvasImageSource {
 public:
  CircleImageSource(int size, SkColor color)
      : gfx::CanvasImageSource(gfx::Size(size, size)), color_(color) {}

  CircleImageSource(const CircleImageSource&) = delete;
  CircleImageSource& operator=(const CircleImageSource&) = delete;

  ~CircleImageSource() override = default;

  void Draw(gfx::Canvas* canvas) override;

 private:
  SkColor color_;
};

void CircleImageSource::Draw(gfx::Canvas* canvas) {
  float radius = size().width() / 2.0f;
  cc::PaintFlags flags;
  flags.setStyle(cc::PaintFlags::kFill_Style);
  flags.setAntiAlias(true);
  flags.setColor(color_);
  canvas->DrawCircle(gfx::PointF(radius, radius), radius, flags);
}

}  // namespace

TailoredSecurityUnconsentedMessageAndroid::
    TailoredSecurityUnconsentedMessageAndroid(
        content::WebContents* web_contents,
        base::OnceClosure dismiss_callback,
        bool is_in_flow)
    : dismiss_callback_(std::move(dismiss_callback)),
      web_contents_(web_contents),
      is_in_flow_(is_in_flow) {
  message_ = std::make_unique<messages::MessageWrapper>(
      messages::MessageIdentifier::TAILORED_SECURITY_ENABLED,
      base::BindOnce(
          &TailoredSecurityUnconsentedMessageAndroid::HandleMessageAccepted,
          base::Unretained(this)),
      base::BindOnce(
          &TailoredSecurityUnconsentedMessageAndroid::HandleMessageDismissed,
          base::Unretained(this)));

  int message_title =
      is_in_flow_ ? IDS_TAILORED_SECURITY_UNCONSENTED_MESSAGE_TITLE
                  : IDS_TAILORED_SECURITY_UNCONSENTED_PROMOTION_MESSAGE_TITLE;
  int primary_button =
      is_in_flow_ ? IDS_TAILORED_SECURITY_UNCONSENTED_MESSAGE_ACCEPT
                  : IDS_TAILORED_SECURITY_UNCONSENTED_PROMOTION_MESSAGE_ACCEPT;
  message_->SetTitle(l10n_util::GetStringUTF16(message_title));
  message_->SetPrimaryButtonText(l10n_util::GetStringUTF16(primary_button));
  if (!is_in_flow_) {
    message_->SetDescription(l10n_util::GetStringUTF16(
        IDS_TAILORED_SECURITY_UNCONSENTED_PROMOTION_MESSAGE_DESCRIPTION));
  }

  message_->SetIconResourceId(
      ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_MESSAGE_SHIELD_BLUE));
  // Need to disable tint here because it removes a shade of blue from the
  // shield which distorts the image.
  message_->DisableIconTint();

  LogMessageOutcome(TailoredSecurityOutcome::kShown, is_in_flow_);
  messages::MessageDispatcherBridge::Get()->EnqueueMessage(
      message_.get(), web_contents_, messages::MessageScopeType::NAVIGATION,
      messages::MessagePriority::kNormal);
}

TailoredSecurityUnconsentedMessageAndroid::
    ~TailoredSecurityUnconsentedMessageAndroid() {
  if (message_) {
    messages::MessageDispatcherBridge::Get()->DismissMessage(
        message_.get(), messages::DismissReason::UNKNOWN);
    if (dismiss_callback_)
      std::move(dismiss_callback_).Run();
  }
}

void TailoredSecurityUnconsentedMessageAndroid::HandleMessageAccepted() {
  Profile* profile =
      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
  if (is_in_flow_) {
    LogMessageOutcome(TailoredSecurityOutcome::kAccepted, is_in_flow_);
    SetSafeBrowsingState(profile->GetPrefs(),
                         SafeBrowsingState::ENHANCED_PROTECTION);
  } else {
    LogMessageOutcome(TailoredSecurityOutcome::kSettings, is_in_flow_);
  }

  ShowSafeBrowsingSettings(web_contents_->GetTopLevelNativeWindow(),
                           SettingsAccessPoint::kTailoredSecurity);
}

void TailoredSecurityUnconsentedMessageAndroid::HandleMessageDismissed(
    messages::DismissReason dismiss_reason) {
  LogMessageOutcome(TailoredSecurityOutcome::kDismissed, is_in_flow_);
  message_.reset();
  // `dismiss_callback_` may delete `this`.
  if (dismiss_callback_)
    std::move(dismiss_callback_).Run();
}

}  // namespace safe_browsing