#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
#include <stddef.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "base/location.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/extensions/extension_action_dispatcher.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
#include "content/public/common/color_parser.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/prefs_helper.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/extension_action.h"
#include "extensions/browser/extension_action_manager.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/icon_util.h"
#include "extensions/common/api/extension_action/action_info.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/image_util.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/mojom/view_type.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "url/origin.h"
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
#include "base/logging.h"
#include "ohos_nweb/src/cef_delegate/nweb_extension_action_cef_delegate.h"
#include "ohos_nweb/src/capi/nweb_extension_action_icon.h"
#endif
using content::WebContents;
namespace extensions {
namespace {
const char kNoExtensionActionError[] =
"This extension has no action specified.";
const char kNoTabError[] = "No tab with id: *.";
bool g_report_error_for_invisible_icon = false;
bool ParseColor(const base::Value& color_value, SkColor& color) {
if (color_value.is_string()) {
return content::ParseCssColorString(color_value.GetString(), &color);
}
if (!color_value.is_list()) {
return false;
}
const base::Value::List& color_list = color_value.GetList();
if (color_list.size() != 4 ||
std::ranges::any_of(color_list,
[](const auto& color) { return !color.is_int(); })) {
return false;
}
color = SkColorSetARGB(color_list[3].GetInt(), color_list[0].GetInt(),
color_list[1].GetInt(), color_list[2].GetInt());
return true;
}
}
ExtensionActionFunction::ExtensionActionFunction()
: details_(nullptr),
tab_id_(ExtensionAction::kDefaultTabId),
contents_(nullptr),
extension_action_(nullptr) {}
ExtensionActionFunction::~ExtensionActionFunction() = default;
ExtensionFunction::ResponseAction ExtensionActionFunction::Run() {
ExtensionActionManager* manager =
ExtensionActionManager::Get(browser_context());
extension_action_ = manager->GetExtensionAction(*extension());
if (!extension_action_) {
return RespondNow(Error(kNoExtensionActionError));
}
EXTENSION_FUNCTION_VALIDATE(ExtractDataFromArguments());
if (tab_id_ != ExtensionAction::kDefaultTabId) {
content::WebContents* contents_out_param = nullptr;
ExtensionTabUtil::GetTabById(tab_id_, browser_context(),
include_incognito_information(),
&contents_out_param);
if (!contents_out_param) {
return RespondNow(Error(kNoTabError, base::NumberToString(tab_id_)));
}
contents_ = contents_out_param;
} else {
EXTENSION_FUNCTION_VALIDATE(extension_action_->action_type() !=
ActionInfo::Type::kPage);
}
return RunExtensionAction();
}
bool ExtensionActionFunction::ExtractDataFromArguments() {
if (args().empty()) {
return true;
}
const base::Value& first_arg = args()[0];
switch (first_arg.type()) {
case base::Value::Type::INTEGER:
tab_id_ = first_arg.GetInt();
break;
case base::Value::Type::DICT: {
details_ = &first_arg.GetDict();
if (const base::Value* tab_id_value = details_->Find("tabId")) {
switch (tab_id_value->type()) {
case base::Value::Type::NONE:
return true;
case base::Value::Type::INTEGER:
tab_id_ = tab_id_value->GetInt();
return true;
default:
return false;
}
}
break;
}
case base::Value::Type::NONE:
break;
default:
return false;
}
return true;
}
void ExtensionActionFunction::NotifyChange() {
ExtensionActionDispatcher::Get(browser_context())
->NotifyChange(extension_action_, contents_, browser_context());
}
void ExtensionActionFunction::SetVisible(bool visible) {
if (extension_action_->GetIsVisible(tab_id_) == visible) {
return;
}
extension_action_->SetIsVisible(tab_id_, visible);
NotifyChange();
}
ExtensionFunction::ResponseAction
ExtensionActionShowFunction::RunExtensionAction() {
SetVisible(true);
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
std::optional<int> tab_id;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
tab_id = tab_id_;
}
LOG(INFO) << "ExtensionActionShowFunction::RunExtensionAction";
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnEnable(
extension()->id(), tab_id,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionHideFunction::RunExtensionAction() {
SetVisible(false);
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
std::optional<int> tab_id;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
tab_id = tab_id_;
}
LOG(INFO) << "ExtensionActionHideFunction::RunExtensionAction";
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnDisable(
extension()->id(), tab_id,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ActionIsEnabledFunction::RunExtensionAction() {
return RespondNow(WithArguments(
extension_action_->GetIsVisibleIgnoringDeclarative(tab_id_)));
}
void ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting(
bool value) {
g_report_error_for_invisible_icon = value;
}
ExtensionFunction::ResponseAction
ExtensionActionSetIconFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const base::Value::Dict* canvas_set = details_->FindDict("imageData");
if (canvas_set) {
gfx::ImageSkia icon;
extensions::IconParseResult parse_result =
extensions::ParseIconFromCanvasDictionary(*canvas_set, &icon);
EXTENSION_FUNCTION_VALIDATE(parse_result ==
extensions::IconParseResult::kSuccess);
if (icon.isNull()) {
return RespondNow(Error("Icon invalid."));
}
gfx::Image icon_image(icon);
const SkBitmap bitmap = icon_image.AsBitmap();
const bool is_visible = image_util::IsIconSufficientlyVisible(bitmap);
if (!is_visible && g_report_error_for_invisible_icon) {
return RespondNow(Error("Icon not sufficiently visible."));
}
extension_action_->SetIcon(tab_id_, icon_image);
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
LOG(INFO) << "ExtensionActionSetIconFunction::RunExtensionAction";
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnSetIcon(
extension()->id(), icon_image, tab_id_,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
} else if (details_->FindInt("iconIndex")) {
return RespondNow(NoArguments());
} else {
EXTENSION_FUNCTION_VALIDATE(false);
}
NotifyChange();
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionSetTitleFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const std::string* title = details_->FindString("title");
EXTENSION_FUNCTION_VALIDATE(title);
extension_action_->SetTitle(tab_id_, *title);
NotifyChange();
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
NWebExtensionActionSetTitleDetails details;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
details.tabId = tab_id_;
}
details.title = *title;
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnSetTitle(
extension()->id(), details,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionSetPopupFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const std::string* popup_string = details_->FindString("popup");
EXTENSION_FUNCTION_VALIDATE(popup_string);
GURL popup_url;
if (!popup_string->empty()) {
popup_url = extension()->ResolveExtensionURL(*popup_string);
if (!popup_url.is_valid()) {
return RespondNow(Error(manifest_errors::kInvalidExtensionPopupPath));
}
}
extension_action_->SetPopupUrl(tab_id_, popup_url);
NotifyChange();
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
NWebExtensionActionSetPopupDetails details;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
details.tabId = tab_id_;
}
details.popup = *popup_string;
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnSetPopup(
extension()->id(), details,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionSetBadgeTextFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const std::string* badge_text = details_->FindString("text");
if (badge_text) {
extension_action_->SetBadgeText(tab_id_, *badge_text);
} else {
extension_action_->ClearBadgeText(tab_id_);
}
NotifyChange();
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionSetBadgeBackgroundColorFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const base::Value* color_value = details_->Find("color");
EXTENSION_FUNCTION_VALIDATE(color_value);
SkColor color = 0;
if (!ParseColor(*color_value, color)) {
return RespondNow(Error(extension_misc::kInvalidColorError));
}
extension_action_->SetBadgeBackgroundColor(tab_id_, color);
NotifyChange();
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
NWebExtensionActionSetBadgeBackgroundColorDetails details;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
details.tabId = tab_id_;
}
details.color = {SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
SkColorGetA(color)};
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()
->OnSetBadgeBackgroundColor(
extension()->id(), details,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ActionSetBadgeTextColorFunction::RunExtensionAction() {
EXTENSION_FUNCTION_VALIDATE(details_);
const base::Value* color_value = details_->Find("color");
EXTENSION_FUNCTION_VALIDATE(color_value);
SkColor color = 0;
if (!ParseColor(*color_value, color)) {
return RespondNow(Error(extension_misc::kInvalidColorError));
}
if (SkColorGetA(color) == SK_AlphaTRANSPARENT) {
return RespondNow(Error(extension_misc::kInvalidColorError));
}
extension_action_->SetBadgeTextColor(tab_id_, color);
NotifyChange();
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
NWebExtensionActionSetBadgeTextColorDetails details;
if (tab_id_ != ExtensionAction::kDefaultTabId) {
details.tabId = tab_id_;
}
details.color = {SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
SkColorGetA(color)};
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()
->OnSetBadgeTextColor(
extension()->id(), details,
OHOS::NWeb::GetExtensionContextType(browser_context()),
include_incognito_information());
#endif
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
ExtensionActionGetTitleFunction::RunExtensionAction() {
return RespondNow(WithArguments(extension_action_->GetTitle(tab_id_)));
}
ExtensionFunction::ResponseAction
ExtensionActionGetPopupFunction::RunExtensionAction() {
return RespondNow(
WithArguments(extension_action_->GetPopupUrl(tab_id_).spec()));
}
ExtensionFunction::ResponseAction
ExtensionActionGetBadgeTextFunction::RunExtensionAction() {
declarative_net_request::PrefsHelper helper(
*ExtensionPrefs::Get(browser_context()));
bool is_dnr_action_count_active =
helper.GetUseActionCountAsBadgeText(extension_id()) &&
!extension_action_->HasBadgeText(tab_id_);
if (is_dnr_action_count_active &&
!declarative_net_request::HasDNRFeedbackPermission(extension(),
tab_id_)) {
return RespondNow(WithArguments(
std::move(declarative_net_request::kActionCountPlaceholderBadgeText)));
}
return RespondNow(
WithArguments(extension_action_->GetDisplayBadgeText(tab_id_)));
}
ExtensionFunction::ResponseAction
ExtensionActionGetBadgeBackgroundColorFunction::RunExtensionAction() {
base::Value::List list;
SkColor color = extension_action_->GetBadgeBackgroundColor(tab_id_);
list.Append(static_cast<int>(SkColorGetR(color)));
list.Append(static_cast<int>(SkColorGetG(color)));
list.Append(static_cast<int>(SkColorGetB(color)));
list.Append(static_cast<int>(SkColorGetA(color)));
return RespondNow(WithArguments(std::move(list)));
}
ExtensionFunction::ResponseAction
ActionGetBadgeTextColorFunction::RunExtensionAction() {
base::Value::List list;
SkColor color = extension_action_->GetBadgeTextColor(tab_id_);
list.Append(static_cast<int>(SkColorGetR(color)));
list.Append(static_cast<int>(SkColorGetG(color)));
list.Append(static_cast<int>(SkColorGetB(color)));
list.Append(static_cast<int>(SkColorGetA(color)));
return RespondNow(WithArguments(std::move(list)));
}
ActionGetUserSettingsFunction::ActionGetUserSettingsFunction() = default;
ActionGetUserSettingsFunction::~ActionGetUserSettingsFunction() = default;
ExtensionFunction::ResponseAction ActionGetUserSettingsFunction::Run() {
DCHECK(extension());
#if BUILDFLAG(ARKWEB_ARKWEB_EXTENSIONS)
std::optional<NWebExtensionActionUserSettings> user_settings =
OHOS::NWeb::NWebExtensionActionCefDelegate::GetInstance()->OnGetUserSettings(extension()->id());
bool is_pinned = false;
if (user_settings) {
is_pinned = user_settings->isOnToolbar;
}
#else
ExtensionActionManager* const action_manager =
ExtensionActionManager::Get(browser_context());
ExtensionAction* const action =
action_manager->GetExtensionAction(*extension());
DCHECK(action);
DCHECK_EQ(ActionInfo::Type::kAction, action->action_type());
const bool is_pinned =
ToolbarActionsModel::Get(Profile::FromBrowserContext(browser_context()))
->IsActionPinned(extension_id());
#endif
base::Value::Dict ui_settings;
ui_settings.Set("isOnToolbar", is_pinned);
return RespondNow(WithArguments(std::move(ui_settings)));
}
}