• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2021 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4 
5 #include "libcef/browser/chrome/chrome_context_menu_handler.h"
6 
7 #include "libcef/browser/browser_host_base.h"
8 #include "libcef/browser/context_menu_params_impl.h"
9 #include "libcef/browser/simple_menu_model_impl.h"
10 
11 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
12 
13 namespace context_menu {
14 
15 namespace {
16 
17 // Lifespan is controlled by RenderViewContextMenu.
18 class CefContextMenuObserver : public RenderViewContextMenuObserver,
19                                public CefSimpleMenuModelImpl::StateDelegate {
20  public:
CefContextMenuObserver(RenderViewContextMenu * context_menu,CefRefPtr<CefBrowserHostBase> browser,CefRefPtr<CefContextMenuHandler> handler)21   CefContextMenuObserver(RenderViewContextMenu* context_menu,
22                          CefRefPtr<CefBrowserHostBase> browser,
23                          CefRefPtr<CefContextMenuHandler> handler)
24       : context_menu_(context_menu), browser_(browser), handler_(handler) {}
25 
26   CefContextMenuObserver(const CefContextMenuObserver&) = delete;
27   CefContextMenuObserver& operator=(const CefContextMenuObserver&) = delete;
28 
29   // RenderViewContextMenuObserver methods:
30 
InitMenu(const content::ContextMenuParams & params)31   void InitMenu(const content::ContextMenuParams& params) override {
32     params_ = new CefContextMenuParamsImpl(
33         const_cast<content::ContextMenuParams*>(&context_menu_->params()));
34     model_ = new CefSimpleMenuModelImpl(
35         const_cast<ui::SimpleMenuModel*>(&context_menu_->menu_model()),
36         context_menu_, this, /*is_owned=*/false, /*is_popup=*/false);
37 
38     handler_->OnBeforeContextMenu(browser_, GetFrame(), params_, model_);
39   }
40 
IsCommandIdSupported(int command_id)41   bool IsCommandIdSupported(int command_id) override {
42     // Always claim support for the reserved user ID range.
43     if (command_id >= MENU_ID_USER_FIRST && command_id <= MENU_ID_USER_LAST)
44       return true;
45 
46     // Also claim support in specific cases where an ItemInfo exists.
47     return GetItemInfo(command_id) != nullptr;
48   }
49 
50   // Only called if IsCommandIdSupported() returns true.
IsCommandIdEnabled(int command_id)51   bool IsCommandIdEnabled(int command_id) override {
52     // Always return true to use the SimpleMenuModel state.
53     return true;
54   }
55 
56   // Only called if IsCommandIdSupported() returns true.
IsCommandIdChecked(int command_id)57   bool IsCommandIdChecked(int command_id) override {
58     auto* info = GetItemInfo(command_id);
59     return info ? info->checked : false;
60   }
61 
62   // Only called if IsCommandIdSupported() returns true.
GetAccelerator(int command_id,ui::Accelerator * accel)63   bool GetAccelerator(int command_id, ui::Accelerator* accel) override {
64     auto* info = GetItemInfo(command_id);
65     if (info && info->accel) {
66       *accel = *info->accel;
67       return true;
68     }
69     return false;
70   }
71 
CommandWillBeExecuted(int command_id)72   void CommandWillBeExecuted(int command_id) override {
73     if (handler_->OnContextMenuCommand(browser_, GetFrame(), params_,
74                                        command_id, EVENTFLAG_NONE)) {
75       // Create an ItemInfo so that we get the ExecuteCommand() callback
76       // instead of the default handler.
77       GetOrCreateItemInfo(command_id);
78     }
79   }
80 
81   // Only called if IsCommandIdSupported() returns true.
ExecuteCommand(int command_id)82   void ExecuteCommand(int command_id) override {
83     auto* info = GetItemInfo(command_id);
84     if (info) {
85       // In case it was added in CommandWillBeExecuted().
86       MaybeDeleteItemInfo(command_id, info);
87     }
88   }
89 
OnMenuClosed()90   void OnMenuClosed() override {
91     handler_->OnContextMenuDismissed(browser_, GetFrame());
92     model_->Detach();
93 
94     // Clear stored state because this object won't be deleted until a new
95     // context menu is created or the associated browser is destroyed.
96     browser_ = nullptr;
97     handler_ = nullptr;
98     params_ = nullptr;
99     model_ = nullptr;
100     iteminfomap_.clear();
101   }
102 
103   // CefSimpleMenuModelImpl::StateDelegate methods:
104 
SetChecked(int command_id,bool checked)105   void SetChecked(int command_id, bool checked) override {
106     // No-op if already at the default state.
107     if (!checked && !GetItemInfo(command_id))
108       return;
109 
110     auto* info = GetOrCreateItemInfo(command_id);
111     info->checked = checked;
112     if (!checked)
113       MaybeDeleteItemInfo(command_id, info);
114   }
115 
SetAccelerator(int command_id,absl::optional<ui::Accelerator> accel)116   void SetAccelerator(int command_id,
117                       absl::optional<ui::Accelerator> accel) override {
118     // No-op if already at the default state.
119     if (!accel && !GetItemInfo(command_id))
120       return;
121 
122     auto* info = GetOrCreateItemInfo(command_id);
123     info->accel = accel;
124     if (!accel)
125       MaybeDeleteItemInfo(command_id, info);
126   }
127 
128  private:
129   struct ItemInfo {
ItemInfocontext_menu::__anon140a525b0111::CefContextMenuObserver::ItemInfo130     ItemInfo() {}
131 
132     bool checked = false;
133     absl::optional<ui::Accelerator> accel;
134   };
135 
GetItemInfo(int command_id)136   ItemInfo* GetItemInfo(int command_id) {
137     auto it = iteminfomap_.find(command_id);
138     if (it != iteminfomap_.end()) {
139       return &it->second;
140     }
141     return nullptr;
142   }
143 
GetOrCreateItemInfo(int command_id)144   ItemInfo* GetOrCreateItemInfo(int command_id) {
145     if (auto info = GetItemInfo(command_id))
146       return info;
147 
148     auto result = iteminfomap_.insert(std::make_pair(command_id, ItemInfo()));
149     return &result.first->second;
150   }
151 
MaybeDeleteItemInfo(int command_id,ItemInfo * info)152   void MaybeDeleteItemInfo(int command_id, ItemInfo* info) {
153     // Remove if all info has reverted to the default state.
154     if (!info->checked && !info->accel) {
155       auto it = iteminfomap_.find(command_id);
156       iteminfomap_.erase(it);
157     }
158   }
159 
GetFrame() const160   CefRefPtr<CefFrame> GetFrame() const {
161     CefRefPtr<CefFrame> frame;
162 
163     // May return nullptr if the frame is destroyed while the menu is pending.
164     auto* rfh = context_menu_->GetRenderFrameHost();
165     if (rfh) {
166       frame = browser_->GetFrameForHost(rfh);
167     }
168     if (!frame) {
169       frame = browser_->GetMainFrame();
170     }
171     return frame;
172   }
173 
174   RenderViewContextMenu* const context_menu_;
175   CefRefPtr<CefBrowserHostBase> browser_;
176   CefRefPtr<CefContextMenuHandler> handler_;
177   CefRefPtr<CefContextMenuParams> params_;
178   CefRefPtr<CefSimpleMenuModelImpl> model_;
179 
180   // Map of command_id to ItemInfo.
181   using ItemInfoMap = std::map<int, ItemInfo>;
182   ItemInfoMap iteminfomap_;
183 };
184 
MenuCreatedCallback(RenderViewContextMenu * context_menu)185 std::unique_ptr<RenderViewContextMenuObserver> MenuCreatedCallback(
186     RenderViewContextMenu* context_menu) {
187   auto browser = CefBrowserHostBase::GetBrowserForContents(
188       context_menu->source_web_contents());
189   if (browser) {
190     if (auto client = browser->GetClient()) {
191       if (auto handler = client->GetContextMenuHandler()) {
192         return std::make_unique<CefContextMenuObserver>(context_menu, browser,
193                                                         handler);
194       }
195     }
196   }
197 
198   return nullptr;
199 }
200 
201 }  // namespace
202 
RegisterMenuCreatedCallback()203 void RegisterMenuCreatedCallback() {
204   RenderViewContextMenu::RegisterMenuCreatedCallback(
205       base::BindRepeating(&MenuCreatedCallback));
206 }
207 
208 }  // namespace context_menu
209