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