• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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