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