2021-04-09 01:15:51 +02:00
|
|
|
// Copyright (c) 2021 The Chromium Embedded Framework 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 "libcef/browser/chrome/chrome_context_menu_handler.h"
|
|
|
|
|
|
|
|
#include "libcef/browser/browser_host_base.h"
|
|
|
|
#include "libcef/browser/context_menu_params_impl.h"
|
|
|
|
#include "libcef/browser/simple_menu_model_impl.h"
|
|
|
|
|
|
|
|
#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
|
|
|
|
|
|
|
|
namespace context_menu {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Lifespan is controlled by RenderViewContextMenu.
|
|
|
|
class CefContextMenuObserver : public RenderViewContextMenuObserver,
|
|
|
|
public CefSimpleMenuModelImpl::StateDelegate {
|
|
|
|
public:
|
|
|
|
CefContextMenuObserver(RenderViewContextMenu* context_menu,
|
|
|
|
CefRefPtr<CefBrowserHostBase> browser,
|
|
|
|
CefRefPtr<CefContextMenuHandler> handler)
|
|
|
|
: context_menu_(context_menu), browser_(browser), handler_(handler) {}
|
|
|
|
|
2021-12-06 21:40:25 +01:00
|
|
|
CefContextMenuObserver(const CefContextMenuObserver&) = delete;
|
|
|
|
CefContextMenuObserver& operator=(const CefContextMenuObserver&) = delete;
|
|
|
|
|
2021-04-09 01:15:51 +02:00
|
|
|
// RenderViewContextMenuObserver methods:
|
|
|
|
|
|
|
|
void InitMenu(const content::ContextMenuParams& params) override {
|
|
|
|
params_ = new CefContextMenuParamsImpl(
|
|
|
|
const_cast<content::ContextMenuParams*>(&context_menu_->params()));
|
|
|
|
model_ = new CefSimpleMenuModelImpl(
|
|
|
|
const_cast<ui::SimpleMenuModel*>(&context_menu_->menu_model()),
|
|
|
|
context_menu_, this, /*is_owned=*/false, /*is_popup=*/false);
|
|
|
|
|
|
|
|
handler_->OnBeforeContextMenu(browser_, GetFrame(), params_, model_);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsCommandIdSupported(int command_id) override {
|
|
|
|
// Always claim support for the reserved user ID range.
|
2023-01-02 23:59:03 +01:00
|
|
|
if (command_id >= MENU_ID_USER_FIRST && command_id <= MENU_ID_USER_LAST) {
|
2021-04-09 01:15:51 +02:00
|
|
|
return true;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
|
|
|
|
// Also claim support in specific cases where an ItemInfo exists.
|
|
|
|
return GetItemInfo(command_id) != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only called if IsCommandIdSupported() returns true.
|
|
|
|
bool IsCommandIdEnabled(int command_id) override {
|
|
|
|
// Always return true to use the SimpleMenuModel state.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only called if IsCommandIdSupported() returns true.
|
|
|
|
bool IsCommandIdChecked(int command_id) override {
|
|
|
|
auto* info = GetItemInfo(command_id);
|
|
|
|
return info ? info->checked : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only called if IsCommandIdSupported() returns true.
|
|
|
|
bool GetAccelerator(int command_id, ui::Accelerator* accel) override {
|
|
|
|
auto* info = GetItemInfo(command_id);
|
|
|
|
if (info && info->accel) {
|
|
|
|
*accel = *info->accel;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CommandWillBeExecuted(int command_id) override {
|
|
|
|
if (handler_->OnContextMenuCommand(browser_, GetFrame(), params_,
|
|
|
|
command_id, EVENTFLAG_NONE)) {
|
|
|
|
// Create an ItemInfo so that we get the ExecuteCommand() callback
|
|
|
|
// instead of the default handler.
|
|
|
|
GetOrCreateItemInfo(command_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only called if IsCommandIdSupported() returns true.
|
|
|
|
void ExecuteCommand(int command_id) override {
|
|
|
|
auto* info = GetItemInfo(command_id);
|
|
|
|
if (info) {
|
|
|
|
// In case it was added in CommandWillBeExecuted().
|
|
|
|
MaybeDeleteItemInfo(command_id, info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnMenuClosed() override {
|
|
|
|
handler_->OnContextMenuDismissed(browser_, GetFrame());
|
|
|
|
model_->Detach();
|
|
|
|
|
|
|
|
// Clear stored state because this object won't be deleted until a new
|
|
|
|
// context menu is created or the associated browser is destroyed.
|
|
|
|
browser_ = nullptr;
|
|
|
|
handler_ = nullptr;
|
|
|
|
params_ = nullptr;
|
|
|
|
model_ = nullptr;
|
|
|
|
iteminfomap_.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// CefSimpleMenuModelImpl::StateDelegate methods:
|
|
|
|
|
|
|
|
void SetChecked(int command_id, bool checked) override {
|
|
|
|
// No-op if already at the default state.
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!checked && !GetItemInfo(command_id)) {
|
2021-04-09 01:15:51 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
|
|
|
|
auto* info = GetOrCreateItemInfo(command_id);
|
|
|
|
info->checked = checked;
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!checked) {
|
2021-04-09 01:15:51 +02:00
|
|
|
MaybeDeleteItemInfo(command_id, info);
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetAccelerator(int command_id,
|
2021-06-04 03:34:56 +02:00
|
|
|
absl::optional<ui::Accelerator> accel) override {
|
2021-04-09 01:15:51 +02:00
|
|
|
// No-op if already at the default state.
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!accel && !GetItemInfo(command_id)) {
|
2021-04-09 01:15:51 +02:00
|
|
|
return;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
|
|
|
|
auto* info = GetOrCreateItemInfo(command_id);
|
|
|
|
info->accel = accel;
|
2023-01-02 23:59:03 +01:00
|
|
|
if (!accel) {
|
2021-04-09 01:15:51 +02:00
|
|
|
MaybeDeleteItemInfo(command_id, info);
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct ItemInfo {
|
|
|
|
ItemInfo() {}
|
|
|
|
|
|
|
|
bool checked = false;
|
2021-06-04 03:34:56 +02:00
|
|
|
absl::optional<ui::Accelerator> accel;
|
2021-04-09 01:15:51 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ItemInfo* GetItemInfo(int command_id) {
|
|
|
|
auto it = iteminfomap_.find(command_id);
|
|
|
|
if (it != iteminfomap_.end()) {
|
|
|
|
return &it->second;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ItemInfo* GetOrCreateItemInfo(int command_id) {
|
2023-01-02 23:59:03 +01:00
|
|
|
if (auto info = GetItemInfo(command_id)) {
|
2021-04-09 01:15:51 +02:00
|
|
|
return info;
|
2023-01-02 23:59:03 +01:00
|
|
|
}
|
2021-04-09 01:15:51 +02:00
|
|
|
|
|
|
|
auto result = iteminfomap_.insert(std::make_pair(command_id, ItemInfo()));
|
|
|
|
return &result.first->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaybeDeleteItemInfo(int command_id, ItemInfo* info) {
|
|
|
|
// Remove if all info has reverted to the default state.
|
|
|
|
if (!info->checked && !info->accel) {
|
|
|
|
auto it = iteminfomap_.find(command_id);
|
|
|
|
iteminfomap_.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefFrame> GetFrame() const {
|
|
|
|
CefRefPtr<CefFrame> frame;
|
|
|
|
|
|
|
|
// May return nullptr if the frame is destroyed while the menu is pending.
|
|
|
|
auto* rfh = context_menu_->GetRenderFrameHost();
|
|
|
|
if (rfh) {
|
|
|
|
frame = browser_->GetFrameForHost(rfh);
|
|
|
|
}
|
|
|
|
if (!frame) {
|
|
|
|
frame = browser_->GetMainFrame();
|
|
|
|
}
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderViewContextMenu* const context_menu_;
|
|
|
|
CefRefPtr<CefBrowserHostBase> browser_;
|
|
|
|
CefRefPtr<CefContextMenuHandler> handler_;
|
|
|
|
CefRefPtr<CefContextMenuParams> params_;
|
|
|
|
CefRefPtr<CefSimpleMenuModelImpl> model_;
|
|
|
|
|
|
|
|
// Map of command_id to ItemInfo.
|
|
|
|
using ItemInfoMap = std::map<int, ItemInfo>;
|
|
|
|
ItemInfoMap iteminfomap_;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<RenderViewContextMenuObserver> MenuCreatedCallback(
|
|
|
|
RenderViewContextMenu* context_menu) {
|
|
|
|
auto browser = CefBrowserHostBase::GetBrowserForContents(
|
|
|
|
context_menu->source_web_contents());
|
|
|
|
if (browser) {
|
|
|
|
if (auto client = browser->GetClient()) {
|
|
|
|
if (auto handler = client->GetContextMenuHandler()) {
|
|
|
|
return std::make_unique<CefContextMenuObserver>(context_menu, browser,
|
|
|
|
handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void RegisterMenuCreatedCallback() {
|
|
|
|
RenderViewContextMenu::RegisterMenuCreatedCallback(
|
|
|
|
base::BindRepeating(&MenuCreatedCallback));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace context_menu
|