1 // Copyright (c) 2012 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/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 #include "base/bind.h"
14 #include "base/logging.h"
15 #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"
16 #include "ui/base/accelerators/accelerator.h"
17 #include "ui/base/models/image_model.h"
18 #include "ui/gfx/geometry/point.h"
19 #include "ui/gfx/image/image.h"
20
21 namespace {
22
23 const int kSeparatorId = -1;
24 const int kInvalidGroupId = -1;
25 const int kInvalidCommandId = -1;
26 const int kDefaultIndex = -1;
27 const int kInvalidIndex = -2;
28
29 // A simple MenuModel implementation that delegates to CefMenuModelImpl.
30 class CefSimpleMenuModel : public ui::MenuModel {
31 public:
32 // The Delegate can be NULL, though if it is items can't be checked or
33 // disabled.
CefSimpleMenuModel(CefMenuModelImpl * impl)34 explicit CefSimpleMenuModel(CefMenuModelImpl* impl) : impl_(impl) {}
35
36 CefSimpleMenuModel(const CefSimpleMenuModel&) = delete;
37 CefSimpleMenuModel& operator=(const CefSimpleMenuModel&) = delete;
38
39 // MenuModel methods.
HasIcons() const40 bool HasIcons() const override { return false; }
41
GetItemCount() const42 int GetItemCount() const override { return impl_->GetCount(); }
43
GetTypeAt(int index) const44 ItemType GetTypeAt(int index) const override {
45 switch (impl_->GetTypeAt(index)) {
46 case MENUITEMTYPE_COMMAND:
47 return TYPE_COMMAND;
48 case MENUITEMTYPE_CHECK:
49 return TYPE_CHECK;
50 case MENUITEMTYPE_RADIO:
51 return TYPE_RADIO;
52 case MENUITEMTYPE_SEPARATOR:
53 return TYPE_SEPARATOR;
54 case MENUITEMTYPE_SUBMENU:
55 return TYPE_SUBMENU;
56 default:
57 NOTREACHED();
58 return TYPE_COMMAND;
59 }
60 }
61
GetSeparatorTypeAt(int index) const62 ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
63 return ui::NORMAL_SEPARATOR;
64 }
65
GetCommandIdAt(int index) const66 int GetCommandIdAt(int index) const override {
67 return impl_->GetCommandIdAt(index);
68 }
69
GetLabelAt(int index) const70 std::u16string GetLabelAt(int index) const override {
71 return impl_->GetFormattedLabelAt(index);
72 }
73
IsItemDynamicAt(int index) const74 bool IsItemDynamicAt(int index) const override { return false; }
75
GetLabelFontListAt(int index) const76 const gfx::FontList* GetLabelFontListAt(int index) const override {
77 return impl_->GetLabelFontListAt(index);
78 }
79
GetAcceleratorAt(int index,ui::Accelerator * accelerator) const80 bool GetAcceleratorAt(int index,
81 ui::Accelerator* accelerator) const override {
82 int key_code = 0;
83 bool shift_pressed = false;
84 bool ctrl_pressed = false;
85 bool alt_pressed = false;
86 if (impl_->GetAcceleratorAt(index, key_code, shift_pressed, ctrl_pressed,
87 alt_pressed)) {
88 int modifiers = 0;
89 if (shift_pressed)
90 modifiers |= ui::EF_SHIFT_DOWN;
91 if (ctrl_pressed)
92 modifiers |= ui::EF_CONTROL_DOWN;
93 if (alt_pressed)
94 modifiers |= ui::EF_ALT_DOWN;
95
96 *accelerator =
97 ui::Accelerator(static_cast<ui::KeyboardCode>(key_code), modifiers);
98 return true;
99 }
100 return false;
101 }
102
IsItemCheckedAt(int index) const103 bool IsItemCheckedAt(int index) const override {
104 return impl_->IsCheckedAt(index);
105 }
106
GetGroupIdAt(int index) const107 int GetGroupIdAt(int index) const override {
108 return impl_->GetGroupIdAt(index);
109 }
110
GetIconAt(int index) const111 ui::ImageModel GetIconAt(int index) const override {
112 return ui::ImageModel();
113 }
114
GetButtonMenuItemAt(int index) const115 ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
116 return nullptr;
117 }
118
IsEnabledAt(int index) const119 bool IsEnabledAt(int index) const override {
120 return impl_->IsEnabledAt(index);
121 }
122
IsVisibleAt(int index) const123 bool IsVisibleAt(int index) const override {
124 return impl_->IsVisibleAt(index);
125 }
126
ActivatedAt(int index)127 void ActivatedAt(int index) override { ActivatedAt(index, 0); }
128
ActivatedAt(int index,int event_flags)129 void ActivatedAt(int index, int event_flags) override {
130 impl_->ActivatedAt(index, static_cast<cef_event_flags_t>(event_flags));
131 }
132
GetSubmenuModelAt(int index) const133 MenuModel* GetSubmenuModelAt(int index) const override {
134 CefRefPtr<CefMenuModel> submenu = impl_->GetSubMenuAt(index);
135 if (submenu.get())
136 return static_cast<CefMenuModelImpl*>(submenu.get())->model();
137 return nullptr;
138 }
139
MouseOutsideMenu(const gfx::Point & screen_point)140 void MouseOutsideMenu(const gfx::Point& screen_point) override {
141 impl_->MouseOutsideMenu(screen_point);
142 }
143
UnhandledOpenSubmenu(bool is_rtl)144 void UnhandledOpenSubmenu(bool is_rtl) override {
145 impl_->UnhandledOpenSubmenu(is_rtl);
146 }
147
UnhandledCloseSubmenu(bool is_rtl)148 void UnhandledCloseSubmenu(bool is_rtl) override {
149 impl_->UnhandledCloseSubmenu(is_rtl);
150 }
151
GetTextColor(int index,bool is_minor,bool is_hovered,SkColor * override_color) const152 bool GetTextColor(int index,
153 bool is_minor,
154 bool is_hovered,
155 SkColor* override_color) const override {
156 return impl_->GetTextColor(index, is_minor, is_hovered, override_color);
157 }
158
GetBackgroundColor(int index,bool is_hovered,SkColor * override_color) const159 bool GetBackgroundColor(int index,
160 bool is_hovered,
161 SkColor* override_color) const override {
162 return impl_->GetBackgroundColor(index, is_hovered, override_color);
163 }
164
MenuWillShow()165 void MenuWillShow() override { impl_->MenuWillShow(); }
166
MenuWillClose()167 void MenuWillClose() override { impl_->MenuWillClose(); }
168
169 private:
170 CefMenuModelImpl* impl_;
171 };
172
GetMenuColorType(bool is_text,bool is_accelerator,bool is_hovered)173 cef_menu_color_type_t GetMenuColorType(bool is_text,
174 bool is_accelerator,
175 bool is_hovered) {
176 if (is_text) {
177 if (is_accelerator) {
178 return is_hovered ? CEF_MENU_COLOR_TEXT_ACCELERATOR_HOVERED
179 : CEF_MENU_COLOR_TEXT_ACCELERATOR;
180 }
181 return is_hovered ? CEF_MENU_COLOR_TEXT_HOVERED : CEF_MENU_COLOR_TEXT;
182 }
183
184 DCHECK(!is_accelerator);
185 return is_hovered ? CEF_MENU_COLOR_BACKGROUND_HOVERED
186 : CEF_MENU_COLOR_BACKGROUND;
187 }
188
189 } // namespace
190
191 // static
CreateMenuModel(CefRefPtr<CefMenuModelDelegate> delegate)192 CefRefPtr<CefMenuModel> CefMenuModel::CreateMenuModel(
193 CefRefPtr<CefMenuModelDelegate> delegate) {
194 CEF_REQUIRE_UIT_RETURN(nullptr);
195 DCHECK(delegate);
196 if (!delegate)
197 return nullptr;
198
199 CefRefPtr<CefMenuModelImpl> menu_model =
200 new CefMenuModelImpl(nullptr, delegate, false);
201 return menu_model;
202 }
203
204 struct CefMenuModelImpl::Item {
ItemCefMenuModelImpl::Item205 Item(cef_menu_item_type_t type,
206 int command_id,
207 const CefString& label,
208 int group_id)
209 : type_(type),
210 command_id_(command_id),
211 label_(label),
212 group_id_(group_id) {}
213
214 // Basic information.
215 cef_menu_item_type_t type_;
216 int command_id_;
217 CefString label_;
218 int group_id_;
219 CefRefPtr<CefMenuModelImpl> submenu_;
220
221 // State information.
222 bool enabled_ = true;
223 bool visible_ = true;
224 bool checked_ = false;
225
226 // Accelerator information.
227 bool has_accelerator_ = false;
228 int key_code_ = 0;
229 bool shift_pressed_ = false;
230 bool ctrl_pressed_ = false;
231 bool alt_pressed_ = false;
232
233 cef_color_t colors_[CEF_MENU_COLOR_COUNT] = {0};
234 gfx::FontList font_list_;
235 bool has_font_list_ = false;
236 };
237
CefMenuModelImpl(Delegate * delegate,CefRefPtr<CefMenuModelDelegate> menu_model_delegate,bool is_submenu)238 CefMenuModelImpl::CefMenuModelImpl(
239 Delegate* delegate,
240 CefRefPtr<CefMenuModelDelegate> menu_model_delegate,
241 bool is_submenu)
242 : supported_thread_id_(base::PlatformThread::CurrentId()),
243 delegate_(delegate),
244 menu_model_delegate_(menu_model_delegate),
245 is_submenu_(is_submenu) {
246 DCHECK(delegate_ || menu_model_delegate_);
247 model_.reset(new CefSimpleMenuModel(this));
248 }
249
~CefMenuModelImpl()250 CefMenuModelImpl::~CefMenuModelImpl() {}
251
IsSubMenu()252 bool CefMenuModelImpl::IsSubMenu() {
253 if (!VerifyContext())
254 return false;
255 return is_submenu_;
256 }
257
Clear()258 bool CefMenuModelImpl::Clear() {
259 if (!VerifyContext())
260 return false;
261
262 items_.clear();
263 return true;
264 }
265
GetCount()266 int CefMenuModelImpl::GetCount() {
267 if (!VerifyContext())
268 return 0;
269
270 return static_cast<int>(items_.size());
271 }
272
AddSeparator()273 bool CefMenuModelImpl::AddSeparator() {
274 if (!VerifyContext())
275 return false;
276
277 AppendItem(
278 Item(MENUITEMTYPE_SEPARATOR, kSeparatorId, CefString(), kInvalidGroupId));
279 return true;
280 }
281
AddItem(int command_id,const CefString & label)282 bool CefMenuModelImpl::AddItem(int command_id, const CefString& label) {
283 if (!VerifyContext())
284 return false;
285
286 AppendItem(Item(MENUITEMTYPE_COMMAND, command_id, label, kInvalidGroupId));
287 return true;
288 }
289
AddCheckItem(int command_id,const CefString & label)290 bool CefMenuModelImpl::AddCheckItem(int command_id, const CefString& label) {
291 if (!VerifyContext())
292 return false;
293
294 AppendItem(Item(MENUITEMTYPE_CHECK, command_id, label, kInvalidGroupId));
295 return true;
296 }
297
AddRadioItem(int command_id,const CefString & label,int group_id)298 bool CefMenuModelImpl::AddRadioItem(int command_id,
299 const CefString& label,
300 int group_id) {
301 if (!VerifyContext())
302 return false;
303
304 AppendItem(Item(MENUITEMTYPE_RADIO, command_id, label, group_id));
305 return true;
306 }
307
AddSubMenu(int command_id,const CefString & label)308 CefRefPtr<CefMenuModel> CefMenuModelImpl::AddSubMenu(int command_id,
309 const CefString& label) {
310 if (!VerifyContext())
311 return nullptr;
312
313 Item item(MENUITEMTYPE_SUBMENU, command_id, label, kInvalidGroupId);
314 item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
315 AppendItem(item);
316 return item.submenu_.get();
317 }
318
InsertSeparatorAt(int index)319 bool CefMenuModelImpl::InsertSeparatorAt(int index) {
320 if (!VerifyContext())
321 return false;
322
323 InsertItemAt(
324 Item(MENUITEMTYPE_SEPARATOR, kSeparatorId, CefString(), kInvalidGroupId),
325 index);
326 return true;
327 }
328
InsertItemAt(int index,int command_id,const CefString & label)329 bool CefMenuModelImpl::InsertItemAt(int index,
330 int command_id,
331 const CefString& label) {
332 if (!VerifyContext())
333 return false;
334
335 InsertItemAt(Item(MENUITEMTYPE_COMMAND, command_id, label, kInvalidGroupId),
336 index);
337 return true;
338 }
339
InsertCheckItemAt(int index,int command_id,const CefString & label)340 bool CefMenuModelImpl::InsertCheckItemAt(int index,
341 int command_id,
342 const CefString& label) {
343 if (!VerifyContext())
344 return false;
345
346 InsertItemAt(Item(MENUITEMTYPE_CHECK, command_id, label, kInvalidGroupId),
347 index);
348 return true;
349 }
350
InsertRadioItemAt(int index,int command_id,const CefString & label,int group_id)351 bool CefMenuModelImpl::InsertRadioItemAt(int index,
352 int command_id,
353 const CefString& label,
354 int group_id) {
355 if (!VerifyContext())
356 return false;
357
358 InsertItemAt(Item(MENUITEMTYPE_RADIO, command_id, label, group_id), index);
359 return true;
360 }
361
InsertSubMenuAt(int index,int command_id,const CefString & label)362 CefRefPtr<CefMenuModel> CefMenuModelImpl::InsertSubMenuAt(
363 int index,
364 int command_id,
365 const CefString& label) {
366 if (!VerifyContext())
367 return nullptr;
368
369 Item item(MENUITEMTYPE_SUBMENU, command_id, label, kInvalidGroupId);
370 item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
371 InsertItemAt(item, index);
372 return item.submenu_.get();
373 }
374
Remove(int command_id)375 bool CefMenuModelImpl::Remove(int command_id) {
376 return RemoveAt(GetIndexOf(command_id));
377 }
378
RemoveAt(int index)379 bool CefMenuModelImpl::RemoveAt(int index) {
380 if (!VerifyContext())
381 return false;
382
383 if (index >= 0 && index < static_cast<int>(items_.size())) {
384 items_.erase(items_.begin() + index);
385 return true;
386 }
387 return false;
388 }
389
GetIndexOf(int command_id)390 int CefMenuModelImpl::GetIndexOf(int command_id) {
391 if (!VerifyContext())
392 return kInvalidIndex;
393
394 for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) {
395 if ((*i).command_id_ == command_id) {
396 return static_cast<int>(std::distance(items_.begin(), i));
397 }
398 }
399 return kInvalidIndex;
400 }
401
GetCommandIdAt(int index)402 int CefMenuModelImpl::GetCommandIdAt(int index) {
403 if (!VerifyContext())
404 return kInvalidCommandId;
405
406 if (index >= 0 && index < static_cast<int>(items_.size()))
407 return items_[index].command_id_;
408 return kInvalidCommandId;
409 }
410
SetCommandIdAt(int index,int command_id)411 bool CefMenuModelImpl::SetCommandIdAt(int index, int command_id) {
412 if (!VerifyContext())
413 return false;
414
415 if (index >= 0 && index < static_cast<int>(items_.size())) {
416 items_[index].command_id_ = command_id;
417 return true;
418 }
419 return false;
420 }
421
GetLabel(int command_id)422 CefString CefMenuModelImpl::GetLabel(int command_id) {
423 return GetLabelAt(GetIndexOf(command_id));
424 }
425
GetLabelAt(int index)426 CefString CefMenuModelImpl::GetLabelAt(int index) {
427 if (!VerifyContext())
428 return CefString();
429
430 if (index >= 0 && index < static_cast<int>(items_.size()))
431 return items_[index].label_;
432 return CefString();
433 }
434
SetLabel(int command_id,const CefString & label)435 bool CefMenuModelImpl::SetLabel(int command_id, const CefString& label) {
436 return SetLabelAt(GetIndexOf(command_id), label);
437 }
438
SetLabelAt(int index,const CefString & label)439 bool CefMenuModelImpl::SetLabelAt(int index, const CefString& label) {
440 if (!VerifyContext())
441 return false;
442
443 if (index >= 0 && index < static_cast<int>(items_.size())) {
444 items_[index].label_ = label;
445 return true;
446 }
447 return false;
448 }
449
GetType(int command_id)450 CefMenuModelImpl::MenuItemType CefMenuModelImpl::GetType(int command_id) {
451 return GetTypeAt(GetIndexOf(command_id));
452 }
453
GetTypeAt(int index)454 CefMenuModelImpl::MenuItemType CefMenuModelImpl::GetTypeAt(int index) {
455 if (!VerifyContext())
456 return MENUITEMTYPE_NONE;
457
458 if (index >= 0 && index < static_cast<int>(items_.size()))
459 return items_[index].type_;
460 return MENUITEMTYPE_NONE;
461 }
462
GetGroupId(int command_id)463 int CefMenuModelImpl::GetGroupId(int command_id) {
464 return GetGroupIdAt(GetIndexOf(command_id));
465 }
466
GetGroupIdAt(int index)467 int CefMenuModelImpl::GetGroupIdAt(int index) {
468 if (!VerifyContext())
469 return kInvalidGroupId;
470
471 if (index >= 0 && index < static_cast<int>(items_.size()))
472 return items_[index].group_id_;
473 return kInvalidGroupId;
474 }
475
SetGroupId(int command_id,int group_id)476 bool CefMenuModelImpl::SetGroupId(int command_id, int group_id) {
477 return SetGroupIdAt(GetIndexOf(command_id), group_id);
478 }
479
SetGroupIdAt(int index,int group_id)480 bool CefMenuModelImpl::SetGroupIdAt(int index, int group_id) {
481 if (!VerifyContext())
482 return false;
483
484 if (index >= 0 && index < static_cast<int>(items_.size())) {
485 items_[index].group_id_ = group_id;
486 return true;
487 }
488 return false;
489 }
490
GetSubMenu(int command_id)491 CefRefPtr<CefMenuModel> CefMenuModelImpl::GetSubMenu(int command_id) {
492 return GetSubMenuAt(GetIndexOf(command_id));
493 }
494
GetSubMenuAt(int index)495 CefRefPtr<CefMenuModel> CefMenuModelImpl::GetSubMenuAt(int index) {
496 if (!VerifyContext())
497 return nullptr;
498
499 if (index >= 0 && index < static_cast<int>(items_.size()))
500 return items_[index].submenu_.get();
501 return nullptr;
502 }
503
IsVisible(int command_id)504 bool CefMenuModelImpl::IsVisible(int command_id) {
505 return IsVisibleAt(GetIndexOf(command_id));
506 }
507
IsVisibleAt(int index)508 bool CefMenuModelImpl::IsVisibleAt(int index) {
509 if (!VerifyContext())
510 return false;
511
512 if (index >= 0 && index < static_cast<int>(items_.size()))
513 return items_[index].visible_;
514 return false;
515 }
516
SetVisible(int command_id,bool visible)517 bool CefMenuModelImpl::SetVisible(int command_id, bool visible) {
518 return SetVisibleAt(GetIndexOf(command_id), visible);
519 }
520
SetVisibleAt(int index,bool visible)521 bool CefMenuModelImpl::SetVisibleAt(int index, bool visible) {
522 if (!VerifyContext())
523 return false;
524
525 if (index >= 0 && index < static_cast<int>(items_.size())) {
526 items_[index].visible_ = visible;
527 return true;
528 }
529 return false;
530 }
531
IsEnabled(int command_id)532 bool CefMenuModelImpl::IsEnabled(int command_id) {
533 return IsEnabledAt(GetIndexOf(command_id));
534 }
535
IsEnabledAt(int index)536 bool CefMenuModelImpl::IsEnabledAt(int index) {
537 if (!VerifyContext())
538 return false;
539
540 if (index >= 0 && index < static_cast<int>(items_.size()))
541 return items_[index].enabled_;
542 return false;
543 }
544
SetEnabled(int command_id,bool enabled)545 bool CefMenuModelImpl::SetEnabled(int command_id, bool enabled) {
546 return SetEnabledAt(GetIndexOf(command_id), enabled);
547 }
548
SetEnabledAt(int index,bool enabled)549 bool CefMenuModelImpl::SetEnabledAt(int index, bool enabled) {
550 if (!VerifyContext())
551 return false;
552
553 if (index >= 0 && index < static_cast<int>(items_.size())) {
554 items_[index].enabled_ = enabled;
555 return true;
556 }
557 return false;
558 }
559
IsChecked(int command_id)560 bool CefMenuModelImpl::IsChecked(int command_id) {
561 return IsCheckedAt(GetIndexOf(command_id));
562 }
563
IsCheckedAt(int index)564 bool CefMenuModelImpl::IsCheckedAt(int index) {
565 if (!VerifyContext())
566 return false;
567
568 if (index >= 0 && index < static_cast<int>(items_.size()))
569 return items_[index].checked_;
570 return false;
571 }
572
SetChecked(int command_id,bool checked)573 bool CefMenuModelImpl::SetChecked(int command_id, bool checked) {
574 return SetCheckedAt(GetIndexOf(command_id), checked);
575 }
576
SetCheckedAt(int index,bool checked)577 bool CefMenuModelImpl::SetCheckedAt(int index, bool checked) {
578 if (!VerifyContext())
579 return false;
580
581 if (index >= 0 && index < static_cast<int>(items_.size())) {
582 items_[index].checked_ = checked;
583 return true;
584 }
585 return false;
586 }
587
HasAccelerator(int command_id)588 bool CefMenuModelImpl::HasAccelerator(int command_id) {
589 return HasAcceleratorAt(GetIndexOf(command_id));
590 }
591
HasAcceleratorAt(int index)592 bool CefMenuModelImpl::HasAcceleratorAt(int index) {
593 if (!VerifyContext())
594 return false;
595
596 if (index >= 0 && index < static_cast<int>(items_.size()))
597 return items_[index].has_accelerator_;
598 return false;
599 }
600
SetAccelerator(int command_id,int key_code,bool shift_pressed,bool ctrl_pressed,bool alt_pressed)601 bool CefMenuModelImpl::SetAccelerator(int command_id,
602 int key_code,
603 bool shift_pressed,
604 bool ctrl_pressed,
605 bool alt_pressed) {
606 return SetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
607 ctrl_pressed, alt_pressed);
608 }
609
SetAcceleratorAt(int index,int key_code,bool shift_pressed,bool ctrl_pressed,bool alt_pressed)610 bool CefMenuModelImpl::SetAcceleratorAt(int index,
611 int key_code,
612 bool shift_pressed,
613 bool ctrl_pressed,
614 bool alt_pressed) {
615 if (!VerifyContext())
616 return false;
617
618 if (index >= 0 && index < static_cast<int>(items_.size())) {
619 Item& item = items_[index];
620 item.has_accelerator_ = true;
621 item.key_code_ = key_code;
622 item.shift_pressed_ = shift_pressed;
623 item.ctrl_pressed_ = ctrl_pressed;
624 item.alt_pressed_ = alt_pressed;
625 return true;
626 }
627 return false;
628 }
629
RemoveAccelerator(int command_id)630 bool CefMenuModelImpl::RemoveAccelerator(int command_id) {
631 return RemoveAcceleratorAt(GetIndexOf(command_id));
632 }
633
RemoveAcceleratorAt(int index)634 bool CefMenuModelImpl::RemoveAcceleratorAt(int index) {
635 if (!VerifyContext())
636 return false;
637
638 if (index >= 0 && index < static_cast<int>(items_.size())) {
639 Item& item = items_[index];
640 if (item.has_accelerator_) {
641 item.has_accelerator_ = false;
642 item.key_code_ = 0;
643 item.shift_pressed_ = false;
644 item.ctrl_pressed_ = false;
645 item.alt_pressed_ = false;
646 }
647 return true;
648 }
649 return false;
650 }
651
GetAccelerator(int command_id,int & key_code,bool & shift_pressed,bool & ctrl_pressed,bool & alt_pressed)652 bool CefMenuModelImpl::GetAccelerator(int command_id,
653 int& key_code,
654 bool& shift_pressed,
655 bool& ctrl_pressed,
656 bool& alt_pressed) {
657 return GetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
658 ctrl_pressed, alt_pressed);
659 }
660
GetAcceleratorAt(int index,int & key_code,bool & shift_pressed,bool & ctrl_pressed,bool & alt_pressed)661 bool CefMenuModelImpl::GetAcceleratorAt(int index,
662 int& key_code,
663 bool& shift_pressed,
664 bool& ctrl_pressed,
665 bool& alt_pressed) {
666 if (!VerifyContext())
667 return false;
668
669 if (index >= 0 && index < static_cast<int>(items_.size())) {
670 const Item& item = items_[index];
671 if (item.has_accelerator_) {
672 key_code = item.key_code_;
673 shift_pressed = item.shift_pressed_;
674 ctrl_pressed = item.ctrl_pressed_;
675 alt_pressed = item.alt_pressed_;
676 return true;
677 }
678 }
679 return false;
680 }
681
SetColor(int command_id,cef_menu_color_type_t color_type,cef_color_t color)682 bool CefMenuModelImpl::SetColor(int command_id,
683 cef_menu_color_type_t color_type,
684 cef_color_t color) {
685 return SetColorAt(GetIndexOf(command_id), color_type, color);
686 }
687
SetColorAt(int index,cef_menu_color_type_t color_type,cef_color_t color)688 bool CefMenuModelImpl::SetColorAt(int index,
689 cef_menu_color_type_t color_type,
690 cef_color_t color) {
691 if (!VerifyContext())
692 return false;
693
694 if (color_type < 0 || color_type >= CEF_MENU_COLOR_COUNT)
695 return false;
696
697 if (index == kDefaultIndex) {
698 default_colors_[color_type] = color;
699 return true;
700 }
701
702 if (index >= 0 && index < static_cast<int>(items_.size())) {
703 Item& item = items_[index];
704 item.colors_[color_type] = color;
705 return true;
706 }
707
708 return false;
709 }
710
GetColor(int command_id,cef_menu_color_type_t color_type,cef_color_t & color)711 bool CefMenuModelImpl::GetColor(int command_id,
712 cef_menu_color_type_t color_type,
713 cef_color_t& color) {
714 return GetColorAt(GetIndexOf(command_id), color_type, color);
715 }
716
GetColorAt(int index,cef_menu_color_type_t color_type,cef_color_t & color)717 bool CefMenuModelImpl::GetColorAt(int index,
718 cef_menu_color_type_t color_type,
719 cef_color_t& color) {
720 if (!VerifyContext())
721 return false;
722
723 if (color_type < 0 || color_type >= CEF_MENU_COLOR_COUNT)
724 return false;
725
726 if (index == kDefaultIndex) {
727 color = default_colors_[color_type];
728 return true;
729 }
730
731 if (index >= 0 && index < static_cast<int>(items_.size())) {
732 Item& item = items_[index];
733 color = item.colors_[color_type];
734 return true;
735 }
736
737 return false;
738 }
739
SetFontList(int command_id,const CefString & font_list)740 bool CefMenuModelImpl::SetFontList(int command_id, const CefString& font_list) {
741 return SetFontListAt(GetIndexOf(command_id), font_list);
742 }
743
SetFontListAt(int index,const CefString & font_list)744 bool CefMenuModelImpl::SetFontListAt(int index, const CefString& font_list) {
745 if (!VerifyContext())
746 return false;
747
748 if (index == kDefaultIndex) {
749 if (font_list.empty()) {
750 has_default_font_list_ = false;
751 } else {
752 default_font_list_ = gfx::FontList(font_list);
753 has_default_font_list_ = true;
754 }
755 return true;
756 }
757
758 if (index >= 0 && index < static_cast<int>(items_.size())) {
759 Item& item = items_[index];
760 if (font_list.empty()) {
761 item.has_font_list_ = false;
762 } else {
763 item.font_list_ = gfx::FontList(font_list);
764 item.has_font_list_ = true;
765 }
766 return true;
767 }
768 return false;
769 }
770
ActivatedAt(int index,cef_event_flags_t event_flags)771 void CefMenuModelImpl::ActivatedAt(int index, cef_event_flags_t event_flags) {
772 if (!VerifyContext())
773 return;
774
775 const int command_id = GetCommandIdAt(index);
776 if (delegate_)
777 delegate_->ExecuteCommand(this, command_id, event_flags);
778 if (menu_model_delegate_)
779 menu_model_delegate_->ExecuteCommand(this, command_id, event_flags);
780 }
781
MouseOutsideMenu(const gfx::Point & screen_point)782 void CefMenuModelImpl::MouseOutsideMenu(const gfx::Point& screen_point) {
783 if (!VerifyContext())
784 return;
785
786 // Allow the callstack to unwind before notifying the delegate since it may
787 // result in the menu being destroyed.
788 CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
789 FROM_HERE, base::BindOnce(&CefMenuModelImpl::OnMouseOutsideMenu, this,
790 screen_point));
791 }
792
UnhandledOpenSubmenu(bool is_rtl)793 void CefMenuModelImpl::UnhandledOpenSubmenu(bool is_rtl) {
794 if (!VerifyContext())
795 return;
796
797 // Allow the callstack to unwind before notifying the delegate since it may
798 // result in the menu being destroyed.
799 CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
800 FROM_HERE,
801 base::BindOnce(&CefMenuModelImpl::OnUnhandledOpenSubmenu, this, is_rtl));
802 }
803
UnhandledCloseSubmenu(bool is_rtl)804 void CefMenuModelImpl::UnhandledCloseSubmenu(bool is_rtl) {
805 if (!VerifyContext())
806 return;
807
808 // Allow the callstack to unwind before notifying the delegate since it may
809 // result in the menu being destroyed.
810 CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
811 FROM_HERE,
812 base::BindOnce(&CefMenuModelImpl::OnUnhandledCloseSubmenu, this, is_rtl));
813 }
814
GetTextColor(int index,bool is_accelerator,bool is_hovered,SkColor * override_color) const815 bool CefMenuModelImpl::GetTextColor(int index,
816 bool is_accelerator,
817 bool is_hovered,
818 SkColor* override_color) const {
819 if (index >= 0 && index < static_cast<int>(items_.size())) {
820 const Item& item = items_[index];
821 if (!item.enabled_) {
822 // Use accelerator color for disabled item text.
823 is_accelerator = true;
824 }
825
826 const cef_menu_color_type_t color_type =
827 GetMenuColorType(true, is_accelerator, is_hovered);
828 if (item.colors_[color_type] != 0) {
829 *override_color = item.colors_[color_type];
830 return true;
831 }
832 }
833
834 const cef_menu_color_type_t color_type =
835 GetMenuColorType(true, is_accelerator, is_hovered);
836 if (default_colors_[color_type] != 0) {
837 *override_color = default_colors_[color_type];
838 return true;
839 }
840
841 return false;
842 }
843
GetBackgroundColor(int index,bool is_hovered,SkColor * override_color) const844 bool CefMenuModelImpl::GetBackgroundColor(int index,
845 bool is_hovered,
846 SkColor* override_color) const {
847 const cef_menu_color_type_t color_type =
848 GetMenuColorType(false, false, is_hovered);
849
850 if (index >= 0 && index < static_cast<int>(items_.size())) {
851 const Item& item = items_[index];
852 if (item.colors_[color_type] != 0) {
853 *override_color = item.colors_[color_type];
854 return true;
855 }
856 }
857
858 if (default_colors_[color_type] != 0) {
859 *override_color = default_colors_[color_type];
860 return true;
861 }
862
863 return false;
864 }
865
MenuWillShow()866 void CefMenuModelImpl::MenuWillShow() {
867 if (!VerifyContext())
868 return;
869
870 if (delegate_)
871 delegate_->MenuWillShow(this);
872 if (menu_model_delegate_)
873 menu_model_delegate_->MenuWillShow(this);
874 }
875
MenuWillClose()876 void CefMenuModelImpl::MenuWillClose() {
877 if (!VerifyContext())
878 return;
879
880 if (!auto_notify_menu_closed_)
881 return;
882
883 // Due to how menus work on the different platforms, ActivatedAt will be
884 // called after this. It's more convenient for the delegate to be called
885 // afterwards, though, so post a task.
886 CefTaskRunnerImpl::GetCurrentTaskRunner()->PostTask(
887 FROM_HERE, base::BindOnce(&CefMenuModelImpl::OnMenuClosed, this));
888 }
889
GetFormattedLabelAt(int index)890 std::u16string CefMenuModelImpl::GetFormattedLabelAt(int index) {
891 std::u16string label = GetLabelAt(index).ToString16();
892 if (delegate_)
893 delegate_->FormatLabel(this, label);
894 if (menu_model_delegate_) {
895 CefString new_label = label;
896 if (menu_model_delegate_->FormatLabel(this, new_label))
897 label = new_label;
898 }
899 return label;
900 }
901
GetLabelFontListAt(int index) const902 const gfx::FontList* CefMenuModelImpl::GetLabelFontListAt(int index) const {
903 if (index >= 0 && index < static_cast<int>(items_.size())) {
904 const Item& item = items_[index];
905 if (item.has_font_list_)
906 return &item.font_list_;
907 }
908
909 if (has_default_font_list_)
910 return &default_font_list_;
911 return nullptr;
912 }
913
VerifyRefCount()914 bool CefMenuModelImpl::VerifyRefCount() {
915 if (!VerifyContext())
916 return false;
917
918 if (!HasOneRef())
919 return false;
920
921 for (ItemVector::iterator i = items_.begin(); i != items_.end(); ++i) {
922 if ((*i).submenu_.get()) {
923 if (!(*i).submenu_->VerifyRefCount())
924 return false;
925 }
926 }
927
928 return true;
929 }
930
AddMenuItem(const blink::mojom::CustomContextMenuItem & menu_item)931 void CefMenuModelImpl::AddMenuItem(
932 const blink::mojom::CustomContextMenuItem& menu_item) {
933 const int command_id = static_cast<int>(menu_item.action);
934
935 switch (menu_item.type) {
936 case blink::mojom::CustomContextMenuItemType::kOption:
937 AddItem(command_id, menu_item.label);
938 break;
939 case blink::mojom::CustomContextMenuItemType::kCheckableOption:
940 AddCheckItem(command_id, menu_item.label);
941 break;
942 case blink::mojom::CustomContextMenuItemType::kGroup:
943 AddRadioItem(command_id, menu_item.label, 0);
944 break;
945 case blink::mojom::CustomContextMenuItemType::kSeparator:
946 AddSeparator();
947 break;
948 case blink::mojom::CustomContextMenuItemType::kSubMenu: {
949 CefRefPtr<CefMenuModelImpl> sub_menu = static_cast<CefMenuModelImpl*>(
950 AddSubMenu(command_id, menu_item.label).get());
951 for (size_t i = 0; i < menu_item.submenu.size(); ++i)
952 sub_menu->AddMenuItem(*menu_item.submenu[i]);
953 break;
954 }
955 }
956
957 if (!menu_item.enabled &&
958 menu_item.type != blink::mojom::CustomContextMenuItemType::kSeparator)
959 SetEnabled(command_id, false);
960
961 if (menu_item.checked &&
962 (menu_item.type ==
963 blink::mojom::CustomContextMenuItemType::kCheckableOption ||
964 menu_item.type == blink::mojom::CustomContextMenuItemType::kGroup)) {
965 SetChecked(command_id, true);
966 }
967 }
968
NotifyMenuClosed()969 void CefMenuModelImpl::NotifyMenuClosed() {
970 DCHECK(!auto_notify_menu_closed_);
971 OnMenuClosed();
972 }
973
AppendItem(const Item & item)974 void CefMenuModelImpl::AppendItem(const Item& item) {
975 ValidateItem(item);
976 items_.push_back(item);
977 }
978
InsertItemAt(const Item & item,int index)979 void CefMenuModelImpl::InsertItemAt(const Item& item, int index) {
980 // Sanitize the index.
981 if (index < 0)
982 index = 0;
983 else if (index > static_cast<int>(items_.size()))
984 index = items_.size();
985
986 ValidateItem(item);
987 items_.insert(items_.begin() + index, item);
988 }
989
ValidateItem(const Item & item)990 void CefMenuModelImpl::ValidateItem(const Item& item) {
991 #if DCHECK_IS_ON()
992 if (item.type_ == MENUITEMTYPE_SEPARATOR) {
993 DCHECK_EQ(item.command_id_, kSeparatorId);
994 } else {
995 DCHECK_GE(item.command_id_, 0);
996 }
997 #endif
998 }
999
OnMouseOutsideMenu(const gfx::Point & screen_point)1000 void CefMenuModelImpl::OnMouseOutsideMenu(const gfx::Point& screen_point) {
1001 if (delegate_)
1002 delegate_->MouseOutsideMenu(this, screen_point);
1003 if (menu_model_delegate_) {
1004 menu_model_delegate_->MouseOutsideMenu(
1005 this, CefPoint(screen_point.x(), screen_point.y()));
1006 }
1007 }
1008
OnUnhandledOpenSubmenu(bool is_rtl)1009 void CefMenuModelImpl::OnUnhandledOpenSubmenu(bool is_rtl) {
1010 if (delegate_)
1011 delegate_->UnhandledOpenSubmenu(this, is_rtl);
1012 if (menu_model_delegate_)
1013 menu_model_delegate_->UnhandledOpenSubmenu(this, is_rtl);
1014 }
1015
OnUnhandledCloseSubmenu(bool is_rtl)1016 void CefMenuModelImpl::OnUnhandledCloseSubmenu(bool is_rtl) {
1017 if (delegate_)
1018 delegate_->UnhandledCloseSubmenu(this, is_rtl);
1019 if (menu_model_delegate_)
1020 menu_model_delegate_->UnhandledCloseSubmenu(this, is_rtl);
1021 }
1022
OnMenuClosed()1023 void CefMenuModelImpl::OnMenuClosed() {
1024 if (delegate_)
1025 delegate_->MenuClosed(this);
1026 if (menu_model_delegate_)
1027 menu_model_delegate_->MenuClosed(this);
1028 }
1029
VerifyContext()1030 bool CefMenuModelImpl::VerifyContext() {
1031 if (base::PlatformThread::CurrentId() != supported_thread_id_) {
1032 // This object should only be accessed from the thread that created it.
1033 NOTREACHED();
1034 return false;
1035 }
1036
1037 return true;
1038 }
1039