// 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/permissions/permission_update_message_delegate_android.h"
#include <memory>
#include <utility>
#include "chrome/browser/android/android_theme_resources.h"
#include "chrome/browser/android/resource_mapper.h"
#include "components/messages/android/message_dispatcher_bridge.h"
#include "components/permissions/android/android_permission_util.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
PermissionUpdateMessageDelegate::PermissionUpdateMessageDelegate(
content::WebContents* web_contents,
const std::vector<std::string> required_android_permissions,
const std::vector<std::string> optional_android_permissions,
const std::vector<ContentSettingsType> content_settings_types,
int icon_id,
int title_id,
int description_id,
PermissionUpdatedCallback callback,
base::OnceCallback<void(PermissionUpdateMessageDelegate*)> delete_callback_)
: content_settings_types_(content_settings_types),
delete_callback_(std::move(delete_callback_)) {
DCHECK(callback);
callbacks_.push_back(std::move(callback));
message_ = std::make_unique<messages::MessageWrapper>(
messages::MessageIdentifier::PERMISSION_UPDATE,
base::BindOnce(
&PermissionUpdateMessageDelegate::HandlePrimaryActionCallback,
base::Unretained(this)),
base::BindOnce(&PermissionUpdateMessageDelegate::HandleDismissCallback,
base::Unretained(this)));
title_id_ = title_id;
message_->SetTitle(l10n_util::GetStringUTF16(title_id));
message_->SetDescription(l10n_util::GetStringUTF16(description_id));
message_->SetPrimaryButtonText(
l10n_util::GetStringUTF16(IDS_INFOBAR_UPDATE_PERMISSIONS_BUTTON_TEXT));
message_->SetIconResourceId(ResourceMapper::MapToJavaDrawableId(icon_id));
messages::MessageDispatcherBridge::Get()->EnqueueMessage(
message_.get(), web_contents, messages::MessageScopeType::NAVIGATION,
messages::MessagePriority::kNormal);
permission_update_requester_ = std::make_unique<PermissionUpdateRequester>(
web_contents, required_android_permissions, optional_android_permissions,
base::BindOnce(&PermissionUpdateMessageDelegate::OnPermissionResult,
base::Unretained(this)));
}
PermissionUpdateMessageDelegate::~PermissionUpdateMessageDelegate() {
DismissInternal();
}
void PermissionUpdateMessageDelegate::OnPermissionResult(
bool all_permissions_granted) {
RunCallbacks(all_permissions_granted);
permissions::PermissionUmaUtil::RecordMissingPermissionInfobarAction(
permissions::PermissionAction::GRANTED, content_settings_types_);
// The callback may destroy `this`.
// Do not access any member variables after this point.
std::move(delete_callback_).Run(this);
}
int PermissionUpdateMessageDelegate::GetTitleId() {
return title_id_;
}
void PermissionUpdateMessageDelegate::AttachAdditionalCallback(
PermissionUpdatedCallback callback) {
callbacks_.push_back(std::move(callback));
}
void PermissionUpdateMessageDelegate::HandlePrimaryActionCallback() {
permission_update_requester_->RequestPermissions();
}
void PermissionUpdateMessageDelegate::HandleDismissCallback(
messages::DismissReason dismiss_reason) {
// If it is dismissed by clicking on primary action, metrics and callback
// will be recorded and run in OnPermissionResult
message_.reset();
// PermissionUpdateRequester::RequestPermissions can invoke its callback
// synchronously in some cases. In that case, |OnPermissionResult| will be
// executed before this callback and |callbacks_| will be empty.
if (dismiss_reason == messages::DismissReason::PRIMARY_ACTION ||
callbacks_.empty()) {
return;
}
permissions::PermissionUmaUtil::RecordMissingPermissionInfobarAction(
dismiss_reason == messages::DismissReason::GESTURE
? permissions::PermissionAction::DISMISSED
: permissions::PermissionAction::IGNORED,
content_settings_types_);
RunCallbacks(/*all_permissions_granted=*/false);
// This dismiss callback should be executed in the end, because this can
// destroy the current object.
std::move(delete_callback_).Run(this);
}
void PermissionUpdateMessageDelegate::DismissInternal() {
if (message_) {
messages::MessageDispatcherBridge::Get()->DismissMessage(
message_.get(), messages::DismissReason::UNKNOWN);
}
}
void PermissionUpdateMessageDelegate::RunCallbacks(
bool all_permissions_granted) {
for (auto& callback : callbacks_) {
std::move(callback).Run(all_permissions_granted);
}
callbacks_.clear();
}