1 // Copyright (c) 2021 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/simple_menu_model_impl.h"
7
8 #include <vector>
9
10 #include "libcef/browser/thread_util.h"
11 #include "libcef/common/task_runner_impl.h"
12
13 namespace {
14
15 // Value based on the documentation on SimpleMenuModel::GetIndexOfCommandId().
16 const int kInvalidIndex = -1;
17 const int kInvalidCommandId = -1;
18 const int kInvalidGroupId = -1;
19
GetCefItemType(ui::MenuModel::ItemType type)20 cef_menu_item_type_t GetCefItemType(ui::MenuModel::ItemType type) {
21 switch (type) {
22 case ui::MenuModel::TYPE_COMMAND:
23 return MENUITEMTYPE_COMMAND;
24 case ui::MenuModel::TYPE_CHECK:
25 return MENUITEMTYPE_CHECK;
26 case ui::MenuModel::TYPE_RADIO:
27 return MENUITEMTYPE_RADIO;
28 case ui::MenuModel::TYPE_SEPARATOR:
29 return MENUITEMTYPE_SEPARATOR;
30 case ui::MenuModel::TYPE_SUBMENU:
31 return MENUITEMTYPE_SUBMENU;
32 default:
33 return MENUITEMTYPE_NONE;
34 }
35 }
36
37 } // namespace
38
CefSimpleMenuModelImpl(ui::SimpleMenuModel * model,ui::SimpleMenuModel::Delegate * delegate,StateDelegate * state_delegate,bool is_owned,bool is_submenu)39 CefSimpleMenuModelImpl::CefSimpleMenuModelImpl(
40 ui::SimpleMenuModel* model,
41 ui::SimpleMenuModel::Delegate* delegate,
42 StateDelegate* state_delegate,
43 bool is_owned,
44 bool is_submenu)
45 : supported_thread_id_(base::PlatformThread::CurrentId()),
46 model_(model),
47 delegate_(delegate),
48 state_delegate_(state_delegate),
49 is_owned_(is_owned),
50 is_submenu_(is_submenu) {
51 DCHECK(model_);
52 DCHECK(delegate_);
53 DCHECK(state_delegate_);
54 }
55
~CefSimpleMenuModelImpl()56 CefSimpleMenuModelImpl::~CefSimpleMenuModelImpl() {
57 // Detach() must be called before object destruction.
58 DCHECK(!model_);
59 DCHECK(submenumap_.empty());
60 }
61
Detach()62 void CefSimpleMenuModelImpl::Detach() {
63 DCHECK(VerifyContext());
64
65 if (!submenumap_.empty()) {
66 auto it = submenumap_.begin();
67 for (; it != submenumap_.end(); ++it) {
68 it->second->Detach();
69 }
70 submenumap_.clear();
71 }
72
73 if (is_owned_)
74 delete model_;
75 model_ = nullptr;
76 }
77
IsSubMenu()78 bool CefSimpleMenuModelImpl::IsSubMenu() {
79 if (!VerifyContext())
80 return false;
81 return is_submenu_;
82 }
83
Clear()84 bool CefSimpleMenuModelImpl::Clear() {
85 if (!VerifyContext())
86 return false;
87
88 model_->Clear();
89 return true;
90 }
91
GetCount()92 int CefSimpleMenuModelImpl::GetCount() {
93 if (!VerifyContext())
94 return 0;
95
96 return model_->GetItemCount();
97 }
98
AddSeparator()99 bool CefSimpleMenuModelImpl::AddSeparator() {
100 if (!VerifyContext())
101 return false;
102
103 model_->AddSeparator(ui::NORMAL_SEPARATOR);
104 return true;
105 }
106
AddItem(int command_id,const CefString & label)107 bool CefSimpleMenuModelImpl::AddItem(int command_id, const CefString& label) {
108 if (!VerifyContext())
109 return false;
110
111 model_->AddItem(command_id, label);
112 return true;
113 }
114
AddCheckItem(int command_id,const CefString & label)115 bool CefSimpleMenuModelImpl::AddCheckItem(int command_id,
116 const CefString& label) {
117 if (!VerifyContext())
118 return false;
119
120 model_->AddCheckItem(command_id, label);
121 return true;
122 }
123
AddRadioItem(int command_id,const CefString & label,int group_id)124 bool CefSimpleMenuModelImpl::AddRadioItem(int command_id,
125 const CefString& label,
126 int group_id) {
127 if (!VerifyContext())
128 return false;
129
130 model_->AddRadioItem(command_id, label, group_id);
131 return true;
132 }
133
AddSubMenu(int command_id,const CefString & label)134 CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::AddSubMenu(
135 int command_id,
136 const CefString& label) {
137 if (!VerifyContext())
138 return nullptr;
139
140 auto new_menu = CreateNewSubMenu(nullptr);
141 model_->AddSubMenu(command_id, label, new_menu->model());
142 return new_menu;
143 }
144
InsertSeparatorAt(int index)145 bool CefSimpleMenuModelImpl::InsertSeparatorAt(int index) {
146 if (!VerifyContext())
147 return false;
148
149 model_->InsertSeparatorAt(index, ui::NORMAL_SEPARATOR);
150 return true;
151 }
152
InsertItemAt(int index,int command_id,const CefString & label)153 bool CefSimpleMenuModelImpl::InsertItemAt(int index,
154 int command_id,
155 const CefString& label) {
156 if (!VerifyContext() || !ValidIndex(index))
157 return false;
158
159 model_->InsertItemAt(index, command_id, label);
160 return true;
161 }
162
InsertCheckItemAt(int index,int command_id,const CefString & label)163 bool CefSimpleMenuModelImpl::InsertCheckItemAt(int index,
164 int command_id,
165 const CefString& label) {
166 if (!VerifyContext() || !ValidIndex(index))
167 return false;
168
169 model_->InsertCheckItemAt(index, command_id, label);
170 return true;
171 }
172
InsertRadioItemAt(int index,int command_id,const CefString & label,int group_id)173 bool CefSimpleMenuModelImpl::InsertRadioItemAt(int index,
174 int command_id,
175 const CefString& label,
176 int group_id) {
177 if (!VerifyContext() || !ValidIndex(index))
178 return false;
179
180 model_->InsertRadioItemAt(index, command_id, label, group_id);
181 return true;
182 }
183
InsertSubMenuAt(int index,int command_id,const CefString & label)184 CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::InsertSubMenuAt(
185 int index,
186 int command_id,
187 const CefString& label) {
188 if (!VerifyContext() || !ValidIndex(index))
189 return nullptr;
190
191 auto new_menu = CreateNewSubMenu(nullptr);
192 model_->InsertSubMenuAt(index, command_id, label, new_menu->model());
193 return new_menu;
194 }
195
Remove(int command_id)196 bool CefSimpleMenuModelImpl::Remove(int command_id) {
197 return RemoveAt(GetIndexOf(command_id));
198 }
199
RemoveAt(int index)200 bool CefSimpleMenuModelImpl::RemoveAt(int index) {
201 if (!VerifyContext() || !ValidIndex(index))
202 return false;
203
204 auto* sub_menu =
205 static_cast<ui::SimpleMenuModel*>(model_->GetSubmenuModelAt(index));
206 if (sub_menu) {
207 auto it = submenumap_.find(sub_menu);
208 if (it != submenumap_.end()) {
209 it->second->Detach();
210 submenumap_.erase(it);
211 }
212 }
213
214 model_->RemoveItemAt(index);
215 return true;
216 }
217
GetIndexOf(int command_id)218 int CefSimpleMenuModelImpl::GetIndexOf(int command_id) {
219 if (!VerifyContext())
220 return kInvalidIndex;
221
222 return model_->GetIndexOfCommandId(command_id);
223 }
224
GetCommandIdAt(int index)225 int CefSimpleMenuModelImpl::GetCommandIdAt(int index) {
226 if (!VerifyContext() || !ValidIndex(index))
227 return kInvalidCommandId;
228
229 return model_->GetCommandIdAt(index);
230 }
231
SetCommandIdAt(int index,int command_id)232 bool CefSimpleMenuModelImpl::SetCommandIdAt(int index, int command_id) {
233 NOTIMPLEMENTED();
234 return false;
235 }
236
GetLabel(int command_id)237 CefString CefSimpleMenuModelImpl::GetLabel(int command_id) {
238 return GetLabelAt(GetIndexOf(command_id));
239 }
240
GetLabelAt(int index)241 CefString CefSimpleMenuModelImpl::GetLabelAt(int index) {
242 if (!VerifyContext() || !ValidIndex(index))
243 return CefString();
244
245 return model_->GetLabelAt(index);
246 }
247
SetLabel(int command_id,const CefString & label)248 bool CefSimpleMenuModelImpl::SetLabel(int command_id, const CefString& label) {
249 return SetLabelAt(GetIndexOf(command_id), label);
250 }
251
SetLabelAt(int index,const CefString & label)252 bool CefSimpleMenuModelImpl::SetLabelAt(int index, const CefString& label) {
253 if (!VerifyContext() || !ValidIndex(index))
254 return false;
255
256 model_->SetLabel(index, label);
257 return true;
258 }
259
GetType(int command_id)260 CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetType(
261 int command_id) {
262 return GetTypeAt(GetIndexOf(command_id));
263 }
264
GetTypeAt(int index)265 CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetTypeAt(
266 int index) {
267 if (!VerifyContext() || !ValidIndex(index))
268 return MENUITEMTYPE_NONE;
269
270 return GetCefItemType(model_->GetTypeAt(index));
271 }
272
GetGroupId(int command_id)273 int CefSimpleMenuModelImpl::GetGroupId(int command_id) {
274 return GetGroupIdAt(GetIndexOf(command_id));
275 }
276
GetGroupIdAt(int index)277 int CefSimpleMenuModelImpl::GetGroupIdAt(int index) {
278 if (!VerifyContext() || !ValidIndex(index))
279 return kInvalidGroupId;
280
281 return model_->GetGroupIdAt(index);
282 }
283
SetGroupId(int command_id,int group_id)284 bool CefSimpleMenuModelImpl::SetGroupId(int command_id, int group_id) {
285 return SetGroupIdAt(GetIndexOf(command_id), group_id);
286 }
287
SetGroupIdAt(int index,int group_id)288 bool CefSimpleMenuModelImpl::SetGroupIdAt(int index, int group_id) {
289 NOTIMPLEMENTED();
290 return false;
291 }
292
GetSubMenu(int command_id)293 CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::GetSubMenu(int command_id) {
294 return GetSubMenuAt(GetIndexOf(command_id));
295 }
296
GetSubMenuAt(int index)297 CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::GetSubMenuAt(int index) {
298 if (!VerifyContext() || !ValidIndex(index))
299 return nullptr;
300
301 auto* sub_model =
302 static_cast<ui::SimpleMenuModel*>(model_->GetSubmenuModelAt(index));
303 auto it = submenumap_.find(sub_model);
304 if (it != submenumap_.end())
305 return it->second;
306 return CreateNewSubMenu(sub_model);
307 }
308
IsVisible(int command_id)309 bool CefSimpleMenuModelImpl::IsVisible(int command_id) {
310 return IsVisibleAt(GetIndexOf(command_id));
311 }
312
IsVisibleAt(int index)313 bool CefSimpleMenuModelImpl::IsVisibleAt(int index) {
314 if (!VerifyContext() || !ValidIndex(index))
315 return false;
316
317 return model_->IsVisibleAt(index);
318 }
319
SetVisible(int command_id,bool visible)320 bool CefSimpleMenuModelImpl::SetVisible(int command_id, bool visible) {
321 return SetVisibleAt(GetIndexOf(command_id), visible);
322 }
323
SetVisibleAt(int index,bool visible)324 bool CefSimpleMenuModelImpl::SetVisibleAt(int index, bool visible) {
325 if (!VerifyContext() || !ValidIndex(index))
326 return false;
327
328 model_->SetVisibleAt(index, visible);
329 return true;
330 }
331
IsEnabled(int command_id)332 bool CefSimpleMenuModelImpl::IsEnabled(int command_id) {
333 return IsEnabledAt(GetIndexOf(command_id));
334 }
335
IsEnabledAt(int index)336 bool CefSimpleMenuModelImpl::IsEnabledAt(int index) {
337 if (!VerifyContext() || !ValidIndex(index))
338 return false;
339
340 return model_->IsEnabledAt(index);
341 }
342
SetEnabled(int command_id,bool enabled)343 bool CefSimpleMenuModelImpl::SetEnabled(int command_id, bool enabled) {
344 return SetEnabledAt(GetIndexOf(command_id), enabled);
345 }
346
SetEnabledAt(int index,bool enabled)347 bool CefSimpleMenuModelImpl::SetEnabledAt(int index, bool enabled) {
348 if (!VerifyContext() || !ValidIndex(index))
349 return false;
350
351 model_->SetEnabledAt(index, enabled);
352 return true;
353 }
354
IsChecked(int command_id)355 bool CefSimpleMenuModelImpl::IsChecked(int command_id) {
356 return IsCheckedAt(GetIndexOf(command_id));
357 }
358
IsCheckedAt(int index)359 bool CefSimpleMenuModelImpl::IsCheckedAt(int index) {
360 if (!VerifyContext() || !ValidIndex(index))
361 return false;
362
363 return model_->IsItemCheckedAt(index);
364 }
365
SetChecked(int command_id,bool checked)366 bool CefSimpleMenuModelImpl::SetChecked(int command_id, bool checked) {
367 if (!VerifyContext() || command_id == kInvalidIndex)
368 return false;
369
370 state_delegate_->SetChecked(command_id, checked);
371 return true;
372 }
373
SetCheckedAt(int index,bool checked)374 bool CefSimpleMenuModelImpl::SetCheckedAt(int index, bool checked) {
375 return SetChecked(GetCommandIdAt(index), checked);
376 }
377
HasAccelerator(int command_id)378 bool CefSimpleMenuModelImpl::HasAccelerator(int command_id) {
379 return HasAcceleratorAt(GetIndexOf(command_id));
380 }
381
HasAcceleratorAt(int index)382 bool CefSimpleMenuModelImpl::HasAcceleratorAt(int index) {
383 if (!VerifyContext() || !ValidIndex(index))
384 return false;
385
386 ui::Accelerator accelerator;
387 return model_->GetAcceleratorAt(index, &accelerator);
388 }
389
SetAccelerator(int command_id,int key_code,bool shift_pressed,bool ctrl_pressed,bool alt_pressed)390 bool CefSimpleMenuModelImpl::SetAccelerator(int command_id,
391 int key_code,
392 bool shift_pressed,
393 bool ctrl_pressed,
394 bool alt_pressed) {
395 if (!VerifyContext() || command_id == kInvalidIndex)
396 return false;
397
398 int modifiers = 0;
399 if (shift_pressed)
400 modifiers |= ui::EF_SHIFT_DOWN;
401 if (ctrl_pressed)
402 modifiers |= ui::EF_CONTROL_DOWN;
403 if (alt_pressed)
404 modifiers |= ui::EF_ALT_DOWN;
405
406 state_delegate_->SetAccelerator(
407 command_id,
408 ui::Accelerator(static_cast<ui::KeyboardCode>(key_code), modifiers));
409 return true;
410 }
411
SetAcceleratorAt(int index,int key_code,bool shift_pressed,bool ctrl_pressed,bool alt_pressed)412 bool CefSimpleMenuModelImpl::SetAcceleratorAt(int index,
413 int key_code,
414 bool shift_pressed,
415 bool ctrl_pressed,
416 bool alt_pressed) {
417 return SetAccelerator(GetCommandIdAt(index), key_code, shift_pressed,
418 ctrl_pressed, alt_pressed);
419 }
420
RemoveAccelerator(int command_id)421 bool CefSimpleMenuModelImpl::RemoveAccelerator(int command_id) {
422 if (!VerifyContext() || command_id == kInvalidIndex)
423 return false;
424 state_delegate_->SetAccelerator(command_id, absl::nullopt);
425 return true;
426 }
427
RemoveAcceleratorAt(int index)428 bool CefSimpleMenuModelImpl::RemoveAcceleratorAt(int index) {
429 return RemoveAccelerator(GetCommandIdAt(index));
430 }
431
GetAccelerator(int command_id,int & key_code,bool & shift_pressed,bool & ctrl_pressed,bool & alt_pressed)432 bool CefSimpleMenuModelImpl::GetAccelerator(int command_id,
433 int& key_code,
434 bool& shift_pressed,
435 bool& ctrl_pressed,
436 bool& alt_pressed) {
437 return GetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
438 ctrl_pressed, alt_pressed);
439 }
440
GetAcceleratorAt(int index,int & key_code,bool & shift_pressed,bool & ctrl_pressed,bool & alt_pressed)441 bool CefSimpleMenuModelImpl::GetAcceleratorAt(int index,
442 int& key_code,
443 bool& shift_pressed,
444 bool& ctrl_pressed,
445 bool& alt_pressed) {
446 if (!VerifyContext() || !ValidIndex(index))
447 return false;
448
449 ui::Accelerator accel;
450 if (model_->GetAcceleratorAt(index, &accel)) {
451 key_code = accel.key_code();
452 shift_pressed = accel.modifiers() & ui::EF_SHIFT_DOWN;
453 ctrl_pressed = accel.modifiers() & ui::EF_CONTROL_DOWN;
454 alt_pressed = accel.modifiers() & ui::EF_ALT_DOWN;
455 return true;
456 }
457 return false;
458 }
459
SetColor(int command_id,cef_menu_color_type_t color_type,cef_color_t color)460 bool CefSimpleMenuModelImpl::SetColor(int command_id,
461 cef_menu_color_type_t color_type,
462 cef_color_t color) {
463 NOTIMPLEMENTED();
464 return false;
465 }
466
SetColorAt(int index,cef_menu_color_type_t color_type,cef_color_t color)467 bool CefSimpleMenuModelImpl::SetColorAt(int index,
468 cef_menu_color_type_t color_type,
469 cef_color_t color) {
470 NOTIMPLEMENTED();
471 return false;
472 }
473
GetColor(int command_id,cef_menu_color_type_t color_type,cef_color_t & color)474 bool CefSimpleMenuModelImpl::GetColor(int command_id,
475 cef_menu_color_type_t color_type,
476 cef_color_t& color) {
477 NOTIMPLEMENTED();
478 return false;
479 }
480
GetColorAt(int index,cef_menu_color_type_t color_type,cef_color_t & color)481 bool CefSimpleMenuModelImpl::GetColorAt(int index,
482 cef_menu_color_type_t color_type,
483 cef_color_t& color) {
484 NOTIMPLEMENTED();
485 return false;
486 }
487
SetFontList(int command_id,const CefString & font_list)488 bool CefSimpleMenuModelImpl::SetFontList(int command_id,
489 const CefString& font_list) {
490 NOTIMPLEMENTED();
491 return false;
492 }
493
SetFontListAt(int index,const CefString & font_list)494 bool CefSimpleMenuModelImpl::SetFontListAt(int index,
495 const CefString& font_list) {
496 NOTIMPLEMENTED();
497 return false;
498 }
499
VerifyContext()500 bool CefSimpleMenuModelImpl::VerifyContext() {
501 if (base::PlatformThread::CurrentId() != supported_thread_id_) {
502 // This object should only be accessed from the thread that created it.
503 NOTREACHED();
504 return false;
505 }
506
507 if (!model_)
508 return false;
509
510 return true;
511 }
512
ValidIndex(int index)513 bool CefSimpleMenuModelImpl::ValidIndex(int index) {
514 return index > kInvalidIndex && index < model_->GetItemCount();
515 }
516
CreateNewSubMenu(ui::SimpleMenuModel * model)517 CefRefPtr<CefSimpleMenuModelImpl> CefSimpleMenuModelImpl::CreateNewSubMenu(
518 ui::SimpleMenuModel* model) {
519 bool is_owned = false;
520 if (!model) {
521 model = new ui::SimpleMenuModel(delegate_);
522 is_owned = true;
523 }
524
525 CefRefPtr<CefSimpleMenuModelImpl> new_impl = new CefSimpleMenuModelImpl(
526 model, delegate_, state_delegate_, is_owned, /*is_submodel=*/true);
527 submenumap_.insert(std::make_pair(model, new_impl));
528 return new_impl;
529 }
530